Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion PQXDHLean/AEAD.lean
Original file line number Diff line number Diff line change
@@ -1,7 +1,31 @@
/-
Authenticated Encryption with Associated Data (AEAD).

Definitions follow Bhargavan et al., USENIX Security 2024.
Definitions follow Bhargavan et al., USENIX Security 2024, §2.1.
Signal uses AES-256-CBC + HMAC in Encrypt-then-MAC mode.

## Current scope

This structure suffices for **correctness proofs** (X3DH_handshake_correct):
- `encrypt` and `decrypt` are deterministic
- Nonce is omitted — X3DH uses each session key exactly once

## Future work (security proofs)

When filling in the IND-CPA + INT-CTXT security proof (Theorem 2,
§3.2), two extensions may be needed:

1. **Nonce parameter**: Real AEAD requires a nonce to avoid
key-nonce reuse. The paper shows `enc(msg, 0, AD, SK)` where
`0` is the nonce. For a general AEAD formalization, `encrypt`
should become `K → PT → Nonce → AD → CT`.

2. **Probabilistic encryption**: Some AEAD modes involve
randomness (e.g., IV generation). Security proofs (IND-CPA)
may require `encrypt` to be an `OracleComp unifSpec`.

The security assumption (IND-CPA + INT-CTXT) is formalized
separately in `SecurityDefs.lean` as an `opaque Prop`.
-/

/-- AEAD scheme parameterized by key `K`, plaintext `PT`, ciphertext `CT`,
Expand Down
25 changes: 24 additions & 1 deletion PQXDHLean/KEM.lean
Original file line number Diff line number Diff line change
@@ -1,7 +1,30 @@
/-
Key Encapsulation Mechanism (KEM).

Definitions follow Bhargavan et al., USENIX Security 2024.
Definitions follow Bhargavan et al., USENIX Security 2024, §2.1.

## Current scope

This structure suffices for **correctness proofs** (PQXDH_agree):
- `encaps` and `decaps` are deterministic
- `keygen` is omitted — callers supply matching (pk, sk) pairs

## Future work (security proofs)

When filling in the IND-CCA security proof (Theorem 3, §3.2) or
the SH-CR proof (Theorem 5, §4), two extensions are needed:

1. **`keygen`**: The IND-CCA game begins with `(pk, sk) ← KEM.keygen`.
This should be a probabilistic operation (`OracleComp unifSpec`).

2. **Probabilistic `encaps`**: Real KEM encapsulation is randomized.
For security proofs, `encaps` should become
`PK → OracleComp unifSpec (CT × SS)`, similar to how `KDF` has
a deterministic form (`KDF.derive`) and a probabilistic form
(`KDFOracle` as a random oracle).

One approach: introduce a separate `KEMSpec` (probabilistic, for
security) alongside this `KEM` (deterministic, for correctness).
-/

/-- KEM parameterized by public key `PK`, secret key `SK_kem`,
Expand Down
9 changes: 7 additions & 2 deletions PQXDHLean/X3DH/X3DH.lean
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ structure KeyPair (F G : Type _) [Field F] [AddCommGroup G] [Module F G]

/-! ## X3DH shared secret computation

**Notation convention:**
- Lowercase names (`ikₐ`, `ekₐ`, `spkᵦ`, `opkᵦ`) denote **private keys** (scalars in `F`)
- Uppercase names (`IKᵦ`, `SPKᵦ`, `OPKᵦ`) denote **public keys** (group elements in `G`)
- Subscripts `ₐ` / `ᵦ` indicate the owner (Alice / Bob)

Alice and Bob each compute DH values from their private keys
and the other party's public keys:

Expand Down Expand Up @@ -98,13 +103,13 @@ def X3DH_SK_Alice
(ikₐ ekₐ : F) (IKᵦ SPKᵦ : G) (OPKᵦ : Option G) : SK :=
kdf.derive (X3DH_Alice ikₐ ekₐ IKᵦ SPKᵦ OPKᵦ)

