import Foundation
import Testing
@testable import IrdP2PCrypto
@Suite struct IdentityVectorTests {
/// Public keys and fingerprints are deterministic; signatures are verified rather
/// than compared byte for byte because CryptoKit may randomize Ed25519 nonces.
@Test func casesMatchReference() throws {
for c in try vector("identity")["cases"]!.arr {
let id = try Identity(seedBase64URL: c["seed"]!.str)
#expect(id.seedBase64URL == c["seed"]!.str)
#expect(id.publicKeyBase64URL == c["publicKeyB64url"]!.str, "\(c["name"]!.str)")
#expect(id.fingerprint.emoji == c["fingerprint"]!["emoji"]!.str)
#expect(id.fingerprint.hex == c["fingerprint"]!["hex"]!.str)
for a in c["assertions"]!.arr {
let (salt, room, p) = (a["appSalt"]!.str, a["roomId"]!.str, a["peerId"]!.str)
#expect(Identity.verifyAssertion(appSalt: salt, roomId: room, peerId: p, publicKeyBase64URL: id.publicKeyBase64URL, signature: a["sigB64"]!.str) == id.fingerprint)
let ours = Bytes.base64(id.signRaw(Identity.bindingBytes(appSalt: salt, roomId: room, peerId: p)))
#expect(Identity.verifyAssertion(appSalt: salt, roomId: room, peerId: p, publicKeyBase64URL: id.publicKeyBase64URL, signature: ours) != nil)
}
for r in c["rawSignatures"]!.arr {
let data = Data(r["dataUtf8"]!.str.utf8)
#expect(Identity.verifyRaw(publicKeyBase64URL: id.publicKeyBase64URL, signatureBase64URL: r["sigB64url"]!.str, data: data))
#expect(Identity.verifyRaw(publicKeyBase64URL: id.publicKeyBase64URL, signatureBase64URL: Bytes.base64URL(id.signRaw(data)), data: data))
}
}
}
@Test func assertRoundTripsThroughPublicAPI() throws {
let id = Identity.generate()
let room = RoomCrypto.roomId(secret: .random(), appSalt: "x/v1")
let p = PeerId.random()
let sig = id.assert(appSalt: "x/v1", roomId: room, peerId: p)
#expect(Bytes.fromBase64(sig)?.count == 64)
#expect(Identity.verifyAssertion(appSalt: "x/v1", roomId: room, peerId: p, publicKeyBase64URL: id.publicKeyBase64URL, signature: sig) == id.fingerprint)
#expect(Identity.verifyAssertion(appSalt: "y/v1", roomId: room, peerId: p, publicKeyBase64URL: id.publicKeyBase64URL, signature: sig) == nil)
}
@Test func verifyAssertionTable() throws {
for c in try vector("identity")["verifyAssertion"]!.arr {
let a = c["args"]!.arr.map(\.str)
let result = Identity.verifyAssertion(appSalt: a[0], roomId: a[1], peerId: a[2], publicKeyBase64URL: a[3], signature: a[4])
#expect((result != nil) == c["ok"]!.boolValue!, "\(c["desc"]!.str)")
}
}
@Test func seedValidation() throws {
#expect(throws: IdentifierError.malformedSeed) { try Identity(seedBase64URL: "AAAA") }
#expect(throws: IdentifierError.malformedSeed) { try Identity(seedBase64URL: "***") }
let g = Identity.generate()
#expect(g.seedBase64URL.count == 43)
let back = try Identity(seedBase64URL: g.seedBase64URL)
#expect(back.publicKey == g.publicKey)
}
@Test func emojiTableIsIntact() {
#expect(Fingerprint.emojiTable.count == 64)
#expect(Set(Fingerprint.emojiTable).count == 64)
}
}