moving to scripts

This commit is contained in:
eneller
2021-11-16 23:55:48 +01:00
parent f591ca2077
commit 14bfb7f96f
2575 changed files with 465862 additions and 0 deletions

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 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",
]

View File

@@ -0,0 +1,216 @@
# 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 os
import typing
from cryptography import exceptions, utils
from cryptography.hazmat.backends.openssl import aead
from cryptography.hazmat.backends.openssl.backend import backend
class ChaCha20Poly1305(object):
_MAX_SIZE = 2 ** 32
def __init__(self, key: bytes):
if not backend.aead_cipher_supported(self):
raise exceptions.UnsupportedAlgorithm(
"ChaCha20Poly1305 is not supported by this version of OpenSSL",
exceptions._Reasons.UNSUPPORTED_CIPHER,
)
utils._check_byteslike("key", key)
if len(key) != 32:
raise ValueError("ChaCha20Poly1305 key must be 32 bytes.")
self._key = key
@classmethod
def generate_key(cls) -> bytes:
return os.urandom(32)
def encrypt(
self,
nonce: bytes,
data: bytes,
associated_data: typing.Optional[bytes],
) -> bytes:
if associated_data is None:
associated_data = b""
if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE:
# This is OverflowError to match what cffi would raise
raise OverflowError(
"Data or associated data too long. Max 2**32 bytes"
)
self._check_params(nonce, data, associated_data)
return aead._encrypt(backend, self, nonce, data, associated_data, 16)
def decrypt(
self,
nonce: bytes,
data: bytes,
associated_data: typing.Optional[bytes],
) -> bytes:
if associated_data is None:
associated_data = b""
self._check_params(nonce, data, associated_data)
return aead._decrypt(backend, self, nonce, data, associated_data, 16)
def _check_params(
self,
nonce: bytes,
data: bytes,
associated_data: bytes,
) -> None:
utils._check_byteslike("nonce", nonce)
utils._check_bytes("data", data)
utils._check_bytes("associated_data", associated_data)
if len(nonce) != 12:
raise ValueError("Nonce must be 12 bytes")
class AESCCM(object):
_MAX_SIZE = 2 ** 32
def __init__(self, key: bytes, tag_length: int = 16):
utils._check_byteslike("key", key)
if len(key) not in (16, 24, 32):
raise ValueError("AESCCM key must be 128, 192, or 256 bits.")
self._key = key
if not isinstance(tag_length, int):
raise TypeError("tag_length must be an integer")
if tag_length not in (4, 6, 8, 10, 12, 14, 16):
raise ValueError("Invalid tag_length")
self._tag_length = tag_length
@classmethod
def generate_key(cls, bit_length: int) -> bytes:
if not isinstance(bit_length, int):
raise TypeError("bit_length must be an integer")
if bit_length not in (128, 192, 256):
raise ValueError("bit_length must be 128, 192, or 256")
return os.urandom(bit_length // 8)
def encrypt(
self,
nonce: bytes,
data: bytes,
associated_data: typing.Optional[bytes],
) -> bytes:
if associated_data is None:
associated_data = b""
if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE:
# This is OverflowError to match what cffi would raise
raise OverflowError(
"Data or associated data too long. Max 2**32 bytes"
)
self._check_params(nonce, data, associated_data)
self._validate_lengths(nonce, len(data))
return aead._encrypt(
backend, self, nonce, data, associated_data, self._tag_length
)
def decrypt(
self,
nonce: bytes,
data: bytes,
associated_data: typing.Optional[bytes],
) -> bytes:
if associated_data is None:
associated_data = b""
self._check_params(nonce, data, associated_data)
return aead._decrypt(
backend, self, nonce, data, associated_data, self._tag_length
)
def _validate_lengths(self, nonce: bytes, data_len: int) -> None:
# For information about computing this, see
# https://tools.ietf.org/html/rfc3610#section-2.1
l_val = 15 - len(nonce)
if 2 ** (8 * l_val) < data_len:
raise ValueError("Data too long for nonce")
def _check_params(
self, nonce: bytes, data: bytes, associated_data: bytes
) -> None:
utils._check_byteslike("nonce", nonce)
utils._check_bytes("data", data)
utils._check_bytes("associated_data", associated_data)
if not 7 <= len(nonce) <= 13:
raise ValueError("Nonce must be between 7 and 13 bytes")
class AESGCM(object):
_MAX_SIZE = 2 ** 32
def __init__(self, key: bytes):
utils._check_byteslike("key", key)
if len(key) not in (16, 24, 32):
raise ValueError("AESGCM key must be 128, 192, or 256 bits.")
self._key = key
@classmethod
def generate_key(cls, bit_length: int) -> bytes:
if not isinstance(bit_length, int):
raise TypeError("bit_length must be an integer")
if bit_length not in (128, 192, 256):
raise ValueError("bit_length must be 128, 192, or 256")
return os.urandom(bit_length // 8)
def encrypt(
self,
nonce: bytes,
data: bytes,
associated_data: typing.Optional[bytes],
) -> bytes:
if associated_data is None:
associated_data = b""
if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE:
# This is OverflowError to match what cffi would raise
raise OverflowError(
"Data or associated data too long. Max 2**32 bytes"
)
self._check_params(nonce, data, associated_data)
return aead._encrypt(backend, self, nonce, data, associated_data, 16)
def decrypt(
self,
nonce: bytes,
data: bytes,
associated_data: typing.Optional[bytes],
) -> bytes:
if associated_data is None:
associated_data = b""
self._check_params(nonce, data, associated_data)
return aead._decrypt(backend, self, nonce, data, associated_data, 16)
def _check_params(
self,
nonce: bytes,
data: bytes,
associated_data: bytes,
) -> None:
utils._check_byteslike("nonce", nonce)
utils._check_bytes("data", data)
utils._check_bytes("associated_data", associated_data)
if len(nonce) < 8 or len(nonce) > 128:
raise ValueError("Nonce must be between 8 and 128 bytes")

View File

@@ -0,0 +1,168 @@
# 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 import utils
from cryptography.hazmat.primitives.ciphers import (
BlockCipherAlgorithm,
CipherAlgorithm,
)
from cryptography.hazmat.primitives.ciphers.modes import ModeWithNonce
def _verify_key_size(algorithm: CipherAlgorithm, key: bytes) -> bytes:
# Verify that the key is instance of bytes
utils._check_byteslike("key", key)
# Verify that the key size matches the expected key size
if len(key) * 8 not in algorithm.key_sizes:
raise ValueError(
"Invalid key size ({}) for {}.".format(
len(key) * 8, algorithm.name
)
)
return key
class AES(CipherAlgorithm, BlockCipherAlgorithm):
name = "AES"
block_size = 128
# 512 added to support AES-256-XTS, which uses 512-bit keys
key_sizes = frozenset([128, 192, 256, 512])
def __init__(self, key: bytes):
self.key = _verify_key_size(self, key)
@property
def key_size(self) -> int:
return len(self.key) * 8
class Camellia(CipherAlgorithm, BlockCipherAlgorithm):
name = "camellia"
block_size = 128
key_sizes = frozenset([128, 192, 256])
def __init__(self, key: bytes):
self.key = _verify_key_size(self, key)
@property
def key_size(self) -> int:
return len(self.key) * 8
class TripleDES(CipherAlgorithm, BlockCipherAlgorithm):
name = "3DES"
block_size = 64
key_sizes = frozenset([64, 128, 192])
def __init__(self, key: bytes):
if len(key) == 8:
key += key + key
elif len(key) == 16:
key += key[:8]
self.key = _verify_key_size(self, key)
@property
def key_size(self) -> int:
return len(self.key) * 8
class Blowfish(CipherAlgorithm, BlockCipherAlgorithm):
name = "Blowfish"
block_size = 64
key_sizes = frozenset(range(32, 449, 8))
def __init__(self, key: bytes):
self.key = _verify_key_size(self, key)
@property
def key_size(self) -> int:
return len(self.key) * 8
class CAST5(CipherAlgorithm, BlockCipherAlgorithm):
name = "CAST5"
block_size = 64
key_sizes = frozenset(range(40, 129, 8))
def __init__(self, key: bytes):
self.key = _verify_key_size(self, key)
@property
def key_size(self) -> int:
return len(self.key) * 8
class ARC4(CipherAlgorithm):
name = "RC4"
key_sizes = frozenset([40, 56, 64, 80, 128, 160, 192, 256])
def __init__(self, key: bytes):
self.key = _verify_key_size(self, key)
@property
def key_size(self) -> int:
return len(self.key) * 8
class IDEA(CipherAlgorithm):
name = "IDEA"
block_size = 64
key_sizes = frozenset([128])
def __init__(self, key: bytes):
self.key = _verify_key_size(self, key)
@property
def key_size(self) -> int:
return len(self.key) * 8
class SEED(CipherAlgorithm, BlockCipherAlgorithm):
name = "SEED"
block_size = 128
key_sizes = frozenset([128])
def __init__(self, key: bytes):
self.key = _verify_key_size(self, key)
@property
def key_size(self) -> int:
return len(self.key) * 8
class ChaCha20(CipherAlgorithm, ModeWithNonce):
name = "ChaCha20"
key_sizes = frozenset([256])
def __init__(self, key: bytes, nonce: bytes):
self.key = _verify_key_size(self, key)
utils._check_byteslike("nonce", nonce)
if len(nonce) != 16:
raise ValueError("nonce must be 128-bits (16 bytes)")
self._nonce = nonce
@property
def nonce(self) -> bytes:
return self._nonce
@property
def key_size(self) -> int:
return len(self.key) * 8
class SM4(CipherAlgorithm, BlockCipherAlgorithm):
name = "SM4"
block_size = 128
key_sizes = frozenset([128])
def __init__(self, key: bytes):
self.key = _verify_key_size(self, key)
@property
def key_size(self) -> int:
return len(self.key) * 8

View File

@@ -0,0 +1,218 @@
# 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 abc
import typing
from cryptography import utils
from cryptography.exceptions import (
AlreadyFinalized,
AlreadyUpdated,
NotYetFinalized,
UnsupportedAlgorithm,
_Reasons,
)
from cryptography.hazmat.backends import _get_backend
from cryptography.hazmat.backends.interfaces import Backend, CipherBackend
from cryptography.hazmat.primitives._cipheralgorithm import CipherAlgorithm
from cryptography.hazmat.primitives.ciphers import modes
class CipherContext(metaclass=abc.ABCMeta):
@abc.abstractmethod
def update(self, data: bytes) -> bytes:
"""
Processes the provided bytes through the cipher and returns the results
as bytes.
"""
@abc.abstractmethod
def update_into(self, data: bytes, buf) -> int:
"""
Processes the provided bytes and writes the resulting data into the
provided buffer. Returns the number of bytes written.
"""
@abc.abstractmethod
def finalize(self) -> bytes:
"""
Returns the results of processing the final block as bytes.
"""
class AEADCipherContext(metaclass=abc.ABCMeta):
@abc.abstractmethod
def authenticate_additional_data(self, data: bytes) -> None:
"""
Authenticates the provided bytes.
"""
class AEADDecryptionContext(metaclass=abc.ABCMeta):
@abc.abstractmethod
def finalize_with_tag(self, tag: bytes) -> bytes:
"""
Returns the results of processing the final block as bytes and allows
delayed passing of the authentication tag.
"""
class AEADEncryptionContext(metaclass=abc.ABCMeta):
@abc.abstractproperty
def tag(self) -> bytes:
"""
Returns tag bytes. This is only available after encryption is
finalized.
"""
class Cipher(object):
def __init__(
self,
algorithm: CipherAlgorithm,
mode: typing.Optional[modes.Mode],
backend: typing.Optional[Backend] = None,
):
backend = _get_backend(backend)
if not isinstance(backend, CipherBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement CipherBackend.",
_Reasons.BACKEND_MISSING_INTERFACE,
)
if not isinstance(algorithm, CipherAlgorithm):
raise TypeError("Expected interface of CipherAlgorithm.")
if mode is not None:
mode.validate_for_algorithm(algorithm)
self.algorithm = algorithm
self.mode = mode
self._backend = backend
def encryptor(self):
if isinstance(self.mode, modes.ModeWithAuthenticationTag):
if self.mode.tag is not None:
raise ValueError(
"Authentication tag must be None when encrypting."
)
ctx = self._backend.create_symmetric_encryption_ctx(
self.algorithm, self.mode
)
return self._wrap_ctx(ctx, encrypt=True)
def decryptor(self):
ctx = self._backend.create_symmetric_decryption_ctx(
self.algorithm, self.mode
)
return self._wrap_ctx(ctx, encrypt=False)
def _wrap_ctx(self, ctx, encrypt):
if isinstance(self.mode, modes.ModeWithAuthenticationTag):
if encrypt:
return _AEADEncryptionContext(ctx)
else:
return _AEADCipherContext(ctx)
else:
return _CipherContext(ctx)
@utils.register_interface(CipherContext)
class _CipherContext(object):
def __init__(self, ctx):
self._ctx = ctx
def update(self, data: bytes) -> bytes:
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized.")
return self._ctx.update(data)
def update_into(self, data: bytes, buf) -> int:
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized.")
return self._ctx.update_into(data, buf)
def finalize(self) -> bytes:
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized.")
data = self._ctx.finalize()
self._ctx = None
return data
@utils.register_interface(AEADCipherContext)
@utils.register_interface(CipherContext)
@utils.register_interface(AEADDecryptionContext)
class _AEADCipherContext(object):
def __init__(self, ctx):
self._ctx = ctx
self._bytes_processed = 0
self._aad_bytes_processed = 0
self._tag = None
self._updated = False
def _check_limit(self, data_size: int) -> None:
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized.")
self._updated = True
self._bytes_processed += data_size
if self._bytes_processed > self._ctx._mode._MAX_ENCRYPTED_BYTES:
raise ValueError(
"{} has a maximum encrypted byte limit of {}".format(
self._ctx._mode.name, self._ctx._mode._MAX_ENCRYPTED_BYTES
)
)
def update(self, data: bytes) -> bytes:
self._check_limit(len(data))
return self._ctx.update(data)
def update_into(self, data: bytes, buf) -> int:
self._check_limit(len(data))
return self._ctx.update_into(data, buf)
def finalize(self) -> bytes:
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized.")
data = self._ctx.finalize()
self._tag = self._ctx.tag
self._ctx = None
return data
def finalize_with_tag(self, tag: bytes) -> bytes:
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized.")
data = self._ctx.finalize_with_tag(tag)
self._tag = self._ctx.tag
self._ctx = None
return data
def authenticate_additional_data(self, data: bytes) -> None:
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized.")
if self._updated:
raise AlreadyUpdated("Update has been called on this context.")
self._aad_bytes_processed += len(data)
if self._aad_bytes_processed > self._ctx._mode._MAX_AAD_BYTES:
raise ValueError(
"{} has a maximum AAD byte limit of {}".format(
self._ctx._mode.name, self._ctx._mode._MAX_AAD_BYTES
)
)
self._ctx.authenticate_additional_data(data)
@utils.register_interface(AEADEncryptionContext)
class _AEADEncryptionContext(_AEADCipherContext):
@property
def tag(self) -> bytes:
if self._ctx is not None:
raise NotYetFinalized(
"You must finalize encryption before " "getting the tag."
)
assert self._tag is not None
return self._tag

View File

@@ -0,0 +1,247 @@
# 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 abc
import typing
from cryptography import utils
from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
from cryptography.hazmat.primitives._cipheralgorithm import (
BlockCipherAlgorithm,
CipherAlgorithm,
)
class Mode(metaclass=abc.ABCMeta):
@abc.abstractproperty
def name(self) -> str:
"""
A string naming this mode (e.g. "ECB", "CBC").
"""
@abc.abstractmethod
def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None:
"""
Checks that all the necessary invariants of this (mode, algorithm)
combination are met.
"""
class ModeWithInitializationVector(metaclass=abc.ABCMeta):
@abc.abstractproperty
def initialization_vector(self) -> bytes:
"""
The value of the initialization vector for this mode as bytes.
"""
class ModeWithTweak(metaclass=abc.ABCMeta):
@abc.abstractproperty
def tweak(self) -> bytes:
"""
The value of the tweak for this mode as bytes.
"""
class ModeWithNonce(metaclass=abc.ABCMeta):
@abc.abstractproperty
def nonce(self) -> bytes:
"""
The value of the nonce for this mode as bytes.
"""
class ModeWithAuthenticationTag(metaclass=abc.ABCMeta):
@abc.abstractproperty
def tag(self) -> typing.Optional[bytes]:
"""
The value of the tag supplied to the constructor of this mode.
"""
def _check_aes_key_length(self, algorithm):
if algorithm.key_size > 256 and algorithm.name == "AES":
raise ValueError(
"Only 128, 192, and 256 bit keys are allowed for this AES mode"
)
def _check_iv_length(self, algorithm):
if len(self.initialization_vector) * 8 != algorithm.block_size:
raise ValueError(
"Invalid IV size ({}) for {}.".format(
len(self.initialization_vector), self.name
)
)
def _check_nonce_length(nonce: bytes, name: str, algorithm) -> None:
if len(nonce) * 8 != algorithm.block_size:
raise ValueError(
"Invalid nonce size ({}) for {}.".format(len(nonce), name)
)
def _check_iv_and_key_length(self, algorithm):
_check_aes_key_length(self, algorithm)
_check_iv_length(self, algorithm)
class CBC(Mode, ModeWithInitializationVector):
name = "CBC"
def __init__(self, initialization_vector: bytes):
utils._check_byteslike("initialization_vector", initialization_vector)
self._initialization_vector = initialization_vector
@property
def initialization_vector(self) -> bytes:
return self._initialization_vector
validate_for_algorithm = _check_iv_and_key_length
class XTS(Mode, ModeWithTweak):
name = "XTS"
def __init__(self, tweak: bytes):
utils._check_byteslike("tweak", tweak)
if len(tweak) != 16:
raise ValueError("tweak must be 128-bits (16 bytes)")
self._tweak = tweak
@property
def tweak(self) -> bytes:
return self._tweak
def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None:
if algorithm.key_size not in (256, 512):
raise ValueError(
"The XTS specification requires a 256-bit key for AES-128-XTS"
" and 512-bit key for AES-256-XTS"
)
class ECB(Mode):
name = "ECB"
validate_for_algorithm = _check_aes_key_length
class OFB(Mode, ModeWithInitializationVector):
name = "OFB"
def __init__(self, initialization_vector: bytes):
utils._check_byteslike("initialization_vector", initialization_vector)
self._initialization_vector = initialization_vector
@property
def initialization_vector(self) -> bytes:
return self._initialization_vector
validate_for_algorithm = _check_iv_and_key_length
class CFB(Mode, ModeWithInitializationVector):
name = "CFB"
def __init__(self, initialization_vector: bytes):
utils._check_byteslike("initialization_vector", initialization_vector)
self._initialization_vector = initialization_vector
@property
def initialization_vector(self) -> bytes:
return self._initialization_vector
validate_for_algorithm = _check_iv_and_key_length
class CFB8(Mode, ModeWithInitializationVector):
name = "CFB8"
def __init__(self, initialization_vector: bytes):
utils._check_byteslike("initialization_vector", initialization_vector)
self._initialization_vector = initialization_vector
@property
def initialization_vector(self) -> bytes:
return self._initialization_vector
validate_for_algorithm = _check_iv_and_key_length
class CTR(Mode, ModeWithNonce):
name = "CTR"
def __init__(self, nonce: bytes):
utils._check_byteslike("nonce", nonce)
self._nonce = nonce
@property
def nonce(self) -> bytes:
return self._nonce
def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None:
_check_aes_key_length(self, algorithm)
_check_nonce_length(self.nonce, self.name, algorithm)
class GCM(Mode, ModeWithInitializationVector, ModeWithAuthenticationTag):
name = "GCM"
_MAX_ENCRYPTED_BYTES = (2 ** 39 - 256) // 8
_MAX_AAD_BYTES = (2 ** 64) // 8
def __init__(
self,
initialization_vector: bytes,
tag: typing.Optional[bytes] = None,
min_tag_length: int = 16,
):
# OpenSSL 3.0.0 constrains GCM IVs to [64, 1024] bits inclusive
# This is a sane limit anyway so we'll enforce it here.
utils._check_byteslike("initialization_vector", initialization_vector)
if len(initialization_vector) < 8 or len(initialization_vector) > 128:
raise ValueError(
"initialization_vector must be between 8 and 128 bytes (64 "
"and 1024 bits)."
)
self._initialization_vector = initialization_vector
if tag is not None:
utils._check_bytes("tag", tag)
if min_tag_length < 4:
raise ValueError("min_tag_length must be >= 4")
if len(tag) < min_tag_length:
raise ValueError(
"Authentication tag must be {} bytes or longer.".format(
min_tag_length
)
)
self._tag = tag
self._min_tag_length = min_tag_length
@property
def tag(self) -> typing.Optional[bytes]:
return self._tag
@property
def initialization_vector(self) -> bytes:
return self._initialization_vector
def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None:
_check_aes_key_length(self, algorithm)
if not isinstance(algorithm, BlockCipherAlgorithm):
raise UnsupportedAlgorithm(
"GCM requires a block cipher algorithm",
_Reasons.UNSUPPORTED_CIPHER,
)
block_size_bytes = algorithm.block_size // 8
if self._tag is not None and len(self._tag) > block_size_bytes:
raise ValueError(
"Authentication tag cannot be more than {} bytes.".format(
block_size_bytes
)
)