File size: 5,123 Bytes
76f9669 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 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
|