109 lines
3.5 KiB
Swift
109 lines
3.5 KiB
Swift
//
|
|
// Srp6aExchangeTests.swift
|
|
// PortalKitTests
|
|
//
|
|
// Verification of 2048-bit SRP-6a end-to-end key exchange with mock server parameters.
|
|
//
|
|
|
|
import XCTest
|
|
@testable import PortalKit
|
|
|
|
final class Srp6aExchangeTests: XCTestCase {
|
|
func testEndToEndExchangeSuccess() throws {
|
|
let pin = "654321"
|
|
let mockCertHash = SrpGroup.rfc5054_2048.hashAlgorithm.hash("MOCK_CERT_LEAF_DER".data(using: .utf8)!)
|
|
|
|
let server = SrpServerMock(pin: pin)
|
|
let client = PortalSrpClient()
|
|
|
|
// Client computes M1 bound to mock TLS cert hash
|
|
let clientM1Hex = try client.computeM1(
|
|
saltHex: server.saltHex,
|
|
pubBHex: server.pubBHex,
|
|
pin: pin,
|
|
tlsCertSha256: mockCertHash
|
|
)
|
|
|
|
XCTAssertFalse(clientM1Hex.isEmpty)
|
|
XCTAssertNotNil(client.sessionKey)
|
|
|
|
// Server verifies client's A and M1
|
|
let verifyResult = server.verifyClient(
|
|
pubAHex: client.pubAHex,
|
|
clientM1Hex: clientM1Hex,
|
|
serverTlsCertSha256: mockCertHash
|
|
)
|
|
|
|
XCTAssertTrue(verifyResult.success, "Server rejected valid client proof")
|
|
XCTAssertNotNil(verifyResult.M2Hex)
|
|
XCTAssertNotNil(verifyResult.token)
|
|
|
|
// Client verifies server M2
|
|
XCTAssertNoThrow(
|
|
try client.verifyServerM2(serverM2Hex: verifyResult.M2Hex!),
|
|
"Client failed to verify genuine server M2"
|
|
)
|
|
}
|
|
|
|
func testWrongPinFailsExchange() throws {
|
|
let actualPin = "123456"
|
|
let wrongPin = "999999"
|
|
let mockCertHash = Data(repeating: 0x42, count: 32)
|
|
|
|
let server = SrpServerMock(pin: actualPin)
|
|
let client = PortalSrpClient()
|
|
|
|
// Client computes M1 with wrong PIN
|
|
let clientM1Hex = try client.computeM1(
|
|
saltHex: server.saltHex,
|
|
pubBHex: server.pubBHex,
|
|
pin: wrongPin,
|
|
tlsCertSha256: mockCertHash
|
|
)
|
|
|
|
// Server verification MUST fail
|
|
let verifyResult = server.verifyClient(
|
|
pubAHex: client.pubAHex,
|
|
clientM1Hex: clientM1Hex,
|
|
serverTlsCertSha256: mockCertHash
|
|
)
|
|
|
|
XCTAssertFalse(verifyResult.success)
|
|
XCTAssertNil(verifyResult.token)
|
|
XCTAssertNil(verifyResult.M2Hex)
|
|
XCTAssertTrue(verifyResult.errorMessage?.contains("M1 verification failed") ?? false)
|
|
}
|
|
|
|
func testMultipleRandomExchanges() throws {
|
|
// Run 5 randomized rounds to verify no modular arithmetic edge cases
|
|
for round in 1...5 {
|
|
let pin = String(format: "%06d", round * 111111 % 1000000)
|
|
let mockCertHash = SrpGroup.rfc5054_2048.hashAlgorithm.hash("CERT_ROUND_\(round)".data(using: .utf8)!)
|
|
|
|
let server = SrpServerMock(pin: pin)
|
|
let client = PortalSrpClient()
|
|
|
|
let m1Hex = try client.computeM1(
|
|
saltHex: server.saltHex,
|
|
pubBHex: server.pubBHex,
|
|
pin: pin,
|
|
tlsCertSha256: mockCertHash
|
|
)
|
|
|
|
let serverRes = server.verifyClient(
|
|
pubAHex: client.pubAHex,
|
|
clientM1Hex: m1Hex,
|
|
serverTlsCertSha256: mockCertHash
|
|
)
|
|
|
|
XCTAssertTrue(serverRes.success, "Round \(round) failed server verification")
|
|
guard let m2 = serverRes.M2Hex else {
|
|
XCTFail("Round \(round) missing M2")
|
|
continue
|
|
}
|
|
|
|
XCTAssertNoThrow(try client.verifyServerM2(serverM2Hex: m2), "Round \(round) failed client M2 verification")
|
|
}
|
|
}
|
|
}
|