Commit 89f010d8 authored by robpercival's avatar robpercival Committed by Commit bot

Adds a VerifyAuditProof method to CTLogVerifier

This allows audit (aka. inclusion) proofs to be verified, which helps check that CT logs are behaving correctly.

BUG=631087

Review-Url: https://codereview.chromium.org/2182533002
Cr-Commit-Position: refs/heads/master@{#430264}
parent 974ae20b
......@@ -6,11 +6,14 @@
#include <string.h>
#include <vector>
#include "base/logging.h"
#include "crypto/openssl_util.h"
#include "crypto/sha2.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/bytestring.h"
......@@ -242,6 +245,61 @@ bool CTLogVerifier::VerifyConsistencyProof(
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 {
// Implements the algorithm described in
// https://tools.ietf.org/html/draft-ietf-trans-rfc6962-bis-19#section-10.4.1
//
// It maintains a hash |r|, initialized to |leaf_hash|, and hashes nodes from
// |proof| into it. The proof is then valid if |r| is |root_hash|, proving
// that |root_hash| includes |leaf_hash|.
// 1. Compare "leaf_index" against "tree_size". If "leaf_index" is
// greater than or equal to "tree_size" fail the proof verification.
if (proof.leaf_index >= proof.tree_size)
return false;
// 2. Set "fn" to "leaf_index" and "sn" to "tree_size - 1".
uint64_t fn = proof.leaf_index;
uint64_t sn = proof.tree_size - 1;
// 3. Set "r" to "hash".
std::string r = leaf_hash;
// 4. For each value "p" in the "inclusion_path" array:
for (const std::string& p : proof.nodes) {
// If "sn" is 0, stop the iteration and fail the proof verification.
if (sn == 0)
return false;
// If "LSB(fn)" is set, or if "fn" is equal to "sn", then:
if ((fn & 1) || fn == sn) {
// 1. Set "r" to "HASH(0x01 || p || r)"
r = ct::internal::HashNodes(p, r);
// 2. If "LSB(fn)" is not set, then right-shift both "fn" and "sn"
// equally until either "LSB(fn)" is set or "fn" is "0".
while (!(fn & 1) && fn != 0) {
fn >>= 1;
sn >>= 1;
}
} else { // Otherwise:
// Set "r" to "HASH(0x01 || r || p)"
r = ct::internal::HashNodes(r, p);
}
// Finally, right-shift both "fn" and "sn" one time.
fn >>= 1;
sn >>= 1;
}
// 5. Compare "sn" to 0. Compare "r" against the "root_hash". If "sn"
// is equal to 0, and "r" and the "root_hash" are equal, then the
// log has proven the inclusion of "hash". Otherwise, fail the
// proof verification.
return sn == 0 && r == root_hash;
}
CTLogVerifier::~CTLogVerifier() {
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
......
......@@ -21,10 +21,9 @@ typedef struct evp_pkey_st EVP_PKEY;
namespace net {
namespace ct {
struct SignedTreeHead;
struct MerkleAuditProof;
struct MerkleConsistencyProof;
struct SignedTreeHead;
} // namespace ct
// Class for verifying signatures of a single Certificate Transparency
......@@ -76,6 +75,14 @@ class NET_EXPORT CTLogVerifier
const std::string& old_tree_hash,
const std::string& new_tree_hash) const;
// Verifies that |proof| is a valid audit proof (RFC 6962, Section 2.1.1) for
// this log, and which proves that the certificate represented by |leaf_hash|
// has been incorporated into the Merkle tree represented by |root_hash|.
// Returns true if verification succeeds, false otherwise.
bool VerifyAuditProof(const ct::MerkleAuditProof& proof,
const std::string& root_hash,
const std::string& leaf_hash) const;
private:
FRIEND_TEST_ALL_PREFIXES(CTLogVerifierTest, VerifySignature);
friend class base::RefCountedThreadSafe<CTLogVerifier>;
......
This diff is collapsed.
......@@ -31,8 +31,9 @@ uint64_t CalculateAuditPathLength(uint64_t leaf_index, uint64_t tree_size) {
MerkleAuditProof::MerkleAuditProof() {}
MerkleAuditProof::MerkleAuditProof(uint64_t leaf_index,
uint64_t tree_size,
const std::vector<std::string>& audit_path)
: leaf_index(leaf_index), nodes(audit_path) {}
: leaf_index(leaf_index), tree_size(tree_size), nodes(audit_path) {}
MerkleAuditProof::~MerkleAuditProof() {}
......
......@@ -26,13 +26,21 @@ NET_EXPORT uint64_t CalculateAuditPathLength(uint64_t leaf_index,
struct NET_EXPORT MerkleAuditProof {
MerkleAuditProof();
MerkleAuditProof(uint64_t leaf_index,
uint64_t tree_size,
const std::vector<std::string>& audit_path);
~MerkleAuditProof();
// Index of the tree leaf in the log.
// Must be provided when fetching the proof from the log.
uint64_t leaf_index = 0;
// The proof works only in conjunction with an STH for this tree size.
// Must be provided when fetching the proof from the log.
uint64_t tree_size = 0;
// Audit path nodes.
// Using the leaf hash and these nodes, the STH hash can be reconstructed to
// prove that leaf was included in the log's tree.
std::vector<std::string> nodes;
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment