This commit is contained in:
2025-09-27 18:18:37 -04:00
parent 055c330f29
commit 244775f689
2276 changed files with 422897 additions and 30 deletions

View File

@@ -0,0 +1,17 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
__all__ = [
"__version__",
"__author__",
"__copyright__",
]
__version__ = "42.0.7"
__author__ = "The Python Cryptographic Authority and individual contributors"
__copyright__ = f"Copyright 2013-2024 {__author__}"

View File

@@ -0,0 +1,13 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
from cryptography.__about__ import __author__, __copyright__, __version__
__all__ = [
"__version__",
"__author__",
"__copyright__",
]

View File

@@ -0,0 +1,52 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
import typing
from cryptography.hazmat.bindings._rust import exceptions as rust_exceptions
if typing.TYPE_CHECKING:
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
_Reasons = rust_exceptions._Reasons
class UnsupportedAlgorithm(Exception):
def __init__(self, message: str, reason: _Reasons | None = None) -> None:
super().__init__(message)
self._reason = reason
class AlreadyFinalized(Exception):
pass
class AlreadyUpdated(Exception):
pass
class NotYetFinalized(Exception):
pass
class InvalidTag(Exception):
pass
class InvalidSignature(Exception):
pass
class InternalError(Exception):
def __init__(
self, msg: str, err_code: list[rust_openssl.OpenSSLError]
) -> None:
super().__init__(msg)
self.err_code = err_code
class InvalidKey(Exception):
pass

View File

@@ -0,0 +1,215 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
import base64
import binascii
import os
import time
import typing
from cryptography import utils
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives import hashes, padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.hmac import HMAC
class InvalidToken(Exception):
pass
_MAX_CLOCK_SKEW = 60
class Fernet:
def __init__(
self,
key: bytes | str,
backend: typing.Any = None,
) -> None:
try:
key = base64.urlsafe_b64decode(key)
except binascii.Error as exc:
raise ValueError(
"Fernet key must be 32 url-safe base64-encoded bytes."
) from exc
if len(key) != 32:
raise ValueError(
"Fernet key must be 32 url-safe base64-encoded bytes."
)
self._signing_key = key[:16]
self._encryption_key = key[16:]
@classmethod
def generate_key(cls) -> bytes:
return base64.urlsafe_b64encode(os.urandom(32))
def encrypt(self, data: bytes) -> bytes:
return self.encrypt_at_time(data, int(time.time()))
def encrypt_at_time(self, data: bytes, current_time: int) -> bytes:
iv = os.urandom(16)
return self._encrypt_from_parts(data, current_time, iv)
def _encrypt_from_parts(
self, data: bytes, current_time: int, iv: bytes
) -> bytes:
utils._check_bytes("data", data)
padder = padding.PKCS7(algorithms.AES.block_size).padder()
padded_data = padder.update(data) + padder.finalize()
encryptor = Cipher(
algorithms.AES(self._encryption_key),
modes.CBC(iv),
).encryptor()
ciphertext = encryptor.update(padded_data) + encryptor.finalize()
basic_parts = (
b"\x80"
+ current_time.to_bytes(length=8, byteorder="big")
+ iv
+ ciphertext
)
h = HMAC(self._signing_key, hashes.SHA256())
h.update(basic_parts)
hmac = h.finalize()
return base64.urlsafe_b64encode(basic_parts + hmac)
def decrypt(self, token: bytes | str, ttl: int | None = None) -> bytes:
timestamp, data = Fernet._get_unverified_token_data(token)
if ttl is None:
time_info = None
else:
time_info = (ttl, int(time.time()))
return self._decrypt_data(data, timestamp, time_info)
def decrypt_at_time(
self, token: bytes | str, ttl: int, current_time: int
) -> bytes:
if ttl is None:
raise ValueError(
"decrypt_at_time() can only be used with a non-None ttl"
)
timestamp, data = Fernet._get_unverified_token_data(token)
return self._decrypt_data(data, timestamp, (ttl, current_time))
def extract_timestamp(self, token: bytes | str) -> int:
timestamp, data = Fernet._get_unverified_token_data(token)
# Verify the token was not tampered with.
self._verify_signature(data)
return timestamp
@staticmethod
def _get_unverified_token_data(token: bytes | str) -> tuple[int, bytes]:
if not isinstance(token, (str, bytes)):
raise TypeError("token must be bytes or str")
try:
data = base64.urlsafe_b64decode(token)
except (TypeError, binascii.Error):
raise InvalidToken
if not data or data[0] != 0x80:
raise InvalidToken
if len(data) < 9:
raise InvalidToken
timestamp = int.from_bytes(data[1:9], byteorder="big")
return timestamp, data
def _verify_signature(self, data: bytes) -> None:
h = HMAC(self._signing_key, hashes.SHA256())
h.update(data[:-32])
try:
h.verify(data[-32:])
except InvalidSignature:
raise InvalidToken
def _decrypt_data(
self,
data: bytes,
timestamp: int,
time_info: tuple[int, int] | None,
) -> bytes:
if time_info is not None:
ttl, current_time = time_info
if timestamp + ttl < current_time:
raise InvalidToken
if current_time + _MAX_CLOCK_SKEW < timestamp:
raise InvalidToken
self._verify_signature(data)
iv = data[9:25]
ciphertext = data[25:-32]
decryptor = Cipher(
algorithms.AES(self._encryption_key), modes.CBC(iv)
).decryptor()
plaintext_padded = decryptor.update(ciphertext)
try:
plaintext_padded += decryptor.finalize()
except ValueError:
raise InvalidToken
unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
unpadded = unpadder.update(plaintext_padded)
try:
unpadded += unpadder.finalize()
except ValueError:
raise InvalidToken
return unpadded
class MultiFernet:
def __init__(self, fernets: typing.Iterable[Fernet]):
fernets = list(fernets)
if not fernets:
raise ValueError(
"MultiFernet requires at least one Fernet instance"
)
self._fernets = fernets
def encrypt(self, msg: bytes) -> bytes:
return self.encrypt_at_time(msg, int(time.time()))
def encrypt_at_time(self, msg: bytes, current_time: int) -> bytes:
return self._fernets[0].encrypt_at_time(msg, current_time)
def rotate(self, msg: bytes | str) -> bytes:
timestamp, data = Fernet._get_unverified_token_data(msg)
for f in self._fernets:
try:
p = f._decrypt_data(data, timestamp, None)
break
except InvalidToken:
pass
else:
raise InvalidToken
iv = os.urandom(16)
return self._fernets[0]._encrypt_from_parts(p, timestamp, iv)
def decrypt(self, msg: bytes | str, ttl: int | None = None) -> bytes:
for f in self._fernets:
try:
return f.decrypt(msg, ttl)
except InvalidToken:
pass
raise InvalidToken
def decrypt_at_time(
self, msg: bytes | str, ttl: int, current_time: int
) -> bytes:
for f in self._fernets:
try:
return f.decrypt_at_time(msg, ttl, current_time)
except InvalidToken:
pass
raise InvalidToken

View File

@@ -0,0 +1,13 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
"""
Hazardous Materials
This is a "Hazardous Materials" module. You should ONLY use it if you're
100% absolutely sure that you know what you're doing because this module
is full of land mines, dragons, and dinosaurs with laser guns.
"""

View File

@@ -0,0 +1,296 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
from cryptography.hazmat.bindings._rust import (
ObjectIdentifier as ObjectIdentifier,
)
from cryptography.hazmat.primitives import hashes
class ExtensionOID:
SUBJECT_DIRECTORY_ATTRIBUTES = ObjectIdentifier("2.5.29.9")
SUBJECT_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.14")
KEY_USAGE = ObjectIdentifier("2.5.29.15")
SUBJECT_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.17")
ISSUER_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.18")
BASIC_CONSTRAINTS = ObjectIdentifier("2.5.29.19")
NAME_CONSTRAINTS = ObjectIdentifier("2.5.29.30")
CRL_DISTRIBUTION_POINTS = ObjectIdentifier("2.5.29.31")
CERTIFICATE_POLICIES = ObjectIdentifier("2.5.29.32")
POLICY_MAPPINGS = ObjectIdentifier("2.5.29.33")
AUTHORITY_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.35")
POLICY_CONSTRAINTS = ObjectIdentifier("2.5.29.36")
EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37")
FRESHEST_CRL = ObjectIdentifier("2.5.29.46")
INHIBIT_ANY_POLICY = ObjectIdentifier("2.5.29.54")
ISSUING_DISTRIBUTION_POINT = ObjectIdentifier("2.5.29.28")
AUTHORITY_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.1")
SUBJECT_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.11")
OCSP_NO_CHECK = ObjectIdentifier("1.3.6.1.5.5.7.48.1.5")
TLS_FEATURE = ObjectIdentifier("1.3.6.1.5.5.7.1.24")
CRL_NUMBER = ObjectIdentifier("2.5.29.20")
DELTA_CRL_INDICATOR = ObjectIdentifier("2.5.29.27")
PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS = ObjectIdentifier(
"1.3.6.1.4.1.11129.2.4.2"
)
PRECERT_POISON = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.3")
SIGNED_CERTIFICATE_TIMESTAMPS = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.5")
MS_CERTIFICATE_TEMPLATE = ObjectIdentifier("1.3.6.1.4.1.311.21.7")
class OCSPExtensionOID:
NONCE = ObjectIdentifier("1.3.6.1.5.5.7.48.1.2")
ACCEPTABLE_RESPONSES = ObjectIdentifier("1.3.6.1.5.5.7.48.1.4")
class CRLEntryExtensionOID:
CERTIFICATE_ISSUER = ObjectIdentifier("2.5.29.29")
CRL_REASON = ObjectIdentifier("2.5.29.21")
INVALIDITY_DATE = ObjectIdentifier("2.5.29.24")
class NameOID:
COMMON_NAME = ObjectIdentifier("2.5.4.3")
COUNTRY_NAME = ObjectIdentifier("2.5.4.6")
LOCALITY_NAME = ObjectIdentifier("2.5.4.7")
STATE_OR_PROVINCE_NAME = ObjectIdentifier("2.5.4.8")
STREET_ADDRESS = ObjectIdentifier("2.5.4.9")
ORGANIZATION_IDENTIFIER = ObjectIdentifier("2.5.4.97")
ORGANIZATION_NAME = ObjectIdentifier("2.5.4.10")
ORGANIZATIONAL_UNIT_NAME = ObjectIdentifier("2.5.4.11")
SERIAL_NUMBER = ObjectIdentifier("2.5.4.5")
SURNAME = ObjectIdentifier("2.5.4.4")
GIVEN_NAME = ObjectIdentifier("2.5.4.42")
TITLE = ObjectIdentifier("2.5.4.12")
INITIALS = ObjectIdentifier("2.5.4.43")
GENERATION_QUALIFIER = ObjectIdentifier("2.5.4.44")
X500_UNIQUE_IDENTIFIER = ObjectIdentifier("2.5.4.45")
DN_QUALIFIER = ObjectIdentifier("2.5.4.46")
PSEUDONYM = ObjectIdentifier("2.5.4.65")
USER_ID = ObjectIdentifier("0.9.2342.19200300.100.1.1")
DOMAIN_COMPONENT = ObjectIdentifier("0.9.2342.19200300.100.1.25")
EMAIL_ADDRESS = ObjectIdentifier("1.2.840.113549.1.9.1")
JURISDICTION_COUNTRY_NAME = ObjectIdentifier("1.3.6.1.4.1.311.60.2.1.3")
JURISDICTION_LOCALITY_NAME = ObjectIdentifier("1.3.6.1.4.1.311.60.2.1.1")
JURISDICTION_STATE_OR_PROVINCE_NAME = ObjectIdentifier(
"1.3.6.1.4.1.311.60.2.1.2"
)
BUSINESS_CATEGORY = ObjectIdentifier("2.5.4.15")
POSTAL_ADDRESS = ObjectIdentifier("2.5.4.16")
POSTAL_CODE = ObjectIdentifier("2.5.4.17")
INN = ObjectIdentifier("1.2.643.3.131.1.1")
OGRN = ObjectIdentifier("1.2.643.100.1")
SNILS = ObjectIdentifier("1.2.643.100.3")
UNSTRUCTURED_NAME = ObjectIdentifier("1.2.840.113549.1.9.2")
class SignatureAlgorithmOID:
RSA_WITH_MD5 = ObjectIdentifier("1.2.840.113549.1.1.4")
RSA_WITH_SHA1 = ObjectIdentifier("1.2.840.113549.1.1.5")
# This is an alternate OID for RSA with SHA1 that is occasionally seen
_RSA_WITH_SHA1 = ObjectIdentifier("1.3.14.3.2.29")
RSA_WITH_SHA224 = ObjectIdentifier("1.2.840.113549.1.1.14")
RSA_WITH_SHA256 = ObjectIdentifier("1.2.840.113549.1.1.11")
RSA_WITH_SHA384 = ObjectIdentifier("1.2.840.113549.1.1.12")
RSA_WITH_SHA512 = ObjectIdentifier("1.2.840.113549.1.1.13")
RSA_WITH_SHA3_224 = ObjectIdentifier("2.16.840.1.101.3.4.3.13")
RSA_WITH_SHA3_256 = ObjectIdentifier("2.16.840.1.101.3.4.3.14")
RSA_WITH_SHA3_384 = ObjectIdentifier("2.16.840.1.101.3.4.3.15")
RSA_WITH_SHA3_512 = ObjectIdentifier("2.16.840.1.101.3.4.3.16")
RSASSA_PSS = ObjectIdentifier("1.2.840.113549.1.1.10")
ECDSA_WITH_SHA1 = ObjectIdentifier("1.2.840.10045.4.1")
ECDSA_WITH_SHA224 = ObjectIdentifier("1.2.840.10045.4.3.1")
ECDSA_WITH_SHA256 = ObjectIdentifier("1.2.840.10045.4.3.2")
ECDSA_WITH_SHA384 = ObjectIdentifier("1.2.840.10045.4.3.3")
ECDSA_WITH_SHA512 = ObjectIdentifier("1.2.840.10045.4.3.4")
ECDSA_WITH_SHA3_224 = ObjectIdentifier("2.16.840.1.101.3.4.3.9")
ECDSA_WITH_SHA3_256 = ObjectIdentifier("2.16.840.1.101.3.4.3.10")
ECDSA_WITH_SHA3_384 = ObjectIdentifier("2.16.840.1.101.3.4.3.11")
ECDSA_WITH_SHA3_512 = ObjectIdentifier("2.16.840.1.101.3.4.3.12")
DSA_WITH_SHA1 = ObjectIdentifier("1.2.840.10040.4.3")
DSA_WITH_SHA224 = ObjectIdentifier("2.16.840.1.101.3.4.3.1")
DSA_WITH_SHA256 = ObjectIdentifier("2.16.840.1.101.3.4.3.2")
DSA_WITH_SHA384 = ObjectIdentifier("2.16.840.1.101.3.4.3.3")
DSA_WITH_SHA512 = ObjectIdentifier("2.16.840.1.101.3.4.3.4")
ED25519 = ObjectIdentifier("1.3.101.112")
ED448 = ObjectIdentifier("1.3.101.113")
GOSTR3411_94_WITH_3410_2001 = ObjectIdentifier("1.2.643.2.2.3")
GOSTR3410_2012_WITH_3411_2012_256 = ObjectIdentifier("1.2.643.7.1.1.3.2")
GOSTR3410_2012_WITH_3411_2012_512 = ObjectIdentifier("1.2.643.7.1.1.3.3")
_SIG_OIDS_TO_HASH: dict[ObjectIdentifier, hashes.HashAlgorithm | None] = {
SignatureAlgorithmOID.RSA_WITH_MD5: hashes.MD5(),
SignatureAlgorithmOID.RSA_WITH_SHA1: hashes.SHA1(),
SignatureAlgorithmOID._RSA_WITH_SHA1: hashes.SHA1(),
SignatureAlgorithmOID.RSA_WITH_SHA224: hashes.SHA224(),
SignatureAlgorithmOID.RSA_WITH_SHA256: hashes.SHA256(),
SignatureAlgorithmOID.RSA_WITH_SHA384: hashes.SHA384(),
SignatureAlgorithmOID.RSA_WITH_SHA512: hashes.SHA512(),
SignatureAlgorithmOID.RSA_WITH_SHA3_224: hashes.SHA3_224(),
SignatureAlgorithmOID.RSA_WITH_SHA3_256: hashes.SHA3_256(),
SignatureAlgorithmOID.RSA_WITH_SHA3_384: hashes.SHA3_384(),
SignatureAlgorithmOID.RSA_WITH_SHA3_512: hashes.SHA3_512(),
SignatureAlgorithmOID.ECDSA_WITH_SHA1: hashes.SHA1(),
SignatureAlgorithmOID.ECDSA_WITH_SHA224: hashes.SHA224(),
SignatureAlgorithmOID.ECDSA_WITH_SHA256: hashes.SHA256(),
SignatureAlgorithmOID.ECDSA_WITH_SHA384: hashes.SHA384(),
SignatureAlgorithmOID.ECDSA_WITH_SHA512: hashes.SHA512(),
SignatureAlgorithmOID.ECDSA_WITH_SHA3_224: hashes.SHA3_224(),
SignatureAlgorithmOID.ECDSA_WITH_SHA3_256: hashes.SHA3_256(),
SignatureAlgorithmOID.ECDSA_WITH_SHA3_384: hashes.SHA3_384(),
SignatureAlgorithmOID.ECDSA_WITH_SHA3_512: hashes.SHA3_512(),
SignatureAlgorithmOID.DSA_WITH_SHA1: hashes.SHA1(),
SignatureAlgorithmOID.DSA_WITH_SHA224: hashes.SHA224(),
SignatureAlgorithmOID.DSA_WITH_SHA256: hashes.SHA256(),
SignatureAlgorithmOID.ED25519: None,
SignatureAlgorithmOID.ED448: None,
SignatureAlgorithmOID.GOSTR3411_94_WITH_3410_2001: None,
SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_256: None,
SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_512: None,
}
class ExtendedKeyUsageOID:
SERVER_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.1")
CLIENT_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.2")
CODE_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.3")
EMAIL_PROTECTION = ObjectIdentifier("1.3.6.1.5.5.7.3.4")
TIME_STAMPING = ObjectIdentifier("1.3.6.1.5.5.7.3.8")
OCSP_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.9")
ANY_EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37.0")
SMARTCARD_LOGON = ObjectIdentifier("1.3.6.1.4.1.311.20.2.2")
KERBEROS_PKINIT_KDC = ObjectIdentifier("1.3.6.1.5.2.3.5")
IPSEC_IKE = ObjectIdentifier("1.3.6.1.5.5.7.3.17")
CERTIFICATE_TRANSPARENCY = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.4")
class AuthorityInformationAccessOID:
CA_ISSUERS = ObjectIdentifier("1.3.6.1.5.5.7.48.2")
OCSP = ObjectIdentifier("1.3.6.1.5.5.7.48.1")
class SubjectInformationAccessOID:
CA_REPOSITORY = ObjectIdentifier("1.3.6.1.5.5.7.48.5")
class CertificatePoliciesOID:
CPS_QUALIFIER = ObjectIdentifier("1.3.6.1.5.5.7.2.1")
CPS_USER_NOTICE = ObjectIdentifier("1.3.6.1.5.5.7.2.2")
ANY_POLICY = ObjectIdentifier("2.5.29.32.0")
class AttributeOID:
CHALLENGE_PASSWORD = ObjectIdentifier("1.2.840.113549.1.9.7")
UNSTRUCTURED_NAME = ObjectIdentifier("1.2.840.113549.1.9.2")
_OID_NAMES = {
NameOID.COMMON_NAME: "commonName",
NameOID.COUNTRY_NAME: "countryName",
NameOID.LOCALITY_NAME: "localityName",
NameOID.STATE_OR_PROVINCE_NAME: "stateOrProvinceName",
NameOID.STREET_ADDRESS: "streetAddress",
NameOID.ORGANIZATION_NAME: "organizationName",
NameOID.ORGANIZATIONAL_UNIT_NAME: "organizationalUnitName",
NameOID.SERIAL_NUMBER: "serialNumber",
NameOID.SURNAME: "surname",
NameOID.GIVEN_NAME: "givenName",
NameOID.TITLE: "title",
NameOID.GENERATION_QUALIFIER: "generationQualifier",
NameOID.X500_UNIQUE_IDENTIFIER: "x500UniqueIdentifier",
NameOID.DN_QUALIFIER: "dnQualifier",
NameOID.PSEUDONYM: "pseudonym",
NameOID.USER_ID: "userID",
NameOID.DOMAIN_COMPONENT: "domainComponent",
NameOID.EMAIL_ADDRESS: "emailAddress",
NameOID.JURISDICTION_COUNTRY_NAME: "jurisdictionCountryName",
NameOID.JURISDICTION_LOCALITY_NAME: "jurisdictionLocalityName",
NameOID.JURISDICTION_STATE_OR_PROVINCE_NAME: (
"jurisdictionStateOrProvinceName"
),
NameOID.BUSINESS_CATEGORY: "businessCategory",
NameOID.POSTAL_ADDRESS: "postalAddress",
NameOID.POSTAL_CODE: "postalCode",
NameOID.INN: "INN",
NameOID.OGRN: "OGRN",
NameOID.SNILS: "SNILS",
NameOID.UNSTRUCTURED_NAME: "unstructuredName",
SignatureAlgorithmOID.RSA_WITH_MD5: "md5WithRSAEncryption",
SignatureAlgorithmOID.RSA_WITH_SHA1: "sha1WithRSAEncryption",
SignatureAlgorithmOID.RSA_WITH_SHA224: "sha224WithRSAEncryption",
SignatureAlgorithmOID.RSA_WITH_SHA256: "sha256WithRSAEncryption",
SignatureAlgorithmOID.RSA_WITH_SHA384: "sha384WithRSAEncryption",
SignatureAlgorithmOID.RSA_WITH_SHA512: "sha512WithRSAEncryption",
SignatureAlgorithmOID.RSASSA_PSS: "RSASSA-PSS",
SignatureAlgorithmOID.ECDSA_WITH_SHA1: "ecdsa-with-SHA1",
SignatureAlgorithmOID.ECDSA_WITH_SHA224: "ecdsa-with-SHA224",
SignatureAlgorithmOID.ECDSA_WITH_SHA256: "ecdsa-with-SHA256",
SignatureAlgorithmOID.ECDSA_WITH_SHA384: "ecdsa-with-SHA384",
SignatureAlgorithmOID.ECDSA_WITH_SHA512: "ecdsa-with-SHA512",
SignatureAlgorithmOID.DSA_WITH_SHA1: "dsa-with-sha1",
SignatureAlgorithmOID.DSA_WITH_SHA224: "dsa-with-sha224",
SignatureAlgorithmOID.DSA_WITH_SHA256: "dsa-with-sha256",
SignatureAlgorithmOID.ED25519: "ed25519",
SignatureAlgorithmOID.ED448: "ed448",
SignatureAlgorithmOID.GOSTR3411_94_WITH_3410_2001: (
"GOST R 34.11-94 with GOST R 34.10-2001"
),
SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_256: (
"GOST R 34.10-2012 with GOST R 34.11-2012 (256 bit)"
),
SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_512: (
"GOST R 34.10-2012 with GOST R 34.11-2012 (512 bit)"
),
ExtendedKeyUsageOID.SERVER_AUTH: "serverAuth",
ExtendedKeyUsageOID.CLIENT_AUTH: "clientAuth",
ExtendedKeyUsageOID.CODE_SIGNING: "codeSigning",
ExtendedKeyUsageOID.EMAIL_PROTECTION: "emailProtection",
ExtendedKeyUsageOID.TIME_STAMPING: "timeStamping",
ExtendedKeyUsageOID.OCSP_SIGNING: "OCSPSigning",
ExtendedKeyUsageOID.SMARTCARD_LOGON: "msSmartcardLogin",
ExtendedKeyUsageOID.KERBEROS_PKINIT_KDC: "pkInitKDC",
ExtensionOID.SUBJECT_DIRECTORY_ATTRIBUTES: "subjectDirectoryAttributes",
ExtensionOID.SUBJECT_KEY_IDENTIFIER: "subjectKeyIdentifier",
ExtensionOID.KEY_USAGE: "keyUsage",
ExtensionOID.SUBJECT_ALTERNATIVE_NAME: "subjectAltName",
ExtensionOID.ISSUER_ALTERNATIVE_NAME: "issuerAltName",
ExtensionOID.BASIC_CONSTRAINTS: "basicConstraints",
ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS: (
"signedCertificateTimestampList"
),
ExtensionOID.SIGNED_CERTIFICATE_TIMESTAMPS: (
"signedCertificateTimestampList"
),
ExtensionOID.PRECERT_POISON: "ctPoison",
ExtensionOID.MS_CERTIFICATE_TEMPLATE: "msCertificateTemplate",
CRLEntryExtensionOID.CRL_REASON: "cRLReason",
CRLEntryExtensionOID.INVALIDITY_DATE: "invalidityDate",
CRLEntryExtensionOID.CERTIFICATE_ISSUER: "certificateIssuer",
ExtensionOID.NAME_CONSTRAINTS: "nameConstraints",
ExtensionOID.CRL_DISTRIBUTION_POINTS: "cRLDistributionPoints",
ExtensionOID.CERTIFICATE_POLICIES: "certificatePolicies",
ExtensionOID.POLICY_MAPPINGS: "policyMappings",
ExtensionOID.AUTHORITY_KEY_IDENTIFIER: "authorityKeyIdentifier",
ExtensionOID.POLICY_CONSTRAINTS: "policyConstraints",
ExtensionOID.EXTENDED_KEY_USAGE: "extendedKeyUsage",
ExtensionOID.FRESHEST_CRL: "freshestCRL",
ExtensionOID.INHIBIT_ANY_POLICY: "inhibitAnyPolicy",
ExtensionOID.ISSUING_DISTRIBUTION_POINT: "issuingDistributionPoint",
ExtensionOID.AUTHORITY_INFORMATION_ACCESS: "authorityInfoAccess",
ExtensionOID.SUBJECT_INFORMATION_ACCESS: "subjectInfoAccess",
ExtensionOID.OCSP_NO_CHECK: "OCSPNoCheck",
ExtensionOID.CRL_NUMBER: "cRLNumber",
ExtensionOID.DELTA_CRL_INDICATOR: "deltaCRLIndicator",
ExtensionOID.TLS_FEATURE: "TLSFeature",
AuthorityInformationAccessOID.OCSP: "OCSP",
AuthorityInformationAccessOID.CA_ISSUERS: "caIssuers",
SubjectInformationAccessOID.CA_REPOSITORY: "caRepository",
CertificatePoliciesOID.CPS_QUALIFIER: "id-qt-cps",
CertificatePoliciesOID.CPS_USER_NOTICE: "id-qt-unotice",
OCSPExtensionOID.NONCE: "OCSPNonce",
AttributeOID.CHALLENGE_PASSWORD: "challengePassword",
}

