def _submit_to_all_logs(log_list, certs_chain): """Submits the chain to all logs in log_list and validates SCTs.""" log_id_to_verifier = _map_log_id_to_verifier(log_list) chain_der = [c.to_der() for c in certs_chain] raw_scts_for_cert = [] for log_url in log_list.keys(): res = _submit_to_single_log(log_url, chain_der) if res: raw_scts_for_cert.append(res) else: logging.info("No SCT from log %s", log_url) validated_scts = [] for raw_sct in raw_scts_for_cert: key_id = raw_sct.id.key_id try: log_id_to_verifier[key_id].verify_sct(raw_sct, certs_chain) validated_scts.append(raw_sct) except error.SignatureError as err: logging.warning( 'Discarding SCT from log_id %s which does not validate: %s', key_id.encode('hex'), err) except KeyError as err: logging.warning('Could not find CT log validator for log_id %s. ' 'The log key for this log is probably misconfigured.', key_id.encode('hex')) scts_for_cert = [tls_message.encode(proto_sct) for proto_sct in validated_scts if proto_sct] sct_list = client_pb2.SignedCertificateTimestampList() sct_list.sct_list.extend(scts_for_cert) return tls_message.encode(sct_list)
def verify_sct(self, sct, chain): """Verify the SCT over the X.509 certificate provided Args: sct: client_pb2.SignedCertificateTimestamp proto. Must have all fields present. chain: list of cert.Certificate instances. Begins with the certificate to be checked. Returns: True. The return value is enforced by a decorator and need not be checked by the caller. Raises: ct.crypto.error.EncodingError: failed to encode signature input, or decode the signature. ct.crypto.error.SignatureError: invalid signature. ct.crypto.error.IncompleteChainError: a certificate is missing from the chain. """ if sct.version != ct_pb2.V1: raise error.UnsupportedVersionError("Cannot handle version: %s" % sct.version) entry = _create_dst_entry(sct, chain) signature_input = tls_message.encode(entry) self._assert_correct_signature_algorithms(sct.signature.hash_algorithm, sct.signature.sig_algorithm) return self.__sig_verifier.verify(signature_input, sct.signature.signature)
def verify_sct(self, sct, chain): """Verify the SCT over the X.509 certificate provided Args: sct: client_pb2.SignedCertificateTimestamp proto. Must have all fields present. chain: list of cert.Certificate instances. Begins with the certificate to be checked. Returns: True. The return value is enforced by a decorator and need not be checked by the caller. Raises: ct.crypto.error.EncodingError: failed to encode signature input, or decode the signature. ct.crypto.error.SignatureError: invalid signature. ct.crypto.error.IncompleteChainError: a certificate is missing from the chain. """ if sct.version != ct_pb2.V1: raise error.UnsupportedVersionError("Cannot handle version: %s" % sct.version) entry = _create_dst_entry(sct, chain) signature_input = tls_message.encode(entry) return self._verify(signature_input, sct.signature.signature)
def verify_sct(self, sct, certificate): """Verify the SCT over the X.509 certificate provided Not suitable for Precertificates. Args: sct: client_pb2.SignedCertificateTimestamp proto. Must have all fields present. certificate: cert.Certificate instance. Returns: True. The return value is enforced by a decorator and need not be checked by the caller. Raises: ct.crypto.error.EncodingError: failed to encode signature input, or decode the signature. ct.crypto.error.SignatureError: invalid signature. """ if sct.version != ct_pb2.V1: raise error.UnsupportedVersionError("Cannot handle version: %s" % sct.version) dsentry = client_pb2.DigitallySignedTimestampedEntry() dsentry.sct_version = ct_pb2.V1 dsentry.signature_type = client_pb2.CERTIFICATE_TIMESTAMP dsentry.timestamp = sct.timestamp dsentry.entry_type = client_pb2.X509_ENTRY dsentry.asn1_cert = certificate.to_der() dsentry.ct_extensions = sct.extensions signature_input = tls_message.encode(dsentry) return self._verify(signature_input, sct.signature.signature)
def test_correctly_encodes_sct(self): sct = tls_message.encode(self._sct_proto) expected_sct = ("00a4b90990b418581487bb13a2cc67700a3c359804f91bdfb8e377" "cd0ec80ddc100000013de9d2b29b000004030047304502210089de" "897f603e590b1aa0d7c4236c2f697e90602795f7a469215fda5e46" "0123fc022065ab501ce3dbaf49bd563d1c9ff0ac76120bc11f65a4" "4122b3cd8b89fc77a48c").decode("hex") self.assertEqual(sct, expected_sct)
def create_leaf(timestamp, x509_cert_bytes): """Creates a MerkleTreeLeaf for the given X509 certificate.""" leaf = client_pb2.MerkleTreeLeaf() leaf.version = client_pb2.V1 leaf.leaf_type = client_pb2.TIMESTAMPED_ENTRY leaf.timestamped_entry.timestamp = timestamp leaf.timestamped_entry.entry_type = client_pb2.X509_ENTRY leaf.timestamped_entry.asn1_cert = x509_cert_bytes return tls_message.encode(leaf)
def test_correctly_encodes_sct_list_one_sct(self): # Taken from the C++ serializer test, to ensure this encoder # produces results compatible with the C++ one. single_sct = ("0069616d617075626c69636b657973686174776f6669766573697864" "696765737400000000000004d20000040300097369676e6174757265" ).decode("hex") sct_list = client_pb2.SignedCertificateTimestampList() sct_list.sct_list.append(single_sct) encoded_sct_list = tls_message.encode(sct_list) self.assertEqual(encoded_sct_list[:4], "003a0038".decode("hex")) self.assertEqual(encoded_sct_list[4:], single_sct)
def test_correctly_encodes_sct_list_multiple_scts(self): first_sct = tls_message.encode(self._sct_proto) sct_proto_2 = client_pb2.SignedCertificateTimestamp() sct_proto_2.CopyFrom(self._sct_proto) sct_proto_2.timestamp = 1365427530000 second_sct = tls_message.encode(sct_proto_2) sct_list = client_pb2.SignedCertificateTimestampList() sct_list.sct_list.extend([first_sct, second_sct]) encoded_sct_list = tls_message.encode(sct_list) # First 2 bytes are list length prefix - 240 bytes in total # Next 2 bytes are the length of the first SCT: 118 self.assertEqual(encoded_sct_list[:4], "00f00076".decode("hex")) first_sct_end = len(first_sct) + 4 # The actual SCT self.assertEqual(encoded_sct_list[4:first_sct_end], first_sct) # Next 2 bytes are the length of the second SCT (118 again) self.assertEqual(encoded_sct_list[first_sct_end:first_sct_end + 2], "0076".decode("hex")) # The 2nd SCT self.assertEqual(encoded_sct_list[first_sct_end + 2:], second_sct)
def test_correctly_encodes_sct_list_multiple_scts(self): first_sct = tls_message.encode(self._sct_proto) sct_proto_2 = client_pb2.SignedCertificateTimestamp() sct_proto_2.CopyFrom(self._sct_proto) sct_proto_2.timestamp = 1365427530000 second_sct = tls_message.encode(sct_proto_2) sct_list = client_pb2.SignedCertificateTimestampList() sct_list.sct_list.extend([first_sct, second_sct]) encoded_sct_list = tls_message.encode(sct_list) # First 2 bytes are list length prefix - 240 bytes in total # Next 2 bytes are the length of the first SCT: 118 self.assertEqual(encoded_sct_list[:4], "00f00076".decode("hex")) first_sct_end = len(first_sct) + 4 # The actual SCT self.assertEqual(encoded_sct_list[4:first_sct_end], first_sct) # Next 2 bytes are the length of the second SCT (118 again) self.assertEqual(encoded_sct_list[first_sct_end:first_sct_end+2], "0076".decode("hex")) # The 2nd SCT self.assertEqual(encoded_sct_list[first_sct_end+2:], second_sct)
def verify_encode(self, test_message, test_vector): serialized = tls_message.encode(test_message) self.assertEqual("".join(test_vector), serialized.encode("hex"))