Bug 1918279 - implement a signature cache r=jschanck

Differential Revision: https://phabricator.services.mozilla.com/D221904
This commit is contained in:
Dana Keeler 2024-09-23 21:47:53 +00:00
Родитель c787aab229
Коммит 4e10a310f1
22 изменённых файлов: 572 добавлений и 162 удалений

8
Cargo.lock сгенерированный
Просмотреть файл

@ -2358,6 +2358,7 @@ dependencies = [
"rure",
"rusqlite",
"rust_minidump_writer_linux",
"signature_cache",
"static_prefs",
"storage",
"unic-langid",
@ -5463,6 +5464,13 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
[[package]]
name = "signature_cache"
version = "0.1.0"
dependencies = [
"hashlink",
]
[[package]]
name = "siphasher"
version = "0.3.10"

Просмотреть файл

@ -15544,6 +15544,24 @@
value: true
mirror: always
# The number of entries in the certificate signature cache.
# A higher number increases memory usage, but should increase the cache hit rate.
# Each entry is approximately 64 bytes, and the maximum number of entries is 65535.
- name: security.pki.cert_signature_cache_size
type: RelaxedAtomicUint32
value: 128
mirror: always
# The number of entries in the SCT signature cache.
# A higher number increases memory usage, but should increase the cache hit rate.
# Each entry is approximately 64 bytes, and the maximum number of entries is 65535.
# There will be 2-3 SCTs per certificate, so this probably needs to be 2-3x
# security.pki.cert_signature_cache_size to achieve similar hit rates.
- name: security.pki.sct_signature_cache_size
type: RelaxedAtomicUint32
value: 256
mirror: always
- name: security.tls.version.min
type: RelaxedAtomicUint32
value: 3

Просмотреть файл

@ -20,17 +20,18 @@
#include "mozilla/Casting.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/Logging.h"
#include "nsNSSComponent.h"
#include "mozilla/StaticPrefs_security.h"
#include "mozilla/SyncRunnable.h"
#include "nsPromiseFlatString.h"
#include "nsServiceManagerUtils.h"
#include "pk11pub.h"
#include "mozpkix/pkix.h"
#include "mozpkix/pkixcheck.h"
#include "mozpkix/pkixnss.h"
#include "mozpkix/pkixutil.h"
#include "secmod.h"
#include "nsNSSComponent.h"
#include "nsNetCID.h"
#include "nsPromiseFlatString.h"
#include "nsServiceManagerUtils.h"
#include "pk11pub.h"
#include "secmod.h"
using namespace mozilla::ct;
using namespace mozilla::pkix;
@ -68,7 +69,11 @@ CertVerifier::CertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc,
mCertShortLifetimeInDays(certShortLifetimeInDays),
mNetscapeStepUpPolicy(netscapeStepUpPolicy),
mCTMode(ctMode),
mCRLiteMode(crliteMode) {
mCRLiteMode(crliteMode),
mSignatureCache(
signature_cache_new(
StaticPrefs::security_pki_cert_signature_cache_size()),
signature_cache_free) {
LoadKnownCTLogs();
mThirdPartyCerts = thirdPartyCerts.Clone();
for (const auto& root : mThirdPartyCerts) {
@ -459,12 +464,12 @@ Result CertVerifier::VerifyCert(
// XXX: We don't really have a trust bit for SSL client authentication so
// just use trustEmail as it is the closest alternative.
NSSCertDBTrustDomain trustDomain(
trustEmail, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
mOCSPTimeoutHard, mCertShortLifetimeInDays, MIN_RSA_BITS_WEAK,
ValidityCheckingMode::CheckingOff, NetscapeStepUpPolicy::NeverMatch,
mCRLiteMode, originAttributes, mThirdPartyRootInputs,
mThirdPartyIntermediateInputs, extraCertificates, builtChain, nullptr,
nullptr);
trustEmail, defaultOCSPFetching, mOCSPCache, mSignatureCache.get(),
pinArg, mOCSPTimeoutSoft, mOCSPTimeoutHard, mCertShortLifetimeInDays,
MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff,
NetscapeStepUpPolicy::NeverMatch, mCRLiteMode, originAttributes,
mThirdPartyRootInputs, mThirdPartyIntermediateInputs,
extraCertificates, builtChain, nullptr, nullptr);
rv = BuildCertChain(
trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity,
KeyUsage::digitalSignature, KeyPurposeId::id_kp_clientAuth,
@ -492,12 +497,12 @@ Result CertVerifier::VerifyCert(
rv = Result::ERROR_UNKNOWN_ERROR;
for (const auto& evPolicy : evPolicies) {
NSSCertDBTrustDomain trustDomain(
trustSSL, evOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
mOCSPTimeoutHard, mCertShortLifetimeInDays, MIN_RSA_BITS,
ValidityCheckingMode::CheckForEV, mNetscapeStepUpPolicy,
mCRLiteMode, originAttributes, mThirdPartyRootInputs,
mThirdPartyIntermediateInputs, extraCertificates, builtChain,
pinningTelemetryInfo, hostname);
trustSSL, evOCSPFetching, mOCSPCache, mSignatureCache.get(), pinArg,
mOCSPTimeoutSoft, mOCSPTimeoutHard, mCertShortLifetimeInDays,
MIN_RSA_BITS, ValidityCheckingMode::CheckForEV,
mNetscapeStepUpPolicy, mCRLiteMode, originAttributes,
mThirdPartyRootInputs, mThirdPartyIntermediateInputs,
extraCertificates, builtChain, pinningTelemetryInfo, hostname);
rv = BuildCertChainForOneKeyUsage(
trustDomain, certDER, time,
KeyUsage::digitalSignature, // (EC)DHE
@ -554,8 +559,9 @@ Result CertVerifier::VerifyCert(
}
NSSCertDBTrustDomain trustDomain(
trustSSL, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
mOCSPTimeoutHard, mCertShortLifetimeInDays, keySizeOptions[i],
trustSSL, defaultOCSPFetching, mOCSPCache, mSignatureCache.get(),
pinArg, mOCSPTimeoutSoft, mOCSPTimeoutHard,
mCertShortLifetimeInDays, keySizeOptions[i],
ValidityCheckingMode::CheckingOff, mNetscapeStepUpPolicy,
mCRLiteMode, originAttributes, mThirdPartyRootInputs,
mThirdPartyIntermediateInputs, extraCertificates, builtChain,
@ -608,12 +614,12 @@ Result CertVerifier::VerifyCert(
case certificateUsageSSLCA: {
NSSCertDBTrustDomain trustDomain(
trustSSL, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
mOCSPTimeoutHard, mCertShortLifetimeInDays, MIN_RSA_BITS_WEAK,
ValidityCheckingMode::CheckingOff, mNetscapeStepUpPolicy, mCRLiteMode,
originAttributes, mThirdPartyRootInputs,
mThirdPartyIntermediateInputs, extraCertificates, builtChain, nullptr,
nullptr);
trustSSL, defaultOCSPFetching, mOCSPCache, mSignatureCache.get(),
pinArg, mOCSPTimeoutSoft, mOCSPTimeoutHard, mCertShortLifetimeInDays,
MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff,
mNetscapeStepUpPolicy, mCRLiteMode, originAttributes,
mThirdPartyRootInputs, mThirdPartyIntermediateInputs,
extraCertificates, builtChain, nullptr, nullptr);
rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeCA,
KeyUsage::keyCertSign, KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy, stapledOCSPResponse);
@ -626,12 +632,12 @@ Result CertVerifier::VerifyCert(
case certificateUsageEmailSigner: {
NSSCertDBTrustDomain trustDomain(
trustEmail, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
mOCSPTimeoutHard, mCertShortLifetimeInDays, MIN_RSA_BITS_WEAK,
ValidityCheckingMode::CheckingOff, NetscapeStepUpPolicy::NeverMatch,
mCRLiteMode, originAttributes, mThirdPartyRootInputs,
mThirdPartyIntermediateInputs, extraCertificates, builtChain, nullptr,
nullptr);
trustEmail, defaultOCSPFetching, mOCSPCache, mSignatureCache.get(),
pinArg, mOCSPTimeoutSoft, mOCSPTimeoutHard, mCertShortLifetimeInDays,
MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff,
NetscapeStepUpPolicy::NeverMatch, mCRLiteMode, originAttributes,
mThirdPartyRootInputs, mThirdPartyIntermediateInputs,
extraCertificates, builtChain, nullptr, nullptr);
rv = BuildCertChain(
trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity,
KeyUsage::digitalSignature, KeyPurposeId::id_kp_emailProtection,
@ -654,12 +660,12 @@ Result CertVerifier::VerifyCert(
// usage it is trying to verify for, and base its algorithm choices
// based on the result of the verification(s).
NSSCertDBTrustDomain trustDomain(
trustEmail, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
mOCSPTimeoutHard, mCertShortLifetimeInDays, MIN_RSA_BITS_WEAK,
ValidityCheckingMode::CheckingOff, NetscapeStepUpPolicy::NeverMatch,
mCRLiteMode, originAttributes, mThirdPartyRootInputs,
mThirdPartyIntermediateInputs, extraCertificates, builtChain, nullptr,
nullptr);
trustEmail, defaultOCSPFetching, mOCSPCache, mSignatureCache.get(),
pinArg, mOCSPTimeoutSoft, mOCSPTimeoutHard, mCertShortLifetimeInDays,
MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff,
NetscapeStepUpPolicy::NeverMatch, mCRLiteMode, originAttributes,
mThirdPartyRootInputs, mThirdPartyIntermediateInputs,
extraCertificates, builtChain, nullptr, nullptr);
rv = BuildCertChain(trustDomain, certDER, time,
EndEntityOrCA::MustBeEndEntity,
KeyUsage::keyEncipherment, // RSA
@ -867,5 +873,110 @@ Result CertVerifier::VerifySSLServerCert(
return Success;
}
// Take the (data, signature, subjectPublicKeyInfo, publicKeyAlgorithm,
// digestAlgorithm) tuple that defines a signature and derive a hash that
// uniquely identifies it. This is done by prefixing each variable-length
// component (data, signature, and subjectPublicKeyInfo) with
// sizeof(pkix::Input::size_type) bytes (currently 2) indicating the length of
// that component and concatenating them together, followed by one byte for the
// digestAlgorithm. The concatenation is then hashed with sha512.
// It should be computationally infeasible to find two distinct sets of inputs
// that have the same sha512 hash (and if it were possible, then it would be
// possible to break the signature scheme itself).
void HashSignatureParams(pkix::Input data, pkix::Input signature,
pkix::Input subjectPublicKeyInfo,
pkix::der::PublicKeyAlgorithm publicKeyAlgorithm,
pkix::DigestAlgorithm digestAlgorithm,
/*out*/ Maybe<nsTArray<uint8_t>>& sha512Hash) {
sha512Hash.reset();
Digest digest;
if (NS_FAILED(digest.Begin(SEC_OID_SHA512))) {
return;
}
pkix::Input::size_type dataLength = data.GetLength();
if (NS_FAILED(digest.Update(reinterpret_cast<const uint8_t*>(&dataLength),
sizeof(dataLength)))) {
return;
}
if (NS_FAILED(digest.Update(data.UnsafeGetData(), dataLength))) {
return;
}
pkix::Input::size_type signatureLength = signature.GetLength();
if (NS_FAILED(
digest.Update(reinterpret_cast<const uint8_t*>(&signatureLength),
sizeof(signatureLength)))) {
return;
}
if (NS_FAILED(digest.Update(signature.UnsafeGetData(), signatureLength))) {
return;
}
pkix::Input::size_type spkiLength = subjectPublicKeyInfo.GetLength();
if (NS_FAILED(digest.Update(reinterpret_cast<const uint8_t*>(&spkiLength),
sizeof(spkiLength)))) {
return;
}
if (NS_FAILED(
digest.Update(subjectPublicKeyInfo.UnsafeGetData(), spkiLength))) {
return;
}
if (NS_FAILED(
digest.Update(reinterpret_cast<const uint8_t*>(&publicKeyAlgorithm),
sizeof(publicKeyAlgorithm)))) {
return;
}
if (NS_FAILED(
digest.Update(reinterpret_cast<const uint8_t*>(&digestAlgorithm),
sizeof(digestAlgorithm)))) {
return;
}
nsTArray<uint8_t> result;
if (NS_FAILED(digest.End(result))) {
return;
}
sha512Hash.emplace(std::move(result));
}
Result VerifySignedDataWithCache(
der::PublicKeyAlgorithm publicKeyAlg,
mozilla::glean::impl::DenominatorMetric telemetryDenominator,
mozilla::glean::impl::NumeratorMetric telemetryNumerator, Input data,
DigestAlgorithm digestAlgorithm, Input signature,
Input subjectPublicKeyInfo, SignatureCache* signatureCache, void* pinArg) {
telemetryDenominator.Add(1);
Maybe<nsTArray<uint8_t>> sha512Hash;
HashSignatureParams(data, signature, subjectPublicKeyInfo, publicKeyAlg,
digestAlgorithm, sha512Hash);
// If hashing the signature parameters succeeded, see if this signature is in
// the signature cache.
if (sha512Hash.isSome() &&
signature_cache_get(signatureCache, sha512Hash.ref().Elements())) {
telemetryNumerator.AddToNumerator(1);
return Success;
}
Result result;
switch (publicKeyAlg) {
case der::PublicKeyAlgorithm::ECDSA:
result = VerifyECDSASignedDataNSS(data, digestAlgorithm, signature,
subjectPublicKeyInfo, pinArg);
break;
case der::PublicKeyAlgorithm::RSA_PKCS1:
result = VerifyRSAPKCS1SignedDataNSS(data, digestAlgorithm, signature,
subjectPublicKeyInfo, pinArg);
break;
case der::PublicKeyAlgorithm::RSA_PSS:
result = VerifyRSAPSSSignedDataNSS(data, digestAlgorithm, signature,
subjectPublicKeyInfo, pinArg);
break;
default:
MOZ_ASSERT_UNREACHABLE("unhandled public key algorithm");
return Result::FATAL_ERROR_LIBRARY_FAILURE;
}
// Add this signature to the signature cache.
if (sha512Hash.isSome() && result == Success) {
signature_cache_insert(signatureCache, sha512Hash.ref().Elements());
}
return result;
}
} // namespace psm
} // namespace mozilla

Просмотреть файл

@ -17,8 +17,11 @@
#include "mozilla/Telemetry.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/UniquePtr.h"
#include "nsString.h"
#include "mozilla/glean/GleanMetrics.h"
#include "mozpkix/pkixder.h"
#include "mozpkix/pkixtypes.h"
#include "nsString.h"
#include "signature_cache_ffi.h"
#include "sslt.h"
#if defined(_MSC_VER)
@ -264,6 +267,13 @@ class CertVerifier {
// so we must allocate dynamically.
UniquePtr<mozilla::ct::MultiLogCTVerifier> mCTVerifier;
// If many connections are made to a site using a particular certificate,
// this cache will speed up verifications after the first one by saving the
// results of signature verification.
// This will also be beneficial in situations where different sites use
// different certificates that happen to be issued by the same intermediate.
UniquePtr<SignatureCache, decltype(&signature_cache_free)> mSignatureCache;
void LoadKnownCTLogs();
mozilla::pkix::Result VerifyCertificateTransparencyPolicy(
NSSCertDBTrustDomain& trustDomain,
@ -277,6 +287,18 @@ mozilla::pkix::Result CertListContainsExpectedKeys(const CERTCertList* certList,
const char* hostname,
mozilla::pkix::Time time);
// Verify signed data, making use of the given SignatureCache. That is, if the
// (data, digestAlgorithm, signature, subjectPublicKeyInfo) tuple has already
// been verified and is in the cache, this skips the work of verifying the
// signature (which is slow) and returns the already-known result.
mozilla::pkix::Result VerifySignedDataWithCache(
mozilla::pkix::der::PublicKeyAlgorithm publicKeyAlg,
mozilla::glean::impl::DenominatorMetric telemetryDenominator,
mozilla::glean::impl::NumeratorMetric telemetryNumerator,
mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm,
mozilla::pkix::Input signature, mozilla::pkix::Input subjectPublicKeyInfo,
SignatureCache* signatureCache, void* pinArg);
} // namespace psm
} // namespace mozilla

Просмотреть файл

@ -68,7 +68,7 @@ namespace psm {
NSSCertDBTrustDomain::NSSCertDBTrustDomain(
SECTrustType certDBTrustType, OCSPFetching ocspFetching,
OCSPCache& ocspCache,
OCSPCache& ocspCache, SignatureCache* signatureCache,
/*optional but shouldn't be*/ void* pinArg, TimeDuration ocspTimeoutSoft,
TimeDuration ocspTimeoutHard, uint32_t certShortLifetimeInDays,
unsigned int minRSABits, ValidityCheckingMode validityCheckingMode,
@ -83,6 +83,7 @@ NSSCertDBTrustDomain::NSSCertDBTrustDomain(
: mCertDBTrustType(certDBTrustType),
mOCSPFetching(ocspFetching),
mOCSPCache(ocspCache),
mSignatureCache(signatureCache),
mPinArg(pinArg),
mOCSPTimeoutSoft(ocspTimeoutSoft),
mOCSPTimeoutHard(ocspTimeoutHard),
@ -1470,15 +1471,21 @@ Result NSSCertDBTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
Result NSSCertDBTrustDomain::VerifyRSAPKCS1SignedData(
Input data, DigestAlgorithm digestAlgorithm, Input signature,
Input subjectPublicKeyInfo) {
return VerifyRSAPKCS1SignedDataNSS(data, digestAlgorithm, signature,
subjectPublicKeyInfo, mPinArg);
return VerifySignedDataWithCache(
der::PublicKeyAlgorithm::RSA_PKCS1,
mozilla::glean::cert_signature_cache::total,
mozilla::glean::cert_signature_cache::hits, data, digestAlgorithm,
signature, subjectPublicKeyInfo, mSignatureCache, mPinArg);
}
Result NSSCertDBTrustDomain::VerifyRSAPSSSignedData(
Input data, DigestAlgorithm digestAlgorithm, Input signature,
Input subjectPublicKeyInfo) {
return VerifyRSAPSSSignedDataNSS(data, digestAlgorithm, signature,
subjectPublicKeyInfo, mPinArg);
return VerifySignedDataWithCache(
der::PublicKeyAlgorithm::RSA_PSS,
mozilla::glean::cert_signature_cache::total,
mozilla::glean::cert_signature_cache::hits, data, digestAlgorithm,
signature, subjectPublicKeyInfo, mSignatureCache, mPinArg);
}
Result NSSCertDBTrustDomain::CheckECDSACurveIsAcceptable(
@ -1496,8 +1503,11 @@ Result NSSCertDBTrustDomain::CheckECDSACurveIsAcceptable(
Result NSSCertDBTrustDomain::VerifyECDSASignedData(
Input data, DigestAlgorithm digestAlgorithm, Input signature,
Input subjectPublicKeyInfo) {
return VerifyECDSASignedDataNSS(data, digestAlgorithm, signature,
subjectPublicKeyInfo, mPinArg);
return VerifySignedDataWithCache(
der::PublicKeyAlgorithm::ECDSA,
mozilla::glean::cert_signature_cache::total,
mozilla::glean::cert_signature_cache::hits, data, digestAlgorithm,
signature, subjectPublicKeyInfo, mSignatureCache, mPinArg);
}
Result NSSCertDBTrustDomain::CheckValidityIsAcceptable(

Просмотреть файл

@ -145,7 +145,8 @@ class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain {
NSSCertDBTrustDomain(
SECTrustType certDBTrustType, OCSPFetching ocspFetching,
OCSPCache& ocspCache, void* pinArg, mozilla::TimeDuration ocspTimeoutSoft,
OCSPCache& ocspCache, SignatureCache* signatureCache, void* pinArg,
mozilla::TimeDuration ocspTimeoutSoft,
mozilla::TimeDuration ocspTimeoutHard, uint32_t certShortLifetimeInDays,
unsigned int minRSABits, ValidityCheckingMode validityCheckingMode,
NetscapeStepUpPolicy netscapeStepUpPolicy, CRLiteMode crliteMode,
@ -299,8 +300,9 @@ class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain {
const SECTrustType mCertDBTrustType;
const OCSPFetching mOCSPFetching;
OCSPCache& mOCSPCache; // non-owning!
void* mPinArg; // non-owning!
OCSPCache& mOCSPCache; // non-owning!
SignatureCache* mSignatureCache; // non-owning!
void* mPinArg; // non-owning!
const mozilla::TimeDuration mOCSPTimeoutSoft;
const mozilla::TimeDuration mOCSPTimeoutHard;
const uint32_t mCertShortLifetimeInDays;

Просмотреть файл

@ -98,3 +98,28 @@ cert_verifier:
- jschanck@mozilla.com
expires: 144
unit: trust objects
cert_signature_cache:
hits:
type: rate
description: >
How often a certificate signature to be verified is in the cache already.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1918279
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1918279
notification_emails:
- dkeeler@mozilla.com
expires: never
denominator_metric: cert_signature_cache.total
total:
type: counter
description: >
How many certificate signature verifications are performed.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1918279
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1918279
notification_emails:
- dkeeler@mozilla.com
expires: never

Просмотреть файл

@ -30,6 +30,7 @@ LOCAL_INCLUDES += [
DIRS += [
"../ct",
"signature_cache",
]
TEST_DIRS += [

Просмотреть файл

@ -0,0 +1,7 @@
[package]
name = "signature_cache"
version = "0.1.0"
edition = "2021"
[dependencies]
hashlink = "0.9"

Просмотреть файл

@ -0,0 +1,18 @@
header = """/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"""
autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen. */"""
include_guard = "signature_cache_ffi_h"
include_version = true
braces = "SameLine"
line_length = 100
tab_width = 2
language = "C++"
namespaces = []
includes = []
[export]
exclude = []
item_types = ["opaque", "functions"]

Просмотреть файл

@ -0,0 +1,16 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
FINAL_LIBRARY = "xul"
if CONFIG["COMPILE_ENVIRONMENT"]:
CbindgenHeader(
"signature_cache_ffi.h", inputs=["/security/certverifier/signature_cache"]
)
EXPORTS += [
"!signature_cache_ffi.h",
]

Просмотреть файл

@ -0,0 +1,113 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate hashlink;
use hashlink::LruCache;
use std::sync::Mutex;
const SHA512_LENGTH_IN_BYTES: usize = 64;
/// SignatureCache is a simple least-recently-used cache. The input is a sha512
/// hash representing the parameters defining a signature. A hit in the cache
/// indicates that the signature previously verified successfully.
pub struct SignatureCache {
cache: Mutex<LruCache<[u8; SHA512_LENGTH_IN_BYTES], ()>>,
}
impl SignatureCache {
/// Make a new SignatureCache with the specified number of slots.
fn new(capacity: u16) -> SignatureCache {
SignatureCache {
cache: Mutex::new(LruCache::new(capacity as usize)),
}
}
/// Look up a signature hash in the cache. Returns true if the signature
/// previously verified correctly, and false otherwise.
fn get(&mut self, sha512_hash: &[u8; SHA512_LENGTH_IN_BYTES]) -> bool {
let Ok(mut cache) = self.cache.lock() else {
return false;
};
cache.get(sha512_hash).is_some()
}
/// Insert a signature hash into the cache.
fn insert(&self, sha512_hash: [u8; SHA512_LENGTH_IN_BYTES]) {
let Ok(mut cache) = self.cache.lock() else {
return;
};
let _ = cache.insert(sha512_hash, ());
}
}
/// Create a new SignatureCache.
#[no_mangle]
pub extern "C" fn signature_cache_new(capacity: u16) -> *mut SignatureCache {
// This pattern returns a SignatureCache that will be owned by the caller.
// That is, the SignatureCache will live until `signature_cache_free` is
// called on it.
Box::into_raw(Box::new(SignatureCache::new(capacity)))
}
/// Free a SignatureCache.
///
/// # Safety
///
/// This function must only be called with a null pointer or a pointer returned from
/// `signature_cache_new`.
#[no_mangle]
pub unsafe extern "C" fn signature_cache_free(signature_cache: *mut SignatureCache) {
if signature_cache.is_null() {
return;
}
// This takes a SignatureCache that was created by calling
// `signature_cache_new` and ensures its resources are destroyed.
let _ = Box::from_raw(signature_cache);
}
/// Look up a signature parameter hash in a SignatureCache.
///
/// # Safety
///
/// This function must only be called with a pointer returned from
/// `signature_cache_new` and a pointer to `SHA512_LENGTH_IN_BYTES` bytes.
#[no_mangle]
pub unsafe extern "C" fn signature_cache_get(
signature_cache: *mut SignatureCache,
sha512_hash: *const u8,
) -> bool {
if signature_cache.is_null() || sha512_hash.is_null() {
return false;
}
let sha512_hash = std::slice::from_raw_parts(sha512_hash, SHA512_LENGTH_IN_BYTES);
let Ok(sha512_hash) = sha512_hash.try_into() else {
return false;
};
let signature_cache = &mut *signature_cache;
signature_cache.get(&sha512_hash)
}
/// Add a signature parameter hash to a SignatureCache.
///
/// # Safety
///
/// This function must only be called with a pointer returned from
/// `signature_cache_new` and a pointer to `SHA512_LENGTH_IN_BYTES` bytes.
#[no_mangle]
pub unsafe extern "C" fn signature_cache_insert(
signature_cache: *mut SignatureCache,
sha512_hash: *const u8,
) {
if signature_cache.is_null() || sha512_hash.is_null() {
return;
}
let sha512_hash = std::slice::from_raw_parts(sha512_hash, SHA512_LENGTH_IN_BYTES);
let Ok(sha512_hash) = sha512_hash.try_into() else {
return;
};
let signature_cache = &mut *signature_cache;
signature_cache.insert(sha512_hash);
}

Просмотреть файл

@ -9,15 +9,17 @@
#include <stdint.h>
#include "CTSerialization.h"
#include "CertVerifier.h"
#include "hasht.h"
#include "mozpkix/Result.h"
#include "mozpkix/pkixnss.h"
#include "mozpkix/pkixutil.h"
using namespace mozilla::pkix;
namespace mozilla {
namespace ct {
using namespace mozilla::pkix;
// A TrustDomain used to extract the SCT log signature parameters
// given its subjectPublicKeyInfo.
// Only RSASSA-PKCS1v15 with SHA-256 and ECDSA (using the NIST P-256 curve)
@ -29,75 +31,80 @@ class SignatureParamsTrustDomain final : public TrustDomain {
SignatureParamsTrustDomain()
: mSignatureAlgorithm(DigitallySigned::SignatureAlgorithm::Anonymous) {}
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input,
TrustLevel&) override {
return Result::FATAL_ERROR_LIBRARY_FAILURE;
pkix::Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input,
TrustLevel&) override {
return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
}
Result FindIssuer(Input, IssuerChecker&, Time) override {
return Result::FATAL_ERROR_LIBRARY_FAILURE;
pkix::Result FindIssuer(Input, IssuerChecker&, Time) override {
return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
const Input*, const Input*, const Input*) override {
return Result::FATAL_ERROR_LIBRARY_FAILURE;
pkix::Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
const Input*, const Input*,
const Input*) override {
return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
}
Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override {
return Result::FATAL_ERROR_LIBRARY_FAILURE;
pkix::Result IsChainValid(const DERArray&, Time,
const CertPolicyId&) override {
return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
}
Result DigestBuf(Input, DigestAlgorithm, uint8_t*, size_t) override {
return Result::FATAL_ERROR_LIBRARY_FAILURE;
pkix::Result DigestBuf(Input, DigestAlgorithm, uint8_t*, size_t) override {
return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
}
Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA,
Time) override {
return Result::FATAL_ERROR_LIBRARY_FAILURE;
pkix::Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA,
Time) override {
return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
}
Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve curve) override {
pkix::Result CheckECDSACurveIsAcceptable(EndEntityOrCA,
NamedCurve curve) override {
assert(mSignatureAlgorithm ==
DigitallySigned::SignatureAlgorithm::Anonymous);
if (curve != NamedCurve::secp256r1) {
return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
return pkix::Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
}
mSignatureAlgorithm = DigitallySigned::SignatureAlgorithm::ECDSA;
return Success;
}
Result VerifyECDSASignedData(Input, DigestAlgorithm, Input, Input) override {
return Result::FATAL_ERROR_LIBRARY_FAILURE;
pkix::Result VerifyECDSASignedData(Input, DigestAlgorithm, Input,
Input) override {
return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
}
Result CheckRSAPublicKeyModulusSizeInBits(
pkix::Result CheckRSAPublicKeyModulusSizeInBits(
EndEntityOrCA, unsigned int modulusSizeInBits) override {
assert(mSignatureAlgorithm ==
DigitallySigned::SignatureAlgorithm::Anonymous);
// Require RSA keys of at least 2048 bits. See RFC 6962, Section 2.1.4.
if (modulusSizeInBits < 2048) {
return Result::ERROR_INADEQUATE_KEY_SIZE;
return pkix::Result::ERROR_INADEQUATE_KEY_SIZE;
}
mSignatureAlgorithm = DigitallySigned::SignatureAlgorithm::RSA;
return Success;
}
Result VerifyRSAPKCS1SignedData(Input, DigestAlgorithm, Input,
Input) override {
return Result::FATAL_ERROR_LIBRARY_FAILURE;
pkix::Result VerifyRSAPKCS1SignedData(Input, DigestAlgorithm, Input,
Input) override {
return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
}
Result VerifyRSAPSSSignedData(Input, DigestAlgorithm, Input, Input) override {
return Result::FATAL_ERROR_LIBRARY_FAILURE;
pkix::Result VerifyRSAPSSSignedData(Input, DigestAlgorithm, Input,
Input) override {
return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
}
Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA,
KeyPurposeId) override {
return Result::FATAL_ERROR_LIBRARY_FAILURE;
pkix::Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA,
KeyPurposeId) override {
return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
}
Result NetscapeStepUpMatchesServerAuth(Time, bool&) override {
return Result::FATAL_ERROR_LIBRARY_FAILURE;
pkix::Result NetscapeStepUpMatchesServerAuth(Time, bool&) override {
return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
}
void NoteAuxiliaryExtension(AuxiliaryExtension, Input) override {}
@ -112,10 +119,10 @@ CTLogVerifier::CTLogVerifier(CTLogOperatorId operatorId, CTLogState state,
mState(state),
mTimestamp(timestamp) {}
Result CTLogVerifier::Init(Input subjectPublicKeyInfo) {
pkix::Result CTLogVerifier::Init(Input subjectPublicKeyInfo) {
SignatureParamsTrustDomain trustDomain;
Result rv = CheckSubjectPublicKeyInfo(subjectPublicKeyInfo, trustDomain,
EndEntityOrCA::MustBeEndEntity);
pkix::Result rv = CheckSubjectPublicKeyInfo(subjectPublicKeyInfo, trustDomain,
EndEntityOrCA::MustBeEndEntity);
if (rv != Success) {
return rv;
}
@ -159,17 +166,18 @@ Result CTLogVerifier::Init(Input subjectPublicKeyInfo) {
return Success;
}
Result CTLogVerifier::Verify(const LogEntry& entry,
const SignedCertificateTimestamp& sct) {
if (mKeyId.empty() || sct.logId != mKeyId) {
return Result::FATAL_ERROR_INVALID_ARGS;
pkix::Result CTLogVerifier::Verify(const LogEntry& entry,
const SignedCertificateTimestamp& sct,
SignatureCache* signatureCache) {
if (mKeyId.empty() || sct.logId != mKeyId || !signatureCache) {
return pkix::Result::FATAL_ERROR_INVALID_ARGS;
}
if (!SignatureParametersMatch(sct.signature)) {
return Result::FATAL_ERROR_INVALID_ARGS;
return pkix::Result::FATAL_ERROR_INVALID_ARGS;
}
Buffer serializedLogEntry;
Result rv = EncodeLogEntry(entry, serializedLogEntry);
pkix::Result rv = EncodeLogEntry(entry, serializedLogEntry);
if (rv != Success) {
return rv;
}
@ -196,7 +204,8 @@ Result CTLogVerifier::Verify(const LogEntry& entry,
if (rv != Success) {
return rv;
}
return VerifySignature(serializedData, sct.signature.signatureData);
return VerifySignature(serializedData, sct.signature.signatureData,
signatureCache);
}
bool CTLogVerifier::SignatureParametersMatch(const DigitallySigned& signature) {
@ -204,50 +213,28 @@ bool CTLogVerifier::SignatureParametersMatch(const DigitallySigned& signature) {
DigitallySigned::HashAlgorithm::SHA256, mSignatureAlgorithm);
}
static Result FasterVerifyECDSASignedDataNSS(Input data, Input signature,
UniqueSECKEYPublicKey& pubkey) {
assert(pubkey);
if (!pubkey) {
return Result::FATAL_ERROR_LIBRARY_FAILURE;
}
// The signature is encoded as a DER SEQUENCE of two INTEGERs. PK11_Verify
// expects the signature as only the two integers r and s (so no encoding -
// just two series of bytes each half as long as SECKEY_SignatureLen(pubkey)).
// DSAU_DecodeDerSigToLen converts from the former format to the latter.
SECItem derSignatureSECItem(UnsafeMapInputToSECItem(signature));
size_t signatureLen = SECKEY_SignatureLen(pubkey.get());
if (signatureLen == 0) {
return MapPRErrorCodeToResult(PR_GetError());
}
UniqueSECItem signatureSECItem(
DSAU_DecodeDerSigToLen(&derSignatureSECItem, signatureLen));
if (!signatureSECItem) {
return MapPRErrorCodeToResult(PR_GetError());
}
SECItem dataSECItem(UnsafeMapInputToSECItem(data));
SECStatus srv =
PK11_VerifyWithMechanism(pubkey.get(), CKM_ECDSA_SHA256, nullptr,
signatureSECItem.get(), &dataSECItem, nullptr);
if (srv != SECSuccess) {
return MapPRErrorCodeToResult(PR_GetError());
}
return Success;
}
Result CTLogVerifier::VerifySignature(Input data, Input signature) {
pkix::Result CTLogVerifier::VerifySignature(Input data, Input signature,
SignatureCache* signatureCache) {
Input spki;
Result rv = BufferToInput(mSubjectPublicKeyInfo, spki);
pkix::Result rv = BufferToInput(mSubjectPublicKeyInfo, spki);
if (rv != Success) {
return rv;
}
switch (mSignatureAlgorithm) {
case DigitallySigned::SignatureAlgorithm::RSA:
rv = VerifyRSAPKCS1SignedDataNSS(data, DigestAlgorithm::sha256, signature,
spki, nullptr);
rv = psm::VerifySignedDataWithCache(
der::PublicKeyAlgorithm::RSA_PKCS1,
mozilla::glean::sct_signature_cache::total,
mozilla::glean::sct_signature_cache::hits, data,
DigestAlgorithm::sha256, signature, spki, signatureCache, nullptr);
break;
case DigitallySigned::SignatureAlgorithm::ECDSA:
rv = FasterVerifyECDSASignedDataNSS(data, signature, mPublicECKey);
rv = psm::VerifySignedDataWithCache(
der::PublicKeyAlgorithm::ECDSA,
mozilla::glean::sct_signature_cache::total,
mozilla::glean::sct_signature_cache::hits, data,
DigestAlgorithm::sha256, signature, spki, signatureCache, nullptr);
break;
// We do not expect new values added to this enum any time soon,
// so just listing all the available ones seems to be the easiest way
@ -257,22 +244,23 @@ Result CTLogVerifier::VerifySignature(Input data, Input signature) {
case DigitallySigned::SignatureAlgorithm::DSA:
default:
assert(false);
return Result::FATAL_ERROR_INVALID_ARGS;
return pkix::Result::FATAL_ERROR_INVALID_ARGS;
}
if (rv != Success) {
if (IsFatalError(rv)) {
return rv;
}
// If the error is non-fatal, we assume the signature was invalid.
return Result::ERROR_BAD_SIGNATURE;
return pkix::Result::ERROR_BAD_SIGNATURE;
}
return Success;
}
Result CTLogVerifier::VerifySignature(const Buffer& data,
const Buffer& signature) {
pkix::Result CTLogVerifier::VerifySignature(const Buffer& data,
const Buffer& signature,
SignatureCache* signatureCache) {
Input dataInput;
Result rv = BufferToInput(data, dataInput);
pkix::Result rv = BufferToInput(data, dataInput);
if (rv != Success) {
return rv;
}
@ -281,7 +269,7 @@ Result CTLogVerifier::VerifySignature(const Buffer& data,
if (rv != Success) {
return rv;
}
return VerifySignature(dataInput, signatureInput);
return VerifySignature(dataInput, signatureInput, signatureCache);
}
} // namespace ct

Просмотреть файл

@ -13,9 +13,11 @@
#include "CTLog.h"
#include "CTUtils.h"
#include "SignedCertificateTimestamp.h"
#include "mozilla/glean/GleanMetrics.h"
#include "mozpkix/Input.h"
#include "mozpkix/Result.h"
#include "mozpkix/pkix.h"
#include "signature_cache_ffi.h"
namespace mozilla {
namespace ct {
@ -52,7 +54,8 @@ class CTLogVerifier {
// Verifies that |sct| contains a valid signature for |entry|.
// |sct| must be signed by the verifier's log.
pkix::Result Verify(const LogEntry& entry,
const SignedCertificateTimestamp& sct);
const SignedCertificateTimestamp& sct,
SignatureCache* signatureCache);
// Returns true if the signature and hash algorithms in |signature|
// match those of the log.
@ -64,8 +67,10 @@ class CTLogVerifier {
// DigitallySigned struct encoding).
// Returns Success if passed verification, ERROR_BAD_SIGNATURE if failed
// verification, or other result on error.
pkix::Result VerifySignature(pkix::Input data, pkix::Input signature);
pkix::Result VerifySignature(const Buffer& data, const Buffer& signature);
pkix::Result VerifySignature(pkix::Input data, pkix::Input signature,
SignatureCache* signatureCache);
pkix::Result VerifySignature(const Buffer& data, const Buffer& signature,
SignatureCache* signatureCache);
// mPublicECKey works around an architectural deficiency in NSS. In the case
// of EC, if we don't create, import, and cache this key, NSS will import and

Просмотреть файл

@ -8,25 +8,32 @@
#include "CTObjectsExtractor.h"
#include "CTSerialization.h"
#include "mozilla/StaticPrefs_security.h"
namespace mozilla {
namespace ct {
using namespace mozilla::pkix;
MultiLogCTVerifier::MultiLogCTVerifier()
: mSignatureCache(signature_cache_new(
StaticPrefs::security_pki_sct_signature_cache_size()),
signature_cache_free) {}
void MultiLogCTVerifier::AddLog(CTLogVerifier&& log) {
mLogs.push_back(std::move(log));
}
Result MultiLogCTVerifier::Verify(Input cert, Input issuerSubjectPublicKeyInfo,
Input sctListFromCert,
Input sctListFromOCSPResponse,
Input sctListFromTLSExtension, Time time,
CTVerifyResult& result) {
pkix::Result MultiLogCTVerifier::Verify(Input cert,
Input issuerSubjectPublicKeyInfo,
Input sctListFromCert,
Input sctListFromOCSPResponse,
Input sctListFromTLSExtension,
Time time, CTVerifyResult& result) {
assert(cert.GetLength() > 0);
result.Reset();
Result rv;
pkix::Result rv;
// Verify embedded SCTs
if (issuerSubjectPublicKeyInfo.GetLength() > 0 &&
@ -72,7 +79,7 @@ void DecodeSCTs(Input encodedSctList,
decodedSCTs.clear();
Reader listReader;
Result rv = DecodeSCTList(encodedSctList, listReader);
pkix::Result rv = DecodeSCTList(encodedSctList, listReader);
if (rv != Success) {
decodingErrors++;
return;
@ -97,14 +104,14 @@ void DecodeSCTs(Input encodedSctList,
}
}
Result MultiLogCTVerifier::VerifySCTs(Input encodedSctList,
const LogEntry& expectedEntry,
SCTOrigin origin, Time time,
CTVerifyResult& result) {
pkix::Result MultiLogCTVerifier::VerifySCTs(Input encodedSctList,
const LogEntry& expectedEntry,
SCTOrigin origin, Time time,
CTVerifyResult& result) {
std::vector<SignedCertificateTimestamp> decodedSCTs;
DecodeSCTs(encodedSctList, decodedSCTs, result.decodingErrors);
for (auto sct : decodedSCTs) {
Result rv =
pkix::Result rv =
VerifySingleSCT(std::move(sct), expectedEntry, origin, time, result);
if (rv != Success) {
return rv;
@ -113,10 +120,9 @@ Result MultiLogCTVerifier::VerifySCTs(Input encodedSctList,
return Success;
}
Result MultiLogCTVerifier::VerifySingleSCT(SignedCertificateTimestamp&& sct,
const LogEntry& expectedEntry,
SCTOrigin origin, Time time,
CTVerifyResult& result) {
pkix::Result MultiLogCTVerifier::VerifySingleSCT(
SignedCertificateTimestamp&& sct, const LogEntry& expectedEntry,
SCTOrigin origin, Time time, CTVerifyResult& result) {
switch (origin) {
case SCTOrigin::Embedded:
result.embeddedSCTs++;
@ -149,9 +155,10 @@ Result MultiLogCTVerifier::VerifySingleSCT(SignedCertificateTimestamp&& sct,
return Success;
}
Result rv = matchingLog->Verify(expectedEntry, sct);
pkix::Result rv =
matchingLog->Verify(expectedEntry, sct, mSignatureCache.get());
if (rv != Success) {
if (rv == Result::ERROR_BAD_SIGNATURE) {
if (rv == pkix::Result::ERROR_BAD_SIGNATURE) {
result.sctsWithInvalidSignatures++;
return Success;
}

Просмотреть файл

@ -11,10 +11,11 @@
#include "CTLogVerifier.h"
#include "CTVerifyResult.h"
#include "SignedCertificateTimestamp.h"
#include "mozpkix/Input.h"
#include "mozpkix/Result.h"
#include "mozpkix/Time.h"
#include "SignedCertificateTimestamp.h"
#include "signature_cache_ffi.h"
namespace mozilla {
namespace ct {
@ -27,6 +28,8 @@ void DecodeSCTs(pkix::Input encodedSctList,
// Timestamps from multiple logs.
class MultiLogCTVerifier {
public:
MultiLogCTVerifier();
// Adds a new log to the list of known logs to verify against.
void AddLog(CTLogVerifier&& log);
@ -80,6 +83,11 @@ class MultiLogCTVerifier {
// The list of known logs.
std::vector<CTLogVerifier> mLogs;
// If many connections are made to a site using a particular certificate,
// this cache will speed up verifications after the first one by saving the
// results of verifying the signatures on the SCTs for that certificate.
UniquePtr<SignatureCache, decltype(&signature_cache_free)> mSignatureCache;
};
} // namespace ct

36
security/ct/metrics.yaml Normal file
Просмотреть файл

@ -0,0 +1,36 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# Adding a new metric? We have docs for that!
# https://firefox-source-docs.mozilla.org/toolkit/components/glean/user/new_definitions_file.html
---
$schema: moz://mozilla.org/schemas/glean/metrics/2-0-0
$tags:
- 'Core :: Security: PSM'
sct_signature_cache:
hits:
type: rate
description: >
How often an SCT signature to be verified is in the cache already.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1918279
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1918279
notification_emails:
- dkeeler@mozilla.com
expires: never
denominator_metric: sct_signature_cache.total
total:
type: counter
description: >
How many SCT signature verifications are performed.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1918279
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1918279
notification_emails:
- dkeeler@mozilla.com
expires: never

Просмотреть файл

@ -7,6 +7,7 @@
#include "CTLogVerifier.h"
#include "CTTestUtils.h"
#include "nss.h"
#include "signature_cache_ffi.h"
#include "gtest/gtest.h"
@ -23,12 +24,18 @@ class CTLogVerifierTest : public ::testing::Test {
abort();
}
mSignatureCache = signature_cache_new(1);
ASSERT_EQ(Success, mLog.Init(InputForBuffer(GetTestPublicKey())));
ASSERT_EQ(GetTestPublicKeyId(), mLog.keyId());
}
void TearDown() override { signature_cache_free(mSignatureCache); }
protected:
CTLogVerifier mLog = CTLogVerifier(-1, CTLogState::Admissible, 0);
// For some reason, the templating makes it impossible to use UniquePtr here.
SignatureCache* mSignatureCache;
};
TEST_F(CTLogVerifierTest, VerifiesCertSCT) {
@ -38,7 +45,7 @@ TEST_F(CTLogVerifierTest, VerifiesCertSCT) {
SignedCertificateTimestamp certSct;
GetX509CertSCT(certSct);
EXPECT_EQ(Success, mLog.Verify(certEntry, certSct));
EXPECT_EQ(Success, mLog.Verify(certEntry, certSct, mSignatureCache));
}
TEST_F(CTLogVerifierTest, VerifiesPrecertSCT) {
@ -48,7 +55,7 @@ TEST_F(CTLogVerifierTest, VerifiesPrecertSCT) {
SignedCertificateTimestamp precertSct;
GetPrecertSCT(precertSct);
EXPECT_EQ(Success, mLog.Verify(precertEntry, precertSct));
EXPECT_EQ(Success, mLog.Verify(precertEntry, precertSct, mSignatureCache));
}
TEST_F(CTLogVerifierTest, FailsInvalidTimestamp) {
@ -61,7 +68,8 @@ TEST_F(CTLogVerifierTest, FailsInvalidTimestamp) {
// Mangle the timestamp, so that it should fail signature validation.
certSct.timestamp = 0;
EXPECT_EQ(pkix::Result::ERROR_BAD_SIGNATURE, mLog.Verify(certEntry, certSct));
EXPECT_EQ(pkix::Result::ERROR_BAD_SIGNATURE,
mLog.Verify(certEntry, certSct, mSignatureCache));
}
TEST_F(CTLogVerifierTest, FailsInvalidSignature) {
@ -73,7 +81,8 @@ TEST_F(CTLogVerifierTest, FailsInvalidSignature) {
SignedCertificateTimestamp certSct;
GetX509CertSCT(certSct);
certSct.signature.signatureData[20] ^= '\xFF';
EXPECT_EQ(pkix::Result::ERROR_BAD_SIGNATURE, mLog.Verify(certEntry, certSct));
EXPECT_EQ(pkix::Result::ERROR_BAD_SIGNATURE,
mLog.Verify(certEntry, certSct, mSignatureCache));
// Mangle the encoding of the signature, making the underlying implementation
// return ERROR_BAD_DER. We still expect the verifier to return
@ -82,7 +91,7 @@ TEST_F(CTLogVerifierTest, FailsInvalidSignature) {
GetX509CertSCT(certSct2);
certSct2.signature.signatureData[0] ^= '\xFF';
EXPECT_EQ(pkix::Result::ERROR_BAD_SIGNATURE,
mLog.Verify(certEntry, certSct2));
mLog.Verify(certEntry, certSct2, mSignatureCache));
}
TEST_F(CTLogVerifierTest, FailsInvalidLogID) {
@ -97,7 +106,7 @@ TEST_F(CTLogVerifierTest, FailsInvalidLogID) {
certSct.logId.push_back('\x0');
EXPECT_EQ(pkix::Result::FATAL_ERROR_INVALID_ARGS,
mLog.Verify(certEntry, certSct));
mLog.Verify(certEntry, certSct, mSignatureCache));
}
// Test that excess data after the public key is rejected.

Просмотреть файл

@ -10,6 +10,7 @@
#include "CTTestUtils.h"
#include "gtest/gtest.h"
#include "nss.h"
#include "signature_cache_ffi.h"
namespace mozilla {
namespace ct {
@ -73,7 +74,9 @@ TEST_F(CTObjectsExtractorTest, ComplementarySCTVerifies) {
LogEntry entry;
GetX509LogEntry(InputForBuffer(mTestCert), entry);
EXPECT_EQ(Success, mLog.Verify(entry, sct));
SignatureCache* signatureCache(signature_cache_new(1));
EXPECT_EQ(Success, mLog.Verify(entry, sct, signatureCache));
signature_cache_free(signatureCache);
}
} // namespace ct

Просмотреть файл

@ -42,6 +42,7 @@ gecko_metrics = [
"parser/html/metrics.yaml",
"parser/htmlparser/metrics.yaml",
"security/certverifier/metrics.yaml",
"security/ct/metrics.yaml",
"security/manager/ssl/metrics.yaml",
"services/common/metrics.yaml",
"toolkit/components/antitracking/bouncetrackingprotection/metrics.yaml",

Просмотреть файл

@ -37,6 +37,7 @@ log = {version = "0.4", features = ["release_max_level_info"]}
cose-c = { version = "0.1.5" }
jsrust_shared = { path = "../../../../js/src/rust/shared" }
cascade_bloom_filter = { path = "../../../components/cascade_bloom_filter" }
signature_cache = { path = "../../../../security/certverifier/signature_cache" }
cert_storage = { path = "../../../../security/manager/ssl/cert_storage" }
crypto_hash = { path = "../../../../security/manager/ssl/crypto_hash" }
data_storage = { path = "../../../../security/manager/ssl/data_storage" }

Просмотреть файл

@ -46,6 +46,7 @@ extern crate processtools;
#[cfg(feature = "gecko_profiler")]
extern crate profiler_helper;
extern crate rsdparsa_capi;
extern crate signature_cache;
extern crate static_prefs;
extern crate storage;
extern crate webrender_bindings;