View File

@@ -0,0 +1,13 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
from typing import Any
def default_backend() -> Any:
from cryptography.hazmat.backends.openssl.backend import backend
return backend

View File

@@ -0,0 +1,9 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
from cryptography.hazmat.backends.openssl.backend import backend
__all__ = ["backend"]

View File

@@ -0,0 +1,272 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
import typing
from cryptography.exceptions import InvalidTag
if typing.TYPE_CHECKING:
from cryptography.hazmat.backends.openssl.backend import Backend
from cryptography.hazmat.primitives.ciphers.aead import (
AESCCM,
AESGCM,
)
_AEADTypes = typing.Union[AESCCM, AESGCM]
def _aead_cipher_supported(backend: Backend, cipher: _AEADTypes) -> bool:
cipher_name = _evp_cipher_cipher_name(cipher)
return backend._lib.EVP_get_cipherbyname(cipher_name) != backend._ffi.NULL
def _encrypt(
backend: Backend,
cipher: _AEADTypes,
nonce: bytes,
data: bytes,
associated_data: list[bytes],
tag_length: int,
) -> bytes:
return _evp_cipher_encrypt(
backend, cipher, nonce, data, associated_data, tag_length
)
def _decrypt(
backend: Backend,
cipher: _AEADTypes,
nonce: bytes,
data: bytes,
associated_data: list[bytes],
tag_length: int,
) -> bytes:
return _evp_cipher_decrypt(
backend, cipher, nonce, data, associated_data, tag_length
)
_ENCRYPT = 1
_DECRYPT = 0
def _evp_cipher_cipher_name(cipher: _AEADTypes) -> bytes:
from cryptography.hazmat.primitives.ciphers.aead import (
AESCCM,
AESGCM,
)
if isinstance(cipher, AESCCM):
return f"aes-{len(cipher._key) * 8}-ccm".encode("ascii")
else:
assert isinstance(cipher, AESGCM)
return f"aes-{len(cipher._key) * 8}-gcm".encode("ascii")
def _evp_cipher(cipher_name: bytes, backend: Backend):
evp_cipher = backend._lib.EVP_get_cipherbyname(cipher_name)
backend.openssl_assert(evp_cipher != backend._ffi.NULL)
return evp_cipher
def _evp_cipher_aead_setup(
backend: Backend,
cipher_name: bytes,
key: bytes,
nonce: bytes,
tag: bytes | None,
tag_len: int,
operation: int,
):
evp_cipher = _evp_cipher(cipher_name, backend)
ctx = backend._lib.EVP_CIPHER_CTX_new()
ctx = backend._ffi.gc(ctx, backend._lib.EVP_CIPHER_CTX_free)
res = backend._lib.EVP_CipherInit_ex(
ctx,
evp_cipher,
backend._ffi.NULL,
backend._ffi.NULL,
backend._ffi.NULL,
int(operation == _ENCRYPT),
)
backend.openssl_assert(res != 0)
# CCM requires the IVLEN to be set before calling SET_TAG on decrypt
res = backend._lib.EVP_CIPHER_CTX_ctrl(
ctx,
backend._lib.EVP_CTRL_AEAD_SET_IVLEN,
len(nonce),
backend._ffi.NULL,
)
backend.openssl_assert(res != 0)
if operation == _DECRYPT:
assert tag is not None
_evp_cipher_set_tag(backend, ctx, tag)
elif cipher_name.endswith(b"-ccm"):
res = backend._lib.EVP_CIPHER_CTX_ctrl(
ctx,
backend._lib.EVP_CTRL_AEAD_SET_TAG,
tag_len,
backend._ffi.NULL,
)
backend.openssl_assert(res != 0)
nonce_ptr = backend._ffi.from_buffer(nonce)
key_ptr = backend._ffi.from_buffer(key)
res = backend._lib.EVP_CipherInit_ex(
ctx,
backend._ffi.NULL,
backend._ffi.NULL,
key_ptr,
nonce_ptr,
int(operation == _ENCRYPT),
)
backend.openssl_assert(res != 0)
return ctx
def _evp_cipher_set_tag(backend, ctx, tag: bytes) -> None:
tag_ptr = backend._ffi.from_buffer(tag)
res = backend._lib.EVP_CIPHER_CTX_ctrl(
ctx, backend._lib.EVP_CTRL_AEAD_SET_TAG, len(tag), tag_ptr
)
backend.openssl_assert(res != 0)
def _evp_cipher_set_length(backend: Backend, ctx, data_len: int) -> None:
intptr = backend._ffi.new("int *")
res = backend._lib.EVP_CipherUpdate(
ctx, backend._ffi.NULL, intptr, backend._ffi.NULL, data_len
)
backend.openssl_assert(res != 0)
def _evp_cipher_process_aad(
backend: Backend, ctx, associated_data: bytes
) -> None:
outlen = backend._ffi.new("int *")
a_data_ptr = backend._ffi.from_buffer(associated_data)
res = backend._lib.EVP_CipherUpdate(
ctx, backend._ffi.NULL, outlen, a_data_ptr, len(associated_data)
)
backend.openssl_assert(res != 0)
def _evp_cipher_process_data(backend: Backend, ctx, data: bytes) -> bytes:
outlen = backend._ffi.new("int *")
buf = backend._ffi.new("unsigned char[]", len(data))
data_ptr = backend._ffi.from_buffer(data)
res = backend._lib.EVP_CipherUpdate(ctx, buf, outlen, data_ptr, len(data))
backend.openssl_assert(res != 0)
return backend._ffi.buffer(buf, outlen[0])[:]
def _evp_cipher_encrypt(
backend: Backend,
cipher: _AEADTypes,
nonce: bytes,
data: bytes,
associated_data: list[bytes],
tag_length: int,
) -> bytes:
from cryptography.hazmat.primitives.ciphers.aead import AESCCM
cipher_name = _evp_cipher_cipher_name(cipher)
ctx = _evp_cipher_aead_setup(
backend,
cipher_name,
cipher._key,
nonce,
None,
tag_length,
_ENCRYPT,
)
# CCM requires us to pass the length of the data before processing
# anything.
# However calling this with any other AEAD results in an error
if isinstance(cipher, AESCCM):
_evp_cipher_set_length(backend, ctx, len(data))
for ad in associated_data:
_evp_cipher_process_aad(backend, ctx, ad)
processed_data = _evp_cipher_process_data(backend, ctx, data)
outlen = backend._ffi.new("int *")
# All AEADs we support besides OCB are streaming so they return nothing
# in finalization. OCB can return up to (16 byte block - 1) bytes so
# we need a buffer here too.
buf = backend._ffi.new("unsigned char[]", 16)
res = backend._lib.EVP_CipherFinal_ex(ctx, buf, outlen)
backend.openssl_assert(res != 0)
processed_data += backend._ffi.buffer(buf, outlen[0])[:]
tag_buf = backend._ffi.new("unsigned char[]", tag_length)
res = backend._lib.EVP_CIPHER_CTX_ctrl(
ctx, backend._lib.EVP_CTRL_AEAD_GET_TAG, tag_length, tag_buf
)
backend.openssl_assert(res != 0)
tag = backend._ffi.buffer(tag_buf)[:]
return processed_data + tag
def _evp_cipher_decrypt(
backend: Backend,
cipher: _AEADTypes,
nonce: bytes,
data: bytes,
associated_data: list[bytes],
tag_length: int,
) -> bytes:
from cryptography.hazmat.primitives.ciphers.aead import AESCCM
if len(data) < tag_length:
raise InvalidTag
tag = data[-tag_length:]
data = data[:-tag_length]
cipher_name = _evp_cipher_cipher_name(cipher)
ctx = _evp_cipher_aead_setup(
backend,
cipher_name,
cipher._key,
nonce,
tag,
tag_length,
_DECRYPT,
)
# CCM requires us to pass the length of the data before processing
# anything.
# However calling this with any other AEAD results in an error
if isinstance(cipher, AESCCM):
_evp_cipher_set_length(backend, ctx, len(data))
for ad in associated_data:
_evp_cipher_process_aad(backend, ctx, ad)
# CCM has a different error path if the tag doesn't match. Errors are
# raised in Update and Final is irrelevant.
if isinstance(cipher, AESCCM):
outlen = backend._ffi.new("int *")
buf = backend._ffi.new("unsigned char[]", len(data))
d_ptr = backend._ffi.from_buffer(data)
res = backend._lib.EVP_CipherUpdate(ctx, buf, outlen, d_ptr, len(data))
if res != 1:
backend._consume_errors()
raise InvalidTag
processed_data = backend._ffi.buffer(buf, outlen[0])[:]
else:
processed_data = _evp_cipher_process_data(backend, ctx, data)
outlen = backend._ffi.new("int *")
# OCB can return up to 15 bytes (16 byte block - 1) in finalization
buf = backend._ffi.new("unsigned char[]", 16)
res = backend._lib.EVP_CipherFinal_ex(ctx, buf, outlen)
processed_data += backend._ffi.buffer(buf, outlen[0])[:]
if res == 0:
backend._consume_errors()
raise InvalidTag
return processed_data

View File

