source: trunk/src/allmydata/crypto/aes.py

Last change on this file was ee2b932, checked in by Itamar Turner-Trauring <itamar@…>, at 2024-08-13T16:16:30Z

Correct type annotations

  • Property mode set to 100644
File size: 4.4 KB
Line 
1"""
2Helper functions for cryptograhpy-related operations inside Tahoe
3using AES
4
5These functions use and return objects that are documented in the
6`cryptography` library -- however, code inside Tahoe should only use
7functions from allmydata.crypto.aes and not rely on features of any
8objects that `cryptography` documents.
9
10Ported to Python 3.
11"""
12
13from dataclasses import dataclass
14from typing import Optional
15
16from cryptography.hazmat.backends import default_backend
17from cryptography.hazmat.primitives.ciphers import (
18    Cipher,
19    algorithms,
20    modes,
21    CipherContext,
22)
23
24
25DEFAULT_IV = b'\x00' * 16
26
27
28@dataclass
29class Encryptor:
30    """
31    An object which can encrypt data.
32
33    Create one using :func:`create_encryptor` and use it with
34    :func:`encrypt_data`
35    """
36    encrypt_context: CipherContext
37
38
39@dataclass
40class Decryptor:
41    """
42    An object which can decrypt data.
43
44    Create one using :func:`create_decryptor` and use it with
45    :func:`decrypt_data`
46    """
47    decrypt_context: CipherContext
48
49
50def create_encryptor(key: bytes, iv: Optional[bytes]=None) -> Encryptor:
51    """
52    Create and return a new object which can do AES encryptions with
53    the given key and initialization vector (IV). The default IV is 16
54    zero-bytes.
55
56    :param bytes key: the key bytes, should be 128 or 256 bits (16 or
57        32 bytes)
58
59    :param bytes iv: the Initialization Vector consisting of 16 bytes,
60        or None for the default (which is 16 zero bytes)
61
62    :returns: an object suitable for use with :func:`encrypt_data` (an
63        :class:`Encryptor`)
64    """
65    cryptor = _create_cryptor(key, iv)
66    return Encryptor(cryptor)
67
68
69def encrypt_data(encryptor: Encryptor, plaintext: bytes) -> bytes:
70    """
71    AES-encrypt `plaintext` with the given `encryptor`.
72
73    :param encryptor: an instance of :class:`Encryptor` previously
74        returned from `create_encryptor`
75
76    :param bytes plaintext: the data to encrypt
77
78    :returns: bytes of ciphertext
79    """
80    if not isinstance(plaintext, (bytes, memoryview)):
81        raise ValueError(f'Plaintext must be bytes or memoryview: {type(plaintext)}')
82
83    return encryptor.encrypt_context.update(plaintext)
84
85
86def create_decryptor(key: bytes, iv: Optional[bytes]=None) -> Decryptor:
87    """
88    Create and return a new object which can do AES decryptions with
89    the given key and initialization vector (IV). The default IV is 16
90    zero-bytes.
91
92    :param bytes key: the key bytes, should be 128 or 256 bits (16 or
93        32 bytes)
94
95    :param bytes iv: the Initialization Vector consisting of 16 bytes,
96        or None for the default (which is 16 zero bytes)
97
98    :returns: an object suitable for use with :func:`decrypt_data` (an
99        :class:`Decryptor` instance)
100    """
101    cryptor = _create_cryptor(key, iv)
102    return Decryptor(cryptor)
103
104
105def decrypt_data(decryptor: Decryptor, plaintext: bytes) -> bytes:
106    """
107    AES-decrypt `plaintext` with the given `decryptor`.
108
109    :param decryptor: an instance of :class:`Decryptor` previously
110        returned from `create_decryptor`
111
112    :param bytes plaintext: the data to decrypt
113
114    :returns: bytes of ciphertext
115    """
116    if not isinstance(plaintext, (bytes, memoryview)):
117        raise ValueError(f'Plaintext must be bytes or memoryview: {type(plaintext)}')
118
119    return decryptor.decrypt_context.update(plaintext)
120
121
122def _create_cryptor(key: bytes, iv: Optional[bytes]) -> CipherContext:
123    """
124    Internal helper.
125
126    See :func:`create_encryptor` or :func:`create_decryptor`.
127    """
128    key = _validate_key(key)
129    iv = _validate_iv(iv)
130    cipher = Cipher(
131        algorithms.AES(key),
132        modes.CTR(iv),
133        backend=default_backend()
134    )
135    return cipher.encryptor()  # type: ignore[return-type]
136
137
138def _validate_key(key: bytes) -> bytes:
139    """
140    confirm `key` is suitable for AES encryption, or raise ValueError
141    """
142    if not isinstance(key, bytes):
143        raise TypeError('Key must be bytes')
144    if len(key) not in (16, 32):
145        raise ValueError('Key must be 16 or 32 bytes long')
146    return key
147
148
149def _validate_iv(iv: Optional[bytes]) -> bytes:
150    """
151    Returns a suitable initialiation vector. If `iv` is `None`, a
152    default is returned. If `iv` is not a suitable initialization
153    vector an error is raised. `iv` is returned if it valid.
154    """
155    if iv is None:
156        return DEFAULT_IV
157    if not isinstance(iv, bytes):
158        raise TypeError('IV must be bytes')
159    if len(iv) != 16:
160        raise ValueError('IV must be 16 bytes long')
161    return iv
Note: See TracBrowser for help on using the repository browser.