How filosottile/cpace works
1. Repository shape
- This is a very small Go library, not an application.
- The codebase is centered on a single package, filippo.io/cpace, with one implementation file and one test file.
- There is no server, CLI, database, or external service boundary.
2. Primary responsibility
- The package implements the CPace password-authenticated key exchange (PAKE).
- It produces a shared session key between two peers who know the same password, while binding the exchange to identities and optional associated data.
3. Cryptographic core
- The implementation is instantiated over ristretto255, a prime-order group.
- That choice simplifies the protocol implementation because it avoids cofactor handling, twist checks, and many low-level curve edge cases.
- The package also uses HKDF and HMAC to separate password material, salt, and context cleanly.
4. Key inputs
- The core inputs are:
- the shared password,
- initiator identity idA,
- responder identity idB,
- optional associated data ad.
- These are combined into a context string (ci) used throughout the exchange.
5. Context binding boundary
- The repository deliberately treats context construction as security-critical.
- Instead of naive concatenation, it uses structured serialization with length prefixes to avoid ambiguity between different identity pairs.
- This is one of the important architectural boundaries in the package: it separates application-provided identity/context data from protocol cryptographic state.
6. Initiator-side flow
- The initiator creates a random salt.
- It generates a random scalar a.
- It derives a group element from the password and context, maps it into the group, and computes the first public message A.
- The initiator sends (salt, A) to the responder.
7. Responder-side flow
- The responder receives salt and A.
- It recomputes the same context ci, generates its own random scalar b, and computes its public message B.
- It also derives the responder-side shared key material K_b from the transcript and the group result.
8. Key agreement boundary
- After the responder sends B, the initiator computes its own key material K_a.
- The intended architecture is symmetric at the cryptographic result level: both sides derive the same shared key from the transcript and password.
- The protocol transcript is part of the key derivation, so the key is bound to the exact exchanged values.
9. Transcript and session binding
- The derived key depends on salt, A, B, and the context.
- The README explicitly notes that the salt is generated by the initiator and sent to the peer, rather than being caller-supplied.
- That makes the salt part of the protocol message flow rather than an application-managed parameter.
10. Security model boundaries
- The package is intentionally low-level and does not include a higher-level verifier/HMAC confirmation round.
- The README suggests that a stronger API could wrap this package to prove both peers actually derived the same secret before exposing application data.
- That means the library currently stops at key agreement, not full authenticated channel establishment.
11. Known API caveats
- The README calls out two possible misuse cases:
- identities may be empty,
- the initiator state may be reused with multiple responder messages.
- These are important diagram annotations because they indicate where the current API leaves security policy to the caller.
12. Testing and validation
- cpace_test.go provides package-level tests for the implementation.
- There are no integration tests with external infrastructure because the repo is fully self-contained cryptographic code.
13. What to draw in an architecture diagram
- Draw a single library boundary around cpace.go.
- Inside it, show:
- context construction (idA, idB, ad → ci),
- initiator state,
- responder state,
- shared cryptographic primitives (ristretto255, HKDF, HMAC, randomness).
- Outside the boundary, show two peers exchanging (salt, A) and B, both deriving the same session key from the password and transcript.
Main entry points: cpace.go cpace.go · cpace_test.go cpace_test.go
Components 13
cpace package
Single Go library boundary implementing the CPace PAKE.
- cpace.go · Main implementation of the CPace protocol.
cpace.go - Context · Structured serialization of idA, idB, and associated data into ci.
- Salt · Initiator-generated salt carried in the first message.
- Initiator state · Derives A from password, context, random scalar, and salt.
- Responder state · Derives B and shared key material from transcript and password.
- Transcript · Binds salt, A, B, and context into the final key derivation.
- HKDF · Separates password material, salt, and context into protocol inputs.
- HMAC · Used for keyed transcript and secret derivation.
- ristretto255 · Prime-order group used for CPace scalar and point operations.
- Randomness · Sources the salt and ephemeral scalars a and b.
Other components
- Initiator · Peer that starts the exchange and sends the first message.
- Responder · Peer that receives the first message and returns the response.
- cpace_test.go · Package-level tests for the protocol implementation.
cpace_test.go
Connections 18
- Initiator → cpace.go: calls · Initiator uses the package API to start the exchange.
- Responder → cpace.go: calls · Responder uses the package API to complete the exchange.
- Context → Initiator state: binds · Context is incorporated before initiator message generation.
- Context → Responder state: binds · Context is recomputed on the responder side.
- Randomness → Salt: creates · Initiator generates the protocol salt.
- Randomness → Initiator state: samples a · Initiator draws ephemeral scalar a.
- Randomness → Responder state: samples b · Responder draws ephemeral scalar b.
- HKDF → Initiator state: derives · Password and context are turned into protocol input material.
- HKDF → Responder state: derives · Responder derives matching input material from the shared password and salt.
- ristretto255 → Initiator state: computes A · Initiator maps password-derived material into a group element and forms A.
- ristretto255 → Responder state: computes B · Responder computes its public message B.
- Initiator state → Transcript: uses · Initiator later folds the transcript into key material Ka.
- Responder state → Transcript: uses · Responder folds the transcript into key material Kb.
- Transcript → HMAC: authenticates · Transcript binding uses keyed MAC construction.
- Transcript → HKDF: stretches · Key derivation separates the final shared key from protocol inputs.
- cpace.go → cpace_test.go: validated by · Tests exercise the package-level implementation.
- Initiator → Responder: sends (salt, A) · Initiator sends the first protocol message.
- Responder → Initiator: returns B · Responder sends the second protocol message.