@@ -0,0 +1,898 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
import collections
import contextlib
import itertools
import typing
from cryptography import utils, x509
from cryptography.exceptions import UnsupportedAlgorithm
from cryptography.hazmat.backends.openssl import aead
from cryptography.hazmat.backends.openssl.ciphers import _CipherContext
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
from cryptography.hazmat.bindings.openssl import binding
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives._asymmetric import AsymmetricPadding
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric import utils as asym_utils
from cryptography.hazmat.primitives.asymmetric.padding import (
MGF1,
OAEP,
PSS,
PKCS1v15,
)
from cryptography.hazmat.primitives.asymmetric.types import (
PrivateKeyTypes,
)
from cryptography.hazmat.primitives.ciphers import (
CipherAlgorithm,
)
from cryptography.hazmat.primitives.ciphers.algorithms import (
AES,
AES128,
AES256,
ARC4,
SM4,
Camellia,
ChaCha20,
TripleDES,
_BlowfishInternal,
_CAST5Internal,
_IDEAInternal,
_SEEDInternal,
)
from cryptography.hazmat.primitives.ciphers.modes import (
CBC,
CFB,
CFB8,
CTR,
ECB,
GCM,
OFB,
XTS,
Mode,
)
from cryptography.hazmat.primitives.serialization.pkcs12 import (
PBES,
PKCS12Certificate,
PKCS12KeyAndCertificates,
PKCS12PrivateKeyTypes,
_PKCS12CATypes,
)
_MemoryBIO = collections.namedtuple("_MemoryBIO", ["bio", "char_ptr"])
# Not actually supported, just used as a marker for some serialization tests.
class _RC2:
pass
class Backend:
"""
OpenSSL API binding interfaces.
"""
name = "openssl"
# FIPS has opinions about acceptable algorithms and key sizes, but the
# disallowed algorithms are still present in OpenSSL. They just error if
# you try to use them. To avoid that we allowlist the algorithms in
# FIPS 140-3. This isn't ideal, but FIPS 140-3 is trash so here we are.
_fips_aead: typing.ClassVar[set[bytes]] = {
b"aes-128-ccm",
b"aes-192-ccm",
b"aes-256-ccm",
b"aes-128-gcm",
b"aes-192-gcm",
b"aes-256-gcm",
}
# TripleDES encryption is disallowed/deprecated throughout 2023 in
# FIPS 140-3. To keep it simple we denylist any use of TripleDES (TDEA).
_fips_ciphers = (AES,)
# Sometimes SHA1 is still permissible. That logic is contained
# within the various *_supported methods.
_fips_hashes = (
hashes.SHA224,
hashes.SHA256,
hashes.SHA384,
hashes.SHA512,
hashes.SHA512_224,
hashes.SHA512_256,
hashes.SHA3_224,
hashes.SHA3_256,
hashes.SHA3_384,
hashes.SHA3_512,
hashes.SHAKE128,
hashes.SHAKE256,
)
_fips_ecdh_curves = (
ec.SECP224R1,
ec.SECP256R1,
ec.SECP384R1,
ec.SECP521R1,
)
_fips_rsa_min_key_size = 2048
_fips_rsa_min_public_exponent = 65537
_fips_dsa_min_modulus = 1 << 2048
_fips_dh_min_key_size = 2048
_fips_dh_min_modulus = 1 << _fips_dh_min_key_size
def __init__(self) -> None:
self._binding = binding.Binding()
self._ffi = self._binding.ffi
self._lib = self._binding.lib
self._fips_enabled = rust_openssl.is_fips_enabled()
self._cipher_registry: dict[
tuple[type[CipherAlgorithm], type[Mode]],
typing.Callable,
] = {}
self._register_default_ciphers()
def __repr__(self) -> str:
return "<OpenSSLBackend(version: {}, FIPS: {}, Legacy: {})>".format(
self.openssl_version_text(),
self._fips_enabled,
rust_openssl._legacy_provider_loaded,
)
def openssl_assert(
self,
ok: bool,
errors: list[rust_openssl.OpenSSLError] | None = None,
) -> None:
return binding._openssl_assert(ok, errors=errors)
def _enable_fips(self) -> None:
# This function enables FIPS mode for OpenSSL 3.0.0 on installs that
# have the FIPS provider installed properly.
self._binding._enable_fips()
assert rust_openssl.is_fips_enabled()
self._fips_enabled = rust_openssl.is_fips_enabled()
def openssl_version_text(self) -> str:
"""
Friendly string name of the loaded OpenSSL library. This is not
necessarily the same version as it was compiled against.
Example: OpenSSL 1.1.1d 10 Sep 2019
"""
return self._ffi.string(
self._lib.OpenSSL_version(self._lib.OPENSSL_VERSION)
).decode("ascii")
def openssl_version_number(self) -> int:
return self._lib.OpenSSL_version_num()
def _evp_md_from_algorithm(self, algorithm: hashes.HashAlgorithm):
if algorithm.name in ("blake2b", "blake2s"):
alg = f"{algorithm.name}{algorithm.digest_size * 8}".encode(
"ascii"
)
else:
alg = algorithm.name.encode("ascii")
evp_md = self._lib.EVP_get_digestbyname(alg)
return evp_md
def _evp_md_non_null_from_algorithm(self, algorithm: hashes.HashAlgorithm):
evp_md = self._evp_md_from_algorithm(algorithm)
self.openssl_assert(evp_md != self._ffi.NULL)
return evp_md
def hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
if self._fips_enabled and not isinstance(algorithm, self._fips_hashes):
return False
evp_md = self._evp_md_from_algorithm(algorithm)
return evp_md != self._ffi.NULL
def signature_hash_supported(
self, algorithm: hashes.HashAlgorithm
) -> bool:
# Dedicated check for hashing algorithm use in message digest for
# signatures, e.g. RSA PKCS#1 v1.5 SHA1 (sha1WithRSAEncryption).
if self._fips_enabled and isinstance(algorithm, hashes.SHA1):
return False
return self.hash_supported(algorithm)
def scrypt_supported(self) -> bool:
if self._fips_enabled:
return False
else:
return self._lib.Cryptography_HAS_SCRYPT == 1
def hmac_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
# FIPS mode still allows SHA1 for HMAC
if self._fips_enabled and isinstance(algorithm, hashes.SHA1):
return True
return self.hash_supported(algorithm)
def cipher_supported(self, cipher: CipherAlgorithm, mode: Mode) -> bool:
if self._fips_enabled:
# FIPS mode requires AES. TripleDES is disallowed/deprecated in
# FIPS 140-3.
if not isinstance(cipher, self._fips_ciphers):
return False
try:
adapter = self._cipher_registry[type(cipher), type(mode)]
except KeyError:
return False
evp_cipher = adapter(self, cipher, mode)
return self._ffi.NULL != evp_cipher
def register_cipher_adapter(self, cipher_cls, mode_cls, adapter) -> None:
if (cipher_cls, mode_cls) in self._cipher_registry:
raise ValueError(
f"Duplicate registration for: {cipher_cls} {mode_cls}."
)
self._cipher_registry[cipher_cls, mode_cls] = adapter
def _register_default_ciphers(self) -> None:
for cipher_cls in [AES, AES128, AES256]:
for mode_cls in [CBC, CTR, ECB, OFB, CFB, CFB8, GCM]:
self.register_cipher_adapter(
cipher_cls,
mode_cls,
GetCipherByName(
"{cipher.name}-{cipher.key_size}-{mode.name}"
),
)
for mode_cls in [CBC, CTR, ECB, OFB, CFB]:
self.register_cipher_adapter(
Camellia,
mode_cls,
GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}"),
)
for mode_cls in [CBC, CFB, CFB8, OFB]:
self.register_cipher_adapter(
TripleDES, mode_cls, GetCipherByName("des-ede3-{mode.name}")
)
self.register_cipher_adapter(
TripleDES, ECB, GetCipherByName("des-ede3")
)
# ChaCha20 uses the Long Name "chacha20" in OpenSSL, but in LibreSSL
# it uses "chacha"
self.register_cipher_adapter(
ChaCha20,
type(None),
GetCipherByName(
"chacha" if self._lib.CRYPTOGRAPHY_IS_LIBRESSL else "chacha20"
),
)
self.register_cipher_adapter(AES, XTS, _get_xts_cipher)
for mode_cls in [ECB, CBC, OFB, CFB, CTR, GCM]:
self.register_cipher_adapter(
SM4, mode_cls, GetCipherByName("sm4-{mode.name}")
)
# Don't register legacy ciphers if they're unavailable. Hypothetically
# this wouldn't be necessary because we test availability by seeing if
# we get an EVP_CIPHER * in the _CipherContext __init__, but OpenSSL 3
# will return a valid pointer even though the cipher is unavailable.
if (
rust_openssl._legacy_provider_loaded
or not self._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER
):
for mode_cls in [CBC, CFB, OFB, ECB]:
self.register_cipher_adapter(
_BlowfishInternal,
mode_cls,
GetCipherByName("bf-{mode.name}"),
)
for mode_cls in [CBC, CFB, OFB, ECB]:
self.register_cipher_adapter(
_SEEDInternal,
mode_cls,
GetCipherByName("seed-{mode.name}"),
)
for cipher_cls, mode_cls in itertools.product(
[_CAST5Internal, _IDEAInternal],
[CBC, OFB, CFB, ECB],
):
self.register_cipher_adapter(
cipher_cls,
mode_cls,
GetCipherByName("{cipher.name}-{mode.name}"),
)
self.register_cipher_adapter(
ARC4, type(None), GetCipherByName("rc4")
)
# We don't actually support RC2, this is just used by some tests.
self.register_cipher_adapter(
_RC2, type(None), GetCipherByName("rc2")
)
def create_symmetric_encryption_ctx(
self, cipher: CipherAlgorithm, mode: Mode
) -> _CipherContext:
return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT)
def create_symmetric_decryption_ctx(
self, cipher: CipherAlgorithm, mode: Mode
) -> _CipherContext:
return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT)
def pbkdf2_hmac_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
return self.hmac_supported(algorithm)
def _consume_errors(self) -> list[rust_openssl.OpenSSLError]:
return rust_openssl.capture_error_stack()
def generate_rsa_parameters_supported(
self, public_exponent: int, key_size: int
) -> bool:
return (
public_exponent >= 3
and public_exponent & 1 != 0
and key_size >= 512
)
def _bytes_to_bio(self, data: bytes) -> _MemoryBIO:
"""
Return a _MemoryBIO namedtuple of (BIO, char*).
The char* is the storage for the BIO and it must stay alive until the
BIO is finished with.
"""
data_ptr = self._ffi.from_buffer(data)
bio = self._lib.BIO_new_mem_buf(data_ptr, len(data))
self.openssl_assert(bio != self._ffi.NULL)
return _MemoryBIO(self._ffi.gc(bio, self._lib.BIO_free), data_ptr)
def _create_mem_bio_gc(self):
"""
Creates an empty memory BIO.
"""
bio_method = self._lib.BIO_s_mem()
self.openssl_assert(bio_method != self._ffi.NULL)
bio = self._lib.BIO_new(bio_method)
self.openssl_assert(bio != self._ffi.NULL)
bio = self._ffi.gc(bio, self._lib.BIO_free)
return bio
def _read_mem_bio(self, bio) -> bytes:
"""
Reads a memory BIO. This only works on memory BIOs.
"""
buf = self._ffi.new("char **")
buf_len = self._lib.BIO_get_mem_data(bio, buf)
self.openssl_assert(buf_len > 0)
self.openssl_assert(buf[0] != self._ffi.NULL)
bio_data = self._ffi.buffer(buf[0], buf_len)[:]
return bio_data
def _oaep_hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
if self._fips_enabled and isinstance(algorithm, hashes.SHA1):
return False
return isinstance(
algorithm,
(
hashes.SHA1,
hashes.SHA224,
hashes.SHA256,
hashes.SHA384,
hashes.SHA512,
),
)
def rsa_padding_supported(self, padding: AsymmetricPadding) -> bool:
if isinstance(padding, PKCS1v15):
return True
elif isinstance(padding, PSS) and isinstance(padding._mgf, MGF1):
# SHA1 is permissible in MGF1 in FIPS even when SHA1 is blocked
# as signature algorithm.
if self._fips_enabled and isinstance(
padding._mgf._algorithm, hashes.SHA1
):
return True
else:
return self.hash_supported(padding._mgf._algorithm)
elif isinstance(padding, OAEP) and isinstance(padding._mgf, MGF1):
return self._oaep_hash_supported(
padding._mgf._algorithm
) and self._oaep_hash_supported(padding._algorithm)
else:
return False
def rsa_encryption_supported(self, padding: AsymmetricPadding) -> bool:
if self._fips_enabled and isinstance(padding, PKCS1v15):
return False
else:
return self.rsa_padding_supported(padding)
def dsa_supported(self) -> bool:
return (
not self._lib.CRYPTOGRAPHY_IS_BORINGSSL and not self._fips_enabled
)
def dsa_hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
if not self.dsa_supported():
return False
return self.signature_hash_supported(algorithm)
def cmac_algorithm_supported(self, algorithm) -> bool:
return self.cipher_supported(
algorithm, CBC(b"\x00" * algorithm.block_size)
)
def _cert2ossl(self, cert: x509.Certificate) -> typing.Any:
data = cert.public_bytes(serialization.Encoding.DER)
mem_bio = self._bytes_to_bio(data)
x509 = self._lib.d2i_X509_bio(mem_bio.bio, self._ffi.NULL)
self.openssl_assert(x509 != self._ffi.NULL)
x509 = self._ffi.gc(x509, self._lib.X509_free)
return x509
def _ossl2cert(self, x509_ptr: typing.Any) -> x509.Certificate:
bio = self._create_mem_bio_gc()
res = self._lib.i2d_X509_bio(bio, x509_ptr)
self.openssl_assert(res == 1)
return x509.load_der_x509_certificate(self._read_mem_bio(bio))
def _key2ossl(self, key: PKCS12PrivateKeyTypes) -> typing.Any:
data = key.private_bytes(
serialization.Encoding.DER,
serialization.PrivateFormat.PKCS8,
serialization.NoEncryption(),
)
mem_bio = self._bytes_to_bio(data)
evp_pkey = self._lib.d2i_PrivateKey_bio(
mem_bio.bio,
self._ffi.NULL,
)
self.openssl_assert(evp_pkey != self._ffi.NULL)
return self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)
def _handle_key_loading_error(
self, errors: list[rust_openssl.OpenSSLError]
) -> typing.NoReturn:
if not errors:
raise ValueError(
"Could not deserialize key data. The data may be in an "
"incorrect format or it may be encrypted with an unsupported "
"algorithm."
)
elif (
errors[0]._lib_reason_match(
self._lib.ERR_LIB_EVP, self._lib.EVP_R_BAD_DECRYPT
)
or errors[0]._lib_reason_match(
self._lib.ERR_LIB_PKCS12,
self._lib.PKCS12_R_PKCS12_CIPHERFINAL_ERROR,
)
or (
self._lib.Cryptography_HAS_PROVIDERS
and errors[0]._lib_reason_match(
self._lib.ERR_LIB_PROV,
self._lib.PROV_R_BAD_DECRYPT,
)
)
):
raise ValueError("Bad decrypt. Incorrect password?")
elif any(
error._lib_reason_match(
self._lib.ERR_LIB_EVP,
self._lib.EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM,
)
for error in errors
):
raise ValueError("Unsupported public key algorithm.")
else:
raise ValueError(
"Could not deserialize key data. The data may be in an "
"incorrect format, it may be encrypted with an unsupported "
"algorithm, or it may be an unsupported key type (e.g. EC "
"curves with explicit parameters).",
errors,
)
def elliptic_curve_supported(self, curve: ec.EllipticCurve) -> bool:
if self._fips_enabled and not isinstance(
curve, self._fips_ecdh_curves
):
return False
return rust_openssl.ec.curve_supported(curve)
def elliptic_curve_signature_algorithm_supported(
self,
signature_algorithm: ec.EllipticCurveSignatureAlgorithm,
curve: ec.EllipticCurve,
) -> bool:
# We only support ECDSA right now.
if not isinstance(signature_algorithm, ec.ECDSA):
return False
return self.elliptic_curve_supported(curve) and (
isinstance(signature_algorithm.algorithm, asym_utils.Prehashed)
or self.hash_supported(signature_algorithm.algorithm)
)
def elliptic_curve_exchange_algorithm_supported(
self, algorithm: ec.ECDH, curve: ec.EllipticCurve
) -> bool:
return self.elliptic_curve_supported(curve) and isinstance(
algorithm, ec.ECDH
)
def dh_supported(self) -> bool:
return not self._lib.CRYPTOGRAPHY_IS_BORINGSSL
def dh_x942_serialization_supported(self) -> bool:
return self._lib.Cryptography_HAS_EVP_PKEY_DHX == 1
def x25519_supported(self) -> bool:
if self._fips_enabled:
return False
return True
def x448_supported(self) -> bool:
if self._fips_enabled:
return False
return (
not self._lib.CRYPTOGRAPHY_IS_LIBRESSL
and not self._lib.CRYPTOGRAPHY_IS_BORINGSSL
)
def ed25519_supported(self) -> bool:
if self._fips_enabled:
return False
return True
def ed448_supported(self) -> bool:
if self._fips_enabled:
return False
return (
not self._lib.CRYPTOGRAPHY_IS_LIBRESSL
and not self._lib.CRYPTOGRAPHY_IS_BORINGSSL
)
def aead_cipher_supported(self, cipher) -> bool:
return aead._aead_cipher_supported(self, cipher)
def _zero_data(self, data, length: int) -> None:
# We clear things this way because at the moment we're not
# sure of a better way that can guarantee it overwrites the
# memory of a bytearray and doesn't just replace the underlying char *.
for i in range(length):
data[i] = 0
@contextlib.contextmanager
def _zeroed_null_terminated_buf(self, data):
"""
This method takes bytes, which can be a bytestring or a mutable
buffer like a bytearray, and yields a null-terminated version of that
data. This is required because PKCS12_parse doesn't take a length with
its password char * and ffi.from_buffer doesn't provide null
termination. So, to support zeroing the data via bytearray we
need to build this ridiculous construct that copies the memory, but
zeroes it after use.
"""
if data is None:
yield self._ffi.NULL
else:
data_len = len(data)
buf = self._ffi.new("char[]", data_len + 1)
self._ffi.memmove(buf, data, data_len)
try:
yield buf
finally:
# Cast to a uint8_t * so we can assign by integer
self._zero_data(self._ffi.cast("uint8_t *", buf), data_len)
def load_key_and_certificates_from_pkcs12(
self, data: bytes, password: bytes | None
) -> tuple[
PrivateKeyTypes | None,
x509.Certificate | None,
list[x509.Certificate],
]:
pkcs12 = self.load_pkcs12(data, password)
return (
pkcs12.key,
pkcs12.cert.certificate if pkcs12.cert else None,
[cert.certificate for cert in pkcs12.additional_certs],
)
def load_pkcs12(
self, data: bytes, password: bytes | None
) -> PKCS12KeyAndCertificates:
if password is not None:
utils._check_byteslike("password", password)
bio = self._bytes_to_bio(data)
p12 = self._lib.d2i_PKCS12_bio(bio.bio, self._ffi.NULL)
if p12 == self._ffi.NULL:
self._consume_errors()
raise ValueError("Could not deserialize PKCS12 data")
p12 = self._ffi.gc(p12, self._lib.PKCS12_free)
evp_pkey_ptr = self._ffi.new("EVP_PKEY **")
x509_ptr = self._ffi.new("X509 **")
sk_x509_ptr = self._ffi.new("Cryptography_STACK_OF_X509 **")
with self._zeroed_null_terminated_buf(password) as password_buf:
res = self._lib.PKCS12_parse(
p12, password_buf, evp_pkey_ptr, x509_ptr, sk_x509_ptr
)
if res == 0:
self._consume_errors()
raise ValueError("Invalid password or PKCS12 data")
cert = None
key = None
additional_certificates = []
if evp_pkey_ptr[0] != self._ffi.NULL:
evp_pkey = self._ffi.gc(evp_pkey_ptr[0], self._lib.EVP_PKEY_free)
# We don't support turning off RSA key validation when loading
# PKCS12 keys
key = rust_openssl.keys.private_key_from_ptr(
int(self._ffi.cast("uintptr_t", evp_pkey)),
unsafe_skip_rsa_key_validation=False,
)
if x509_ptr[0] != self._ffi.NULL:
x509 = self._ffi.gc(x509_ptr[0], self._lib.X509_free)
cert_obj = self._ossl2cert(x509)
name = None
maybe_name = self._lib.X509_alias_get0(x509, self._ffi.NULL)
if maybe_name != self._ffi.NULL:
name = self._ffi.string(maybe_name)
cert = PKCS12Certificate(cert_obj, name)
if sk_x509_ptr[0] != self._ffi.NULL:
sk_x509 = self._ffi.gc(sk_x509_ptr[0], self._lib.sk_X509_free)
num = self._lib.sk_X509_num(sk_x509_ptr[0])
# In OpenSSL < 3.0.0 PKCS12 parsing reverses the order of the
# certificates.
indices: typing.Iterable[int]
if (
self._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER
or self._lib.CRYPTOGRAPHY_IS_BORINGSSL
):
indices = range(num)
else:
indices = reversed(range(num))
for i in indices:
x509 = self._lib.sk_X509_value(sk_x509, i)
self.openssl_assert(x509 != self._ffi.NULL)
x509 = self._ffi.gc(x509, self._lib.X509_free)
addl_cert = self._ossl2cert(x509)
addl_name = None
maybe_name = self._lib.X509_alias_get0(x509, self._ffi.NULL)
if maybe_name != self._ffi.NULL:
addl_name = self._ffi.string(maybe_name)
additional_certificates.append(
PKCS12Certificate(addl_cert, addl_name)
)
return PKCS12KeyAndCertificates(key, cert, additional_certificates)
def serialize_key_and_certificates_to_pkcs12(
self,
name: bytes | None,
key: PKCS12PrivateKeyTypes | None,
cert: x509.Certificate | None,
cas: list[_PKCS12CATypes] | None,
encryption_algorithm: serialization.KeySerializationEncryption,
) -> bytes:
password = None
if name is not None:
utils._check_bytes("name", name)
if isinstance(encryption_algorithm, serialization.NoEncryption):
nid_cert = -1
nid_key = -1
pkcs12_iter = 0
mac_iter = 0
mac_alg = self._ffi.NULL
elif isinstance(
encryption_algorithm, serialization.BestAvailableEncryption
):
# PKCS12 encryption is hopeless trash and can never be fixed.
# OpenSSL 3 supports PBESv2, but Libre and Boring do not, so
# we use PBESv1 with 3DES on the older paths.
if self._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER:
nid_cert = self._lib.NID_aes_256_cbc
nid_key = self._lib.NID_aes_256_cbc
else:
nid_cert = self._lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC
nid_key = self._lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC
# At least we can set this higher than OpenSSL's default
pkcs12_iter = 20000
# mac_iter chosen for compatibility reasons, see:
# https://www.openssl.org/docs/man1.1.1/man3/PKCS12_create.html
# Did we mention how lousy PKCS12 encryption is?
mac_iter = 1
# MAC algorithm can only be set on OpenSSL 3.0.0+
mac_alg = self._ffi.NULL
password = encryption_algorithm.password
elif (
isinstance(
encryption_algorithm, serialization._KeySerializationEncryption
)
and encryption_algorithm._format
is serialization.PrivateFormat.PKCS12
):
# Default to OpenSSL's defaults. Behavior will vary based on the
# version of OpenSSL cryptography is compiled against.
nid_cert = 0
nid_key = 0
# Use the default iters we use in best available
pkcs12_iter = 20000
# See the Best Available comment for why this is 1
mac_iter = 1
password = encryption_algorithm.password
keycertalg = encryption_algorithm._key_cert_algorithm
if keycertalg is PBES.PBESv1SHA1And3KeyTripleDESCBC:
nid_cert = self._lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC
nid_key = self._lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC
elif keycertalg is PBES.PBESv2SHA256AndAES256CBC:
if not self._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER:
raise UnsupportedAlgorithm(
"PBESv2 is not supported by this version of OpenSSL"
)
nid_cert = self._lib.NID_aes_256_cbc
nid_key = self._lib.NID_aes_256_cbc
else:
assert keycertalg is None
# We use OpenSSL's defaults
if encryption_algorithm._hmac_hash is not None:
if not self._lib.Cryptography_HAS_PKCS12_SET_MAC:
raise UnsupportedAlgorithm(
"Setting MAC algorithm is not supported by this "
"version of OpenSSL."
)
mac_alg = self._evp_md_non_null_from_algorithm(
encryption_algorithm._hmac_hash
)
self.openssl_assert(mac_alg != self._ffi.NULL)
else:
mac_alg = self._ffi.NULL
if encryption_algorithm._kdf_rounds is not None:
pkcs12_iter = encryption_algorithm._kdf_rounds
else:
raise ValueError("Unsupported key encryption type")
if cas is None or len(cas) == 0:
sk_x509 = self._ffi.NULL
else:
sk_x509 = self._lib.sk_X509_new_null()
sk_x509 = self._ffi.gc(sk_x509, self._lib.sk_X509_free)
# This list is to keep the x509 values alive until end of function
ossl_cas = []
for ca in cas:
if isinstance(ca, PKCS12Certificate):
ca_alias = ca.friendly_name
ossl_ca = self._cert2ossl(ca.certificate)
if ca_alias is None:
res = self._lib.X509_alias_set1(
ossl_ca, self._ffi.NULL, -1
)
else:
res = self._lib.X509_alias_set1(
ossl_ca, ca_alias, len(ca_alias)
)
self.openssl_assert(res == 1)
else:
ossl_ca = self._cert2ossl(ca)
ossl_cas.append(ossl_ca)
res = self._lib.sk_X509_push(sk_x509, ossl_ca)
backend.openssl_assert(res >= 1)
with self._zeroed_null_terminated_buf(password) as password_buf:
with self._zeroed_null_terminated_buf(name) as name_buf:
ossl_cert = self._cert2ossl(cert) if cert else self._ffi.NULL
ossl_pkey = (
self._key2ossl(key) if key is not None else self._ffi.NULL
)
p12 = self._lib.PKCS12_create(
password_buf,
name_buf,
ossl_pkey,
ossl_cert,
sk_x509,
nid_key,
nid_cert,
pkcs12_iter,
mac_iter,
0,
)
if p12 == self._ffi.NULL:
errors = self._consume_errors()
raise ValueError(
(
"Failed to create PKCS12 (does the key match the "
"certificate?)"
),
errors,
)
if (
self._lib.Cryptography_HAS_PKCS12_SET_MAC
and mac_alg != self._ffi.NULL
):
self._lib.PKCS12_set_mac(
p12,
password_buf,
-1,
self._ffi.NULL,
0,
mac_iter,
mac_alg,
)
self.openssl_assert(p12 != self._ffi.NULL)
p12 = self._ffi.gc(p12, self._lib.PKCS12_free)
bio = self._create_mem_bio_gc()
res = self._lib.i2d_PKCS12_bio(bio, p12)
self.openssl_assert(res > 0)
return self._read_mem_bio(bio)
def poly1305_supported(self) -> bool:
if self._fips_enabled:
return False
elif (
self._lib.CRYPTOGRAPHY_IS_BORINGSSL
or self._lib.CRYPTOGRAPHY_IS_LIBRESSL
):
return True
else:
return self._lib.Cryptography_HAS_POLY1305 == 1
def pkcs7_supported(self) -> bool:
return not self._lib.CRYPTOGRAPHY_IS_BORINGSSL
class GetCipherByName:
def __init__(self, fmt: str):
self._fmt = fmt
def __call__(self, backend: Backend, cipher: CipherAlgorithm, mode: Mode):
cipher_name = self._fmt.format(cipher=cipher, mode=mode).lower()
evp_cipher = backend._lib.EVP_get_cipherbyname(
cipher_name.encode("ascii")
)
# try EVP_CIPHER_fetch if present
if (
evp_cipher == backend._ffi.NULL
and backend._lib.Cryptography_HAS_300_EVP_CIPHER
):
evp_cipher = backend._lib.EVP_CIPHER_fetch(
backend._ffi.NULL,
cipher_name.encode("ascii"),
backend._ffi.NULL,
)
backend._consume_errors()
return evp_cipher
def _get_xts_cipher(backend: Backend, cipher: AES, mode):
cipher_name = f"aes-{cipher.key_size // 2}-xts"
return backend._lib.EVP_get_cipherbyname(cipher_name.encode("ascii"))
backend = Backend()

