4 ms·
So, it sounds like (please correct me if I misunderstood something) the issue is: # cache signing objects and get chain cache, chain = parse_chain_from_cer
by archgoon 7y ago
So, it sounds like (please correct me if I misunderstood something) the issue is:
# cache signing objects and get chain
cache, chain = parse_chain_from_cert(full_cert)
root = chain[-1]
validate_root_authenticity(root)
# go up chain from leaf to the root
for cert in chain[:-1]:
parent = cert.parent()
# Wrong Signing object retrieved here:
signing_object = cache[parent.public_key]
signing_object.validate(cert)
So the leaf signing object is used instead of the roots, as they share the same public key, but that doesn't contain the parameters of the curve.
- rfoo 7y agoInterested in how exactly it was fucked up I looked at crypt32.dll. It's more like (heavily-adjusted to demonstrate the problem without >9k unrelated details): from the_heavy_lifting_crypto_heavy_module import verify_signature def is_known_trusted_ca(cert): # BOOM return cert in root_ca_cache[cert.pubkey] def verify_cert(cert, issuer): # BOOM if verified_cache[(cert.pubkey, issuer.pubkey)]: return True if not verify_signature(cert, issuer): return False verified_cache[(cert.pubkey, issuer.pubkey)] = True return True # This has to be a search instead of simple chain-walking because servers # do not send a "chain", they only send several additional certificates and # let the client find a trusted path out of them, it is also how cross signing # works. This justifies using a cache above. def try_to_build_cert_chain(cert, cert_store): if is_known_trusted_ca(cert): return [cert] for possible_issuer in cert_store.find_certs_with_dn(dn=cert.issuer_dn): if verify_cert(cert, possible_issuer): chain = try_to_build_cert_chain(possible_issuer, cert_store) if chain: return [cert] + chain Then someone tasked with (why?!) "adding unnamed curve support" added (correct!) code to `the_heavy_lifting_crypto_heavy_module.verify_signature` but didn't know this breaks an assumption made in another module. The exact detail does not matter though, you can build up working exploits even if you don't know the details listed above.
- tialaramex 7y agoThe servers are supposed to send a chain ("SHOULD" is the strongest language an RFC uses about chains) but, in practice, the only way to get where you're going as a client is to treat it as one leaf and then everything else is hints that might help you construct the chain. The reason is that a server doesn't know which roots you actually trust, so maybe it thinks you trust X and X->A->B->C so it sends X->A, A->B, B->C but actually you trust A so you only needed to receive A->B, and B->C - X->A doesn't help you make a decision so your code needs to disregard it.
- rfoo 7y agoIn a more convoluted scenario (cross sign) a server can really send you two (or more) chains so I'd take that "SHOULD" with a grain of salt. For example, Let's Encrypt Authority X3 is signed by both ISRG Root X1 (their own root CA) and DST Root CA X3. The server could send you: 1. Server cert 2. Let's Encrypt Authority X3 (Signed by ISRG Root X1) 3. Let's Encrypt Authority X3 (Signed by DST Root CA X3) Combined with: 4. ISRG Root X1 5. DST Root CA X3 If the client trusts 4, then it could form a path of 1-2-4. If it does not but trusts 5, then 1-3-5 would be viable.