291 lines
10 KiB
C++
291 lines
10 KiB
C++
// Copyright 2015 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "net/cert/pki/verify_signed_data.h"
|
|
|
|
#include "crypto/openssl_util.h"
|
|
#include "net/cert/pki/cert_errors.h"
|
|
#include "net/cert/pki/signature_algorithm.h"
|
|
#include "net/cert/pki/signature_verify_cache.h"
|
|
#include "net/der/input.h"
|
|
#include "net/der/parse_values.h"
|
|
#include "net/der/parser.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/evp.h"
|
|
#include "third_party/boringssl/src/include/openssl/rsa.h"
|
|
#include "third_party/boringssl/src/include/openssl/sha.h"
|
|
|
|
namespace net {
|
|
|
|
namespace {
|
|
|
|
bool SHA256UpdateWithLengthPrefixedData(SHA256_CTX* s_ctx,
|
|
const uint8_t* data,
|
|
uint64_t length) {
|
|
return (SHA256_Update(s_ctx, reinterpret_cast<uint8_t*>(&length),
|
|
sizeof(length)) &&
|
|
SHA256_Update(s_ctx, data, length));
|
|
}
|
|
|
|
// Increase to make incompatible changes in the computation of the
|
|
// cache key.
|
|
constexpr uint32_t VerifyCacheKeyVersion = 1;
|
|
|
|
std::string SignatureVerifyCacheKey(std::string_view algorithm_name,
|
|
const der::Input& signed_data,
|
|
const der::Input& signature_value_bytes,
|
|
EVP_PKEY* public_key) {
|
|
SHA256_CTX s_ctx;
|
|
bssl::ScopedCBB public_key_cbb;
|
|
uint8_t digest[SHA256_DIGEST_LENGTH];
|
|
uint32_t version = VerifyCacheKeyVersion;
|
|
if (CBB_init(public_key_cbb.get(), 128) &&
|
|
EVP_marshal_public_key(public_key_cbb.get(), public_key) &&
|
|
SHA256_Init(&s_ctx) &&
|
|
SHA256_Update(&s_ctx, reinterpret_cast<uint8_t*>(&version),
|
|
sizeof(version)) &&
|
|
SHA256UpdateWithLengthPrefixedData(
|
|
&s_ctx, reinterpret_cast<const uint8_t*>(algorithm_name.data()),
|
|
algorithm_name.length()) &&
|
|
SHA256UpdateWithLengthPrefixedData(&s_ctx, CBB_data(public_key_cbb.get()),
|
|
CBB_len(public_key_cbb.get())) &&
|
|
SHA256UpdateWithLengthPrefixedData(&s_ctx,
|
|
signature_value_bytes.UnsafeData(),
|
|
signature_value_bytes.Length()) &&
|
|
SHA256UpdateWithLengthPrefixedData(&s_ctx, signed_data.UnsafeData(),
|
|
signed_data.Length()) &&
|
|
SHA256_Final(digest, &s_ctx)) {
|
|
return std::string(reinterpret_cast<char*>(digest), sizeof(digest));
|
|
}
|
|
return std::string();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// Parses an RSA public key or EC public key from SPKI to an EVP_PKEY. Returns
|
|
// true on success.
|
|
//
|
|
// This function only recognizes the "pk-rsa" (rsaEncryption) flavor of RSA
|
|
// public key from RFC 5912.
|
|
//
|
|
// 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}
|
|
// }
|
|
//
|
|
// 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.
|
|
//
|
|
// In the case of parsing EC keys, 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
|
|
// }
|
|
bool ParsePublicKey(const der::Input& public_key_spki,
|
|
bssl::UniquePtr<EVP_PKEY>* public_key) {
|
|
// Parse the SPKI to an EVP_PKEY.
|
|
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
|
|
|
|
CBS cbs;
|
|
CBS_init(&cbs, public_key_spki.UnsafeData(), public_key_spki.Length());
|
|
public_key->reset(EVP_parse_public_key(&cbs));
|
|
if (!*public_key || CBS_len(&cbs) != 0) {
|
|
public_key->reset();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool VerifySignedData(SignatureAlgorithm algorithm,
|
|
const der::Input& signed_data,
|
|
const der::BitString& signature_value,
|
|
EVP_PKEY* public_key,
|
|
SignatureVerifyCache* cache) {
|
|
int expected_pkey_id = 1;
|
|
const EVP_MD* digest = nullptr;
|
|
bool is_rsa_pss = false;
|
|
std::string_view cache_algorithm_name;
|
|
switch (algorithm) {
|
|
case SignatureAlgorithm::kRsaPkcs1Sha1:
|
|
expected_pkey_id = EVP_PKEY_RSA;
|
|
digest = EVP_sha1();
|
|
cache_algorithm_name = "RsaPkcs1Sha1";
|
|
break;
|
|
case SignatureAlgorithm::kRsaPkcs1Sha256:
|
|
expected_pkey_id = EVP_PKEY_RSA;
|
|
digest = EVP_sha256();
|
|
cache_algorithm_name = "RsaPkcs1Sha256";
|
|
break;
|
|
case SignatureAlgorithm::kRsaPkcs1Sha384:
|
|
expected_pkey_id = EVP_PKEY_RSA;
|
|
digest = EVP_sha384();
|
|
cache_algorithm_name = "RsaPkcs1Sha384";
|
|
break;
|
|
case SignatureAlgorithm::kRsaPkcs1Sha512:
|
|
expected_pkey_id = EVP_PKEY_RSA;
|
|
digest = EVP_sha512();
|
|
cache_algorithm_name = "RsaPkcs1Sha512";
|
|
break;
|
|
|
|
case SignatureAlgorithm::kEcdsaSha1:
|
|
expected_pkey_id = EVP_PKEY_EC;
|
|
digest = EVP_sha1();
|
|
cache_algorithm_name = "EcdsaSha1";
|
|
break;
|
|
case SignatureAlgorithm::kEcdsaSha256:
|
|
expected_pkey_id = EVP_PKEY_EC;
|
|
digest = EVP_sha256();
|
|
cache_algorithm_name = "EcdsaSha256";
|
|
break;
|
|
case SignatureAlgorithm::kEcdsaSha384:
|
|
expected_pkey_id = EVP_PKEY_EC;
|
|
digest = EVP_sha384();
|
|
cache_algorithm_name = "EcdsaSha384";
|
|
break;
|
|
case SignatureAlgorithm::kEcdsaSha512:
|
|
expected_pkey_id = EVP_PKEY_EC;
|
|
digest = EVP_sha512();
|
|
cache_algorithm_name = "EcdsaSha512";
|
|
break;
|
|
|
|
case SignatureAlgorithm::kRsaPssSha256:
|
|
expected_pkey_id = EVP_PKEY_RSA;
|
|
digest = EVP_sha256();
|
|
cache_algorithm_name = "RsaPssSha256";
|
|
is_rsa_pss = true;
|
|
break;
|
|
case SignatureAlgorithm::kRsaPssSha384:
|
|
expected_pkey_id = EVP_PKEY_RSA;
|
|
digest = EVP_sha384();
|
|
cache_algorithm_name = "RsaPssSha384";
|
|
is_rsa_pss = true;
|
|
break;
|
|
case SignatureAlgorithm::kRsaPssSha512:
|
|
expected_pkey_id = EVP_PKEY_RSA;
|
|
digest = EVP_sha512();
|
|
cache_algorithm_name = "RsaPssSha512";
|
|
is_rsa_pss = true;
|
|
break;
|
|
}
|
|
|
|
if (expected_pkey_id != EVP_PKEY_id(public_key))
|
|
return false;
|
|
|
|
// 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();
|
|
|
|
std::string cache_key;
|
|
if (cache) {
|
|
cache_key = SignatureVerifyCacheKey(cache_algorithm_name, signed_data,
|
|
signature_value_bytes, public_key);
|
|
if (!cache_key.empty()) {
|
|
switch (cache->Check(cache_key)) {
|
|
case SignatureVerifyCache::Value::kValid:
|
|
return true;
|
|
case SignatureVerifyCache::Value::kInvalid:
|
|
return false;
|
|
case SignatureVerifyCache::Value::kUnknown:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
|
|
|
|
bssl::ScopedEVP_MD_CTX ctx;
|
|
EVP_PKEY_CTX* pctx = nullptr; // Owned by |ctx|.
|
|
|
|
if (!EVP_DigestVerifyInit(ctx.get(), &pctx, digest, nullptr, public_key))
|
|
return false;
|
|
|
|
if (is_rsa_pss) {
|
|
// All supported RSASSA-PSS algorithms match signing and MGF-1 digest. They
|
|
// also use the digest length as the salt length, which is specified with -1
|
|
// in OpenSSL's API.
|
|
if (!EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) ||
|
|
!EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, -1)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!EVP_DigestVerifyUpdate(ctx.get(), signed_data.UnsafeData(),
|
|
signed_data.Length())) {
|
|
return false;
|
|
}
|
|
|
|
bool ret =
|
|
1 == EVP_DigestVerifyFinal(ctx.get(), signature_value_bytes.UnsafeData(),
|
|
signature_value_bytes.Length());
|
|
if (!cache_key.empty()) {
|
|
cache->Store(cache_key, ret ? SignatureVerifyCache::Value::kValid
|
|
: SignatureVerifyCache::Value::kInvalid);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool VerifySignedData(SignatureAlgorithm algorithm,
|
|
const der::Input& signed_data,
|
|
const der::BitString& signature_value,
|
|
const der::Input& public_key_spki,
|
|
SignatureVerifyCache* cache) {
|
|
bssl::UniquePtr<EVP_PKEY> public_key;
|
|
if (!ParsePublicKey(public_key_spki, &public_key))
|
|
return false;
|
|
return VerifySignedData(algorithm, signed_data, signature_value,
|
|
public_key.get(), cache);
|
|
}
|
|
|
|
} // namespace net
|