View File

@@ -0,0 +1,282 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
import typing
from cryptography.exceptions import InvalidTag, UnsupportedAlgorithm, _Reasons
from cryptography.hazmat.primitives import ciphers
from cryptography.hazmat.primitives.ciphers import algorithms, modes
if typing.TYPE_CHECKING:
from cryptography.hazmat.backends.openssl.backend import Backend
class _CipherContext:
_ENCRYPT = 1
_DECRYPT = 0
_MAX_CHUNK_SIZE = 2**29
def __init__(self, backend: Backend, cipher, mode, operation: int) -> None:
self._backend = backend
self._cipher = cipher
self._mode = mode
self._operation = operation
self._tag: bytes | None = None
if isinstance(self._cipher, ciphers.BlockCipherAlgorithm):
self._block_size_bytes = self._cipher.block_size // 8
else:
self._block_size_bytes = 1
ctx = self._backend._lib.EVP_CIPHER_CTX_new()
ctx = self._backend._ffi.gc(
ctx, self._backend._lib.EVP_CIPHER_CTX_free
)
registry = self._backend._cipher_registry
try:
adapter = registry[type(cipher), type(mode)]
except KeyError:
raise UnsupportedAlgorithm(
"cipher {} in {} mode is not supported "
"by this backend.".format(
cipher.name, mode.name if mode else mode
),
_Reasons.UNSUPPORTED_CIPHER,
)
evp_cipher = adapter(self._backend, cipher, mode)
if evp_cipher == self._backend._ffi.NULL:
msg = f"cipher {cipher.name} "
if mode is not None:
msg += f"in {mode.name} mode "
msg += (
"is not supported by this backend (Your version of OpenSSL "
"may be too old. Current version: {}.)"
).format(self._backend.openssl_version_text())
raise UnsupportedAlgorithm(msg, _Reasons.UNSUPPORTED_CIPHER)
if isinstance(mode, modes.ModeWithInitializationVector):
iv_nonce = self._backend._ffi.from_buffer(
mode.initialization_vector
)
elif isinstance(mode, modes.ModeWithTweak):
iv_nonce = self._backend._ffi.from_buffer(mode.tweak)
elif isinstance(mode, modes.ModeWithNonce):
iv_nonce = self._backend._ffi.from_buffer(mode.nonce)
elif isinstance(cipher, algorithms.ChaCha20):
iv_nonce = self._backend._ffi.from_buffer(cipher.nonce)
else:
iv_nonce = self._backend._ffi.NULL
# begin init with cipher and operation type
res = self._backend._lib.EVP_CipherInit_ex(
ctx,
evp_cipher,
self._backend._ffi.NULL,
self._backend._ffi.NULL,
self._backend._ffi.NULL,
operation,
)
self._backend.openssl_assert(res != 0)
# set the key length to handle variable key ciphers
res = self._backend._lib.EVP_CIPHER_CTX_set_key_length(
ctx, len(cipher.key)
)
self._backend.openssl_assert(res != 0)
if isinstance(mode, modes.GCM):
res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
ctx,
self._backend._lib.EVP_CTRL_AEAD_SET_IVLEN,
len(iv_nonce),
self._backend._ffi.NULL,
)
self._backend.openssl_assert(res != 0)
if mode.tag is not None:
res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
ctx,
self._backend._lib.EVP_CTRL_AEAD_SET_TAG,
len(mode.tag),
mode.tag,
)
self._backend.openssl_assert(res != 0)
self._tag = mode.tag
# pass key/iv
res = self._backend._lib.EVP_CipherInit_ex(
ctx,
self._backend._ffi.NULL,
self._backend._ffi.NULL,
self._backend._ffi.from_buffer(cipher.key),
iv_nonce,
operation,
)
# Check for XTS mode duplicate keys error
errors = self._backend._consume_errors()
lib = self._backend._lib
if res == 0 and (
(
not lib.CRYPTOGRAPHY_IS_LIBRESSL
and errors[0]._lib_reason_match(
lib.ERR_LIB_EVP, lib.EVP_R_XTS_DUPLICATED_KEYS
)
)
or (
lib.Cryptography_HAS_PROVIDERS
and errors[0]._lib_reason_match(
lib.ERR_LIB_PROV, lib.PROV_R_XTS_DUPLICATED_KEYS
)
)
):
raise ValueError("In XTS mode duplicated keys are not allowed")
self._backend.openssl_assert(res != 0, errors=errors)
# We purposely disable padding here as it's handled higher up in the
# API.
self._backend._lib.EVP_CIPHER_CTX_set_padding(ctx, 0)
self._ctx = ctx
def update(self, data: bytes) -> bytes:
buf = bytearray(len(data) + self._block_size_bytes - 1)
n = self.update_into(data, buf)
return bytes(buf[:n])
def update_into(self, data: bytes, buf: bytes) -> int:
total_data_len = len(data)
if len(buf) < (total_data_len + self._block_size_bytes - 1):
raise ValueError(
"buffer must be at least {} bytes for this payload".format(
len(data) + self._block_size_bytes - 1
)
)
data_processed = 0
total_out = 0
outlen = self._backend._ffi.new("int *")
baseoutbuf = self._backend._ffi.from_buffer(buf, require_writable=True)
baseinbuf = self._backend._ffi.from_buffer(data)
while data_processed != total_data_len:
outbuf = baseoutbuf + total_out
inbuf = baseinbuf + data_processed
inlen = min(self._MAX_CHUNK_SIZE, total_data_len - data_processed)
res = self._backend._lib.EVP_CipherUpdate(
self._ctx, outbuf, outlen, inbuf, inlen
)
if res == 0 and isinstance(self._mode, modes.XTS):
self._backend._consume_errors()
raise ValueError(
"In XTS mode you must supply at least a full block in the "
"first update call. For AES this is 16 bytes."
)
else:
self._backend.openssl_assert(res != 0)
data_processed += inlen
total_out += outlen[0]
return total_out
def finalize(self) -> bytes:
if (
self._operation == self._DECRYPT
and isinstance(self._mode, modes.ModeWithAuthenticationTag)
and self.tag is None
):
raise ValueError(
"Authentication tag must be provided when decrypting."
)
buf = self._backend._ffi.new("unsigned char[]", self._block_size_bytes)
outlen = self._backend._ffi.new("int *")
res = self._backend._lib.EVP_CipherFinal_ex(self._ctx, buf, outlen)
if res == 0:
errors = self._backend._consume_errors()
if not errors and isinstance(self._mode, modes.GCM):
raise InvalidTag
lib = self._backend._lib
self._backend.openssl_assert(
errors[0]._lib_reason_match(
lib.ERR_LIB_EVP,
lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH,
)
or (
lib.Cryptography_HAS_PROVIDERS
and errors[0]._lib_reason_match(
lib.ERR_LIB_PROV,
lib.PROV_R_WRONG_FINAL_BLOCK_LENGTH,
)
)
or (
lib.CRYPTOGRAPHY_IS_BORINGSSL
and errors[0].reason
== lib.CIPHER_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH
),
errors=errors,
)
raise ValueError(
"The length of the provided data is not a multiple of "
"the block length."
)
if (
isinstance(self._mode, modes.GCM)
and self._operation == self._ENCRYPT
):
tag_buf = self._backend._ffi.new(
"unsigned char[]", self._block_size_bytes
)
res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
self._ctx,
self._backend._lib.EVP_CTRL_AEAD_GET_TAG,
self._block_size_bytes,
tag_buf,
)
self._backend.openssl_assert(res != 0)
self._tag = self._backend._ffi.buffer(tag_buf)[:]
res = self._backend._lib.EVP_CIPHER_CTX_reset(self._ctx)
self._backend.openssl_assert(res == 1)
return self._backend._ffi.buffer(buf)[: outlen[0]]
def finalize_with_tag(self, tag: bytes) -> bytes:
tag_len = len(tag)
if tag_len < self._mode._min_tag_length:
raise ValueError(
"Authentication tag must be {} bytes or longer.".format(
self._mode._min_tag_length
)
)
elif tag_len > self._block_size_bytes:
raise ValueError(
"Authentication tag cannot be more than {} bytes.".format(
self._block_size_bytes
)
)
res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
self._ctx, self._backend._lib.EVP_CTRL_AEAD_SET_TAG, len(tag), tag
)
self._backend.openssl_assert(res != 0)
self._tag = tag
return self.finalize()
def authenticate_additional_data(self, data: bytes) -> None:
outlen = self._backend._ffi.new("int *")
res = self._backend._lib.EVP_CipherUpdate(
self._ctx,
self._backend._ffi.NULL,
outlen,
self._backend._ffi.from_buffer(data),
len(data),
)
self._backend.openssl_assert(res != 0)
@property
def tag(self) -> bytes | None:
return self._tag

View File

@@ -0,0 +1,32 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
from cryptography import x509
# CRLReason ::= ENUMERATED {
# unspecified (0),
# keyCompromise (1),
# cACompromise (2),
# affiliationChanged (3),
# superseded (4),
# cessationOfOperation (5),
# certificateHold (6),
# -- value 7 is not used
# removeFromCRL (8),
# privilegeWithdrawn (9),
# aACompromise (10) }
_CRL_ENTRY_REASON_ENUM_TO_CODE = {
x509.ReasonFlags.unspecified: 0,
x509.ReasonFlags.key_compromise: 1,
x509.ReasonFlags.ca_compromise: 2,
x509.ReasonFlags.affiliation_changed: 3,
x509.ReasonFlags.superseded: 4,
x509.ReasonFlags.cessation_of_operation: 5,
x509.ReasonFlags.certificate_hold: 6,
x509.ReasonFlags.remove_from_crl: 8,
x509.ReasonFlags.privilege_withdrawn: 9,
x509.ReasonFlags.aa_compromise: 10,
}

View File

@@ -0,0 +1,3 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

View File

@@ -0,0 +1,17 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
import typing
def check_pkcs7_padding(data: bytes) -> bool: ...
def check_ansix923_padding(data: bytes) -> bool: ...
class ObjectIdentifier:
def __init__(self, val: str) -> None: ...
@property
def dotted_string(self) -> str: ...
@property
def _name(self) -> str: ...
T = typing.TypeVar("T")

View File

@@ -0,0 +1,8 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
import typing
lib = typing.Any
ffi = typing.Any

View File

@@ -0,0 +1,14 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
class TestCertificate:
not_after_tag: int
not_before_tag: int
issuer_value_tags: list[int]
subject_value_tags: list[int]
def decode_dss_signature(signature: bytes) -> tuple[int, int]: ...
def encode_dss_signature(r: int, s: int) -> bytes: ...
def parse_spki_for_data(data: bytes) -> bytes: ...
def test_parse_certificate(data: bytes) -> TestCertificate: ...

View File

@@ -0,0 +1,17 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
class _Reasons:
BACKEND_MISSING_INTERFACE: _Reasons
UNSUPPORTED_HASH: _Reasons
UNSUPPORTED_CIPHER: _Reasons
UNSUPPORTED_PADDING: _Reasons
UNSUPPORTED_MGF: _Reasons
UNSUPPORTED_PUBLIC_KEY_ALGORITHM: _Reasons
UNSUPPORTED_ELLIPTIC_CURVE: _Reasons
UNSUPPORTED_SERIALIZATION: _Reasons
UNSUPPORTED_X509: _Reasons
UNSUPPORTED_EXCHANGE_ALGORITHM: _Reasons
UNSUPPORTED_DIFFIE_HELLMAN: _Reasons
UNSUPPORTED_MAC: _Reasons

