def generate_csr(domain): key = generate_private_key(SECP384R1(), default_backend()) builder = x509.CertificateSigningRequestBuilder() builder = builder.subject_name(x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, domain), ])) builder = builder.add_extension(x509.SubjectAlternativeName([ x509.DNSName(domain), ]), critical=False) csr = builder.sign(key, SHA256(), default_backend()) csr = b64(csr.public_bytes(Encoding.DER)) key = key.private_bytes(Encoding.PEM, PrivateFormat.TraditionalOpenSSL, NoEncryption()) return csr, key
def encrypt(cls, data: bytes, key: SSSSKey, name: str, iv: Optional[IV] = None) -> SSSSData: if iv: ivb = bytearray(iv) else: ivb = bytearray(secrets.token_bytes(16)) ivb[8] = ivb[8] & 0x7f; iv = IV(bytes(ivb)) keys = key.derive_keys(name) ct = aes256ctr(iv, keys.aes_key, data) hm = HMAC(keys.hmac_key, SHA256(), backend = default_backend()) hm.update(ct) mac = MAC(hm.finalize()) return cls(mac, iv, ct)
def test_default_repository_integrity(self): # Given the local repo of certificates repo = RootCertificatesRepository.get_default() # Each certificate that it returns is stored at the expected location expected_repo_path = Path(os.path.abspath( os.path.dirname(__file__))) / '..' / 'certificates' for certificate in repo.get_all_certificates(): expected_file_name = hexlify(certificate.fingerprint( SHA256())).decode('ascii') expected_cert_path = expected_repo_path / f'{expected_file_name}.pem' with open(expected_cert_path) as stored_cert_file: stored_cert_pem = stored_cert_file.read() stored_cert = load_pem_x509_certificate( stored_cert_pem.encode(encoding='ascii'), default_backend()) self.assertEqual(stored_cert, certificate)
def decrypt(secret, hash, data): """ Decrypt per telegram docs at https://core.telegram.org/passport. Args: secret (:obj:`str` or :obj:`bytes`): The encryption secret, either as bytes or as a base64 encoded string. hash (:obj:`str` or :obj:`bytes`): The hash, either as bytes or as a base64 encoded string. data (:obj:`str` or :obj:`bytes`): The data to decrypt, either as bytes or as a base64 encoded string. file (:obj:`bool`): Force data to be treated as raw data, instead of trying to b64decode it. Raises: :class:`TelegramDecryptionError`: Given hash does not match hash of decrypted data. Returns: :obj:`bytes`: The decrypted data as bytes. """ if not CRYPTO_INSTALLED: raise RuntimeError( 'To use Telegram Passports, PTB must be installed via `pip install ' 'python-telegram-bot[passport]`.') # Make a SHA512 hash of secret + update digest = Hash(SHA512(), backend=default_backend()) digest.update(secret + hash) secret_hash_hash = digest.finalize() # First 32 chars is our key, next 16 is the initialisation vector key, init_vector = secret_hash_hash[:32], secret_hash_hash[32:32 + 16] # Init a AES-CBC cipher and decrypt the data cipher = Cipher(AES(key), CBC(init_vector), backend=default_backend()) decryptor = cipher.decryptor() data = decryptor.update(data) + decryptor.finalize() # Calculate SHA256 hash of the decrypted data digest = Hash(SHA256(), backend=default_backend()) digest.update(data) data_hash = digest.finalize() # If the newly calculated hash did not match the one telegram gave us if data_hash != hash: # Raise a error that is caught inside telegram.PassportData and transformed into a warning raise TelegramDecryptionError( f"Hashes are not equal! {data_hash} != {hash}") # Return data without padding return data[data[0]:]
def attest_fido_u2f( att_stmt: FIDOU2FAttestationStatement, att_obj: AttestationObject, auth_data: bytes, client_data_hash: bytes) -> Tuple[AttestationType, TrustedPath]: if len(att_stmt.x5c) != 1: raise ValidationError( 'FIDO U2F verification failed: must have a single X.509 certificate' ) att_cert = att_stmt.x5c[0] att_cert_x509 = cryptography.x509.load_pem_x509_certificate( att_cert, default_backend()) att_cert_x509_pk = att_cert_x509.public_key() if not isinstance(att_cert_x509_pk, EllipticCurvePublicKey): raise ValidationError( 'FIDO U2F verification failed: must use an Elliptic Curve Public Key' ) if not isinstance(att_cert_x509_pk.curve, SECP256R1): raise ValidationError( 'FIDO U2F verification failed: must use an Elliptic Curve Public Key' ) assert att_obj.auth_data is not None assert att_obj.auth_data.attested_credential_data is not None credential_public_key = cast( EC2CredentialPublicKey, att_obj.auth_data.attested_credential_data.credential_public_key) assert credential_public_key is not None public_key_u2f = bytes.fromhex('04') + (credential_public_key.x + credential_public_key.y) rp_id_hash = att_obj.auth_data.rp_id_hash credential_id = att_obj.auth_data.attested_credential_data.credential_id verification_data = (bytes.fromhex('00') + rp_id_hash + (client_data_hash + credential_id + public_key_u2f)) try: att_cert_x509_pk.verify(att_stmt.sig, verification_data, SHA256()) except cryptography.exceptions.InvalidSignature: raise VerificationError( 'FIDO U2F verification failed: invalid signature') return AttestationType.UNCERTAIN, [att_cert_x509]
def test_decrypt_and_unpack_bad_signature(caplog, mock_open_streams): orig, new, _ = mock_open_streams orig_contents = orig._fd.getvalue() cipher = Cipher(AES(TMP_KEY), CTR(TMP_NONCE), backend=default_backend()).encryptor() ct = cipher.update(zlib.compress(orig_contents)) hmac = HMAC(TMP_KEY, SHA256(), default_backend()) hmac.update(ct) signature = hmac.finalize() orig._fd.write(ct) with pytest.raises(BackupCorruptedError): decrypt_and_unpack( orig, new, TMP_KEYPAIR + signature[:-2] + b'f', dict(use_compression=True, use_encryption=True), )
def decrypt(self, ct, iv, mac): ct, iv, mac = bytes(ct, "ascii"), bytes(iv, "ascii"), bytes(mac, "ascii") mac = b64decode(mac) h = HMAC(self.macKey, SHA256(), default_backend()) h.update(ct + iv) h.verify(mac) #verify MAC ct, iv = b64decode(ct), b64decode(iv) decryptor = Cipher(self.cipherAlg, self.mode(iv), default_backend()).decryptor() message = decryptor.update(ct) + decryptor.finalize() #decrypt return message
def decrypt(self, ct, nonce, mac): ct, nonce, mac = bytes(ct, "ascii"), bytes(nonce, "ascii"), bytes(mac, "ascii") mac = b64decode(mac) h = HMAC(self.macKey, SHA256(), default_backend()) h.update(ct + nonce) h.verify(mac) #verify MAC ct, nonce = b64decode(ct), b64decode(nonce) decryptor = Cipher(self.cipherAlg(self.key, nonce), None, default_backend()).decryptor() message = decryptor.update(ct) + decryptor.finalize() #decrypt return message
def pbkdf2(sefl, plaintext): """ Perform PBKDF2 """ backend = default_backend() salt = os.urandom(16) kdf = PBKDF2HMAC(algorithm=SHA256(), length=32, salt=salt, iterations=1000, backend=backend) key = kdf.derive(bytes(plaintext.encode('UTF-8'))) return { "salt": base64.b16encode(salt).decode(), "ciphertext": base64.b16encode(key).decode() }
def test_compress_and_encrypt_no_compression(caplog, mock_open_streams): orig, new, _ = mock_open_streams signature = compress_and_encrypt( orig, new, TMP_KEYPAIR, dict(use_compression=False, use_encryption=True), ) cipher = Cipher(AES(TMP_KEY), CTR(TMP_NONCE), backend=default_backend()).decryptor() hmac = HMAC(TMP_KEY, SHA256(), default_backend()) decrypted = cipher.update(new._fd.getvalue()) hmac.update(new._fd.getvalue()) assert decrypted == orig._fd.getvalue() assert count_matching_log_lines('read 2 bytes from /orig', caplog) == 4 assert count_matching_log_lines('wrote 2 bytes to /new', caplog) == 4 hmac.verify(signature)
def encrypt(self, message): iv = urandom(int(self.cipherAlg.block_size / 8)) #generate random iv encryptor = Cipher(self.cipherAlg, self.mode(iv), default_backend()).encryptor() ct = encryptor.update(message) + encryptor.finalize() #encryption ct, iv = b64encode(ct), b64encode(iv) h = HMAC(self.macKey, SHA256(), default_backend()) h.update(ct + iv) mac = h.finalize() #generate MAC return { "ct": ct.decode("ascii"), "iv_nonce": iv.decode("ascii"), "mac": b64encode(mac).decode("ascii") }
def decrypt(): with open('temp', 'rb') as f: iv = f.read(ivSize) kdf = PBKDF2HMAC( algorithm=SHA256(), length=32, salt=iv, iterations=1000, backend=backend) key2 = kdf.derive(key1) cipher = Cipher(AES(key2), CBC(iv), backend) decryptor = cipher.decryptor() unpadder = pkcs7.unpadder() data = BytesIO() data.write(unpadder.update(decryptor.update(f.read()))) data.write(unpadder.update(decryptor.finalize())) data.write(unpadder.finalize()) return data.getvalue()
def _check_signature(self, payload, public_key, signature): try: loaded_public_key = serialization.load_pem_public_key( data=public_key.encode("utf-8"), backend=default_backend()) loaded_public_key.verify( signature=base64.b64decode(signature), data=payload.encode("utf-8"), # This validates the ECDSA and SHA256 part signature_algorithm=ECDSA(algorithm=SHA256()), ) except InvalidSignature as exc: raise InvalidTokenLeakRequest("Invalid signature", "invalid_signature") from exc except Exception as exc: # Maybe the key is not a valid ECDSA key, maybe the data is not properly # padded, etc. So many things can go wrong... raise InvalidTokenLeakRequest("Invalid cryptographic values", "invalid_crypto") from exc
def create_token(self): a_token = { 'type': 'access_token', 'client': self.id, 'scope': self.scope, } token_ser = tokenserializer.dumps(a_token) h = Hash(SHA256(), default_backend()) h.update(token_ser.encode('utf-8')) self.token = h.finalize() db.session.add(self) db.session.commit() return token_ser
def encrypt(self, message): nonce = urandom(16) #generate random nonce encryptor = Cipher(self.cipherAlg(self.key, nonce), None, default_backend()).encryptor() ct = encryptor.update(message) + encryptor.finalize() #encryption ct, nonce = b64encode(ct), b64encode(nonce) h = HMAC(self.macKey, SHA256(), default_backend()) h.update(ct + nonce) mac = h.finalize() #generate MAC return { "ct": ct.decode("ascii"), "iv_nonce": nonce.decode("ascii"), "mac": b64encode(mac).decode("ascii") }
def check_qrcode_credential(request): if request.method == 'POST': if not Method.objects.get(name='QRCode').status: return JsonResponse(create_msg_to_send(create_status_msg(400, 'QRCode authentication disabled!'), RASP_RSAPUB_KEY)) msg = json.loads(decrypt_msg(request.POST, RASP_ECCPUB_KEY)) msg = json.loads(msg) email = msg['identity'] if User.objects.filter(username=email).exists(): user = User.objects.get(username=email) else: return JsonResponse(create_msg_to_send(create_status_msg(400, 'User does not exist!'), RASP_RSAPUB_KEY)) # TODO: Permission validation (and perm.end_time > timezone.now()) perm = Permission.objects.get(user=user) if perm.state and perm.start_time < timezone.now(): credential = Credential.objects.get(user=user) if credential is not None and credential.status == 'valid': key = bytes.fromhex(credential.data) totp = TOTP(key, 8, SHA256(), 30, backend=default_backend()) try: totp.verify(msg['password'].encode(), time.time()) except InvalidToken: return JsonResponse(create_msg_to_send(create_status_msg(400, 'Authentication Failed!'), RASP_RSAPUB_KEY)) logs = Log.objects.select_related().filter(user=user).all().order_by('time_stamp').reverse() if logs: if logs[0].log_type == 'leave': log = Log(user=user, log_type='entry', time_stamp=timezone.now()) log.save() last_access = {'name': user.last_name, 'img': user.profile.photo.url} send_last_access(last_access) else: log = Log(user=user, log_type='leave', time_stamp=timezone.now()) log.save() else: log = Log(user=user, log_type='entry', time_stamp=timezone.now()) log.save() last_access = {'name': user.last_name, 'img': user.profile.photo.url} send_last_access(last_access) return JsonResponse(create_msg_to_send(create_status_msg(200, 'Authentication Successful', user.last_name.split()[0]), RASP_RSAPUB_KEY)) else: return JsonResponse(create_msg_to_send(create_status_msg(400, 'No permission!'), RASP_RSAPUB_KEY)) else: return JsonResponse(create_msg_to_send(create_status_msg(405, 'Only POST method is allowed!'), RASP_RSAPUB_KEY))
def __init__(self, key: str): """ :param key: The passphrase used to encrypt and decrypt messages. If it is None, no encryption/decryption takes place """ if key is not None and len(key) == 0: key = None if key is None: self.key = None else: # hash the key so that it has a fixed length and is urlsafe as required by the Fernet cipher key = PBKDF2HMAC(algorithm=SHA256(), length=32, salt=b"bit-bots", backend=default_backend(), iterations=10000).derive( bytes(key, encoding="UTF-8")) self.key = base64.urlsafe_b64encode(key)
def kdf_tls13(secret, label, length): digest_type = SHA256() key = b'' block = b'' label = b'tls13 ' + label len_ = struct.pack('!H', length) label = b'%s%s%s%s' % (len_, struct.pack('B', len(label)), label, b'\x00') ind = 0 while len(key) < length: ind += 1 block = IQUIC.hmac( secret, digest_type, b'%s%s%s' % (block, label, struct.pack('B', ind))) key += block return bytearray(key[:length])
def verify_signature(sig, data, pubkey): from cryptography.hazmat.primitives.hashes import SHA256 from cryptography.hazmat.primitives.asymmetric.ec import ECDSA from pyasn1.codec.der.encoder import encode from pyasn1.type.univ import Integer, SequenceOf if len(sig) != 64: return False seq = SequenceOf(componentType=Integer()) seq[0] = Util.long_from_bytes(sig[:32]) seq[1] = Util.long_from_bytes(sig[32:]) try: pubkey.verify(encode(seq), data, ECDSA(SHA256())) except: return False return True
def make_root_certificate( subject_name: Name, days_valid: int, private_key: RSAPrivateKeyWithSerialization, ) -> Certificate: return ( _make_cert_builder( subject_name, days_valid, private_key.public_key(), ) .issuer_name(subject_name) .add_extension( SubjectKeyIdentifier.from_public_key(private_key.public_key()), critical=False, ) .add_extension( BasicConstraints( ca=True, path_length=0, ), critical=True, ) .add_extension( KeyUsage( digital_signature=False, content_commitment=False, key_encipherment=False, data_encipherment=False, key_agreement=False, key_cert_sign=True, crl_sign=True, encipher_only=False, decipher_only=False, ), critical=True, ) .sign( private_key, SHA256(), ) )
def create_x509_cert(privkey, pubkey, subject_info, issuer_info, days): """Main cert creation code. """ if not isinstance(subject_info, CertInfo): info = CertInfo() info.load_from_existing(subject_info) subject_info = info if not isinstance(issuer_info, CertInfo): info = CertInfo() info.load_from_existing(issuer_info) issuer_info = info dt_now = datetime.utcnow() dt_start = dt_now - timedelta(hours=1) dt_end = dt_now + timedelta(days=days) builder = (x509.CertificateBuilder() .subject_name(subject_info.get_name()) .issuer_name(issuer_info.get_name()) .not_valid_before(dt_start) .not_valid_after(dt_end) .serial_number(int(uuid.uuid4())) .public_key(pubkey)) builder = subject_info.install_extensions(builder) # SubjectKeyIdentifier ext = x509.SubjectKeyIdentifier.from_public_key(pubkey) builder = builder.add_extension(ext, critical=False) # AuthorityKeyIdentifier ext = x509.AuthorityKeyIdentifier.from_issuer_public_key(privkey.public_key()) builder = builder.add_extension(ext, critical=False) # IssuerAlternativeName if issuer_info.san: ext = x509.IssuerAlternativeName(issuer_info.get_san_gnames()) builder = builder.add_extension(ext, critical=False) # final cert cert = builder.sign(private_key=privkey, algorithm=SHA256(), backend=get_backend()) return cert
def compress_and_encrypt( input_file: IOIter, output_file: IOIter, key_pair: Optional[bytes], options: OptionsDict, ) -> bytes: """ Read data from an open file descriptor, and write the compressed, encrypted data to another file descriptor; compute the HMAC of the encrypted data to ensure integrity :param input_file: an IOIter object to read plaintext data from :param output_file: an IOIter object to write compressed ciphertext to """ key, nonce = (key_pair[:AES_KEY_SIZE], key_pair[AES_KEY_SIZE:]) if key_pair else (b'', b'') compressobj = zlib.compressobj() zip_fn: Callable[[bytes], bytes] = ( # type: ignore compressobj.compress if options['use_compression'] else identity ) encrypt_fn: Callable[[bytes], bytes] = ( Cipher(AES(key), CTR(nonce), backend=default_backend()).encryptor().update if options['use_encryption'] else identity ) hmac = HMAC(key, SHA256(), default_backend()) def last_block() -> Generator[Tuple[bytes, bool], None, None]: yield (compressobj.flush(), False) if options['use_compression'] else (b'', False) writer = output_file.writer(); next(writer) logger.debug2('starting to compress') for block, needs_compression in chain(zip(input_file.reader(), repeat(True)), last_block()): if needs_compression: block = zip_fn(block) logger.debug2(f'zip_fn returned {len(block)} bytes') block = encrypt_fn(block) logger.debug2(f'encrypt_fn returned {len(block)} bytes') if options['use_encryption']: hmac.update(block) writer.send(block) if options['use_encryption']: return hmac.finalize() else: return b''
def test_default_repository_integrity(self): # Given the local repo of certificates repo = RootCertificatesRepository.get_default() # Each certificate that it returns is stored at the expected location expected_repo_path = Path(os.path.abspath( os.path.dirname(__file__))) / ".." / "certificates" all_certificates = repo.get_all_certificates() assert all_certificates for certificate in all_certificates: expected_file_name = hexlify(certificate.fingerprint( SHA256())).decode("ascii") expected_cert_path = expected_repo_path / f"{expected_file_name}.pem" with open(expected_cert_path) as stored_cert_file: stored_cert_pem = stored_cert_file.read() stored_cert = load_pem_x509_certificate( stored_cert_pem.encode(encoding="ascii"), default_backend()) assert stored_cert == certificate
def encrypt(key, pt, associated_data=None): if not isinstance(key, bytes): raise TypeError("key must be of type: bytes") if not len(key) == AES256GCM.KEY_LEN: raise ValueError("key must be 32 bytes") if not isinstance(pt, bytes): raise TypeError("pt must be of type: bytes") if associated_data and not isinstance(associated_data, bytes): raise TypeError("associated_data must be of type: bytes") aes_key, hmac_key, iv = AES256CBCHMAC._gen_keys(key) padder = padding.PKCS7(AES256CBCHMAC.IV_LEN * 8).padder() padded_pt = padder.update(pt) + padder.finalize() aes_cbc = AES256CBCHMAC._aes_cipher(aes_key, iv).encryptor() ct = aes_cbc.update(padded_pt) + aes_cbc.finalize() tag = hmac(hmac_key, associated_data + ct, SHA256(), default_backend()) return ct + tag
def test_decrypt_and_unpack_no_compression(caplog, mock_open_streams): orig, new, _ = mock_open_streams orig_contents = orig._fd.getvalue() cipher = Cipher(AES(TMP_KEY), CTR(TMP_NONCE), backend=default_backend()).encryptor() ct = cipher.update(orig_contents) hmac = HMAC(TMP_KEY, SHA256(), default_backend()) hmac.update(ct) signature = hmac.finalize() orig._fd.write(ct) decrypt_and_unpack( orig, new, TMP_KEYPAIR + signature, dict(use_compression=False, use_encryption=True), ) assert new._fd.getvalue() == orig_contents assert count_matching_log_lines('read 2 bytes from /orig', caplog) == 4 assert count_matching_log_lines('wrote 2 bytes to /new', caplog) == 4
def create_csr(key: EllipticCurvePrivateKey, template_cert: Certificate) -> CertificateSigningRequest: """ Create a Certificate Signing Request (CSR) matching to a corresponding template_cert and sign it with a private key. :param key: the private key to sign the CSR :param template_cert: a template for retrieving the subject information. :return: CSR (https://en.wikipedia.org/wiki/Certificate_signing_request) """ csr_builder = CertificateSigningRequestBuilder().subject_name( template_cert.subject) try: subject_alt_name = template_cert.extensions.get_extension_for_class( SubjectAlternativeName) csr_builder = csr_builder.add_extension(subject_alt_name.value, critical=False) except ExtensionNotFound: pass return csr_builder.sign(key, SHA256(), backends.default_backend())
def create_raw_transaction( self, inputs: List[TransactionInput], outputs: List[TransactionOutput]) -> Transaction: if len(inputs) >= 256: raise ValueError("Transaction has too many inputs (%d): %r" % (len(inputs), inputs)) if len(outputs) >= 256: raise ValueError("Transaction has too many outputs (%d): %r" % (len(outputs), outputs)) txn = Transaction(payer=self.public_serialized, inputs=inputs, outputs=outputs, signature=b'', transaction_hash=b'') txn.signature = self.private_key.sign(txn.to_signature_data(), ec.ECDSA(SHA256())) assert txn.verify_signature( ), "Newly signed transaction should have valid signature" txn.transaction_hash = sha256(txn.signature) return txn
def cert(issuer, subject, pubkey, privkey, ca): builder = CertificateBuilder().issuer_name( Name([NameAttribute(NameOID.COMMON_NAME, issuer)]), ).subject_name( Name([NameAttribute(NameOID.COMMON_NAME, subject)]), ).add_extension( SubjectAlternativeName([DNSName(subject)]), critical=False, ) if ca: builder = builder.add_extension( BasicConstraints(True, None), critical=True, ) return builder.public_key( pubkey, ).serial_number(random_serial_number(), ).not_valid_before( datetime.utcnow(), ).not_valid_after( datetime.utcnow() + timedelta(seconds=1), ).sign( privkey, SHA256(), default_backend(), )
def get_new_token(self): """ Get a new token using the email address and RSA Key. :return: Dictionary containing token information :rtype: ``dict`` """ # The header is always the same header = {'alg': 'RS256', 'typ': 'JWT'} header_enc = base64.urlsafe_b64encode(b(json.dumps(header))) # Construct a claim set claim_set = { 'iss': self.user_id, 'scope': self.scopes, 'aud': 'https://accounts.google.com/o/oauth2/token', 'exp': int(time.time()) + 3600, 'iat': int(time.time()) } claim_set_enc = base64.urlsafe_b64encode(b(json.dumps(claim_set))) # The message contains both the header and claim set message = b'.'.join((header_enc, claim_set_enc)) # Then the message is signed using the key supplied key = serialization.load_pem_private_key(b(self.key), password=None, backend=default_backend()) signature = key.sign(data=b(message), padding=PKCS1v15(), algorithm=SHA256()) signature = base64.urlsafe_b64encode(signature) # Finally the message and signature are sent to get a token jwt = b'.'.join((message, signature)) request = { 'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer', 'assertion': jwt } return self._token_request(request)
def sign_csr( csr: CertificateSigningRequest, ca_cert: Certificate, key: EllipticCurvePrivateKey, expiration_date: date, custom_extensions: Iterable[Union[KeyUsage, UnrecognizedExtension, BasicConstraints]] ) -> Certificate: """ Sign a CSR with CA credentials. :param csr: the CSR :param ca_cert: the CA certificate :param key: the CA private key :param expiration_date: expiration date :param custom_extensions: custom extensions to be added to the certificate :return: a certificate object """ issuer = ca_cert.subject now = datetime.utcnow() cert_builder = CertificateBuilder().issuer_name(issuer).subject_name( csr.subject).public_key(csr.public_key()).serial_number( x509.random_serial_number()).not_valid_before(now).not_valid_after( datetime.combine(expiration_date, time(), None)).add_extension( extension=AuthorityKeyIdentifier.from_issuer_public_key( ca_cert.public_key()), critical=False) try: cert_builder = cert_builder.add_extension( csr.extensions.get_extension_for_class( SubjectAlternativeName).value, critical=False) except ExtensionNotFound: pass for extension in custom_extensions: if isinstance(extension, UnrecognizedExtension): critical = False else: critical = True # pyre-fixme[6]: Expected `ExtensionType` for 1st param but got # `Union[BasicConstraints, KeyUsage, UnrecognizedExtension]`. cert_builder = cert_builder.add_extension(extension, critical=critical) return cert_builder.sign(key, SHA256(), backends.default_backend())