Chromium Code Reviews| Index: net/cert/internal/verify_signed_data.cc |
| diff --git a/net/cert/internal/verify_signed_data.cc b/net/cert/internal/verify_signed_data.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..76addd7e72b14744cea19e2cd0dc3f3d33e2d294 |
| --- /dev/null |
| +++ b/net/cert/internal/verify_signed_data.cc |
| @@ -0,0 +1,185 @@ |
| +// Copyright 2015 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "net/cert/internal/verify_signed_data.h" |
| + |
| +#include "net/cert/internal/signature_algorithm.h" |
| + |
| +// TODO(eroman): There is no intention to implement this for non-OpenSSL. Remove |
| +// this branch once the migration is complete. This could have been done as a |
| +// conditional file (_openssl.cc) in the build file instead, but that is likely |
| +// not worth the effort at this point. |
| + |
| +#if !defined(USE_OPENSSL) |
| + |
| +namespace net { |
| + |
| +bool VerifySignedData(const SignatureAlgorithm& signature_algorithm, |
| + const der::Input& signed_data, |
| + const der::Input& signature_value, |
| + const der::Input& public_key) { |
| + // Not implemented |
| + return false; |
| +} |
| + |
| +} // namespace net |
| + |
| +#else |
| + |
| +#include <openssl/evp.h> |
| +#include <openssl/pkcs12.h> |
|
davidben
2015/07/07 18:03:56
What's this include for?
eroman
2015/07/07 18:34:43
I was apparently relying on it for its transitive
|
| +#include <openssl/rand.h> |
|
eroman
2015/07/07 18:34:43
This similarly was not needed, and removed.
|
| + |
| +#include "crypto/openssl_util.h" |
| +#include "crypto/scoped_openssl_types.h" |
| +#include "net/der/input.h" |
| +#include "net/der/parser.h" |
| + |
| +namespace net { |
| + |
| +namespace { |
| + |
| +// Creates a EVP_PKEY given the der-encoded bytes of the SPKI. This function |
| +// does not do any sort of validation of the resultant key. The consumer is |
| +// responsible for verifying the key's type and validity of its parameters. |
| +WARN_UNUSED_RESULT bool ImportUnverifiedPkeyFromSpki( |
| + const der::Input& spki, |
| + crypto::ScopedEVP_PKEY* pkey) { |
| + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
| + |
| + const uint8_t* ptr = spki.UnsafeData(); |
| + pkey->reset(d2i_PUBKEY(nullptr, &ptr, spki.Length())); |
|
davidben
2015/07/07 18:03:56
This needs x509.h. I'd rather we not use x509.h, b
eroman
2015/07/07 18:34:43
Thanks David!
So my take-away is:
(1) Switch t
davidben
2015/07/07 18:53:21
Oh, good point. I hadn't thought about WebCrypto.
|
| + if (!pkey->get() || ptr != spki.UnsafeData() + spki.Length()) |
| + return false; |
| + |
| + return true; |
| +} |
| + |
| +// Converts a DigestAlgorithm to an equivalent EVP_MD*. |
| +WARN_UNUSED_RESULT bool GetDigest(DigestAlgorithm digest, const EVP_MD** out) { |
| + switch (digest) { |
| + case DigestAlgorithm::Sha1: |
| + *out = EVP_sha1(); |
| + break; |
| + case DigestAlgorithm::Sha256: |
| + *out = EVP_sha256(); |
| + break; |
| + case DigestAlgorithm::Sha384: |
| + *out = EVP_sha384(); |
| + break; |
| + case DigestAlgorithm::Sha512: |
| + *out = EVP_sha512(); |
| + break; |
| + } |
| + |
| + return *out; |
|
davidben
2015/07/07 18:03:57
I believe MSVC will grump about this and want !!*o
eroman
2015/07/07 18:34:43
Done.
|
| +} |
| + |
| +// Sets the RSASSA-PSS parameters on |pctx|. Returns true on success. |
| +bool ApplyRsaPssOptions(const RsaPssParameters* params, EVP_PKEY_CTX* pctx) { |
| + // BoringSSL takes a signed int for the salt length, and interprets |
| + // negative values in a special manner. Make sure not to silently underflow. |
| + base::CheckedNumeric<int> salt_length_bytes_int(params->salt_length()); |
| + if (!salt_length_bytes_int.IsValid()) |
| + return false; |
| + |
| + const EVP_MD* mgf1_hash; |
| + if (!GetDigest(params->mgf1_hash(), &mgf1_hash)) |
| + return false; |
| + |
| + return (1 == EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) && |
| + 1 == EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, mgf1_hash) && |
| + 1 == EVP_PKEY_CTX_set_rsa_pss_saltlen( |
| + pctx, salt_length_bytes_int.ValueOrDie())); |
| +} |
| + |
| +bool RsaVerify(const SignatureAlgorithm& algorithm, |
| + const der::Input& signed_data, |
| + const der::Input& signature_value, |
| + EVP_PKEY* public_key) { |
| + // TODO(eroman): Is it necessary to verify that the public key's parameters |
| + // are congruent with the signature algorithm, or will BoringSSL already do |
| + // the needed checks? |
|
Ryan Sleevi
2015/07/07 14:07:31
David: Look here
davidben
2015/07/07 18:03:56
I don't believe BoringSSL is even passed the signa
eroman
2015/07/07 18:34:43
Thanks!
Indeed, seems the checks are in fact quit
|
| + |
| + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
| + crypto::ScopedEVP_MD_CTX ctx(EVP_MD_CTX_create()); |
| + EVP_PKEY_CTX* pctx = NULL; // Owned by |ctx|. |
| + |
| + const EVP_MD* digest; |
| + if (!GetDigest(algorithm.digest(), &digest)) |
| + return false; |
| + |
| + if (!EVP_DigestVerifyInit(ctx.get(), &pctx, digest, NULL, public_key)) |
| + return false; |
| + |
| + // Set the RSASSA-PSS specific options. |
| + if (algorithm.algorithm() == SignatureAlgorithmId::RsaPss && |
| + !ApplyRsaPssOptions(algorithm.ParamsForRsaPss(), pctx)) { |
| + return false; |
| + } |
| + |
| + if (!EVP_DigestVerifyUpdate(ctx.get(), signed_data.UnsafeData(), |
| + signed_data.Length())) { |
| + return false; |
| + } |
| + |
| + return 1 == EVP_DigestVerifyFinal(ctx.get(), signature_value.UnsafeData(), |
| + signature_value.Length()); |
| +} |
| + |
| +bool EcdsaVerify(const SignatureAlgorithm& algorithm, |
| + const der::Input& signed_data, |
| + const der::Input& signature_value, |
| + EVP_PKEY* public_key) { |
| + // TODO(eroman): Is it necessary to verify that the public key's parameters |
| + // are congruent with the signature algorithm, or will BoringSSL already do |
| + // the needed checks? |
|
Ryan Sleevi
2015/07/07 14:07:31
David: And here
davidben
2015/07/07 18:03:56
Ditto.
|
| + |
| + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
| + crypto::ScopedEVP_MD_CTX ctx(EVP_MD_CTX_create()); |
| + |
| + const EVP_MD* digest; |
| + if (!GetDigest(algorithm.digest(), &digest)) |
| + return false; |
| + |
| + if (!EVP_DigestVerifyInit(ctx.get(), NULL, digest, NULL, public_key) || |
| + !EVP_DigestVerifyUpdate(ctx.get(), signed_data.UnsafeData(), |
| + signed_data.Length())) { |
| + return false; |
| + } |
| + |
| + return 1 == EVP_DigestVerifyFinal(ctx.get(), signature_value.UnsafeData(), |
| + signature_value.Length()); |
| +} |
| + |
| +} // namespace |
| + |
| +bool VerifySignedData(const SignatureAlgorithm& signature_algorithm, |
| + const der::Input& signed_data, |
| + const der::Input& signature_value, |
| + const der::Input& public_key_spki) { |
| + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
| + |
| + // The validity checks on the public key are done in the algorithm-specific |
| + // verification functions. |
| + crypto::ScopedEVP_PKEY public_key; |
| + if (!ImportUnverifiedPkeyFromSpki(public_key_spki, &public_key)) |
| + return false; |
| + |
| + switch (signature_algorithm.algorithm()) { |
| + case SignatureAlgorithmId::RsaPkcs1: |
| + case SignatureAlgorithmId::RsaPss: |
| + return RsaVerify(signature_algorithm, signed_data, signature_value, |
| + public_key.get()); |
| + case SignatureAlgorithmId::Ecdsa: |
| + return EcdsaVerify(signature_algorithm, signed_data, signature_value, |
| + public_key.get()); |
| + } |
| + |
| + return false; // Unsupported algorithm. |
| +} |
| + |
| +} // namespace net |
| + |
| +#endif |