def run(): """Fetch the proof for the supplied certificate.""" #TODO(eranm): Attempt fetching the SCT for this chain if none was given. if FLAGS.sct: cert_sct = client_pb2.SignedCertificateTimestamp() sct_data = open(FLAGS.sct, 'rb').read() if FLAGS.binary_sct: tls_message.decode(sct_data, cert_sct) else: cert_sct.ParseFromString(sct_data) sct_timestamp = cert_sct.timestamp print 'SCT for cert:', cert_sct else: sct_timestamp = FLAGS.timestamp constructed_leaf = construct_leaf_from_file(FLAGS.cert, sct_timestamp) leaf_hash = merkle.TreeHasher().hash_leaf(constructed_leaf) if FLAGS.verbose: print "Leaf hash: %s" % (leaf_hash.encode("hex")) (sth, proof) = fetch_single_proof(leaf_hash, FLAGS.log_url) if FLAGS.verbose: print "Leaf index in tree is %d, proof has %d hashes" % ( proof.leaf_index, len(proof.audit_path)) print "Audit path: %s" % ([t.encode('hex') for t in proof.audit_path]) verifier = merkle.MerkleVerifier() if verifier.verify_leaf_inclusion(constructed_leaf, proof.leaf_index, proof.audit_path, sth): print 'Proof verifies OK.'
def test_verify_leaf_inclusion_single_node_in_tree(self): # If there is only one entry in the tree, the tree root hash should be # equal to the leaf hash. verifier = merkle.MerkleVerifier(HexTreeHasher()) sth = self.STH(self.leaf_hash, 1) self.assertTrue( verifier.verify_leaf_inclusion(self.raw_hex_leaf, 0, [], sth))
def test_verify_leaf_inclusion_good_proof(self): verifier = merkle.MerkleVerifier(HexTreeHasher()) sth = self.STH(self.expected_root_hash, self.tree_size) self.assertTrue( verifier.verify_leaf_inclusion( self.raw_hex_leaf, self.leaf_index, self.sha256_audit_path, sth))
def test_verify_leaf_inclusion_bad_proof(self): verifier = merkle.MerkleVerifier(HexTreeHasher()) # Expect this test to fail by providing an incorrect root hash. sth = self.STH(self.zeros, self.tree_size) self.assertRaises( error.ProofError, verifier.verify_leaf_inclusion, self.raw_hex_leaf, self.leaf_index, self.sha256_audit_path, sth)
def test_verify_leaf_inclusion_all_nodes_all_tree_sizes_up_to_4(self): leaves = ["aa", "bb", "cc", "dd"] hh = HexTreeHasher() leaf_hashes = [hh.hash_leaf(l) for l in leaves] hc = hh.hash_children proofs_per_tree_size = { 1: [[] ], 2: [[leaf_hashes[1]], [leaf_hashes[0]]], 3: [[leaf_hashes[1], leaf_hashes[2]], # leaf 0 [leaf_hashes[0], leaf_hashes[2]], # leaf 1 [hc(leaf_hashes[0], leaf_hashes[1])]], # leaf 2 4: [[leaf_hashes[1], hc(leaf_hashes[2], leaf_hashes[3])], # leaf 0 [leaf_hashes[0], hc(leaf_hashes[2], leaf_hashes[3])], # leaf 1 [leaf_hashes[3], hc(leaf_hashes[0], leaf_hashes[1])], # leaf 2 [leaf_hashes[2], hc(leaf_hashes[0], leaf_hashes[1])], # leaf 3 ] } tree = merkle.CompactMerkleTree(hasher=HexTreeHasher()) verifier = merkle.MerkleVerifier(HexTreeHasher()) # Increase the tree by one leaf each time for i in range(4): tree.append(leaves[i]) tree_size = i + 1 # ... and check inclusion proof validates for each node # of the tree for j in range(tree_size): proof = proofs_per_tree_size[tree_size][j] sth = self.STH(tree.root_hash(), tree_size) self.assertTrue( verifier.verify_leaf_inclusion( leaves[j], j, proof, sth))
def test_calculate_root_hash_good_proof(self): verifier = merkle.MerkleVerifier(HexTreeHasher()) self.assertEqual( verifier._calculate_root_hash_from_audit_path( self.leaf_hash, self.leaf_index, self.sha256_audit_path[:], self.tree_size), self.expected_root_hash)
def test_verify_leaf_inclusion_throws_on_bad_indices(self): verifier = merkle.MerkleVerifier(HexTreeHasher()) sth = self.STH("", 6) self.assertRaises(ValueError, verifier.verify_leaf_inclusion, "", -3, [], sth) negative_sth = self.STH("", -3) self.assertRaises(ValueError, verifier.verify_leaf_inclusion, "", 3, [], negative_sth)
def test_verify_tree_consistency_proof_too_short(self): verifier = merkle.MerkleVerifier(HexTreeHasher()) self.assertRaises( error.ProofError, verifier.verify_tree_consistency, 6, 8, "76e67dadbcdf1e10e1b74ddc608abd2f98dfb16fbce75277b5232a127f2087ef", "5dc9da79a70659a9ad559cb701ded9a2ab9d823aad2f4960cfe370eff4604328", ["0ebc5d3437fbe2db158b9f126a1d118e308181031d0a949f8dededebc558ef6a", "ca854ea128ed050b41b35ffc1b87b8eb2bde461e9e3b5596ece6b9d5975a0ae0"] )
def test_verify_tree_consistency_newer_tree_is_smaller(self): verifier = merkle.MerkleVerifier(HexTreeHasher()) self.assertRaises( ValueError, verifier.verify_tree_consistency, 5, 2, "4e3bbb1f7b478dcfe71fb631631519a3bca12c9aefca1612bfce4c13a86264d4", "fac54203e7cc696cf0dfcb42c92a1d9dbaf70ad9e621f4bd8d98662f00e3c125", ["5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e", "bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b"] )
def test_verify_tree_consistency_always_accepts_empty_tree(self): verifier = merkle.MerkleVerifier(HexTreeHasher()) # Give some bogus proof too; it should be ignored. self.assertTrue(verifier.verify_tree_consistency( 0, 1, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", ["6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d"] ))
def test_calculate_root_too_short_proof(self): verifier = merkle.MerkleVerifier(HexTreeHasher()) leaf_index = self.leaf_index + int( math.pow(2, len(self.sha256_audit_path) + 1)) self.assertRaises( error.ProofError, verifier._calculate_root_hash_from_audit_path, self.leaf_hash, leaf_index, self.sha256_audit_path[:], self.tree_size)
def __init__(self, key_info, merkle_verifier=merkle.MerkleVerifier()): """Initialize from KeyInfo protocol buffer and a MerkleVerifier.""" self.__merkle_verifier = merkle_verifier if (key_info.type == client_pb2.KeyInfo.ECDSA): self.__sig_verifier = verify_ecdsa.EcdsaVerifier(key_info) elif (key_info.type == client_pb2.KeyInfo.RSA): self.__sig_verifier = verify_rsa.RsaVerifier(key_info) else: raise error.UnsupportedAlgorithmError("Key type %d not supported" % key_info.type)
def test_verify_leaf_inclusion_incorrect_length_proof(self): verifier = merkle.MerkleVerifier(HexTreeHasher()) sth = self.STH(self.zeros, 4) # Too long a proof self.assertRaises(error.ProofError, verifier.verify_leaf_inclusion, self.ones, 0, [self.zeros, self.zeros, self.zeros], sth) # Too short a proof self.assertRaises(error.ProofError, verifier.verify_leaf_inclusion, self.ones, 0, [self.zeros], sth)
def test_verify_tree_consistency_bad_first_hash(self): verifier = merkle.MerkleVerifier(HexTreeHasher()) # A bit has been flipped in the first hash. self.assertRaises( error.ConsistencyError, verifier.verify_tree_consistency, 6, 8, "76e67dadbcdf1e10e1b74ddc608abd2f98dfb16fbce75277b5232a127f2087ee", "5dc9da79a70659a9ad559cb701ded9a2ab9d823aad2f4960cfe370eff4604328", ["0ebc5d3437fbe2db158b9f126a1d118e308181031d0a949f8dededebc558ef6a", "ca854ea128ed050b41b35ffc1b87b8eb2bde461e9e3b5596ece6b9d5975a0ae0", "d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7"] )
def test_verify_leaf_inclusion_rightmost_node_in_unbalanced_odd_tree( self): # Show that verify_leaf_inclusion works when required to check a proof # for the right-most, even-indexed node: In a tree of 5 nodes, ask for # inclusion proof check for leaf 4 (the 5th). verifier = merkle.MerkleVerifier(HexTreeHasher()) hh = HexTreeHasher() h_s1 = hh.hash_leaf(self.ones) h_root = hh.hash_children(self.zeros, h_s1) sth = self.STH(h_root, 5) self.assertTrue( verifier.verify_leaf_inclusion(self.ones, 4, [self.zeros, ], sth))
def __init__(self, key_info, merkle_verifier=merkle.MerkleVerifier()): """Initialize from KeyInfo protocol buffer and a MerkleVerifier.""" self.__merkle_verifier = merkle_verifier if key_info.type != client_pb2.KeyInfo.ECDSA: raise error.UnsupportedAlgorithmError("Key type %d not supported" % key_info.type) # Will raise a PemError on invalid encoding self.__der, _ = pem.from_pem(key_info.pem_key, LogVerifier.__ECDSA_READ_MARKERS) try: self.__pubkey = ecdsa.VerifyingKey.from_der(self.__der) except ecdsa.der.UnexpectedDER as e: raise error.EncodingError(e)
def test_verify_leaf_inclusion_rightmost_node_in_tree(self): # Show that verify_leaf_inclusion works when required to check a proof # for the right-most node: In a tree of 8 nodes, ask for inclusion # proof check for leaf 7. verifier = merkle.MerkleVerifier(HexTreeHasher()) hh = HexTreeHasher() h_s1 = hh.hash_leaf(self.ones) h_c3 = hh.hash_children(self.zeros, h_s1) h_c2 = hh.hash_children(self.zeros, h_c3) h_root = hh.hash_children(self.zeros, h_c2) sth = self.STH(h_root, 8) self.assertTrue( verifier.verify_leaf_inclusion( self.ones, 7, [self.zeros, self.zeros, self.zeros], sth))
def test_verify_tree_consistency_for_equal_tree_sizes(self): verifier = merkle.MerkleVerifier(HexTreeHasher()) # Equal tree sizes and hashes, and a bogus proof that should be ignored. self.assertTrue(verifier.verify_tree_consistency( 3, 3, "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", ["6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d"] )) # Equal tree sizes but different hashes. self.assertRaises( error.ConsistencyError, verifier.verify_tree_consistency, 3, 3, "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01e", "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", [])
def __init__(self, ct_logs, db, cert_db, temp_db_factory, monitor_state_dir, agent=None, state_keeper_class=None): """Initialize from a CtLogs proto.""" threading.Thread.__init__(self) self.__monitors = [] self.__db = db if not agent: agent = twisted_client.Agent(reactor) if not state_keeper_class: state_keeper_class = state.StateKeeper for log in ct_logs.ctlog: if not log.log_server or not log.log_id or not log.public_key_info: raise RuntimeError("Cannot start monitor: log proto has " "missing or empty fields: %s" % log) try: temp_db = temp_db_factory.create_storage(log.log_server) client = async_log_client.AsyncLogClient( agent, log.log_server, temp_db) hasher = merkle.TreeHasher() verifier = verify.LogVerifier(log.public_key_info, merkle.MerkleVerifier(hasher)) # Convert from standard Base64 to URL-safe Base64 so that the # log ID can be used as part of a file path. log_id_urlsafe = log.log_id.replace('/', '_').replace('+', '-') state_keeper = state_keeper_class(monitor_state_dir + "/" + log_id_urlsafe) log_key = db.get_log_id(log.log_server) self.__monitors.append( monitor.Monitor(client, verifier, hasher, db, cert_db, log_key, state_keeper)) except: logging.error("Error starting monitor for log: %s" % log) raise self.__last_update_start_time = 0 self.__stopped = False self.__called_later = None
def test_tree_consistency_proof_generated(self): """Test consistency proof generation. Consistency proof generation correctness test for generated proofs. """ leaves = [] for i in range(128): leaves.append(chr(i) * 32) tree = in_memory_merkle_tree.InMemoryMerkleTree(leaves) verifier = merkle.MerkleVerifier() for i in range(1, tree.tree_size()): for j in range(i): consistency_proof = tree.get_consistency_proof(j, i) self.assertTrue(verifier.verify_tree_consistency( j, i, tree.get_root_hash(j), tree.get_root_hash(i), consistency_proof))
def test_tree_inclusion_proof_generated(self): """Test inclusion proof generation. Test inclusion proof generation correctness for generated proofs. """ leaves = [] leaf_hashes = [] hasher = merkle.TreeHasher() for i in range(128): leaves.append(chr(i) * 32) leaf_hashes.append(hasher.hash_leaf(leaves[-1])) tree = in_memory_merkle_tree.InMemoryMerkleTree(leaves) verifier = merkle.MerkleVerifier() for i in range(1, tree.tree_size()): for j in range(i): audit_path = tree.get_inclusion_proof(j, i) dummy_sth = DummySTH(i, tree.get_root_hash(i)) verifier.verify_leaf_hash_inclusion( leaf_hashes[j], j, audit_path, dummy_sth)
def test_tree_inclusion_proof_precomputed(self): """Test inclusion proof generation. Test inclusion proof generation correctness test for known-good proofs. """ tree = in_memory_merkle_tree.InMemoryMerkleTree(TEST_VECTOR_DATA) verifier = merkle.MerkleVerifier() for v in PRECOMPUTED_PATH_TEST_VECTORS: audit_path = tree.get_inclusion_proof(v.leaf, v.tree_size_snapshot) self.assertEqual(len(audit_path), v.path_length) self.assertEqual(audit_path, v.path) leaf_data = TEST_VECTOR_DATA[v.leaf] leaf_hash = merkle.TreeHasher().hash_leaf(leaf_data) dummy_sth = DummySTH(v.tree_size_snapshot, tree.get_root_hash(v.tree_size_snapshot)) if v.tree_size_snapshot > 0: verifier.verify_leaf_hash_inclusion( leaf_hash, v.leaf, audit_path, dummy_sth)
def __init__(self, ct_logs, db, temp_db_factory, monitor_state_dir): """Initialize from a CtLogs proto.""" threading.Thread.__init__(self) self.__monitors = [] self.__db = db for log in ct_logs.ctlog: if not log.log_server or not log.log_id or not log.public_key_info: raise RuntimeError("Cannot start monitor: log proto has " "missing or empty fields: %s" % log) client = log_client.LogClient(log.log_server) hasher = merkle.TreeHasher() verifier = verify.LogVerifier(log.public_key_info, merkle.MerkleVerifier(hasher)) state_keeper = state.StateKeeper(FLAGS.monitor_state_dir + "/" + log.log_id) temp_db = temp_db_factory.create_storage(log.log_server) self.__monitors.append( monitor.Monitor(client, verifier, hasher, db, temp_db, state_keeper)) self.__last_update_start_time = 0 self.__stopped = False
leaf_input = j['entries'][0]['leaf_input'] logging.info('leaf = %s', leaf_input) leaf = base64.b64decode(leaf_input) leaf_hash = hashlib.sha256(chr(0) + leaf).digest() keyinfo = client_pb2.KeyInfo() keyinfo.type = keyinfo.ECDSA keyinfo.pem_key = keypem log_verifier = verify.LogVerifier(keyinfo) lookup = CTDNSLookup(logdns, log_verifier) sth = lookup.GetSTH() logging.info('sth = %s', sth) logging.info('hash = %s', base64.b64encode(leaf_hash)) verifier = merkle.MerkleVerifier() index = int(index) audit_path = [] prev = None apl = verifier.audit_path_length(index, sth.tree_size) for level in range(0, apl): h = lookup.GetEntry(level, index, sth.tree_size) logging.info('hash = %s', base64.b64encode(h)) audit_path.append(h[:32]) if prev: if level < apl - 6: assert prev[32:] == h[:-32] else: assert prev[32:] == h else:
def setUp(self): self.verifier = merkle.MerkleVerifier(HexTreeHasher()) self.STH = namedtuple("STH", ["sha256_root_hash", "tree_size"]) self.ones = "11" * 32 self.zeros = "00" * 32
def test_verify_tree_consistency(self): verifier = merkle.MerkleVerifier(HexTreeHasher()) for test_vector in MerkleVerifierTest.sha256_proofs: self.assertTrue(verifier.verify_tree_consistency(*test_vector))