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 | |
|
davidben
2015/07/21 16:26:27
Nit: NOTIMPLEMENTED()
eroman
2015/07/21 19:24:28
Done.
| |
| 23 return false; | |
| 24 } | |
| 25 | |
| 26 } // namespace net | |
| 27 | |
| 28 #else | |
| 29 | |
| 30 #include <openssl/digest.h> | |
| 31 #include <openssl/ec.h> | |
| 32 #include <openssl/ec_key.h> | |
| 33 #include <openssl/evp.h> | |
| 34 #include <openssl/rsa.h> | |
| 35 #include <openssl/x509.h> | |
| 36 | |
| 37 #include "crypto/openssl_util.h" | |
| 38 #include "crypto/scoped_openssl_types.h" | |
| 39 #include "net/der/input.h" | |
| 40 #include "net/der/parser.h" | |
| 41 | |
| 42 namespace net { | |
| 43 | |
| 44 namespace { | |
| 45 | |
| 46 // Converts a DigestAlgorithm to an equivalent EVP_MD*. | |
| 47 WARN_UNUSED_RESULT bool GetDigest(DigestAlgorithm digest, const EVP_MD** out) { | |
|
davidben
2015/07/21 16:26:27
Optional: Could make this return const EVP_MD* and
davidben
2015/07/21 16:26:27
#include "base/compiler_specific.h"
eroman
2015/07/21 19:24:28
My thinking is to consistently return "bool" since
eroman
2015/07/21 19:24:28
Done.
| |
| 48 *out = nullptr; | |
| 49 | |
| 50 switch (digest) { | |
| 51 case DigestAlgorithm::Sha1: | |
| 52 *out = EVP_sha1(); | |
| 53 break; | |
| 54 case DigestAlgorithm::Sha256: | |
| 55 *out = EVP_sha256(); | |
| 56 break; | |
| 57 case DigestAlgorithm::Sha384: | |
| 58 *out = EVP_sha384(); | |
| 59 break; | |
| 60 case DigestAlgorithm::Sha512: | |
| 61 *out = EVP_sha512(); | |
| 62 break; | |
| 63 } | |
| 64 | |
| 65 return *out != nullptr; | |
| 66 } | |
| 67 | |
| 68 // Sets the RSASSA-PSS parameters on |pctx|. Returns true on success. | |
| 69 WARN_UNUSED_RESULT bool ApplyRsaPssOptions(const RsaPssParameters* params, | |
| 70 EVP_PKEY_CTX* pctx) { | |
| 71 // BoringSSL takes a signed int for the salt length, and interprets | |
| 72 // negative values in a special manner. Make sure not to silently underflow. | |
| 73 base::CheckedNumeric<int> salt_length_bytes_int(params->salt_length()); | |
| 74 if (!salt_length_bytes_int.IsValid()) | |
| 75 return false; | |
| 76 | |
| 77 const EVP_MD* mgf1_hash; | |
| 78 if (!GetDigest(params->mgf1_hash(), &mgf1_hash)) | |
| 79 return false; | |
| 80 | |
| 81 return (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) && | |
|
davidben
2015/07/21 16:26:27
Nit: Unnecessary parentheses. (Chromium's not very
eroman
2015/07/21 19:24:28
Done.
| |
| 82 EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, mgf1_hash) && | |
| 83 EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, | |
| 84 salt_length_bytes_int.ValueOrDie())); | |
| 85 } | |
| 86 | |
| 87 WARN_UNUSED_RESULT bool ImportPkeyFromSpki(const der::Input& spki, | |
| 88 int expected_pkey_id, | |
| 89 crypto::ScopedEVP_PKEY* pkey) { | |
|
davidben
2015/07/21 16:26:27
Optional: Could return crypto::ScopedEVP_PKEY rath
eroman
2015/07/21 19:24:28
My preference is for a bool, as it will likely be
| |
| 90 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
| 91 | |
| 92 const uint8_t* ptr = spki.UnsafeData(); | |
| 93 pkey->reset(d2i_PUBKEY(nullptr, &ptr, spki.Length())); | |
|
davidben
2015/07/21 16:26:27
This probably needs a TODO to fix the strictness (
eroman
2015/07/21 19:24:28
Done.
| |
| 94 if (!pkey->get() || ptr != spki.UnsafeData() + spki.Length() || | |
| 95 EVP_PKEY_id(pkey->get()) != expected_pkey_id) { | |
| 96 pkey->reset(); | |
| 97 return false; | |
| 98 } | |
| 99 | |
| 100 return true; | |
| 101 } | |
| 102 | |
| 103 // Parses an RSA public key from SPKI to an EVP_PKEY. | |
| 104 // | |
| 105 // Returns true on success. | |
| 106 // | |
| 107 // There are two flavors of RSA public key that this function should recognize | |
| 108 // from RFC 5912 (however note that pk-rsaSSA-PSS is not supported in the | |
| 109 // current implementation). | |
| 110 // TODO(eroman): Support id-RSASSA-PSS and its associated parameters. | |
| 111 // | |
| 112 // pk-rsa PUBLIC-KEY ::= { | |
| 113 // IDENTIFIER rsaEncryption | |
| 114 // KEY RSAPublicKey | |
| 115 // PARAMS TYPE NULL ARE absent | |
| 116 // -- Private key format not in this module -- | |
| 117 // CERT-KEY-USAGE {digitalSignature, nonRepudiation, | |
| 118 // keyEncipherment, dataEncipherment, keyCertSign, cRLSign} | |
| 119 // } | |
| 120 // | |
| 121 // ... | |
| 122 // | |
| 123 // pk-rsaSSA-PSS PUBLIC-KEY ::= { | |
| 124 // IDENTIFIER id-RSASSA-PSS | |
| 125 // KEY RSAPublicKey | |
| 126 // PARAMS TYPE RSASSA-PSS-params ARE optional | |
| 127 // -- Private key format not in this module -- | |
| 128 // CERT-KEY-USAGE { nonRepudiation, digitalSignature, | |
| 129 // keyCertSign, cRLSign } | |
| 130 // } | |
| 131 // | |
| 132 // Any RSA signature algorithm can accept a "pk-rsa" (rsaEncryption). However a | |
| 133 // "pk-rsaSSA-PSS" key is only accepted if the signature algorithm was for PSS | |
| 134 // mode: | |
| 135 // | |
| 136 // sa-rsaSSA-PSS SIGNATURE-ALGORITHM ::= { | |
| 137 // IDENTIFIER id-RSASSA-PSS | |
| 138 // PARAMS TYPE RSASSA-PSS-params ARE required | |
| 139 // HASHES { mda-sha1 | mda-sha224 | mda-sha256 | mda-sha384 | |
| 140 // | mda-sha512 } | |
| 141 // PUBLIC-KEYS { pk-rsa | pk-rsaSSA-PSS } | |
| 142 // SMIME-CAPS { IDENTIFIED BY id-RSASSA-PSS } | |
| 143 // } | |
| 144 // | |
| 145 // Moreover, if a "pk-rsaSSA-PSS" key was used and it optionally provided | |
| 146 // parameters for the algorithm, they must match those of the signature | |
| 147 // algorithm. | |
| 148 // | |
| 149 // COMPATIBILITY NOTE: RFC 5912 specifies that the algorithm parameters for | |
| 150 // pk-rsa (rsaEncryption) are absent. However this is in contradiction with | |
| 151 // RFC 3279 section 2.3.1 which says it should be NULL: | |
|
davidben
2015/07/21 16:26:27
Nit: should -> MUST
(I.e. it's not that RFC 3279
eroman
2015/07/21 19:24:28
Done.
| |
| 152 // | |
| 153 // The rsaEncryption OID is intended to be used in the algorithm field | |
| 154 // of a value of type AlgorithmIdentifier. The parameters field MUST | |
| 155 // have ASN.1 type NULL for this algorithm identifier. | |
| 156 // | |
| 157 // In practice implementations follow the rules from RFC 3279 and not RFC 5912. | |
| 158 WARN_UNUSED_RESULT bool ParseRsaKeyFromSpki(const der::Input& public_key_spki, | |
| 159 crypto::ScopedEVP_PKEY* pkey) { | |
|
davidben
2015/07/21 16:26:27
Optional: Could return crypto::ScopedEVP_PKEY rath
eroman
2015/07/21 19:24:28
Same response as earlier.
| |
| 160 return ImportPkeyFromSpki(public_key_spki, EVP_PKEY_RSA, pkey); | |
| 161 } | |
| 162 | |
| 163 // Does signature verification using either RSA or ECDSA. | |
| 164 WARN_UNUSED_RESULT bool DoVerify(const SignatureAlgorithm& algorithm, | |
| 165 const der::Input& signed_data, | |
| 166 const der::Input& signature_value, | |
| 167 EVP_PKEY* public_key) { | |
| 168 DCHECK(algorithm.algorithm() == SignatureAlgorithmId::RsaPkcs1 || | |
|
davidben
2015/07/21 16:26:27
#include "base/logging.h"
eroman
2015/07/21 19:24:28
Done.
| |
| 169 algorithm.algorithm() == SignatureAlgorithmId::RsaPss || | |
| 170 algorithm.algorithm() == SignatureAlgorithmId::Ecdsa); | |
| 171 | |
| 172 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
| 173 | |
| 174 crypto::ScopedEVP_MD_CTX ctx(EVP_MD_CTX_create()); | |
| 175 EVP_PKEY_CTX* pctx = nullptr; // Owned by |ctx|. | |
| 176 | |
| 177 const EVP_MD* digest; | |
| 178 if (!GetDigest(algorithm.digest(), &digest)) | |
| 179 return false; | |
| 180 | |
| 181 if (!EVP_DigestVerifyInit(ctx.get(), &pctx, digest, nullptr, public_key)) | |
| 182 return false; | |
| 183 | |
| 184 // Set the RSASSA-PSS specific options. | |
| 185 if (algorithm.algorithm() == SignatureAlgorithmId::RsaPss && | |
| 186 !ApplyRsaPssOptions(algorithm.ParamsForRsaPss(), pctx)) { | |
| 187 return false; | |
| 188 } | |
| 189 | |
| 190 if (!EVP_DigestVerifyUpdate(ctx.get(), signed_data.UnsafeData(), | |
| 191 signed_data.Length())) { | |
| 192 return false; | |
| 193 } | |
| 194 | |
| 195 return EVP_DigestVerifyFinal(ctx.get(), signature_value.UnsafeData(), | |
| 196 signature_value.Length()); | |
| 197 } | |
| 198 | |
| 199 // Returns true if the given curve is allowed for ECDSA. The input is a | |
| 200 // BoringSSL NID. | |
| 201 // | |
| 202 // TODO(eroman): Extract policy decisions such as allowed curves, hashes, RSA | |
| 203 // modulus size, to somewhere more central. | |
| 204 WARN_UNUSED_RESULT bool IsAllowedCurveName(int curve_nid) { | |
| 205 switch (curve_nid) { | |
| 206 case NID_X9_62_prime256v1: | |
| 207 case NID_secp384r1: | |
| 208 case NID_secp521r1: | |
| 209 return true; | |
| 210 } | |
| 211 return false; | |
| 212 } | |
| 213 | |
| 214 // Parses an EC public key from SPKI to an EVP_PKEY. | |
| 215 // | |
| 216 // Returns true on success. | |
| 217 // | |
| 218 // RFC 5912 describes all the ECDSA signature algorithms as requiring a public | |
| 219 // key of type "pk-ec": | |
| 220 // | |
| 221 // pk-ec PUBLIC-KEY ::= { | |
| 222 // IDENTIFIER id-ecPublicKey | |
| 223 // KEY ECPoint | |
| 224 // PARAMS TYPE ECParameters ARE required | |
| 225 // -- Private key format not in this module -- | |
| 226 // CERT-KEY-USAGE { digitalSignature, nonRepudiation, keyAgreement, | |
| 227 // keyCertSign, cRLSign } | |
| 228 // } | |
| 229 // | |
| 230 // Moreover RFC 5912 stipulates what curves are allowed. The ECParameters | |
| 231 // MUST NOT use an implicitCurve or specificCurve for PKIX: | |
| 232 // | |
| 233 // ECParameters ::= CHOICE { | |
| 234 // namedCurve CURVE.&id({NamedCurve}) | |
| 235 // -- implicitCurve NULL | |
| 236 // -- implicitCurve MUST NOT be used in PKIX | |
| 237 // -- specifiedCurve SpecifiedCurve | |
| 238 // -- specifiedCurve MUST NOT be used in PKIX | |
| 239 // -- Details for specifiedCurve can be found in [X9.62] | |
| 240 // -- Any future additions to this CHOICE should be coordinated | |
| 241 // -- with ANSI X.9. | |
| 242 // } | |
| 243 // -- If you need to be able to decode ANSI X.9 parameter structures, | |
| 244 // -- uncomment the implicitCurve and specifiedCurve above, and also | |
| 245 // -- uncomment the following: | |
| 246 // --(WITH COMPONENTS {namedCurve PRESENT}) | |
| 247 // | |
| 248 // The namedCurves are extensible. The ones described by RFC 5912 are: | |
| 249 // | |
| 250 // NamedCurve CURVE ::= { | |
| 251 // { ID secp192r1 } | { ID sect163k1 } | { ID sect163r2 } | | |
| 252 // { ID secp224r1 } | { ID sect233k1 } | { ID sect233r1 } | | |
| 253 // { ID secp256r1 } | { ID sect283k1 } | { ID sect283r1 } | | |
| 254 // { ID secp384r1 } | { ID sect409k1 } | { ID sect409r1 } | | |
| 255 // { ID secp521r1 } | { ID sect571k1 } | { ID sect571r1 }, | |
| 256 // ... -- Extensible | |
| 257 // } | |
| 258 WARN_UNUSED_RESULT bool ParseEcKeyFromSpki(const der::Input& public_key_spki, | |
| 259 crypto::ScopedEVP_PKEY* pkey) { | |
|
davidben
2015/07/21 16:26:27
Optional: Could return crypto::ScopedEVP_PKEY rath
eroman
2015/07/21 19:24:28
Same response as earlier.
| |
| 260 if (!ImportPkeyFromSpki(public_key_spki, EVP_PKEY_EC, pkey)) | |
| 261 return false; | |
| 262 | |
| 263 // Enforce policy on allowed curves in case ImportPkeyFromSpki() were to | |
| 264 // recognize and allow use of a weak curve. | |
| 265 crypto::ScopedEC_KEY ec(EVP_PKEY_get1_EC_KEY(pkey->get())); | |
| 266 if (!ec.get()) | |
| 267 return false; // Unexpected. | |
| 268 | |
| 269 int curve_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec.get())); | |
| 270 return IsAllowedCurveName(curve_nid); | |
| 271 } | |
| 272 | |
| 273 } // namespace | |
| 274 | |
| 275 bool VerifySignedData(const SignatureAlgorithm& signature_algorithm, | |
| 276 const der::Input& signed_data, | |
| 277 const der::Input& signature_value, | |
| 278 const der::Input& public_key_spki) { | |
| 279 crypto::ScopedEVP_PKEY public_key; | |
| 280 | |
| 281 // Parse the SPKI to an EVP_PKEY appropriate for the signature algorithm. | |
| 282 switch (signature_algorithm.algorithm()) { | |
| 283 case SignatureAlgorithmId::RsaPkcs1: | |
| 284 case SignatureAlgorithmId::RsaPss: | |
| 285 if (!ParseRsaKeyFromSpki(public_key_spki, &public_key)) | |
| 286 return false; | |
| 287 break; | |
| 288 case SignatureAlgorithmId::Ecdsa: | |
| 289 if (!ParseEcKeyFromSpki(public_key_spki, &public_key)) | |
| 290 return false; | |
| 291 break; | |
| 292 } | |
| 293 | |
| 294 return DoVerify(signature_algorithm, signed_data, signature_value, | |
| 295 public_key.get()); | |
| 296 } | |
| 297 | |
| 298 } // namespace net | |
| 299 | |
| 300 #endif | |
| OLD | NEW |