diff --git a/.gitignore b/.gitignore index 84637359..0127a1e0 100644 --- a/.gitignore +++ b/.gitignore @@ -25,5 +25,4 @@ ios/Local.xcconfig cli/where_cli_state.json *.swp *.txt -*.log -TODO_SPEC_UPDATES.md +.log diff --git a/shared/src/commonMain/kotlin/net/af0/where/e2ee/E2eeStore.kt b/shared/src/commonMain/kotlin/net/af0/where/e2ee/E2eeStore.kt index 31e6f917..3c9009ca 100644 --- a/shared/src/commonMain/kotlin/net/af0/where/e2ee/E2eeStore.kt +++ b/shared/src/commonMain/kotlin/net/af0/where/e2ee/E2eeStore.kt @@ -409,7 +409,7 @@ internal class E2eeStore( database.invitesQueries.insertPendingInvite( ekPub = invite.qrPayload.ekPub, suggestedName = invite.qrPayload.suggestedName, - fingerprint = invite.qrPayload.fingerprint, + fingerprint = invite.qrPayload.fingerprint ?: "", discoverySecret = invite.qrPayload.discoverySecret, privKeyBlob = invite.aliceEkPriv, createdAt = invite.createdAt, diff --git a/shared/src/commonMain/kotlin/net/af0/where/e2ee/KeyExchange.kt b/shared/src/commonMain/kotlin/net/af0/where/e2ee/KeyExchange.kt index 1eeb7e69..f1abf4c2 100644 --- a/shared/src/commonMain/kotlin/net/af0/where/e2ee/KeyExchange.kt +++ b/shared/src/commonMain/kotlin/net/af0/where/e2ee/KeyExchange.kt @@ -33,11 +33,10 @@ object KeyExchange { } /** - * Bob: verify the QR fingerprint, perform single-term DH, derive SK, compute - * key_confirmation HMAC, and return the KeyExchangeInit message plus the initial - * session state. + * Bob: perform single-term DH, derive SK, compute key_confirmation HMAC, and + * return the KeyExchangeInit message plus the initial session state. * - * Throws AuthenticationException if the fingerprint or key_confirmation fails. + * Throws AuthenticationException if key_confirmation fails. */ fun bobProcessQr( qr: QrPayload, @@ -48,12 +47,6 @@ object KeyExchange { throw ProtocolVersionException("Unsupported protocol version ${qr.protocolVersion}") } - // VERIFY FINGERPRINT (#157) - val expectedFp = qrFingerprint(qr.ekPub) - if (expectedFp != qr.fingerprint) { - throw AuthenticationException("QR code fingerprint mismatch — possible tampering or mis-scan") - } - val ekB = generateX25519KeyPair() val sk = x25519(ekB.priv, qr.ekPub) diff --git a/shared/src/commonMain/kotlin/net/af0/where/e2ee/Types.kt b/shared/src/commonMain/kotlin/net/af0/where/e2ee/Types.kt index 7d01f0ef..ba43fe25 100644 --- a/shared/src/commonMain/kotlin/net/af0/where/e2ee/Types.kt +++ b/shared/src/commonMain/kotlin/net/af0/where/e2ee/Types.kt @@ -216,9 +216,9 @@ data class QrPayload( @Serializable(with = ByteArrayBase64Serializer::class) val ekPub: ByteArray, @SerialName("suggested_name") val suggestedName: String, - // hex(SHA-256(ekPub)[0:20]) + // hex(SHA-256(ekPub)[0:20]); optional — ignored by receiver (Stage 1 of §4.2 removal) @SerialName("fingerprint") - val fingerprint: String, + val fingerprint: String? = null, // Fresh random 32-byte secret; HKDF IKM for the discovery token (§4.2). @SerialName("discovery_secret") @Serializable(with = ByteArrayBase64Serializer::class) val discoverySecret: ByteArray, diff --git a/shared/src/commonTest/kotlin/net/af0/where/e2ee/KeyExchangeTest.kt b/shared/src/commonTest/kotlin/net/af0/where/e2ee/KeyExchangeTest.kt index 9c21c5ea..5e955349 100644 --- a/shared/src/commonTest/kotlin/net/af0/where/e2ee/KeyExchangeTest.kt +++ b/shared/src/commonTest/kotlin/net/af0/where/e2ee/KeyExchangeTest.kt @@ -23,7 +23,7 @@ class KeyExchangeTest { val (qr, ekPriv) = KeyExchange.aliceCreateQrPayload("Alice") assertEquals(32, qr.ekPub.size) - assertEquals(40, qr.fingerprint.length) // hex(SHA-256(ekPub)[0:20]) + assertEquals(40, qr.fingerprint?.length) // hex(SHA-256(ekPub)[0:20]); still sent by Alice // ekPriv is 32 bytes and non-zero. assertEquals(32, ekPriv.size) @@ -214,21 +214,6 @@ class KeyExchangeTest { sk.zeroize() } - @Test - fun `bobProcessQr rejects tampered fingerprint`() { - val (qr, _) = KeyExchange.aliceCreateQrPayload("Alice") - val badQr = qr.copy(fingerprint = "0".repeat(40)) // Tampered fingerprint - - val threw = - try { - KeyExchange.bobProcessQr(badQr, "Bob") - false - } catch (_: AuthenticationException) { - true - } - assertTrue(threw, "Expected AuthenticationException for tampered fingerprint") - } - @Test fun `name encryption and decryption works correctly`() { val (qr, aliceEkPriv) = KeyExchange.aliceCreateQrPayload("Alice")