| Index: net/cert/internal/signature_algorithm.cc
|
| diff --git a/net/cert/internal/signature_algorithm.cc b/net/cert/internal/signature_algorithm.cc
|
| index 62d50eaf0f60d66ace3c0b40b06d34414ca5c87f..468fa16c3a62b8d8eb84fdbedabf6648ecff606f 100644
|
| --- a/net/cert/internal/signature_algorithm.cc
|
| +++ b/net/cert/internal/signature_algorithm.cc
|
| @@ -4,7 +4,9 @@
|
|
|
| #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 {
|
| @@ -103,6 +105,61 @@ const uint8_t kOidEcdsaWithSha384[] =
|
| 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 {
|
| @@ -260,6 +317,222 @@ scoped_ptr<SignatureAlgorithm> ParseEcdsa(DigestAlgorithm digest,
|
| 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<uint32_t> 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<SignatureAlgorithm> 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,
|
| @@ -308,7 +581,8 @@ scoped_ptr<SignatureAlgorithm> SignatureAlgorithm::CreateFromDer(
|
| if (oid.Equals(der::Input(kOidEcdsaWithSha512)))
|
| return ParseEcdsa(DigestAlgorithm::Sha512, params);
|
|
|
| - // TODO(eroman): Add parsing of RSASSA-PSS
|
| + if (oid.Equals(der::Input(kOidRsaSsaPss)))
|
| + return ParseRsaPss(params);
|
|
|
| if (oid.Equals(der::Input(kOidSha1WithRsaSignature)))
|
| return ParseRsaPkcs1(DigestAlgorithm::Sha1, params);
|
|
|