Skip to content

Commit 7477b7c

Browse files
committed
Merge branch 'maint'
* maint: public_key: Fix encoding and decoding extensions
2 parents 215ac90 + bb462d3 commit 7477b7c

File tree

3 files changed

+157
-81
lines changed

3 files changed

+157
-81
lines changed

lib/public_key/src/pubkey_cert_records.erl

Lines changed: 55 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -365,34 +365,58 @@ encode_supportedPublicKey(#'OTPSubjectPublicKeyInfo'{
365365

366366
%%% Extensions
367367

368-
extension_id(?'id-ce-authorityKeyIdentifier') -> 'AuthorityKeyIdentifier';
369-
extension_id(?'id-ce-subjectKeyIdentifier') -> 'SubjectKeyIdentifier';
370-
extension_id(?'id-ce-keyUsage') -> 'KeyUsage';
371-
extension_id(?'id-ce-privateKeyUsagePeriod') -> 'PrivateKeyUsagePeriod';
372-
extension_id(?'id-ce-certificatePolicies') -> 'CertificatePolicies';
373-
extension_id(?'id-ce-policyMappings') -> 'PolicyMappings';
374-
extension_id(?'id-ce-subjectAltName') -> 'SubjectAltName';
375-
extension_id(?'id-ce-issuerAltName') -> 'IssuerAltName';
376-
extension_id(?'id-ce-subjectDirectoryAttributes') -> 'SubjectDirectoryAttributes';
377-
extension_id(?'id-ce-basicConstraints' ) -> 'BasicConstraints';
378-
extension_id(?'id-ce-nameConstraints') -> 'NameConstraints';
379-
extension_id(?'id-ce-policyConstraints') -> 'PolicyConstraints';
380-
extension_id(?'id-ce-extKeyUsage') -> 'ExtKeyUsageSyntax';
381-
extension_id(?'id-ce-inhibitAnyPolicy') -> 'InhibitAnyPolicy';
382-
extension_id(?'id-ce-freshestCRL') -> 'FreshestCRL';
383-
extension_id(?'id-ce-issuingDistributionPoint') -> 'IssuingDistributionPoint';
384-
%% Missing in public_key doc
385-
extension_id(?'id-pe-authorityInfoAccess') -> 'AuthorityInfoAccessSyntax';
386-
extension_id(?'id-pe-subjectInfoAccess') -> 'SubjectInfoAccessSyntax';
387-
extension_id(?'id-ce-cRLNumber') -> 'CRLNumber';
388-
extension_id(?'id-ce-deltaCRLIndicator') -> 'BaseCRLNumber';
389-
extension_id(?'id-ce-cRLReasons') -> 'CRLReason';
390-
extension_id(?'id-ce-certificateIssuer') -> 'CertificateIssuer';
391-
extension_id(?'id-ce-holdInstructionCode') -> 'HoldInstructionCode';
392-
extension_id(?'id-ce-invalidityDate') -> 'InvalidityDate';
393-
extension_id(?'id-ce-cRLDistributionPoints') -> 'CRLDistributionPoints';
368+
extension_id(?'id-ce-authorityKeyIdentifier') ->
369+
{'PKIX1Implicit-2009', getdec_CrlExtensions, getenc_CrlExtensions, 'AuthorityKeyIdentifier'};
370+
extension_id(?'id-ce-subjectKeyIdentifier') ->
371+
{'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'SubjectKeyIdentifier'};
372+
extension_id(?'id-ce-keyUsage') ->
373+
{'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'KeyUsage'};
374+
extension_id(?'id-ce-privateKeyUsagePeriod') ->
375+
{'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'PrivateKeyUsagePeriod'};
376+
extension_id(?'id-ce-certificatePolicies') ->
377+
{'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'CertificatePolicies'};
378+
extension_id(?'id-ce-policyMappings') ->
379+
{'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'PolicyMappings'};
380+
extension_id(?'id-ce-subjectAltName') ->
381+
{'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'SubjectAltName'};
382+
extension_id(?'id-ce-issuerAltName') ->
383+
{'PKIX1Implicit-2009', getdec_CrlExtensions, getenc_CrlExtensions, 'IssuerAltName'};
384+
extension_id(?'id-ce-subjectDirectoryAttributes') ->
385+
{'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'SubjectDirectoryAttributes'};
386+
extension_id(?'id-ce-basicConstraints' ) ->
387+
{'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'BasicConstraints'};
388+
extension_id(?'id-ce-nameConstraints') ->
389+
{'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'NameConstraints'};
390+
extension_id(?'id-ce-policyConstraints') ->
391+
{'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'PolicyConstraints'};
392+
extension_id(?'id-ce-extKeyUsage') ->
393+
{'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'ExtKeyUsageSyntax'};
394+
extension_id(?'id-ce-inhibitAnyPolicy') ->
395+
{'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'InhibitAnyPolicy'};
396+
extension_id(?'id-ce-freshestCRL') ->
397+
{'PKIX1Implicit-2009', getdec_CrlExtensions, getenc_CrlExtensions, 'FreshestCRL'};
398+
extension_id(?'id-ce-issuingDistributionPoint') ->
399+
{'PKIX1Implicit-2009', getdec_CrlExtensions, getenc_CrlExtensions, 'IssuingDistributionPoint'};
400+
extension_id(?'id-pe-authorityInfoAccess') ->
401+
{'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'AuthorityInfoAccessSyntax'};
402+
extension_id(?'id-pe-subjectInfoAccess') ->
403+
{'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'SubjectInfoAccessSyntax'};
404+
extension_id(?'id-ce-cRLNumber') ->
405+
{'PKIX1Implicit-2009', getdec_CrlExtensions, getenc_CrlExtensions, 'CRLNumber'};
406+
extension_id(?'id-ce-deltaCRLIndicator') ->
407+
{'PKIX1Implicit-2009', getdec_CrlExtensions, getenc_CrlExtensions, 'BaseCRLNumber'};
408+
extension_id(?'id-ce-cRLReasons') ->
409+
{'PKIX1Implicit-2009', getdec_CrlEntryExtensions, getenc_CrlEntryExtensions, 'CRLReason'};
410+
extension_id(?'id-ce-certificateIssuer') ->
411+
{'PKIX1Implicit-2009', getdec_CrlEntryExtensions, getenc_CrlEntryExtensions, 'CertificateIssuer'};
412+
extension_id(?'id-ce-holdInstructionCode') ->
413+
{'PKIX1Implicit-2009', getdec_CrlEntryExtensions, getenc_CrlEntryExtensions, 'HoldInstructionCode'};
414+
extension_id(?'id-ce-invalidityDate') ->
415+
{'PKIX1Implicit-2009', getdec_CrlEntryExtensions, getenc_CrlEntryExtensions, 'InvalidityDate'};
416+
extension_id(?'id-ce-cRLDistributionPoints') ->
417+
{'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'CRLDistributionPoints'};
394418
extension_id(_) ->
395-
undefined.
419+
{undefined, undefined, undefined, undefined}.
396420

397421
ext_oid('AuthorityKeyIdentifier') -> ?'id-ce-authorityKeyIdentifier';
398422
ext_oid('SubjectKeyIdentifier') -> ?'id-ce-subjectKeyIdentifier';
@@ -432,10 +456,8 @@ decode_extensions(Exts, WhenCRL) ->
432456
lists:map(fun(Ext = #'Extension'{extnID=Id, extnValue=Value0}) ->
433457
%% Some Extensions only has special decoding functions
434458
%% with other naming-convention
435-
ExtId = extension_id(Id),
436-
case ExtId =/= undefined andalso
437-
'PKIX1Implicit-2009':getdec_CertExtensions(Id)
438-
of
459+
{Mod, DecLookup, _Enc, ExtId} = extension_id(Id),
460+
case ExtId =/= undefined andalso Mod:DecLookup(Id) of
439461
false ->
440462
Ext;
441463
DecodeExt when ExtId =:= 'CertificatePolicies',
@@ -486,10 +508,8 @@ encode_extensions(Exts) ->
486508
%% Some Extensions only has special decoding functions
487509
%% with other naming-convention
488510
lists:map(fun(Ext = #'Extension'{extnID=Id, extnValue=Value0}) ->
489-
ExtId = extension_id(Id),
490-
case ExtId =/= undefined andalso
491-
'PKIX1Implicit-2009':getenc_CertExtensions(Id)
492-
of
511+
{Mod, _Dec, EncLookup, ExtId} = extension_id(Id),
512+
case ExtId =/= undefined andalso Mod:EncLookup(Id) of
493513
false ->
494514
Ext;
495515
EncodeExt when is_function(EncodeExt, 3) ->

lib/public_key/src/public_key.erl

Lines changed: 39 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -608,28 +608,25 @@ der_decode('ExtensionRequest', Value) ->
608608
der_decode('AttributeTypeAndValue', Der) ->
609609
{ok, Decoded} = 'PKIX1Explicit-2009':decode('SingleAttribute', Der),
610610
pubkey_cert_records:transform(Decoded, decode);
611-
der_decode(Asn1ExtType, Der) when Asn1ExtType == 'SubjectAltName';
612-
Asn1ExtType == 'IssuerAltName';
613-
Asn1ExtType == 'ExtKeyUsage';
614-
Asn1ExtType == 'InhibitAnyPolicy';
615-
Asn1ExtType == 'FreshestCRL';
616-
Asn1ExtType == 'AuthorityInfoAccess';
617-
Asn1ExtType == 'DeltaCRLIndicator';
618-
Asn1ExtType == 'CertificateIssuer';
619-
Asn1ExtType == 'HoldInstructionCode';
620-
Asn1ExtType == 'InvalidityDate' ->
621-
Oid = pubkey_cert_records:ext_oid(Asn1ExtType),
622-
[#'Extension'{extnValue = Value}]
623-
= pubkey_cert_records:decode_extensions([#'Extension'{extnID = Oid, extnValue = Der}]),
624-
Value;
625611
der_decode(Asn1Type, Der) when is_atom(Asn1Type), is_binary(Der) ->
626-
Asn1Module = get_asn1_module(Asn1Type),
627-
try
628-
{ok, Decoded} = Asn1Module:decode(Asn1Type, Der),
629-
pubkey_translation:decode(Decoded)
630-
catch
631-
error:{badmatch, {error, _}} = Error ->
632-
erlang:error(Error)
612+
case get_asn1_module(Asn1Type) of
613+
undefined ->
614+
case pubkey_cert_records:ext_oid(Asn1Type) of
615+
undefined ->
616+
error({badarg, {unknown_type, Asn1Type}});
617+
ExtOid ->
618+
Ext = #'Extension'{extnID = ExtOid, extnValue = Der},
619+
[Res] = pubkey_cert_records:decode_extensions([Ext]),
620+
Res#'Extension'.extnValue
621+
end;
622+
Asn1Module when is_atom(Asn1Module) ->
623+
try
624+
{ok, Decoded} = Asn1Module:decode(Asn1Type, Der),
625+
pubkey_translation:decode(Decoded)
626+
catch
627+
error:{badmatch, {error, _}} = Error ->
628+
erlang:error(Error)
629+
end
633630
end.
634631

635632
%% X509 RFC 5280
@@ -640,11 +637,9 @@ get_asn1_module('ExtKeyUsageSyntax') -> 'PKIX1Implicit-2009';
640637
get_asn1_module('KeyUsage') -> 'PKIX1Implicit-2009';
641638
get_asn1_module('Certificate') -> 'PKIX1Explicit-2009';
642639
get_asn1_module('TBSCertificate') -> 'PKIX1Explicit-2009';
643-
get_asn1_module('SubjectAltName') -> 'PKIX1Implicit-2009';
644640
get_asn1_module('CRLDistributionPoints') -> 'PKIX1Implicit-2009';
645641
get_asn1_module('CRLReason') -> 'PKIX1Implicit-2009';
646642
get_asn1_module('CRLNumber') -> 'PKIX1Implicit-2009';
647-
get_asn1_module('FreshestCRL') -> 'PKIX1Implicit-2009';
648643
get_asn1_module('IssuingDistributionPoint') -> 'PKIX1Implicit-2009';
649644
get_asn1_module('GeneralNames') -> 'PKIX1Implicit-2009';
650645
get_asn1_module('SubjectPublicKeyInfo') -> 'PKIX1Explicit-2009';
@@ -710,7 +705,8 @@ get_asn1_module('PBMParameter') -> 'PKIXCMP-2023';
710705
get_asn1_module('ProtectedPart') -> 'PKIXCMP-2023';
711706
%% PKIXCRMF
712707
get_asn1_module('OldCertId') -> 'PKIXCRMF-2009';
713-
get_asn1_module('CertRequest') -> 'PKIXCRMF-2009'.
708+
get_asn1_module('CertRequest') -> 'PKIXCRMF-2009';
709+
get_asn1_module(_) -> undefined.
714710

715711

716712
handle_pkcs_frame_error('PrivateKeyInfo', Der, _) ->
@@ -963,29 +959,26 @@ der_encode('AttributeTypeAndValue', Value) ->
963959
Term = pubkey_cert_records:transform(Value, encode),
964960
{ok, Encoded} = 'PKIX1Explicit-2009':encode('SingleAttribute', Term),
965961
Encoded;
966-
der_encode(Asn1ExtType, Value) when Asn1ExtType == 'SubjectAltName';
967-
Asn1ExtType == 'IssuerAltName';
968-
Asn1ExtType == 'ExtKeyUsage';
969-
Asn1ExtType == 'InhibitAnyPolicy';
970-
Asn1ExtType == 'FreshestCRL';
971-
Asn1ExtType == 'AuthorityInfoAccess';
972-
Asn1ExtType == 'DeltaCRLIndicator';
973-
Asn1ExtType == 'CertificateIssuer';
974-
Asn1ExtType == 'HoldInstructionCode';
975-
Asn1ExtType == 'InvalidityDate' ->
976-
Oid = pubkey_cert_records:ext_oid(Asn1ExtType),
977-
[#'Extension'{extnValue = Encoded}] =
978-
pubkey_cert_records:encode_extensions([#'Extension'{extnID = Oid, extnValue = Value}]),
979-
Encoded;
980962
der_encode(Asn1Type, Entity0) when is_atom(Asn1Type) ->
981-
Asn1Module = get_asn1_module(Asn1Type),
982-
try
983-
Entity = pubkey_translation:encode(Entity0),
984-
{ok, Encoded} = Asn1Module:encode(Asn1Type, Entity),
985-
Encoded
986-
catch
987-
error:{badmatch, {error, _}} = Error ->
988-
erlang:error(Error)
963+
case get_asn1_module(Asn1Type) of
964+
undefined ->
965+
case pubkey_cert_records:ext_oid(Asn1Type) of
966+
undefined ->
967+
error({badarg, {unknown_type, Asn1Type}});
968+
ExtOid ->
969+
Ext = #'Extension'{extnID = ExtOid, extnValue = Entity0},
970+
[Res] = pubkey_cert_records:encode_extensions([Ext]),
971+
Res#'Extension'.extnValue
972+
end;
973+
Asn1Module when is_atom(Asn1Module) ->
974+
try
975+
Entity = pubkey_translation:encode(Entity0),
976+
{ok, Encoded} = Asn1Module:encode(Asn1Type, Entity),
977+
Encoded
978+
catch
979+
error:{badmatch, {error, _}} = Error ->
980+
erlang:error(Error)
981+
end
989982
end.
990983

991984
%%--------------------------------------------------------------------

lib/public_key/test/public_key_SUITE.erl

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
encrypted_pem_pwdstring/1,
7878
encrypted_pem_pwdfun/0,
7979
encrypted_pem_pwdfun/1,
80+
ext_encoding/1,
8081
dh_pem/0,
8182
dh_pem/1,
8283
pkcs10_pem/0,
@@ -193,6 +194,7 @@ all() ->
193194
{group, pem_decode_encode},
194195
encrypt_decrypt,
195196
encrypt_decrypt_sign_fun,
197+
ext_encoding,
196198
{group, sign_verify},
197199
pkix,
198200
pkix_cmp,
@@ -780,6 +782,67 @@ cert_pem(Config) when is_list(Config) ->
780782
asn1_encode_decode(Entry1),
781783
asn1_encode_decode(Entry2).
782784

785+
%%--------------------------------------------------------------------
786+
ext_encoding(_Config) ->
787+
Exts = [
788+
{'AuthorityKeyIdentifier',
789+
#'AuthorityKeyIdentifier'{authorityCertIssuer = [{rfc822Name, "a name"}]}},
790+
{'SubjectKeyIdentifier', "Octet String"},
791+
{'KeyUsage', [keyEncipherment, encipherOnly]},
792+
{'PrivateKeyUsagePeriod', #'PrivateKeyUsagePeriod'{notAfter = "20220127000000Z"}},
793+
{'CertificatePolicies', [#'PolicyInformation'{policyIdentifier = ?'anyPolicy'}]},
794+
{'PolicyMappings', [#'PolicyMappings_SEQOF'{issuerDomainPolicy = ?'anyPolicy',
795+
subjectDomainPolicy = ?'anyPolicy'}]},
796+
{'SubjectAltName', [{rfc822Name, "a name"}]},
797+
{'IssuerAltName', [{rfc822Name, "a name"}]},
798+
{'SubjectDirectoryAttributes', [#'AttributeSet'{type = ?'id-at-name',
799+
values = [{utf8String, ~"a string"}]}]},
800+
{'BasicConstraints', #'BasicConstraints'{cA = true}},
801+
{'NameConstraints', #'NameConstraints'{}},
802+
{'PolicyConstraints', #'PolicyConstraints'{requireExplicitPolicy = 5}},
803+
{'ExtKeyUsageSyntax', [?anyPolicy]},
804+
{'InhibitAnyPolicy', 42},
805+
{'FreshestCRL', [#'DistributionPoint'{}]},
806+
{'IssuingDistributionPoint',
807+
#'IssuingDistributionPoint'{indirectCRL = true,
808+
onlyContainsUserCerts = false,
809+
onlyContainsCACerts = false,
810+
onlyContainsAttributeCerts = false
811+
}},
812+
{'AuthorityInfoAccessSyntax',
813+
[#'AccessDescription'{accessMethod = ?'id-at-name',
814+
accessLocation = {rfc822Name, "a name"}}]},
815+
{'SubjectInfoAccessSyntax',
816+
[#'AccessDescription'{accessMethod = ?'id-at-name',
817+
accessLocation = {rfc822Name, "a name"}}]},
818+
{'CRLNumber', 4711},
819+
{'BaseCRLNumber', 4710},
820+
{'CRLReason', 'cessationOfOperation'},
821+
{'CertificateIssuer', [{rfc822Name, "a name"}]},
822+
{'HoldInstructionCode', ?'id-holdinstruction-callissuer'},
823+
{'InvalidityDate', "20220127000000Z"},
824+
{'CRLDistributionPoints', [#'DistributionPoint'{}]}
825+
],
826+
Check = fun({Ext, Value}) ->
827+
try
828+
Der = public_key:der_encode(Ext, Value),
829+
true = is_binary(Der),
830+
case public_key:der_decode(Ext, Der) of
831+
Value -> false;
832+
Bin when is_binary(Bin) ->
833+
Value =/= binary_to_list(Bin)
834+
end
835+
catch Err:Reason:St ->
836+
{true, {fail, Ext, Value, Err, Reason, St}}
837+
end
838+
end,
839+
[] = lists:filtermap(Check, Exts),
840+
841+
Badarg = {badarg, {unknown_type, type_do_not_exist}},
842+
{'EXIT', {Badarg,_}} = (catch public_key:der_encode(type_do_not_exist, 4711)),
843+
{'EXIT', {Badarg,_}} = (catch public_key:der_decode(type_do_not_exist, <<47,11>>)),
844+
ok.
845+
783846
%%--------------------------------------------------------------------
784847
encrypt_decrypt() ->
785848
[{doc, "Test public_key:encrypt_private and public_key:decrypt_public"}].

0 commit comments

Comments
 (0)