diff --git a/proto/asn1-pdu/x509_certificate.proto b/proto/asn1-pdu/x509_certificate.proto index b2a990a..4ee6dff 100644 --- a/proto/asn1-pdu/x509_certificate.proto +++ b/proto/asn1-pdu/x509_certificate.proto @@ -165,6 +165,9 @@ message Extension { oneof types { AuthorityKeyIdentifier authority_key_identifier = 4; SubjectKeyIdentifier subject_key_identifier = 5; + KeyUsage key_usage = 6; + BasicConstraints basic_constraints = 7; + ExtendedKeyUsage extended_key_usage = 8; } } @@ -188,12 +191,34 @@ message SubjectKeyIdentifier { required asn1_universal_types.OctetString key_identifier = 1; } +// See RFC 5280, 4.2.1.3. +message KeyUsage { + // KeyUsage ::= BIT STRING. Use bool to represent each bit of KeyUsage. + required bool digital_signature = 1; + required bool non_repudation = 2; + required bool key_encipherment = 3; + required bool data_encipherment = 4; + required bool key_agreement = 5; + required bool key_cert_sign = 6; + required bool crl_sign = 7; + required bool encipher_only = 8; + required bool decipher_only = 9; +} + // See RFC 5280, 4.2.1.9. message BasicConstraints { - required asn1_universal_types.Boolean cA = 1; + required asn1_universal_types.Boolean ca = 1; optional asn1_universal_types.Integer path_len_constraint = 2; } +// See RFC 5280, 4.2.1.12. +message ExtendedKeyUsage { + // ExtendedKeyUsage is SEQUENCE SIZE (1..MAX) OF KeyPurposeId. + // This ensures that there is at least one |key_purpose_id| is present. + required asn1_universal_types.ObjectIdentifier key_purpose_id = 1; + repeated asn1_universal_types.ObjectIdentifier key_purpose_ids = 2; +} + // See RFC 5280, 4.1 & 4.1.1.2. message SignatureAlgorithm { // If |pdu| is present, encode |SignatureAlgorithm| as an arbitraty pdu. diff --git a/proto/asn1-pdu/x509_certificate_to_der.cc b/proto/asn1-pdu/x509_certificate_to_der.cc index 08989b0..b47caf0 100644 --- a/proto/asn1-pdu/x509_certificate_to_der.cc +++ b/proto/asn1-pdu/x509_certificate_to_der.cc @@ -43,10 +43,78 @@ DECLARE_ENCODE_FUNCTION(AlgorithmIdentifierSequence) { EncodeTagAndLength(kAsn1Sequence, der.size() - tag_len_pos, tag_len_pos, der); } +DECLARE_ENCODE_FUNCTION(ExtendedKeyUsage) { + // RFC 5280, 4.2.1.9: |ExtendedKeyUsage| is a sequence of (1..MAX) + // |key_purpose_id|. + // Save the current size in |tag_len_pos| to place sequence + // tag and length after the values are encoded. + size_t tag_len_pos = der.size(); + + // First |key_purpose_id| is set by protobuf and always encoded to comply + // with spec. + Encode(val.key_purpose_id(), der); + for (const auto& key_purpose_id : val.key_purpose_ids()) { + Encode(key_purpose_id, der); + } + + // The current size of |der| subtracted by |tag_len_pos| + // equates to the size of the sequence |ExtendedKeyUsage|. + EncodeTagAndLength(kAsn1Sequence, der.size() - tag_len_pos, tag_len_pos, der); +} + DECLARE_ENCODE_FUNCTION(BasicConstraints) { - if(val.cA().val()) { - Encode(val.cA()); + // RFC 5280, 4.2.1.9: |BasicConstraints| is a sequence of |ca| and + // |path_len_constraint|. + // Save the current size in |tag_len_pos| to place sequence tag and length + // after the values are encoded. + size_t tag_len_pos = der.size(); + + // RFC 5280, 4.2.1.9: |ca| is BOOLEAN DEFAULT FALSE. + // (X.690 (2015), 11.5): DEFAULT value in a sequence field is not encoded. + if (val.ca().val()) { + Encode(val.ca(), der); + } + + // RFC 5280, 4.2.1.9: |path_len_constrant| is OPTIONAL. Encode only if + // present. + if (val.has_path_len_constraint()) { + Encode(val.path_len_constraint(), der); + } + + // The current size of |der| subtracted by |tag_len_pos| + // equates to the size of the sequence |BasicConstraints|. + EncodeTagAndLength(kAsn1Sequence, der.size() - tag_len_pos, tag_len_pos, der); +} + +DECLARE_ENCODE_FUNCTION(KeyUsage) { + constexpr struct Mask { + using Fn = bool (KeyUsage::*)(void) const; + Fn key_usage_set; + uint32_t key_usage_value; + } kMasks[] = { + {&KeyUsage::digital_signature, 0x01}, + {&KeyUsage::non_repudation, 0x02}, + {&KeyUsage::key_encipherment, 0x04}, + {&KeyUsage::data_encipherment, 0x08}, + {&KeyUsage::key_agreement, 0x10}, + {&KeyUsage::key_cert_sign, 0x20}, + {&KeyUsage::crl_sign, 0x40}, + {&KeyUsage::encipher_only, 0x80}, + {&KeyUsage::decipher_only, 0x100}, + }; + uint16_t key_usage = 0x00; + for (const auto& mask : kMasks) { + if ((val.*(mask.key_usage_set))()) { + key_usage |= mask.key_usage_value; + } } + // RFC 5280, 4.2.1.3: KeyUsage ::= BIT STRING. + // Save the current size in |tag_len_pos| to place BitString tag and length + // after |key_usage| is encoded. + size_t tag_len_pos = der.size(); + InsertVariableIntBase256(key_usage, der.size(), der); + EncodeTagAndLength(kAsn1BitString, der.size() - tag_len_pos, tag_len_pos, + der); } DECLARE_ENCODE_FUNCTION(SubjectKeyIdentifier) { @@ -54,6 +122,13 @@ DECLARE_ENCODE_FUNCTION(SubjectKeyIdentifier) { } DECLARE_ENCODE_FUNCTION(AuthorityKeyIdentifier) { + // RFC 5280, 4.2.1.1: |AuthorityKeyIdentifier)| is a sequence of + // |key_identifier|, |authority_cert_issuer|, and + // |authority_cert_serial_number|. + // Save the current size in |tag_len_pos| to place sequence tag and length + // after the value is encoded. + size_t tag_len_pos = der.size(); + if (val.has_key_identifier()) { size_t pos_of_tag = der.size(); Encode(val.key_identifier(), der); @@ -75,6 +150,10 @@ DECLARE_ENCODE_FUNCTION(AuthorityKeyIdentifier) { // 5280, 4.2.1.1). ReplaceTag(kAsn1ContextSpecific | 0x02, pos_of_tag, der); } + + // The current size of |der| subtracted by |tag_len_pos| + // equates to the size of the |AuthorityKeyIdentifier|. + EncodeTagAndLength(kAsn1Sequence, der.size() - tag_len_pos, tag_len_pos, der); } DECLARE_ENCODE_FUNCTION(RawExtension) { @@ -98,6 +177,15 @@ void EncodeExtensionValue(const Extension& val, std::vector& der) { case Extension::TypesCase::kSubjectKeyIdentifier: Encode(val.subject_key_identifier(), der); break; + case Extension::TypesCase::kBasicConstraints: + Encode(val.basic_constraints(), der); + break; + case Extension::TypesCase::kExtendedKeyUsage: + Encode(val.extended_key_usage(), der); + break; + case Extension::TypesCase::kKeyUsage: + Encode(val.key_usage(), der); + break; case Extension::TypesCase::TYPES_NOT_SET: Encode(val.raw_extension(), der); break; @@ -120,6 +208,18 @@ void EncodeExtensionID(const Extension& val, std::vector& der) { // RFC 5280, 4.2.1.2: |SubjectKeyIdentifier| OID is {2 5 29 14}. encoded_oid = {(2 * 40) + 5, 29, 14}; break; + case Extension::TypesCase::kKeyUsage: + // RFC 5280, 4.2.1.3: |KeyUsage| OID is {2 5 29 15}. + encoded_oid = {(2 * 40) + 5, 29, 15}; + break; + case Extension::TypesCase::kBasicConstraints: + // RFC 5280, 4.2.1.9: |BasicConstraints| OID is {2 5 29 14}. + encoded_oid = {(2 * 40) + 5, 29, 19}; + break; + case Extension::TypesCase::kExtendedKeyUsage: + // RFC 5280, 4.2.1.12: |ExtendedKeyUsage| OID is {2 5 29 37}. + encoded_oid = {(2 * 40) + 5, 29, 37}; + break; case Extension::TypesCase::TYPES_NOT_SET: Encode(val.raw_extension().extn_id(), der); return; @@ -150,10 +250,21 @@ DECLARE_ENCODE_FUNCTION(Extension) { } DECLARE_ENCODE_FUNCTION(ExtensionSequence) { + // RFC 5280, 4.2.1.9: |ExtensionSequence| is a sequence of (1..MAX) Extension. + // Save the current size in |tag_len_pos| to place sequence tag and length + // after the value is encoded. + size_t tag_len_pos = der.size(); + + // First |extension| is set by protobuf and always encoded to comply + // with spec. Encode(val.extension(), der); for (const auto& extension : val.extensions()) { Encode(extension, der); } + + // The current size of |der| subtracted by |tag_len_pos| + // equates to the size of the |ExtensionSequence|. + EncodeTagAndLength(kAsn1Sequence, der.size() - tag_len_pos, tag_len_pos, der); } DECLARE_ENCODE_FUNCTION(SubjectPublicKeyInfoSequence) { diff --git a/proto/asn1-pdu/x509_certificate_to_der.h b/proto/asn1-pdu/x509_certificate_to_der.h index fd7a38f..2eea760 100644 --- a/proto/asn1-pdu/x509_certificate_to_der.h +++ b/proto/asn1-pdu/x509_certificate_to_der.h @@ -54,6 +54,9 @@ DECLARE_ENCODE_FUNCTION(TimeChoice); DECLARE_ENCODE_FUNCTION(ExtensionSequence); DECLARE_ENCODE_FUNCTION(Extension); DECLARE_ENCODE_FUNCTION(RawExtension); +DECLARE_ENCODE_FUNCTION(KeyUsage); +DECLARE_ENCODE_FUNCTION(BasicConstraints); +DECLARE_ENCODE_FUNCTION(ExtendedKeyUsage); DECLARE_ENCODE_FUNCTION(AuthorityKeyIdentifier); DECLARE_ENCODE_FUNCTION(SubjectKeyIdentifier); DECLARE_ENCODE_FUNCTION(SubjectPublicKeyInfoSequence);