1# This file is dual licensed under the terms of the Apache License, Version 
    2# 2.0, and the BSD License. See the LICENSE file in the root of this repository 
    3# for complete details. 
    4 
    5from __future__ import annotations 
    6 
    7import abc 
    8 
    9from cryptography import utils 
    10from cryptography.hazmat.primitives.hashes import HashAlgorithm 
    11 
    12# This exists to break an import cycle. These classes are normally accessible 
    13# from the serialization module. 
    14 
    15 
    16class PBES(utils.Enum): 
    17    PBESv1SHA1And3KeyTripleDESCBC = "PBESv1 using SHA1 and 3-Key TripleDES" 
    18    PBESv2SHA256AndAES256CBC = "PBESv2 using SHA256 PBKDF2 and AES256 CBC" 
    19 
    20 
    21class Encoding(utils.Enum): 
    22    PEM = "PEM" 
    23    DER = "DER" 
    24    OpenSSH = "OpenSSH" 
    25    Raw = "Raw" 
    26    X962 = "ANSI X9.62" 
    27    SMIME = "S/MIME" 
    28 
    29 
    30class PrivateFormat(utils.Enum): 
    31    PKCS8 = "PKCS8" 
    32    TraditionalOpenSSL = "TraditionalOpenSSL" 
    33    Raw = "Raw" 
    34    OpenSSH = "OpenSSH" 
    35    PKCS12 = "PKCS12" 
    36 
    37    def encryption_builder(self) -> KeySerializationEncryptionBuilder: 
    38        if self not in (PrivateFormat.OpenSSH, PrivateFormat.PKCS12): 
    39            raise ValueError( 
    40                "encryption_builder only supported with PrivateFormat.OpenSSH" 
    41                " and PrivateFormat.PKCS12" 
    42            ) 
    43        return KeySerializationEncryptionBuilder(self) 
    44 
    45 
    46class PublicFormat(utils.Enum): 
    47    SubjectPublicKeyInfo = "X.509 subjectPublicKeyInfo with PKCS#1" 
    48    PKCS1 = "Raw PKCS#1" 
    49    OpenSSH = "OpenSSH" 
    50    Raw = "Raw" 
    51    CompressedPoint = "X9.62 Compressed Point" 
    52    UncompressedPoint = "X9.62 Uncompressed Point" 
    53 
    54 
    55class ParameterFormat(utils.Enum): 
    56    PKCS3 = "PKCS3" 
    57 
    58 
    59class KeySerializationEncryption(metaclass=abc.ABCMeta): 
    60    pass 
    61 
    62 
    63class BestAvailableEncryption(KeySerializationEncryption): 
    64    def __init__(self, password: bytes): 
    65        if not isinstance(password, bytes) or len(password) == 0: 
    66            raise ValueError("Password must be 1 or more bytes.") 
    67 
    68        self.password = password 
    69 
    70 
    71class NoEncryption(KeySerializationEncryption): 
    72    pass 
    73 
    74 
    75class KeySerializationEncryptionBuilder: 
    76    def __init__( 
    77        self, 
    78        format: PrivateFormat, 
    79        *, 
    80        _kdf_rounds: int | None = None, 
    81        _hmac_hash: HashAlgorithm | None = None, 
    82        _key_cert_algorithm: PBES | None = None, 
    83    ) -> None: 
    84        self._format = format 
    85 
    86        self._kdf_rounds = _kdf_rounds 
    87        self._hmac_hash = _hmac_hash 
    88        self._key_cert_algorithm = _key_cert_algorithm 
    89 
    90    def kdf_rounds(self, rounds: int) -> KeySerializationEncryptionBuilder: 
    91        if self._kdf_rounds is not None: 
    92            raise ValueError("kdf_rounds already set") 
    93 
    94        if not isinstance(rounds, int): 
    95            raise TypeError("kdf_rounds must be an integer") 
    96 
    97        if rounds < 1: 
    98            raise ValueError("kdf_rounds must be a positive integer") 
    99 
    100        return KeySerializationEncryptionBuilder( 
    101            self._format, 
    102            _kdf_rounds=rounds, 
    103            _hmac_hash=self._hmac_hash, 
    104            _key_cert_algorithm=self._key_cert_algorithm, 
    105        ) 
    106 
    107    def hmac_hash( 
    108        self, algorithm: HashAlgorithm 
    109    ) -> KeySerializationEncryptionBuilder: 
    110        if self._format is not PrivateFormat.PKCS12: 
    111            raise TypeError( 
    112                "hmac_hash only supported with PrivateFormat.PKCS12" 
    113            ) 
    114 
    115        if self._hmac_hash is not None: 
    116            raise ValueError("hmac_hash already set") 
    117        return KeySerializationEncryptionBuilder( 
    118            self._format, 
    119            _kdf_rounds=self._kdf_rounds, 
    120            _hmac_hash=algorithm, 
    121            _key_cert_algorithm=self._key_cert_algorithm, 
    122        ) 
    123 
    124    def key_cert_algorithm( 
    125        self, algorithm: PBES 
    126    ) -> KeySerializationEncryptionBuilder: 
    127        if self._format is not PrivateFormat.PKCS12: 
    128            raise TypeError( 
    129                "key_cert_algorithm only supported with PrivateFormat.PKCS12" 
    130            ) 
    131        if self._key_cert_algorithm is not None: 
    132            raise ValueError("key_cert_algorithm already set") 
    133        return KeySerializationEncryptionBuilder( 
    134            self._format, 
    135            _kdf_rounds=self._kdf_rounds, 
    136            _hmac_hash=self._hmac_hash, 
    137            _key_cert_algorithm=algorithm, 
    138        ) 
    139 
    140    def build(self, password: bytes) -> KeySerializationEncryption: 
    141        if not isinstance(password, bytes) or len(password) == 0: 
    142            raise ValueError("Password must be 1 or more bytes.") 
    143 
    144        return _KeySerializationEncryption( 
    145            self._format, 
    146            password, 
    147            kdf_rounds=self._kdf_rounds, 
    148            hmac_hash=self._hmac_hash, 
    149            key_cert_algorithm=self._key_cert_algorithm, 
    150        ) 
    151 
    152 
    153class _KeySerializationEncryption(KeySerializationEncryption): 
    154    def __init__( 
    155        self, 
    156        format: PrivateFormat, 
    157        password: bytes, 
    158        *, 
    159        kdf_rounds: int | None, 
    160        hmac_hash: HashAlgorithm | None, 
    161        key_cert_algorithm: PBES | None, 
    162    ): 
    163        self._format = format 
    164        self.password = password 
    165 
    166        self._kdf_rounds = kdf_rounds 
    167        self._hmac_hash = hmac_hash 
    168        self._key_cert_algorithm = key_cert_algorithm