blob: 28c8b7d273527555ae87aa3b6ed0842f06369cd7 [file] [log] [blame]
// 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