summaryrefslogtreecommitdiff
path: root/lib/public_key/src/pubkey_cert.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/public_key/src/pubkey_cert.erl')
-rw-r--r--lib/public_key/src/pubkey_cert.erl100
1 files changed, 76 insertions, 24 deletions
diff --git a/lib/public_key/src/pubkey_cert.erl b/lib/public_key/src/pubkey_cert.erl
index abe69d9ae2..4d448fd42c 100644
--- a/lib/public_key/src/pubkey_cert.erl
+++ b/lib/public_key/src/pubkey_cert.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2022. 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.
@@ -72,7 +72,7 @@ verify_data(DerCert) ->
-spec init_validation_state(#'OTPCertificate'{}, integer(), list()) ->
#path_validation_state{}.
%%
-%% Description: Creates inital version of path_validation_state for
+%% Description: Creates initial version of path_validation_state for
%% basic path validation of x509 certificates.
%%--------------------------------------------------------------------
init_validation_state(#'OTPCertificate'{} = OtpCert, DefaultPathLen,
@@ -134,7 +134,7 @@ prepare_for_next_cert(OtpCert, ValidationState = #path_validation_state{
}.
%%--------------------------------------------------------------------
--spec validate_time(#'OTPCertificate'{}, term(), fun()) -> term().
+-spec validate_time(#'OTPCertificate'{}, term(), fun()) -> term() | no_return().
%%
%% Description: Check that the certificate validity period includes the
%% current time.
@@ -144,8 +144,8 @@ validate_time(OtpCert, UserState, VerifyFun) ->
{'Validity', NotBeforeStr, NotAfterStr}
= TBSCert#'OTPTBSCertificate'.validity,
Now = calendar:datetime_to_gregorian_seconds(calendar:universal_time()),
- NotBefore = time_str_2_gregorian_sec(NotBeforeStr),
- NotAfter = time_str_2_gregorian_sec(NotAfterStr),
+ NotBefore = time_str_2_gregorian_sec(notBefore, NotBeforeStr),
+ NotAfter = time_str_2_gregorian_sec(notAfter, NotAfterStr),
case ((NotBefore =< Now) and (Now =< NotAfter)) of
true ->
@@ -154,7 +154,7 @@ validate_time(OtpCert, UserState, VerifyFun) ->
verify_fun(OtpCert, {bad_cert, cert_expired}, UserState, VerifyFun)
end.
%%--------------------------------------------------------------------
--spec validate_issuer(#'OTPCertificate'{}, term(), term(), fun()) -> term().
+-spec validate_issuer(#'OTPCertificate'{}, term(), term(), fun()) -> term() | no_return().
%%
%% Description: Check that the certificate issuer name is the working_issuer_name
%% in path_validation_state.
@@ -169,7 +169,7 @@ validate_issuer(OtpCert, Issuer, UserState, VerifyFun) ->
end.
%%--------------------------------------------------------------------
-spec validate_signature(#'OTPCertificate'{}, DER::binary(),
- term(),term(), term(), fun()) -> term().
+ term(),term(), term(), fun()) -> term() | no_return().
%%
%% Description: Check that the signature on the certificate can be verified using
@@ -187,7 +187,7 @@ validate_signature(OtpCert, DerCert, Key, KeyParams,
end.
%%--------------------------------------------------------------------
-spec validate_names(#'OTPCertificate'{}, no_constraints | list(), list(),
- term(), term(), fun())-> term().
+ term(), term(), fun())-> term() | no_return().
%%
%% Description: Validate Subject Alternative Name.
%%--------------------------------------------------------------------
@@ -247,7 +247,7 @@ validate_extensions(OtpCert, ValidationState, UserState, VerifyFun) ->
-spec normalize_general_name({rdnSequence, term()}| binary()) -> {rdnSequence, term()}.
%%
%% Description: Normalizes a general name so that it can be easily
-%% compared to another genral name.
+%% compared to another general name.
%%--------------------------------------------------------------------
normalize_general_name({rdnSequence, Issuer}) ->
NormIssuer = do_normalize_general_name(Issuer),
@@ -333,7 +333,7 @@ is_fixed_dh_cert(#'OTPCertificate'{tbsCertificate =
%%--------------------------------------------------------------------
-spec verify_fun(#'OTPCertificate'{}, {bad_cert, atom()} | {extension, #'Extension'{}}|
- valid | valid_peer, term(), fun()) -> term().
+ valid | valid_peer, term(), fun()) -> term() | no_return().
%%
%% Description: Gives the user application the opportunity handle path
%% validation errors and unknown extensions and optional do other
@@ -370,6 +370,9 @@ select_extension(_, asn1_NOVALUE) ->
undefined;
select_extension(_, []) ->
undefined;
+select_extension(Id, [#'Extension'{extnID = ?'id-ce-cRLDistributionPoints' = Id,
+ extnValue = Value} = Extension | _]) when is_binary(Value) ->
+ Extension#'Extension'{extnValue = public_key:der_decode('CRLDistributionPoints', Value)};
select_extension(Id, [#'Extension'{extnID = Id} = Extension | _]) ->
Extension;
select_extension(Id, [_ | Extensions]) ->
@@ -633,19 +636,44 @@ public_key_info(PublicKeyInfo,
end,
{Algorithm, PublicKey, NewPublicKeyParams}.
-time_str_2_gregorian_sec({utcTime, [Y1,Y2,M1,M2,D1,D2,H1,H2,M3,M4,S1,S2,Z]}) ->
- case list_to_integer([Y1,Y2]) of
- N when N >= 50 ->
- time_str_2_gregorian_sec({generalTime,
- [$1,$9,Y1,Y2,M1,M2,D1,D2,
- H1,H2,M3,M4,S1,S2,Z]});
- _ ->
- time_str_2_gregorian_sec({generalTime,
- [$2,$0,Y1,Y2,M1,M2,D1,D2,
- H1,H2,M3,M4,S1,S2,Z]})
- end;
-
-time_str_2_gregorian_sec({_,[Y1,Y2,Y3,Y4,M1,M2,D1,D2,H1,H2,M3,M4,S1,S2,$Z]}) ->
+%% time_str_2_gregorian_sec/2 is a wrapper (decorator pattern) over
+%% time_str_2_gregorian_sec/1. the decorator deals with notBefore and notAfter
+%% property differently when we pass utcTime because the data format is
+%% ambiguous YYMMDD. on generalTime the year ambiguity cannot happen because
+%% years are expressed in a 4-digit format, i.e., YYYYMMDD.
+-spec time_str_2_gregorian_sec(PeriodOfTime, Time) -> Seconds :: non_neg_integer() when
+ PeriodOfTime :: notBefore | notAfter,
+ Time :: {utcTime | generalTime, [non_neg_integer() | char()]}.
+time_str_2_gregorian_sec(notBefore, {utcTime, [FirstDigitYear | _]=UtcTime}) ->
+ %% To be compliant with PKITS Certification Path Validation,
+ %% we must accept certificates with notBefore = 50, meaning 1950.
+ %% Once the PKITS certification path validation is updated,
+ %% we must update this function body and test case
+ %% {"4.2.3", "Valid pre2000 UTC notBefore Date Test3 EE"}
+ %% in pkits_SUITE.erl
+ Y1 = erlang:list_to_integer([FirstDigitYear]),
+ YearPrefix = case (Y1 > 4 andalso Y1 =< 9) of
+ true -> [$1, $9];
+ false ->
+ {Y, _M, _D} = erlang:date(),
+ integer_to_list(Y div 100)
+ end,
+ time_str_2_gregorian_sec({generalTime, YearPrefix ++ UtcTime});
+
+time_str_2_gregorian_sec(notAfter, {utcTime, UtcTime}) ->
+ SlidingDate = sliding_year_window(UtcTime),
+ time_str_2_gregorian_sec({generalTime, SlidingDate});
+
+time_str_2_gregorian_sec(_, {generalTime, _Time}=GeneralTime) ->
+ time_str_2_gregorian_sec(GeneralTime).
+
+%% converts 'Time' as a string into gregorian time in seconds.
+-spec time_str_2_gregorian_sec(Time) -> Seconds :: non_neg_integer() when
+ Time :: {generalTime | utcTime, string()}.
+time_str_2_gregorian_sec({utcTime, UtcTime}) ->
+ time_str_2_gregorian_sec(notAfter, {utcTime, UtcTime});
+
+time_str_2_gregorian_sec({generalTime,[Y1,Y2,Y3,Y4,M1,M2,D1,D2,H1,H2,M3,M4,S1,S2,$Z]}) ->
Year = list_to_integer([Y1, Y2, Y3, Y4]),
Month = list_to_integer([M1, M2]),
Day = list_to_integer([D1, D2]),
@@ -655,6 +683,28 @@ time_str_2_gregorian_sec({_,[Y1,Y2,Y3,Y4,M1,M2,D1,D2,H1,H2,M3,M4,S1,S2,$Z]}) ->
calendar:datetime_to_gregorian_seconds({{Year, Month, Day},
{Hour, Min, Sec}}).
+%% Sliding window algorithm to calculate the time.
+%% The value is set as taking {Y1, Y2} from the first two digits of
+%% current_date - 50 or current_date - 49.
+sliding_year_window([Y1,Y2,M1,M2,D1,D2,H1,H2,M3,M4,S1,S2,Z]) ->
+ {{CurrentYear,_, _}, _} = calendar:universal_time(),
+ LastTwoDigitYear = CurrentYear rem 100,
+ MinYear = mod(LastTwoDigitYear - 50, 100),
+ YearWindow = case list_to_integer([Y1,Y2]) of
+ N when N < MinYear -> CurrentYear + 50;
+ N when N >= MinYear -> CurrentYear - 49
+ end,
+ [Year1, Year2] = integer_to_list(YearWindow div 100),
+ [Year1,Year2,Y1,Y2,M1,M2,D1,D2,H1,H2,M3,M4,S1,S2,Z].
+
+
+%% Helper function to perform modulo calculation for integer
+-spec mod(A :: integer(), B :: non_neg_integer()) -> non_neg_integer().
+mod(A, B) when A > 0 -> A rem B;
+mod(A, B) when A < 0 -> mod(A+B, B);
+mod(0, _) -> 0.
+
+
is_dir_name([], [], _Exact) -> true;
is_dir_name([H|R1],[H|R2], Exact) -> is_dir_name(R1,R2, Exact);
is_dir_name([[{'AttributeTypeAndValue', Type, What1}]|Rest1],
@@ -1027,6 +1077,8 @@ is_permitted_ip([CandidatIp | CandidatIpRest],
mask_cmp(Canditate, Permitted, Mask) ->
(Canditate band Mask) == Permitted.
+is_valid_host_or_domain([], _) ->
+ false; %% Can happen if URI was not a HTTP URI
is_valid_host_or_domain(Canditate, [$.|_] = Permitted) ->
is_suffix(Permitted, Canditate);
is_valid_host_or_domain(Canditate, Permitted) ->
@@ -1301,7 +1353,7 @@ cert_chain(Role, IssuerCert, IssuerKey, [PeerOpts], _, Acc) ->
cert_chain(Role, IssuerCert, IssuerKey, [CAOpts | Rest], N, Acc) ->
Key = gen_key(proplists:get_value(key, CAOpts, default_key_gen())),
Cert = cert(Role, public_key:pkix_decode_cert(IssuerCert, otp), IssuerKey, Key, "webadmin",
- " Intermidiate CA " ++ integer_to_list(N), CAOpts, ca),
+ " Intermediate CA " ++ integer_to_list(N), CAOpts, ca),
cert_chain(Role, Cert, Key, Rest, N+1, [{IssuerCert, encode_key(IssuerKey)} | Acc]).
cert(Role, #'OTPCertificate'{tbsCertificate = #'OTPTBSCertificate'{subject = Issuer}},