diff options
author | Linus Nordberg <linus@nordberg.se> | 2015-02-20 12:20:09 +0100 |
---|---|---|
committer | Linus Nordberg <linus@nordberg.se> | 2015-02-20 12:20:09 +0100 |
commit | 4d2993890fed46c5611735e84d4f737e8c342718 (patch) | |
tree | 6da78e0a5362b8b7e6b0e6331f58e9b45c44dfbf /src | |
parent | 7cbbfa7c7e0fba134838c5c9c58d2d3174232882 (diff) |
Stop validating that cert.issuer matches issuer.subject.
Even canoncalized versions of this data mismatch in otherwise proper
chains. Since we're not here to validate chains for any other reasons
than attribution and spam control, let's stop validate
cert.issuer==candidate.subject. We still verify the cryptographic
chain with signatures of tbsCertificates of course.
Resolves CATLFISH-19.
Diffstat (limited to 'src')
-rw-r--r-- | src/x509.erl | 73 |
1 files changed, 27 insertions, 46 deletions
diff --git a/src/x509.erl b/src/x509.erl index 32ade83..c815ca4 100644 --- a/src/x509.erl +++ b/src/x509.erl @@ -9,7 +9,6 @@ -type reason() :: {chain_too_long | root_unknown | - chain_broken | signature_mismatch | encoding_invalid}. @@ -58,7 +57,10 @@ valid_chain_p(AcceptableRootCerts, [BottomCert|Rest], MaxChainLength) -> Err -> Err end. -%% @doc Return first cert in list signing Cert, or notfound. +%% @doc Return first cert in list signing Cert, or notfound. NOTE: +%% This is potentially expensive. It'd be more efficient to search for +%% Cert.issuer in a list of Issuer.subject's. If so, maybe make the +%% matching somewhat fuzzy unless that too is expensive. -spec signer(binary(), [binary()]) -> notfound | binary(). signer(_Cert, []) -> notfound; @@ -68,15 +70,15 @@ signer(Cert, [H|T]) -> {false, _} -> signer(Cert, T) end. -%% encoded_tbs_cert: verbatim from pubkey_cert.erl -encoded_tbs_cert(Cert) -> +%% Code from pubkey_cert:encoded_tbs_cert/1. +encoded_tbs_cert(DerCert) -> {ok, PKIXCert} = - 'OTP-PUB-KEY':decode_TBSCert_exclusive(Cert), - {'Certificate', - {'Certificate_tbsCertificate', EncodedTBSCert}, _, _} = PKIXCert, + 'OTP-PUB-KEY':decode_TBSCert_exclusive(DerCert), + {'Certificate', {'Certificate_tbsCertificate', EncodedTBSCert}, _, _} = + PKIXCert, EncodedTBSCert. -%% extract_verify_data: close to pubkey_cert:extract_verify_data/2 +%% Code from pubkey_cert:extract_verify_data/2. verifydata_from_cert(Cert, DerCert) -> PlainText = encoded_tbs_cert(DerCert), {_, Sig} = Cert#'Certificate'.signature, @@ -85,18 +87,20 @@ verifydata_from_cert(Cert, DerCert) -> {DigestType,_} = public_key:pkix_sign_types(SigAlg), {PlainText, DigestType, Sig}. -verify(Cert, DerCert, - #'Certificate'{ +%% @doc Verify that Cert/DerCert is signed by Issuer. +verify(Cert, DerCert, % Certificate to verify. + #'Certificate'{ % Issuer. tbsCertificate = #'TBSCertificate'{ subjectPublicKeyInfo = IssuerSPKI}}) -> - {DigestOrPlainText, DigestType, Signature} = - verifydata_from_cert(Cert, DerCert), + + %% Dig out digest, digest type and signature from Cert/DerCert. + {DigestOrPlainText, DigestType, Signature} = verifydata_from_cert(Cert, + DerCert), + %% Dig out issuer key from issuer cert. #'SubjectPublicKeyInfo'{ algorithm = #'AlgorithmIdentifier'{algorithm = Alg, parameters = Params}, subjectPublicKey = {0, Key0}} = IssuerSPKI, KeyType = pubkey_cert_records:supportedPublicKeyAlgorithms(Alg), - - %% public_key:pem_entry_decode() IssuerKey = case KeyType of 'RSAPublicKey' -> @@ -107,43 +111,20 @@ verify(Cert, DerCert, 'ECPoint' -> public_key:der_decode(KeyType, Key0) end, + + %% Verify the signature. public_key:verify(DigestOrPlainText, DigestType, Signature, IssuerKey). +%% @doc Is Cert signed by Issuer? Only verify that the signature +%% matches and don't check things like Cert.issuer == Issuer.subject. -spec signed_by_p(binary(), binary()) -> true | {false, reason()}. signed_by_p(DerCert, IssuerDerCert) when is_binary(DerCert), is_binary(IssuerDerCert) -> - Cert = public_key:pkix_decode_cert(DerCert, plain), - TBSCert = Cert#'Certificate'.tbsCertificate, - IssuerCert = public_key:pkix_decode_cert(IssuerDerCert, plain), - IssuerTBSCert = IssuerCert#'Certificate'.tbsCertificate, - case pubkey_cert:is_issuer(TBSCert#'TBSCertificate'.issuer, - IssuerTBSCert#'TBSCertificate'.subject) of - false -> - {false, chain_broken}; - true -> % Verify signature. - case verify(Cert, DerCert, IssuerCert) of - false -> {false, signature_mismatch}; - true -> true - end - end; -signed_by_p(#'OTPCertificate'{} = Cert, - #'OTPCertificate'{} = IssuerCert) -> - %% FIXME: Validate presence and contents (against constraints) of - %% names (subject, subjectAltName, emailAddress) too? - case (catch public_key:pkix_is_issuer(Cert, IssuerCert)) of - {'EXIT', Reason} -> - lager:info("invalid certificate: ~p: ~p", - [cert_string(Cert), Reason]), - {false, encoding_invalid}; - true -> - %% Cert.issuer does match IssuerCert.subject. Now verify - %% the signature. - case public_key:pkix_verify(Cert, public_key(IssuerCert)) of - true -> true; - false -> {false, signature_mismatch} - end; - false -> - {false, chain_broken} + case verify(public_key:pkix_decode_cert(DerCert, plain), + DerCert, + public_key:pkix_decode_cert(IssuerDerCert, plain)) of + false -> {false, signature_mismatch}; + true -> true end. -spec public_key(binary() | #'OTPCertificate'{}) -> public_key:public_key(). |