Chromium Code Reviews| Index: net/cert/internal/signature_algorithm.cc |
| diff --git a/net/cert/internal/signature_algorithm.cc b/net/cert/internal/signature_algorithm.cc |
| index 2a4ce7678fbf0bc4ff664d9fd93de8f589c64585..7be9073ea74283d28f9c97815b130c28a52b4e96 100644 |
| --- a/net/cert/internal/signature_algorithm.cc |
| +++ b/net/cert/internal/signature_algorithm.cc |
| @@ -6,7 +6,9 @@ |
| #include <stdint.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 { |
| @@ -96,6 +98,43 @@ const uint8_t kOidEcdsaWithSha512[] = |
| const uint8_t kOidRsaSsaPss[] = |
| {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a}; |
| +// From RFC 4055: |
| +// id-sha1 OBJECT IDENTIFIER ::= { iso(1) |
| +// identified-organization(3) oiw(14) |
| +// secsig(3) algorithms(2) 26 } |
| +// In dotted notation: 1.3.14.3.2.26 |
| +const uint8_t kOidSha1[] = {0x2B, 0x0E, 0x03, 0x02, 0x1A}; |
| + |
| +// From RFC 4055: |
| +// id-sha256 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) |
| +// country(16) us(840) organization(1) gov(101) |
| +// csor(3) nistalgorithm(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 4055: |
| +// id-sha384 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) |
| +// country(16) us(840) organization(1) gov(101) |
| +// csor(3) nistalgorithm(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 4055: |
| +// id-sha512 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) |
| +// country(16) us(840) organization(1) gov(101) |
| +// csor(3) nistalgorithm(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 4055: |
| +// 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 { |
| @@ -207,6 +246,126 @@ WARN_UNUSED_RESULT bool ParseEcdsa(DigestAlgorithm digest, |
| return true; |
| } |
| +// Parses an AlgorithmIdentifier representing an RFC 4055 "HashAlgorithm". |
| +// Examples of HashAlgorithms are: |
| +// |
| +// sha1Identifier AlgorithmIdentifier ::= { id-sha1, NULL } |
| +// sha224Identifier AlgorithmIdentifier ::= { id-sha224, NULL } |
| +// sha256Identifier AlgorithmIdentifier ::= { id-sha256, NULL } |
| +// sha384Identifier AlgorithmIdentifier ::= { id-sha384, NULL } |
| +// sha512Identifier AlgorithmIdentifier ::= { id-sha512, NULL } |
| +// |
| +// Note that the parameters needn't be NULL as in the examples above. RFC 4055 |
| +// says that: |
| +// |
| +// All implementations MUST accept both NULL and absent parameters as |
| +// legal and equivalent encodings. |
| +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; |
| + } |
| + |
| + if (!IsNullOrEmpty(params)) |
| + return false; |
| + |
| + *out = hash; |
| + return true; |
| +} |
| + |
| +// Parses a MaskGenAlgorithm as defined by RFC 4055: |
| +// |
| +// MaskGenAlgorithm ::= AlgorithmIdentifier |
| +// |
| +// This function currently only supports MGF1, as that is the only function |
| +// defined in RFC 4055. |
| +// |
| +// RFC 4055 section 2.2: |
| +// |
| +// One mask generation function is used with the RSASSA-PSS signature |
| +// algorithm and the RSAES-OAEP key transport algorithm: MGF1 [P1v2.1]. |
| +// No other mask generation functions are supported by this |
| +// specification. |
| +// |
| +// MGF1 is identified by the following object identifier: |
| +// |
| +// id-mgf1 OBJECT IDENTIFIER ::= { pkcs-1 8 } |
| +// |
| +// The parameters field associated with id-mgf1 MUST have a |
| +// hashAlgorithm value which identifies the hash function being used |
| +// with MGF1. This value MUST be sha1Identifier, sha224Identifier, |
| +// sha256Identifier, sha384Identifier, or sha512Identifier, as specified |
| +// in Section 2.1. Implementations MUST support the default value, |
| +// sha1Identifier, and MAY support the other four values. |
| +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; |
| +} |
| + |
| // From RFC 4055: |
| // RSASSA-PSS-params ::= SEQUENCE { |
| // hashAlgorithm [0] HashAlgorithm DEFAULT |
| @@ -221,8 +380,73 @@ WARN_UNUSED_RESULT bool ParseEcdsa(DigestAlgorithm digest, |
| // MaskGenAlgorithm ::= AlgorithmIdentifier |
| WARN_UNUSED_RESULT bool ParseRsaPss(const der::Input& params, |
| SignatureAlgorithm* out) { |
| - // TODO(eroman): Implement. |
| - return false; |
| + // RFC 4055 says that the RSASSA-PSS-params must be present in signature |
| + // algorithms. (However because each field is optional, the sequence could be |
| + // empty when using all the defaults). |
| + // |
| + // Section 3.1: |
| + // |
| + // When RSASSA-PSS is used in an AlgorithmIdentifier, the parameters |
| + // MUST employ the RSASSA-PSS-params syntax. The parameters may be |
| + // either absent or present when used as subject public key information. |
| + // The parameters MUST be present when used in the algorithm identifier |
| + // associated with a signature value. |
| + der::Parser parser(params); |
| + der::Parser params_parser; |
| + if (!parser.ReadSequence(¶ms_parser)) |
| + return false; |
| + |
| + // There shouldn't be anything after the sequence. |
| + if (parser.HasMore()) |
|
Ryan Sleevi
2015/07/08 11:30:39
This is fine
|
| + return false; |
| + |
| + // Initialize parameters to their default values. |
| + DigestAlgorithm hash = DigestAlgorithm::Sha1; |
| + DigestAlgorithm mgf1_hash = DigestAlgorithm::Sha1; |
| + uint32_t salt_length = 20u; |
| + uint32_t trailer_field = 1u; |
| + |
| + bool has_field; |
| + der::Input field; |
| + |
| + // Parse hashAlgorithm [0] |
| + if (!params_parser.ReadOptionalTag(der::ContextSpecificConstructed(0), &field, |
| + &has_field)) { |
| + return false; |
| + } |
| + if (has_field && !ParseHashAlgorithm(field, &hash)) |
| + return false; |
| + |
| + // Parse maskGenAlgorithm [1] |
| + if (!params_parser.ReadOptionalTag(der::ContextSpecificConstructed(1), &field, |
| + &has_field)) { |
| + return false; |
| + } |
| + if (has_field && !ParseMaskGenAlgorithm(field, &mgf1_hash)) |
| + return false; |
| + |
| + // Parse saltLength [2] |
| + if (!ReadOptionalContextSpecificUint32(¶ms_parser, 2, &salt_length, |
| + &has_field)) { |
| + return false; |
| + } |
| + |
| + // Parse trailerField [3] |
| + if (!ReadOptionalContextSpecificUint32(¶ms_parser, 3, &trailer_field, |
| + &has_field)) { |
| + return false; |
| + } |
| + |
| + // The trailer field MUST be 1 per RFC 4055. |
| + if (trailer_field != 1) |
| + return false; |
| + |
| + // There must not be any unconsumed data left. |
| + if (params_parser.HasMore()) |
|
Ryan Sleevi
2015/07/08 11:30:39
The related discussion (re; RFC 5912, RFC 6025, re
eroman
2015/07/14 20:50:27
I have updated comments to reference RFC 5912, and
|
| + return false; |
| + |
| + out->AssignRsaPss(hash, mgf1_hash, salt_length); |
| + return true; |
| } |
| } // namespace |