/-- Bob derives the session key SK_B = KDF(DH1 ‖ DH2 ‖ DH3 ‖ DH4). -/
/-- Bob derives the session key SKᵦ = KDF(DH1 ‖ DH2 ‖ DH3 ‖ DH4). -/
def X3DH_SK_Bob
(kdf : KDF (G × G × G × G) SK)
(ikᵦ spkᵦ : F) (opkᵦ : Option F) (IKₐ EKₐ : G) : SK :=
kdf.derive (X3DH_Bob ikᵦ spkᵦ opkᵦ IKₐ EKₐ)

/-- Session key agreement: SKₐ = SK_B.
/-- Session key agreement: SKₐ = SKᵦ.
Composes `X3DH_agree` with the determinism of the KDF. -/
theorem X3DH_session_key_agree
(kdf : KDF (G × G × G × G) SK)
Expand Down
14 changes: 12 additions & 2 deletions PQXDHLean/X3DH/X3DHPassiveMessageSecrecy.lean
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ distinguish the session key from a random key.

## The game

1. Sample all private keys uniformly from F.
1. Sample the 5 private keys uniformly from F:
ikₐ, ekₐ (Alice's identity + ephemeral),
ikᵦ, spkᵦ, opkᵦ (Bob's identity + signed prekey + one-time prekey).
2. Compute public keys and the four DH values via X3DH.
3. Query the KDF oracle (random oracle) on the DH tuple to get
the real session key.
4. Sample a random key.
4. Sample a random key uniformly from SK (same distribution as the
KDF's output space).
5. Flip a bit b; give the adversary the public transcript
and either the real key (b = true) or the random key.
6. The adversary outputs a guess b'.
Expand Down Expand Up @@ -170,6 +173,13 @@ The reduction receives the DDH challenge `(g, EKₐ, SPKᵦ, T)`,
embeds T as DH3, queries the ROM on the resulting DH tuple to
get a session key, and passes it directly to the adversary.

Why DH3? Among the four X3DH DH values, DH3 = ekₐ • SPKᵦ is the
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you revise and elaborate this further?

Copy link
Copy Markdown
Author

@jinxinglim jinxinglim May 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh. While I was reading through this file, I was wondering why do we embeds T as DH3 and not other DH. So wanted this paragraph to briefly explains why DH3 was chosen to be embedded by T. Perhaps it is not clear in my explanation. The following is an updated version of it:

"""
Why the reduction embeds T as DH3? The DDH challenge fixes two group elements (A, B) whose discrete logs are hidden from the reduction; these are installed as EKₐ and SPKᵦ in the simulated game, while ikₐ, ikᵦ, opkᵦ are sampled by the reduction itself. Of the four DH values, three can then be computed directly from the public keys and the reduction's own scalars: DH1 = ikₐ • SPKᵦ uses ikₐ, and DH2 = ekₐ • IKᵦ, DH4 = ekₐ • OPKᵦ are recovered via DH commutativity as ikᵦ • EKₐ and opkᵦ • EKₐ. DH3 = ekₐ • SPKᵦ is the only one requiring both hidden scalars — exactly the product ab • g that the challenge T encodes. So T is embedded at DH3, and the rest of the tuple is filled in honestly.
"""

WDYT? If you think providing more explanation may be even more confusing, then we can simply remove this newly added paragraph completely.

only one whose computation needs both of the secret scalars
hidden in the DDH challenge (ekₐ and spkᵦ). The other DH values
(DH1, DH2, DH4) each involve at least one scalar the reduction
samples itself (ikₐ, ikᵦ, or opkᵦ), so they can be computed
honestly. Embedding T at DH3 is what makes the reduction possible.

No internal coin flip — the DDH experiment's own bit handles
the real/random branching. The reduction simply forwards the
adversary's guess. -/
Expand Down