| // 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 "base/compiler_specific.h" |
| #include "base/logging.h" |
| #include "base/numerics/safe_math.h" |
| #include "crypto/openssl_util.h" |
| #include "net/cert/internal/cert_errors.h" |
| #include "net/cert/internal/signature_algorithm.h" |
| #include "net/cert/internal/signature_policy.h" |
| #include "net/der/input.h" |
| #include "net/der/parse_values.h" |
| #include "net/der/parser.h" |
| #include "third_party/boringssl/src/include/openssl/bn.h" |
| #include "third_party/boringssl/src/include/openssl/bytestring.h" |
| #include "third_party/boringssl/src/include/openssl/digest.h" |
| #include "third_party/boringssl/src/include/openssl/ec.h" |
| #include "third_party/boringssl/src/include/openssl/ec_key.h" |
| #include "third_party/boringssl/src/include/openssl/evp.h" |
| #include "third_party/boringssl/src/include/openssl/rsa.h" |
| |
| namespace net { |
| |
| namespace { |
| |
| DEFINE_CERT_ERROR_ID(kUnacceptableSignatureAlgorithm, |
| "Unacceptable signature algorithm"); |
| DEFINE_CERT_ERROR_ID(kUnacceptableRsaModulusLength, |
| "Unacceptable modulus length for RSA key"); |
| DEFINE_CERT_ERROR_ID(kUnacceptableEcdsaCurve, |
| "Unacceptable curve for ECDSA key"); |
| DEFINE_CERT_ERROR_ID(kSignatureVerificationFailed, |
| "Signature verification failed"); |
| |
| // Converts a DigestAlgorithm to an equivalent EVP_MD*. |
| WARN_UNUSED_RESULT bool GetDigest(DigestAlgorithm digest, const EVP_MD** out) { |
| *out = nullptr; |
| |
| switch (digest) { |
| case DigestAlgorithm::Md2: |
| case DigestAlgorithm::Md4: |
| case DigestAlgorithm::Md5: |
| // Unsupported. |
| break; |
| 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 != nullptr; |
| } |
| |
| // Sets the RSASSA-PSS parameters on |pctx|. Returns true on success. |
| WARN_UNUSED_RESULT 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 EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) && |
| EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, mgf1_hash) && |
| EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, |
| salt_length_bytes_int.ValueOrDie()); |
| } |
| |
| // TODO(eroman): This function is not strict enough. It accepts BER, other RSA |
| // OIDs, and does not check id-rsaEncryption parameters. |
| // See https://crbug.com/522228 and https://crbug.com/522232 |
| WARN_UNUSED_RESULT bool ImportPkeyFromSpki(const der::Input& spki, |
| int expected_pkey_id, |
| bssl::UniquePtr<EVP_PKEY>* pkey) { |
| crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
| |
| CBS cbs; |
| CBS_init(&cbs, spki.UnsafeData(), spki.Length()); |
| pkey->reset(EVP_parse_public_key(&cbs)); |
| if (!*pkey || CBS_len(&cbs) != 0 || |
| EVP_PKEY_id(pkey->get()) != expected_pkey_id) { |
| pkey->reset(); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Parses an RSA public key from SPKI to an EVP_PKEY. |
| // |
| // Returns true on success. |
| // |
| // There are two flavors of RSA public key that this function should recognize |
| // from RFC 5912 (however note that pk-rsaSSA-PSS is not supported in the |
| // current implementation). |
| // TODO(eroman): Support id-RSASSA-PSS and its associated parameters. See |
| // https://crbug.com/522232 |
| // |
| // pk-rsa PUBLIC-KEY ::= { |
| // IDENTIFIER rsaEncryption |
| // KEY RSAPublicKey |
| // PARAMS TYPE NULL ARE absent |
| // -- Private key format not in this module -- |
| // CERT-KEY-USAGE {digitalSignature, nonRepudiation, |
| // keyEncipherment, dataEncipherment, keyCertSign, cRLSign} |
| // } |
| // |
| // ... |
| // |
| // pk-rsaSSA-PSS PUBLIC-KEY ::= { |
| // IDENTIFIER id-RSASSA-PSS |
| // KEY RSAPublicKey |
| // PARAMS TYPE RSASSA-PSS-params ARE optional |
| // -- Private key format not in this module -- |
| // CERT-KEY-USAGE { nonRepudiation, digitalSignature, |
| // keyCertSign, cRLSign } |
| // } |
| // |
| // Any RSA signature algorithm can accept a "pk-rsa" (rsaEncryption). However a |
| // "pk-rsaSSA-PSS" key is only accepted if the signature algorithm was for PSS |
| // mode: |
| // |
| // sa-rsaSSA-PSS SIGNATURE-ALGORITHM ::= { |
| // IDENTIFIER id-RSASSA-PSS |
| // PARAMS TYPE RSASSA-PSS-params ARE required |
| // HASHES { mda-sha1 | mda-sha224 | mda-sha256 | mda-sha384 |
| // | mda-sha512 } |
| // PUBLIC-KEYS { pk-rsa | pk-rsaSSA-PSS } |
| // SMIME-CAPS { IDENTIFIED BY id-RSASSA-PSS } |
| // } |
| // |
| // Moreover, if a "pk-rsaSSA-PSS" key was used and it optionally provided |
| // parameters for the algorithm, they must match those of the signature |
| // algorithm. |
| // |
| // COMPATIBILITY NOTE: RFC 5912 and RFC 3279 are in disagreement on the value |
| // of parameters for rsaEncryption. Whereas RFC 5912 says they must be absent, |
| // RFC 3279 says they must be NULL: |
| // |
| // The rsaEncryption OID is intended to be used in the algorithm field |
| // of a value of type AlgorithmIdentifier. The parameters field MUST |
| // have ASN.1 type NULL for this algorithm identifier. |
| // |
| // Following RFC 3279 in this case. |
| WARN_UNUSED_RESULT bool ParseRsaKeyFromSpki(const der::Input& public_key_spki, |
| bssl::UniquePtr<EVP_PKEY>* pkey, |
| const SignaturePolicy* policy, |
| CertErrors* errors) { |
| // TODO(crbug.com/634443): Add more specific errors. |
| if (!ImportPkeyFromSpki(public_key_spki, EVP_PKEY_RSA, pkey)) |
| return false; |
| |
| // Extract the modulus length from the key. |
| RSA* rsa = EVP_PKEY_get0_RSA(pkey->get()); |
| if (!rsa) |
| return false; |
| unsigned int modulus_length_bits = BN_num_bits(rsa->n); |
| |
| if (!policy->IsAcceptableModulusLengthForRsa(modulus_length_bits, errors)) { |
| errors->AddError(kUnacceptableRsaModulusLength); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Does signature verification using either RSA or ECDSA. |
| WARN_UNUSED_RESULT bool DoVerify(const SignatureAlgorithm& algorithm, |
| const der::Input& signed_data, |
| const der::BitString& signature_value, |
| EVP_PKEY* public_key) { |
| DCHECK(algorithm.algorithm() == SignatureAlgorithmId::RsaPkcs1 || |
| algorithm.algorithm() == SignatureAlgorithmId::RsaPss || |
| algorithm.algorithm() == SignatureAlgorithmId::Ecdsa); |
| |
| // For the supported algorithms the signature value must be a whole |
| // number of bytes. |
| if (signature_value.unused_bits() != 0) |
| return false; |
| const der::Input& signature_value_bytes = signature_value.bytes(); |
| |
| crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
| |
| bssl::ScopedEVP_MD_CTX ctx; |
| EVP_PKEY_CTX* pctx = nullptr; // Owned by |ctx|. |
| |
| const EVP_MD* digest; |
| if (!GetDigest(algorithm.digest(), &digest)) |
| return false; |
| |
| if (!EVP_DigestVerifyInit(ctx.get(), &pctx, digest, nullptr, 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_bytes.UnsafeData(), |
| signature_value_bytes.Length()); |
| } |
| |
| // Parses an EC public key from SPKI to an EVP_PKEY. |
| // |
| // Returns true on success. |
| // |
| // RFC 5912 describes all the ECDSA signature algorithms as requiring a public |
| // key of type "pk-ec": |
| // |
| // pk-ec PUBLIC-KEY ::= { |
| // IDENTIFIER id-ecPublicKey |
| // KEY ECPoint |
| // PARAMS TYPE ECParameters ARE required |
| // -- Private key format not in this module -- |
| // CERT-KEY-USAGE { digitalSignature, nonRepudiation, keyAgreement, |
| // keyCertSign, cRLSign } |
| // } |
| // |
| // Moreover RFC 5912 stipulates what curves are allowed. The ECParameters |
| // MUST NOT use an implicitCurve or specificCurve for PKIX: |
| // |
| // ECParameters ::= CHOICE { |
| // namedCurve CURVE.&id({NamedCurve}) |
| // -- implicitCurve NULL |
| // -- implicitCurve MUST NOT be used in PKIX |
| // -- specifiedCurve SpecifiedCurve |
| // -- specifiedCurve MUST NOT be used in PKIX |
| // -- Details for specifiedCurve can be found in [X9.62] |
| // -- Any future additions to this CHOICE should be coordinated |
| // -- with ANSI X.9. |
| // } |
| // -- If you need to be able to decode ANSI X.9 parameter structures, |
| // -- uncomment the implicitCurve and specifiedCurve above, and also |
| // -- uncomment the following: |
| // --(WITH COMPONENTS {namedCurve PRESENT}) |
| // |
| // The namedCurves are extensible. The ones described by RFC 5912 are: |
| // |
| // NamedCurve CURVE ::= { |
| // { ID secp192r1 } | { ID sect163k1 } | { ID sect163r2 } | |
| // { ID secp224r1 } | { ID sect233k1 } | { ID sect233r1 } | |
| // { ID secp256r1 } | { ID sect283k1 } | { ID sect283r1 } | |
| // { ID secp384r1 } | { ID sect409k1 } | { ID sect409r1 } | |
| // { ID secp521r1 } | { ID sect571k1 } | { ID sect571r1 }, |
| // ... -- Extensible |
| // } |
| WARN_UNUSED_RESULT bool ParseEcKeyFromSpki(const der::Input& public_key_spki, |
| bssl::UniquePtr<EVP_PKEY>* pkey, |
| const SignaturePolicy* policy, |
| CertErrors* errors) { |
| // TODO(crbug.com/634443): Add more specific errors. |
| if (!ImportPkeyFromSpki(public_key_spki, EVP_PKEY_EC, pkey)) |
| return false; |
| |
| // Extract the curve name. |
| EC_KEY* ec = EVP_PKEY_get0_EC_KEY(pkey->get()); |
| if (!ec) |
| return false; // Unexpected. |
| int curve_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); |
| |
| if (!policy->IsAcceptableCurveForEcdsa(curve_nid, errors)) { |
| errors->AddError(kUnacceptableEcdsaCurve); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // namespace |
| |
| bool VerifySignedData(const SignatureAlgorithm& signature_algorithm, |
| const der::Input& signed_data, |
| const der::BitString& signature_value, |
| const der::Input& public_key_spki, |
| const SignaturePolicy* policy, |
| CertErrors* errors) { |
| if (!policy->IsAcceptableSignatureAlgorithm(signature_algorithm, errors)) { |
| // TODO(crbug.com/634443): Include the DER for the AlgorithmIdentifier |
| errors->AddError(kUnacceptableSignatureAlgorithm); |
| return false; |
| } |
| |
| bssl::UniquePtr<EVP_PKEY> public_key; |
| |
| // Parse the SPKI to an EVP_PKEY appropriate for the signature algorithm. |
| switch (signature_algorithm.algorithm()) { |
| case SignatureAlgorithmId::RsaPkcs1: |
| case SignatureAlgorithmId::RsaPss: |
| if (!ParseRsaKeyFromSpki(public_key_spki, &public_key, policy, errors)) |
| return false; |
| break; |
| case SignatureAlgorithmId::Ecdsa: |
| if (!ParseEcKeyFromSpki(public_key_spki, &public_key, policy, errors)) |
| return false; |
| break; |
| } |
| |
| if (!DoVerify(signature_algorithm, signed_data, signature_value, |
| public_key.get())) { |
| errors->AddError(kSignatureVerificationFailed); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // namespace net |