Whether attribute certificates can be used in the public_key module.

We are trying to implement a feature for issuing attribute certificates using records such as AttributeCertificate, which are defined in public_key.hrl

We checked if a certificate generated by the public_key module could be read/parsed correctly by a modified OpenSSL 3 implementation with the attribute certificates.

Since I could confirm that the record was defined in the header, I obtained a test acert.pem that was included in the pull request to make openssl3 support attribute certificates, and tried to see if I could parse it.

I tried to see if I could expand it with public_key:pem_decode/1, but it only returned an empty list.

1> {ok, Bin} = file:read_file("acert.pem").
<<"-----BEGIN ATTRIBUTE CERTIFICATE-----\nMIICPTCCASUCAQEwN6AWMBGkDzANMQswCQYDVQQDDAJDQQIBAqEdpBswGTEXMBUG\nA1UEAwwOc2Vyd"...>>
2> public_key:pem_decode(Bin).
[]

After removing the header and footer listed in the PEM format and changing it to DER format with base64 decoding, we confirmed that it could be read with public_key:der_decode/2.

1> {ok, Bin} = file:read_file("acert.der").
{ok,<<48,130,2,61,48,130,1,37,2,1,1,48,55,160,22,48,17,
      164,15,48,13,49,11,48,9,6,3,...>>}
2> public_key:der_decode('AttributeCertificate', Bin).
{'AttributeCertificate',{'AttributeCertificateInfo',v2,
                                                    {'Holder',{'IssuerSerial',[{directoryName,{rdnSequence,[[{'AttributeTypeAndValue',{2,
                                                                                                                                       5,4,3},
                                                                                                                                      <<12,2,67,65>>}]]}}],
                                                                              2,asn1_NOVALUE},
                                                              [{directoryName,{rdnSequence,[[{'AttributeTypeAndValue',{2,
                                                                                                                       5,4,3},
                                                                                                                      <<12,14,115,101,114,118,101,114,46,101,120,...>>}]]}}],
                                                              asn1_NOVALUE},
                                                    {v2Form,{'V2Form',[{directoryName,{rdnSequence,[[{'AttributeTypeAndValue',{2,
                                                                                                                               5,4,3},
                                                                                                                              <<12,28,65,116,116,114,105,98,117,...>>}]]}}],
                                                                      asn1_NOVALUE,asn1_NOVALUE}},
                                                    {'AlgorithmIdentifier',{1,2,840,113549,1,1,11},<<5,0>>},
                                                    21175981651213461252787528108986572854611892162,
                                                    {'AttCertValidityPeriod',"20210615123500Z",
                                                                             "20310613123500Z"},
                                                    [{'Attribute',{1,3,6,1,5,5,7,10,4},
                                                                  [<<48,21,160,9,134,7,84,101,115,116,118,97,108,48,...>>]},
                                                     {'Attribute',{2,5,4,72},
                                                                  [<<48,17,161,15,131,13,97,100,109,105,110,105,115,...>>]}],
                                                    asn1_NOVALUE,
                                                    [{'Extension',{2,5,29,35},
                                                                  false,
                                                                  <<48,22,128,20,98,110,201,104,103,108,100,187,...>>},
                                                     {'Extension',{2,5,29,56},false,<<5,0>>}]},
                        {'AlgorithmIdentifier',{1,2,840,113549,1,1,11},<<5,0>>},
                        <<69,138,69,18,58,43,116,222,188,53,116,139,152,54,158,
                          186,187,177,135,15,153,178,191,70,174,...>>}

The current functionality that has already been implemented in public_key module is sufficient for our objective of handling the certificate in DER format.

We would like to use the public_key module functionality for issuing a platform certificate defined by TCG.

https://trustedcomputinggroup.org/resource/tcg-platform-certificate-profile/

I would like to confirm that the handling of attribute certificates in the public_key module of Erlang/OTP will not be deprecated and will remain supported.

Are the attribute certificates defined in RFC5755 and the related RFCs currently supported in the public_key module?

3 Likes

My understanding is that the support, as it is for general X.509 work, is this support is mostly incidental as this is all just pure ASN.1 decoding.

Lurking in https://github.com/erlang/otp/tree/master/lib/public_key/asn1 is a list of .asn1 files that tend to be just verbatim extracted from IETF materials and elsewhere.

During the building of OTP, these get converted into records that represent the underlying ASN.1 form. public_key then provides some functions to work with these records (typically outputting DER).

Anything not in there I suspect would just appear as raw non-human readable objects…plus nothing stops anyone just using their own ASN.1 definitions.

So probably not a question of ‘support’ as all this needs is an ASN.1 decoder which OTP has built in.

To actually work with AttributeCertificate you still need to do the hard coding part which not even OpenSSL does :slight_smile:

2 Likes

AttributeCertificate is decoded/encoded using code generated from ASN.1, so I recognized that it can be used regardless of whether or not documentation is available.

Therefore, we tried to generate a module that reads ASN.1 of PlatformCertificate, which is currently being developed by TCG, and performs encode and decode.

ASN.1 of PlatformCertificate defines the following SEQUENCE in which the encoding and decoding process is already provided in the public_key module (OTP-PUB-KEY.erl) AlgorithmIdentifier.