View File

@@ -0,0 +1,23 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes
from cryptography.x509.ocsp import (
OCSPRequest,
OCSPRequestBuilder,
OCSPResponse,
OCSPResponseBuilder,
OCSPResponseStatus,
)
def load_der_ocsp_request(data: bytes) -> OCSPRequest: ...
def load_der_ocsp_response(data: bytes) -> OCSPResponse: ...
def create_ocsp_request(builder: OCSPRequestBuilder) -> OCSPRequest: ...
def create_ocsp_response(
status: OCSPResponseStatus,
builder: OCSPResponseBuilder | None,
private_key: PrivateKeyTypes | None,
hash_algorithm: hashes.HashAlgorithm | None,
) -> OCSPResponse: ...

View File

@@ -0,0 +1,59 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
import typing
from cryptography.hazmat.bindings._rust.openssl import (
aead,
cmac,
dh,
dsa,
ec,
ed448,
ed25519,
hashes,
hmac,
kdf,
keys,
poly1305,
rsa,
x448,
x25519,
)
__all__ = [
"openssl_version",
"raise_openssl_error",
"aead",
"cmac",
"dh",
"dsa",
"ec",
"hashes",
"hmac",
"kdf",
"keys",
"ed448",
"ed25519",
"rsa",
"poly1305",
"x448",
"x25519",
]
_legacy_provider_loaded: bool
def openssl_version() -> int: ...
def raise_openssl_error() -> typing.NoReturn: ...
def capture_error_stack() -> list[OpenSSLError]: ...
def is_fips_enabled() -> bool: ...
class OpenSSLError:
@property
def lib(self) -> int: ...
@property
def reason(self) -> int: ...
@property
def reason_text(self) -> bytes: ...
def _lib_reason_match(self, lib: int, reason: int) -> bool: ...

View File

@@ -0,0 +1,69 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
class ChaCha20Poly1305:
def __init__(self, key: bytes) -> None: ...
@staticmethod
def generate_key() -> bytes: ...
def encrypt(
self,
nonce: bytes,
data: bytes,
associated_data: bytes | None,
) -> bytes: ...
def decrypt(
self,
nonce: bytes,
data: bytes,
associated_data: bytes | None,
) -> bytes: ...
class AESSIV:
def __init__(self, key: bytes) -> None: ...
@staticmethod
def generate_key(key_size: int) -> bytes: ...
def encrypt(
self,
data: bytes,
associated_data: list[bytes] | None,
) -> bytes: ...
def decrypt(
self,
data: bytes,
associated_data: list[bytes] | None,
) -> bytes: ...
class AESOCB3:
def __init__(self, key: bytes) -> None: ...
@staticmethod
def generate_key(key_size: int) -> bytes: ...
def encrypt(
self,
nonce: bytes,
data: bytes,
associated_data: bytes | None,
) -> bytes: ...
def decrypt(
self,
nonce: bytes,
data: bytes,
associated_data: bytes | None,
) -> bytes: ...
class AESGCMSIV:
def __init__(self, key: bytes) -> None: ...
@staticmethod
def generate_key(key_size: int) -> bytes: ...
def encrypt(
self,
nonce: bytes,
data: bytes,
associated_data: bytes | None,
) -> bytes: ...
def decrypt(
self,
nonce: bytes,
data: bytes,
associated_data: bytes | None,
) -> bytes: ...

View File

@@ -0,0 +1,18 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
import typing
from cryptography.hazmat.primitives import ciphers
class CMAC:
def __init__(
self,
algorithm: ciphers.BlockCipherAlgorithm,
backend: typing.Any = None,
) -> None: ...
def update(self, data: bytes) -> None: ...
def finalize(self) -> bytes: ...
def verify(self, signature: bytes) -> None: ...
def copy(self) -> CMAC: ...

View File

@@ -0,0 +1,51 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
import typing
from cryptography.hazmat.primitives.asymmetric import dh
MIN_MODULUS_SIZE: int
class DHPrivateKey: ...
class DHPublicKey: ...
class DHParameters: ...
class DHPrivateNumbers:
def __init__(self, x: int, public_numbers: DHPublicNumbers) -> None: ...
def private_key(self, backend: typing.Any = None) -> dh.DHPrivateKey: ...
@property
def x(self) -> int: ...
@property
def public_numbers(self) -> DHPublicNumbers: ...
class DHPublicNumbers:
def __init__(
self, y: int, parameter_numbers: DHParameterNumbers
) -> None: ...
def public_key(self, backend: typing.Any = None) -> dh.DHPublicKey: ...
@property
def y(self) -> int: ...
@property
def parameter_numbers(self) -> DHParameterNumbers: ...
class DHParameterNumbers:
def __init__(self, p: int, g: int, q: int | None = None) -> None: ...
def parameters(self, backend: typing.Any = None) -> dh.DHParameters: ...
@property
def p(self) -> int: ...
@property
def g(self) -> int: ...
@property
def q(self) -> int | None: ...
def generate_parameters(
generator: int, key_size: int, backend: typing.Any = None
) -> dh.DHParameters: ...
def from_pem_parameters(
data: bytes, backend: typing.Any = None
) -> dh.DHParameters: ...
def from_der_parameters(
data: bytes, backend: typing.Any = None
) -> dh.DHParameters: ...

View File

@@ -0,0 +1,41 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
import typing
from cryptography.hazmat.primitives.asymmetric import dsa
class DSAPrivateKey: ...
class DSAPublicKey: ...
class DSAParameters: ...
class DSAPrivateNumbers:
def __init__(self, x: int, public_numbers: DSAPublicNumbers) -> None: ...
@property
def x(self) -> int: ...
@property
def public_numbers(self) -> DSAPublicNumbers: ...
def private_key(self, backend: typing.Any = None) -> dsa.DSAPrivateKey: ...
class DSAPublicNumbers:
def __init__(
self, y: int, parameter_numbers: DSAParameterNumbers
) -> None: ...
@property
def y(self) -> int: ...
@property
def parameter_numbers(self) -> DSAParameterNumbers: ...
def public_key(self, backend: typing.Any = None) -> dsa.DSAPublicKey: ...
class DSAParameterNumbers:
def __init__(self, p: int, q: int, g: int) -> None: ...
@property
def p(self) -> int: ...
@property
def q(self) -> int: ...
@property
def g(self) -> int: ...
def parameters(self, backend: typing.Any = None) -> dsa.DSAParameters: ...
def generate_parameters(key_size: int) -> dsa.DSAParameters: ...

View File

@@ -0,0 +1,52 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
import typing
from cryptography.hazmat.primitives.asymmetric import ec
class ECPrivateKey: ...
class ECPublicKey: ...
class EllipticCurvePrivateNumbers:
def __init__(
self, private_value: int, public_numbers: EllipticCurvePublicNumbers
) -> None: ...
def private_key(
self, backend: typing.Any = None
) -> ec.EllipticCurvePrivateKey: ...
@property
def private_value(self) -> int: ...
@property
def public_numbers(self) -> EllipticCurvePublicNumbers: ...
class EllipticCurvePublicNumbers:
def __init__(self, x: int, y: int, curve: ec.EllipticCurve) -> None: ...
def public_key(
self, backend: typing.Any = None
) -> ec.EllipticCurvePublicKey: ...
@property
def x(self) -> int: ...
@property
def y(self) -> int: ...
@property
def curve(self) -> ec.EllipticCurve: ...
def __eq__(self, other: object) -> bool: ...
def curve_supported(curve: ec.EllipticCurve) -> bool: ...
def generate_private_key(
curve: ec.EllipticCurve, backend: typing.Any = None
) -> ec.EllipticCurvePrivateKey: ...
def from_private_numbers(
numbers: ec.EllipticCurvePrivateNumbers,
) -> ec.EllipticCurvePrivateKey: ...
def from_public_numbers(
numbers: ec.EllipticCurvePublicNumbers,
) -> ec.EllipticCurvePublicKey: ...
def from_public_bytes(
curve: ec.EllipticCurve, data: bytes
) -> ec.EllipticCurvePublicKey: ...
def derive_private_key(
private_value: int, curve: ec.EllipticCurve
) -> ec.EllipticCurvePrivateKey: ...

View File

@@ -0,0 +1,12 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from cryptography.hazmat.primitives.asymmetric import ed25519
class Ed25519PrivateKey: ...
class Ed25519PublicKey: ...
def generate_key() -> ed25519.Ed25519PrivateKey: ...
def from_private_bytes(data: bytes) -> ed25519.Ed25519PrivateKey: ...
def from_public_bytes(data: bytes) -> ed25519.Ed25519PublicKey: ...

View File

@@ -0,0 +1,12 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from cryptography.hazmat.primitives.asymmetric import ed448
class Ed448PrivateKey: ...
class Ed448PublicKey: ...
def generate_key() -> ed448.Ed448PrivateKey: ...
def from_private_bytes(data: bytes) -> ed448.Ed448PrivateKey: ...
def from_public_bytes(data: bytes) -> ed448.Ed448PublicKey: ...

View File

@@ -0,0 +1,17 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
import typing
from cryptography.hazmat.primitives import hashes
class Hash(hashes.HashContext):
def __init__(
self, algorithm: hashes.HashAlgorithm, backend: typing.Any = None
) -> None: ...
@property
def algorithm(self) -> hashes.HashAlgorithm: ...
def update(self, data: bytes) -> None: ...
def finalize(self) -> bytes: ...
def copy(self) -> Hash: ...

View File

@@ -0,0 +1,21 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
import typing
from cryptography.hazmat.primitives import hashes
class HMAC(hashes.HashContext):
def __init__(
self,
key: bytes,
algorithm: hashes.HashAlgorithm,
backend: typing.Any = None,
) -> None: ...
@property
def algorithm(self) -> hashes.HashAlgorithm: ...
def update(self, data: bytes) -> None: ...
def finalize(self) -> bytes: ...
def verify(self, signature: bytes) -> None: ...
def copy(self) -> HMAC: ...

View File

@@ -0,0 +1,22 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from cryptography.hazmat.primitives.hashes import HashAlgorithm
def derive_pbkdf2_hmac(
key_material: bytes,
algorithm: HashAlgorithm,
salt: bytes,
iterations: int,
length: int,
) -> bytes: ...
def derive_scrypt(
key_material: bytes,
salt: bytes,
n: int,
r: int,
p: int,
max_mem: int,
length: int,
) -> bytes: ...

View File

@@ -0,0 +1,37 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
import typing
from cryptography.hazmat.primitives.asymmetric.types import (
PrivateKeyTypes,
PublicKeyTypes,
)
def private_key_from_ptr(
ptr: int,
unsafe_skip_rsa_key_validation: bool,
) -> PrivateKeyTypes: ...
def load_der_private_key(
data: bytes,
password: bytes | None,
backend: typing.Any = None,
*,
unsafe_skip_rsa_key_validation: bool = False,
) -> PrivateKeyTypes: ...
def load_pem_private_key(
data: bytes,
password: bytes | None,
backend: typing.Any = None,
*,
unsafe_skip_rsa_key_validation: bool = False,
) -> PrivateKeyTypes: ...
def load_der_public_key(
data: bytes,
backend: typing.Any = None,
) -> PublicKeyTypes: ...
def load_pem_public_key(
data: bytes,
backend: typing.Any = None,
) -> PublicKeyTypes: ...

View File

@@ -0,0 +1,13 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
class Poly1305:
def __init__(self, key: bytes) -> None: ...
@staticmethod
def generate_tag(key: bytes, data: bytes) -> bytes: ...
@staticmethod
def verify_tag(key: bytes, data: bytes, tag: bytes) -> None: ...
def update(self, data: bytes) -> None: ...
def finalize(self) -> bytes: ...
def verify(self, tag: bytes) -> None: ...

View File

@@ -0,0 +1,55 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
import typing
from cryptography.hazmat.primitives.asymmetric import rsa
class RSAPrivateKey: ...
class RSAPublicKey: ...
class RSAPrivateNumbers:
def __init__(
self,
p: int,
q: int,
d: int,
dmp1: int,
dmq1: int,
iqmp: int,
public_numbers: RSAPublicNumbers,
) -> None: ...
@property
def p(self) -> int: ...
@property
def q(self) -> int: ...
@property
def d(self) -> int: ...
@property
def dmp1(self) -> int: ...
@property
def dmq1(self) -> int: ...
@property
def iqmp(self) -> int: ...
@property
def public_numbers(self) -> RSAPublicNumbers: ...
def private_key(
self,
backend: typing.Any = None,
*,
unsafe_skip_rsa_key_validation: bool = False,
) -> rsa.RSAPrivateKey: ...
class RSAPublicNumbers:
def __init__(self, e: int, n: int) -> None: ...
@property
def n(self) -> int: ...
@property
def e(self) -> int: ...
def public_key(self, backend: typing.Any = None) -> rsa.RSAPublicKey: ...
def generate_private_key(
public_exponent: int,
key_size: int,
) -> rsa.RSAPrivateKey: ...

View File

@@ -0,0 +1,12 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from cryptography.hazmat.primitives.asymmetric import x25519
class X25519PrivateKey: ...
class X25519PublicKey: ...
def generate_key() -> x25519.X25519PrivateKey: ...
def from_private_bytes(data: bytes) -> x25519.X25519PrivateKey: ...
def from_public_bytes(data: bytes) -> x25519.X25519PublicKey: ...

View File

@@ -0,0 +1,12 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from cryptography.hazmat.primitives.asymmetric import x448
class X448PrivateKey: ...
class X448PublicKey: ...
def generate_key() -> x448.X448PrivateKey: ...
def from_private_bytes(data: bytes) -> x448.X448PrivateKey: ...
def from_public_bytes(data: bytes) -> x448.X448PublicKey: ...

View File

@@ -0,0 +1,21 @@
import typing
from cryptography import x509
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.serialization import pkcs7
def serialize_certificates(
certs: list[x509.Certificate],
encoding: serialization.Encoding,
) -> bytes: ...
def sign_and_serialize(
builder: pkcs7.PKCS7SignatureBuilder,
encoding: serialization.Encoding,
options: typing.Iterable[pkcs7.PKCS7Options],
) -> bytes: ...
def load_pem_pkcs7_certificates(
data: bytes,
) -> list[x509.Certificate]: ...
def load_der_pkcs7_certificates(
data: bytes,
) -> list[x509.Certificate]: ...

View File

@@ -0,0 +1,88 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
import datetime
import typing
from cryptography import x509
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric.padding import PSS, PKCS1v15
from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes
def load_pem_x509_certificate(
data: bytes, backend: typing.Any = None
) -> x509.Certificate: ...
def load_der_x509_certificate(
data: bytes, backend: typing.Any = None
) -> x509.Certificate: ...
def load_pem_x509_certificates(
data: bytes,
) -> list[x509.Certificate]: ...
def load_pem_x509_crl(
data: bytes, backend: typing.Any = None
) -> x509.CertificateRevocationList: ...
def load_der_x509_crl(
data: bytes, backend: typing.Any = None
) -> x509.CertificateRevocationList: ...
def load_pem_x509_csr(
data: bytes, backend: typing.Any = None
) -> x509.CertificateSigningRequest: ...
def load_der_x509_csr(
data: bytes, backend: typing.Any = None
) -> x509.CertificateSigningRequest: ...
def encode_name_bytes(name: x509.Name) -> bytes: ...
def encode_extension_value(extension: x509.ExtensionType) -> bytes: ...
def create_x509_certificate(
builder: x509.CertificateBuilder,
private_key: PrivateKeyTypes,
hash_algorithm: hashes.HashAlgorithm | None,
rsa_padding: PKCS1v15 | PSS | None,
) -> x509.Certificate: ...
def create_x509_csr(
builder: x509.CertificateSigningRequestBuilder,
private_key: PrivateKeyTypes,
hash_algorithm: hashes.HashAlgorithm | None,
rsa_padding: PKCS1v15 | PSS | None,
) -> x509.CertificateSigningRequest: ...
def create_x509_crl(
builder: x509.CertificateRevocationListBuilder,
private_key: PrivateKeyTypes,
hash_algorithm: hashes.HashAlgorithm | None,
rsa_padding: PKCS1v15 | PSS | None,
) -> x509.CertificateRevocationList: ...
class Sct: ...
class Certificate: ...
class RevokedCertificate: ...
class CertificateRevocationList: ...
class CertificateSigningRequest: ...
class PolicyBuilder:
def time(self, new_time: datetime.datetime) -> PolicyBuilder: ...
def store(self, new_store: Store) -> PolicyBuilder: ...
def max_chain_depth(self, new_max_chain_depth: int) -> PolicyBuilder: ...
def build_server_verifier(
self, subject: x509.verification.Subject
) -> ServerVerifier: ...
class ServerVerifier:
@property
def subject(self) -> x509.verification.Subject: ...
@property
def validation_time(self) -> datetime.datetime: ...
@property
def store(self) -> Store: ...
@property
def max_chain_depth(self) -> int: ...
def verify(
self,
leaf: x509.Certificate,
intermediates: list[x509.Certificate],
) -> list[x509.Certificate]: ...
class Store:
def __init__(self, certs: list[x509.Certificate]) -> None: ...
class VerificationError(Exception):
pass

View File

@@ -0,0 +1,3 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

View File

