Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "net/cert/internal/verify_signed_data.h" | |
| 6 | |
| 7 #include "net/cert/internal/signature_algorithm.h" | |
| 8 | |
| 9 // TODO(eroman): There is no intention to implement this for non-OpenSSL. Remove | |
| 10 // this branch once the migration is complete. This could have been done as a | |
| 11 // conditional file (_openssl.cc) in the build file instead, but that is likely | |
| 12 // not worth the effort at this point. | |
| 13 | |
| 14 #if !defined(USE_OPENSSL) | |
| 15 | |
| 16 namespace net { | |
| 17 | |
| 18 bool VerifySignedData(const SignatureAlgorithm& signature_algorithm, | |
| 19 const der::Input& signed_data, | |
| 20 const der::Input& signature_value, | |
| 21 const der::Input& public_key) { | |
| 22 // Not implemented | |
| 23 return false; | |
| 24 } | |
| 25 | |
| 26 } // namespace net | |
| 27 | |
| 28 #else | |
| 29 | |
| 30 #include <openssl/evp.h> | |
| 31 #include <openssl/x509.h> | |
| 32 | |
| 33 #include "crypto/openssl_util.h" | |
| 34 #include "crypto/scoped_openssl_types.h" | |
| 35 #include "net/der/input.h" | |
| 36 #include "net/der/parser.h" | |
| 37 | |
| 38 namespace net { | |
| 39 | |
| 40 namespace { | |
| 41 | |
| 42 // Converts a DigestAlgorithm to an equivalent EVP_MD*. | |
| 43 WARN_UNUSED_RESULT bool GetDigest(DigestAlgorithm digest, const EVP_MD** out) { | |
| 44 switch (digest) { | |
| 45 case DigestAlgorithm::Sha1: | |
| 46 *out = EVP_sha1(); | |
|
davidben
2015/07/20 19:21:59
#include <openssl/digest.h>
eroman
2015/07/20 20:03:40
Done.
| |
| 47 break; | |
| 48 case DigestAlgorithm::Sha256: | |
| 49 *out = EVP_sha256(); | |
| 50 break; | |
| 51 case DigestAlgorithm::Sha384: | |
| 52 *out = EVP_sha384(); | |
| 53 break; | |
| 54 case DigestAlgorithm::Sha512: | |
| 55 *out = EVP_sha512(); | |
| 56 break; | |
| 57 } | |
| 58 | |
| 59 return *out != nullptr; | |
|
davidben
2015/07/20 19:21:59
This assumes that *out was initialized to nullptr
eroman
2015/07/20 20:03:40
Done. Good catch.
| |
| 60 } | |
| 61 | |
| 62 // Sets the RSASSA-PSS parameters on |pctx|. Returns true on success. | |
| 63 WARN_UNUSED_RESULT bool ApplyRsaPssOptions(const RsaPssParameters* params, | |
| 64 EVP_PKEY_CTX* pctx) { | |
| 65 // BoringSSL takes a signed int for the salt length, and interprets | |
| 66 // negative values in a special manner. Make sure not to silently underflow. | |
| 67 base::CheckedNumeric<int> salt_length_bytes_int(params->salt_length()); | |
| 68 if (!salt_length_bytes_int.IsValid()) | |
| 69 return false; | |
| 70 | |
| 71 const EVP_MD* mgf1_hash; | |
| 72 if (!GetDigest(params->mgf1_hash(), &mgf1_hash)) | |
| 73 return false; | |
| 74 | |
| 75 return (1 == EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) && | |
| 76 1 == EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, mgf1_hash) && | |
| 77 1 == EVP_PKEY_CTX_set_rsa_pss_saltlen( | |
| 78 pctx, salt_length_bytes_int.ValueOrDie())); | |
|
davidben
2015/07/20 19:21:59
In BoringSSL, these can only return 0 or 1.
eroman
2015/07/20 20:03:40
Done.
| |
| 79 } | |
| 80 | |
| 81 // Parses an RSA public key from SPKI to an EVP_PKEY. | |
| 82 // | |
| 83 // Returns true on success. | |
| 84 // | |
| 85 // There are two flavors of RSA public key that this function recognizes from | |
|
davidben
2015/07/20 19:21:59
Currently, d2i_RSA_PUBKEY also accepts 2.5.8.1.1.
eroman
2015/07/20 20:03:40
Acknowledged. Note that this is covered by my test
| |
| 86 // RFC 5912: | |
| 87 // | |
| 88 // pk-rsa PUBLIC-KEY ::= { | |
| 89 // IDENTIFIER rsaEncryption | |
| 90 // KEY RSAPublicKey | |
| 91 // PARAMS TYPE NULL ARE absent | |
| 92 // -- Private key format not in this module -- | |
| 93 // CERT-KEY-USAGE {digitalSignature, nonRepudiation, | |
| 94 // keyEncipherment, dataEncipherment, keyCertSign, cRLSign} | |
| 95 // } | |
| 96 // | |
| 97 // ... | |
| 98 // | |
| 99 // pk-rsaSSA-PSS PUBLIC-KEY ::= { | |
|
davidben
2015/07/20 19:21:59
d2i_RSA_PUBKEY doesn't actually recognize this one
eroman
2015/07/20 20:03:40
Acknowledged. Same response as above.
I will add
| |
| 100 // IDENTIFIER id-RSASSA-PSS | |
| 101 // KEY RSAPublicKey | |
| 102 // PARAMS TYPE RSASSA-PSS-params ARE optional | |
| 103 // -- Private key format not in this module -- | |
| 104 // CERT-KEY-USAGE { nonRepudiation, digitalSignature, | |
| 105 // keyCertSign, cRLSign } | |
| 106 // } | |
| 107 // | |
| 108 // Any RSA signature algorithm can accept a "pk-rsa" (rsaEncryption). However a | |
| 109 // "pk-rsaSSA-PSS" key is only accepted if the signature algorithm was for PSS | |
| 110 // mode: | |
| 111 // | |
| 112 // sa-rsaSSA-PSS SIGNATURE-ALGORITHM ::= { | |
| 113 // IDENTIFIER id-RSASSA-PSS | |
| 114 // PARAMS TYPE RSASSA-PSS-params ARE required | |
| 115 // HASHES { mda-sha1 | mda-sha224 | mda-sha256 | mda-sha384 | |
| 116 // | mda-sha512 } | |
| 117 // PUBLIC-KEYS { pk-rsa | pk-rsaSSA-PSS } | |
| 118 // SMIME-CAPS { IDENTIFIED BY id-RSASSA-PSS } | |
| 119 // } | |
| 120 // | |
| 121 // Moreover, if a "pk-rsaSSA-PSS" key was used and it optionally provided | |
| 122 // parameters for the algorithm, they must match those of the signature | |
| 123 // algorithm. | |
|
davidben
2015/07/20 19:21:59
This isn't implemented.
eroman
2015/07/20 20:03:40
Acknowledged.
| |
| 124 // | |
| 125 // COMPATIBILITY NOTE: RFC 5912 specifies that the algorithm parameters for | |
| 126 // pk-rsa (rsaEncryption) are absent. However in practice though it is common | |
| 127 // for these to be present and NULL. | |
|
davidben
2015/07/20 19:21:59
This one says it MUST be NULL.
https://tools.ietf.
eroman
2015/07/20 20:03:40
Correct, "ARE absent" in this case means MUST be a
davidben
2015/07/20 21:17:22
If RFC 3279 is treated authoritative, it's not eve
| |
| 128 WARN_UNUSED_RESULT bool ParseRsaKeyFromSpki(const der::Input& public_key_spki, | |
| 129 crypto::ScopedEVP_PKEY* pkey) { | |
| 130 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
| 131 | |
| 132 const uint8_t* ptr = public_key_spki.UnsafeData(); | |
| 133 crypto::ScopedRSA rsa( | |
|
davidben
2015/07/20 19:21:59
#include <openssl/rsa.h>
eroman
2015/07/20 20:03:40
Done.
| |
| 134 d2i_RSA_PUBKEY(nullptr, &ptr, public_key_spki.Length())); | |
| 135 | |
| 136 if (!rsa || ptr != public_key_spki.UnsafeData() + public_key_spki.Length()) | |
| 137 return false; | |
| 138 | |
| 139 // Create a corresponding EVP_PKEY. | |
| 140 pkey->reset(EVP_PKEY_new()); | |
| 141 if (!pkey || !EVP_PKEY_set1_RSA(pkey->get(), rsa.get())) | |
|
davidben
2015/07/20 19:21:59
This is slightly odd since d2i_RSA_PUBKEY will act
eroman
2015/07/20 20:03:40
Do you want me to go back to using d2i_PUBKEY() as
davidben
2015/07/20 21:17:22
*shrug* That's all d2i_RSA_PUBKEY is doing.
But i
eroman
2015/07/20 22:28:08
Done, changed to use d2i_PUBKEY().
davidben
2015/07/21 15:29:05
Fair enough. Though RSASSA-PSS SPKI isn't the only
| |
| 142 return false; | |
| 143 | |
| 144 return true; | |
| 145 } | |
| 146 | |
| 147 // Does signature verification using either RSA or ECDSA. | |
| 148 WARN_UNUSED_RESULT bool DoVerify(const SignatureAlgorithm& algorithm, | |
| 149 const der::Input& signed_data, | |
| 150 const der::Input& signature_value, | |
| 151 EVP_PKEY* public_key) { | |
| 152 DCHECK(algorithm.algorithm() == SignatureAlgorithmId::RsaPkcs1 || | |
| 153 algorithm.algorithm() == SignatureAlgorithmId::RsaPss || | |
| 154 algorithm.algorithm() == SignatureAlgorithmId::Ecdsa); | |
| 155 | |
| 156 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
| 157 | |
| 158 crypto::ScopedEVP_MD_CTX ctx(EVP_MD_CTX_create()); | |
| 159 EVP_PKEY_CTX* pctx = nullptr; // Owned by |ctx|. | |
| 160 | |
| 161 const EVP_MD* digest; | |
| 162 if (!GetDigest(algorithm.digest(), &digest)) | |
| 163 return false; | |
| 164 | |
| 165 if (!EVP_DigestVerifyInit(ctx.get(), &pctx, digest, nullptr, public_key)) | |
| 166 return false; | |
| 167 | |
| 168 // Set the RSASSA-PSS specific options. | |
| 169 if (algorithm.algorithm() == SignatureAlgorithmId::RsaPss && | |
| 170 !ApplyRsaPssOptions(algorithm.ParamsForRsaPss(), pctx)) { | |
| 171 return false; | |
| 172 } | |
| 173 | |
| 174 if (!EVP_DigestVerifyUpdate(ctx.get(), signed_data.UnsafeData(), | |
| 175 signed_data.Length())) { | |
| 176 return false; | |
| 177 } | |
| 178 | |
| 179 return 1 == EVP_DigestVerifyFinal(ctx.get(), signature_value.UnsafeData(), | |
| 180 signature_value.Length()); | |
| 181 } | |
| 182 | |
| 183 // Returns true if the given curve is allowed for ECDSA. The input is a | |
| 184 // BoringSSL NID. | |
| 185 // | |
| 186 // TODO(eroman): Extract policy decisions such as allowed curves, hashes, RSA | |
| 187 // modulus size, to somewhere more central. | |
| 188 WARN_UNUSED_RESULT bool IsAllowedCurveName(int curve_nid) { | |
| 189 switch (curve_nid) { | |
| 190 case NID_X9_62_prime256v1: | |
| 191 case NID_secp384r1: | |
| 192 case NID_secp521r1: | |
| 193 return true; | |
| 194 } | |
| 195 return false; | |
| 196 } | |
| 197 | |
| 198 // Parses an EC public key from SPKI to an EVP_PKEY. | |
| 199 // | |
| 200 // Returns true on success. | |
| 201 // | |
| 202 // RFC 5912 describes all the ECDSA signature algorithms as requiring a public | |
| 203 // key of type "pk-ec": | |
| 204 // | |
| 205 // pk-ec PUBLIC-KEY ::= { | |
| 206 // IDENTIFIER id-ecPublicKey | |
| 207 // KEY ECPoint | |
| 208 // PARAMS TYPE ECParameters ARE required | |
| 209 // -- Private key format not in this module -- | |
| 210 // CERT-KEY-USAGE { digitalSignature, nonRepudiation, keyAgreement, | |
| 211 // keyCertSign, cRLSign } | |
| 212 // } | |
| 213 // | |
| 214 // Moreover RFC 5912 stipulates what curves are allowed. The ECParameters | |
| 215 // MUST NOT use an implicitCurve or specificCurve for PKIX: | |
| 216 // | |
| 217 // ECParameters ::= CHOICE { | |
| 218 // namedCurve CURVE.&id({NamedCurve}) | |
| 219 // -- implicitCurve NULL | |
| 220 // -- implicitCurve MUST NOT be used in PKIX | |
| 221 // -- specifiedCurve SpecifiedCurve | |
| 222 // -- specifiedCurve MUST NOT be used in PKIX | |
| 223 // -- Details for specifiedCurve can be found in [X9.62] | |
| 224 // -- Any future additions to this CHOICE should be coordinated | |
| 225 // -- with ANSI X.9. | |
| 226 // } | |
| 227 // -- If you need to be able to decode ANSI X.9 parameter structures, | |
| 228 // -- uncomment the implicitCurve and specifiedCurve above, and also | |
| 229 // -- uncomment the following: | |
| 230 // --(WITH COMPONENTS {namedCurve PRESENT}) | |
| 231 // | |
| 232 // The namedCurves are extensible. The ones described by RFC 5912 are: | |
| 233 // | |
| 234 // NamedCurve CURVE ::= { | |
| 235 // { ID secp192r1 } | { ID sect163k1 } | { ID sect163r2 } | | |
| 236 // { ID secp224r1 } | { ID sect233k1 } | { ID sect233r1 } | | |
| 237 // { ID secp256r1 } | { ID sect283k1 } | { ID sect283r1 } | | |
| 238 // { ID secp384r1 } | { ID sect409k1 } | { ID sect409r1 } | | |
| 239 // { ID secp521r1 } | { ID sect571k1 } | { ID sect571r1 }, | |
| 240 // ... -- Extensible | |
| 241 // } | |
| 242 WARN_UNUSED_RESULT bool ParseEcKeyFromSpki(const der::Input& public_key_spki, | |
| 243 crypto::ScopedEVP_PKEY* pkey) { | |
| 244 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
| 245 | |
| 246 const uint8_t* ptr = public_key_spki.UnsafeData(); | |
| 247 crypto::ScopedEC_KEY ec( | |
| 248 d2i_EC_PUBKEY(nullptr, &ptr, public_key_spki.Length())); | |
| 249 | |
| 250 if (!ec || ptr != public_key_spki.UnsafeData() + public_key_spki.Length()) | |
| 251 return false; | |
| 252 | |
| 253 // Enforce policy on allowed curves in case d2i_EC_PUBKEY() were to recognize | |
| 254 // and allow use of a weak curve. | |
| 255 if (!IsAllowedCurveName(EC_GROUP_get_curve_name(EC_KEY_get0_group(ec.get())))) | |
|
davidben
2015/07/20 19:21:59
#include <openssl/ec.h>
#include <openssl/ec_key.h
eroman
2015/07/20 20:03:40
Done.
| |
| 256 return false; | |
| 257 | |
| 258 // Create a corresponding EVP_PKEY. | |
| 259 pkey->reset(EVP_PKEY_new()); | |
| 260 if (!pkey || !EVP_PKEY_set1_EC_KEY(pkey->get(), ec.get())) | |
|
davidben
2015/07/20 19:21:59
(Ditto about this dismantling an EVP_PKEY, only to
eroman
2015/07/20 22:28:08
Done.
| |
| 261 return false; | |
| 262 | |
| 263 return true; | |
| 264 } | |
| 265 | |
| 266 } // namespace | |
| 267 | |
| 268 bool VerifySignedData(const SignatureAlgorithm& signature_algorithm, | |
| 269 const der::Input& signed_data, | |
| 270 const der::Input& signature_value, | |
| 271 const der::Input& public_key_spki) { | |
| 272 crypto::ScopedEVP_PKEY public_key; | |
| 273 | |
| 274 // Parse the SPKI to an EVP_PKEY appropriate for the signature algorithm. | |
| 275 switch (signature_algorithm.algorithm()) { | |
| 276 case SignatureAlgorithmId::RsaPkcs1: | |
| 277 case SignatureAlgorithmId::RsaPss: | |
| 278 // TODO(eroman): This does not enforce that the SPKI's algorithm matches | |
| 279 // the signature algorithm. For instance it would not forbid a key with | |
| 280 // PSS parameters being used with PKCS1 padding. | |
| 281 if (!ParseRsaKeyFromSpki(public_key_spki, &public_key)) | |
| 282 return false; | |
| 283 break; | |
| 284 case SignatureAlgorithmId::Ecdsa: | |
| 285 if (!ParseEcKeyFromSpki(public_key_spki, &public_key)) | |
| 286 return false; | |
| 287 break; | |
| 288 } | |
| 289 | |
| 290 return DoVerify(signature_algorithm, signed_data, signature_value, | |
| 291 public_key.get()); | |
| 292 } | |
| 293 | |
| 294 } // namespace net | |
| 295 | |
| 296 #endif | |
| OLD | NEW |