IMPORTS
    AlgorithmIdentifier
        FROM PKIX1Explicit88          {
            iso(1)
            identified-organization(3)
            dod(6)
            internet(1)
            security(5)
            mechanisms(5)
            pkix(7)
            id-mod(0)
            id-pkix1-explicit(18)
        };
URIReference                  ::= SEQUENCE {
    uniformResourceIdentifier     IA5String (SIZE (1..urimax)),
    hashAlgorithm                 AlgorithmIdentifier OPTIONAL,
    hashValue                     BIT STRING OPTIONAL
}

When an Erlang module is generated from the ASN.1 definition of PlatformCertificate, I want the encoding and decoding process for AlgorithmIdentifier to invoke the ‘OTP-PUB-KEY’:‘enc_AlgorithmIdentifier’/2 function defined in OTP-PUB-KEY.erl.

We are currently creating an Erlang module by executing erlc -bber +der +noobj PlatformCertificate.set.asn with an ASN.1 file named PlatformCertificate.asn1. The PlatformCertificate.set.asn file contains only the description of PlatformCertificate.asn1.

The replacement of OTP-PUB-KEY is carried out on the generated Erlang module by using sed for substitution.

sed -i -e "s/PKIX1Explicit88/OTP-PUB-KEY/g" PlatformCertificate.erl

Is it possible to specify the use of OTP-PUB-KEY in the decoding or encoding process of an AlgorithmIdentifier imported from ASN.1 via the erlc options, such as through a configuration file?

Would it perhaps be easier to use public_key:der_decode(<Asn1Type>, DER)

In your case public_key:der_decode('AlgorithmIdentifier', DER). in your erlang module.

1 Like

Thanks for the idea.

The following is an excerpt of the URIReference encoding process from the erlang code generated from the ASN.1 definition.

%%================================
%%  URIReference
%%================================
enc_URIReference(Val) ->
    enc_URIReference(Val, [<<48>>]).

enc_URIReference(Val, TagIn) ->
    {_, Cindex1, Cindex2, Cindex3} = Val,

    %%-------------------------------------------------
    %% attribute uniformResourceIdentifier(1) with type IA5String
    %%-------------------------------------------------
    {EncBytes1, EncLen1} = encode_restricted_string(Cindex1, [<<22>>]),

    %%-------------------------------------------------
    %% attribute hashAlgorithm(2)   External PKIX1Explicit88:AlgorithmIdentifier OPTIONAL
    %%-------------------------------------------------
    {EncBytes2, EncLen2} =
        case Cindex2 of
            asn1_NOVALUE ->
                {<<>>, 0};
            _ ->
                'PKIX1Explicit88':'enc_AlgorithmIdentifier'(Cindex2, [<<48>>])
        end,

    %%-------------------------------------------------
    %% attribute hashValue(3) with type BIT STRING OPTIONAL
    %%-------------------------------------------------
    {EncBytes3, EncLen3} =
        case Cindex3 of
            asn1_NOVALUE ->
                {<<>>, 0};
            _ ->
                encode_unnamed_bit_string(Cindex3, [<<3>>])
        end,

    BytesSoFar = [EncBytes1, EncBytes2, EncBytes3],
    LenSoFar = EncLen1 + EncLen2 + EncLen3,
    encode_tags(TagIn, BytesSoFar, LenSoFar).

This shows that 'PKIX1Explicit88':'enc_AlgorithmIdentifier'(Cindex2, [<<48>>]) is called as an encoding process for AlgorithmIdentifier.

The definition of AlgorithmIdentifier actually exists in PKIX1Explicit88.asn1.
In erlang/otp, it is named OTP-PUB-KEY and is grouped together with other ASN.1 definitions.

The following is the process of calling AlgorithmIdentifier in OTP-PUB-KEY.erl, which is actually called by public_key.

%%================================
%%  AlgorithmIdentifier
%%================================
enc_AlgorithmIdentifier(Val) ->
    enc_AlgorithmIdentifier(Val, [<<48>>]).

enc_AlgorithmIdentifier(Val, TagIn) ->
    {_, Cindex1, Cindex2} = Val,

    %%-------------------------------------------------
    %% attribute algorithm(1) with type OBJECT IDENTIFIER
    %%-------------------------------------------------
    {EncBytes1, EncLen1} = encode_object_identifier(Cindex1, [<<6>>]),

    %%-------------------------------------------------
    %% attribute parameters(2) with type ASN1_OPEN_TYPE OPTIONAL
    %%-------------------------------------------------
    {EncBytes2, EncLen2} =
        case Cindex2 of
            asn1_NOVALUE ->
                {<<>>, 0};
            _ ->
                encode_open_type(Cindex2, [])
        end,

    BytesSoFar = [EncBytes1, EncBytes2],
    LenSoFar = EncLen1 + EncLen2,
    encode_tags(TagIn, BytesSoFar, LenSoFar).

Checking the Erlang User’s Guide ASN.1, it is introduced that erlang code can be generated with any module name by specifying MyModule.set.asn.

Even if you make use of public_key:der_encode/2 and public_key:der_decode/2, you will have to change the code generated from ASN.1.

Is there a better way to do this in erlc settings?