diff --git a/lib/public_key/src/pubkey_cert_records.erl b/lib/public_key/src/pubkey_cert_records.erl index 4bc174e39db9..b82d47da740e 100644 --- a/lib/public_key/src/pubkey_cert_records.erl +++ b/lib/public_key/src/pubkey_cert_records.erl @@ -352,34 +352,58 @@ encode_supportedPublicKey(#'OTPSubjectPublicKeyInfo'{ %%% Extensions -extension_id(?'id-ce-authorityKeyIdentifier') -> 'AuthorityKeyIdentifier'; -extension_id(?'id-ce-subjectKeyIdentifier') -> 'SubjectKeyIdentifier'; -extension_id(?'id-ce-keyUsage') -> 'KeyUsage'; -extension_id(?'id-ce-privateKeyUsagePeriod') -> 'PrivateKeyUsagePeriod'; -extension_id(?'id-ce-certificatePolicies') -> 'CertificatePolicies'; -extension_id(?'id-ce-policyMappings') -> 'PolicyMappings'; -extension_id(?'id-ce-subjectAltName') -> 'SubjectAltName'; -extension_id(?'id-ce-issuerAltName') -> 'IssuerAltName'; -extension_id(?'id-ce-subjectDirectoryAttributes') -> 'SubjectDirectoryAttributes'; -extension_id(?'id-ce-basicConstraints' ) -> 'BasicConstraints'; -extension_id(?'id-ce-nameConstraints') -> 'NameConstraints'; -extension_id(?'id-ce-policyConstraints') -> 'PolicyConstraints'; -extension_id(?'id-ce-extKeyUsage') -> 'ExtKeyUsageSyntax'; -extension_id(?'id-ce-inhibitAnyPolicy') -> 'InhibitAnyPolicy'; -extension_id(?'id-ce-freshestCRL') -> 'FreshestCRL'; -extension_id(?'id-ce-issuingDistributionPoint') -> 'IssuingDistributionPoint'; -%% Missing in public_key doc -extension_id(?'id-pe-authorityInfoAccess') -> 'AuthorityInfoAccessSyntax'; -extension_id(?'id-pe-subjectInfoAccess') -> 'SubjectInfoAccessSyntax'; -extension_id(?'id-ce-cRLNumber') -> 'CRLNumber'; -extension_id(?'id-ce-deltaCRLIndicator') -> 'BaseCRLNumber'; -extension_id(?'id-ce-cRLReasons') -> 'CRLReason'; -extension_id(?'id-ce-certificateIssuer') -> 'CertificateIssuer'; -extension_id(?'id-ce-holdInstructionCode') -> 'HoldInstructionCode'; -extension_id(?'id-ce-invalidityDate') -> 'InvalidityDate'; -extension_id(?'id-ce-cRLDistributionPoints') -> 'CRLDistributionPoints'; +extension_id(?'id-ce-authorityKeyIdentifier') -> + {'PKIX1Implicit-2009', getdec_CrlExtensions, getenc_CrlExtensions, 'AuthorityKeyIdentifier'}; +extension_id(?'id-ce-subjectKeyIdentifier') -> + {'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'SubjectKeyIdentifier'}; +extension_id(?'id-ce-keyUsage') -> + {'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'KeyUsage'}; +extension_id(?'id-ce-privateKeyUsagePeriod') -> + {'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'PrivateKeyUsagePeriod'}; +extension_id(?'id-ce-certificatePolicies') -> + {'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'CertificatePolicies'}; +extension_id(?'id-ce-policyMappings') -> + {'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'PolicyMappings'}; +extension_id(?'id-ce-subjectAltName') -> + {'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'SubjectAltName'}; +extension_id(?'id-ce-issuerAltName') -> + {'PKIX1Implicit-2009', getdec_CrlExtensions, getenc_CrlExtensions, 'IssuerAltName'}; +extension_id(?'id-ce-subjectDirectoryAttributes') -> + {'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'SubjectDirectoryAttributes'}; +extension_id(?'id-ce-basicConstraints' ) -> + {'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'BasicConstraints'}; +extension_id(?'id-ce-nameConstraints') -> + {'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'NameConstraints'}; +extension_id(?'id-ce-policyConstraints') -> + {'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'PolicyConstraints'}; +extension_id(?'id-ce-extKeyUsage') -> + {'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'ExtKeyUsageSyntax'}; +extension_id(?'id-ce-inhibitAnyPolicy') -> + {'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'InhibitAnyPolicy'}; +extension_id(?'id-ce-freshestCRL') -> + {'PKIX1Implicit-2009', getdec_CrlExtensions, getenc_CrlExtensions, 'FreshestCRL'}; +extension_id(?'id-ce-issuingDistributionPoint') -> + {'PKIX1Implicit-2009', getdec_CrlExtensions, getenc_CrlExtensions, 'IssuingDistributionPoint'}; +extension_id(?'id-pe-authorityInfoAccess') -> + {'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'AuthorityInfoAccessSyntax'}; +extension_id(?'id-pe-subjectInfoAccess') -> + {'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'SubjectInfoAccessSyntax'}; +extension_id(?'id-ce-cRLNumber') -> + {'PKIX1Implicit-2009', getdec_CrlExtensions, getenc_CrlExtensions, 'CRLNumber'}; +extension_id(?'id-ce-deltaCRLIndicator') -> + {'PKIX1Implicit-2009', getdec_CrlExtensions, getenc_CrlExtensions, 'BaseCRLNumber'}; +extension_id(?'id-ce-cRLReasons') -> + {'PKIX1Implicit-2009', getdec_CrlEntryExtensions, getenc_CrlEntryExtensions, 'CRLReason'}; +extension_id(?'id-ce-certificateIssuer') -> + {'PKIX1Implicit-2009', getdec_CrlEntryExtensions, getenc_CrlEntryExtensions, 'CertificateIssuer'}; +extension_id(?'id-ce-holdInstructionCode') -> + {'PKIX1Implicit-2009', getdec_CrlEntryExtensions, getenc_CrlEntryExtensions, 'HoldInstructionCode'}; +extension_id(?'id-ce-invalidityDate') -> + {'PKIX1Implicit-2009', getdec_CrlEntryExtensions, getenc_CrlEntryExtensions, 'InvalidityDate'}; +extension_id(?'id-ce-cRLDistributionPoints') -> + {'PKIX1Implicit-2009', getdec_CertExtensions, getenc_CertExtensions, 'CRLDistributionPoints'}; extension_id(_) -> - undefined. + {undefined, undefined, undefined, undefined}. ext_oid('AuthorityKeyIdentifier') -> ?'id-ce-authorityKeyIdentifier'; ext_oid('SubjectKeyIdentifier') -> ?'id-ce-subjectKeyIdentifier'; @@ -419,10 +443,8 @@ decode_extensions(Exts, WhenCRL) -> lists:map(fun(Ext = #'Extension'{extnID=Id, extnValue=Value0}) -> %% Some Extensions only has special decoding functions %% with other naming-convention - ExtId = extension_id(Id), - case ExtId =/= undefined andalso - 'PKIX1Implicit-2009':getdec_CertExtensions(Id) - of + {Mod, DecLookup, _Enc, ExtId} = extension_id(Id), + case ExtId =/= undefined andalso Mod:DecLookup(Id) of false -> Ext; DecodeExt when ExtId =:= 'CertificatePolicies', @@ -473,10 +495,8 @@ encode_extensions(Exts) -> %% Some Extensions only has special decoding functions %% with other naming-convention lists:map(fun(Ext = #'Extension'{extnID=Id, extnValue=Value0}) -> - ExtId = extension_id(Id), - case ExtId =/= undefined andalso - 'PKIX1Implicit-2009':getenc_CertExtensions(Id) - of + {Mod, _Dec, EncLookup, ExtId} = extension_id(Id), + case ExtId =/= undefined andalso Mod:EncLookup(Id) of false -> Ext; EncodeExt when is_function(EncodeExt, 3) -> diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index e7ddaa284f53..fa34c0b47309 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -608,28 +608,25 @@ der_decode('ExtensionRequest', Value) -> der_decode('AttributeTypeAndValue', Der) -> {ok, Decoded} = 'PKIX1Explicit-2009':decode('SingleAttribute', Der), pubkey_cert_records:transform(Decoded, decode); -der_decode(Asn1ExtType, Der) when Asn1ExtType == 'SubjectAltName'; - Asn1ExtType == 'IssuerAltName'; - Asn1ExtType == 'ExtKeyUsage'; - Asn1ExtType == 'InhibitAnyPolicy'; - Asn1ExtType == 'FreshestCRL'; - Asn1ExtType == 'AuthorityInfoAccess'; - Asn1ExtType == 'DeltaCRLIndicator'; - Asn1ExtType == 'CertificateIssuer'; - Asn1ExtType == 'HoldInstructionCode'; - Asn1ExtType == 'InvalidityDate' -> - Oid = pubkey_cert_records:ext_oid(Asn1ExtType), - [#'Extension'{extnValue = Value}] - = pubkey_cert_records:decode_extensions([#'Extension'{extnID = Oid, extnValue = Der}]), - Value; der_decode(Asn1Type, Der) when is_atom(Asn1Type), is_binary(Der) -> - Asn1Module = get_asn1_module(Asn1Type), - try - {ok, Decoded} = Asn1Module:decode(Asn1Type, Der), - pubkey_translation:decode(Decoded) - catch - error:{badmatch, {error, _}} = Error -> - erlang:error(Error) + case get_asn1_module(Asn1Type) of + undefined -> + case pubkey_cert_records:ext_oid(Asn1Type) of + undefined -> + error({badarg, {unknown_type, Asn1Type}}); + ExtOid -> + Ext = #'Extension'{extnID = ExtOid, extnValue = Der}, + [Res] = pubkey_cert_records:decode_extensions([Ext]), + Res#'Extension'.extnValue + end; + Asn1Module when is_atom(Asn1Module) -> + try + {ok, Decoded} = Asn1Module:decode(Asn1Type, Der), + pubkey_translation:decode(Decoded) + catch + error:{badmatch, {error, _}} = Error -> + erlang:error(Error) + end end. %% X509 RFC 5280 @@ -640,11 +637,9 @@ get_asn1_module('ExtKeyUsageSyntax') -> 'PKIX1Implicit-2009'; get_asn1_module('KeyUsage') -> 'PKIX1Implicit-2009'; get_asn1_module('Certificate') -> 'PKIX1Explicit-2009'; get_asn1_module('TBSCertificate') -> 'PKIX1Explicit-2009'; -get_asn1_module('SubjectAltName') -> 'PKIX1Implicit-2009'; get_asn1_module('CRLDistributionPoints') -> 'PKIX1Implicit-2009'; get_asn1_module('CRLReason') -> 'PKIX1Implicit-2009'; get_asn1_module('CRLNumber') -> 'PKIX1Implicit-2009'; -get_asn1_module('FreshestCRL') -> 'PKIX1Implicit-2009'; get_asn1_module('IssuingDistributionPoint') -> 'PKIX1Implicit-2009'; get_asn1_module('GeneralNames') -> 'PKIX1Implicit-2009'; get_asn1_module('SubjectPublicKeyInfo') -> 'PKIX1Explicit-2009'; @@ -710,7 +705,8 @@ get_asn1_module('PBMParameter') -> 'PKIXCMP-2023'; get_asn1_module('ProtectedPart') -> 'PKIXCMP-2023'; %% PKIXCRMF get_asn1_module('OldCertId') -> 'PKIXCRMF-2009'; -get_asn1_module('CertRequest') -> 'PKIXCRMF-2009'. +get_asn1_module('CertRequest') -> 'PKIXCRMF-2009'; +get_asn1_module(_) -> undefined. handle_pkcs_frame_error('PrivateKeyInfo', Der, _) -> @@ -963,29 +959,26 @@ der_encode('AttributeTypeAndValue', Value) -> Term = pubkey_cert_records:transform(Value, encode), {ok, Encoded} = 'PKIX1Explicit-2009':encode('SingleAttribute', Term), Encoded; -der_encode(Asn1ExtType, Value) when Asn1ExtType == 'SubjectAltName'; - Asn1ExtType == 'IssuerAltName'; - Asn1ExtType == 'ExtKeyUsage'; - Asn1ExtType == 'InhibitAnyPolicy'; - Asn1ExtType == 'FreshestCRL'; - Asn1ExtType == 'AuthorityInfoAccess'; - Asn1ExtType == 'DeltaCRLIndicator'; - Asn1ExtType == 'CertificateIssuer'; - Asn1ExtType == 'HoldInstructionCode'; - Asn1ExtType == 'InvalidityDate' -> - Oid = pubkey_cert_records:ext_oid(Asn1ExtType), - [#'Extension'{extnValue = Encoded}] = - pubkey_cert_records:encode_extensions([#'Extension'{extnID = Oid, extnValue = Value}]), - Encoded; der_encode(Asn1Type, Entity0) when is_atom(Asn1Type) -> - Asn1Module = get_asn1_module(Asn1Type), - try - Entity = pubkey_translation:encode(Entity0), - {ok, Encoded} = Asn1Module:encode(Asn1Type, Entity), - Encoded - catch - error:{badmatch, {error, _}} = Error -> - erlang:error(Error) + case get_asn1_module(Asn1Type) of + undefined -> + case pubkey_cert_records:ext_oid(Asn1Type) of + undefined -> + error({badarg, {unknown_type, Asn1Type}}); + ExtOid -> + Ext = #'Extension'{extnID = ExtOid, extnValue = Entity0}, + [Res] = pubkey_cert_records:encode_extensions([Ext]), + Res#'Extension'.extnValue + end; + Asn1Module when is_atom(Asn1Module) -> + try + Entity = pubkey_translation:encode(Entity0), + {ok, Encoded} = Asn1Module:encode(Asn1Type, Entity), + Encoded + catch + error:{badmatch, {error, _}} = Error -> + erlang:error(Error) + end end. %%-------------------------------------------------------------------- diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl index 387efd76e289..fd15609a42d5 100644 --- a/lib/public_key/test/public_key_SUITE.erl +++ b/lib/public_key/test/public_key_SUITE.erl @@ -77,6 +77,7 @@ encrypted_pem_pwdstring/1, encrypted_pem_pwdfun/0, encrypted_pem_pwdfun/1, + ext_encoding/1, dh_pem/0, dh_pem/1, pkcs10_pem/0, @@ -191,6 +192,7 @@ all() -> {group, pem_decode_encode}, encrypt_decrypt, encrypt_decrypt_sign_fun, + ext_encoding, {group, sign_verify}, pkix, pkix_cmp, @@ -777,6 +779,67 @@ cert_pem(Config) when is_list(Config) -> asn1_encode_decode(Entry1), asn1_encode_decode(Entry2). +%%-------------------------------------------------------------------- +ext_encoding(_Config) -> + Exts = [ + {'AuthorityKeyIdentifier', + #'AuthorityKeyIdentifier'{authorityCertIssuer = [{rfc822Name, "a name"}]}}, + {'SubjectKeyIdentifier', "Octet String"}, + {'KeyUsage', [keyEncipherment, encipherOnly]}, + {'PrivateKeyUsagePeriod', #'PrivateKeyUsagePeriod'{notAfter = "20220127000000Z"}}, + {'CertificatePolicies', [#'PolicyInformation'{policyIdentifier = ?'anyPolicy'}]}, + {'PolicyMappings', [#'PolicyMappings_SEQOF'{issuerDomainPolicy = ?'anyPolicy', + subjectDomainPolicy = ?'anyPolicy'}]}, + {'SubjectAltName', [{rfc822Name, "a name"}]}, + {'IssuerAltName', [{rfc822Name, "a name"}]}, + {'SubjectDirectoryAttributes', [#'AttributeSet'{type = ?'id-at-name', + values = [{utf8String, ~"a string"}]}]}, + {'BasicConstraints', #'BasicConstraints'{cA = true}}, + {'NameConstraints', #'NameConstraints'{}}, + {'PolicyConstraints', #'PolicyConstraints'{requireExplicitPolicy = 5}}, + {'ExtKeyUsageSyntax', [?anyPolicy]}, + {'InhibitAnyPolicy', 42}, + {'FreshestCRL', [#'DistributionPoint'{}]}, + {'IssuingDistributionPoint', + #'IssuingDistributionPoint'{indirectCRL = true, + onlyContainsUserCerts = false, + onlyContainsCACerts = false, + onlyContainsAttributeCerts = false + }}, + {'AuthorityInfoAccessSyntax', + [#'AccessDescription'{accessMethod = ?'id-at-name', + accessLocation = {rfc822Name, "a name"}}]}, + {'SubjectInfoAccessSyntax', + [#'AccessDescription'{accessMethod = ?'id-at-name', + accessLocation = {rfc822Name, "a name"}}]}, + {'CRLNumber', 4711}, + {'BaseCRLNumber', 4710}, + {'CRLReason', 'cessationOfOperation'}, + {'CertificateIssuer', [{rfc822Name, "a name"}]}, + {'HoldInstructionCode', ?'id-holdinstruction-callissuer'}, + {'InvalidityDate', "20220127000000Z"}, + {'CRLDistributionPoints', [#'DistributionPoint'{}]} + ], + Check = fun({Ext, Value}) -> + try + Der = public_key:der_encode(Ext, Value), + true = is_binary(Der), + case public_key:der_decode(Ext, Der) of + Value -> false; + Bin when is_binary(Bin) -> + Value =/= binary_to_list(Bin) + end + catch Err:Reason:St -> + {true, {fail, Ext, Value, Err, Reason, St}} + end + end, + [] = lists:filtermap(Check, Exts), + + Badarg = {badarg, {unknown_type, type_do_not_exist}}, + {'EXIT', {Badarg,_}} = (catch public_key:der_encode(type_do_not_exist, 4711)), + {'EXIT', {Badarg,_}} = (catch public_key:der_decode(type_do_not_exist, <<47,11>>)), + ok. + %%-------------------------------------------------------------------- encrypt_decrypt() -> [{doc, "Test public_key:encrypt_private and public_key:decrypt_public"}].