1 | """ |
---|
2 | Helper functions for cryptograhpy-related operations inside Tahoe |
---|
3 | using AES |
---|
4 | |
---|
5 | These functions use and return objects that are documented in the |
---|
6 | `cryptography` library -- however, code inside Tahoe should only use |
---|
7 | functions from allmydata.crypto.aes and not rely on features of any |
---|
8 | objects that `cryptography` documents. |
---|
9 | |
---|
10 | Ported to Python 3. |
---|
11 | """ |
---|
12 | |
---|
13 | from dataclasses import dataclass |
---|
14 | from typing import Optional |
---|
15 | |
---|
16 | from cryptography.hazmat.backends import default_backend |
---|
17 | from cryptography.hazmat.primitives.ciphers import ( |
---|
18 | Cipher, |
---|
19 | algorithms, |
---|
20 | modes, |
---|
21 | CipherContext, |
---|
22 | ) |
---|
23 | |
---|
24 | |
---|
25 | DEFAULT_IV = b'\x00' * 16 |
---|
26 | |
---|
27 | |
---|
28 | @dataclass |
---|
29 | class 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 |
---|
40 | class 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 | |
---|
50 | def 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 | |
---|
69 | def 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 | |
---|
86 | def 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 | |
---|
105 | def 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 | |
---|
122 | def _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 | |
---|
138 | def _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 | |
---|
149 | def _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 |
---|