task 1: S-1 remediation — no-pop posture doc + OQ-TLS-09 + permanent impersonation pin
- AcceptAnyCertVerifier doc: the presented CertificateVerify signature
is not verified (no proof-of-possession) — the server-extracted
fingerprint is attacker-suppliable from observed public cert/SPKI
bytes; states the two safe patterns (auth-layer challenge-response /
a verifying verifier) and points at OQ-TLS-09
- FingerprintPinVerifier doc (N-1): fixed the "stolen-but-stale
fingerprint" phrasing (the cert is presented fresh each handshake;
the signature check defeats a stolen/observed cert used by a party
without the private key) and added the server-verifier cross-reference
- OQ-TLS-09 recorded (open, high): which layer owns server-path
proof-of-possession — three options; deferral noted (needs the
auth-layer design or an API call before the first consumer)
- tests/impersonation_posture.rs (tcp-gated): the S-1 probe made
permanent, both variants — X.509 victim cert + attacker key and RFC
7250 victim SPKI + attacker key complete the handshake, application
data flows, and the server extracts the victim's fingerprint; any
future pop change must fail/update this test with the doc + OQ
- server.md / client.md synced with the same posture
- task note: the review's N-4 parenthetical ("alknet's client resolver
offers both types") is inaccurate — rustls 0.23.41/0.23.44 offer
[RawPublicKey] iff the resolver's only_raw_public_keys() is true;
task 6 should write N-4 from the rustls sources
Verified: cargo test 68 default / 77 all-features (+2) green; clippy
-D warnings clean (default + all-features); fmt clean; cargo doc
--no-deps warning-free
This commit is contained in:
1 parent
d7db6b17a6
commit
e86b8ba1b5
7 files changed
+414
-21
No files matched your search
+11
-3
@@ -231,9 +231,17 @@ impl rustls::client::ResolvesClientCert for NoClientCertResolver {
|
||||
/// CA verification and no name verification, only the fingerprint pin.
|
||||
///
|
||||
/// Handshake signatures are still verified (using the aws-lc-rs default
|
||||
/// signature verification algorithms) so that a stolen-but-stale fingerprint
|
||||
/// can't be replayed with a forged signature: the presenter must prove
|
||||
/// possession of the private key corresponding to the pinned public key.
|
||||
/// signature verification algorithms): the presenter must prove possession
|
||||
/// of the private key corresponding to the pinned public key, so the pin
|
||||
/// cannot be satisfied by replaying the cert's public bytes under a
|
||||
/// different key. The cert is presented fresh in every handshake — the
|
||||
/// threat the signature check addresses is a stolen or observed
|
||||
/// *certificate* being used by a party that does not hold the matching
|
||||
/// private key.
|
||||
///
|
||||
/// This verifier checks proof-of-possession. The **server-side**
|
||||
/// `AcceptAnyCertVerifier` does not — see that type's docs (S-1 in
|
||||
/// `docs/reviews/001-implementation-review.md`, OQ-TLS-09).
|
||||
pub struct FingerprintPinVerifier {
|
||||
fingerprint: String,
|
||||
supported: rustls::crypto::WebPkiSupportedAlgorithms,
|
||||
|
||||
+37
-1
@@ -249,11 +249,47 @@ pub fn generate_self_signed_cert() -> Result<SelfSignedCert, TlsError> {
|
||||
/// (`IdentityProvider::resolve_from_fingerprint`) is the auth layer's
|
||||
/// concern, outside this crate (alktls ADR-005).
|
||||
///
|
||||
/// # The presented signature is NOT verified (no proof-of-possession)
|
||||
///
|
||||
/// The client's CertificateVerify signature is never checked against the
|
||||
/// presented cert's public key: `verify_client_cert` accepts any cert and
|
||||
/// the `verify_tls12_signature`/`verify_tls13_signature` methods assert
|
||||
/// validity unconditionally. Consequently **the fingerprint this verifier
|
||||
/// lets the server extract is attacker-suppliable**: any party that
|
||||
/// observes a peer's public cert bytes (X.509) or SPKI (RFC 7250) can
|
||||
/// complete a handshake presenting those bytes under its own key, and
|
||||
/// `peer_certificates()` yields the victim's cert — every downstream
|
||||
/// fingerprint decision (scopes, tokens, resumption) is then made against
|
||||
/// a spoofed identity. The auth layer's peer table cannot detect this: the
|
||||
/// fingerprint it is handed *is* the victim's. Until the caller enforces
|
||||
/// possession, treat the extracted fingerprint as an unauthenticated
|
||||
/// claim, not proof of identity.
|
||||
///
|
||||
/// Two patterns make the identity safe — the auth layer owns either:
|
||||
///
|
||||
/// 1. **Challenge-response over the established channel**, bound to the
|
||||
/// presented public key: possession is proven post-handshake, so a
|
||||
/// stolen/observed fingerprint cannot be replayed without the key.
|
||||
/// 2. **A verifier that does verify the CertificateVerify** against the
|
||||
/// presented cert — the same
|
||||
/// `verify_tls13_signature_with_raw_key` / `verify_tls12_signature`
|
||||
/// routing the client-side `FingerprintPinVerifier` implements. The
|
||||
/// crate has the routing code, just not on this type (a tracked
|
||||
/// question — see OQ-TLS-09 in `docs/architecture/open-questions.md`).
|
||||
///
|
||||
/// This behavior is pinned end-to-end by
|
||||
/// `tests/impersonation_posture.rs`: a handshake with the victim's cert
|
||||
/// bytes + an attacker signer completes and the server extracts the
|
||||
/// victim's fingerprint. A change that adds proof-of-possession (or an
|
||||
/// upstream rustls change that enforces it) must fail that test and
|
||||
/// update this doc + OQ-TLS-09 together.
|
||||
///
|
||||
/// **Server-side only.** This must not be reused as a client-side
|
||||
/// `ServerCertVerifier` — client-side verification is alknet ADR-034's
|
||||
/// selection matrix (see the client module): CA verification for unknown
|
||||
/// X.509 remotes, fingerprint pinning for known peers, fail closed for
|
||||
/// unknown raw keys.
|
||||
/// unknown raw keys. Unlike the client-side pin verifier, this type has
|
||||
/// **no proof-of-possession check** (see above).
|
||||
pub struct AcceptAnyCertVerifier;
|
||||
|
||||
impl std::fmt::Debug for AcceptAnyCertVerifier {
|
||||
|
||||
Reference in new issue
Block a user