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. | |
|
Ryan Sleevi
2015/07/16 04:05:23
iOS is still affected by this at present.
eroman
2015/07/16 17:34:07
Would you prefer I designate this file _openssl an
| |
| 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(); | |
| 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; | |
| 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())); | |
| 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 | |
| 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 ::= { | |
| 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. | |
| 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. | |
| 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( | |
| 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())) | |
| 142 return false; | |
| 143 | |
| 144 return true; | |
| 145 } | |
| 146 | |
| 147 WARN_UNUSED_RESULT bool RsaVerify(const SignatureAlgorithm& algorithm, | |
| 148 const der::Input& signed_data, | |
| 149 const der::Input& signature_value, | |
| 150 const der::Input& public_key_spki) { | |
| 151 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
| 152 | |
| 153 // TODO(eroman): This does not enforce that the SPKI's algorithm matches the | |
| 154 // signature algorithm. For instance it would not | |
| 155 // forbid a key with PSS parameters being used | |
| 156 // with PKCS1 padding. | |
| 157 crypto::ScopedEVP_PKEY public_key; | |
| 158 if (!ParseRsaKeyFromSpki(public_key_spki, &public_key)) | |
| 159 return false; | |
| 160 | |
| 161 crypto::ScopedEVP_MD_CTX ctx(EVP_MD_CTX_create()); | |
| 162 EVP_PKEY_CTX* pctx = NULL; // Owned by |ctx|. | |
|
Ryan Sleevi
2015/07/16 04:05:23
nullptr?
| |
| 163 | |
| 164 const EVP_MD* digest; | |
| 165 if (!GetDigest(algorithm.digest(), &digest)) | |
| 166 return false; | |
| 167 | |
| 168 if (!EVP_DigestVerifyInit(ctx.get(), &pctx, digest, NULL, public_key.get())) | |
|
Ryan Sleevi
2015/07/16 04:05:24
nullptr
| |
| 169 return false; | |
| 170 | |
| 171 // Set the RSASSA-PSS specific options. | |
| 172 if (algorithm.algorithm() == SignatureAlgorithmId::RsaPss && | |
| 173 !ApplyRsaPssOptions(algorithm.ParamsForRsaPss(), pctx)) { | |
| 174 return false; | |
| 175 } | |
| 176 | |
| 177 if (!EVP_DigestVerifyUpdate(ctx.get(), signed_data.UnsafeData(), | |
| 178 signed_data.Length())) { | |
| 179 return false; | |
| 180 } | |
| 181 | |
| 182 return 1 == EVP_DigestVerifyFinal(ctx.get(), signature_value.UnsafeData(), | |
| 183 signature_value.Length()); | |
| 184 } | |
| 185 | |
| 186 // Returns true if the given curve is allowed for ECDSA. The input is a | |
| 187 // BoringSSL NID. | |
| 188 // | |
| 189 // TODO(eroman): Extract policy decisions such as allowed curves, hashes, RSA | |
| 190 // modulus size, to somewhere more central. | |
| 191 WARN_UNUSED_RESULT bool IsAllowedCurveName(int curve_nid) { | |
| 192 switch (curve_nid) { | |
| 193 case NID_X9_62_prime256v1: | |
| 194 case NID_secp384r1: | |
| 195 case NID_secp521r1: | |
| 196 return true; | |
| 197 } | |
| 198 return false; | |
| 199 } | |
| 200 | |
| 201 // Parses an EC public key from SPKI to an EVP_PKEY. | |
| 202 // | |
| 203 // Returns true on success. | |
| 204 // | |
| 205 // RFC 5912 describes all the ECDSA signature algorithms as requiring a public | |
| 206 // key of type "pk-ec": | |
| 207 // | |
| 208 // pk-ec PUBLIC-KEY ::= { | |
| 209 // IDENTIFIER id-ecPublicKey | |
| 210 // KEY ECPoint | |
| 211 // PARAMS TYPE ECParameters ARE required | |
| 212 // -- Private key format not in this module -- | |
| 213 // CERT-KEY-USAGE { digitalSignature, nonRepudiation, keyAgreement, | |
| 214 // keyCertSign, cRLSign } | |
| 215 // } | |
| 216 // | |
| 217 // Moreover RFC 5912 stipulates what curves are allowed. The ECParameters | |
| 218 // MUST NOT use an implicitCurve or specificCurve for PKIX: | |
| 219 // | |
| 220 // ECParameters ::= CHOICE { | |
| 221 // namedCurve CURVE.&id({NamedCurve}) | |
| 222 // -- implicitCurve NULL | |
| 223 // -- implicitCurve MUST NOT be used in PKIX | |
| 224 // -- specifiedCurve SpecifiedCurve | |
| 225 // -- specifiedCurve MUST NOT be used in PKIX | |
| 226 // -- Details for specifiedCurve can be found in [X9.62] | |
| 227 // -- Any future additions to this CHOICE should be coordinated | |
| 228 // -- with ANSI X.9. | |
| 229 // } | |
| 230 // -- If you need to be able to decode ANSI X.9 parameter structures, | |
| 231 // -- uncomment the implicitCurve and specifiedCurve above, and also | |
| 232 // -- uncomment the following: | |
| 233 // --(WITH COMPONENTS {namedCurve PRESENT}) | |
| 234 // | |
| 235 // The namedCurves are extensible. The ones described by RFC 5912 are: | |
| 236 // | |
| 237 // NamedCurve CURVE ::= { | |
| 238 // { ID secp192r1 } | { ID sect163k1 } | { ID sect163r2 } | | |
| 239 // { ID secp224r1 } | { ID sect233k1 } | { ID sect233r1 } | | |
| 240 // { ID secp256r1 } | { ID sect283k1 } | { ID sect283r1 } | | |
| 241 // { ID secp384r1 } | { ID sect409k1 } | { ID sect409r1 } | | |
| 242 // { ID secp521r1 } | { ID sect571k1 } | { ID sect571r1 }, | |
| 243 // ... -- Extensible | |
| 244 // } | |
| 245 WARN_UNUSED_RESULT bool ParseEcKeyFromSpki(const der::Input& public_key_spki, | |
| 246 crypto::ScopedEVP_PKEY* pkey) { | |
| 247 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
| 248 | |
| 249 const uint8_t* ptr = public_key_spki.UnsafeData(); | |
| 250 crypto::ScopedEC_KEY ec( | |
| 251 d2i_EC_PUBKEY(nullptr, &ptr, public_key_spki.Length())); | |
| 252 | |
| 253 if (!ec || ptr != public_key_spki.UnsafeData() + public_key_spki.Length()) | |
| 254 return false; | |
| 255 | |
| 256 // Enforce policy on allowed curves in case d2i_EC_PUBKEY() were to recognize | |
| 257 // and allow use of a weak curve. | |
| 258 if (!IsAllowedCurveName(EC_GROUP_get_curve_name(EC_KEY_get0_group(ec.get())))) | |
| 259 return false; | |
| 260 | |
| 261 // Create a corresponding EVP_PKEY. | |
| 262 pkey->reset(EVP_PKEY_new()); | |
| 263 if (!pkey || !EVP_PKEY_set1_EC_KEY(pkey->get(), ec.get())) | |
| 264 return false; | |
| 265 | |
| 266 return true; | |
| 267 } | |
| 268 | |
| 269 WARN_UNUSED_RESULT bool EcdsaVerify(const SignatureAlgorithm& algorithm, | |
| 270 const der::Input& signed_data, | |
| 271 const der::Input& signature_value, | |
| 272 const der::Input& public_key_spki) { | |
| 273 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
| 274 | |
| 275 crypto::ScopedEVP_PKEY public_key; | |
| 276 if (!ParseEcKeyFromSpki(public_key_spki, &public_key)) | |
| 277 return false; | |
| 278 | |
| 279 crypto::ScopedEVP_MD_CTX ctx(EVP_MD_CTX_create()); | |
|
Ryan Sleevi
2015/07/16 04:05:24
From here through 292, it seems the same as RsaVer
eroman
2015/07/16 17:34:07
The RSA version is somewhat different, in that it
eroman
2015/07/18 22:44:23
Done.
| |
| 280 | |
| 281 const EVP_MD* digest; | |
| 282 if (!GetDigest(algorithm.digest(), &digest)) | |
| 283 return false; | |
| 284 | |
| 285 if (!EVP_DigestVerifyInit(ctx.get(), NULL, digest, NULL, public_key.get()) || | |
|
Ryan Sleevi
2015/07/16 04:05:24
nullptr
eroman
2015/07/18 22:44:23
Done (throughout)
| |
| 286 !EVP_DigestVerifyUpdate(ctx.get(), signed_data.UnsafeData(), | |
| 287 signed_data.Length())) { | |
| 288 return false; | |
| 289 } | |
| 290 | |
| 291 return 1 == EVP_DigestVerifyFinal(ctx.get(), signature_value.UnsafeData(), | |
| 292 signature_value.Length()); | |
| 293 } | |
| 294 | |
| 295 } // namespace | |
| 296 | |
| 297 bool VerifySignedData(const SignatureAlgorithm& signature_algorithm, | |
| 298 const der::Input& signed_data, | |
| 299 const der::Input& signature_value, | |
| 300 const der::Input& public_key_spki) { | |
| 301 switch (signature_algorithm.algorithm()) { | |
| 302 case SignatureAlgorithmId::RsaPkcs1: | |
| 303 case SignatureAlgorithmId::RsaPss: | |
| 304 return RsaVerify(signature_algorithm, signed_data, signature_value, | |
| 305 public_key_spki); | |
| 306 case SignatureAlgorithmId::Ecdsa: | |
| 307 return EcdsaVerify(signature_algorithm, signed_data, signature_value, | |
| 308 public_key_spki); | |
| 309 } | |
| 310 | |
| 311 return false; // Unsupported algorithm. | |
| 312 } | |
| 313 | |
| 314 } // namespace net | |
| 315 | |
| 316 #endif | |
| OLD | NEW |