@@ -0,0 +1,226 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
def cryptography_has_set_cert_cb() -> list[str]:
return [
"SSL_CTX_set_cert_cb",
"SSL_set_cert_cb",
]
def cryptography_has_ssl_st() -> list[str]:
return [
"SSL_ST_BEFORE",
"SSL_ST_OK",
"SSL_ST_INIT",
"SSL_ST_RENEGOTIATE",
]
def cryptography_has_tls_st() -> list[str]:
return [
"TLS_ST_BEFORE",
"TLS_ST_OK",
]
def cryptography_has_ed448() -> list[str]:
return [
"EVP_PKEY_ED448",
]
def cryptography_has_ssl_sigalgs() -> list[str]:
return [
"SSL_CTX_set1_sigalgs_list",
]
def cryptography_has_psk() -> list[str]:
return [
"SSL_CTX_use_psk_identity_hint",
"SSL_CTX_set_psk_server_callback",
"SSL_CTX_set_psk_client_callback",
]
def cryptography_has_psk_tlsv13() -> list[str]:
return [
"SSL_CTX_set_psk_find_session_callback",
"SSL_CTX_set_psk_use_session_callback",
"Cryptography_SSL_SESSION_new",
"SSL_CIPHER_find",
"SSL_SESSION_set1_master_key",
"SSL_SESSION_set_cipher",
"SSL_SESSION_set_protocol_version",
]
def cryptography_has_custom_ext() -> list[str]:
return [
"SSL_CTX_add_client_custom_ext",
"SSL_CTX_add_server_custom_ext",
"SSL_extension_supported",
]
def cryptography_has_tlsv13_functions() -> list[str]:
return [
"SSL_VERIFY_POST_HANDSHAKE",
"SSL_CTX_set_ciphersuites",
"SSL_verify_client_post_handshake",
"SSL_CTX_set_post_handshake_auth",
"SSL_set_post_handshake_auth",
"SSL_SESSION_get_max_early_data",
"SSL_write_early_data",
"SSL_read_early_data",
"SSL_CTX_set_max_early_data",
]
def cryptography_has_engine() -> list[str]:
return [
"ENGINE_by_id",
"ENGINE_init",
"ENGINE_finish",
"ENGINE_get_default_RAND",
"ENGINE_set_default_RAND",
"ENGINE_unregister_RAND",
"ENGINE_ctrl_cmd",
"ENGINE_free",
"ENGINE_get_name",
"ENGINE_ctrl_cmd_string",
"ENGINE_load_builtin_engines",
"ENGINE_load_private_key",
"ENGINE_load_public_key",
"SSL_CTX_set_client_cert_engine",
]
def cryptography_has_verified_chain() -> list[str]:
return [
"SSL_get0_verified_chain",
]
def cryptography_has_srtp() -> list[str]:
return [
"SSL_CTX_set_tlsext_use_srtp",
"SSL_set_tlsext_use_srtp",
"SSL_get_selected_srtp_profile",
]
def cryptography_has_providers() -> list[str]:
return [
"OSSL_PROVIDER_load",
"OSSL_PROVIDER_unload",
"ERR_LIB_PROV",
"PROV_R_WRONG_FINAL_BLOCK_LENGTH",
"PROV_R_BAD_DECRYPT",
]
def cryptography_has_op_no_renegotiation() -> list[str]:
return [
"SSL_OP_NO_RENEGOTIATION",
]
def cryptography_has_dtls_get_data_mtu() -> list[str]:
return [
"DTLS_get_data_mtu",
]
def cryptography_has_300_fips() -> list[str]:
return [
"EVP_default_properties_enable_fips",
]
def cryptography_has_ssl_cookie() -> list[str]:
return [
"SSL_OP_COOKIE_EXCHANGE",
"DTLSv1_listen",
"SSL_CTX_set_cookie_generate_cb",
"SSL_CTX_set_cookie_verify_cb",
]
def cryptography_has_pkcs7_funcs() -> list[str]:
return [
"PKCS7_verify",
"SMIME_read_PKCS7",
]
def cryptography_has_prime_checks() -> list[str]:
return [
"BN_prime_checks_for_size",
]
def cryptography_has_300_evp_cipher() -> list[str]:
return ["EVP_CIPHER_fetch", "EVP_CIPHER_free"]
def cryptography_has_unexpected_eof_while_reading() -> list[str]:
return ["SSL_R_UNEXPECTED_EOF_WHILE_READING"]
def cryptography_has_pkcs12_set_mac() -> list[str]:
return ["PKCS12_set_mac"]
def cryptography_has_ssl_op_ignore_unexpected_eof() -> list[str]:
return [
"SSL_OP_IGNORE_UNEXPECTED_EOF",
]
def cryptography_has_get_extms_support() -> list[str]:
return ["SSL_get_extms_support"]
# This is a mapping of
# {condition: function-returning-names-dependent-on-that-condition} so we can
# loop over them and delete unsupported names at runtime. It will be removed
# when cffi supports #if in cdef. We use functions instead of just a dict of
# lists so we can use coverage to measure which are used.
CONDITIONAL_NAMES = {
"Cryptography_HAS_SET_CERT_CB": cryptography_has_set_cert_cb,
"Cryptography_HAS_SSL_ST": cryptography_has_ssl_st,
"Cryptography_HAS_TLS_ST": cryptography_has_tls_st,
"Cryptography_HAS_ED448": cryptography_has_ed448,
"Cryptography_HAS_SIGALGS": cryptography_has_ssl_sigalgs,
"Cryptography_HAS_PSK": cryptography_has_psk,
"Cryptography_HAS_PSK_TLSv1_3": cryptography_has_psk_tlsv13,
"Cryptography_HAS_CUSTOM_EXT": cryptography_has_custom_ext,
"Cryptography_HAS_TLSv1_3_FUNCTIONS": cryptography_has_tlsv13_functions,
"Cryptography_HAS_ENGINE": cryptography_has_engine,
"Cryptography_HAS_VERIFIED_CHAIN": cryptography_has_verified_chain,
"Cryptography_HAS_SRTP": cryptography_has_srtp,
"Cryptography_HAS_PROVIDERS": cryptography_has_providers,
"Cryptography_HAS_OP_NO_RENEGOTIATION": (
cryptography_has_op_no_renegotiation
),
"Cryptography_HAS_DTLS_GET_DATA_MTU": cryptography_has_dtls_get_data_mtu,
"Cryptography_HAS_300_FIPS": cryptography_has_300_fips,
"Cryptography_HAS_SSL_COOKIE": cryptography_has_ssl_cookie,
"Cryptography_HAS_PKCS7_FUNCS": cryptography_has_pkcs7_funcs,
"Cryptography_HAS_PRIME_CHECKS": cryptography_has_prime_checks,
"Cryptography_HAS_300_EVP_CIPHER": cryptography_has_300_evp_cipher,
"Cryptography_HAS_UNEXPECTED_EOF_WHILE_READING": (
cryptography_has_unexpected_eof_while_reading
),
"Cryptography_HAS_PKCS12_SET_MAC": cryptography_has_pkcs12_set_mac,
"Cryptography_HAS_SSL_OP_IGNORE_UNEXPECTED_EOF": (
cryptography_has_ssl_op_ignore_unexpected_eof
),
"Cryptography_HAS_GET_EXTMS_SUPPORT": cryptography_has_get_extms_support,
}

View File

@@ -0,0 +1,144 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
import os
import sys
import threading
import types
import typing
import warnings
import cryptography
from cryptography.exceptions import InternalError
from cryptography.hazmat.bindings._rust import _openssl, openssl
from cryptography.hazmat.bindings.openssl._conditional import CONDITIONAL_NAMES
def _openssl_assert(
ok: bool,
errors: list[openssl.OpenSSLError] | None = None,
) -> None:
if not ok:
if errors is None:
errors = openssl.capture_error_stack()
raise InternalError(
"Unknown OpenSSL error. This error is commonly encountered when "
"another library is not cleaning up the OpenSSL error stack. If "
"you are using cryptography with another library that uses "
"OpenSSL try disabling it before reporting a bug. Otherwise "
"please file an issue at https://github.com/pyca/cryptography/"
"issues with information on how to reproduce "
f"this. ({errors!r})",
errors,
)
def build_conditional_library(
lib: typing.Any,
conditional_names: dict[str, typing.Callable[[], list[str]]],
) -> typing.Any:
conditional_lib = types.ModuleType("lib")
conditional_lib._original_lib = lib # type: ignore[attr-defined]
excluded_names = set()
for condition, names_cb in conditional_names.items():
if not getattr(lib, condition):
excluded_names.update(names_cb())
for attr in dir(lib):
if attr not in excluded_names:
setattr(conditional_lib, attr, getattr(lib, attr))
return conditional_lib
class Binding:
"""
OpenSSL API wrapper.
"""
lib: typing.ClassVar = None
ffi = _openssl.ffi
_lib_loaded = False
_init_lock = threading.Lock()
_legacy_provider: typing.Any = ffi.NULL
_default_provider: typing.Any = ffi.NULL
def __init__(self) -> None:
self._ensure_ffi_initialized()
def _enable_fips(self) -> None:
# This function enables FIPS mode for OpenSSL 3.0.0 on installs that
# have the FIPS provider installed properly.
_openssl_assert(self.lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER)
self._base_provider = self.lib.OSSL_PROVIDER_load(
self.ffi.NULL, b"base"
)
_openssl_assert(self._base_provider != self.ffi.NULL)
self.lib._fips_provider = self.lib.OSSL_PROVIDER_load(
self.ffi.NULL, b"fips"
)
_openssl_assert(self.lib._fips_provider != self.ffi.NULL)
res = self.lib.EVP_default_properties_enable_fips(self.ffi.NULL, 1)
_openssl_assert(res == 1)
@classmethod
def _ensure_ffi_initialized(cls) -> None:
with cls._init_lock:
if not cls._lib_loaded:
cls.lib = build_conditional_library(
_openssl.lib, CONDITIONAL_NAMES
)
cls._lib_loaded = True
@classmethod
def init_static_locks(cls) -> None:
cls._ensure_ffi_initialized()
def _verify_package_version(version: str) -> None:
# Occasionally we run into situations where the version of the Python
# package does not match the version of the shared object that is loaded.
# This may occur in environments where multiple versions of cryptography
# are installed and available in the python path. To avoid errors cropping
# up later this code checks that the currently imported package and the
# shared object that were loaded have the same version and raise an
# ImportError if they do not
so_package_version = _openssl.ffi.string(
_openssl.lib.CRYPTOGRAPHY_PACKAGE_VERSION
)
if version.encode("ascii") != so_package_version:
raise ImportError(
"The version of cryptography does not match the loaded "
"shared object. This can happen if you have multiple copies of "
"cryptography installed in your Python path. Please try creating "
"a new virtual environment to resolve this issue. "
"Loaded python version: {}, shared object version: {}".format(
version, so_package_version
)
)
_openssl_assert(
_openssl.lib.OpenSSL_version_num() == openssl.openssl_version(),
)
_verify_package_version(cryptography.__version__)
Binding.init_static_locks()
if (
sys.platform == "win32"
and os.environ.get("PROCESSOR_ARCHITEW6432") is not None
):
warnings.warn(
"You are using cryptography on a 32-bit Python on a 64-bit Windows "
"Operating System. Cryptography will be significantly faster if you "
"switch to using a 64-bit Python.",
UserWarning,
stacklevel=2,
)

View File

@@ -0,0 +1,3 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

View File

@@ -0,0 +1,19 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
import abc
# This exists to break an import cycle. It is normally accessible from the
# asymmetric padding module.
class AsymmetricPadding(metaclass=abc.ABCMeta):
@property
@abc.abstractmethod
def name(self) -> str:
"""
A string naming this padding (e.g. "PSS", "PKCS1").
"""

View File

@@ -0,0 +1,44 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
import abc
# This exists to break an import cycle. It is normally accessible from the
# ciphers module.
class CipherAlgorithm(metaclass=abc.ABCMeta):
@property
@abc.abstractmethod
def name(self) -> str:
"""
A string naming this mode (e.g. "AES", "Camellia").
"""
@property
@abc.abstractmethod
def key_sizes(self) -> frozenset[int]:
"""
Valid key sizes for this algorithm in bits
"""
@property
@abc.abstractmethod
def key_size(self) -> int:
"""
The size of the key being used as an integer in bits (e.g. 128, 256).
"""
class BlockCipherAlgorithm(CipherAlgorithm):
key: bytes
@property
@abc.abstractmethod
def block_size(self) -> int:
"""
The size of a block as an integer in bits (e.g. 64, 128).
"""

View File

@@ -0,0 +1,169 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
import abc
from cryptography import utils
from cryptography.hazmat.primitives.hashes import HashAlgorithm
# This exists to break an import cycle. These classes are normally accessible
# from the serialization module.
class PBES(utils.Enum):
PBESv1SHA1And3KeyTripleDESCBC = "PBESv1 using SHA1 and 3-Key TripleDES"
PBESv2SHA256AndAES256CBC = "PBESv2 using SHA256 PBKDF2 and AES256 CBC"
class Encoding(utils.Enum):
PEM = "PEM"
DER = "DER"
OpenSSH = "OpenSSH"
Raw = "Raw"
X962 = "ANSI X9.62"
SMIME = "S/MIME"
class PrivateFormat(utils.Enum):
PKCS8 = "PKCS8"
TraditionalOpenSSL = "TraditionalOpenSSL"
Raw = "Raw"
OpenSSH = "OpenSSH"
PKCS12 = "PKCS12"
def encryption_builder(self) -> KeySerializationEncryptionBuilder:
if self not in (PrivateFormat.OpenSSH, PrivateFormat.PKCS12):
raise ValueError(
"encryption_builder only supported with PrivateFormat.OpenSSH"
" and PrivateFormat.PKCS12"
)
return KeySerializationEncryptionBuilder(self)
class PublicFormat(utils.Enum):
SubjectPublicKeyInfo = "X.509 subjectPublicKeyInfo with PKCS#1"
PKCS1 = "Raw PKCS#1"
OpenSSH = "OpenSSH"
Raw = "Raw"
CompressedPoint = "X9.62 Compressed Point"
UncompressedPoint = "X9.62 Uncompressed Point"
class ParameterFormat(utils.Enum):
PKCS3 = "PKCS3"
class KeySerializationEncryption(metaclass=abc.ABCMeta):
pass
class BestAvailableEncryption(KeySerializationEncryption):
def __init__(self, password: bytes):
if not isinstance(password, bytes) or len(password) == 0:
raise ValueError("Password must be 1 or more bytes.")
self.password = password
class NoEncryption(KeySerializationEncryption):
pass
class KeySerializationEncryptionBuilder:
def __init__(
self,
format: PrivateFormat,
*,
_kdf_rounds: int | None = None,
_hmac_hash: HashAlgorithm | None = None,
_key_cert_algorithm: PBES | None = None,
) -> None:
self._format = format
self._kdf_rounds = _kdf_rounds
self._hmac_hash = _hmac_hash
self._key_cert_algorithm = _key_cert_algorithm
def kdf_rounds(self, rounds: int) -> KeySerializationEncryptionBuilder:
if self._kdf_rounds is not None:
raise ValueError("kdf_rounds already set")
if not isinstance(rounds, int):
raise TypeError("kdf_rounds must be an integer")
if rounds < 1:
raise ValueError("kdf_rounds must be a positive integer")
return KeySerializationEncryptionBuilder(
self._format,
_kdf_rounds=rounds,
_hmac_hash=self._hmac_hash,
_key_cert_algorithm=self._key_cert_algorithm,
)
def hmac_hash(
self, algorithm: HashAlgorithm
) -> KeySerializationEncryptionBuilder:
if self._format is not PrivateFormat.PKCS12:
raise TypeError(
"hmac_hash only supported with PrivateFormat.PKCS12"
)
if self._hmac_hash is not None:
raise ValueError("hmac_hash already set")
return KeySerializationEncryptionBuilder(
self._format,
_kdf_rounds=self._kdf_rounds,
_hmac_hash=algorithm,
_key_cert_algorithm=self._key_cert_algorithm,
)
def key_cert_algorithm(
self, algorithm: PBES
) -> KeySerializationEncryptionBuilder:
if self._format is not PrivateFormat.PKCS12:
raise TypeError(
"key_cert_algorithm only supported with "
"PrivateFormat.PKCS12"
)
if self._key_cert_algorithm is not None:
raise ValueError("key_cert_algorithm already set")
return KeySerializationEncryptionBuilder(
self._format,
_kdf_rounds=self._kdf_rounds,
_hmac_hash=self._hmac_hash,
_key_cert_algorithm=algorithm,
)
def build(self, password: bytes) -> KeySerializationEncryption:
if not isinstance(password, bytes) or len(password) == 0:
raise ValueError("Password must be 1 or more bytes.")
return _KeySerializationEncryption(
self._format,
password,
kdf_rounds=self._kdf_rounds,
hmac_hash=self._hmac_hash,
key_cert_algorithm=self._key_cert_algorithm,
)
class _KeySerializationEncryption(KeySerializationEncryption):
def __init__(
self,
format: PrivateFormat,
password: bytes,
*,
kdf_rounds: int | None,
hmac_hash: HashAlgorithm | None,
key_cert_algorithm: PBES | None,
):
self._format = format
self.password = password
self._kdf_rounds = kdf_rounds
self._hmac_hash = hmac_hash
self._key_cert_algorithm = key_cert_algorithm

View File

@@ -0,0 +1,3 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

View File

@@ -0,0 +1,135 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
import abc
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
from cryptography.hazmat.primitives import _serialization
generate_parameters = rust_openssl.dh.generate_parameters
DHPrivateNumbers = rust_openssl.dh.DHPrivateNumbers
DHPublicNumbers = rust_openssl.dh.DHPublicNumbers
DHParameterNumbers = rust_openssl.dh.DHParameterNumbers
class DHParameters(metaclass=abc.ABCMeta):
@abc.abstractmethod
def generate_private_key(self) -> DHPrivateKey:
"""
Generates and returns a DHPrivateKey.
"""
@abc.abstractmethod
def parameter_bytes(
self,
encoding: _serialization.Encoding,
format: _serialization.ParameterFormat,
) -> bytes:
"""
Returns the parameters serialized as bytes.
"""
@abc.abstractmethod
def parameter_numbers(self) -> DHParameterNumbers:
"""
Returns a DHParameterNumbers.
"""
DHParametersWithSerialization = DHParameters
DHParameters.register(rust_openssl.dh.DHParameters)
class DHPublicKey(metaclass=abc.ABCMeta):
@property
@abc.abstractmethod
def key_size(self) -> int:
"""
The bit length of the prime modulus.
"""
@abc.abstractmethod
def parameters(self) -> DHParameters:
"""
The DHParameters object associated with this public key.
"""
@abc.abstractmethod
def public_numbers(self) -> DHPublicNumbers:
"""
Returns a DHPublicNumbers.
"""
@abc.abstractmethod
def public_bytes(
self,
encoding: _serialization.Encoding,
format: _serialization.PublicFormat,
) -> bytes:
"""
Returns the key serialized as bytes.
"""
@abc.abstractmethod
def __eq__(self, other: object) -> bool:
"""
Checks equality.
"""
DHPublicKeyWithSerialization = DHPublicKey
DHPublicKey.register(rust_openssl.dh.DHPublicKey)
class DHPrivateKey(metaclass=abc.ABCMeta):
@property
@abc.abstractmethod
def key_size(self) -> int:
"""
The bit length of the prime modulus.
"""
@abc.abstractmethod
def public_key(self) -> DHPublicKey:
"""
The DHPublicKey associated with this private key.
"""
@abc.abstractmethod
def parameters(self) -> DHParameters:
"""
The DHParameters object associated with this private key.
"""
@abc.abstractmethod
def exchange(self, peer_public_key: DHPublicKey) -> bytes:
"""
Given peer's DHPublicKey, carry out the key exchange and
return shared key as bytes.
"""
@abc.abstractmethod
def private_numbers(self) -> DHPrivateNumbers:
"""
Returns a DHPrivateNumbers.
"""
@abc.abstractmethod
def private_bytes(
self,
encoding: _serialization.Encoding,
format: _serialization.PrivateFormat,
encryption_algorithm: _serialization.KeySerializationEncryption,
) -> bytes:
"""
Returns the key serialized as bytes.
"""
DHPrivateKeyWithSerialization = DHPrivateKey
DHPrivateKey.register(rust_openssl.dh.DHPrivateKey)

View File

