Init
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
//
|
||||
// SafetyChecksTests.swift
|
||||
// PortalKitTests
|
||||
//
|
||||
// Verification of SRP parameter validation and safety guards (RFC 5054).
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import PortalKit
|
||||
|
||||
final class SafetyChecksTests: XCTestCase {
|
||||
let validCertHash = Data(repeating: 0xaa, count: 32)
|
||||
let validSaltHex = "0102030405060708090a0b0c0d0e0f10"
|
||||
let validPubBHex = SrpFormat.bytesToHex(BigUInt(123456789).toPadded256Data())
|
||||
let validPin = "123456"
|
||||
|
||||
func testInvalidSaltThrows() {
|
||||
let client = PortalSrpClient()
|
||||
|
||||
// Empty salt
|
||||
XCTAssertThrowsError(
|
||||
try client.computeM1(saltHex: "", pubBHex: validPubBHex, pin: validPin, tlsCertSha256: validCertHash)
|
||||
) { error in
|
||||
guard case SrpError.invalidParameter(let m) = error else {
|
||||
XCTFail("Wrong error: \(error)")
|
||||
return
|
||||
}
|
||||
XCTAssertTrue(m.contains("salt"))
|
||||
}
|
||||
|
||||
// Odd length hex salt
|
||||
XCTAssertThrowsError(
|
||||
try client.computeM1(saltHex: "abc", pubBHex: validPubBHex, pin: validPin, tlsCertSha256: validCertHash)
|
||||
)
|
||||
|
||||
// Non-hex characters in salt
|
||||
XCTAssertThrowsError(
|
||||
try client.computeM1(saltHex: "invalid-hex-characters!!", pubBHex: validPubBHex, pin: validPin, tlsCertSha256: validCertHash)
|
||||
)
|
||||
}
|
||||
|
||||
func testInvalidBModuloNThrows() {
|
||||
let client = PortalSrpClient()
|
||||
|
||||
// B = 0 -> B % N == 0
|
||||
let zeroBHex = SrpFormat.bytesToHex(BigUInt(0).toPadded256Data())
|
||||
XCTAssertThrowsError(
|
||||
try client.computeM1(saltHex: validSaltHex, pubBHex: zeroBHex, pin: validPin, tlsCertSha256: validCertHash)
|
||||
) { error in
|
||||
guard case SrpError.invalidParameter(let m) = error else {
|
||||
XCTFail("Wrong error: \(error)")
|
||||
return
|
||||
}
|
||||
XCTAssertTrue(m.contains("B % N == 0"))
|
||||
}
|
||||
|
||||
// B = N -> B % N == 0
|
||||
let nBHex = SrpFormat.bytesToHex(PortalSrpClient.N.toPadded256Data())
|
||||
XCTAssertThrowsError(
|
||||
try client.computeM1(saltHex: validSaltHex, pubBHex: nBHex, pin: validPin, tlsCertSha256: validCertHash)
|
||||
) { error in
|
||||
guard case SrpError.invalidParameter(let m) = error else {
|
||||
XCTFail("Wrong error: \(error)")
|
||||
return
|
||||
}
|
||||
XCTAssertTrue(m.contains("B % N == 0"))
|
||||
}
|
||||
|
||||
// B = 2*N -> B % N == 0
|
||||
let twoNBHex = SrpFormat.bytesToHex((PortalSrpClient.N * 2).serialize())
|
||||
XCTAssertThrowsError(
|
||||
try client.computeM1(saltHex: validSaltHex, pubBHex: twoNBHex, pin: validPin, tlsCertSha256: validCertHash)
|
||||
) { error in
|
||||
guard case SrpError.invalidParameter(let m) = error else {
|
||||
XCTFail("Wrong error: \(error)")
|
||||
return
|
||||
}
|
||||
XCTAssertTrue(m.contains("B % N == 0"))
|
||||
}
|
||||
|
||||
// Empty pubBHex
|
||||
XCTAssertThrowsError(
|
||||
try client.computeM1(saltHex: validSaltHex, pubBHex: "", pin: validPin, tlsCertSha256: validCertHash)
|
||||
)
|
||||
|
||||
// Non-hex pubBHex
|
||||
XCTAssertThrowsError(
|
||||
try client.computeM1(saltHex: validSaltHex, pubBHex: "not_hex", pin: validPin, tlsCertSha256: validCertHash)
|
||||
)
|
||||
}
|
||||
|
||||
func testEmptyPinThrows() {
|
||||
let client = PortalSrpClient()
|
||||
XCTAssertThrowsError(
|
||||
try client.computeM1(saltHex: validSaltHex, pubBHex: validPubBHex, pin: "", tlsCertSha256: validCertHash)
|
||||
) { error in
|
||||
guard case SrpError.invalidParameter(let m) = error else {
|
||||
XCTFail("Wrong error: \(error)")
|
||||
return
|
||||
}
|
||||
XCTAssertTrue(m.contains("PIN"))
|
||||
}
|
||||
}
|
||||
|
||||
func testEmptyTlsCertHashThrows() {
|
||||
let client = PortalSrpClient()
|
||||
XCTAssertThrowsError(
|
||||
try client.computeM1(saltHex: validSaltHex, pubBHex: validPubBHex, pin: validPin, tlsCertSha256: Data())
|
||||
) { error in
|
||||
guard case SrpError.invalidParameter(let m) = error else {
|
||||
XCTFail("Wrong error: \(error)")
|
||||
return
|
||||
}
|
||||
XCTAssertTrue(m.contains("certificate"))
|
||||
}
|
||||
}
|
||||
|
||||
func testInvalidUZeroCheck() {
|
||||
let _ = SrpServerMock(pin: validPin)
|
||||
// If a server or client encounters u == 0, it must be rejected as an invalid scrambler.
|
||||
let uZero = BigUInt(0)
|
||||
XCTAssertEqual(uZero, 0)
|
||||
|
||||
// Verify SrpError formatting for u == 0
|
||||
let err = SrpError.invalidParameter("Computed u == 0")
|
||||
XCTAssertEqual(err.localizedDescription, "SRP Parameter Error: Computed u == 0")
|
||||
}
|
||||
|
||||
func testInvalidM2Throws() throws {
|
||||
let client = PortalSrpClient()
|
||||
|
||||
// Verify before computeM1 must fail
|
||||
XCTAssertThrowsError(try client.verifyServerM2(serverM2Hex: "123456")) { error in
|
||||
guard case SrpError.verificationFailed(let m) = error else {
|
||||
XCTFail("Wrong error: \(error)")
|
||||
return
|
||||
}
|
||||
XCTAssertTrue(m.contains("not initialized"))
|
||||
}
|
||||
|
||||
// Initialize state
|
||||
_ = try client.computeM1(saltHex: validSaltHex, pubBHex: validPubBHex, pin: validPin, tlsCertSha256: validCertHash)
|
||||
|
||||
// Invalid hex
|
||||
XCTAssertThrowsError(try client.verifyServerM2(serverM2Hex: "invalid-hex"))
|
||||
|
||||
// Odd length hex
|
||||
XCTAssertThrowsError(try client.verifyServerM2(serverM2Hex: "abc"))
|
||||
|
||||
// Wrong M2
|
||||
let wrongM2 = SrpFormat.bytesToHex(Data(repeating: 0x99, count: 32))
|
||||
XCTAssertThrowsError(try client.verifyServerM2(serverM2Hex: wrongM2)) { error in
|
||||
guard case SrpError.verificationFailed(let m) = error else {
|
||||
XCTFail("Wrong error: \(error)")
|
||||
return
|
||||
}
|
||||
XCTAssertTrue(m.contains("Server evidence M2 does not match"))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user