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 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 dump_sctlist(sct_list): """Prints the proto representation of the SCTs contained in sct_list. Arguments: sct_list the packed SignedCertificateTransparencyList structure. """ tr = tls_message.TLSReader(sct_list) sctlist = client_pb2.SignedCertificateTimestampList() tr.read(sctlist) for s in sctlist.sct_list: sct = client_pb2.SignedCertificateTimestamp() tls_message.decode(s, sct) print(sct)
def verify_embedded_scts(self, chain): """Extract and verify SCTs embedded in an X.509 certificate. Args: chain: list of cert.Certificate instances. Returns: List of (SignedCertificateTimestamp, bool) pairs, one for each SCT present in the certificate. The boolean is True if the corresponding SCT is valid, False otherwise. Raises: ct.crypto.error.EncodingError: failed to encode signature input, or decode the signature. ct.crypto.error.IncompleteChainError: the chain is empty. """ try: leaf_cert = chain[0] except IndexError: raise error.IncompleteChainError( "Chain must contain leaf certificate.") scts_blob = leaf_cert.embedded_sct_list() if scts_blob is None: return [] scts = client_pb2.SignedCertificateTimestampList() tls_message.decode(scts_blob, scts) result = [] for sct_blob in scts.sct_list: sct = client_pb2.SignedCertificateTimestamp() tls_message.decode(sct_blob, sct) try: self.verify_sct(sct, chain) result.append((sct, True)) except error.VerifyError: result.append((sct, False)) return result
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)