@@ -0,0 +1,154 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
import abc
import typing
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
from cryptography.hazmat.primitives import _serialization, hashes
from cryptography.hazmat.primitives.asymmetric import utils as asym_utils
class DSAParameters(metaclass=abc.ABCMeta):
@abc.abstractmethod
def generate_private_key(self) -> DSAPrivateKey:
"""
Generates and returns a DSAPrivateKey.
"""
@abc.abstractmethod
def parameter_numbers(self) -> DSAParameterNumbers:
"""
Returns a DSAParameterNumbers.
"""
DSAParametersWithNumbers = DSAParameters
DSAParameters.register(rust_openssl.dsa.DSAParameters)
class DSAPrivateKey(metaclass=abc.ABCMeta):
@property
@abc.abstractmethod
def key_size(self) -> int:
"""
The bit length of the prime modulus.
"""
@abc.abstractmethod
def public_key(self) -> DSAPublicKey:
"""
The DSAPublicKey associated with this private key.
"""
@abc.abstractmethod
def parameters(self) -> DSAParameters:
"""
The DSAParameters object associated with this private key.
"""
@abc.abstractmethod
def sign(
self,
data: bytes,
algorithm: asym_utils.Prehashed | hashes.HashAlgorithm,
) -> bytes:
"""
Signs the data
"""
@abc.abstractmethod
def private_numbers(self) -> DSAPrivateNumbers:
"""
Returns a DSAPrivateNumbers.
"""
@abc.abstractmethod
def private_bytes(
self,
encoding: _serialization.Encoding,
format: _serialization.PrivateFormat,
encryption_algorithm: _serialization.KeySerializationEncryption,
) -> bytes:
"""
Returns the key serialized as bytes.
"""
DSAPrivateKeyWithSerialization = DSAPrivateKey
DSAPrivateKey.register(rust_openssl.dsa.DSAPrivateKey)
class DSAPublicKey(metaclass=abc.ABCMeta):
@property
@abc.abstractmethod
def key_size(self) -> int:
"""
The bit length of the prime modulus.
"""
@abc.abstractmethod
def parameters(self) -> DSAParameters:
"""
The DSAParameters object associated with this public key.
"""
@abc.abstractmethod
def public_numbers(self) -> DSAPublicNumbers:
"""
Returns a DSAPublicNumbers.
"""
@abc.abstractmethod
def public_bytes(
self,
encoding: _serialization.Encoding,
format: _serialization.PublicFormat,
) -> bytes:
"""
Returns the key serialized as bytes.
"""
@abc.abstractmethod
def verify(
self,
signature: bytes,
data: bytes,
algorithm: asym_utils.Prehashed | hashes.HashAlgorithm,
) -> None:
"""
Verifies the signature of the data.
"""
@abc.abstractmethod
def __eq__(self, other: object) -> bool:
"""
Checks equality.
"""
DSAPublicKeyWithSerialization = DSAPublicKey
DSAPublicKey.register(rust_openssl.dsa.DSAPublicKey)
DSAPrivateNumbers = rust_openssl.dsa.DSAPrivateNumbers
DSAPublicNumbers = rust_openssl.dsa.DSAPublicNumbers
DSAParameterNumbers = rust_openssl.dsa.DSAParameterNumbers
def generate_parameters(
key_size: int, backend: typing.Any = None
) -> DSAParameters:
if key_size not in (1024, 2048, 3072, 4096):
raise ValueError("Key size must be 1024, 2048, 3072, or 4096 bits.")
return rust_openssl.dsa.generate_parameters(key_size)
def generate_private_key(
key_size: int, backend: typing.Any = None
) -> DSAPrivateKey:
parameters = generate_parameters(key_size)
return parameters.generate_private_key()

View File

@@ -0,0 +1,383 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
import abc
import typing
from cryptography import utils
from cryptography.hazmat._oid import ObjectIdentifier
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
from cryptography.hazmat.primitives import _serialization, hashes
from cryptography.hazmat.primitives.asymmetric import utils as asym_utils
class EllipticCurveOID:
SECP192R1 = ObjectIdentifier("1.2.840.10045.3.1.1")
SECP224R1 = ObjectIdentifier("1.3.132.0.33")
SECP256K1 = ObjectIdentifier("1.3.132.0.10")
SECP256R1 = ObjectIdentifier("1.2.840.10045.3.1.7")
SECP384R1 = ObjectIdentifier("1.3.132.0.34")
SECP521R1 = ObjectIdentifier("1.3.132.0.35")
BRAINPOOLP256R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.7")
BRAINPOOLP384R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.11")
BRAINPOOLP512R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.13")
SECT163K1 = ObjectIdentifier("1.3.132.0.1")
SECT163R2 = ObjectIdentifier("1.3.132.0.15")
SECT233K1 = ObjectIdentifier("1.3.132.0.26")
SECT233R1 = ObjectIdentifier("1.3.132.0.27")
SECT283K1 = ObjectIdentifier("1.3.132.0.16")
SECT283R1 = ObjectIdentifier("1.3.132.0.17")
SECT409K1 = ObjectIdentifier("1.3.132.0.36")
SECT409R1 = ObjectIdentifier("1.3.132.0.37")
SECT571K1 = ObjectIdentifier("1.3.132.0.38")
SECT571R1 = ObjectIdentifier("1.3.132.0.39")
class EllipticCurve(metaclass=abc.ABCMeta):
@property
@abc.abstractmethod
def name(self) -> str:
"""
The name of the curve. e.g. secp256r1.
"""
@property
@abc.abstractmethod
def key_size(self) -> int:
"""
Bit size of a secret scalar for the curve.
"""
class EllipticCurveSignatureAlgorithm(metaclass=abc.ABCMeta):
@property
@abc.abstractmethod
def algorithm(
self,
) -> asym_utils.Prehashed | hashes.HashAlgorithm:
"""
The digest algorithm used with this signature.
"""
class EllipticCurvePrivateKey(metaclass=abc.ABCMeta):
@abc.abstractmethod
def exchange(
self, algorithm: ECDH, peer_public_key: EllipticCurvePublicKey
) -> bytes:
"""
Performs a key exchange operation using the provided algorithm with the
provided peer's public key.
"""
@abc.abstractmethod
def public_key(self) -> EllipticCurvePublicKey:
"""
The EllipticCurvePublicKey for this private key.
"""
@property
@abc.abstractmethod
def curve(self) -> EllipticCurve:
"""
The EllipticCurve that this key is on.
"""
@property
@abc.abstractmethod
def key_size(self) -> int:
"""
Bit size of a secret scalar for the curve.
"""
@abc.abstractmethod
def sign(
self,
data: bytes,
signature_algorithm: EllipticCurveSignatureAlgorithm,
) -> bytes:
"""
Signs the data
"""
@abc.abstractmethod
def private_numbers(self) -> EllipticCurvePrivateNumbers:
"""
Returns an EllipticCurvePrivateNumbers.
"""
@abc.abstractmethod
def private_bytes(
self,
encoding: _serialization.Encoding,
format: _serialization.PrivateFormat,
encryption_algorithm: _serialization.KeySerializationEncryption,
) -> bytes:
"""
Returns the key serialized as bytes.
"""
EllipticCurvePrivateKeyWithSerialization = EllipticCurvePrivateKey
EllipticCurvePrivateKey.register(rust_openssl.ec.ECPrivateKey)
class EllipticCurvePublicKey(metaclass=abc.ABCMeta):
@property
@abc.abstractmethod
def curve(self) -> EllipticCurve:
"""
The EllipticCurve that this key is on.
"""
@property
@abc.abstractmethod
def key_size(self) -> int:
"""
Bit size of a secret scalar for the curve.
"""
@abc.abstractmethod
def public_numbers(self) -> EllipticCurvePublicNumbers:
"""
Returns an EllipticCurvePublicNumbers.
"""
@abc.abstractmethod
def public_bytes(
self,
encoding: _serialization.Encoding,
format: _serialization.PublicFormat,
) -> bytes:
"""
Returns the key serialized as bytes.
"""
@abc.abstractmethod
def verify(
self,
signature: bytes,
data: bytes,
signature_algorithm: EllipticCurveSignatureAlgorithm,
) -> None:
"""
Verifies the signature of the data.
"""
@classmethod
def from_encoded_point(
cls, curve: EllipticCurve, data: bytes
) -> EllipticCurvePublicKey:
utils._check_bytes("data", data)
if len(data) == 0:
raise ValueError("data must not be an empty byte string")
if data[0] not in [0x02, 0x03, 0x04]:
raise ValueError("Unsupported elliptic curve point type")
return rust_openssl.ec.from_public_bytes(curve, data)
@abc.abstractmethod
def __eq__(self, other: object) -> bool:
"""
Checks equality.
"""
EllipticCurvePublicKeyWithSerialization = EllipticCurvePublicKey
EllipticCurvePublicKey.register(rust_openssl.ec.ECPublicKey)
EllipticCurvePrivateNumbers = rust_openssl.ec.EllipticCurvePrivateNumbers
EllipticCurvePublicNumbers = rust_openssl.ec.EllipticCurvePublicNumbers
class SECT571R1(EllipticCurve):
name = "sect571r1"
key_size = 570
class SECT409R1(EllipticCurve):
name = "sect409r1"
key_size = 409
class SECT283R1(EllipticCurve):
name = "sect283r1"
key_size = 283
class SECT233R1(EllipticCurve):
name = "sect233r1"
key_size = 233
class SECT163R2(EllipticCurve):
name = "sect163r2"
key_size = 163
class SECT571K1(EllipticCurve):
name = "sect571k1"
key_size = 571
class SECT409K1(EllipticCurve):
name = "sect409k1"
key_size = 409
class SECT283K1(EllipticCurve):
name = "sect283k1"
key_size = 283
class SECT233K1(EllipticCurve):
name = "sect233k1"
key_size = 233
class SECT163K1(EllipticCurve):
name = "sect163k1"
key_size = 163
class SECP521R1(EllipticCurve):
name = "secp521r1"
key_size = 521
class SECP384R1(EllipticCurve):
name = "secp384r1"
key_size = 384
class SECP256R1(EllipticCurve):
name = "secp256r1"
key_size = 256
class SECP256K1(EllipticCurve):
name = "secp256k1"
key_size = 256
class SECP224R1(EllipticCurve):
name = "secp224r1"
key_size = 224
class SECP192R1(EllipticCurve):
name = "secp192r1"
key_size = 192
class BrainpoolP256R1(EllipticCurve):
name = "brainpoolP256r1"
key_size = 256
class BrainpoolP384R1(EllipticCurve):
name = "brainpoolP384r1"
key_size = 384
class BrainpoolP512R1(EllipticCurve):
name = "brainpoolP512r1"
key_size = 512
_CURVE_TYPES: dict[str, EllipticCurve] = {
"prime192v1": SECP192R1(),
"prime256v1": SECP256R1(),
"secp192r1": SECP192R1(),
"secp224r1": SECP224R1(),
"secp256r1": SECP256R1(),
"secp384r1": SECP384R1(),
"secp521r1": SECP521R1(),
"secp256k1": SECP256K1(),
"sect163k1": SECT163K1(),
"sect233k1": SECT233K1(),
"sect283k1": SECT283K1(),
"sect409k1": SECT409K1(),
"sect571k1": SECT571K1(),
"sect163r2": SECT163R2(),
"sect233r1": SECT233R1(),
"sect283r1": SECT283R1(),
"sect409r1": SECT409R1(),
"sect571r1": SECT571R1(),
"brainpoolP256r1": BrainpoolP256R1(),
"brainpoolP384r1": BrainpoolP384R1(),
"brainpoolP512r1": BrainpoolP512R1(),
}
class ECDSA(EllipticCurveSignatureAlgorithm):
def __init__(
self,
algorithm: asym_utils.Prehashed | hashes.HashAlgorithm,
):
self._algorithm = algorithm
@property
def algorithm(
self,
) -> asym_utils.Prehashed | hashes.HashAlgorithm:
return self._algorithm
generate_private_key = rust_openssl.ec.generate_private_key
def derive_private_key(
private_value: int,
curve: EllipticCurve,
backend: typing.Any = None,
) -> EllipticCurvePrivateKey:
if not isinstance(private_value, int):
raise TypeError("private_value must be an integer type.")
if private_value <= 0:
raise ValueError("private_value must be a positive integer.")
return rust_openssl.ec.derive_private_key(private_value, curve)
class ECDH:
pass
_OID_TO_CURVE = {
EllipticCurveOID.SECP192R1: SECP192R1,
EllipticCurveOID.SECP224R1: SECP224R1,
EllipticCurveOID.SECP256K1: SECP256K1,
EllipticCurveOID.SECP256R1: SECP256R1,
EllipticCurveOID.SECP384R1: SECP384R1,
EllipticCurveOID.SECP521R1: SECP521R1,
EllipticCurveOID.BRAINPOOLP256R1: BrainpoolP256R1,
EllipticCurveOID.BRAINPOOLP384R1: BrainpoolP384R1,
EllipticCurveOID.BRAINPOOLP512R1: BrainpoolP512R1,
EllipticCurveOID.SECT163K1: SECT163K1,
EllipticCurveOID.SECT163R2: SECT163R2,
EllipticCurveOID.SECT233K1: SECT233K1,
EllipticCurveOID.SECT233R1: SECT233R1,
EllipticCurveOID.SECT283K1: SECT283K1,
EllipticCurveOID.SECT283R1: SECT283R1,
EllipticCurveOID.SECT409K1: SECT409K1,
EllipticCurveOID.SECT409R1: SECT409R1,
EllipticCurveOID.SECT571K1: SECT571K1,
EllipticCurveOID.SECT571R1: SECT571R1,
}
def get_curve_for_oid(oid: ObjectIdentifier) -> type[EllipticCurve]:
try:
return _OID_TO_CURVE[oid]
except KeyError:
raise LookupError(
"The provided object identifier has no matching elliptic "
"curve class"
)

View File

@@ -0,0 +1,116 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
import abc
from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
from cryptography.hazmat.primitives import _serialization
class Ed25519PublicKey(metaclass=abc.ABCMeta):
@classmethod
def from_public_bytes(cls, data: bytes) -> Ed25519PublicKey:
from cryptography.hazmat.backends.openssl.backend import backend
if not backend.ed25519_supported():
raise UnsupportedAlgorithm(
"ed25519 is not supported by this version of OpenSSL.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
)
return rust_openssl.ed25519.from_public_bytes(data)
@abc.abstractmethod
def public_bytes(
self,
encoding: _serialization.Encoding,
format: _serialization.PublicFormat,
) -> bytes:
"""
The serialized bytes of the public key.
"""
@abc.abstractmethod
def public_bytes_raw(self) -> bytes:
"""
The raw bytes of the public key.
Equivalent to public_bytes(Raw, Raw).
"""
@abc.abstractmethod
def verify(self, signature: bytes, data: bytes) -> None:
"""
Verify the signature.
"""
@abc.abstractmethod
def __eq__(self, other: object) -> bool:
"""
Checks equality.
"""
Ed25519PublicKey.register(rust_openssl.ed25519.Ed25519PublicKey)
class Ed25519PrivateKey(metaclass=abc.ABCMeta):
@classmethod
def generate(cls) -> Ed25519PrivateKey:
from cryptography.hazmat.backends.openssl.backend import backend
if not backend.ed25519_supported():
raise UnsupportedAlgorithm(
"ed25519 is not supported by this version of OpenSSL.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
)
return rust_openssl.ed25519.generate_key()
@classmethod
def from_private_bytes(cls, data: bytes) -> Ed25519PrivateKey:
from cryptography.hazmat.backends.openssl.backend import backend
if not backend.ed25519_supported():
raise UnsupportedAlgorithm(
"ed25519 is not supported by this version of OpenSSL.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
)
return rust_openssl.ed25519.from_private_bytes(data)
@abc.abstractmethod
def public_key(self) -> Ed25519PublicKey:
"""
The Ed25519PublicKey derived from the private key.
"""
@abc.abstractmethod
def private_bytes(
self,
encoding: _serialization.Encoding,
format: _serialization.PrivateFormat,
encryption_algorithm: _serialization.KeySerializationEncryption,
) -> bytes:
"""
The serialized bytes of the private key.
"""
@abc.abstractmethod
def private_bytes_raw(self) -> bytes:
"""
The raw bytes of the private key.
Equivalent to private_bytes(Raw, Raw, NoEncryption()).
"""
@abc.abstractmethod
def sign(self, data: bytes) -> bytes:
"""
Signs the data.
"""
Ed25519PrivateKey.register(rust_openssl.ed25519.Ed25519PrivateKey)

View File

@@ -0,0 +1,118 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
import abc
from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
from cryptography.hazmat.primitives import _serialization
class Ed448PublicKey(metaclass=abc.ABCMeta):
@classmethod
def from_public_bytes(cls, data: bytes) -> Ed448PublicKey:
from cryptography.hazmat.backends.openssl.backend import backend
if not backend.ed448_supported():
raise UnsupportedAlgorithm(
"ed448 is not supported by this version of OpenSSL.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
)
return rust_openssl.ed448.from_public_bytes(data)
@abc.abstractmethod
def public_bytes(
self,
encoding: _serialization.Encoding,
format: _serialization.PublicFormat,
) -> bytes:
"""
The serialized bytes of the public key.
"""
@abc.abstractmethod
def public_bytes_raw(self) -> bytes:
"""
The raw bytes of the public key.
Equivalent to public_bytes(Raw, Raw).
"""
@abc.abstractmethod
def verify(self, signature: bytes, data: bytes) -> None:
"""
Verify the signature.
"""
@abc.abstractmethod
def __eq__(self, other: object) -> bool:
"""
Checks equality.
"""
if hasattr(rust_openssl, "ed448"):
Ed448PublicKey.register(rust_openssl.ed448.Ed448PublicKey)
class Ed448PrivateKey(metaclass=abc.ABCMeta):
@classmethod
def generate(cls) -> Ed448PrivateKey:
from cryptography.hazmat.backends.openssl.backend import backend
if not backend.ed448_supported():
raise UnsupportedAlgorithm(
"ed448 is not supported by this version of OpenSSL.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
)
return rust_openssl.ed448.generate_key()
@classmethod
def from_private_bytes(cls, data: bytes) -> Ed448PrivateKey:
from cryptography.hazmat.backends.openssl.backend import backend
if not backend.ed448_supported():
raise UnsupportedAlgorithm(
"ed448 is not supported by this version of OpenSSL.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
)
return rust_openssl.ed448.from_private_bytes(data)
@abc.abstractmethod
def public_key(self) -> Ed448PublicKey:
"""
The Ed448PublicKey derived from the private key.
"""
@abc.abstractmethod
def sign(self, data: bytes) -> bytes:
"""
Signs the data.
"""
@abc.abstractmethod
def private_bytes(
self,
encoding: _serialization.Encoding,
format: _serialization.PrivateFormat,
encryption_algorithm: _serialization.KeySerializationEncryption,
) -> bytes:
"""
The serialized bytes of the private key.
"""
@abc.abstractmethod
def private_bytes_raw(self) -> bytes:
"""
The raw bytes of the private key.
Equivalent to private_bytes(Raw, Raw, NoEncryption()).
"""
if hasattr(rust_openssl, "x448"):
Ed448PrivateKey.register(rust_openssl.ed448.Ed448PrivateKey)

View File

