// // Rfc5054VectorsTests.swift // PortalKitTests // // Verification of RFC 5054 Appendix B test vectors (1024-bit group with SHA-1). // import XCTest @testable import PortalKit final class Rfc5054VectorsTests: XCTestCase { let group = SrpGroup.rfc5054_1024 // RFC 5054 Appendix B inputs let I = "alice" let P = "password123" let sHex = "BEB25379D1A8581EB5A727673A2441EE" // RFC 5054 Appendix B expected values let expectedKHex = "7556AA045AEF2CDD07ABAF0F665C3E818913186F" let expectedXHex = "94B7555AABE9127CC58CCF4993DB6CF84D16C124" let expectedVHex = """ 7E273DE8696FFC4F4E337D05B4B375BEB0DDE1569E8FA00A9886D8129BADA1F1\ 822223CA1A605B530E379BA4729FDC59F105B4787E5186F5C671085A1447B52A\ 48CF1970B4FB6F8400BBF4CEBFBB168152E08AB5EA53D15C1AFF87B2B9DA6E04\ E058AD51CC72BFC9033B564E26480D78E955A5E29E7AB245DB2BE315E2099AFB """ let expectedAHex = "60975527035CF2AD1989806F0407210BC81EDC04E2762A56AFD529DDDA2D4393" let expectedBHex = "E487CB59D31AC550471E81F00F6928E01DDA08E974A004F49E61F5D105284D20" let expectedPubAHex = """ 61D5E490F6F1B79547B0704C436F523DD0E560F0C64115BB72557EC44352E890\ 3211C04692272D8B2D1A5358A2CF1B6E0BFCF99F921530EC8E39356179EAE45E\ 42BA92AEACED825171E1E8B9AF6D9C03E1327F44BE087EF06530E69F66615261\ EEF54073CA11CF5858F0EDFDFE15EFEAB349EF5D76988A3672FAC47B0769447B """ let expectedPubBHex = """ BD0C61512C692C0CB6D041FA01BB152D4916A1E77AF46AE105393011BAF38964\ DC46A0670DD125B95A981652236F99D9B681CBF87837EC996C6DA04453728610\ D0C6DDB58B318885D7D82C7F8DEB75CE7BD4FBAA37089E6F9C6059F388838E7A\ 00030B331EB76840910440B1B27AAEAEEB4012B7D7665238A8E3FB004B117B58 """ let expectedUHex = "CE38B9593487DA98554ED47D70A7AE5F462EF019" let expectedSHex = """ B0DC82BABCF30674AE450C0287745E7990A3381F63B387AAF271A10D233861E3\ 59B48220F7C4693C9AE12B0A6F67809F0876E2D013800D6C41BB59B6D5979B5C\ 00A172B4A2A5903A0BDCAF8A709585EB2AFAFA8F3499B200210DCC1F10EB3394\ 3CD67FC88A2F39A4BE5BEC4EC0A3212DC346D7E474B29EDE8A469FFECA686E5A """ func testMultiplierK() { let computedK = group.k let computedKHex = SrpFormat.bytesToHex(computedK.serialize()) XCTAssertEqual(computedKHex.lowercased(), expectedKHex.lowercased(), "RFC 5054 multiplier k mismatch") } func testPrivateKeyX() { guard let sData = SrpFormat.hexToBytes(sHex) else { XCTFail("Failed to decode salt hex") return } let x = SrpMath.computeRfc5054X(identity: I, password: P, salt: sData, hashAlgorithm: group.hashAlgorithm) let xHex = SrpFormat.bytesToHex(x.serialize()) XCTAssertEqual(xHex.lowercased(), expectedXHex.lowercased(), "RFC 5054 private key x mismatch") } func testVerifierV() { guard let xVal = BigUInt(expectedXHex, radix: 16) else { XCTFail("Failed to parse expected x") return } let v = SrpMath.computeVerifier(g: group.g, x: xVal, N: group.N) let vHex = SrpFormat.bytesToHex(v.toPaddedData(byteCount: 128)) XCTAssertEqual(vHex.lowercased(), expectedVHex.lowercased(), "RFC 5054 verifier v mismatch") } func testPublicA() { guard let aVal = BigUInt(expectedAHex, radix: 16) else { XCTFail("Failed to parse expected a") return } let A = SrpMath.computeA(g: group.g, a: aVal, N: group.N) let aHex = SrpFormat.bytesToHex(A.toPaddedData(byteCount: 128)) XCTAssertEqual(aHex.lowercased(), expectedPubAHex.lowercased(), "RFC 5054 public key A mismatch") } func testPublicB() { guard let bVal = BigUInt(expectedBHex, radix: 16), let vVal = BigUInt(expectedVHex, radix: 16) else { XCTFail("Failed to parse b or v") return } let B = SrpMath.computeB(k: group.k, v: vVal, g: group.g, b: bVal, N: group.N) let bHex = SrpFormat.bytesToHex(B.toPaddedData(byteCount: 128)) XCTAssertEqual(bHex.lowercased(), expectedPubBHex.lowercased(), "RFC 5054 public key B mismatch") } func testScramblerU() { guard let aVal = BigUInt(expectedPubAHex, radix: 16), let bVal = BigUInt(expectedPubBHex, radix: 16) else { XCTFail("Failed to parse A or B") return } let u = SrpMath.computeU(A: aVal, B: bVal, padLength: 128, hashAlgorithm: group.hashAlgorithm) let uHex = SrpFormat.bytesToHex(u.serialize()) XCTAssertEqual(uHex.lowercased(), expectedUHex.lowercased(), "RFC 5054 scrambler u mismatch") } func testPremasterSecretS() { guard let aVal = BigUInt(expectedAHex, radix: 16), let bVal = BigUInt(expectedBHex, radix: 16), let pubA = BigUInt(expectedPubAHex, radix: 16), let pubB = BigUInt(expectedPubBHex, radix: 16), let xVal = BigUInt(expectedXHex, radix: 16), let vVal = BigUInt(expectedVHex, radix: 16), let uVal = BigUInt(expectedUHex, radix: 16) else { XCTFail("Failed to parse test parameters") return } // Client computation: S = (B - k * (g^x mod N)) ^ (a + u * x) mod N let clientS = SrpMath.computeClientS( B: pubB, k: group.k, g: group.g, x: xVal, a: aVal, u: uVal, N: group.N ) let clientSHex = SrpFormat.bytesToHex(clientS.toPaddedData(byteCount: 128)) XCTAssertEqual(clientSHex.lowercased(), expectedSHex.lowercased(), "Client premaster secret S mismatch") // Server computation: S = (A * (v^u mod N)) ^ b mod N let serverS = SrpMath.computeServerS( A: pubA, v: vVal, u: uVal, b: bVal, N: group.N ) let serverSHex = SrpFormat.bytesToHex(serverS.toPaddedData(byteCount: 128)) XCTAssertEqual(serverSHex.lowercased(), expectedSHex.lowercased(), "Server premaster secret S mismatch") // Mutual agreement XCTAssertEqual(clientS, serverS, "Client and Server premaster secrets do not match") } }