summaryrefslogtreecommitdiff
path: root/src/x509.erl
diff options
context:
space:
mode:
authorLinus Nordberg <linus@nordberg.se>2015-02-20 12:20:09 +0100
committerLinus Nordberg <linus@nordberg.se>2015-02-20 12:20:09 +0100
commit4d2993890fed46c5611735e84d4f737e8c342718 (patch)
tree6da78e0a5362b8b7e6b0e6331f58e9b45c44dfbf /src/x509.erl
parent7cbbfa7c7e0fba134838c5c9c58d2d3174232882 (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/x509.erl')
-rw-r--r--src/x509.erl73
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().