#include "net/cert/ct_log_verifier.h"
#include <stdint.h>
#include <bit>
#include <string>
#include <string_view>
#include <vector>
#include "base/logging.h"
#include "base/notreached.h"
#include "base/strings/string_view_util.h"
#include "crypto/evp.h"
#include "crypto/hash.h"
#include "crypto/openssl_util.h"
#include "net/cert/ct_log_verifier_util.h"
#include "net/cert/ct_serialization.h"
#include "net/cert/merkle_audit_proof.h"
#include "net/cert/merkle_consistency_proof.h"
#include "net/cert/signed_tree_head.h"
#include "third_party/boringssl/src/include/openssl/evp.h"
namespace net {
namespace {
constexpr std::array<uint8_t, ct::kSthRootHashLength> kSHA256EmptyStringHash = {
0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4,
0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b,
0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55};
const EVP_MD* GetEvpAlg(ct::DigitallySigned::HashAlgorithm alg) {
switch (alg) {
case ct::DigitallySigned::HASH_ALGO_MD5:
return EVP_md5();
case ct::DigitallySigned::HASH_ALGO_SHA1:
return EVP_sha1();
case ct::DigitallySigned::HASH_ALGO_SHA224:
return EVP_sha224();
case ct::DigitallySigned::HASH_ALGO_SHA256:
return EVP_sha256();
case ct::DigitallySigned::HASH_ALGO_SHA384:
return EVP_sha384();
case ct::DigitallySigned::HASH_ALGO_SHA512:
return EVP_sha512();
case ct::DigitallySigned::HASH_ALGO_NONE:
default:
NOTREACHED();
}
}
}
scoped_refptr<const CTLogVerifier> CTLogVerifier::Create(
std::string_view public_key,
std::string description) {
auto result = base::WrapRefCounted(new CTLogVerifier(std::move(description)));
if (!result->Init(public_key))
return nullptr;
return result;
}
CTLogVerifier::CTLogVerifier(std::string description)
: description_(std::move(description)) {}
bool CTLogVerifier::Verify(const ct::SignedEntryData& entry,
const ct::SignedCertificateTimestamp& sct) const {
std::string serialized_log_entry;
std::string serialized_data;
return sct.log_id == key_id_ && SignatureParametersMatch(sct.signature) &&
ct::EncodeSignedEntry(entry, &serialized_log_entry) &&
ct::EncodeV1SCTSignedData(sct.timestamp, serialized_log_entry,
sct.extensions, &serialized_data) &&
VerifySignature(serialized_data, sct.signature.signature_data);
}
bool CTLogVerifier::VerifySignedTreeHead(
const ct::SignedTreeHead& signed_tree_head) const {
std::string serialized_data;
if (!SignatureParametersMatch(signed_tree_head.signature) ||
!ct::EncodeTreeHeadSignature(signed_tree_head, &serialized_data) ||
!VerifySignature(serialized_data,
signed_tree_head.signature.signature_data)) {
return false;
}
if (signed_tree_head.tree_size == 0) {
return signed_tree_head.sha256_root_hash == kSHA256EmptyStringHash;
}
return true;
}
bool CTLogVerifier::SignatureParametersMatch(
const ct::DigitallySigned& signature) const {
return signature.SignatureParametersMatch(hash_algorithm_,
signature_algorithm_);
}
bool CTLogVerifier::VerifyConsistencyProof(
const ct::MerkleConsistencyProof& proof,
const std::string& old_tree_hash,
const std::string& new_tree_hash) const {
if (key_id_ != proof.log_id)
return false;
if (proof.first_tree_size > proof.second_tree_size)
return false;
if (proof.first_tree_size == proof.second_tree_size)
return proof.nodes.empty() && old_tree_hash == new_tree_hash;
if (proof.first_tree_size == 0)
return proof.nodes.empty();
DCHECK_LT(0u, proof.first_tree_size);
DCHECK_LT(proof.first_tree_size, proof.second_tree_size);
std::string_view first_proof_node = old_tree_hash;
auto iter = proof.nodes.begin();
if (!std::has_single_bit(proof.first_tree_size)) {
if (iter == proof.nodes.end())
return false;
first_proof_node = *iter;
++iter;
}
uint64_t fn = proof.first_tree_size - 1;
uint64_t sn = proof.second_tree_size - 1;
while (fn & 1) {
fn >>= 1;
sn >>= 1;
}
std::string fr(first_proof_node);
std::string sr(first_proof_node);
for (; iter != proof.nodes.end(); ++iter) {
if (sn == 0)
return false;
if ((fn & 1) || fn == sn) {
fr = ct::internal::HashNodes(*iter, fr);
sr = ct::internal::HashNodes(*iter, sr);
while (!(fn & 1) && fn != 0) {
fn >>= 1;
sn >>= 1;
}
} else {
sr = ct::internal::HashNodes(sr, *iter);
}
fn >>= 1;
sn >>= 1;
}
return fr == old_tree_hash && sr == new_tree_hash && sn == 0;
}
bool CTLogVerifier::VerifyAuditProof(const ct::MerkleAuditProof& proof,
const std::string& root_hash,
const std::string& leaf_hash) const {
if (proof.leaf_index >= proof.tree_size)
return false;
uint64_t fn = proof.leaf_index;
uint64_t sn = proof.tree_size - 1;
std::string r = leaf_hash;
for (const std::string& p : proof.nodes) {
if (sn == 0)
return false;
if ((fn & 1) || fn == sn) {
r = ct::internal::HashNodes(p, r);
while (!(fn & 1) && fn != 0) {
fn >>= 1;
sn >>= 1;
}
} else {
r = ct::internal::HashNodes(r, p);
}
fn >>= 1;
sn >>= 1;
}
return sn == 0 && r == root_hash;
}
CTLogVerifier::~CTLogVerifier() = default;
bool CTLogVerifier::Init(std::string_view public_key) {
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
public_key_ = crypto::evp::PublicKeyFromBytes(base::as_byte_span(public_key));
if (!public_key_) {
return false;
}
key_id_ = base::as_string_view(crypto::hash::Sha256(public_key));
switch (EVP_PKEY_id(public_key_.get())) {
case EVP_PKEY_RSA:
hash_algorithm_ = ct::DigitallySigned::HASH_ALGO_SHA256;
signature_algorithm_ = ct::DigitallySigned::SIG_ALGO_RSA;
break;
case EVP_PKEY_EC:
hash_algorithm_ = ct::DigitallySigned::HASH_ALGO_SHA256;
signature_algorithm_ = ct::DigitallySigned::SIG_ALGO_ECDSA;
break;
default:
return false;
}
if (signature_algorithm_ == ct::DigitallySigned::SIG_ALGO_RSA &&
EVP_PKEY_size(public_key_.get()) < 256) {
return false;
}
return true;
}
bool CTLogVerifier::VerifySignature(std::string_view data_to_sign,
std::string_view signature) const {
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
const EVP_MD* hash_alg = GetEvpAlg(hash_algorithm_);
bssl::ScopedEVP_MD_CTX ctx;
return hash_alg &&
EVP_DigestVerifyInit(ctx.get(), nullptr, hash_alg, nullptr,
public_key_.get()) &&
EVP_DigestVerifyUpdate(ctx.get(), data_to_sign.data(),
data_to_sign.size()) &&
EVP_DigestVerifyFinal(
ctx.get(), reinterpret_cast<const uint8_t*>(signature.data()),
signature.size());
}
}