// Copyright 2015 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "signature_algorithm.h" #include #include #include "input.h" #include "parse_values.h" #include "parser.h" namespace bssl { namespace { // From RFC 5912: // // sha1WithRSAEncryption OBJECT IDENTIFIER ::= { // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) // pkcs-1(1) 5 } // // In dotted notation: 1.2.840.113549.1.1.5 const uint8_t kOidSha1WithRsaEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05}; // sha1WithRSASignature is a deprecated equivalent of // sha1WithRSAEncryption. // // It originates from the NIST Open Systems Environment (OSE) // Implementor's Workshop (OIW). // // It is supported for compatibility with Microsoft's certificate APIs and // tools, particularly makecert.exe, which default(ed/s) to this OID for SHA-1. // // See also: https://bugzilla.mozilla.org/show_bug.cgi?id=1042479 // // In dotted notation: 1.3.14.3.2.29 const uint8_t kOidSha1WithRsaSignature[] = {0x2b, 0x0e, 0x03, 0x02, 0x1d}; // From RFC 5912: // // pkcs-1 OBJECT IDENTIFIER ::= // { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } // From RFC 5912: // // sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 } // // In dotted notation: 1.2.840.113549.1.1.11 const uint8_t kOidSha256WithRsaEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b}; // From RFC 5912: // // sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 } // // In dotted notation: 1.2.840.113549.1.1.11 const uint8_t kOidSha384WithRsaEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c}; // From RFC 5912: // // sha512WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 13 } // // In dotted notation: 1.2.840.113549.1.1.13 const uint8_t kOidSha512WithRsaEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d}; // From RFC 5912: // // ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { // iso(1) member-body(2) us(840) ansi-X9-62(10045) // signatures(4) 1 } // // In dotted notation: 1.2.840.10045.4.1 const uint8_t kOidEcdsaWithSha1[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01}; // From RFC 5912: // // ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { // iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) // ecdsa-with-SHA2(3) 2 } // // In dotted notation: 1.2.840.10045.4.3.2 const uint8_t kOidEcdsaWithSha256[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02}; // From RFC 5912: // // ecdsa-with-SHA384 OBJECT IDENTIFIER ::= { // iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) // ecdsa-with-SHA2(3) 3 } // // In dotted notation: 1.2.840.10045.4.3.3 const uint8_t kOidEcdsaWithSha384[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03}; // From RFC 5912: // // ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { // iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) // ecdsa-with-SHA2(3) 4 } // // In dotted notation: 1.2.840.10045.4.3.4 const uint8_t kOidEcdsaWithSha512[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04}; // From RFC 5912: // // id-RSASSA-PSS OBJECT IDENTIFIER ::= { pkcs-1 10 } // // In dotted notation: 1.2.840.113549.1.1.10 const uint8_t kOidRsaSsaPss[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a}; // From RFC 5912: // // id-mgf1 OBJECT IDENTIFIER ::= { pkcs-1 8 } // // In dotted notation: 1.2.840.113549.1.1.8 const uint8_t kOidMgf1[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08}; // Returns true if the entirety of the input is a NULL value. [[nodiscard]] bool IsNull(der::Input input) { der::Parser parser(input); der::Input null_value; if (!parser.ReadTag(CBS_ASN1_NULL, &null_value)) { return false; } // NULL values are TLV encoded; the value is expected to be empty. if (!null_value.empty()) { return false; } // By definition of this function, the entire input must be a NULL. return !parser.HasMore(); } [[nodiscard]] bool IsNullOrEmpty(der::Input input) { return IsNull(input) || input.empty(); } // Parses a MaskGenAlgorithm as defined by RFC 5912: // // MaskGenAlgorithm ::= AlgorithmIdentifier{ALGORITHM, // {PKCS1MGFAlgorithms}} // // mgf1SHA1 MaskGenAlgorithm ::= { // algorithm id-mgf1, // parameters HashAlgorithm : sha1Identifier // } // // -- // -- Define the set of mask generation functions // -- // -- If the identifier is id-mgf1, any of the listed hash // -- algorithms may be used. // -- // // PKCS1MGFAlgorithms ALGORITHM ::= { // { IDENTIFIER id-mgf1 PARAMS TYPE HashAlgorithm ARE required }, // ... // } // // Note that the possible mask gen algorithms is extensible. However at present // the only function supported is MGF1, as that is the singular mask gen // function defined by RFC 4055 / RFC 5912. [[nodiscard]] bool ParseMaskGenAlgorithm(const der::Input input, DigestAlgorithm *mgf1_hash) { der::Input oid; der::Input params; if (!ParseAlgorithmIdentifier(input, &oid, ¶ms)) { return false; } // MGF1 is the only supported mask generation algorithm. if (oid != der::Input(kOidMgf1)) { return false; } return ParseHashAlgorithm(params, mgf1_hash); } // Parses the parameters for an RSASSA-PSS signature algorithm, as defined by // RFC 5912: // // sa-rsaSSA-PSS SIGNATURE-ALGORITHM ::= { // IDENTIFIER id-RSASSA-PSS // PARAMS TYPE RSASSA-PSS-params ARE required // HASHES { mda-sha1 | mda-sha224 | mda-sha256 | mda-sha384 // | mda-sha512 } // PUBLIC-KEYS { pk-rsa | pk-rsaSSA-PSS } // SMIME-CAPS { IDENTIFIED BY id-RSASSA-PSS } // } // // RSASSA-PSS-params ::= SEQUENCE { // hashAlgorithm [0] HashAlgorithm DEFAULT sha1Identifier, // maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT mgf1SHA1, // saltLength [2] INTEGER DEFAULT 20, // trailerField [3] INTEGER DEFAULT 1 // } // // Which is to say the parameters MUST be present, and of type // RSASSA-PSS-params. Additionally, we only support the RSA-PSS parameter // combinations representable by TLS 1.3 (RFC 8446). // // Note also that DER encoding (ITU-T X.690 section 11.5) prohibits // specifying default values explicitly. The parameter should instead be // omitted to indicate a default value. std::optional ParseRsaPss(der::Input params) { der::Parser parser(params); der::Parser params_parser; if (!parser.ReadSequence(¶ms_parser)) { return std::nullopt; } // There shouldn't be anything after the sequence (by definition the // parameters is a single sequence). if (parser.HasMore()) { return std::nullopt; } // The default values for hashAlgorithm, maskGenAlgorithm, and saltLength // correspond to SHA-1, which we do not support with RSA-PSS, so treat them as // required fields. Explicitly-specified defaults will be rejected later, when // we limit combinations. Additionally, as the trailerField is required to be // the default, we simply ignore it and reject it as any other trailing data. // // hashAlgorithm [0] HashAlgorithm DEFAULT sha1Identifier, // maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT mgf1SHA1, // saltLength [2] INTEGER DEFAULT 20, // trailerField [3] INTEGER DEFAULT 1 der::Input field; DigestAlgorithm hash, mgf1_hash; der::Parser salt_length_parser; uint64_t salt_length; if (!params_parser.ReadTag( CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0, &field) || !ParseHashAlgorithm(field, &hash) || !params_parser.ReadTag( CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1, &field) || !ParseMaskGenAlgorithm(field, &mgf1_hash) || !params_parser.ReadConstructed( CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 2, &salt_length_parser) || !salt_length_parser.ReadUint64(&salt_length) || salt_length_parser.HasMore() || params_parser.HasMore()) { return std::nullopt; } // Only combinations of RSASSA-PSS-params specified by TLS 1.3 (RFC 8446) are // supported. if (hash != mgf1_hash) { return std::nullopt; // TLS 1.3 always matches MGF-1 and message hash. } if (hash == DigestAlgorithm::Sha256 && salt_length == 32) { return SignatureAlgorithm::kRsaPssSha256; } if (hash == DigestAlgorithm::Sha384 && salt_length == 48) { return SignatureAlgorithm::kRsaPssSha384; } if (hash == DigestAlgorithm::Sha512 && salt_length == 64) { return SignatureAlgorithm::kRsaPssSha512; } return std::nullopt; } } // namespace [[nodiscard]] bool ParseAlgorithmIdentifier(der::Input input, der::Input *algorithm, der::Input *parameters) { der::Parser parser(input); der::Parser algorithm_identifier_parser; if (!parser.ReadSequence(&algorithm_identifier_parser)) { return false; } // There shouldn't be anything after the sequence. This is by definition, // as the input to this function is expected to be a single // AlgorithmIdentifier. if (parser.HasMore()) { return false; } if (!algorithm_identifier_parser.ReadTag(CBS_ASN1_OBJECT, algorithm)) { return false; } // Read the optional parameters to a der::Input. The parameters can be at // most one TLV (for instance NULL or a sequence). // // Note that nothing is allowed after the single optional "parameters" TLV. // This is because RFC 5912's notation for AlgorithmIdentifier doesn't // explicitly list an extension point after "parameters". *parameters = der::Input(); if (algorithm_identifier_parser.HasMore() && !algorithm_identifier_parser.ReadRawTLV(parameters)) { return false; } return !algorithm_identifier_parser.HasMore(); } [[nodiscard]] bool ParseHashAlgorithm(der::Input input, DigestAlgorithm *out) { CBS cbs; CBS_init(&cbs, input.data(), input.size()); const EVP_MD *md = EVP_parse_digest_algorithm(&cbs); if (md == EVP_sha1()) { *out = DigestAlgorithm::Sha1; } else if (md == EVP_sha256()) { *out = DigestAlgorithm::Sha256; } else if (md == EVP_sha384()) { *out = DigestAlgorithm::Sha384; } else if (md == EVP_sha512()) { *out = DigestAlgorithm::Sha512; } else { // TODO(eroman): Support MD2, MD4, MD5 for completeness? // Unsupported digest algorithm. return false; } return true; } std::optional ParseSignatureAlgorithm( der::Input algorithm_identifier) { der::Input oid; der::Input params; if (!ParseAlgorithmIdentifier(algorithm_identifier, &oid, ¶ms)) { return std::nullopt; } // TODO(eroman): Each OID is tested for equality in order, which is not // particularly efficient. // RFC 5912 requires that the parameters for RSA PKCS#1 v1.5 algorithms be // NULL ("PARAMS TYPE NULL ARE required"), however an empty parameter is also // allowed for compatibility with non-compliant OCSP responders. // // TODO(svaldez): Add warning about non-strict parsing. if (oid == der::Input(kOidSha1WithRsaEncryption) && IsNullOrEmpty(params)) { return SignatureAlgorithm::kRsaPkcs1Sha1; } if (oid == der::Input(kOidSha256WithRsaEncryption) && IsNullOrEmpty(params)) { return SignatureAlgorithm::kRsaPkcs1Sha256; } if (oid == der::Input(kOidSha384WithRsaEncryption) && IsNullOrEmpty(params)) { return SignatureAlgorithm::kRsaPkcs1Sha384; } if (oid == der::Input(kOidSha512WithRsaEncryption) && IsNullOrEmpty(params)) { return SignatureAlgorithm::kRsaPkcs1Sha512; } if (oid == der::Input(kOidSha1WithRsaSignature) && IsNullOrEmpty(params)) { return SignatureAlgorithm::kRsaPkcs1Sha1; } // RFC 5912 requires that the parameters for ECDSA algorithms be absent // ("PARAMS TYPE NULL ARE absent"): if (oid == der::Input(kOidEcdsaWithSha1) && params.empty()) { return SignatureAlgorithm::kEcdsaSha1; } if (oid == der::Input(kOidEcdsaWithSha256) && params.empty()) { return SignatureAlgorithm::kEcdsaSha256; } if (oid == der::Input(kOidEcdsaWithSha384) && params.empty()) { return SignatureAlgorithm::kEcdsaSha384; } if (oid == der::Input(kOidEcdsaWithSha512) && params.empty()) { return SignatureAlgorithm::kEcdsaSha512; } if (oid == der::Input(kOidRsaSsaPss)) { return ParseRsaPss(params); } // Unknown signature algorithm. return std::nullopt; } std::optional GetTlsServerEndpointDigestAlgorithm( SignatureAlgorithm alg) { // See RFC 5929, section 4.1. RFC 5929 breaks the signature algorithm // abstraction by trying to extract individual digest algorithms. (While // common, this is not a universal property of signature algorithms.) We // implement this within the library, so callers do not need to condition over // all algorithms. switch (alg) { // If the single digest algorithm is SHA-1, use SHA-256. case SignatureAlgorithm::kRsaPkcs1Sha1: case SignatureAlgorithm::kEcdsaSha1: return DigestAlgorithm::Sha256; case SignatureAlgorithm::kRsaPkcs1Sha256: case SignatureAlgorithm::kEcdsaSha256: return DigestAlgorithm::Sha256; case SignatureAlgorithm::kRsaPkcs1Sha384: case SignatureAlgorithm::kEcdsaSha384: return DigestAlgorithm::Sha384; case SignatureAlgorithm::kRsaPkcs1Sha512: case SignatureAlgorithm::kEcdsaSha512: return DigestAlgorithm::Sha512; // It is ambiguous whether hash-matching RSASSA-PSS instantiations count as // using one or multiple digests, but the corresponding digest is the only // reasonable interpretation. case SignatureAlgorithm::kRsaPssSha256: return DigestAlgorithm::Sha256; case SignatureAlgorithm::kRsaPssSha384: return DigestAlgorithm::Sha384; case SignatureAlgorithm::kRsaPssSha512: return DigestAlgorithm::Sha512; } return std::nullopt; } } // namespace bssl