@@ -0,0 +1,113 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
import abc
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives._asymmetric import (
AsymmetricPadding as AsymmetricPadding,
)
from cryptography.hazmat.primitives.asymmetric import rsa
class PKCS1v15(AsymmetricPadding):
name = "EMSA-PKCS1-v1_5"
class _MaxLength:
"Sentinel value for `MAX_LENGTH`."
class _Auto:
"Sentinel value for `AUTO`."
class _DigestLength:
"Sentinel value for `DIGEST_LENGTH`."
class PSS(AsymmetricPadding):
MAX_LENGTH = _MaxLength()
AUTO = _Auto()
DIGEST_LENGTH = _DigestLength()
name = "EMSA-PSS"
_salt_length: int | _MaxLength | _Auto | _DigestLength
def __init__(
self,
mgf: MGF,
salt_length: int | _MaxLength | _Auto | _DigestLength,
) -> None:
self._mgf = mgf
if not isinstance(
salt_length, (int, _MaxLength, _Auto, _DigestLength)
):
raise TypeError(
"salt_length must be an integer, MAX_LENGTH, "
"DIGEST_LENGTH, or AUTO"
)
if isinstance(salt_length, int) and salt_length < 0:
raise ValueError("salt_length must be zero or greater.")
self._salt_length = salt_length
@property
def mgf(self) -> MGF:
return self._mgf
class OAEP(AsymmetricPadding):
name = "EME-OAEP"
def __init__(
self,
mgf: MGF,
algorithm: hashes.HashAlgorithm,
label: bytes | None,
):
if not isinstance(algorithm, hashes.HashAlgorithm):
raise TypeError("Expected instance of hashes.HashAlgorithm.")
self._mgf = mgf
self._algorithm = algorithm
self._label = label
@property
def algorithm(self) -> hashes.HashAlgorithm:
return self._algorithm
@property
def mgf(self) -> MGF:
return self._mgf
class MGF(metaclass=abc.ABCMeta):
_algorithm: hashes.HashAlgorithm
class MGF1(MGF):
MAX_LENGTH = _MaxLength()
def __init__(self, algorithm: hashes.HashAlgorithm):
if not isinstance(algorithm, hashes.HashAlgorithm):
raise TypeError("Expected instance of hashes.HashAlgorithm.")
self._algorithm = algorithm
def calculate_max_pss_salt_length(
key: rsa.RSAPrivateKey | rsa.RSAPublicKey,
hash_algorithm: hashes.HashAlgorithm,
) -> int:
if not isinstance(key, (rsa.RSAPrivateKey, rsa.RSAPublicKey)):
raise TypeError("key must be an RSA public or private key")
# bit length - 1 per RFC 3447
emlen = (key.key_size + 6) // 8
salt_length = emlen - hash_algorithm.digest_size - 2
assert salt_length >= 0
return salt_length

View File

@@ -0,0 +1,239 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
import abc
import typing
from math import gcd
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
from cryptography.hazmat.primitives import _serialization, hashes
from cryptography.hazmat.primitives._asymmetric import AsymmetricPadding
from cryptography.hazmat.primitives.asymmetric import utils as asym_utils
class RSAPrivateKey(metaclass=abc.ABCMeta):
@abc.abstractmethod
def decrypt(self, ciphertext: bytes, padding: AsymmetricPadding) -> bytes:
"""
Decrypts the provided ciphertext.
"""
@property
@abc.abstractmethod
def key_size(self) -> int:
"""
The bit length of the public modulus.
"""
@abc.abstractmethod
def public_key(self) -> RSAPublicKey:
"""
The RSAPublicKey associated with this private key.
"""
@abc.abstractmethod
def sign(
self,
data: bytes,
padding: AsymmetricPadding,
algorithm: asym_utils.Prehashed | hashes.HashAlgorithm,
) -> bytes:
"""
Signs the data.
"""
@abc.abstractmethod
def private_numbers(self) -> RSAPrivateNumbers:
"""
Returns an RSAPrivateNumbers.
"""
@abc.abstractmethod
def private_bytes(
self,
encoding: _serialization.Encoding,
format: _serialization.PrivateFormat,
encryption_algorithm: _serialization.KeySerializationEncryption,
) -> bytes:
"""
Returns the key serialized as bytes.
"""
RSAPrivateKeyWithSerialization = RSAPrivateKey
RSAPrivateKey.register(rust_openssl.rsa.RSAPrivateKey)
class RSAPublicKey(metaclass=abc.ABCMeta):
@abc.abstractmethod
def encrypt(self, plaintext: bytes, padding: AsymmetricPadding) -> bytes:
"""
Encrypts the given plaintext.
"""
@property
@abc.abstractmethod
def key_size(self) -> int:
"""
The bit length of the public modulus.
"""
@abc.abstractmethod
def public_numbers(self) -> RSAPublicNumbers:
"""
Returns an RSAPublicNumbers
"""
@abc.abstractmethod
def public_bytes(
self,
encoding: _serialization.Encoding,
format: _serialization.PublicFormat,
) -> bytes:
"""
Returns the key serialized as bytes.
"""
@abc.abstractmethod
def verify(
self,
signature: bytes,
data: bytes,
padding: AsymmetricPadding,
algorithm: asym_utils.Prehashed | hashes.HashAlgorithm,
) -> None:
"""
Verifies the signature of the data.
"""
@abc.abstractmethod
def recover_data_from_signature(
self,
signature: bytes,
padding: AsymmetricPadding,
algorithm: hashes.HashAlgorithm | None,
) -> bytes:
"""
Recovers the original data from the signature.
"""
@abc.abstractmethod
def __eq__(self, other: object) -> bool:
"""
Checks equality.
"""
RSAPublicKeyWithSerialization = RSAPublicKey
RSAPublicKey.register(rust_openssl.rsa.RSAPublicKey)
RSAPrivateNumbers = rust_openssl.rsa.RSAPrivateNumbers
RSAPublicNumbers = rust_openssl.rsa.RSAPublicNumbers
def generate_private_key(
public_exponent: int,
key_size: int,
backend: typing.Any = None,
) -> RSAPrivateKey:
_verify_rsa_parameters(public_exponent, key_size)
return rust_openssl.rsa.generate_private_key(public_exponent, key_size)
def _verify_rsa_parameters(public_exponent: int, key_size: int) -> None:
if public_exponent not in (3, 65537):
raise ValueError(
"public_exponent must be either 3 (for legacy compatibility) or "
"65537. Almost everyone should choose 65537 here!"
)
if key_size < 512:
raise ValueError("key_size must be at least 512-bits.")
def _modinv(e: int, m: int) -> int:
"""
Modular Multiplicative Inverse. Returns x such that: (x*e) mod m == 1
"""
x1, x2 = 1, 0
a, b = e, m
while b > 0:
q, r = divmod(a, b)
xn = x1 - q * x2
a, b, x1, x2 = b, r, x2, xn
return x1 % m
def rsa_crt_iqmp(p: int, q: int) -> int:
"""
Compute the CRT (q ** -1) % p value from RSA primes p and q.
"""
return _modinv(q, p)
def rsa_crt_dmp1(private_exponent: int, p: int) -> int:
"""
Compute the CRT private_exponent % (p - 1) value from the RSA
private_exponent (d) and p.
"""
return private_exponent % (p - 1)
def rsa_crt_dmq1(private_exponent: int, q: int) -> int:
"""
Compute the CRT private_exponent % (q - 1) value from the RSA
private_exponent (d) and q.
"""
return private_exponent % (q - 1)
# Controls the number of iterations rsa_recover_prime_factors will perform
# to obtain the prime factors. Each iteration increments by 2 so the actual
# maximum attempts is half this number.
_MAX_RECOVERY_ATTEMPTS = 1000
def rsa_recover_prime_factors(n: int, e: int, d: int) -> tuple[int, int]:
"""
Compute factors p and q from the private exponent d. We assume that n has
no more than two factors. This function is adapted from code in PyCrypto.
"""
# See 8.2.2(i) in Handbook of Applied Cryptography.
ktot = d * e - 1
# The quantity d*e-1 is a multiple of phi(n), even,
# and can be represented as t*2^s.
t = ktot
while t % 2 == 0:
t = t // 2
# Cycle through all multiplicative inverses in Zn.
# The algorithm is non-deterministic, but there is a 50% chance
# any candidate a leads to successful factoring.
# See "Digitalized Signatures and Public Key Functions as Intractable
# as Factorization", M. Rabin, 1979
spotted = False
a = 2
while not spotted and a < _MAX_RECOVERY_ATTEMPTS:
k = t
# Cycle through all values a^{t*2^i}=a^k
while k < ktot:
cand = pow(a, k, n)
# Check if a^k is a non-trivial root of unity (mod n)
if cand != 1 and cand != (n - 1) and pow(cand, 2, n) == 1:
# We have found a number such that (cand-1)(cand+1)=0 (mod n).
# Either of the terms divides n.
p = gcd(cand + 1, n)
spotted = True
break
k *= 2
# This value was not any good... let's try another!
a += 2
if not spotted:
raise ValueError("Unable to compute factors p and q from exponent d.")
# Found !
q, r = divmod(n, p)
assert r == 0
p, q = sorted((p, q), reverse=True)
return (p, q)

View File

@@ -0,0 +1,111 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
import typing
from cryptography import utils
from cryptography.hazmat.primitives.asymmetric import (
dh,
dsa,
ec,
ed448,
ed25519,
rsa,
x448,
x25519,
)
# Every asymmetric key type
PublicKeyTypes = typing.Union[
dh.DHPublicKey,
dsa.DSAPublicKey,
rsa.RSAPublicKey,
ec.EllipticCurvePublicKey,
ed25519.Ed25519PublicKey,
ed448.Ed448PublicKey,
x25519.X25519PublicKey,
x448.X448PublicKey,
]
PUBLIC_KEY_TYPES = PublicKeyTypes
utils.deprecated(
PUBLIC_KEY_TYPES,
__name__,
"Use PublicKeyTypes instead",
utils.DeprecatedIn40,
name="PUBLIC_KEY_TYPES",
)
# Every asymmetric key type
PrivateKeyTypes = typing.Union[
dh.DHPrivateKey,
ed25519.Ed25519PrivateKey,
ed448.Ed448PrivateKey,
rsa.RSAPrivateKey,
dsa.DSAPrivateKey,
ec.EllipticCurvePrivateKey,
x25519.X25519PrivateKey,
x448.X448PrivateKey,
]
PRIVATE_KEY_TYPES = PrivateKeyTypes
utils.deprecated(
PRIVATE_KEY_TYPES,
__name__,
"Use PrivateKeyTypes instead",
utils.DeprecatedIn40,
name="PRIVATE_KEY_TYPES",
)
# Just the key types we allow to be used for x509 signing. This mirrors
# the certificate public key types
CertificateIssuerPrivateKeyTypes = typing.Union[
ed25519.Ed25519PrivateKey,
ed448.Ed448PrivateKey,
rsa.RSAPrivateKey,
dsa.DSAPrivateKey,
ec.EllipticCurvePrivateKey,
]
CERTIFICATE_PRIVATE_KEY_TYPES = CertificateIssuerPrivateKeyTypes
utils.deprecated(
CERTIFICATE_PRIVATE_KEY_TYPES,
__name__,
"Use CertificateIssuerPrivateKeyTypes instead",
utils.DeprecatedIn40,
name="CERTIFICATE_PRIVATE_KEY_TYPES",
)
# Just the key types we allow to be used for x509 signing. This mirrors
# the certificate private key types
CertificateIssuerPublicKeyTypes = typing.Union[
dsa.DSAPublicKey,
rsa.RSAPublicKey,
ec.EllipticCurvePublicKey,
ed25519.Ed25519PublicKey,
ed448.Ed448PublicKey,
]
CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES = CertificateIssuerPublicKeyTypes
utils.deprecated(
CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES,
__name__,
"Use CertificateIssuerPublicKeyTypes instead",
utils.DeprecatedIn40,
name="CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES",
)
# This type removes DHPublicKey. x448/x25519 can be a public key
# but cannot be used in signing so they are allowed here.
CertificatePublicKeyTypes = typing.Union[
dsa.DSAPublicKey,
rsa.RSAPublicKey,
ec.EllipticCurvePublicKey,
ed25519.Ed25519PublicKey,
ed448.Ed448PublicKey,
x25519.X25519PublicKey,
x448.X448PublicKey,
]
CERTIFICATE_PUBLIC_KEY_TYPES = CertificatePublicKeyTypes
utils.deprecated(
CERTIFICATE_PUBLIC_KEY_TYPES,
__name__,
"Use CertificatePublicKeyTypes instead",
utils.DeprecatedIn40,
name="CERTIFICATE_PUBLIC_KEY_TYPES",
)

View File

@@ -0,0 +1,24 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
from cryptography.hazmat.bindings._rust import asn1
from cryptography.hazmat.primitives import hashes
decode_dss_signature = asn1.decode_dss_signature
encode_dss_signature = asn1.encode_dss_signature
class Prehashed:
def __init__(self, algorithm: hashes.HashAlgorithm):
if not isinstance(algorithm, hashes.HashAlgorithm):
raise TypeError("Expected instance of HashAlgorithm.")
self._algorithm = algorithm
self._digest_size = algorithm.digest_size
@property
def digest_size(self) -> int:
return self._digest_size

View File

@@ -0,0 +1,109 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
import abc
from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
from cryptography.hazmat.primitives import _serialization
class X25519PublicKey(metaclass=abc.ABCMeta):
@classmethod
def from_public_bytes(cls, data: bytes) -> X25519PublicKey:
from cryptography.hazmat.backends.openssl.backend import backend
if not backend.x25519_supported():
raise UnsupportedAlgorithm(
"X25519 is not supported by this version of OpenSSL.",
_Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
)
return rust_openssl.x25519.from_public_bytes(data)
@abc.abstractmethod
def public_bytes(
self,
encoding: _serialization.Encoding,
format: _serialization.PublicFormat,
) -> bytes:
"""
The serialized bytes of the public key.
"""
@abc.abstractmethod
def public_bytes_raw(self) -> bytes:
"""
The raw bytes of the public key.
Equivalent to public_bytes(Raw, Raw).
"""
@abc.abstractmethod
def __eq__(self, other: object) -> bool:
"""
Checks equality.
"""
X25519PublicKey.register(rust_openssl.x25519.X25519PublicKey)
class X25519PrivateKey(metaclass=abc.ABCMeta):
@classmethod
def generate(cls) -> X25519PrivateKey:
from cryptography.hazmat.backends.openssl.backend import backend
if not backend.x25519_supported():
raise UnsupportedAlgorithm(
"X25519 is not supported by this version of OpenSSL.",
_Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
)
return rust_openssl.x25519.generate_key()
@classmethod
def from_private_bytes(cls, data: bytes) -> X25519PrivateKey:
from cryptography.hazmat.backends.openssl.backend import backend
if not backend.x25519_supported():
raise UnsupportedAlgorithm(
"X25519 is not supported by this version of OpenSSL.",
_Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
)
return rust_openssl.x25519.from_private_bytes(data)
@abc.abstractmethod
def public_key(self) -> X25519PublicKey:
"""
Returns the public key associated with this private key
"""
@abc.abstractmethod
def private_bytes(
self,
encoding: _serialization.Encoding,
format: _serialization.PrivateFormat,
encryption_algorithm: _serialization.KeySerializationEncryption,
) -> bytes:
"""
The serialized bytes of the private key.
"""
@abc.abstractmethod
def private_bytes_raw(self) -> bytes:
"""
The raw bytes of the private key.
Equivalent to private_bytes(Raw, Raw, NoEncryption()).
"""
@abc.abstractmethod
def exchange(self, peer_public_key: X25519PublicKey) -> bytes:
"""
Performs a key exchange operation using the provided peer's public key.
"""
X25519PrivateKey.register(rust_openssl.x25519.X25519PrivateKey)

View File

@@ -0,0 +1,112 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
import abc
from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
from cryptography.hazmat.primitives import _serialization
class X448PublicKey(metaclass=abc.ABCMeta):
@classmethod
def from_public_bytes(cls, data: bytes) -> X448PublicKey:
from cryptography.hazmat.backends.openssl.backend import backend
if not backend.x448_supported():
raise UnsupportedAlgorithm(
"X448 is not supported by this version of OpenSSL.",
_Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
)
return rust_openssl.x448.from_public_bytes(data)
@abc.abstractmethod
def public_bytes(
self,
encoding: _serialization.Encoding,
format: _serialization.PublicFormat,
) -> bytes:
"""
The serialized bytes of the public key.
"""
@abc.abstractmethod
def public_bytes_raw(self) -> bytes:
"""
The raw bytes of the public key.
Equivalent to public_bytes(Raw, Raw).
"""
@abc.abstractmethod
def __eq__(self, other: object) -> bool:
"""
Checks equality.
"""
if hasattr(rust_openssl, "x448"):
X448PublicKey.register(rust_openssl.x448.X448PublicKey)
class X448PrivateKey(metaclass=abc.ABCMeta):
@classmethod
def generate(cls) -> X448PrivateKey:
from cryptography.hazmat.backends.openssl.backend import backend
if not backend.x448_supported():
raise UnsupportedAlgorithm(
"X448 is not supported by this version of OpenSSL.",
_Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
)
return rust_openssl.x448.generate_key()
@classmethod
def from_private_bytes(cls, data: bytes) -> X448PrivateKey:
from cryptography.hazmat.backends.openssl.backend import backend
if not backend.x448_supported():
raise UnsupportedAlgorithm(
"X448 is not supported by this version of OpenSSL.",
_Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
)
return rust_openssl.x448.from_private_bytes(data)
@abc.abstractmethod
def public_key(self) -> X448PublicKey:
"""
Returns the public key associated with this private key
"""
@abc.abstractmethod
def private_bytes(
self,
encoding: _serialization.Encoding,
format: _serialization.PrivateFormat,
encryption_algorithm: _serialization.KeySerializationEncryption,
) -> bytes:
"""
The serialized bytes of the private key.
"""
@abc.abstractmethod
def private_bytes_raw(self) -> bytes:
"""
The raw bytes of the private key.
Equivalent to private_bytes(Raw, Raw, NoEncryption()).
"""
@abc.abstractmethod
def exchange(self, peer_public_key: X448PublicKey) -> bytes:
"""
Performs a key exchange operation using the provided peer's public key.
"""
if hasattr(rust_openssl, "x448"):
X448PrivateKey.register(rust_openssl.x448.X448PrivateKey)

View File

@@ -0,0 +1,27 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations
from cryptography.hazmat.primitives._cipheralgorithm import (
BlockCipherAlgorithm,
CipherAlgorithm,
)
from cryptography.hazmat.primitives.ciphers.base import (
AEADCipherContext,
AEADDecryptionContext,
AEADEncryptionContext,
Cipher,
CipherContext,
)
__all__ = [
"Cipher",
"CipherAlgorithm",
"BlockCipherAlgorithm",
"CipherContext",
"AEADCipherContext",
"AEADDecryptionContext",
"AEADEncryptionContext",
]

Some files were not shown because too many files have changed in this diff Show More