1#
2# Cipher/mode_ecb.py : ECB mode
3#
4# ===================================================================
5# The contents of this file are dedicated to the public domain. To
6# the extent that dedication to the public domain is not available,
7# everyone is granted a worldwide, perpetual, royalty-free,
8# non-exclusive license to exercise all rights associated with the
9# contents of this file for any purpose whatsoever.
10# No rights are reserved.
11#
12# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
16# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
17# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19# SOFTWARE.
20# ===================================================================
21
22"""
23Electronic Code Book (ECB) mode.
24"""
25
26from __future__ import annotations
27
28from typing import Any, Optional, Union, overload
29
30__all__ = ["EcbMode"]
31
32from Crypto.Util._raw_api import (
33 SmartPointer,
34 VoidPointer,
35 c_size_t,
36 c_uint8_ptr_len,
37 c_uint8_ptr_out,
38 create_output_buffer,
39 get_raw_buffer,
40 is_writeable_buffer,
41 load_pycryptodome_raw_lib,
42)
43
44Buffer = Union[bytes, bytearray, memoryview]
45
46raw_ecb_lib = load_pycryptodome_raw_lib(
47 "Crypto.Cipher._raw_ecb",
48 """
49 int ECB_start_operation(void *cipher,
50 void **pResult);
51 int ECB_encrypt(void *ecbState,
52 const uint8_t *in,
53 uint8_t *out,
54 size_t data_len);
55 int ECB_decrypt(void *ecbState,
56 const uint8_t *in,
57 uint8_t *out,
58 size_t data_len);
59 int ECB_stop_operation(void *state);
60 """,
61)
62
63
64class EcbMode:
65 """*Electronic Code Book (ECB)*.
66
67 This is the simplest encryption mode. Each of the plaintext blocks
68 is directly encrypted into a ciphertext block, independently of
69 any other block.
70
71 This mode is dangerous because it exposes frequency of symbols
72 in your plaintext. Other modes (e.g. *CBC*) should be used instead.
73
74 See `NIST SP800-38A`_ , Section 6.1.
75
76 .. _`NIST SP800-38A` : http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
77
78 :undocumented: __init__
79 """
80
81 def __init__(self, block_cipher: Any) -> None:
82 """Create a new block cipher, configured in ECB mode.
83
84 :Parameters:
85 block_cipher : C pointer
86 A smart pointer to the low-level block cipher instance.
87 """
88 self.block_size = block_cipher.block_size
89
90 state = VoidPointer()
91 result = raw_ecb_lib.ECB_start_operation(block_cipher.get(), state.address_of())
92 if result:
93 raise ValueError("Error %d while instantiating the ECB mode" % result)
94
95 # Ensure that object disposal of this Python object will (eventually)
96 # free the memory allocated by the raw library for the cipher
97 # mode
98 self._state = SmartPointer(state.get(), raw_ecb_lib.ECB_stop_operation)
99
100 # Memory allocated for the underlying block cipher is now owned
101 # by the cipher mode
102 block_cipher.release()
103
104 @overload
105 def encrypt(self, plaintext: Buffer) -> bytes: ...
106
107 @overload
108 def encrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ...
109
110 def encrypt(
111 self, plaintext: Buffer, output: Optional[Union[bytearray, memoryview]] = None
112 ) -> Optional[bytes]:
113 """Encrypt data with the key set at initialization.
114
115 The data to encrypt can be broken up in two or
116 more pieces and `encrypt` can be called multiple times.
117
118 That is, the statement:
119
120 >>> c.encrypt(a) + c.encrypt(b)
121
122 is equivalent to:
123
124 >>> c.encrypt(a+b)
125
126 This function does not add any padding to the plaintext.
127
128 :Parameters:
129 plaintext : bytes/bytearray/memoryview
130 The piece of data to encrypt.
131 The length must be multiple of the cipher block length.
132 :Keywords:
133 output : bytearray/memoryview
134 The location where the ciphertext must be written to.
135 If ``None``, the ciphertext is returned.
136 :Return:
137 If ``output`` is ``None``, the ciphertext is returned as ``bytes``.
138 Otherwise, ``None``.
139 """
140
141 if output is None:
142 ciphertext = create_output_buffer(len(plaintext))
143 else:
144 ciphertext = output
145
146 if not is_writeable_buffer(output):
147 raise TypeError("output must be a bytearray or a writeable memoryview")
148
149 if len(plaintext) != len(output):
150 raise ValueError("output must have the same length as the input (%d bytes)" % len(plaintext))
151
152 with c_uint8_ptr_out(ciphertext) as ciphertext_ptr:
153 plaintext_ptr, plaintext_len = c_uint8_ptr_len(plaintext)
154 # Check the lengths of the buffers that C code gets
155 if len(ciphertext_ptr) != plaintext_len:
156 raise ValueError("output must have the same length as the input (%d bytes)" % plaintext_len)
157 result = raw_ecb_lib.ECB_encrypt(
158 self._state.get(), plaintext_ptr, ciphertext_ptr, c_size_t(plaintext_len)
159 )
160 if result:
161 if result == 3:
162 raise ValueError("Data must be aligned to block boundary in ECB mode")
163 raise ValueError("Error %d while encrypting in ECB mode" % result)
164
165 if output is None:
166 return get_raw_buffer(ciphertext)
167 else:
168 return None
169
170 @overload
171 def decrypt(self, ciphertext: Buffer) -> bytes: ...
172
173 @overload
174 def decrypt(self, ciphertext: Buffer, output: Union[bytearray, memoryview]) -> None: ...
175
176 def decrypt(
177 self, ciphertext: Buffer, output: Optional[Union[bytearray, memoryview]] = None
178 ) -> Optional[bytes]:
179 """Decrypt data with the key set at initialization.
180
181 The data to decrypt can be broken up in two or
182 more pieces and `decrypt` can be called multiple times.
183
184 That is, the statement:
185
186 >>> c.decrypt(a) + c.decrypt(b)
187
188 is equivalent to:
189
190 >>> c.decrypt(a+b)
191
192 This function does not remove any padding from the plaintext.
193
194 :Parameters:
195 ciphertext : bytes/bytearray/memoryview
196 The piece of data to decrypt.
197 The length must be multiple of the cipher block length.
198 :Keywords:
199 output : bytearray/memoryview
200 The location where the plaintext must be written to.
201 If ``None``, the plaintext is returned.
202 :Return:
203 If ``output`` is ``None``, the plaintext is returned as ``bytes``.
204 Otherwise, ``None``.
205 """
206
207 if output is None:
208 plaintext = create_output_buffer(len(ciphertext))
209 else:
210 plaintext = output
211
212 if not is_writeable_buffer(output):
213 raise TypeError("output must be a bytearray or a writeable memoryview")
214
215 if len(ciphertext) != len(output):
216 raise ValueError("output must have the same length as the input (%d bytes)" % len(plaintext))
217
218 with c_uint8_ptr_out(plaintext) as plaintext_ptr:
219 ciphertext_ptr, ciphertext_len = c_uint8_ptr_len(ciphertext)
220 # Check the lengths of the buffers that C code gets
221 if len(plaintext_ptr) != ciphertext_len:
222 raise ValueError("output must have the same length as the input (%d bytes)" % ciphertext_len)
223 result = raw_ecb_lib.ECB_decrypt(
224 self._state.get(), ciphertext_ptr, plaintext_ptr, c_size_t(ciphertext_len)
225 )
226 if result:
227 if result == 3:
228 raise ValueError("Data must be aligned to block boundary in ECB mode")
229 raise ValueError("Error %d while decrypting in ECB mode" % result)
230
231 if output is None:
232 return get_raw_buffer(plaintext)
233 else:
234 return None
235
236
237def _create_ecb_cipher(factory, **kwargs):
238 """Instantiate a cipher object that performs ECB encryption/decryption.
239
240 :Parameters:
241 factory : module
242 The underlying block cipher, a module from ``Crypto.Cipher``.
243
244 All keywords are passed to the underlying block cipher.
245 See the relevant documentation for details (at least ``key`` will need
246 to be present"""
247
248 cipher_state = factory._create_base_cipher(kwargs)
249 cipher_state.block_size = factory.block_size
250 if kwargs:
251 raise TypeError("Unknown parameters for ECB: %s" % str(kwargs))
252 return EcbMode(cipher_state)