// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/cert/internal/signature_algorithm.h" #include "base/numerics/safe_math.h" #include "net/der/input.h" #include "net/der/parse_values.h" #include "net/der/parser.h" namespace net { 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-sha1 OBJECT IDENTIFIER ::= { // iso(1) identified-organization(3) oiw(14) secsig(3) // algorithm(2) 26 } // // In dotted notation: 1.3.14.3.2.26 const uint8_t kOidSha1[] = {0x2B, 0x0E, 0x03, 0x02, 0x1A}; // From RFC 5912: // // id-sha256 OBJECT IDENTIFIER ::= // { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) // csor(3) nistAlgorithms(4) hashalgs(2) 1 } // // In dotted notation: 2.16.840.1.101.3.4.2.1 const uint8_t kOidSha256[] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01}; // From RFC 5912: // // id-sha384 OBJECT IDENTIFIER ::= // { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) // csor(3) nistAlgorithms(4) hashalgs(2) 2 } // // In dotted notation: 2.16.840.1.101.3.4.2.2 const uint8_t kOidSha384[] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02}; // From RFC 5912: // // id-sha512 OBJECT IDENTIFIER ::= // { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) // csor(3) nistAlgorithms(4) hashalgs(2) 3 } // // In dotted notation: 2.16.840.1.101.3.4.2.3 const uint8_t kOidSha512[] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03}; // 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}; // RFC 5280 section 4.1.1.2 defines signatureAlgorithm as: // // AlgorithmIdentifier ::= SEQUENCE { // algorithm OBJECT IDENTIFIER, // parameters ANY DEFINED BY algorithm OPTIONAL } WARN_UNUSED_RESULT bool ParseAlgorithmIdentifier(const 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(der::kOid, 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(); } // Returns true if |input| is empty. WARN_UNUSED_RESULT bool IsEmpty(const der::Input& input) { return input.Length() == 0; } // Returns true if the entirety of the input is a NULL value. WARN_UNUSED_RESULT bool IsNull(const der::Input& input) { der::Parser parser(input); der::Input null_value; if (!parser.ReadTag(der::kNull, &null_value)) return false; // NULL values are TLV encoded; the value is expected to be empty. if (!IsEmpty(null_value)) return false; // By definition of this function, the entire input must be a NULL. return !parser.HasMore(); } // Parses an RSA PKCS#1 v1.5 signature algorithm given the DER-encoded // "parameters" from the parsed AlgorithmIdentifier, and the hash algorithm // that was implied by the AlgorithmIdentifier's OID. // // Returns a nullptr on failure. // // RFC 5912 requires that the parameters for RSA PKCS#1 v1.5 algorithms be NULL // ("PARAMS TYPE NULL ARE required"): // // sa-rsaWithSHA1 SIGNATURE-ALGORITHM ::= { // IDENTIFIER sha1WithRSAEncryption // PARAMS TYPE NULL ARE required // HASHES { mda-sha1 } // PUBLIC-KEYS { pk-rsa } // SMIME-CAPS {IDENTIFIED BY sha1WithRSAEncryption } // } // // sa-sha256WithRSAEncryption SIGNATURE-ALGORITHM ::= { // IDENTIFIER sha256WithRSAEncryption // PARAMS TYPE NULL ARE required // HASHES { mda-sha256 } // PUBLIC-KEYS { pk-rsa } // SMIME-CAPS { IDENTIFIED BY sha256WithRSAEncryption } // } // // sa-sha384WithRSAEncryption SIGNATURE-ALGORITHM ::= { // IDENTIFIER sha384WithRSAEncryption // PARAMS TYPE NULL ARE required // HASHES { mda-sha384 } // PUBLIC-KEYS { pk-rsa } // SMIME-CAPS { IDENTIFIED BY sha384WithRSAEncryption } // } // // sa-sha512WithRSAEncryption SIGNATURE-ALGORITHM ::= { // IDENTIFIER sha512WithRSAEncryption // PARAMS TYPE NULL ARE required // HASHES { mda-sha512 } // PUBLIC-KEYS { pk-rsa } // SMIME-CAPS { IDENTIFIED BY sha512WithRSAEncryption } // } scoped_ptr ParseRsaPkcs1(DigestAlgorithm digest, const der::Input& params) { if (!IsNull(params)) return nullptr; return SignatureAlgorithm::CreateRsaPkcs1(digest); } // Parses an ECDSA signature algorithm given the DER-encoded "parameters" from // the parsed AlgorithmIdentifier, and the hash algorithm that was implied by // the AlgorithmIdentifier's OID. // // On failure returns a nullptr. // // RFC 5912 requires that the parameters for ECDSA algorithms be absent // ("PARAMS TYPE NULL ARE absent"): // // sa-ecdsaWithSHA1 SIGNATURE-ALGORITHM ::= { // IDENTIFIER ecdsa-with-SHA1 // VALUE ECDSA-Sig-Value // PARAMS TYPE NULL ARE absent // HASHES { mda-sha1 } // PUBLIC-KEYS { pk-ec } // SMIME-CAPS {IDENTIFIED BY ecdsa-with-SHA1 } // } // // sa-ecdsaWithSHA256 SIGNATURE-ALGORITHM ::= { // IDENTIFIER ecdsa-with-SHA256 // VALUE ECDSA-Sig-Value // PARAMS TYPE NULL ARE absent // HASHES { mda-sha256 } // PUBLIC-KEYS { pk-ec } // SMIME-CAPS { IDENTIFIED BY ecdsa-with-SHA256 } // } // // sa-ecdsaWithSHA384 SIGNATURE-ALGORITHM ::= { // IDENTIFIER ecdsa-with-SHA384 // VALUE ECDSA-Sig-Value // PARAMS TYPE NULL ARE absent // HASHES { mda-sha384 } // PUBLIC-KEYS { pk-ec } // SMIME-CAPS { IDENTIFIED BY ecdsa-with-SHA384 } // } // // sa-ecdsaWithSHA512 SIGNATURE-ALGORITHM ::= { // IDENTIFIER ecdsa-with-SHA512 // VALUE ECDSA-Sig-Value // PARAMS TYPE NULL ARE absent // HASHES { mda-sha512 } // PUBLIC-KEYS { pk-ec } // SMIME-CAPS { IDENTIFIED BY ecdsa-with-SHA512 } // } scoped_ptr ParseEcdsa(DigestAlgorithm digest, const der::Input& params) { if (!IsEmpty(params)) return nullptr; return SignatureAlgorithm::CreateEcdsa(digest); } // Parses a HashAlgorithm as defined by RFC 5912: // // HashAlgorithm ::= AlgorithmIdentifier{DIGEST-ALGORITHM, // {HashAlgorithms}} // // HashAlgorithms DIGEST-ALGORITHM ::= { // { IDENTIFIER id-sha1 PARAMS TYPE NULL ARE preferredPresent } | // { IDENTIFIER id-sha224 PARAMS TYPE NULL ARE preferredPresent } | // { IDENTIFIER id-sha256 PARAMS TYPE NULL ARE preferredPresent } | // { IDENTIFIER id-sha384 PARAMS TYPE NULL ARE preferredPresent } | // { IDENTIFIER id-sha512 PARAMS TYPE NULL ARE preferredPresent } // } WARN_UNUSED_RESULT bool ParseHashAlgorithm(const der::Input input, DigestAlgorithm* out) { der::Input oid; der::Input params; if (!ParseAlgorithmIdentifier(input, &oid, ¶ms)) return false; DigestAlgorithm hash; if (oid.Equals(der::Input(kOidSha1))) { hash = DigestAlgorithm::Sha1; } else if (oid.Equals(der::Input(kOidSha256))) { hash = DigestAlgorithm::Sha256; } else if (oid.Equals(der::Input(kOidSha384))) { hash = DigestAlgorithm::Sha384; } else if (oid.Equals(der::Input(kOidSha512))) { hash = DigestAlgorithm::Sha512; } else { // Unsupported digest algorithm. return false; } // From RFC 5912: "PARAMS TYPE NULL ARE preferredPresent". Which is to say // the can either be absent, or NULL. if (!IsEmpty(params) && !IsNull(params)) return false; *out = hash; return true; } // 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. WARN_UNUSED_RESULT 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.Equals(der::Input(kOidMgf1))) return false; return ParseHashAlgorithm(params, mgf1_hash); } // Consumes an optional, explicitly-tagged INTEGER from |parser|, using the // indicated context-specific class number. Values greater than 32-bits will be // rejected. // // Returns true on success and sets |*present| to true if the field was present. WARN_UNUSED_RESULT bool ReadOptionalContextSpecificUint32(der::Parser* parser, uint8_t class_number, uint32_t* out, bool* present) { der::Input value; bool has_value; // Read the context specific value. if (!parser->ReadOptionalTag(der::ContextSpecificConstructed(class_number), &value, &has_value)) { return false; } if (has_value) { // Parse the integer contained in it. der::Parser number_parser(value); uint64_t uint64_value; if (!number_parser.ReadUint64(&uint64_value)) return false; if (number_parser.HasMore()) return false; // Cast the number to a uint32_t base::CheckedNumeric casted(uint64_value); if (!casted.IsValid()) return false; *out = casted.ValueOrDie(); } *present = has_value; return true; } // 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. scoped_ptr ParseRsaPss(const der::Input& params) { der::Parser parser(params); der::Parser params_parser; if (!parser.ReadSequence(¶ms_parser)) return nullptr; // There shouldn't be anything after the sequence (by definition the // parameters is a single sequence). if (parser.HasMore()) return nullptr; bool has_field; der::Input field; // Parse: // hashAlgorithm [0] HashAlgorithm DEFAULT sha1Identifier, DigestAlgorithm hash = DigestAlgorithm::Sha1; if (!params_parser.ReadOptionalTag(der::ContextSpecificConstructed(0), &field, &has_field)) { return nullptr; } if (has_field && !ParseHashAlgorithm(field, &hash)) return nullptr; // Parse: // maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT mgf1SHA1, DigestAlgorithm mgf1_hash = DigestAlgorithm::Sha1; if (!params_parser.ReadOptionalTag(der::ContextSpecificConstructed(1), &field, &has_field)) { return nullptr; } if (has_field && !ParseMaskGenAlgorithm(field, &mgf1_hash)) return nullptr; // Parse: // saltLength [2] INTEGER DEFAULT 20, uint32_t salt_length = 20u; if (!ReadOptionalContextSpecificUint32(¶ms_parser, 2, &salt_length, &has_field)) { return nullptr; } // Parse: // trailerField [3] INTEGER DEFAULT 1 uint32_t trailer_field = 1u; if (!ReadOptionalContextSpecificUint32(¶ms_parser, 3, &trailer_field, &has_field)) { return nullptr; } // RFC 4055 says that the trailer field must be 1: // // The trailerField field is an integer. It provides // compatibility with IEEE Std 1363a-2004 [P1363A]. The value // MUST be 1, which represents the trailer field with hexadecimal // value 0xBC. Other trailer fields, including the trailer field // composed of HashID concatenated with 0xCC that is specified in // IEEE Std 1363a, are not supported. Implementations that // perform signature generation MUST omit the trailerField field, // indicating that the default trailer field value was used. // Implementations that perform signature validation MUST // recognize both a present trailerField field with value 1 and an // absent trailerField field. if (trailer_field != 1) return nullptr; // There must not be any unconsumed data left. (RFC 5912 does not explicitly // include an extensibility point for RSASSA-PSS-params) if (params_parser.HasMore()) return nullptr; return SignatureAlgorithm::CreateRsaPss(hash, mgf1_hash, salt_length); } } // namespace RsaPssParameters::RsaPssParameters(DigestAlgorithm mgf1_hash, uint32_t salt_length) : mgf1_hash_(mgf1_hash), salt_length_(salt_length) { } bool RsaPssParameters::Equals(const RsaPssParameters* other) const { return mgf1_hash_ == other->mgf1_hash_ && salt_length_ == other->salt_length_; } SignatureAlgorithm::~SignatureAlgorithm() { } scoped_ptr SignatureAlgorithm::CreateFromDer( const der::Input& algorithm_identifier) { der::Input oid; der::Input params; if (!ParseAlgorithmIdentifier(algorithm_identifier, &oid, ¶ms)) return nullptr; // TODO(eroman): Each OID is tested for equality in order, which is not // particularly efficient. if (oid.Equals(der::Input(kOidSha1WithRsaEncryption))) return ParseRsaPkcs1(DigestAlgorithm::Sha1, params); if (oid.Equals(der::Input(kOidSha256WithRsaEncryption))) return ParseRsaPkcs1(DigestAlgorithm::Sha256, params); if (oid.Equals(der::Input(kOidSha384WithRsaEncryption))) return ParseRsaPkcs1(DigestAlgorithm::Sha384, params); if (oid.Equals(der::Input(kOidSha512WithRsaEncryption))) return ParseRsaPkcs1(DigestAlgorithm::Sha512, params); if (oid.Equals(der::Input(kOidEcdsaWithSha1))) return ParseEcdsa(DigestAlgorithm::Sha1, params); if (oid.Equals(der::Input(kOidEcdsaWithSha256))) return ParseEcdsa(DigestAlgorithm::Sha256, params); if (oid.Equals(der::Input(kOidEcdsaWithSha384))) return ParseEcdsa(DigestAlgorithm::Sha384, params); if (oid.Equals(der::Input(kOidEcdsaWithSha512))) return ParseEcdsa(DigestAlgorithm::Sha512, params); if (oid.Equals(der::Input(kOidRsaSsaPss))) return ParseRsaPss(params); if (oid.Equals(der::Input(kOidSha1WithRsaSignature))) return ParseRsaPkcs1(DigestAlgorithm::Sha1, params); return nullptr; // Unsupported OID. } scoped_ptr SignatureAlgorithm::CreateRsaPkcs1( DigestAlgorithm digest) { return make_scoped_ptr( new SignatureAlgorithm(SignatureAlgorithmId::RsaPkcs1, digest, nullptr)); } scoped_ptr SignatureAlgorithm::CreateEcdsa( DigestAlgorithm digest) { return make_scoped_ptr( new SignatureAlgorithm(SignatureAlgorithmId::Ecdsa, digest, nullptr)); } scoped_ptr SignatureAlgorithm::CreateRsaPss( DigestAlgorithm digest, DigestAlgorithm mgf1_hash, uint32_t salt_length) { return make_scoped_ptr(new SignatureAlgorithm( SignatureAlgorithmId::RsaPss, digest, make_scoped_ptr(new RsaPssParameters(mgf1_hash, salt_length)))); } bool SignatureAlgorithm::Equals(const SignatureAlgorithm& other) const { if (algorithm_ != other.algorithm_) return false; if (digest_ != other.digest_) return false; // Check that the parameters are equal. switch (algorithm_) { case SignatureAlgorithmId::RsaPss: { const RsaPssParameters* params1 = ParamsForRsaPss(); const RsaPssParameters* params2 = other.ParamsForRsaPss(); if (!params1 || !params2 || !params1->Equals(params2)) return false; break; } // There shouldn't be any parameters. case SignatureAlgorithmId::RsaPkcs1: case SignatureAlgorithmId::Ecdsa: if (params_ || other.params_) return false; break; } return true; } const RsaPssParameters* SignatureAlgorithm::ParamsForRsaPss() const { if (algorithm_ == SignatureAlgorithmId::RsaPss) return static_cast(params_.get()); return nullptr; } SignatureAlgorithm::SignatureAlgorithm( SignatureAlgorithmId algorithm, DigestAlgorithm digest, scoped_ptr params) : algorithm_(algorithm), digest_(digest), params_(params.Pass()) { } } // namespace net