summaryrefslogtreecommitdiff
path: root/lib/public_key/src/pubkey_ocsp.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/public_key/src/pubkey_ocsp.erl')
-rw-r--r--lib/public_key/src/pubkey_ocsp.erl360
1 files changed, 128 insertions, 232 deletions
diff --git a/lib/public_key/src/pubkey_ocsp.erl b/lib/public_key/src/pubkey_ocsp.erl
index 249b430660..6bdb9a563f 100644
--- a/lib/public_key/src/pubkey_ocsp.erl
+++ b/lib/public_key/src/pubkey_ocsp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2023. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -19,18 +19,55 @@
%%
-module(pubkey_ocsp).
-
-include("public_key.hrl").
--export([otp_cert/1,
- get_ocsp_responder_id/1,
+-export([find_single_response/3,
+ get_acceptable_response_types_extn/0,
get_nonce_extn/1,
- decode_ocsp_response/1,
+ get_ocsp_responder_id/1,
+ ocsp_status/1,
verify_ocsp_response/3,
- get_acceptable_response_types_extn/0,
- find_single_response/3,
- ocsp_status/1]).
+ decode_ocsp_response/1]).
+%% Tracing
+-export([handle_trace/3]).
+
+-spec get_ocsp_responder_id(#'Certificate'{}) -> binary().
+get_ocsp_responder_id(#'Certificate'{tbsCertificate = TbsCert}) ->
+ public_key:der_encode(
+ 'ResponderID', {byName, TbsCert#'TBSCertificate'.subject}).
+
+-spec get_nonce_extn(undefined | binary()) -> undefined | #'Extension'{}.
+get_nonce_extn(undefined) ->
+ undefined;
+get_nonce_extn(Nonce) when is_binary(Nonce) ->
+ #'Extension'{
+ extnID = ?'id-pkix-ocsp-nonce',
+ extnValue = Nonce
+ }.
+
+-spec verify_ocsp_response(#'BasicOCSPResponse'{}, list(), undefined | binary()) ->
+ {ok, term()} | {error, term()}.
+verify_ocsp_response(OCSPResponse, ResponderCerts, Nonce) ->
+ do_verify_ocsp_response(OCSPResponse, ResponderCerts, Nonce).
+
+-spec get_acceptable_response_types_extn() -> #'Extension'{}.
+get_acceptable_response_types_extn() ->
+ #'Extension'{
+ extnID = ?'id-pkix-ocsp-response',
+ extnValue = public_key:der_encode(
+ 'AcceptableResponses', [?'id-pkix-ocsp-basic'])
+ }.
+
+-spec find_single_response(#'OTPCertificate'{}, #'OTPCertificate'{},
+ [#'SingleResponse'{}]) ->
+ {ok, #'SingleResponse'{}} | {error, no_matched_response}.
+find_single_response(Cert, IssuerCert, SingleResponseList) ->
+ IssuerName = get_subject_name(IssuerCert),
+ IssuerKey = get_public_key(IssuerCert),
+ SerialNum = get_serial_num(Cert),
+ match_single_response(IssuerName, IssuerKey, SerialNum, SingleResponseList).
+-spec ocsp_status({atom(), term()}) -> atom() | {atom(), {atom(), term()}}.
ocsp_status({good, _}) ->
valid;
ocsp_status({unknown, Reason}) ->
@@ -38,61 +75,64 @@ ocsp_status({unknown, Reason}) ->
ocsp_status({revoked, Reason}) ->
{bad_cert, {revoked, Reason}}.
-%%--------------------------------------------------------------------
--spec verify_ocsp_response(binary(), list(), undefined | binary()) ->
- {ok, term()} | {error, term()}.
-%%
-%% Description: Verify the OCSP response to get the certificate status
-%%--------------------------------------------------------------------
-verify_ocsp_response(OCSPResponseDer, ResponderCerts, Nonce) ->
- do_verify_ocsp_response(
- decode_ocsp_response(OCSPResponseDer), ResponderCerts, Nonce
- ).
+decode_ocsp_response(ResponseDer) ->
+ Resp = public_key:der_decode('OCSPResponse', ResponseDer),
+ case Resp#'OCSPResponse'.responseStatus of
+ successful ->
+ decode_response_bytes(
+ Resp#'OCSPResponse'.responseBytes
+ );
+ Error ->
+ {error, Error}
+ end.
%%--------------------------------------------------------------------
--spec do_verify_ocsp_response({ok, #'BasicOCSPResponse'{}} | {error, term()},
- list(), undefined | binary()) ->
- {ok, term()} | {error, term()}.
-%%
-%% Description: Verify the OCSP response to get the certificate status
-%%--------------------------------------------------------------------
-do_verify_ocsp_response(
- {ok, #'BasicOCSPResponse'{
- tbsResponseData = ResponseData,
- signatureAlgorithm = SignatureAlgo,
- signature = Signature,
- certs = Certs
- }}, ResponderCerts, Nonce) ->
+match_single_response(_IssuerName, _IssuerKey, _SerialNum, []) ->
+ {error, no_matched_response};
+match_single_response(IssuerName, IssuerKey, SerialNum,
+ [#'SingleResponse'{
+ certID = #'CertID'{hashAlgorithm = Algo} = CertID} =
+ Response | Responses]) ->
+ HashType = public_key:pkix_hash_type(Algo#'AlgorithmIdentifier'.algorithm),
+ case (SerialNum == CertID#'CertID'.serialNumber) andalso
+ (crypto:hash(HashType, IssuerName) == CertID#'CertID'.issuerNameHash) andalso
+ (crypto:hash(HashType, IssuerKey) == CertID#'CertID'.issuerKeyHash) of
+ true ->
+ {ok, Response};
+ false ->
+ match_single_response(IssuerName, IssuerKey, SerialNum, Responses)
+ end.
- #'ResponseData'{
- responderID = ResponderID
- } = ResponseData,
+get_serial_num(#'OTPCertificate'{tbsCertificate = TbsCert}) ->
+ TbsCert#'OTPTBSCertificate'.serialNumber.
+decode_response_bytes(#'ResponseBytes'{
+ responseType = ?'id-pkix-ocsp-basic',
+ response = Data}) ->
+ {ok, public_key:der_decode('BasicOCSPResponse', Data)};
+decode_response_bytes(#'ResponseBytes'{responseType = RespType}) ->
+ {error, {ocsp_response_type_not_supported, RespType}}.
+
+do_verify_ocsp_response(#'BasicOCSPResponse'{
+ tbsResponseData = ResponseData,
+ signatureAlgorithm = SignatureAlgo,
+ signature = Signature},
+ ResponderCerts, Nonce) ->
+ #'ResponseData'{responderID = ResponderID} = ResponseData,
case verify_ocsp_signature(
- public_key:der_encode('ResponseData', ResponseData),
- SignatureAlgo#'AlgorithmIdentifier'.algorithm,
- Signature, Certs ++ ResponderCerts,
- ResponderID) of
+ public_key:der_encode('ResponseData', ResponseData),
+ SignatureAlgo#'AlgorithmIdentifier'.algorithm,
+ Signature, ResponderCerts,
+ ResponderID) of
ok ->
verify_ocsp_nonce(ResponseData, Nonce);
{error, Reason} ->
{error, Reason}
- end;
-do_verify_ocsp_response({error, Reason}, _ResponderCerts, _Nonce) ->
- {error, Reason}.
+ end.
-%%--------------------------------------------------------------------
--spec verify_ocsp_nonce(#'ResponseData'{}, undefined | binary()) ->
- {ok, term()} | {error, nonce_mismatch}.
-%%
-%% Description: Check if the nonces matches in OCSP response
-%%--------------------------------------------------------------------
verify_ocsp_nonce(ResponseData, Nonce) ->
- #'ResponseData'{
- responses = Responses,
- responseExtensions = ResponseExtns
- } = ResponseData,
-
+ #'ResponseData'{responses = Responses, responseExtensions = ResponseExtns} =
+ ResponseData,
case get_nonce_value(ResponseExtns) of
Nonce ->
{ok, Responses};
@@ -100,12 +140,6 @@ verify_ocsp_nonce(ResponseData, Nonce) ->
{error, nonce_mismatch}
end.
-%%--------------------------------------------------------------------
--spec get_nonce_value(asn1_NOVALUE | list()) ->
- undefined | binary().
-%%
-%% Description: Get the nonce value from extensions
-%%--------------------------------------------------------------------
%% no extensions present in response
get_nonce_value(asn1_NOVALUE) ->
undefined;
@@ -119,44 +153,8 @@ get_nonce_value([#'Extension'{
get_nonce_value([_Extn | Rest]) ->
get_nonce_value(Rest).
-%%--------------------------------------------------------------------
--spec decode_ocsp_response(binary()) ->
- {ok, #'BasicOCSPResponse'{}} | {error, term()}.
-%%
-%% Description: Decode the OCSP response der
-%%--------------------------------------------------------------------
-decode_ocsp_response(Response) ->
- Resp = public_key:der_decode('OCSPResponse', Response),
- case Resp#'OCSPResponse'.responseStatus of
- successful ->
- decode_response_bytes(
- Resp#'OCSPResponse'.responseBytes
- );
- Error ->
- {error, Error}
- end.
-
-%%--------------------------------------------------------------------
--spec decode_response_bytes(#'ResponseBytes'{}) ->
- {ok, #'BasicOCSPResponse'{}} | {error, term()}.
-%%
-%% Description: Get basic ocsp response field
-%%--------------------------------------------------------------------
-decode_response_bytes(#'ResponseBytes'{
- responseType = ?'id-pkix-ocsp-basic',
- response = Data}) ->
- {ok, public_key:der_decode('BasicOCSPResponse', Data)};
-decode_response_bytes(#'ResponseBytes'{responseType = RespType}) ->
- {error, {ocsp_response_type_not_supported, RespType}}.
-
-%%--------------------------------------------------------------------
--spec verify_ocsp_signature(binary(), term(), term(), list(), term()) ->
- ok | {error, term()}.
-%%
-%% Description: Verify the signature of OCSP response
-%%--------------------------------------------------------------------
-verify_ocsp_signature(
- ResponseDataDer, SignatureAlgo, Signature, Certs, ResponderID) ->
+verify_ocsp_signature(ResponseDataDer, SignatureAlgo, Signature,
+ Certs, ResponderID) ->
case find_responder_cert(ResponderID, Certs) of
{ok, Cert} ->
do_verify_ocsp_signature(
@@ -165,12 +163,6 @@ verify_ocsp_signature(
{error, Reason}
end.
-%%--------------------------------------------------------------------
--spec find_responder_cert(term(), list()) ->
- {ok, #'Certificate'{} | #'OTPCertificate'{}} | {error, term()}.
-%%
-%% Description: Find the OCSP responder's cert in input list
-%%--------------------------------------------------------------------
find_responder_cert(_ResponderID, []) ->
{error, ocsp_responder_cert_not_found};
find_responder_cert(ResponderID, [Cert | TCerts]) ->
@@ -181,87 +173,29 @@ find_responder_cert(ResponderID, [Cert | TCerts]) ->
find_responder_cert(ResponderID, TCerts)
end.
-%%--------------------------------------------------------------------
--spec do_verify_ocsp_signature(
- binary(), term(), term(), #'Certificate'{} | #'OTPCertificate'{}) ->
- ok | {error, term()}.
-%%
-%% Description: Verify the signature of OCSP response
-%%--------------------------------------------------------------------
do_verify_ocsp_signature(ResponseDataDer, Signature, AlgorithmID, Cert) ->
{DigestType, _SignatureType} = public_key:pkix_sign_types(AlgorithmID),
case public_key:verify(
- ResponseDataDer, DigestType, Signature,
- get_public_key_rec(Cert)) of
+ ResponseDataDer, DigestType, Signature,
+ get_public_key_rec(Cert)) of
true ->
ok;
false ->
{error, ocsp_response_bad_signature}
end.
-%%--------------------------------------------------------------------
--spec get_public_key_rec(#'Certificate'{} | #'OTPCertificate'{}) ->
- term().
-%%
-%% Description: Get the subject public key field
-%%--------------------------------------------------------------------
-get_public_key_rec(#'Certificate'{} = Cert) ->
- get_public_key_rec(otp_cert(Cert));
get_public_key_rec(#'OTPCertificate'{tbsCertificate = TbsCert}) ->
PKInfo = TbsCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
PKInfo#'OTPSubjectPublicKeyInfo'.subjectPublicKey.
-%%--------------------------------------------------------------------
--spec is_responder(tuple(), #'Certificate'{} | #'OTPCertificate'{}) ->
- boolean().
-%%
-%% Description: Check if is OCSP responder's cert
-%%--------------------------------------------------------------------
is_responder({byName, Name}, Cert) ->
public_key:der_encode('Name', Name) == get_subject_name(Cert);
is_responder({byKey, Key}, Cert) ->
Key == crypto:hash(sha, get_public_key(Cert)).
-%%--------------------------------------------------------------------
--spec otp_cert(#'Certificate'{} | #'OTPCertificate'{} | binary()) ->
- #'OTPCertificate'{}.
-%%
-%% Description: Convert to #'OTPCertificate'{}
-%%--------------------------------------------------------------------
-otp_cert(#'OTPCertificate'{} = Cert) ->
- Cert;
-otp_cert(#'Certificate'{} = Cert) ->
- public_key:pkix_decode_cert(
- public_key:der_encode('Certificate', Cert), otp);
-otp_cert(CertDer) when is_binary(CertDer) ->
- public_key:pkix_decode_cert(CertDer, otp).
-
-%%--------------------------------------------------------------------
--spec get_ocsp_responder_id(#'Certificate'{}) -> binary().
-%%
-%% Description: Get the OCSP responder ID der
-%%--------------------------------------------------------------------
-get_ocsp_responder_id(#'Certificate'{tbsCertificate = TbsCert}) ->
- public_key:der_encode(
- 'ResponderID', {byName, TbsCert#'TBSCertificate'.subject}).
-
-%%--------------------------------------------------------------------
--spec get_subject_name(#'Certificate'{} | #'OTPCertificate'{}) -> binary().
-%%
-%% Description: Get the subject der from cert
-%%--------------------------------------------------------------------
-get_subject_name(#'Certificate'{} = Cert) ->
- get_subject_name(otp_cert(Cert));
get_subject_name(#'OTPCertificate'{tbsCertificate = TbsCert}) ->
public_key:pkix_encode('Name', TbsCert#'OTPTBSCertificate'.subject, otp).
-%%--------------------------------------------------------------------
--spec get_public_key(#'Certificate'{} | #'OTPCertificate'{}) -> binary().
-%%
-%% Description: Get the public key from cert
-%%--------------------------------------------------------------------
-get_public_key(#'Certificate'{} = Cert) ->
- get_public_key(otp_cert(Cert));
get_public_key(#'OTPCertificate'{tbsCertificate = TbsCert}) ->
PKInfo = TbsCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
enc_pub_key(PKInfo#'OTPSubjectPublicKeyInfo'.subjectPublicKey).
@@ -273,73 +207,35 @@ enc_pub_key({DsaInt, #'Dss-Parms'{}}) when is_integer(DsaInt) ->
enc_pub_key({#'ECPoint'{point = Key}, _ECParam}) ->
Key.
-%%--------------------------------------------------------------------
--spec get_nonce_extn(undefined | binary()) -> undefined | #'Extension'{}.
-%%
-%% Description: Get an OCSP nonce der
-%%--------------------------------------------------------------------
-get_nonce_extn(undefined) ->
- undefined;
-get_nonce_extn(Nonce) when is_binary(Nonce) ->
- #'Extension'{
- extnID = ?'id-pkix-ocsp-nonce',
- extnValue = Nonce
- }.
-
-%%--------------------------------------------------------------------
--spec get_acceptable_response_types_extn() -> #'Extension'{}.
-%%
-%% Description: Get an acceptable response types der
-%%--------------------------------------------------------------------
-get_acceptable_response_types_extn() ->
- #'Extension'{
- extnID = ?'id-pkix-ocsp-response',
- extnValue = public_key:der_encode(
- 'AcceptableResponses', [?'id-pkix-ocsp-basic'])
- }.
-
-%%--------------------------------------------------------------------
--spec get_serial_num(binary | #'Certificate'{} | #'OTPCertificate'{}) ->
- term().
-%%
-%% Description: Get the serial number of a certificate
-%%--------------------------------------------------------------------
-get_serial_num(Cert) ->
- #'OTPCertificate'{tbsCertificate = TbsCert} = otp_cert(Cert),
- TbsCert#'OTPTBSCertificate'.serialNumber.
-
-
-%%--------------------------------------------------------------------
-%% -spec find_single_response(#'OTPCertificate'{}, #'OTPCertificate'{},
-%% [#'SingleResponse'{}]) ->
-%% #'SingleResponse'{} | {error, no_matched_response}.
-%% %%
-%% Description: Find the matched single response.
-%%--------------------------------------------------------------------
-find_single_response(Cert, IssuerCert, SingleResponseList) ->
- IssuerName = get_subject_name(IssuerCert),
- IssuerKey = get_public_key(IssuerCert),
- SerialNum = get_serial_num(Cert),
- match_single_response(
- IssuerName, IssuerKey, SerialNum, SingleResponseList).
-
-match_single_response(_IssuerName, _IssuerKey, _SerialNum, []) ->
- {error, no_matched_response};
-match_single_response(
- IssuerName, IssuerKey, SerialNum,
- [#'SingleResponse'{
- certID = #'CertID'{hashAlgorithm = Algo} = CertID
- } = Response | Responses]) ->
- HashType = public_key:pkix_hash_type(
- Algo#'AlgorithmIdentifier'.algorithm),
- case (SerialNum == CertID#'CertID'.serialNumber) andalso
- (crypto:hash(
- HashType, IssuerName) == CertID#'CertID'.issuerNameHash) andalso
- (crypto:hash(
- HashType, IssuerKey) == CertID#'CertID'.issuerKeyHash) of
- true ->
- {ok, Response};
- false ->
- match_single_response(IssuerName, IssuerKey, SerialNum, Responses)
- end.
-
+%%%################################################################
+%%%#
+%%%# Tracing
+%%%#
+handle_trace(csp,
+ {call, {?MODULE, do_verify_ocsp_response, [BasicOcspResponse | _]}}, Stack) ->
+ #'BasicOCSPResponse'{
+ tbsResponseData =
+ #'ResponseData'{responderID = ResponderID,
+ producedAt = ProducedAt}} = BasicOcspResponse,
+ {io_lib:format("ResponderId = ~W producedAt = ~p", [ResponderID, 5, ProducedAt]), Stack};
+handle_trace(csp,
+ {call, {?MODULE, match_single_response,
+ [_IssuerName, _IssuerKey, _SerialNum,
+ [#'SingleResponse'{thisUpdate = ThisUpdate,
+ nextUpdate = NextUpdate}]]}}, Stack) ->
+ {io_lib:format("ThisUpdate = ~p NextUpdate = ~p", [ThisUpdate, NextUpdate]), Stack};
+handle_trace(csp,
+ {call, {?MODULE, is_responder, [Id, Cert]}}, Stack) ->
+ {io_lib:format("~nId = ~P~nCert = ~P", [Id, 10, Cert, 10]), Stack};
+handle_trace(csp,
+ {call, {?MODULE, find_single_response, [Cert, IssuerCert | _]}}, Stack) ->
+ {io_lib:format("#2 OCSP validation started~nCert = ~W IssuerCert = ~W",
+ [Cert, 7, IssuerCert, 7]), Stack};
+ %% {io_lib:format("#2 OCSP validation started~nCert = ~s IssuerCert = ~s",
+ %% [ssl_test_lib:format_cert(Cert),
+ %% ssl_test_lib:format_cert(IssuerCert)]), Stack};
+
+handle_trace(csp,
+ {return_from, {?MODULE, is_responder, 2}, Return},
+ Stack) ->
+ {io_lib:format("Return = ~p", [Return]), Stack}.