diff options
author | Determinant <[email protected]> | 2022-11-17 18:08:59 -0800 |
---|---|---|
committer | Determinant <[email protected]> | 2022-11-17 18:08:59 -0800 |
commit | 8154806fe2fccacdc3dafaa68181a07bcf8d6c4c (patch) | |
tree | f477e6a005599bb88c18db142c267b9297c6060b /frozen_deps/Cryptodome | |
parent | be4dc086591c9bced04a507d127c83811c5700c4 (diff) |
v0.1.7
Diffstat (limited to 'frozen_deps/Cryptodome')
107 files changed, 2982 insertions, 635 deletions
diff --git a/frozen_deps/Cryptodome/Cipher/AES.py b/frozen_deps/Cryptodome/Cipher/AES.py index dd2671a..566a207 100644 --- a/frozen_deps/Cryptodome/Cipher/AES.py +++ b/frozen_deps/Cryptodome/Cipher/AES.py @@ -111,7 +111,7 @@ def _create_base_cipher(dict_parameters): def _derive_Poly1305_key_pair(key, nonce): """Derive a tuple (r, s, nonce) for a Poly1305 MAC. - + If nonce is ``None``, a new 16-byte nonce is generated. """ @@ -180,7 +180,7 @@ def new(key, mode, *args, **kwargs): For ``MODE_CTR``, its length must be in the range **[0..15]** (recommended: **8**). - + For ``MODE_SIV``, the nonce is optional, if it is not specified, then no nonce is being used, which renders the encryption deterministic. diff --git a/frozen_deps/Cryptodome/Cipher/ChaCha20.py b/frozen_deps/Cryptodome/Cipher/ChaCha20.py index 0cd9102..b4f8b5f 100644 --- a/frozen_deps/Cryptodome/Cipher/ChaCha20.py +++ b/frozen_deps/Cryptodome/Cipher/ChaCha20.py @@ -94,6 +94,8 @@ class ChaCha20Cipher(object): See also `new()` at the module level.""" + self.nonce = _copy_bytes(None, None, nonce) + # XChaCha20 requires a key derivation with HChaCha20 # See 2.3 in https://tools.ietf.org/html/draft-arciszewski-xchacha-03 if len(nonce) == 24: @@ -102,8 +104,7 @@ class ChaCha20Cipher(object): self._name = "XChaCha20" else: self._name = "ChaCha20" - - self.nonce = _copy_bytes(None, None, nonce) + nonce = self.nonce self._next = ( self.encrypt, self.decrypt ) @@ -112,7 +113,7 @@ class ChaCha20Cipher(object): self._state.address_of(), c_uint8_ptr(key), c_size_t(len(key)), - self.nonce, + nonce, c_size_t(len(nonce))) if result: raise ValueError("Error %d instantiating a %s cipher" % (result, diff --git a/frozen_deps/Cryptodome/Cipher/PKCS1_OAEP.py b/frozen_deps/Cryptodome/Cipher/PKCS1_OAEP.py index 3207bbe..7525c5d 100644 --- a/frozen_deps/Cryptodome/Cipher/PKCS1_OAEP.py +++ b/frozen_deps/Cryptodome/Cipher/PKCS1_OAEP.py @@ -188,9 +188,9 @@ class PKCS1OAEP_Cipher: # Step 3f db = strxor(maskedDB, dbMask) # Step 3g - one_pos = db[hLen:].find(b'\x01') + one_pos = hLen + db[hLen:].find(b'\x01') lHash1 = db[:hLen] - invalid = bord(y) | int(one_pos < 0) + invalid = bord(y) | int(one_pos < hLen) hash_compare = strxor(lHash1, lHash) for x in hash_compare: invalid |= bord(x) @@ -199,7 +199,7 @@ class PKCS1OAEP_Cipher: if invalid != 0: raise ValueError("Incorrect decryption.") # Step 4 - return db[hLen + one_pos + 1:] + return db[one_pos + 1:] def new(key, hashAlgo=None, mgfunc=None, label=b'', randfunc=None): """Return a cipher object :class:`PKCS1OAEP_Cipher` that can be used to perform PKCS#1 OAEP encryption or decryption. diff --git a/frozen_deps/Cryptodome/Cipher/PKCS1_v1_5.py b/frozen_deps/Cryptodome/Cipher/PKCS1_v1_5.py index 1fd1626..17ef9eb 100644 --- a/frozen_deps/Cryptodome/Cipher/PKCS1_v1_5.py +++ b/frozen_deps/Cryptodome/Cipher/PKCS1_v1_5.py @@ -20,12 +20,37 @@ # SOFTWARE. # =================================================================== -__all__ = [ 'new', 'PKCS115_Cipher' ] +__all__ = ['new', 'PKCS115_Cipher'] -from Cryptodome.Util.number import ceil_div, bytes_to_long, long_to_bytes -from Cryptodome.Util.py3compat import bord, _copy_bytes -import Cryptodome.Util.number from Cryptodome import Random +from Cryptodome.Util.number import bytes_to_long, long_to_bytes +from Cryptodome.Util.py3compat import bord, is_bytes, _copy_bytes + +from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, c_size_t, + c_uint8_ptr) + + +_raw_pkcs1_decode = load_pycryptodome_raw_lib("Cryptodome.Cipher._pkcs1_decode", + """ + int pkcs1_decode(const uint8_t *em, size_t len_em, + const uint8_t *sentinel, size_t len_sentinel, + size_t expected_pt_len, + uint8_t *output); + """) + + +def _pkcs1_decode(em, sentinel, expected_pt_len, output): + if len(em) != len(output): + raise ValueError("Incorrect output length") + + ret = _raw_pkcs1_decode.pkcs1_decode(c_uint8_ptr(em), + c_size_t(len(em)), + c_uint8_ptr(sentinel), + c_size_t(len(sentinel)), + c_size_t(expected_pt_len), + c_uint8_ptr(output)) + return ret + class PKCS115_Cipher: """This cipher can perform PKCS#1 v1.5 RSA encryption or decryption. @@ -74,8 +99,7 @@ class PKCS115_Cipher: """ # See 7.2.1 in RFC8017 - modBits = Cryptodome.Util.number.size(self._key.n) - k = ceil_div(modBits,8) # Convert from bits to bytes + k = self._key.size_in_bytes() mLen = len(message) # Step 1 @@ -100,81 +124,76 @@ class PKCS115_Cipher: c = long_to_bytes(m_int, k) return c - def decrypt(self, ciphertext, sentinel): + def decrypt(self, ciphertext, sentinel, expected_pt_len=0): r"""Decrypt a PKCS#1 v1.5 ciphertext. - This function is named ``RSAES-PKCS1-V1_5-DECRYPT``, and is specified in + This is the function ``RSAES-PKCS1-V1_5-DECRYPT`` specified in `section 7.2.2 of RFC8017 <https://tools.ietf.org/html/rfc8017#page-29>`_. - :param ciphertext: + Args: + ciphertext (bytes/bytearray/memoryview): The ciphertext that contains the message to recover. - :type ciphertext: bytes/bytearray/memoryview - - :param sentinel: + sentinel (any type): The object to return whenever an error is detected. - :type sentinel: any type - - :Returns: A byte string. It is either the original message or the ``sentinel`` (in case of an error). + expected_pt_len (integer): + The length the plaintext is known to have, or 0 if unknown. - :Raises ValueError: - If the ciphertext length is incorrect - :Raises TypeError: - If the RSA key has no private half (i.e. it cannot be used for - decyption). + Returns (byte string): + It is either the original message or the ``sentinel`` (in case of an error). .. warning:: - You should **never** let the party who submitted the ciphertext know that - this function returned the ``sentinel`` value. - Armed with such knowledge (for a fair amount of carefully crafted but invalid ciphertexts), - an attacker is able to recontruct the plaintext of any other encryption that were carried out - with the same RSA public key (see `Bleichenbacher's`__ attack). - - In general, it should not be possible for the other party to distinguish - whether processing at the server side failed because the value returned - was a ``sentinel`` as opposed to a random, invalid message. - - In fact, the second option is not that unlikely: encryption done according to PKCS#1 v1.5 - embeds no good integrity check. There is roughly one chance - in 2\ :sup:`16` for a random ciphertext to be returned as a valid message - (although random looking). - - It is therefore advisabled to: - - 1. Select as ``sentinel`` a value that resembles a plausable random, invalid message. - 2. Not report back an error as soon as you detect a ``sentinel`` value. - Put differently, you should not explicitly check if the returned value is the ``sentinel`` or not. - 3. Cover all possible errors with a single, generic error indicator. - 4. Embed into the definition of ``message`` (at the protocol level) a digest (e.g. ``SHA-1``). - It is recommended for it to be the rightmost part ``message``. - 5. Where possible, monitor the number of errors due to ciphertexts originating from the same party, - and slow down the rate of the requests from such party (or even blacklist it altogether). - - **If you are designing a new protocol, consider using the more robust PKCS#1 OAEP.** - - .. __: http://www.bell-labs.com/user/bleichen/papers/pkcs.ps - + PKCS#1 v1.5 decryption is intrinsically vulnerable to timing + attacks (see `Bleichenbacher's`__ attack). + **Use PKCS#1 OAEP instead**. + + This implementation attempts to mitigate the risk + with some constant-time constructs. + However, they are not sufficient by themselves: the type of protocol you + implement and the way you handle errors make a big difference. + + Specifically, you should make it very hard for the (malicious) + party that submitted the ciphertext to quickly understand if decryption + succeeded or not. + + To this end, it is recommended that your protocol only encrypts + plaintexts of fixed length (``expected_pt_len``), + that ``sentinel`` is a random byte string of the same length, + and that processing continues for as long + as possible even if ``sentinel`` is returned (i.e. in case of + incorrect decryption). + + .. __: https://dx.doi.org/10.1007/BFb0055716 """ - # See 7.2.1 in RFC3447 - modBits = Cryptodome.Util.number.size(self._key.n) - k = ceil_div(modBits,8) # Convert from bits to bytes + # See 7.2.2 in RFC8017 + k = self._key.size_in_bytes() # Step 1 if len(ciphertext) != k: - raise ValueError("Ciphertext with incorrect length.") + raise ValueError("Ciphertext with incorrect length (not %d bytes)" % k) + # Step 2a (O2SIP) ct_int = bytes_to_long(ciphertext) + # Step 2b (RSADP) m_int = self._key._decrypt(ct_int) + # Complete step 2c (I2OSP) em = long_to_bytes(m_int, k) - # Step 3 - sep = em.find(b'\x00', 2) - if not em.startswith(b'\x00\x02') or sep < 10: - return sentinel - # Step 4 - return em[sep + 1:] + + # Step 3 (not constant time when the sentinel is not a byte string) + output = bytes(bytearray(k)) + if not is_bytes(sentinel) or len(sentinel) > k: + size = _pkcs1_decode(em, b'', expected_pt_len, output) + if size < 0: + return sentinel + else: + return output[size:] + + # Step 3 (somewhat constant time) + size = _pkcs1_decode(em, sentinel, expected_pt_len, output) + return output[size:] def new(key, randfunc=None): @@ -196,4 +215,3 @@ def new(key, randfunc=None): if randfunc is None: randfunc = Random.get_random_bytes return PKCS115_Cipher(key, randfunc) - diff --git a/frozen_deps/Cryptodome/Cipher/PKCS1_v1_5.pyi b/frozen_deps/Cryptodome/Cipher/PKCS1_v1_5.pyi index ff4e3f2..b69f509 100644 --- a/frozen_deps/Cryptodome/Cipher/PKCS1_v1_5.pyi +++ b/frozen_deps/Cryptodome/Cipher/PKCS1_v1_5.pyi @@ -1,8 +1,9 @@ -from typing import Callable, Union, Any, Optional +from typing import Callable, Union, Any, Optional, TypeVar from Cryptodome.PublicKey.RSA import RsaKey Buffer = Union[bytes, bytearray, memoryview] +T = TypeVar('T') class PKCS115_Cipher: def __init__(self, @@ -11,7 +12,9 @@ class PKCS115_Cipher: def can_encrypt(self) -> bool: ... def can_decrypt(self) -> bool: ... def encrypt(self, message: Buffer) -> bytes: ... - def decrypt(self, ciphertext: Buffer) -> bytes: ... + def decrypt(self, ciphertext: Buffer, + sentinel: T, + expected_pt_len: Optional[int] = ...) -> Union[bytes, T]: ... def new(key: RsaKey, randfunc: Optional[Callable[[int], bytes]] = ...) -> PKCS115_Cipher: ... diff --git a/frozen_deps/Cryptodome/Cipher/_ARC4.abi3.so b/frozen_deps/Cryptodome/Cipher/_ARC4.abi3.so Binary files differnew file mode 100755 index 0000000..c367472 --- /dev/null +++ b/frozen_deps/Cryptodome/Cipher/_ARC4.abi3.so diff --git a/frozen_deps/Cryptodome/Cipher/_Salsa20.abi3.so b/frozen_deps/Cryptodome/Cipher/_Salsa20.abi3.so Binary files differnew file mode 100755 index 0000000..10ba4b7 --- /dev/null +++ b/frozen_deps/Cryptodome/Cipher/_Salsa20.abi3.so diff --git a/frozen_deps/Cryptodome/Cipher/_chacha20.abi3.so b/frozen_deps/Cryptodome/Cipher/_chacha20.abi3.so Binary files differnew file mode 100755 index 0000000..316d6cb --- /dev/null +++ b/frozen_deps/Cryptodome/Cipher/_chacha20.abi3.so diff --git a/frozen_deps/Cryptodome/Cipher/_mode_ctr.py b/frozen_deps/Cryptodome/Cipher/_mode_ctr.py index 99712d0..74783ec 100644 --- a/frozen_deps/Cryptodome/Cipher/_mode_ctr.py +++ b/frozen_deps/Cryptodome/Cipher/_mode_ctr.py @@ -184,15 +184,15 @@ class CtrMode(object): if self.encrypt not in self._next: raise TypeError("encrypt() cannot be called after decrypt()") self._next = [self.encrypt] - + if output is None: ciphertext = create_string_buffer(len(plaintext)) else: ciphertext = output - + if not is_writeable_buffer(output): raise TypeError("output must be a bytearray or a writeable memoryview") - + if len(plaintext) != len(output): raise ValueError("output must have the same length as the input" " (%d bytes)" % len(plaintext)) @@ -206,7 +206,7 @@ class CtrMode(object): raise OverflowError("The counter has wrapped around in" " CTR mode") raise ValueError("Error %X while encrypting in CTR mode" % result) - + if output is None: return get_raw_buffer(ciphertext) else: @@ -248,7 +248,7 @@ class CtrMode(object): if self.decrypt not in self._next: raise TypeError("decrypt() cannot be called after encrypt()") self._next = [self.decrypt] - + if output is None: plaintext = create_string_buffer(len(ciphertext)) else: @@ -256,12 +256,11 @@ class CtrMode(object): if not is_writeable_buffer(output): raise TypeError("output must be a bytearray or a writeable memoryview") - + if len(ciphertext) != len(output): raise ValueError("output must have the same length as the input" " (%d bytes)" % len(plaintext)) - result = raw_ctr_lib.CTR_decrypt(self._state.get(), c_uint8_ptr(ciphertext), c_uint8_ptr(plaintext), @@ -271,7 +270,7 @@ class CtrMode(object): raise OverflowError("The counter has wrapped around in" " CTR mode") raise ValueError("Error %X while decrypting in CTR mode" % result) - + if output is None: return get_raw_buffer(plaintext) else: @@ -324,8 +323,8 @@ def _create_ctr_cipher(factory, **kwargs): raise TypeError("Invalid parameters for CTR mode: %s" % str(kwargs)) if counter is not None and (nonce, initial_value) != (None, None): - raise TypeError("'counter' and 'nonce'/'initial_value'" - " are mutually exclusive") + raise TypeError("'counter' and 'nonce'/'initial_value'" + " are mutually exclusive") if counter is None: # Cryptodome.Util.Counter is not used @@ -337,7 +336,7 @@ def _create_ctr_cipher(factory, **kwargs): else: if len(nonce) >= factory.block_size: raise ValueError("Nonce is too long") - + # What is not nonce is counter counter_len = factory.block_size - len(nonce) @@ -350,7 +349,8 @@ def _create_ctr_cipher(factory, **kwargs): initial_counter_block = nonce + long_to_bytes(initial_value, counter_len) else: if len(initial_value) != counter_len: - raise ValueError("Incorrect length for counter byte string (%d bytes, expected %d)" % (len(initial_value), counter_len)) + raise ValueError("Incorrect length for counter byte string (%d bytes, expected %d)" % + (len(initial_value), counter_len)) initial_counter_block = nonce + initial_value return CtrMode(cipher_state, @@ -379,7 +379,7 @@ def _create_ctr_cipher(factory, **kwargs): while initial_value > 0: words.append(struct.pack('B', initial_value & 255)) initial_value >>= 8 - words += [ b'\x00' ] * max(0, counter_len - len(words)) + words += [b'\x00'] * max(0, counter_len - len(words)) if not little_endian: words.reverse() initial_counter_block = prefix + b"".join(words) + suffix diff --git a/frozen_deps/Cryptodome/Cipher/_mode_ecb.py b/frozen_deps/Cryptodome/Cipher/_mode_ecb.py index 4c381f7..a01a16f 100644 --- a/frozen_deps/Cryptodome/Cipher/_mode_ecb.py +++ b/frozen_deps/Cryptodome/Cipher/_mode_ecb.py @@ -72,6 +72,7 @@ class EcbMode(object): block_cipher : C pointer A smart pointer to the low-level block cipher instance. """ + self.block_size = block_cipher.block_size self._state = VoidPointer() result = raw_ecb_lib.ECB_start_operation(block_cipher.get(), @@ -213,6 +214,7 @@ def _create_ecb_cipher(factory, **kwargs): to be present""" cipher_state = factory._create_base_cipher(kwargs) + cipher_state.block_size = factory.block_size if kwargs: raise TypeError("Unknown parameters for ECB: %s" % str(kwargs)) return EcbMode(cipher_state) diff --git a/frozen_deps/Cryptodome/Cipher/_pkcs1_decode.abi3.so b/frozen_deps/Cryptodome/Cipher/_pkcs1_decode.abi3.so Binary files differnew file mode 100755 index 0000000..cbb4a6f --- /dev/null +++ b/frozen_deps/Cryptodome/Cipher/_pkcs1_decode.abi3.so diff --git a/frozen_deps/Cryptodome/Cipher/_raw_aes.abi3.so b/frozen_deps/Cryptodome/Cipher/_raw_aes.abi3.so Binary files differnew file mode 100755 index 0000000..883fc94 --- /dev/null +++ b/frozen_deps/Cryptodome/Cipher/_raw_aes.abi3.so diff --git a/frozen_deps/Cryptodome/Cipher/_raw_aesni.abi3.so b/frozen_deps/Cryptodome/Cipher/_raw_aesni.abi3.so Binary files differnew file mode 100755 index 0000000..8e971c8 --- /dev/null +++ b/frozen_deps/Cryptodome/Cipher/_raw_aesni.abi3.so diff --git a/frozen_deps/Cryptodome/Cipher/_raw_arc2.abi3.so b/frozen_deps/Cryptodome/Cipher/_raw_arc2.abi3.so Binary files differnew file mode 100755 index 0000000..2370b62 --- /dev/null +++ b/frozen_deps/Cryptodome/Cipher/_raw_arc2.abi3.so diff --git a/frozen_deps/Cryptodome/Cipher/_raw_blowfish.abi3.so b/frozen_deps/Cryptodome/Cipher/_raw_blowfish.abi3.so Binary files differnew file mode 100755 index 0000000..48af6a3 --- /dev/null +++ b/frozen_deps/Cryptodome/Cipher/_raw_blowfish.abi3.so diff --git a/frozen_deps/Cryptodome/Cipher/_raw_cast.abi3.so b/frozen_deps/Cryptodome/Cipher/_raw_cast.abi3.so Binary files differnew file mode 100755 index 0000000..7efb7e7 --- /dev/null +++ b/frozen_deps/Cryptodome/Cipher/_raw_cast.abi3.so diff --git a/frozen_deps/Cryptodome/Cipher/_raw_cbc.abi3.so b/frozen_deps/Cryptodome/Cipher/_raw_cbc.abi3.so Binary files differnew file mode 100755 index 0000000..0696380 --- /dev/null +++ b/frozen_deps/Cryptodome/Cipher/_raw_cbc.abi3.so diff --git a/frozen_deps/Cryptodome/Cipher/_raw_cfb.abi3.so b/frozen_deps/Cryptodome/Cipher/_raw_cfb.abi3.so Binary files differnew file mode 100755 index 0000000..32d333a --- /dev/null +++ b/frozen_deps/Cryptodome/Cipher/_raw_cfb.abi3.so diff --git a/frozen_deps/Cryptodome/Cipher/_raw_ctr.abi3.so b/frozen_deps/Cryptodome/Cipher/_raw_ctr.abi3.so Binary files differnew file mode 100755 index 0000000..1810b56 --- /dev/null +++ b/frozen_deps/Cryptodome/Cipher/_raw_ctr.abi3.so diff --git a/frozen_deps/Cryptodome/Cipher/_raw_des.abi3.so b/frozen_deps/Cryptodome/Cipher/_raw_des.abi3.so Binary files differnew file mode 100755 index 0000000..01a2495 --- /dev/null +++ b/frozen_deps/Cryptodome/Cipher/_raw_des.abi3.so diff --git a/frozen_deps/Cryptodome/Cipher/_raw_des3.abi3.so b/frozen_deps/Cryptodome/Cipher/_raw_des3.abi3.so Binary files differnew file mode 100755 index 0000000..ec932c1 --- /dev/null +++ b/frozen_deps/Cryptodome/Cipher/_raw_des3.abi3.so diff --git a/frozen_deps/Cryptodome/Cipher/_raw_ecb.abi3.so b/frozen_deps/Cryptodome/Cipher/_raw_ecb.abi3.so Binary files differnew file mode 100755 index 0000000..7dc6a67 --- /dev/null +++ b/frozen_deps/Cryptodome/Cipher/_raw_ecb.abi3.so diff --git a/frozen_deps/Cryptodome/Cipher/_raw_eksblowfish.abi3.so b/frozen_deps/Cryptodome/Cipher/_raw_eksblowfish.abi3.so Binary files differnew file mode 100755 index 0000000..43734c9 --- /dev/null +++ b/frozen_deps/Cryptodome/Cipher/_raw_eksblowfish.abi3.so diff --git a/frozen_deps/Cryptodome/Cipher/_raw_ocb.abi3.so b/frozen_deps/Cryptodome/Cipher/_raw_ocb.abi3.so Binary files differnew file mode 100755 index 0000000..50e0016 --- /dev/null +++ b/frozen_deps/Cryptodome/Cipher/_raw_ocb.abi3.so diff --git a/frozen_deps/Cryptodome/Cipher/_raw_ofb.abi3.so b/frozen_deps/Cryptodome/Cipher/_raw_ofb.abi3.so Binary files differnew file mode 100755 index 0000000..e0db6db --- /dev/null +++ b/frozen_deps/Cryptodome/Cipher/_raw_ofb.abi3.so diff --git a/frozen_deps/Cryptodome/Hash/BLAKE2b.pyi b/frozen_deps/Cryptodome/Hash/BLAKE2b.pyi index ac3bf57..d37c374 100644 --- a/frozen_deps/Cryptodome/Hash/BLAKE2b.pyi +++ b/frozen_deps/Cryptodome/Hash/BLAKE2b.pyi @@ -1,4 +1,5 @@ from typing import Any, Union +from types import ModuleType Buffer = Union[bytes, bytearray, memoryview] diff --git a/frozen_deps/Cryptodome/Hash/CMAC.py b/frozen_deps/Cryptodome/Hash/CMAC.py index df69f00..e831700 100644 --- a/frozen_deps/Cryptodome/Hash/CMAC.py +++ b/frozen_deps/Cryptodome/Hash/CMAC.py @@ -20,7 +20,6 @@ # SOFTWARE. # =================================================================== -import sys from binascii import unhexlify from Cryptodome.Hash import BLAKE2s @@ -29,8 +28,6 @@ from Cryptodome.Util.number import long_to_bytes, bytes_to_long from Cryptodome.Util.py3compat import bord, tobytes, _copy_bytes from Cryptodome.Random import get_random_bytes -if sys.version_info[:2] == (2, 6): - memoryview = bytes # The size of the authentication tag produced by the MAC. digest_size = None diff --git a/frozen_deps/Cryptodome/Hash/CMAC.pyi b/frozen_deps/Cryptodome/Hash/CMAC.pyi index 33773aa..acdf055 100644 --- a/frozen_deps/Cryptodome/Hash/CMAC.pyi +++ b/frozen_deps/Cryptodome/Hash/CMAC.pyi @@ -1,5 +1,5 @@ from types import ModuleType -from typing import Union, Dict +from typing import Union, Dict, Any Buffer = Union[bytes, bytearray, memoryview] @@ -12,7 +12,7 @@ class CMAC(object): key: Buffer, msg: Buffer, ciphermod: ModuleType, - cipher_params: dict, + cipher_params: Dict[str, Any], mac_len: int, update_after_digest: bool) -> None: ... def update(self, data: Buffer) -> CMAC: ... def copy(self) -> CMAC: ... @@ -25,6 +25,6 @@ class CMAC(object): def new(key: Buffer, msg: Buffer = ..., ciphermod: ModuleType = ..., - cipher_params: Dict = ..., + cipher_params: Dict[str, Any] = ..., mac_len: int = ..., update_after_digest: bool = ...) -> CMAC: ... diff --git a/frozen_deps/Cryptodome/Hash/HMAC.py b/frozen_deps/Cryptodome/Hash/HMAC.py index ac94b20..165dd83 100644 --- a/frozen_deps/Cryptodome/Hash/HMAC.py +++ b/frozen_deps/Cryptodome/Hash/HMAC.py @@ -31,7 +31,7 @@ # POSSIBILITY OF SUCH DAMAGE. # =================================================================== -from Cryptodome.Util.py3compat import bord, tobytes, _memoryview +from Cryptodome.Util.py3compat import bord, tobytes from binascii import unhexlify @@ -64,7 +64,7 @@ class HMAC(object): self._digestmod = digestmod - if isinstance(key, _memoryview): + if isinstance(key, memoryview): key = key.tobytes() try: diff --git a/frozen_deps/Cryptodome/Hash/KMAC128.py b/frozen_deps/Cryptodome/Hash/KMAC128.py new file mode 100644 index 0000000..afd91c4 --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/KMAC128.py @@ -0,0 +1,179 @@ +# =================================================================== +# +# Copyright (c) 2021, Legrandin <[email protected]> +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# =================================================================== + +from binascii import unhexlify + +from Cryptodome.Util.py3compat import bord, tobytes, is_bytes +from Cryptodome.Random import get_random_bytes + +from . import cSHAKE128, SHA3_256 +from .cSHAKE128 import _bytepad, _encode_str, _right_encode + + +class KMAC_Hash(object): + """A KMAC hash object. + Do not instantiate directly. + Use the :func:`new` function. + """ + + def __init__(self, data, key, mac_len, custom, + oid_variant, cshake, rate): + + # See https://tools.ietf.org/html/rfc8702 + self.oid = "2.16.840.1.101.3.4.2." + oid_variant + self.digest_size = mac_len + + self._mac = None + + partial_newX = _bytepad(_encode_str(tobytes(key)), rate) + self._cshake = cshake._new(partial_newX, custom, b"KMAC") + + if data: + self._cshake.update(data) + + def update(self, data): + """Authenticate the next chunk of message. + + Args: + data (bytes/bytearray/memoryview): The next chunk of the message to + authenticate. + """ + + if self._mac: + raise TypeError("You can only call 'digest' or 'hexdigest' on this object") + + self._cshake.update(data) + return self + + def digest(self): + """Return the **binary** (non-printable) MAC tag of the message. + + :return: The MAC tag. Binary form. + :rtype: byte string + """ + + if not self._mac: + self._cshake.update(_right_encode(self.digest_size * 8)) + self._mac = self._cshake.read(self.digest_size) + + return self._mac + + def hexdigest(self): + """Return the **printable** MAC tag of the message. + + :return: The MAC tag. Hexadecimal encoded. + :rtype: string + """ + + return "".join(["%02x" % bord(x) for x in tuple(self.digest())]) + + def verify(self, mac_tag): + """Verify that a given **binary** MAC (computed by another party) + is valid. + + Args: + mac_tag (bytes/bytearray/memoryview): the expected MAC of the message. + + Raises: + ValueError: if the MAC does not match. It means that the message + has been tampered with or that the MAC key is incorrect. + """ + + secret = get_random_bytes(16) + + mac1 = SHA3_256.new(secret + mac_tag) + mac2 = SHA3_256.new(secret + self.digest()) + + if mac1.digest() != mac2.digest(): + raise ValueError("MAC check failed") + + def hexverify(self, hex_mac_tag): + """Verify that a given **printable** MAC (computed by another party) + is valid. + + Args: + hex_mac_tag (string): the expected MAC of the message, as a hexadecimal string. + + Raises: + ValueError: if the MAC does not match. It means that the message + has been tampered with or that the MAC key is incorrect. + """ + + self.verify(unhexlify(tobytes(hex_mac_tag))) + + def new(self, **kwargs): + """Return a new instance of a KMAC hash object. + See :func:`new`. + """ + + if "mac_len" not in kwargs: + kwargs["mac_len"] = self.digest_size + + return new(**kwargs) + + +def new(**kwargs): + """Create a new KMAC128 object. + + Args: + key (bytes/bytearray/memoryview): + The key to use to compute the MAC. + It must be at least 128 bits long (16 bytes). + data (bytes/bytearray/memoryview): + Optional. The very first chunk of the message to authenticate. + It is equivalent to an early call to :meth:`KMAC_Hash.update`. + mac_len (integer): + Optional. The size of the authentication tag, in bytes. + Default is 64. Minimum is 8. + custom (bytes/bytearray/memoryview): + Optional. A customization byte string (``S`` in SP 800-185). + + Returns: + A :class:`KMAC_Hash` hash object + """ + + key = kwargs.pop("key", None) + if not is_bytes(key): + raise TypeError("You must pass a key to KMAC128") + if len(key) < 16: + raise ValueError("The key must be at least 128 bits long (16 bytes)") + + data = kwargs.pop("data", None) + + mac_len = kwargs.pop("mac_len", 64) + if mac_len < 8: + raise ValueError("'mac_len' must be 8 bytes or more") + + custom = kwargs.pop("custom", b"") + + if kwargs: + raise TypeError("Unknown parameters: " + str(kwargs)) + + return KMAC_Hash(data, key, mac_len, custom, "19", cSHAKE128, 168) diff --git a/frozen_deps/Cryptodome/Hash/KMAC128.pyi b/frozen_deps/Cryptodome/Hash/KMAC128.pyi new file mode 100644 index 0000000..8947dab --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/KMAC128.pyi @@ -0,0 +1,33 @@ +from typing import Union +from types import ModuleType + +Buffer = Union[bytes, bytearray, memoryview] + +class KMAC_Hash(object): + + def __init__(self, + data: Buffer, + key: Buffer, + mac_len: int, + custom: Buffer, + oid_variant: str, + cshake: ModuleType, + rate: int) -> None: ... + + def update(self, data: Buffer) -> KMAC_Hash: ... + + def digest(self) -> bytes: ... + def hexdigest(self) -> str: ... + def verify(self, mac_tag: Buffer) -> None: ... + def hexverify(self, hex_mac_tag: str) -> None: ... + def new(self, + data: Buffer = ..., + mac_len: int = ..., + key: Buffer = ..., + custom: Buffer = ...) -> KMAC_Hash: ... + + +def new(key: Buffer, + data: Buffer = ..., + mac_len: int = ..., + custom: Buffer = ...) -> KMAC_Hash: ... diff --git a/frozen_deps/Cryptodome/Hash/KMAC256.py b/frozen_deps/Cryptodome/Hash/KMAC256.py new file mode 100644 index 0000000..82da062 --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/KMAC256.py @@ -0,0 +1,74 @@ +# =================================================================== +# +# Copyright (c) 2021, Legrandin <[email protected]> +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# =================================================================== + +from Cryptodome.Util.py3compat import is_bytes + +from .KMAC128 import KMAC_Hash +from . import cSHAKE256 + + +def new(**kwargs): + """Create a new KMAC256 object. + + Args: + key (bytes/bytearray/memoryview): + The key to use to compute the MAC. + It must be at least 256 bits long (32 bytes). + data (bytes/bytearray/memoryview): + Optional. The very first chunk of the message to authenticate. + It is equivalent to an early call to :meth:`KMAC_Hash.update`. + mac_len (integer): + Optional. The size of the authentication tag, in bytes. + Default is 64. Minimum is 8. + custom (bytes/bytearray/memoryview): + Optional. A customization byte string (``S`` in SP 800-185). + + Returns: + A :class:`KMAC_Hash` hash object + """ + + key = kwargs.pop("key", None) + if not is_bytes(key): + raise TypeError("You must pass a key to KMAC256") + if len(key) < 32: + raise ValueError("The key must be at least 256 bits long (32 bytes)") + + data = kwargs.pop("data", None) + + mac_len = kwargs.pop("mac_len", 64) + if mac_len < 8: + raise ValueError("'mac_len' must be 8 bytes or more") + + custom = kwargs.pop("custom", b"") + + if kwargs: + raise TypeError("Unknown parameters: " + str(kwargs)) + + return KMAC_Hash(data, key, mac_len, custom, "20", cSHAKE256, 136) diff --git a/frozen_deps/Cryptodome/Hash/KMAC256.pyi b/frozen_deps/Cryptodome/Hash/KMAC256.pyi new file mode 100644 index 0000000..86cc500 --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/KMAC256.pyi @@ -0,0 +1,10 @@ +from typing import Union + +from .KMAC128 import KMAC_Hash + +Buffer = Union[bytes, bytearray, memoryview] + +def new(key: Buffer, + data: Buffer = ..., + mac_len: int = ..., + custom: Buffer = ...) -> KMAC_Hash: ... diff --git a/frozen_deps/Cryptodome/Hash/KangarooTwelve.py b/frozen_deps/Cryptodome/Hash/KangarooTwelve.py new file mode 100644 index 0000000..44d935f --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/KangarooTwelve.py @@ -0,0 +1,262 @@ +# =================================================================== +# +# Copyright (c) 2021, Legrandin <[email protected]> +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# =================================================================== + +from Cryptodome.Util._raw_api import (VoidPointer, SmartPointer, + create_string_buffer, + get_raw_buffer, c_size_t, + c_uint8_ptr, c_ubyte) + +from Cryptodome.Util.number import long_to_bytes +from Cryptodome.Util.py3compat import bchr + +from .keccak import _raw_keccak_lib + + +def _length_encode(x): + if x == 0: + return b'\x00' + + S = long_to_bytes(x) + return S + bchr(len(S)) + + +# Possible states for a KangarooTwelve instance, which depend on the amount of data processed so far. +SHORT_MSG = 1 # Still within the first 8192 bytes, but it is not certain we will exceed them. +LONG_MSG_S0 = 2 # Still within the first 8192 bytes, and it is certain we will exceed them. +LONG_MSG_SX = 3 # Beyond the first 8192 bytes. +SQUEEZING = 4 # No more data to process. + + +class K12_XOF(object): + """A KangarooTwelve hash object. + Do not instantiate directly. + Use the :func:`new` function. + """ + + def __init__(self, data, custom): + + if custom == None: + custom = b'' + + self._custom = custom + _length_encode(len(custom)) + self._state = SHORT_MSG + self._padding = None # Final padding is only decided in read() + + # Internal hash that consumes FinalNode + self._hash1 = self._create_keccak() + self._length1 = 0 + + # Internal hash that produces CV_i (reset each time) + self._hash2 = None + self._length2 = 0 + + # Incremented by one for each 8192-byte block + self._ctr = 0 + + if data: + self.update(data) + + def _create_keccak(self): + state = VoidPointer() + result = _raw_keccak_lib.keccak_init(state.address_of(), + c_size_t(32), # 32 bytes of capacity (256 bits) + c_ubyte(12)) # Reduced number of rounds + if result: + raise ValueError("Error %d while instantiating KangarooTwelve" + % result) + return SmartPointer(state.get(), _raw_keccak_lib.keccak_destroy) + + def _update(self, data, hash_obj): + result = _raw_keccak_lib.keccak_absorb(hash_obj.get(), + c_uint8_ptr(data), + c_size_t(len(data))) + if result: + raise ValueError("Error %d while updating KangarooTwelve state" + % result) + + def _squeeze(self, hash_obj, length, padding): + bfr = create_string_buffer(length) + result = _raw_keccak_lib.keccak_squeeze(hash_obj.get(), + bfr, + c_size_t(length), + c_ubyte(padding)) + if result: + raise ValueError("Error %d while extracting from KangarooTwelve" + % result) + + return get_raw_buffer(bfr) + + def _reset(self, hash_obj): + result = _raw_keccak_lib.keccak_reset(hash_obj.get()) + if result: + raise ValueError("Error %d while resetting KangarooTwelve state" + % result) + + def update(self, data): + """Hash the next piece of data. + + .. note:: + For better performance, submit chunks with a length multiple of 8192 bytes. + + Args: + data (byte string/byte array/memoryview): The next chunk of the + message to hash. + """ + + if self._state == SQUEEZING: + raise TypeError("You cannot call 'update' after the first 'read'") + + if self._state == SHORT_MSG: + next_length = self._length1 + len(data) + + if next_length + len(self._custom) <= 8192: + self._length1 = next_length + self._update(data, self._hash1) + return self + + # Switch to tree hashing + self._state = LONG_MSG_S0 + + if self._state == LONG_MSG_S0: + data_mem = memoryview(data) + assert(self._length1 < 8192) + dtc = min(len(data), 8192 - self._length1) + self._update(data_mem[:dtc], self._hash1) + self._length1 += dtc + + if self._length1 < 8192: + return self + + # Finish hashing S_0 and start S_1 + assert(self._length1 == 8192) + + divider = b'\x03' + b'\x00' * 7 + self._update(divider, self._hash1) + self._length1 += 8 + + self._hash2 = self._create_keccak() + self._length2 = 0 + self._ctr = 1 + + self._state = LONG_MSG_SX + return self.update(data_mem[dtc:]) + + # LONG_MSG_SX + assert(self._state == LONG_MSG_SX) + index = 0 + len_data = len(data) + + # All iteractions could actually run in parallel + data_mem = memoryview(data) + while index < len_data: + + new_index = min(index + 8192 - self._length2, len_data) + self._update(data_mem[index:new_index], self._hash2) + self._length2 += new_index - index + index = new_index + + if self._length2 == 8192: + cv_i = self._squeeze(self._hash2, 32, 0x0B) + self._update(cv_i, self._hash1) + self._length1 += 32 + self._reset(self._hash2) + self._length2 = 0 + self._ctr += 1 + + return self + + def read(self, length): + """ + Produce more bytes of the digest. + + .. note:: + You cannot use :meth:`update` anymore after the first call to + :meth:`read`. + + Args: + length (integer): the amount of bytes this method must return + + :return: the next piece of XOF output (of the given length) + :rtype: byte string + """ + + custom_was_consumed = False + + if self._state == SHORT_MSG: + self._update(self._custom, self._hash1) + self._padding = 0x07 + self._state = SQUEEZING + + if self._state == LONG_MSG_S0: + self.update(self._custom) + custom_was_consumed = True + assert(self._state == LONG_MSG_SX) + + if self._state == LONG_MSG_SX: + if not custom_was_consumed: + self.update(self._custom) + + # Is there still some leftover data in hash2? + if self._length2 > 0: + cv_i = self._squeeze(self._hash2, 32, 0x0B) + self._update(cv_i, self._hash1) + self._length1 += 32 + self._reset(self._hash2) + self._length2 = 0 + self._ctr += 1 + + trailer = _length_encode(self._ctr - 1) + b'\xFF\xFF' + self._update(trailer, self._hash1) + + self._padding = 0x06 + self._state = SQUEEZING + + return self._squeeze(self._hash1, length, self._padding) + + def new(self, data=None, custom=b''): + return type(self)(data, custom) + + +def new(data=None, custom=None): + """Return a fresh instance of a KangarooTwelve object. + + Args: + data (bytes/bytearray/memoryview): + Optional. + The very first chunk of the message to hash. + It is equivalent to an early call to :meth:`update`. + custom (bytes): + Optional. + A customization byte string. + + :Return: A :class:`K12_XOF` object + """ + + return K12_XOF(data, custom) diff --git a/frozen_deps/Cryptodome/Hash/KangarooTwelve.pyi b/frozen_deps/Cryptodome/Hash/KangarooTwelve.pyi new file mode 100644 index 0000000..8b3fd74 --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/KangarooTwelve.pyi @@ -0,0 +1,16 @@ +from typing import Union, Optional + +Buffer = Union[bytes, bytearray, memoryview] + +class K12_XOF(object): + def __init__(self, + data: Optional[Buffer] = ..., + custom: Optional[bytes] = ...) -> None: ... + def update(self, data: Buffer) -> K12_XOF: ... + def read(self, length: int) -> bytes: ... + def new(self, + data: Optional[Buffer] = ..., + custom: Optional[bytes] = ...) -> None: ... + +def new(data: Optional[Buffer] = ..., + custom: Optional[Buffer] = ...) -> K12_XOF: ... diff --git a/frozen_deps/Cryptodome/Hash/SHA3_224.py b/frozen_deps/Cryptodome/Hash/SHA3_224.py index 3196bd6..34888c5 100644 --- a/frozen_deps/Cryptodome/Hash/SHA3_224.py +++ b/frozen_deps/Cryptodome/Hash/SHA3_224.py @@ -24,7 +24,7 @@ from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer, SmartPointer, create_string_buffer, get_raw_buffer, c_size_t, - c_uint8_ptr) + c_uint8_ptr, c_ubyte) from Cryptodome.Hash.keccak import _raw_keccak_lib @@ -46,14 +46,18 @@ class SHA3_224_Hash(object): # ASN.1 Object ID oid = "2.16.840.1.101.3.4.2.7" + # Input block size for HMAC + block_size = 144 + def __init__(self, data, update_after_digest): self._update_after_digest = update_after_digest self._digest_done = False + self._padding = 0x06 state = VoidPointer() result = _raw_keccak_lib.keccak_init(state.address_of(), c_size_t(self.digest_size * 2), - 0x06) + c_ubyte(24)) if result: raise ValueError("Error %d while instantiating SHA-3/224" % result) @@ -74,7 +78,8 @@ class SHA3_224_Hash(object): result = _raw_keccak_lib.keccak_absorb(self._state.get(), c_uint8_ptr(data), - c_size_t(len(data))) + c_size_t(len(data)) + ) if result: raise ValueError("Error %d while updating SHA-3/224" % result) @@ -93,7 +98,8 @@ class SHA3_224_Hash(object): bfr = create_string_buffer(self.digest_size) result = _raw_keccak_lib.keccak_digest(self._state.get(), bfr, - c_size_t(self.digest_size)) + c_size_t(self.digest_size), + c_ubyte(self._padding)) if result: raise ValueError("Error %d while instantiating SHA-3/224" % result) @@ -111,10 +117,28 @@ class SHA3_224_Hash(object): return "".join(["%02x" % bord(x) for x in self.digest()]) - def new(self): + def copy(self): + """Return a copy ("clone") of the hash object. + + The copy will have the same internal state as the original hash + object. + This can be used to efficiently compute the digests of strings that + share a common initial substring. + + :return: A hash object of the same type + """ + + clone = self.new() + result = _raw_keccak_lib.keccak_copy(self._state.get(), + clone._state.get()) + if result: + raise ValueError("Error %d while copying SHA3-224" % result) + return clone + + def new(self, data=None): """Create a fresh SHA3-224 hash object.""" - return type(self)(None, self._update_after_digest) + return type(self)(data, self._update_after_digest) def new(*args, **kwargs): @@ -145,3 +169,6 @@ def new(*args, **kwargs): # The size of the resulting hash in bytes. digest_size = SHA3_224_Hash.digest_size + +# Input block size for HMAC +block_size = 144 diff --git a/frozen_deps/Cryptodome/Hash/SHA3_224.pyi b/frozen_deps/Cryptodome/Hash/SHA3_224.pyi index 3437042..2180821 100644 --- a/frozen_deps/Cryptodome/Hash/SHA3_224.pyi +++ b/frozen_deps/Cryptodome/Hash/SHA3_224.pyi @@ -4,13 +4,16 @@ Buffer = Union[bytes, bytearray, memoryview] class SHA3_224_Hash(object): digest_size: int + block_size: int oid: str def __init__(self, data: Optional[Buffer], update_after_digest: bool) -> None: ... def update(self, data: Buffer) -> SHA3_224_Hash: ... def digest(self) -> bytes: ... def hexdigest(self) -> str: ... - def new(self) -> SHA3_224_Hash: ... + def copy(self) -> SHA3_224_Hash: ... + def new(self, data: Optional[Buffer]) -> SHA3_224_Hash: ... def new(__data: Buffer = ..., update_after_digest: bool = ...) -> SHA3_224_Hash: ... digest_size: int +block_size: int diff --git a/frozen_deps/Cryptodome/Hash/SHA3_256.py b/frozen_deps/Cryptodome/Hash/SHA3_256.py index 89e3b42..024962f 100644 --- a/frozen_deps/Cryptodome/Hash/SHA3_256.py +++ b/frozen_deps/Cryptodome/Hash/SHA3_256.py @@ -24,7 +24,7 @@ from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer, SmartPointer, create_string_buffer, get_raw_buffer, c_size_t, - c_uint8_ptr) + c_uint8_ptr, c_ubyte) from Cryptodome.Hash.keccak import _raw_keccak_lib @@ -46,14 +46,18 @@ class SHA3_256_Hash(object): # ASN.1 Object ID oid = "2.16.840.1.101.3.4.2.8" + # Input block size for HMAC + block_size = 136 + def __init__(self, data, update_after_digest): self._update_after_digest = update_after_digest self._digest_done = False + self._padding = 0x06 state = VoidPointer() result = _raw_keccak_lib.keccak_init(state.address_of(), c_size_t(self.digest_size * 2), - 0x06) + c_ubyte(24)) if result: raise ValueError("Error %d while instantiating SHA-3/256" % result) @@ -74,7 +78,8 @@ class SHA3_256_Hash(object): result = _raw_keccak_lib.keccak_absorb(self._state.get(), c_uint8_ptr(data), - c_size_t(len(data))) + c_size_t(len(data)) + ) if result: raise ValueError("Error %d while updating SHA-3/256" % result) @@ -93,7 +98,8 @@ class SHA3_256_Hash(object): bfr = create_string_buffer(self.digest_size) result = _raw_keccak_lib.keccak_digest(self._state.get(), bfr, - c_size_t(self.digest_size)) + c_size_t(self.digest_size), + c_ubyte(self._padding)) if result: raise ValueError("Error %d while instantiating SHA-3/256" % result) @@ -111,10 +117,28 @@ class SHA3_256_Hash(object): return "".join(["%02x" % bord(x) for x in self.digest()]) - def new(self): + def copy(self): + """Return a copy ("clone") of the hash object. + + The copy will have the same internal state as the original hash + object. + This can be used to efficiently compute the digests of strings that + share a common initial substring. + + :return: A hash object of the same type + """ + + clone = self.new() + result = _raw_keccak_lib.keccak_copy(self._state.get(), + clone._state.get()) + if result: + raise ValueError("Error %d while copying SHA3-256" % result) + return clone + + def new(self, data=None): """Create a fresh SHA3-256 hash object.""" - return type(self)(None, self._update_after_digest) + return type(self)(data, self._update_after_digest) def new(*args, **kwargs): @@ -145,3 +169,6 @@ def new(*args, **kwargs): # The size of the resulting hash in bytes. digest_size = SHA3_256_Hash.digest_size + +# Input block size for HMAC +block_size = 136 diff --git a/frozen_deps/Cryptodome/Hash/SHA3_256.pyi b/frozen_deps/Cryptodome/Hash/SHA3_256.pyi index c1a07fa..88436bd 100644 --- a/frozen_deps/Cryptodome/Hash/SHA3_256.pyi +++ b/frozen_deps/Cryptodome/Hash/SHA3_256.pyi @@ -4,13 +4,16 @@ Buffer = Union[bytes, bytearray, memoryview] class SHA3_256_Hash(object): digest_size: int + block_size: int oid: str def __init__(self, data: Optional[Buffer], update_after_digest: bool) -> None: ... def update(self, data: Buffer) -> SHA3_256_Hash: ... def digest(self) -> bytes: ... def hexdigest(self) -> str: ... - def new(self) -> SHA3_256_Hash: ... + def copy(self) -> SHA3_256_Hash: ... + def new(self, data: Optional[Buffer]) -> SHA3_256_Hash: ... def new(__data: Buffer = ..., update_after_digest: bool = ...) -> SHA3_256_Hash: ... digest_size: int +block_size: int diff --git a/frozen_deps/Cryptodome/Hash/SHA3_384.py b/frozen_deps/Cryptodome/Hash/SHA3_384.py index e6baf3f..26eeb79 100644 --- a/frozen_deps/Cryptodome/Hash/SHA3_384.py +++ b/frozen_deps/Cryptodome/Hash/SHA3_384.py @@ -24,7 +24,7 @@ from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer, SmartPointer, create_string_buffer, get_raw_buffer, c_size_t, - c_uint8_ptr) + c_uint8_ptr, c_ubyte) from Cryptodome.Hash.keccak import _raw_keccak_lib @@ -46,14 +46,18 @@ class SHA3_384_Hash(object): # ASN.1 Object ID oid = "2.16.840.1.101.3.4.2.9" + # Input block size for HMAC + block_size = 104 + def __init__(self, data, update_after_digest): self._update_after_digest = update_after_digest self._digest_done = False + self._padding = 0x06 state = VoidPointer() result = _raw_keccak_lib.keccak_init(state.address_of(), c_size_t(self.digest_size * 2), - 0x06) + c_ubyte(24)) if result: raise ValueError("Error %d while instantiating SHA-3/384" % result) @@ -93,7 +97,8 @@ class SHA3_384_Hash(object): bfr = create_string_buffer(self.digest_size) result = _raw_keccak_lib.keccak_digest(self._state.get(), bfr, - c_size_t(self.digest_size)) + c_size_t(self.digest_size), + c_ubyte(self._padding)) if result: raise ValueError("Error %d while instantiating SHA-3/384" % result) @@ -111,10 +116,34 @@ class SHA3_384_Hash(object): return "".join(["%02x" % bord(x) for x in self.digest()]) - def new(self): + def copy(self): + """Return a copy ("clone") of the hash object. + + The copy will have the same internal state as the original hash + object. + This can be used to efficiently compute the digests of strings that + share a common initial substring. + + :return: A hash object of the same type + """ + + clone = self.new() + result = _raw_keccak_lib.keccak_copy(self._state.get(), + clone._state.get()) + if result: + raise ValueError("Error %d while copying SHA3-384" % result) + return clone + + def new(self, data=None): + """Create a fresh SHA3-256 hash object.""" + + return type(self)(data, self._update_after_digest) + + + def new(self, data=None): """Create a fresh SHA3-384 hash object.""" - return type(self)(None, self._update_after_digest) + return type(self)(data, self._update_after_digest) def new(*args, **kwargs): @@ -145,3 +174,6 @@ def new(*args, **kwargs): # The size of the resulting hash in bytes. digest_size = SHA3_384_Hash.digest_size + +# Input block size for HMAC +block_size = 104 diff --git a/frozen_deps/Cryptodome/Hash/SHA3_384.pyi b/frozen_deps/Cryptodome/Hash/SHA3_384.pyi index d029ab6..98d00c6 100644 --- a/frozen_deps/Cryptodome/Hash/SHA3_384.pyi +++ b/frozen_deps/Cryptodome/Hash/SHA3_384.pyi @@ -4,13 +4,16 @@ Buffer = Union[bytes, bytearray, memoryview] class SHA3_384_Hash(object): digest_size: int + block_size: int oid: str def __init__(self, data: Optional[Buffer], update_after_digest: bool) -> None: ... def update(self, data: Buffer) -> SHA3_384_Hash: ... def digest(self) -> bytes: ... def hexdigest(self) -> str: ... - def new(self) -> SHA3_384_Hash: ... + def copy(self) -> SHA3_384_Hash: ... + def new(self, data: Optional[Buffer]) -> SHA3_384_Hash: ... def new(__data: Buffer = ..., update_after_digest: bool = ...) -> SHA3_384_Hash: ... digest_size: int +block_size: int diff --git a/frozen_deps/Cryptodome/Hash/SHA3_512.py b/frozen_deps/Cryptodome/Hash/SHA3_512.py index 676ce2f..99b1c37 100644 --- a/frozen_deps/Cryptodome/Hash/SHA3_512.py +++ b/frozen_deps/Cryptodome/Hash/SHA3_512.py @@ -24,7 +24,7 @@ from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer, SmartPointer, create_string_buffer, get_raw_buffer, c_size_t, - c_uint8_ptr) + c_uint8_ptr, c_ubyte) from Cryptodome.Hash.keccak import _raw_keccak_lib @@ -46,14 +46,18 @@ class SHA3_512_Hash(object): # ASN.1 Object ID oid = "2.16.840.1.101.3.4.2.10" + # Input block size for HMAC + block_size = 72 + def __init__(self, data, update_after_digest): self._update_after_digest = update_after_digest self._digest_done = False + self._padding = 0x06 state = VoidPointer() result = _raw_keccak_lib.keccak_init(state.address_of(), c_size_t(self.digest_size * 2), - 0x06) + c_ubyte(24)) if result: raise ValueError("Error %d while instantiating SHA-3/512" % result) @@ -94,7 +98,8 @@ class SHA3_512_Hash(object): bfr = create_string_buffer(self.digest_size) result = _raw_keccak_lib.keccak_digest(self._state.get(), bfr, - c_size_t(self.digest_size)) + c_size_t(self.digest_size), + c_ubyte(self._padding)) if result: raise ValueError("Error %d while instantiating SHA-3/512" % result) @@ -112,10 +117,28 @@ class SHA3_512_Hash(object): return "".join(["%02x" % bord(x) for x in self.digest()]) - def new(self): - """Create a fresh SHA3-512 hash object.""" + def copy(self): + """Return a copy ("clone") of the hash object. + + The copy will have the same internal state as the original hash + object. + This can be used to efficiently compute the digests of strings that + share a common initial substring. + + :return: A hash object of the same type + """ + + clone = self.new() + result = _raw_keccak_lib.keccak_copy(self._state.get(), + clone._state.get()) + if result: + raise ValueError("Error %d while copying SHA3-512" % result) + return clone + + def new(self, data=None): + """Create a fresh SHA3-521 hash object.""" - return type(self)(None, self._update_after_digest) + return type(self)(data, self._update_after_digest) def new(*args, **kwargs): @@ -146,3 +169,6 @@ def new(*args, **kwargs): # The size of the resulting hash in bytes. digest_size = SHA3_512_Hash.digest_size + +# Input block size for HMAC +block_size = 72 diff --git a/frozen_deps/Cryptodome/Hash/SHA3_512.pyi b/frozen_deps/Cryptodome/Hash/SHA3_512.pyi index 2c5403b..cdeec16 100644 --- a/frozen_deps/Cryptodome/Hash/SHA3_512.pyi +++ b/frozen_deps/Cryptodome/Hash/SHA3_512.pyi @@ -4,13 +4,16 @@ Buffer = Union[bytes, bytearray, memoryview] class SHA3_512_Hash(object): digest_size: int + block_size: int oid: str def __init__(self, data: Optional[Buffer], update_after_digest: bool) -> None: ... def update(self, data: Buffer) -> SHA3_512_Hash: ... def digest(self) -> bytes: ... def hexdigest(self) -> str: ... - def new(self) -> SHA3_512_Hash: ... + def copy(self) -> SHA3_512_Hash: ... + def new(self, data: Optional[Buffer]) -> SHA3_512_Hash: ... def new(__data: Buffer = ..., update_after_digest: bool = ...) -> SHA3_512_Hash: ... digest_size: int +block_size: int diff --git a/frozen_deps/Cryptodome/Hash/SHAKE128.py b/frozen_deps/Cryptodome/Hash/SHAKE128.py index be2b22e..5bde2b6 100644 --- a/frozen_deps/Cryptodome/Hash/SHAKE128.py +++ b/frozen_deps/Cryptodome/Hash/SHAKE128.py @@ -34,7 +34,7 @@ from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer, SmartPointer, create_string_buffer, get_raw_buffer, c_size_t, - c_uint8_ptr) + c_uint8_ptr, c_ubyte) from Cryptodome.Hash.keccak import _raw_keccak_lib @@ -54,13 +54,14 @@ class SHAKE128_XOF(object): state = VoidPointer() result = _raw_keccak_lib.keccak_init(state.address_of(), c_size_t(32), - 0x1F) + c_ubyte(24)) if result: raise ValueError("Error %d while instantiating SHAKE128" % result) self._state = SmartPointer(state.get(), _raw_keccak_lib.keccak_destroy) self._is_squeezing = False + self._padding = 0x1F if data: self.update(data) @@ -101,7 +102,8 @@ class SHAKE128_XOF(object): bfr = create_string_buffer(length) result = _raw_keccak_lib.keccak_squeeze(self._state.get(), bfr, - c_size_t(length)) + c_size_t(length), + c_ubyte(self._padding)) if result: raise ValueError("Error %d while extracting from SHAKE128" % result) diff --git a/frozen_deps/Cryptodome/Hash/SHAKE256.py b/frozen_deps/Cryptodome/Hash/SHAKE256.py index 46040e1..8c37f6a 100644 --- a/frozen_deps/Cryptodome/Hash/SHAKE256.py +++ b/frozen_deps/Cryptodome/Hash/SHAKE256.py @@ -34,7 +34,7 @@ from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer, SmartPointer, create_string_buffer, get_raw_buffer, c_size_t, - c_uint8_ptr) + c_uint8_ptr, c_ubyte) from Cryptodome.Hash.keccak import _raw_keccak_lib @@ -54,13 +54,15 @@ class SHAKE256_XOF(object): state = VoidPointer() result = _raw_keccak_lib.keccak_init(state.address_of(), c_size_t(64), - 0x1F) + c_ubyte(24)) if result: raise ValueError("Error %d while instantiating SHAKE256" % result) self._state = SmartPointer(state.get(), _raw_keccak_lib.keccak_destroy) self._is_squeezing = False + self._padding = 0x1F + if data: self.update(data) @@ -101,7 +103,8 @@ class SHAKE256_XOF(object): bfr = create_string_buffer(length) result = _raw_keccak_lib.keccak_squeeze(self._state.get(), bfr, - c_size_t(length)) + c_size_t(length), + c_ubyte(self._padding)) if result: raise ValueError("Error %d while extracting from SHAKE256" % result) diff --git a/frozen_deps/Cryptodome/Hash/TupleHash128.py b/frozen_deps/Cryptodome/Hash/TupleHash128.py new file mode 100644 index 0000000..5c910e4 --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/TupleHash128.py @@ -0,0 +1,138 @@ +# =================================================================== +# +# Copyright (c) 2021, Legrandin <[email protected]> +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# =================================================================== + +from Cryptodome.Util.py3compat import bord, is_bytes, tobytes + +from . import cSHAKE128 +from .cSHAKE128 import _encode_str, _right_encode + + +class TupleHash(object): + """A Tuple hash object. + Do not instantiate directly. + Use the :func:`new` function. + """ + + def __init__(self, custom, cshake, digest_size): + + self.digest_size = digest_size + + self._cshake = cshake._new(b'', custom, b'TupleHash') + self._digest = None + + def update(self, data): + """Authenticate the next byte string in the tuple. + + Args: + data (bytes/bytearray/memoryview): The next byte string. + """ + + if self._digest is not None: + raise TypeError("You cannot call 'update' after 'digest' or 'hexdigest'") + + if not is_bytes(data): + raise TypeError("You can only call 'update' on bytes") + + self._cshake.update(_encode_str(tobytes(data))) + + return self + + def digest(self): + """Return the **binary** (non-printable) digest of the tuple of byte strings. + + :return: The hash digest. Binary form. + :rtype: byte string + """ + + if self._digest is None: + self._cshake.update(_right_encode(self.digest_size * 8)) + self._digest = self._cshake.read(self.digest_size) + + return self._digest + + def hexdigest(self): + """Return the **printable** digest of the tuple of byte strings. + + :return: The hash digest. Hexadecimal encoded. + :rtype: string + """ + + return "".join(["%02x" % bord(x) for x in tuple(self.digest())]) + + def new(self, **kwargs): + """Return a new instance of a TupleHash object. + See :func:`new`. + """ + + if "digest_bytes" not in kwargs and "digest_bits" not in kwargs: + kwargs["digest_bytes"] = self.digest_size + + return new(**kwargs) + + +def new(**kwargs): + """Create a new TupleHash128 object. + + Args: + digest_bytes (integer): + Optional. The size of the digest, in bytes. + Default is 64. Minimum is 8. + digest_bits (integer): + Optional and alternative to ``digest_bytes``. + The size of the digest, in bits (and in steps of 8). + Default is 512. Minimum is 64. + custom (bytes): + Optional. + A customization bytestring (``S`` in SP 800-185). + + :Return: A :class:`TupleHash` object + """ + + digest_bytes = kwargs.pop("digest_bytes", None) + digest_bits = kwargs.pop("digest_bits", None) + if None not in (digest_bytes, digest_bits): + raise TypeError("Only one digest parameter must be provided") + if (None, None) == (digest_bytes, digest_bits): + digest_bytes = 64 + if digest_bytes is not None: + if digest_bytes < 8: + raise ValueError("'digest_bytes' must be at least 8") + else: + if digest_bits < 64 or digest_bits % 8: + raise ValueError("'digest_bytes' must be at least 64 " + "in steps of 8") + digest_bytes = digest_bits // 8 + + custom = kwargs.pop("custom", b'') + + if kwargs: + raise TypeError("Unknown parameters: " + str(kwargs)) + + return TupleHash(custom, cSHAKE128, digest_bytes) diff --git a/frozen_deps/Cryptodome/Hash/TupleHash128.pyi b/frozen_deps/Cryptodome/Hash/TupleHash128.pyi new file mode 100644 index 0000000..3b1e81e --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/TupleHash128.pyi @@ -0,0 +1,22 @@ +from typing import Any, Union +from types import ModuleType + +Buffer = Union[bytes, bytearray, memoryview] + +class TupleHash(object): + digest_size: int + def __init__(self, + custom: bytes, + cshake: ModuleType, + digest_size: int) -> None: ... + def update(self, data: Buffer) -> TupleHash: ... + def digest(self) -> bytes: ... + def hexdigest(self) -> str: ... + def new(self, + digest_bytes: int = ..., + digest_bits: int = ..., + custom: int = ...) -> TupleHash: ... + +def new(digest_bytes: int = ..., + digest_bits: int = ..., + custom: int = ...) -> TupleHash: ... diff --git a/frozen_deps/Cryptodome/Hash/TupleHash256.py b/frozen_deps/Cryptodome/Hash/TupleHash256.py new file mode 100644 index 0000000..9b4fba0 --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/TupleHash256.py @@ -0,0 +1,73 @@ +# =================================================================== +# +# Copyright (c) 2021, Legrandin <[email protected]> +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# =================================================================== + +from . import cSHAKE256 +from .TupleHash128 import TupleHash + + +def new(**kwargs): + """Create a new TupleHash256 object. + + Args: + digest_bytes (integer): + Optional. The size of the digest, in bytes. + Default is 64. Minimum is 8. + digest_bits (integer): + Optional and alternative to ``digest_bytes``. + The size of the digest, in bits (and in steps of 8). + Default is 512. Minimum is 64. + custom (bytes): + Optional. + A customization bytestring (``S`` in SP 800-185). + + :Return: A :class:`TupleHash` object + """ + + digest_bytes = kwargs.pop("digest_bytes", None) + digest_bits = kwargs.pop("digest_bits", None) + if None not in (digest_bytes, digest_bits): + raise TypeError("Only one digest parameter must be provided") + if (None, None) == (digest_bytes, digest_bits): + digest_bytes = 64 + if digest_bytes is not None: + if digest_bytes < 8: + raise ValueError("'digest_bytes' must be at least 8") + else: + if digest_bits < 64 or digest_bits % 8: + raise ValueError("'digest_bytes' must be at least 64 " + "in steps of 8") + digest_bytes = digest_bits // 8 + + custom = kwargs.pop("custom", b'') + + if kwargs: + raise TypeError("Unknown parameters: " + str(kwargs)) + + return TupleHash(custom, cSHAKE256, digest_bytes) diff --git a/frozen_deps/Cryptodome/Hash/TupleHash256.pyi b/frozen_deps/Cryptodome/Hash/TupleHash256.pyi new file mode 100644 index 0000000..82d943f --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/TupleHash256.pyi @@ -0,0 +1,5 @@ +from .TupleHash128 import TupleHash + +def new(digest_bytes: int = ..., + digest_bits: int = ..., + custom: int = ...) -> TupleHash: ... diff --git a/frozen_deps/Cryptodome/Hash/_BLAKE2b.abi3.so b/frozen_deps/Cryptodome/Hash/_BLAKE2b.abi3.so Binary files differnew file mode 100755 index 0000000..dfdf331 --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/_BLAKE2b.abi3.so diff --git a/frozen_deps/Cryptodome/Hash/_BLAKE2s.abi3.so b/frozen_deps/Cryptodome/Hash/_BLAKE2s.abi3.so Binary files differnew file mode 100755 index 0000000..26e69e8 --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/_BLAKE2s.abi3.so diff --git a/frozen_deps/Cryptodome/Hash/_MD2.abi3.so b/frozen_deps/Cryptodome/Hash/_MD2.abi3.so Binary files differnew file mode 100755 index 0000000..576ba1e --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/_MD2.abi3.so diff --git a/frozen_deps/Cryptodome/Hash/_MD4.abi3.so b/frozen_deps/Cryptodome/Hash/_MD4.abi3.so Binary files differnew file mode 100755 index 0000000..0afc5ca --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/_MD4.abi3.so diff --git a/frozen_deps/Cryptodome/Hash/_MD5.abi3.so b/frozen_deps/Cryptodome/Hash/_MD5.abi3.so Binary files differnew file mode 100755 index 0000000..38caf8d --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/_MD5.abi3.so diff --git a/frozen_deps/Cryptodome/Hash/_RIPEMD160.abi3.so b/frozen_deps/Cryptodome/Hash/_RIPEMD160.abi3.so Binary files differnew file mode 100755 index 0000000..2c02e71 --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/_RIPEMD160.abi3.so diff --git a/frozen_deps/Cryptodome/Hash/_SHA1.abi3.so b/frozen_deps/Cryptodome/Hash/_SHA1.abi3.so Binary files differnew file mode 100755 index 0000000..2d86b06 --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/_SHA1.abi3.so diff --git a/frozen_deps/Cryptodome/Hash/_SHA224.abi3.so b/frozen_deps/Cryptodome/Hash/_SHA224.abi3.so Binary files differnew file mode 100755 index 0000000..c462c9b --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/_SHA224.abi3.so diff --git a/frozen_deps/Cryptodome/Hash/_SHA256.abi3.so b/frozen_deps/Cryptodome/Hash/_SHA256.abi3.so Binary files differnew file mode 100755 index 0000000..c51c162 --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/_SHA256.abi3.so diff --git a/frozen_deps/Cryptodome/Hash/_SHA384.abi3.so b/frozen_deps/Cryptodome/Hash/_SHA384.abi3.so Binary files differnew file mode 100755 index 0000000..4a4237e --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/_SHA384.abi3.so diff --git a/frozen_deps/Cryptodome/Hash/_SHA512.abi3.so b/frozen_deps/Cryptodome/Hash/_SHA512.abi3.so Binary files differnew file mode 100755 index 0000000..ff9a01f --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/_SHA512.abi3.so diff --git a/frozen_deps/Cryptodome/Hash/__init__.py b/frozen_deps/Cryptodome/Hash/__init__.py index 719cd8d..4bda084 100644 --- a/frozen_deps/Cryptodome/Hash/__init__.py +++ b/frozen_deps/Cryptodome/Hash/__init__.py @@ -19,4 +19,6 @@ # =================================================================== __all__ = ['HMAC', 'MD2', 'MD4', 'MD5', 'RIPEMD160', 'SHA1', - 'SHA224', 'SHA256', 'SHA384', 'SHA512', 'CMAC', 'Poly1305'] + 'SHA224', 'SHA256', 'SHA384', 'SHA512', 'CMAC', 'Poly1305', + 'cSHAKE128', 'cSHAKE256', 'KMAC128', 'KMAC256', + 'TupleHash128', 'TupleHash256', 'KangarooTwelve'] diff --git a/frozen_deps/Cryptodome/Hash/_ghash_clmul.abi3.so b/frozen_deps/Cryptodome/Hash/_ghash_clmul.abi3.so Binary files differnew file mode 100755 index 0000000..248bdb7 --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/_ghash_clmul.abi3.so diff --git a/frozen_deps/Cryptodome/Hash/_ghash_portable.abi3.so b/frozen_deps/Cryptodome/Hash/_ghash_portable.abi3.so Binary files differnew file mode 100755 index 0000000..292f45d --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/_ghash_portable.abi3.so diff --git a/frozen_deps/Cryptodome/Hash/_keccak.abi3.so b/frozen_deps/Cryptodome/Hash/_keccak.abi3.so Binary files differnew file mode 100755 index 0000000..b251995 --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/_keccak.abi3.so diff --git a/frozen_deps/Cryptodome/Hash/_poly1305.abi3.so b/frozen_deps/Cryptodome/Hash/_poly1305.abi3.so Binary files differnew file mode 100755 index 0000000..e8da59e --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/_poly1305.abi3.so diff --git a/frozen_deps/Cryptodome/Hash/cSHAKE128.py b/frozen_deps/Cryptodome/Hash/cSHAKE128.py new file mode 100644 index 0000000..7c2f30a --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/cSHAKE128.py @@ -0,0 +1,187 @@ +# =================================================================== +# +# Copyright (c) 2021, Legrandin <[email protected]> +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# =================================================================== + +from Cryptodome.Util.py3compat import bchr + +from Cryptodome.Util._raw_api import (VoidPointer, SmartPointer, + create_string_buffer, + get_raw_buffer, c_size_t, + c_uint8_ptr, c_ubyte) + +from Cryptodome.Util.number import long_to_bytes + +from Cryptodome.Hash.keccak import _raw_keccak_lib + + +def _left_encode(x): + """Left encode function as defined in NIST SP 800-185""" + + assert (x < (1 << 2040) and x >= 0) + + # Get number of bytes needed to represent this integer. + num = 1 if x == 0 else (x.bit_length() + 7) // 8 + + return bchr(num) + long_to_bytes(x) + + +def _right_encode(x): + """Right encode function as defined in NIST SP 800-185""" + + assert (x < (1 << 2040) and x >= 0) + + # Get number of bytes needed to represent this integer. + num = 1 if x == 0 else (x.bit_length() + 7) // 8 + + return long_to_bytes(x) + bchr(num) + + +def _encode_str(x): + """Encode string function as defined in NIST SP 800-185""" + + bitlen = len(x) * 8 + if bitlen >= (1 << 2040): + raise ValueError("String too large to encode in cSHAKE") + + return _left_encode(bitlen) + x + + +def _bytepad(x, length): + """Zero pad byte string as defined in NIST SP 800-185""" + + to_pad = _left_encode(length) + x + + # Note: this implementation works with byte aligned strings, + # hence no additional bit padding is needed at this point. + npad = (length - len(to_pad) % length) % length + + return to_pad + b'\x00' * npad + + +class cSHAKE_XOF(object): + """A cSHAKE hash object. + Do not instantiate directly. + Use the :func:`new` function. + """ + + def __init__(self, data, custom, capacity, function): + state = VoidPointer() + + if custom or function: + prefix_unpad = _encode_str(function) + _encode_str(custom) + prefix = _bytepad(prefix_unpad, (1600 - capacity)//8) + self._padding = 0x04 + else: + prefix = None + self._padding = 0x1F # for SHAKE + + result = _raw_keccak_lib.keccak_init(state.address_of(), + c_size_t(capacity//8), + c_ubyte(24)) + if result: + raise ValueError("Error %d while instantiating cSHAKE" + % result) + self._state = SmartPointer(state.get(), + _raw_keccak_lib.keccak_destroy) + self._is_squeezing = False + + if prefix: + self.update(prefix) + + if data: + self.update(data) + + def update(self, data): + """Continue hashing of a message by consuming the next chunk of data. + + Args: + data (byte string/byte array/memoryview): The next chunk of the message being hashed. + """ + + if self._is_squeezing: + raise TypeError("You cannot call 'update' after the first 'read'") + + result = _raw_keccak_lib.keccak_absorb(self._state.get(), + c_uint8_ptr(data), + c_size_t(len(data))) + if result: + raise ValueError("Error %d while updating %s state" + % (result, self.name)) + return self + + def read(self, length): + """ + Compute the next piece of XOF output. + + .. note:: + You cannot use :meth:`update` anymore after the first call to + :meth:`read`. + + Args: + length (integer): the amount of bytes this method must return + + :return: the next piece of XOF output (of the given length) + :rtype: byte string + """ + + self._is_squeezing = True + bfr = create_string_buffer(length) + result = _raw_keccak_lib.keccak_squeeze(self._state.get(), + bfr, + c_size_t(length), + c_ubyte(self._padding)) + if result: + raise ValueError("Error %d while extracting from %s" + % (result, self.name)) + + return get_raw_buffer(bfr) + + +def _new(data, custom, function): + # Use Keccak[256] + return cSHAKE_XOF(data, custom, 256, function) + + +def new(data=None, custom=None): + """Return a fresh instance of a cSHAKE128 object. + + Args: + data (bytes/bytearray/memoryview): + Optional. + The very first chunk of the message to hash. + It is equivalent to an early call to :meth:`update`. + custom (bytes): + Optional. + A customization bytestring (``S`` in SP 800-185). + + :Return: A :class:`cSHAKE_XOF` object + """ + + # Use Keccak[256] + return cSHAKE_XOF(data, custom, 256, b'') diff --git a/frozen_deps/Cryptodome/Hash/cSHAKE128.pyi b/frozen_deps/Cryptodome/Hash/cSHAKE128.pyi new file mode 100644 index 0000000..1452fea --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/cSHAKE128.pyi @@ -0,0 +1,14 @@ +from typing import Union, Optional + +Buffer = Union[bytes, bytearray, memoryview] + +class cSHAKE_XOF(object): + def __init__(self, + data: Optional[Buffer] = ..., + function: Optional[bytes] = ..., + custom: Optional[bytes] = ...) -> None: ... + def update(self, data: Buffer) -> cSHAKE_XOF: ... + def read(self, length: int) -> bytes: ... + +def new(data: Optional[Buffer] = ..., + custom: Optional[Buffer] = ...) -> cSHAKE_XOF: ... diff --git a/frozen_deps/Cryptodome/Hash/cSHAKE256.py b/frozen_deps/Cryptodome/Hash/cSHAKE256.py new file mode 100644 index 0000000..a5b8701 --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/cSHAKE256.py @@ -0,0 +1,56 @@ +# =================================================================== +# +# Copyright (c) 2021, Legrandin <[email protected]> +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# =================================================================== + +from Cryptodome.Util._raw_api import c_size_t +from Cryptodome.Hash.cSHAKE128 import cSHAKE_XOF + + +def _new(data, custom, function): + # Use Keccak[512] + return cSHAKE_XOF(data, custom, 512, function) + + +def new(data=None, custom=None): + """Return a fresh instance of a cSHAKE256 object. + + Args: + data (bytes/bytearray/memoryview): + The very first chunk of the message to hash. + It is equivalent to an early call to :meth:`update`. + Optional. + custom (bytes): + Optional. + A customization bytestring (``S`` in SP 800-185). + + :Return: A :class:`cSHAKE_XOF` object + """ + + # Use Keccak[512] + return cSHAKE_XOF(data, custom, 512, b'') diff --git a/frozen_deps/Cryptodome/Hash/cSHAKE256.pyi b/frozen_deps/Cryptodome/Hash/cSHAKE256.pyi new file mode 100644 index 0000000..b910bb6 --- /dev/null +++ b/frozen_deps/Cryptodome/Hash/cSHAKE256.pyi @@ -0,0 +1,8 @@ +from typing import Union, Optional + +from Cryptodome.Hash.cSHAKE128 import cSHAKE_XOF + +Buffer = Union[bytes, bytearray, memoryview] + +def new(data: Optional[Buffer] = ..., + custom: Optional[Buffer] = ...) -> cSHAKE_XOF: ... diff --git a/frozen_deps/Cryptodome/Hash/keccak.py b/frozen_deps/Cryptodome/Hash/keccak.py index 9ae8ec5..f2af202 100644 --- a/frozen_deps/Cryptodome/Hash/keccak.py +++ b/frozen_deps/Cryptodome/Hash/keccak.py @@ -34,21 +34,27 @@ from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer, SmartPointer, create_string_buffer, get_raw_buffer, c_size_t, - c_uint8_ptr) + c_uint8_ptr, c_ubyte) _raw_keccak_lib = load_pycryptodome_raw_lib("Cryptodome.Hash._keccak", """ int keccak_init(void **state, size_t capacity_bytes, - uint8_t padding_byte); + uint8_t rounds); int keccak_destroy(void *state); int keccak_absorb(void *state, const uint8_t *in, size_t len); int keccak_squeeze(const void *state, uint8_t *out, - size_t len); - int keccak_digest(void *state, uint8_t *digest, size_t len); + size_t len, + uint8_t padding); + int keccak_digest(void *state, + uint8_t *digest, + size_t len, + uint8_t padding); + int keccak_copy(const void *src, void *dst); + int keccak_reset(void *state); """) class Keccak_Hash(object): @@ -66,11 +72,12 @@ class Keccak_Hash(object): self._update_after_digest = update_after_digest self._digest_done = False + self._padding = 0x01 state = VoidPointer() result = _raw_keccak_lib.keccak_init(state.address_of(), c_size_t(self.digest_size * 2), - 0x01) + c_ubyte(24)) if result: raise ValueError("Error %d while instantiating keccak" % result) self._state = SmartPointer(state.get(), @@ -107,7 +114,8 @@ class Keccak_Hash(object): bfr = create_string_buffer(self.digest_size) result = _raw_keccak_lib.keccak_digest(self._state.get(), bfr, - c_size_t(self.digest_size)) + c_size_t(self.digest_size), + c_ubyte(self._padding)) if result: raise ValueError("Error %d while squeezing keccak" % result) diff --git a/frozen_deps/Cryptodome/IO/PKCS8.py b/frozen_deps/Cryptodome/IO/PKCS8.py index 7365476..d02aed9 100644 --- a/frozen_deps/Cryptodome/IO/PKCS8.py +++ b/frozen_deps/Cryptodome/IO/PKCS8.py @@ -48,7 +48,7 @@ __all__ = ['wrap', 'unwrap'] def wrap(private_key, key_oid, passphrase=None, protection=None, - prot_params=None, key_params=None, randfunc=None): + prot_params=None, key_params=DerNull(), randfunc=None): """Wrap a private key into a PKCS#8 blob (clear or encrypted). Args: @@ -92,9 +92,10 @@ def wrap(private_key, key_oid, passphrase=None, protection=None, | | value is 1. | +------------------+-----------------------------------------------+ - key_params (DER object): - The algorithm parameters associated to the private key. - It is required for algorithms like DSA, but not for others like RSA. + key_params (DER object or None): + The ``parameters`` field to use in the ``AlgorithmIdentifier`` + SEQUENCE. If ``None``, no ``parameters`` field will be added. + By default, the ASN.1 type ``NULL`` is used. randfunc (callable): Random number generation function; it should accept a single integer @@ -106,9 +107,6 @@ def wrap(private_key, key_oid, passphrase=None, protection=None, The PKCS#8-wrapped private key (possibly encrypted), as a byte string. """ - if key_params is None: - key_params = DerNull() - # # PrivateKeyInfo ::= SEQUENCE { # version Version, @@ -117,12 +115,14 @@ def wrap(private_key, key_oid, passphrase=None, protection=None, # attributes [0] IMPLICIT Attributes OPTIONAL # } # + if key_params is None: + algorithm = DerSequence([DerObjectId(key_oid)]) + else: + algorithm = DerSequence([DerObjectId(key_oid), key_params]) + pk_info = DerSequence([ 0, - DerSequence([ - DerObjectId(key_oid), - key_params - ]), + algorithm, DerOctetString(private_key) ]) pk_info_der = pk_info.encode() @@ -185,11 +185,12 @@ def unwrap(p8_private_key, passphrase=None): if not found: raise ValueError("Error decoding PKCS#8 (%s)" % error_str) - pk_info = DerSequence().decode(p8_private_key, nr_elements=(2, 3, 4)) + pk_info = DerSequence().decode(p8_private_key, nr_elements=(2, 3, 4, 5)) if len(pk_info) == 2 and not passphrase: raise ValueError("Not a valid clear PKCS#8 structure " "(maybe it is encrypted?)") + # RFC5208, PKCS#8, version is v1(0) # # PrivateKeyInfo ::= SEQUENCE { # version Version, @@ -197,22 +198,27 @@ def unwrap(p8_private_key, passphrase=None): # privateKey PrivateKey, # attributes [0] IMPLICIT Attributes OPTIONAL # } - # Version ::= INTEGER - if pk_info[0] != 0: - raise ValueError("Not a valid PrivateKeyInfo SEQUENCE") - - # PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier # - # EncryptedPrivateKeyInfo ::= SEQUENCE { - # encryptionAlgorithm EncryptionAlgorithmIdentifier, - # encryptedData EncryptedData + # RFC5915, Asymmetric Key Package, version is v2(1) + # + # OneAsymmetricKey ::= SEQUENCE { + # version Version, + # privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, + # privateKey PrivateKey, + # attributes [0] Attributes OPTIONAL, + # ..., + # [[2: publicKey [1] PublicKey OPTIONAL ]], + # ... # } - # EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier - # AlgorithmIdentifier ::= SEQUENCE { - # algorithm OBJECT IDENTIFIER, - # parameters ANY DEFINED BY algorithm OPTIONAL - # } + if pk_info[0] == 0: + if len(pk_info) not in (3, 4): + raise ValueError("Not a valid PrivateKeyInfo SEQUENCE") + elif pk_info[0] == 1: + if len(pk_info) not in (3, 4, 5): + raise ValueError("Not a valid PrivateKeyInfo SEQUENCE") + else: + raise ValueError("Not a valid PrivateKeyInfo SEQUENCE") algo = DerSequence().decode(pk_info[1], nr_elements=(1, 2)) algo_oid = DerObjectId().decode(algo[0]).value @@ -225,7 +231,9 @@ def unwrap(p8_private_key, passphrase=None): except: algo_params = algo[1] - # EncryptedData ::= OCTET STRING + # PrivateKey ::= OCTET STRING private_key = DerOctetString().decode(pk_info[2]).payload + # We ignore attributes and (for v2 only) publickey + return (algo_oid, private_key, algo_params) diff --git a/frozen_deps/Cryptodome/IO/PKCS8.pyi b/frozen_deps/Cryptodome/IO/PKCS8.pyi index 135b638..be716af 100644 --- a/frozen_deps/Cryptodome/IO/PKCS8.pyi +++ b/frozen_deps/Cryptodome/IO/PKCS8.pyi @@ -7,7 +7,7 @@ def wrap(private_key: bytes, passphrase: Union[bytes, str] = ..., protection: str = ..., prot_params: Dict = ..., - key_params: DerObject = ..., + key_params: Optional[DerObject] = ..., randfunc: Optional[Callable[[int],str]] = ...) -> bytes: ... diff --git a/frozen_deps/Cryptodome/Math/Primality.py b/frozen_deps/Cryptodome/Math/Primality.py index 08ea3ff..33814fa 100644 --- a/frozen_deps/Cryptodome/Math/Primality.py +++ b/frozen_deps/Cryptodome/Math/Primality.py @@ -67,7 +67,7 @@ def miller_rabin_test(candidate, iterations, randfunc=None): if candidate in (1, 2, 3, 5): return PROBABLY_PRIME - + if candidate.is_even(): return COMPOSITE @@ -93,7 +93,8 @@ def miller_rabin_test(candidate, iterations, randfunc=None): base = 1 while base in (one, minus_one): base = Integer.random_range(min_inclusive=2, - max_inclusive=candidate - 2) + max_inclusive=candidate - 2, + randfunc=randfunc) assert(2 <= base <= candidate - 2) # Step 4.3-4.4 diff --git a/frozen_deps/Cryptodome/Math/_IntegerBase.py b/frozen_deps/Cryptodome/Math/_IntegerBase.py index f8cf333..7d78c4b 100644 --- a/frozen_deps/Cryptodome/Math/_IntegerBase.py +++ b/frozen_deps/Cryptodome/Math/_IntegerBase.py @@ -51,12 +51,12 @@ class IntegerBase(ABC): pass @abc.abstractmethod - def to_bytes(self, block_size=0): + def to_bytes(self, block_size=0, byteorder='big'): pass @staticmethod @abc.abstractmethod - def from_bytes(byte_string): + def from_bytes(byte_string, byteorder='big'): pass # Relations @@ -228,7 +228,7 @@ class IntegerBase(ABC): @abc.abstractmethod def jacobi_symbol(a, n): pass - + @staticmethod def _tonelli_shanks(n, p): """Tonelli-shanks algorithm for computing the square root diff --git a/frozen_deps/Cryptodome/Math/_IntegerBase.pyi b/frozen_deps/Cryptodome/Math/_IntegerBase.pyi index 3f534db..362c512 100644 --- a/frozen_deps/Cryptodome/Math/_IntegerBase.pyi +++ b/frozen_deps/Cryptodome/Math/_IntegerBase.pyi @@ -7,9 +7,9 @@ class IntegerBase: def __int__(self) -> int: ... def __str__(self) -> str: ... def __repr__(self) -> str: ... - def to_bytes(self, block_size: Optional[int]=0) -> bytes: ... + def to_bytes(self, block_size: Optional[int]=0, byteorder: str= ...) -> bytes: ... @staticmethod - def from_bytes(byte_string: bytes) -> IntegerBase: ... + def from_bytes(byte_string: bytes, byteorder: Optional[str] = ...) -> IntegerBase: ... def __eq__(self, term: object) -> bool: ... def __ne__(self, term: object) -> bool: ... def __lt__(self, term: Union[IntegerBase, int]) -> bool: ... diff --git a/frozen_deps/Cryptodome/Math/_IntegerCustom.py b/frozen_deps/Cryptodome/Math/_IntegerCustom.py index b626014..0e23152 100644 --- a/frozen_deps/Cryptodome/Math/_IntegerCustom.py +++ b/frozen_deps/Cryptodome/Math/_IntegerCustom.py @@ -57,7 +57,14 @@ implementation = {"library": "custom", "api": backend} class IntegerCustom(IntegerNative): @staticmethod - def from_bytes(byte_string): + def from_bytes(byte_string, byteorder='big'): + if byteorder == 'big': + pass + elif byteorder == 'little': + byte_string = bytearray(byte_string) + byte_string.reverse() + else: + raise ValueError("Incorrect byteorder") return IntegerCustom(bytes_to_long(byte_string)) def inplace_pow(self, exponent, modulus=None): diff --git a/frozen_deps/Cryptodome/Math/_IntegerGMP.py b/frozen_deps/Cryptodome/Math/_IntegerGMP.py index c860020..3ab7c59 100644 --- a/frozen_deps/Cryptodome/Math/_IntegerGMP.py +++ b/frozen_deps/Cryptodome/Math/_IntegerGMP.py @@ -35,7 +35,7 @@ from Cryptodome.Util.py3compat import tobytes, is_native_int from Cryptodome.Util._raw_api import (backend, load_lib, get_raw_buffer, get_c_string, null_pointer, create_string_buffer, - c_ulong, c_size_t) + c_ulong, c_size_t, c_uint8_ptr) from ._IntegerBase import IntegerBase @@ -43,12 +43,14 @@ gmp_defs = """typedef unsigned long UNIX_ULONG; typedef struct { int a; int b; void *c; } MPZ; typedef MPZ mpz_t[1]; typedef UNIX_ULONG mp_bitcnt_t; + void __gmpz_init (mpz_t x); void __gmpz_init_set (mpz_t rop, const mpz_t op); void __gmpz_init_set_ui (mpz_t rop, UNIX_ULONG op); - int __gmp_sscanf (const char *s, const char *fmt, ...); + + UNIX_ULONG __gmpz_get_ui (const mpz_t op); void __gmpz_set (mpz_t rop, const mpz_t op); - int __gmp_snprintf (uint8_t *buf, size_t size, const char *fmt, ...); + void __gmpz_set_ui (mpz_t rop, UNIX_ULONG op); void __gmpz_add (mpz_t rop, const mpz_t op1, const mpz_t op2); void __gmpz_add_ui (mpz_t rop, const mpz_t op1, UNIX_ULONG op2); void __gmpz_sub_ui (mpz_t rop, const mpz_t op1, UNIX_ULONG op2); @@ -156,28 +158,58 @@ class IntegerGMP(IntegerBase): if isinstance(value, float): raise ValueError("A floating point type is not a natural number") - self._initialized = True - if is_native_int(value): _gmp.mpz_init(self._mpz_p) - result = _gmp.gmp_sscanf(tobytes(str(value)), b"%Zd", self._mpz_p) - if result != 1: - raise ValueError("Error converting '%d'" % value) + self._initialized = True + if value == 0: + return + + tmp = new_mpz() + _gmp.mpz_init(tmp) + + try: + positive = value >= 0 + reduce = abs(value) + slots = (reduce.bit_length() - 1) // 32 + 1 + + while slots > 0: + slots = slots - 1 + _gmp.mpz_set_ui(tmp, + c_ulong(0xFFFFFFFF & (reduce >> (slots * 32)))) + _gmp.mpz_mul_2exp(tmp, tmp, c_ulong(slots * 32)) + _gmp.mpz_add(self._mpz_p, self._mpz_p, tmp) + finally: + _gmp.mpz_clear(tmp) + + if not positive: + _gmp.mpz_neg(self._mpz_p, self._mpz_p) + elif isinstance(value, IntegerGMP): _gmp.mpz_init_set(self._mpz_p, value._mpz_p) + self._initialized = True else: raise NotImplementedError + # Conversions def __int__(self): - # buf will contain the integer encoded in decimal plus the trailing - # zero, and possibly the negative sign. - # dig10(x) < log10(x) + 1 = log2(x)/log2(10) + 1 < log2(x)/3 + 1 - buf_len = _gmp.mpz_sizeinbase(self._mpz_p, 2) // 3 + 3 - buf = create_string_buffer(buf_len) + tmp = new_mpz() + _gmp.mpz_init_set(tmp, self._mpz_p) - _gmp.gmp_snprintf(buf, c_size_t(buf_len), b"%Zd", self._mpz_p) - return int(get_c_string(buf)) + try: + value = 0 + slot = 0 + while _gmp.mpz_cmp(tmp, self._zero_mpz_p) != 0: + lsb = _gmp.mpz_get_ui(tmp) & 0xFFFFFFFF + value |= lsb << (slot * 32) + _gmp.mpz_tdiv_q_2exp(tmp, tmp, c_ulong(32)) + slot = slot + 1 + finally: + _gmp.mpz_clear(tmp) + + if self < 0: + value = -value + return int(value) def __str__(self): return str(int(self)) @@ -193,7 +225,7 @@ class IntegerGMP(IntegerBase): def __index__(self): return int(self) - def to_bytes(self, block_size=0): + def to_bytes(self, block_size=0, byteorder='big'): """Convert the number into a byte string. This method encodes the number in network order and prepends @@ -204,6 +236,8 @@ class IntegerGMP(IntegerBase): block_size : integer The exact size the output byte string must have. If zero, the string has the minimal length. + byteorder : string + 'big' for big-endian integers (default), 'little' for litte-endian. :Returns: A byte string. :Raise ValueError: @@ -217,9 +251,10 @@ class IntegerGMP(IntegerBase): buf_len = (_gmp.mpz_sizeinbase(self._mpz_p, 2) + 7) // 8 if buf_len > block_size > 0: raise ValueError("Number is too big to convert to byte string" - "of prescribed length") + " of prescribed length") buf = create_string_buffer(buf_len) + _gmp.mpz_export( buf, null_pointer, # Ignore countp @@ -229,20 +264,39 @@ class IntegerGMP(IntegerBase): c_size_t(0), # No nails self._mpz_p) - return b'\x00' * max(0, block_size - buf_len) + get_raw_buffer(buf) + result = b'\x00' * max(0, block_size - buf_len) + get_raw_buffer(buf) + if byteorder == 'big': + pass + elif byteorder == 'little': + result = bytearray(result) + result.reverse() + result = bytes(result) + else: + raise ValueError("Incorrect byteorder") + return result @staticmethod - def from_bytes(byte_string): + def from_bytes(byte_string, byteorder='big'): """Convert a byte string into a number. :Parameters: byte_string : byte string The input number, encoded in network order. It can only be non-negative. + byteorder : string + 'big' for big-endian integers (default), 'little' for litte-endian. + :Return: The ``Integer`` object carrying the same value as the input. """ result = IntegerGMP(0) + if byteorder == 'big': + pass + elif byteorder == 'little': + byte_string = bytearray(byte_string) + byte_string.reverse() + else: + raise ValueError("Incorrect byteorder") _gmp.mpz_import( result._mpz_p, c_size_t(len(byte_string)), # Amount of words to read @@ -250,7 +304,7 @@ class IntegerGMP(IntegerBase): c_size_t(1), # Each word is 1 byte long 0, # Endianess within a word - not relevant c_size_t(0), # No nails - byte_string) + c_uint8_ptr(byte_string)) return result # Relations @@ -692,7 +746,7 @@ class IntegerGMP(IntegerBase): if not isinstance(n, IntegerGMP): n = IntegerGMP(n) if n <= 0 or n.is_even(): - raise ValueError("n must be positive even for the Jacobi symbol") + raise ValueError("n must be positive odd for the Jacobi symbol") return _gmp.mpz_jacobi(a._mpz_p, n._mpz_p) # Clean-up diff --git a/frozen_deps/Cryptodome/Math/_IntegerNative.py b/frozen_deps/Cryptodome/Math/_IntegerNative.py index 896107f..9b857ea 100644 --- a/frozen_deps/Cryptodome/Math/_IntegerNative.py +++ b/frozen_deps/Cryptodome/Math/_IntegerNative.py @@ -62,16 +62,31 @@ class IntegerNative(IntegerBase): def __index__(self): return int(self._value) - def to_bytes(self, block_size=0): + def to_bytes(self, block_size=0, byteorder='big'): if self._value < 0: raise ValueError("Conversion only valid for non-negative numbers") result = long_to_bytes(self._value, block_size) if len(result) > block_size > 0: raise ValueError("Value too large to encode") + if byteorder == 'big': + pass + elif byteorder == 'little': + result = bytearray(result) + result.reverse() + result = bytes(result) + else: + raise ValueError("Incorrect byteorder") return result @classmethod - def from_bytes(cls, byte_string): + def from_bytes(cls, byte_string, byteorder='big'): + if byteorder == 'big': + pass + elif byteorder == 'little': + byte_string = bytearray(byte_string) + byte_string.reverse() + else: + raise ValueError("Incorrect byteorder") return cls(bytes_to_long(byte_string)) # Relations @@ -348,7 +363,7 @@ class IntegerNative(IntegerBase): raise ValueError("n must be a positive integer") if (n & 1) == 0: - raise ValueError("n must be even for the Jacobi symbol") + raise ValueError("n must be odd for the Jacobi symbol") # Step 1 a = a % n diff --git a/frozen_deps/Cryptodome/Math/_modexp.abi3.so b/frozen_deps/Cryptodome/Math/_modexp.abi3.so Binary files differnew file mode 100755 index 0000000..3e0e3b2 --- /dev/null +++ b/frozen_deps/Cryptodome/Math/_modexp.abi3.so diff --git a/frozen_deps/Cryptodome/Protocol/_scrypt.abi3.so b/frozen_deps/Cryptodome/Protocol/_scrypt.abi3.so Binary files differnew file mode 100755 index 0000000..6ba8f35 --- /dev/null +++ b/frozen_deps/Cryptodome/Protocol/_scrypt.abi3.so diff --git a/frozen_deps/Cryptodome/PublicKey/DSA.py b/frozen_deps/Cryptodome/PublicKey/DSA.py index 2aaf864..dddd304 100644 --- a/frozen_deps/Cryptodome/PublicKey/DSA.py +++ b/frozen_deps/Cryptodome/PublicKey/DSA.py @@ -94,6 +94,8 @@ class DsaKey(object): :ivar x: Private key :vartype x: integer + + :undocumented: exportKey, publickey """ _keydata = ['y', 'g', 'p', 'q', 'x'] @@ -149,7 +151,7 @@ class DsaKey(object): def can_sign(self): # legacy return True - def publickey(self): + def public_key(self): """A matching DSA public key. Returns: @@ -332,6 +334,7 @@ class DsaKey(object): # Backward-compatibility exportKey = export_key + publickey = public_key # Methods defined in PyCryptodome that we don't support anymore @@ -449,7 +452,7 @@ def generate(bits, randfunc=None, domain=None): ## Perform consistency check on domain parameters # P and Q must be prime fmt_error = test_probable_prime(p) == COMPOSITE - fmt_error = test_probable_prime(q) == COMPOSITE + fmt_error |= test_probable_prime(q) == COMPOSITE # Verify Lagrange's theorem for sub-group fmt_error |= ((p - 1) % q) != 0 fmt_error |= g <= 1 or g >= p @@ -515,7 +518,7 @@ def construct(tup, consistency_check=True): if consistency_check: # P and Q must be prime fmt_error = test_probable_prime(key.p) == COMPOSITE - fmt_error = test_probable_prime(key.q) == COMPOSITE + fmt_error |= test_probable_prime(key.q) == COMPOSITE # Verify Lagrange's theorem for sub-group fmt_error |= ((key.p - 1) % key.q) != 0 fmt_error |= key.g <= 1 or key.g >= key.p diff --git a/frozen_deps/Cryptodome/PublicKey/DSA.pyi b/frozen_deps/Cryptodome/PublicKey/DSA.pyi index 9977a0f..354ac1f 100644 --- a/frozen_deps/Cryptodome/PublicKey/DSA.pyi +++ b/frozen_deps/Cryptodome/PublicKey/DSA.pyi @@ -9,7 +9,7 @@ class DsaKey(object): def has_private(self) -> bool: ... def can_encrypt(self) -> bool: ... # legacy def can_sign(self) -> bool: ... # legacy - def publickey(self) -> DsaKey: ... + def public_key(self) -> DsaKey: ... def __eq__(self, other: object) -> bool: ... def __ne__(self, other: object) -> bool: ... def __getstate__(self) -> None: ... @@ -20,6 +20,7 @@ class DsaKey(object): protection: Optional[str]=None, randfunc: Optional[RNG]=None) -> bytes: ... # Backward-compatibility exportKey = export_key + publickey = public_key def generate(bits: int, randfunc: Optional[RNG]=None, domain: Optional[Tuple[int, int, int]]=None) -> DsaKey: ... def construct(tup: Union[Tuple[int, int, int, int], Tuple[int, int, int, int, int]], consistency_check: Optional[bool]=True) -> DsaKey: ... diff --git a/frozen_deps/Cryptodome/PublicKey/ECC.py b/frozen_deps/Cryptodome/PublicKey/ECC.py index e83664b..84a4e07 100644 --- a/frozen_deps/Cryptodome/PublicKey/ECC.py +++ b/frozen_deps/Cryptodome/PublicKey/ECC.py @@ -31,7 +31,6 @@ from __future__ import print_function import re -import sys import struct import binascii from collections import namedtuple @@ -43,13 +42,15 @@ from Cryptodome.Math.Numbers import Integer from Cryptodome.Util.asn1 import (DerObjectId, DerOctetString, DerSequence, DerBitString) +from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer, + SmartPointer, c_size_t, c_uint8_ptr, + c_ulonglong, null_pointer) + from Cryptodome.PublicKey import (_expand_subject_public_key_info, _create_subject_public_key_info, _extract_subject_public_key_info) -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer, - SmartPointer, c_size_t, c_uint8_ptr, - c_ulonglong) +from Cryptodome.Hash import SHA512, SHAKE256 from Cryptodome.Random import get_random_bytes from Cryptodome.Random.random import getrandbits @@ -70,7 +71,7 @@ int ec_ws_new_point(EcPoint **pecp, const uint8_t *y, size_t len, const EcContext *ec_ctx); -void ec_free_point(EcPoint *ecp); +void ec_ws_free_point(EcPoint *ecp); int ec_ws_get_xy(uint8_t *x, uint8_t *y, size_t len, @@ -82,17 +83,173 @@ int ec_ws_scalar(EcPoint *ecp, size_t len, uint64_t seed); int ec_ws_clone(EcPoint **pecp2, const EcPoint *ecp); -int ec_ws_copy(EcPoint *ecp1, const EcPoint *ecp2); int ec_ws_cmp(const EcPoint *ecp1, const EcPoint *ecp2); int ec_ws_neg(EcPoint *p); -int ec_ws_normalize(EcPoint *ecp); -int ec_ws_is_pai(EcPoint *ecp); """) -_Curve = namedtuple("_Curve", "p b order Gx Gy G modulus_bits oid context desc openssh") +_ed25519_lib = load_pycryptodome_raw_lib("Cryptodome.PublicKey._ed25519", """ +typedef void Point; +int ed25519_new_point(Point **out, + const uint8_t x[32], + const uint8_t y[32], + size_t modsize, + const void *context); +int ed25519_clone(Point **P, const Point *Q); +void ed25519_free_point(Point *p); +int ed25519_cmp(const Point *p1, const Point *p2); +int ed25519_neg(Point *p); +int ed25519_get_xy(uint8_t *xb, uint8_t *yb, size_t modsize, Point *p); +int ed25519_double(Point *p); +int ed25519_add(Point *P1, const Point *P2); +int ed25519_scalar(Point *P, uint8_t *scalar, size_t scalar_len, uint64_t seed); +""") + +_ed448_lib = load_pycryptodome_raw_lib("Cryptodome.PublicKey._ed448", """ +typedef void EcContext; +typedef void PointEd448; +int ed448_new_context(EcContext **pec_ctx); +void ed448_context(EcContext *ec_ctx); +void ed448_free_context(EcContext *ec_ctx); +int ed448_new_point(PointEd448 **out, + const uint8_t x[56], + const uint8_t y[56], + size_t len, + const EcContext *context); +int ed448_clone(PointEd448 **P, const PointEd448 *Q); +void ed448_free_point(PointEd448 *p); +int ed448_cmp(const PointEd448 *p1, const PointEd448 *p2); +int ed448_neg(PointEd448 *p); +int ed448_get_xy(uint8_t *xb, uint8_t *yb, size_t len, const PointEd448 *p); +int ed448_double(PointEd448 *p); +int ed448_add(PointEd448 *P1, const PointEd448 *P2); +int ed448_scalar(PointEd448 *P, const uint8_t *scalar, size_t scalar_len, uint64_t seed); +""") + + +def lib_func(ecc_obj, func_name): + if ecc_obj._curve.desc == "Ed25519": + result = getattr(_ed25519_lib, "ed25519_" + func_name) + elif ecc_obj._curve.desc == "Ed448": + result = getattr(_ed448_lib, "ed448_" + func_name) + else: + result = getattr(_ec_lib, "ec_ws_" + func_name) + return result + +# +# _curves is a database of curve parameters. Items are indexed by their +# human-friendly name, suchas "P-256". Each item has the following fields: +# - p: the prime number that defines the finite field for all modulo operations +# - b: the constant in the Short Weierstrass curve equation +# - order: the number of elements in the group with the generator below +# - Gx the affine coordinate X of the generator point +# - Gy the affine coordinate Y of the generator point +# - G the generator, as an EccPoint object +# - modulus_bits the minimum number of bits for encoding the modulus p +# - oid an ASCII string with the registered ASN.1 Object ID +# - context a raw pointer to memory holding a context for all curve operations (can be NULL) +# - desc an ASCII string describing the curve +# - openssh the ASCII string used in OpenSSH id files for public keys on this curve +# - name the ASCII string which is also a valid key in _curves + + +_Curve = namedtuple("_Curve", "p b order Gx Gy G modulus_bits oid context desc openssh name") _curves = {} +p192_names = ["p192", "NIST P-192", "P-192", "prime192v1", "secp192r1", + "nistp192"] + + +def init_p192(): + p = 0xfffffffffffffffffffffffffffffffeffffffffffffffff + b = 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1 + order = 0xffffffffffffffffffffffff99def836146bc9b1b4d22831 + Gx = 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012 + Gy = 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811 + + p192_modulus = long_to_bytes(p, 24) + p192_b = long_to_bytes(b, 24) + p192_order = long_to_bytes(order, 24) + + ec_p192_context = VoidPointer() + result = _ec_lib.ec_ws_new_context(ec_p192_context.address_of(), + c_uint8_ptr(p192_modulus), + c_uint8_ptr(p192_b), + c_uint8_ptr(p192_order), + c_size_t(len(p192_modulus)), + c_ulonglong(getrandbits(64)) + ) + if result: + raise ImportError("Error %d initializing P-192 context" % result) + + context = SmartPointer(ec_p192_context.get(), _ec_lib.ec_free_context) + p192 = _Curve(Integer(p), + Integer(b), + Integer(order), + Integer(Gx), + Integer(Gy), + None, + 192, + "1.2.840.10045.3.1.1", # ANSI X9.62 / SEC2 + context, + "NIST P-192", + "ecdsa-sha2-nistp192", + "p192") + global p192_names + _curves.update(dict.fromkeys(p192_names, p192)) + + +init_p192() +del init_p192 + + +p224_names = ["p224", "NIST P-224", "P-224", "prime224v1", "secp224r1", + "nistp224"] + + +def init_p224(): + p = 0xffffffffffffffffffffffffffffffff000000000000000000000001 + b = 0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4 + order = 0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d + Gx = 0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21 + Gy = 0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34 + + p224_modulus = long_to_bytes(p, 28) + p224_b = long_to_bytes(b, 28) + p224_order = long_to_bytes(order, 28) + + ec_p224_context = VoidPointer() + result = _ec_lib.ec_ws_new_context(ec_p224_context.address_of(), + c_uint8_ptr(p224_modulus), + c_uint8_ptr(p224_b), + c_uint8_ptr(p224_order), + c_size_t(len(p224_modulus)), + c_ulonglong(getrandbits(64)) + ) + if result: + raise ImportError("Error %d initializing P-224 context" % result) + + context = SmartPointer(ec_p224_context.get(), _ec_lib.ec_free_context) + p224 = _Curve(Integer(p), + Integer(b), + Integer(order), + Integer(Gx), + Integer(Gy), + None, + 224, + "1.3.132.0.33", # SEC 2 + context, + "NIST P-224", + "ecdsa-sha2-nistp224", + "p224") + global p224_names + _curves.update(dict.fromkeys(p224_names, p224)) + + +init_p224() +del init_p224 + + p256_names = ["p256", "NIST P-256", "P-256", "prime256v1", "secp256r1", "nistp256"] @@ -127,10 +284,11 @@ def init_p256(): Integer(Gy), None, 256, - "1.2.840.10045.3.1.7", # ANSI X9.62 + "1.2.840.10045.3.1.7", # ANSI X9.62 / SEC2 context, "NIST P-256", - "ecdsa-sha2-nistp256") + "ecdsa-sha2-nistp256", + "p256") global p256_names _curves.update(dict.fromkeys(p256_names, p256)) @@ -176,7 +334,8 @@ def init_p384(): "1.3.132.0.34", # SEC 2 context, "NIST P-384", - "ecdsa-sha2-nistp384") + "ecdsa-sha2-nistp384", + "p384") global p384_names _curves.update(dict.fromkeys(p384_names, p384)) @@ -222,7 +381,8 @@ def init_p521(): "1.3.132.0.35", # SEC 2 context, "NIST P-521", - "ecdsa-sha2-nistp521") + "ecdsa-sha2-nistp521", + "p521") global p521_names _curves.update(dict.fromkeys(p521_names, p521)) @@ -231,19 +391,84 @@ init_p521() del init_p521 +ed25519_names = ["ed25519", "Ed25519"] + + +def init_ed25519(): + p = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed # 2**255 - 19 + order = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed + Gx = 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a + Gy = 0x6666666666666666666666666666666666666666666666666666666666666658 + + ed25519 = _Curve(Integer(p), + None, + Integer(order), + Integer(Gx), + Integer(Gy), + None, + 255, + "1.3.101.112", # RFC8410 + None, + "Ed25519", # Used throughout; do not change + "ssh-ed25519", + "ed25519") + global ed25519_names + _curves.update(dict.fromkeys(ed25519_names, ed25519)) + + +init_ed25519() +del init_ed25519 + + +ed448_names = ["ed448", "Ed448"] + + +def init_ed448(): + p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff # 2**448 - 2**224 - 1 + order = 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3 + Gx = 0x4f1970c66bed0ded221d15a622bf36da9e146570470f1767ea6de324a3d3a46412ae1af72ab66511433b80e18b00938e2626a82bc70cc05e + Gy = 0x693f46716eb6bc248876203756c9c7624bea73736ca3984087789c1e05a0c2d73ad3ff1ce67c39c4fdbd132c4ed7c8ad9808795bf230fa14 + + ed448_context = VoidPointer() + result = _ed448_lib.ed448_new_context(ed448_context.address_of()) + if result: + raise ImportError("Error %d initializing Ed448 context" % result) + + context = SmartPointer(ed448_context.get(), _ed448_lib.ed448_free_context) + + ed448 = _Curve(Integer(p), + None, + Integer(order), + Integer(Gx), + Integer(Gy), + None, + 448, + "1.3.101.113", # RFC8410 + context, + "Ed448", # Used throughout; do not change + None, + "ed448") + global ed448_names + _curves.update(dict.fromkeys(ed448_names, ed448)) + + +init_ed448() +del init_ed448 + + class UnsupportedEccFeature(ValueError): pass class EccPoint(object): - """A class to abstract a point over an Elliptic Curve. + """A class to model a point on an Elliptic Curve. - The class support special methods for: + The class supports operators for: * Adding two points: ``R = S + T`` * In-place addition: ``S += T`` * Negating a point: ``R = -T`` - * Comparing two points: ``if S == T: ...`` + * Comparing two points: ``if S == T: ...`` or ``if S != T: ...`` * Multiplying a point by a scalar: ``R = S*k`` * In-place multiplication by a scalar: ``T *= k`` @@ -253,7 +478,7 @@ class EccPoint(object): :ivar y: The affine Y-coordinate of the ECC point :vartype y: integer - :ivar xy: The tuple with X- and Y- coordinates + :ivar xy: The tuple with affine X- and Y- coordinates """ def __init__(self, x, y, curve="p256"): @@ -265,19 +490,26 @@ class EccPoint(object): self._curve_name = curve modulus_bytes = self.size_in_bytes() - context = self._curve.context xb = long_to_bytes(x, modulus_bytes) yb = long_to_bytes(y, modulus_bytes) if len(xb) != modulus_bytes or len(yb) != modulus_bytes: raise ValueError("Incorrect coordinate length") + new_point = lib_func(self, "new_point") + free_func = lib_func(self, "free_point") + self._point = VoidPointer() - result = _ec_lib.ec_ws_new_point(self._point.address_of(), - c_uint8_ptr(xb), - c_uint8_ptr(yb), - c_size_t(modulus_bytes), - context.get()) + try: + context = self._curve.context.get() + except AttributeError: + context = null_pointer + result = new_point(self._point.address_of(), + c_uint8_ptr(xb), + c_uint8_ptr(yb), + c_size_t(modulus_bytes), + context) + if result: if result == 15: raise ValueError("The EC point does not belong to the curve") @@ -285,26 +517,34 @@ class EccPoint(object): # Ensure that object disposal of this Python object will (eventually) # free the memory allocated by the raw library for the EC point - self._point = SmartPointer(self._point.get(), - _ec_lib.ec_free_point) + self._point = SmartPointer(self._point.get(), free_func) def set(self, point): + clone = lib_func(self, "clone") + free_func = lib_func(self, "free_point") + self._point = VoidPointer() - result = _ec_lib.ec_ws_clone(self._point.address_of(), - point._point.get()) + result = clone(self._point.address_of(), + point._point.get()) + if result: raise ValueError("Error %d while cloning an EC point" % result) - self._point = SmartPointer(self._point.get(), - _ec_lib.ec_free_point) + self._point = SmartPointer(self._point.get(), free_func) return self def __eq__(self, point): - return 0 == _ec_lib.ec_ws_cmp(self._point.get(), point._point.get()) + cmp_func = lib_func(self, "cmp") + return 0 == cmp_func(self._point.get(), point._point.get()) + + # Only needed for Python 2 + def __ne__(self, point): + return not self == point def __neg__(self): + neg_func = lib_func(self, "neg") np = self.copy() - result = _ec_lib.ec_ws_neg(np._point.get()) + result = neg_func(np._point.get()) if result: raise ValueError("Error %d while inverting an EC point" % result) return np @@ -315,13 +555,24 @@ class EccPoint(object): np = EccPoint(x, y, self._curve_name) return np + def _is_eddsa(self): + return self._curve.name in ("ed25519", "ed448") + def is_point_at_infinity(self): - """``True`` if this is the point-at-infinity.""" - return self.xy == (0, 0) + """``True`` if this is the *point-at-infinity*.""" + + if self._is_eddsa(): + return self.x == 0 + else: + return self.xy == (0, 0) def point_at_infinity(self): - """Return the point-at-infinity for the curve this point is on.""" - return EccPoint(0, 0, self._curve_name) + """Return the *point-at-infinity* for the curve.""" + + if self._is_eddsa(): + return EccPoint(0, 1, self._curve_name) + else: + return EccPoint(0, 0, self._curve_name) @property def x(self): @@ -336,10 +587,11 @@ class EccPoint(object): modulus_bytes = self.size_in_bytes() xb = bytearray(modulus_bytes) yb = bytearray(modulus_bytes) - result = _ec_lib.ec_ws_get_xy(c_uint8_ptr(xb), - c_uint8_ptr(yb), - c_size_t(modulus_bytes), - self._point.get()) + get_xy = lib_func(self, "get_xy") + result = get_xy(c_uint8_ptr(xb), + c_uint8_ptr(yb), + c_size_t(modulus_bytes), + self._point.get()) if result: raise ValueError("Error %d while encoding an EC point" % result) @@ -356,11 +608,12 @@ class EccPoint(object): def double(self): """Double this point (in-place operation). - :Return: - :class:`EccPoint` : this same object (to enable chaining) + Returns: + This same object (to enable chaining). """ - result = _ec_lib.ec_ws_double(self._point.get()) + double_func = lib_func(self, "double") + result = double_func(self._point.get()) if result: raise ValueError("Error %d while doubling an EC point" % result) return self @@ -368,7 +621,8 @@ class EccPoint(object): def __iadd__(self, point): """Add a second point to this one""" - result = _ec_lib.ec_ws_add(self._point.get(), point._point.get()) + add_func = lib_func(self, "add") + result = add_func(self._point.get(), point._point.get()) if result: if result == 16: raise ValueError("EC points are not on the same curve") @@ -385,13 +639,14 @@ class EccPoint(object): def __imul__(self, scalar): """Multiply this point by a scalar""" + scalar_func = lib_func(self, "scalar") if scalar < 0: raise ValueError("Scalar multiplication is only defined for non-negative integers") sb = long_to_bytes(scalar) - result = _ec_lib.ec_ws_scalar(self._point.get(), - c_uint8_ptr(sb), - c_size_t(len(sb)), - c_ulonglong(getrandbits(64))) + result = scalar_func(self._point.get(), + c_uint8_ptr(sb), + c_size_t(len(sb)), + c_ulonglong(getrandbits(64))) if result: raise ValueError("Error %d during scalar multiplication" % result) return self @@ -408,6 +663,16 @@ class EccPoint(object): # Last piece of initialization +p192_G = EccPoint(_curves['p192'].Gx, _curves['p192'].Gy, "p192") +p192 = _curves['p192']._replace(G=p192_G) +_curves.update(dict.fromkeys(p192_names, p192)) +del p192_G, p192, p192_names + +p224_G = EccPoint(_curves['p224'].Gx, _curves['p224'].Gy, "p224") +p224 = _curves['p224']._replace(G=p224_G) +_curves.update(dict.fromkeys(p224_names, p224)) +del p224_G, p224, p224_names + p256_G = EccPoint(_curves['p256'].Gx, _curves['p256'].Gy, "p256") p256 = _curves['p256']._replace(G=p256_G) _curves.update(dict.fromkeys(p256_names, p256)) @@ -423,20 +688,37 @@ p521 = _curves['p521']._replace(G=p521_G) _curves.update(dict.fromkeys(p521_names, p521)) del p521_G, p521, p521_names +ed25519_G = EccPoint(_curves['Ed25519'].Gx, _curves['Ed25519'].Gy, "Ed25519") +ed25519 = _curves['Ed25519']._replace(G=ed25519_G) +_curves.update(dict.fromkeys(ed25519_names, ed25519)) +del ed25519_G, ed25519, ed25519_names + +ed448_G = EccPoint(_curves['Ed448'].Gx, _curves['Ed448'].Gy, "Ed448") +ed448 = _curves['Ed448']._replace(G=ed448_G) +_curves.update(dict.fromkeys(ed448_names, ed448)) +del ed448_G, ed448, ed448_names + class EccKey(object): r"""Class defining an ECC key. Do not instantiate directly. Use :func:`generate`, :func:`construct` or :func:`import_key` instead. - :ivar curve: The name of the ECC as defined in :numref:`curve_names`. + :ivar curve: The name of the curve as defined in the `ECC table`_. :vartype curve: string - :ivar pointQ: an ECC point representating the public component + :ivar pointQ: an ECC point representating the public component. :vartype pointQ: :class:`EccPoint` - :ivar d: A scalar representating the private component + :ivar d: A scalar that represents the private component + in NIST P curves. It is smaller than the + order of the generator point. :vartype d: integer + + :ivar seed: A seed that representats the private component + in EdDSA curves + (Ed25519, 32 bytes; Ed448, 57 bytes). + :vartype seed: bytes """ def __init__(self, **kwargs): @@ -444,34 +726,79 @@ class EccKey(object): Keywords: curve : string - It must be *"p256"*, *"P-256"*, *"prime256v1"* or *"secp256r1"*. + The name of the curve. d : integer - Only for a private key. It must be in the range ``[1..order-1]``. + Mandatory for a private key one NIST P curves. + It must be in the range ``[1..order-1]``. + seed : bytes + Mandatory for a private key on the Ed25519 (32 bytes) + or Ed448 (57 bytes) curve. point : EccPoint Mandatory for a public key. If provided for a private key, the implementation will NOT check whether it matches ``d``. + + Only one parameter among ``d``, ``seed`` or ``point`` may be used. """ kwargs_ = dict(kwargs) curve_name = kwargs_.pop("curve", None) self._d = kwargs_.pop("d", None) + self._seed = kwargs_.pop("seed", None) self._point = kwargs_.pop("point", None) + if curve_name is None and self._point: + curve_name = self._point._curve_name if kwargs_: raise TypeError("Unknown parameters: " + str(kwargs_)) if curve_name not in _curves: - raise ValueError("Unsupported curve (%s)", curve_name) + raise ValueError("Unsupported curve (%s)" % curve_name) self._curve = _curves[curve_name] + self.curve = curve_name + + count = int(self._d is not None) + int(self._seed is not None) - if self._d is None: + if count == 0: if self._point is None: - raise ValueError("Either private or public ECC component must be specified, not both") - else: + raise ValueError("At lest one between parameters 'point', 'd' or 'seed' must be specified") + return + + if count == 2: + raise ValueError("Parameters d and seed are mutually exclusive") + + # NIST P curves work with d, EdDSA works with seed + + if not self._is_eddsa(): + if self._seed is not None: + raise ValueError("Parameter 'seed' can only be used with Ed25519 or Ed448") self._d = Integer(self._d) if not 1 <= self._d < self._curve.order: - raise ValueError("Invalid ECC private component") - - self.curve = self._curve.desc + raise ValueError("Parameter d must be an integer smaller than the curve order") + else: + if self._d is not None: + raise ValueError("Parameter d can only be used with NIST P curves") + # RFC 8032, 5.1.5 + if self._curve.name == "ed25519": + if len(self._seed) != 32: + raise ValueError("Parameter seed must be 32 bytes long for Ed25519") + seed_hash = SHA512.new(self._seed).digest() # h + self._prefix = seed_hash[32:] + tmp = bytearray(seed_hash[:32]) + tmp[0] &= 0xF8 + tmp[31] = (tmp[31] & 0x7F) | 0x40 + # RFC 8032, 5.2.5 + elif self._curve.name == "ed448": + if len(self._seed) != 57: + raise ValueError("Parameter seed must be 57 bytes long for Ed448") + seed_hash = SHAKE256.new(self._seed).read(114) # h + self._prefix = seed_hash[57:] + tmp = bytearray(seed_hash[:57]) + tmp[0] &= 0xFC + tmp[55] |= 0x80 + tmp[56] = 0 + self._d = Integer.from_bytes(tmp, byteorder='little') + + def _is_eddsa(self): + return self._curve.desc in ("Ed25519", "Ed448") def __eq__(self, other): if other.has_private() != self.has_private(): @@ -481,7 +808,10 @@ class EccKey(object): def __repr__(self): if self.has_private(): - extra = ", d=%d" % int(self._d) + if self._is_eddsa(): + extra = ", seed=%s" % self._seed.hex() + else: + extra = ", d=%d" % int(self._d) else: extra = "" x, y = self.pointQ.xy @@ -492,6 +822,7 @@ class EccKey(object): return self._d is not None + # ECDSA def _sign(self, z, k): assert 0 < k < self._curve.order @@ -506,6 +837,7 @@ class EccKey(object): s = inv_blind_k * (blind * z + blind_d * r) % order return (r, s) + # ECDSA def _verify(self, z, rs): order = self._curve.order sinv = rs[1].inverse(order) @@ -520,6 +852,12 @@ class EccKey(object): return self._d @property + def seed(self): + if not self.has_private(): + raise ValueError("This is not a private ECC key") + return self._seed + + @property def pointQ(self): if self._point is None: self._point = self._curve.G * self._d @@ -534,9 +872,12 @@ class EccKey(object): return EccKey(curve=self._curve.desc, point=self.pointQ) - def _export_subjectPublicKeyInfo(self, compress): + def _export_SEC1(self, compress): + if self._is_eddsa(): + raise ValueError("SEC1 format is unsupported for EdDSA curves") # See 2.2 in RFC5480 and 2.3.3 in SEC1 + # # The first byte is: # - 0x02: compressed, only X-coordinate, Y-coordinate is even # - 0x03: compressed, only X-coordinate, Y-coordinate is odd @@ -547,20 +888,45 @@ class EccKey(object): modulus_bytes = self.pointQ.size_in_bytes() if compress: - first_byte = 2 + self.pointQ.y.is_odd() - public_key = (bchr(first_byte) + + if self.pointQ.y.is_odd(): + first_byte = b'\x03' + else: + first_byte = b'\x02' + public_key = (first_byte + self.pointQ.x.to_bytes(modulus_bytes)) else: public_key = (b'\x04' + self.pointQ.x.to_bytes(modulus_bytes) + self.pointQ.y.to_bytes(modulus_bytes)) + return public_key - unrestricted_oid = "1.2.840.10045.2.1" - return _create_subject_public_key_info(unrestricted_oid, + def _export_eddsa(self): + x, y = self.pointQ.xy + if self._curve.name == "ed25519": + result = bytearray(y.to_bytes(32, byteorder='little')) + result[31] = ((x & 1) << 7) | result[31] + elif self._curve.name == "ed448": + result = bytearray(y.to_bytes(57, byteorder='little')) + result[56] = (x & 1) << 7 + else: + raise ValueError("Not an EdDSA key to export") + return bytes(result) + + def _export_subjectPublicKeyInfo(self, compress): + if self._is_eddsa(): + oid = self._curve.oid + public_key = self._export_eddsa() + params = None + else: + oid = "1.2.840.10045.2.1" # unrestricted + public_key = self._export_SEC1(compress) + params = DerObjectId(self._curve.oid) + + return _create_subject_public_key_info(oid, public_key, - DerObjectId(self._curve.oid)) + params) - def _export_private_der(self, include_ec_params=True): + def _export_rfc5915_private_der(self, include_ec_params=True): assert self.has_private() @@ -593,11 +959,18 @@ class EccKey(object): if kwargs.get('passphrase', None) is not None and 'protection' not in kwargs: raise ValueError("At least the 'protection' parameter should be present") - unrestricted_oid = "1.2.840.10045.2.1" - private_key = self._export_private_der(include_ec_params=False) + if self._is_eddsa(): + oid = self._curve.oid + private_key = DerOctetString(self._seed).encode() + params = None + else: + oid = "1.2.840.10045.2.1" # unrestricted + private_key = self._export_rfc5915_private_der(include_ec_params=False) + params = DerObjectId(self._curve.oid) + result = PKCS8.wrap(private_key, - unrestricted_oid, - key_params=DerObjectId(self._curve.oid), + oid, + key_params=params, **kwargs) return result @@ -610,7 +983,7 @@ class EccKey(object): def _export_private_pem(self, passphrase, **kwargs): from Cryptodome.IO import PEM - encoded_der = self._export_private_der() + encoded_der = self._export_rfc5915_private_der() return PEM.encode(encoded_der, "EC PRIVATE KEY", passphrase, **kwargs) def _export_private_clear_pkcs8_in_clear_pem(self): @@ -633,19 +1006,27 @@ class EccKey(object): raise ValueError("Cannot export OpenSSH private keys") desc = self._curve.openssh - modulus_bytes = self.pointQ.size_in_bytes() - if compress: - first_byte = 2 + self.pointQ.y.is_odd() - public_key = (bchr(first_byte) + - self.pointQ.x.to_bytes(modulus_bytes)) + if desc is None: + raise ValueError("Cannot export %s keys as OpenSSH" % self._curve.name) + elif desc == "ssh-ed25519": + public_key = self._export_eddsa() + comps = (tobytes(desc), tobytes(public_key)) else: - public_key = (b'\x04' + - self.pointQ.x.to_bytes(modulus_bytes) + - self.pointQ.y.to_bytes(modulus_bytes)) + modulus_bytes = self.pointQ.size_in_bytes() + + if compress: + first_byte = 2 + self.pointQ.y.is_odd() + public_key = (bchr(first_byte) + + self.pointQ.x.to_bytes(modulus_bytes)) + else: + public_key = (b'\x04' + + self.pointQ.x.to_bytes(modulus_bytes) + + self.pointQ.y.to_bytes(modulus_bytes)) + + middle = desc.split("-")[2] + comps = (tobytes(desc), tobytes(middle), public_key) - middle = desc.split("-")[2] - comps = (tobytes(desc), tobytes(middle), public_key) blob = b"".join([struct.pack(">I", len(x)) + x for x in comps]) return desc + " " + tostr(binascii.b2a_base64(blob)) @@ -665,6 +1046,15 @@ class EccKey(object): - ``'PEM'``. The key will be encoded in a PEM_ envelope (ASCII). - ``'OpenSSH'``. The key will be encoded in the OpenSSH_ format (ASCII, public keys only). + - ``'SEC1'``. The public key (i.e., the EC point) will be encoded + into ``bytes`` according to Section 2.3.3 of `SEC1`_ + (which is a subset of the older X9.62 ITU standard). + Only for NIST P-curves. + - ``'raw'``. The public key will be encoded as ``bytes``, + without any metadata. + + * For NIST P-curves: equivalent to ``'SEC1'``. + * For EdDSA curves: ``bytes`` in the format defined in `RFC8032`_. passphrase (byte string or string): The passphrase to use for protecting the private key. @@ -673,9 +1063,7 @@ class EccKey(object): Only relevant for private keys. If ``True`` (default and recommended), the `PKCS#8`_ representation - will be used. - - If ``False``, the much weaker `PEM encryption`_ mechanism will be used. + will be used. It must be ``True`` for EdDSA curves. protection (string): When a private key is exported with password-protection @@ -684,10 +1072,13 @@ class EccKey(object): It is recommended to use ``PBKDF2WithHMAC-SHA1AndAES128-CBC``. compress (boolean): - If ``True``, a more compact representation of the public key - with the X-coordinate only is used. + If ``True``, the method returns a more compact representation + of the public key, with the X-coordinate only. + + If ``False`` (default), the method returns the full public key. - If ``False`` (default), the full public key will be exported. + This parameter is ignored for EdDSA curves, as compression is + mandatory. .. warning:: If you don't provide a passphrase, the private key will be @@ -700,18 +1091,18 @@ class EccKey(object): .. _PEM: http://www.ietf.org/rfc/rfc1421.txt .. _`PEM encryption`: http://www.ietf.org/rfc/rfc1423.txt - .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt .. _OpenSSH: http://www.openssh.com/txt/rfc5656.txt .. _RFC5480: https://tools.ietf.org/html/rfc5480 - .. _RFC5915: http://www.ietf.org/rfc/rfc5915.txt + .. _SEC1: https://www.secg.org/sec1-v2.pdf Returns: - A multi-line string (for PEM and OpenSSH) or bytes (for DER) with the encoded key. + A multi-line string (for ``'PEM'`` and ``'OpenSSH'``) or + ``bytes`` (for ``'DER'``, ``'SEC1'``, and ``'raw'``) with the encoded key. """ args = kwargs.copy() ext_format = args.pop("format") - if ext_format not in ("PEM", "DER", "OpenSSH"): + if ext_format not in ("PEM", "DER", "OpenSSH", "SEC1", "raw"): raise ValueError("Unknown format '%s'" % ext_format) compress = args.pop("compress", False) @@ -723,6 +1114,10 @@ class EccKey(object): if not passphrase: raise ValueError("Empty passphrase") use_pkcs8 = args.pop("use_pkcs8", True) + + if not use_pkcs8 and self._is_eddsa(): + raise ValueError("'pkcs8' must be True for EdDSA curves") + if ext_format == "PEM": if use_pkcs8: if passphrase: @@ -738,9 +1133,10 @@ class EccKey(object): if use_pkcs8: return self._export_pkcs8(passphrase=passphrase, **args) else: - return self._export_private_der() + return self._export_rfc5915_private_der() else: - raise ValueError("Private keys cannot be exported in OpenSSH format") + raise ValueError("Private keys cannot be exported " + "in the '%s' format" % ext_format) else: # Public key if args: raise ValueError("Unexpected parameters: '%s'" % args) @@ -748,6 +1144,13 @@ class EccKey(object): return self._export_public_pem(compress) elif ext_format == "DER": return self._export_subjectPublicKeyInfo(compress) + elif ext_format == "SEC1": + return self._export_SEC1(compress) + elif ext_format == "raw": + if self._curve.name in ('ed25519', 'ed448'): + return self._export_eddsa() + else: + return self._export_SEC1(compress) else: return self._export_openssh(compress) @@ -758,7 +1161,7 @@ def generate(**kwargs): Args: curve (string): - Mandatory. It must be a curve name defined in :numref:`curve_names`. + Mandatory. It must be a curve name defined in the `ECC table`_. randfunc (callable): Optional. The RNG to read randomness from. @@ -771,30 +1174,46 @@ def generate(**kwargs): if kwargs: raise TypeError("Unknown parameters: " + str(kwargs)) - d = Integer.random_range(min_inclusive=1, - max_exclusive=curve.order, - randfunc=randfunc) + if _curves[curve_name].name == "ed25519": + seed = randfunc(32) + new_key = EccKey(curve=curve_name, seed=seed) + elif _curves[curve_name].name == "ed448": + seed = randfunc(57) + new_key = EccKey(curve=curve_name, seed=seed) + else: + d = Integer.random_range(min_inclusive=1, + max_exclusive=curve.order, + randfunc=randfunc) + new_key = EccKey(curve=curve_name, d=d) - return EccKey(curve=curve_name, d=d) + return new_key def construct(**kwargs): """Build a new ECC key (private or public) starting from some base components. - Args: + In most cases, you will already have an existing key + which you can read in with :func:`import_key` instead + of this function. + Args: curve (string): - Mandatory. It must be a curve name defined in :numref:`curve_names`. + Mandatory. The name of the elliptic curve, as defined in the `ECC table`_. d (integer): - Only for a private key. It must be in the range ``[1..order-1]``. + Mandatory for a private key and a NIST P-curve (e.g., P-256): + the integer in the range ``[1..order-1]`` that represents the key. + + seed (bytes): + Mandatory for a private key and an EdDSA curve. + It must be 32 bytes for Ed25519, and 57 bytes for Ed448. point_x (integer): - Mandatory for a public key. X coordinate (affine) of the ECC point. + Mandatory for a public key: the X coordinate (affine) of the ECC point. point_y (integer): - Mandatory for a public key. Y coordinate (affine) of the ECC point. + Mandatory for a public key: the Y coordinate (affine) of the ECC point. Returns: :class:`EccKey` : a new ECC key object @@ -812,29 +1231,39 @@ def construct(**kwargs): # ValueError is raised if the point is not on the curve kwargs["point"] = EccPoint(point_x, point_y, curve_name) + new_key = EccKey(**kwargs) + # Validate that the private key matches the public one - d = kwargs.get("d", None) - if d is not None and "point" in kwargs: - pub_key = curve.G * d + # because EccKey will not do that automatically + if new_key.has_private() and 'point' in kwargs: + pub_key = curve.G * new_key.d if pub_key.xy != (point_x, point_y): raise ValueError("Private and public ECC keys do not match") - return EccKey(**kwargs) + return new_key -def _import_public_der(curve_oid, ec_point): +def _import_public_der(ec_point, curve_oid=None, curve_name=None): """Convert an encoded EC point into an EccKey object + ec_point: byte string with the EC point (SEC1-encoded) + curve_oid: string with the name the curve curve_name: string with the OID of the curve - ec_point: byte string with the EC point (not DER encoded) + + Either curve_id or curve_name must be specified """ - for curve_name, curve in _curves.items(): - if curve.oid == curve_oid: + for _curve_name, curve in _curves.items(): + if curve_oid and curve.oid == curve_oid: + break + if curve_name == _curve_name: break else: - raise UnsupportedEccFeature("Unsupported ECC curve (OID: %s)" % curve_oid) + if curve_oid: + raise UnsupportedEccFeature("Unsupported ECC curve (OID: %s)" % curve_oid) + else: + raise UnsupportedEccFeature("Unsupported ECC curve (%s)" % curve_name) # See 2.2 in RFC5480 and 2.3.3 in SEC1 # The first byte is: @@ -854,11 +1283,12 @@ def _import_public_der(curve_oid, ec_point): x = Integer.from_bytes(ec_point[1:modulus_bytes+1]) y = Integer.from_bytes(ec_point[modulus_bytes+1:]) # Compressed point - elif point_type in (0x02, 0x3): + elif point_type in (0x02, 0x03): if len(ec_point) != (1 + modulus_bytes): raise ValueError("Incorrect EC point length") x = Integer.from_bytes(ec_point[1:]) - y = (x**3 - x*3 + curve.b).sqrt(curve.p) # Short Weierstrass + # Right now, we only support Short Weierstrass curves + y = (x**3 - x*3 + curve.b).sqrt(curve.p) if point_type == 0x02 and y.is_odd(): y = curve.p - y if point_type == 0x03 and y.is_even(): @@ -866,7 +1296,7 @@ def _import_public_der(curve_oid, ec_point): else: raise ValueError("Incorrect EC point encoding") - return construct(curve=curve_name, point_x=x, point_y=y) + return construct(curve=_curve_name, point_x=x, point_y=y) def _import_subjectPublicKeyInfo(encoded, *kwargs): @@ -877,38 +1307,51 @@ def _import_subjectPublicKeyInfo(encoded, *kwargs): # Parse the generic subjectPublicKeyInfo structure oid, ec_point, params = _expand_subject_public_key_info(encoded) - # ec_point must be an encoded OCTET STRING - # params is encoded ECParameters - - # We accept id-ecPublicKey, id-ecDH, id-ecMQV without making any - # distiction for now. - - # Restrictions can be captured in the key usage certificate - # extension - unrestricted_oid = "1.2.840.10045.2.1" - ecdh_oid = "1.3.132.1.12" - ecmqv_oid = "1.3.132.1.13" + nist_p_oids = ( + "1.2.840.10045.2.1", # id-ecPublicKey (unrestricted) + "1.3.132.1.12", # id-ecDH + "1.3.132.1.13" # id-ecMQV + ) + eddsa_oids = { + "1.3.101.112": ("Ed25519", _import_ed25519_public_key), # id-Ed25519 + "1.3.101.113": ("Ed448", _import_ed448_public_key) # id-Ed448 + } + + if oid in nist_p_oids: + # See RFC5480 + + # Parameters are mandatory and encoded as ECParameters + # ECParameters ::= CHOICE { + # namedCurve OBJECT IDENTIFIER + # -- implicitCurve NULL + # -- specifiedCurve SpecifiedECDomain + # } + # implicitCurve and specifiedCurve are not supported (as per RFC) + if not params: + raise ValueError("Missing ECC parameters for ECC OID %s" % oid) + try: + curve_oid = DerObjectId().decode(params).value + except ValueError: + raise ValueError("Error decoding namedCurve") - if oid not in (unrestricted_oid, ecdh_oid, ecmqv_oid): - raise UnsupportedEccFeature("Unsupported ECC purpose (OID: %s)" % oid) + # ECPoint ::= OCTET STRING + return _import_public_der(ec_point, curve_oid=curve_oid) - # Parameters are mandatory for all three types - if not params: - raise ValueError("Missing ECC parameters") + elif oid in eddsa_oids: + # See RFC8410 + curve_name, import_eddsa_public_key = eddsa_oids[oid] - # ECParameters ::= CHOICE { - # namedCurve OBJECT IDENTIFIER - # -- implicitCurve NULL - # -- specifiedCurve SpecifiedECDomain - # } - # - # implicitCurve and specifiedCurve are not supported (as per RFC) - curve_oid = DerObjectId().decode(params).value + # Parameters must be absent + if params: + raise ValueError("Unexpected ECC parameters for ECC OID %s" % oid) - return _import_public_der(curve_oid, ec_point) + x, y = import_eddsa_public_key(ec_point) + return construct(point_x=x, point_y=y, curve=curve_name) + else: + raise UnsupportedEccFeature("Unsupported ECC OID: %s" % oid) -def _import_private_der(encoded, passphrase, curve_oid=None): +def _import_rfc5915_der(encoded, passphrase, curve_oid=None): # See RFC5915 https://tools.ietf.org/html/rfc5915 # @@ -949,7 +1392,7 @@ def _import_private_der(encoded, passphrase, curve_oid=None): # Decode public key (if any) if len(private_key) == 4: public_key_enc = DerBitString(explicit=1).decode(private_key[3]).value - public_key = _import_public_der(curve_oid, public_key_enc) + public_key = _import_public_der(public_key_enc, curve_oid=curve_oid) point_x = public_key.pointQ.x point_y = public_key.pointQ.y else: @@ -961,28 +1404,30 @@ def _import_private_der(encoded, passphrase, curve_oid=None): def _import_pkcs8(encoded, passphrase): from Cryptodome.IO import PKCS8 - # From RFC5915, Section 1: - # - # Distributing an EC private key with PKCS#8 [RFC5208] involves including: - # a) id-ecPublicKey, id-ecDH, or id-ecMQV (from [RFC5480]) with the - # namedCurve as the parameters in the privateKeyAlgorithm field; and - # b) ECPrivateKey in the PrivateKey field, which is an OCTET STRING. - algo_oid, private_key, params = PKCS8.unwrap(encoded, passphrase) - # We accept id-ecPublicKey, id-ecDH, id-ecMQV without making any - # distiction for now. - unrestricted_oid = "1.2.840.10045.2.1" - ecdh_oid = "1.3.132.1.12" - ecmqv_oid = "1.3.132.1.13" - - if algo_oid not in (unrestricted_oid, ecdh_oid, ecmqv_oid): + nist_p_oids = ( + "1.2.840.10045.2.1", # id-ecPublicKey (unrestricted) + "1.3.132.1.12", # id-ecDH + "1.3.132.1.13" # id-ecMQV + ) + eddsa_oids = { + "1.3.101.112": "Ed25519", # id-Ed25519 + "1.3.101.113": "Ed448", # id-Ed448 + } + + if algo_oid in nist_p_oids: + curve_oid = DerObjectId().decode(params).value + return _import_rfc5915_der(private_key, passphrase, curve_oid) + elif algo_oid in eddsa_oids: + if params is not None: + raise ValueError("EdDSA ECC private key must not have parameters") + curve_oid = None + seed = DerOctetString().decode(private_key).payload + return construct(curve=eddsa_oids[algo_oid], seed=seed) + else: raise UnsupportedEccFeature("Unsupported ECC purpose (OID: %s)" % algo_oid) - curve_oid = DerObjectId().decode(params).value - - return _import_private_der(private_key, passphrase, curve_oid) - def _import_x509_cert(encoded, *kwargs): @@ -1007,7 +1452,7 @@ def _import_der(encoded, passphrase): pass try: - return _import_private_der(encoded, passphrase) + return _import_rfc5915_der(encoded, passphrase) except UnsupportedEccFeature as err: raise err except (ValueError, TypeError, IndexError): @@ -1024,22 +1469,49 @@ def _import_der(encoded, passphrase): def _import_openssh_public(encoded): - keystring = binascii.a2b_base64(encoded.split(b' ')[1]) + parts = encoded.split(b' ') + if len(parts) not in (2, 3): + raise ValueError("Not an openssh public key") - keyparts = [] - while len(keystring) > 4: - lk = struct.unpack(">I", keystring[:4])[0] - keyparts.append(keystring[4:4 + lk]) - keystring = keystring[4 + lk:] + try: + keystring = binascii.a2b_base64(parts[1]) + + keyparts = [] + while len(keystring) > 4: + lk = struct.unpack(">I", keystring[:4])[0] + keyparts.append(keystring[4:4 + lk]) + keystring = keystring[4 + lk:] + + if parts[0] != keyparts[0]: + raise ValueError("Mismatch in openssh public key") + + # NIST P curves + if parts[0].startswith(b"ecdsa-sha2-"): + + for curve_name, curve in _curves.items(): + if curve.openssh is None: + continue + if not curve.openssh.startswith("ecdsa-sha2"): + continue + middle = tobytes(curve.openssh.split("-")[2]) + if keyparts[1] == middle: + break + else: + raise ValueError("Unsupported ECC curve: " + middle) - for curve_name, curve in _curves.items(): - middle = tobytes(curve.openssh.split("-")[2]) - if keyparts[1] == middle: - break - else: - raise ValueError("Unsupported ECC curve") + ecc_key = _import_public_der(keyparts[2], curve_oid=curve.oid) - return _import_public_der(curve.oid, keyparts[2]) + # EdDSA + elif parts[0] == b"ssh-ed25519": + x, y = _import_ed25519_public_key(keyparts[1]) + ecc_key = construct(curve="Ed25519", point_x=x, point_y=y) + else: + raise ValueError("Unsupported SSH key type: " + parts[0]) + + except (IndexError, TypeError, binascii.Error): + raise ValueError("Error parsing SSH key type: " + parts[0]) + + return ecc_key def _import_openssh_private_ecc(data, password): @@ -1047,51 +1519,168 @@ def _import_openssh_private_ecc(data, password): from ._openssh import (import_openssh_private_generic, read_bytes, read_string, check_padding) - ssh_name, decrypted = import_openssh_private_generic(data, password) + key_type, decrypted = import_openssh_private_generic(data, password) + + eddsa_keys = { + "ssh-ed25519": ("Ed25519", _import_ed25519_public_key, 32), + } + + # https://datatracker.ietf.org/doc/html/draft-miller-ssh-agent-04 + if key_type.startswith("ecdsa-sha2"): + + ecdsa_curve_name, decrypted = read_string(decrypted) + if ecdsa_curve_name not in _curves: + raise UnsupportedEccFeature("Unsupported ECC curve %s" % ecdsa_curve_name) + curve = _curves[ecdsa_curve_name] + modulus_bytes = (curve.modulus_bits + 7) // 8 + + public_key, decrypted = read_bytes(decrypted) + + if bord(public_key[0]) != 4: + raise ValueError("Only uncompressed OpenSSH EC keys are supported") + if len(public_key) != 2 * modulus_bytes + 1: + raise ValueError("Incorrect public key length") - name, decrypted = read_string(decrypted) - if name not in _curves: - raise UnsupportedEccFeature("Unsupported ECC curve %s" % name) - curve = _curves[name] - modulus_bytes = (curve.modulus_bits + 7) // 8 + point_x = Integer.from_bytes(public_key[1:1+modulus_bytes]) + point_y = Integer.from_bytes(public_key[1+modulus_bytes:]) - public_key, decrypted = read_bytes(decrypted) + private_key, decrypted = read_bytes(decrypted) + d = Integer.from_bytes(private_key) - if bord(public_key[0]) != 4: - raise ValueError("Only uncompressed OpenSSH EC keys are supported") - if len(public_key) != 2 * modulus_bytes + 1: - raise ValueError("Incorrect public key length") + params = {'d': d, 'curve': ecdsa_curve_name} - point_x = Integer.from_bytes(public_key[1:1+modulus_bytes]) - point_y = Integer.from_bytes(public_key[1+modulus_bytes:]) - point = EccPoint(point_x, point_y, curve=name) + elif key_type in eddsa_keys: - private_key, decrypted = read_bytes(decrypted) - d = Integer.from_bytes(private_key) + curve_name, import_eddsa_public_key, seed_len = eddsa_keys[key_type] + + public_key, decrypted = read_bytes(decrypted) + point_x, point_y = import_eddsa_public_key(public_key) + + private_public_key, decrypted = read_bytes(decrypted) + seed = private_public_key[:seed_len] + + params = {'seed': seed, 'curve': curve_name} + else: + raise ValueError("Unsupport SSH agent key type:" + key_type) _, padded = read_string(decrypted) # Comment check_padding(padded) - return EccKey(curve=name, d=d, point=point) + return construct(point_x=point_x, point_y=point_y, **params) + + +def _import_ed25519_public_key(encoded): + """Import an Ed25519 ECC public key, encoded as raw bytes as described + in RFC8032_. + + Args: + encoded (bytes): + The Ed25519 public key to import. It must be 32 bytes long. + + Returns: + :class:`EccKey` : a new ECC key object + + Raises: + ValueError: when the given key cannot be parsed. + + .. _RFC8032: https://datatracker.ietf.org/doc/html/rfc8032 + """ + + if len(encoded) != 32: + raise ValueError("Incorrect length. Only Ed25519 public keys are supported.") + + p = Integer(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed) # 2**255 - 19 + d = 37095705934669439343138083508754565189542113879843219016388785533085940283555 + + y = bytearray(encoded) + x_lsb = y[31] >> 7 + y[31] &= 0x7F + point_y = Integer.from_bytes(y, byteorder='little') + if point_y >= p: + raise ValueError("Invalid Ed25519 key (y)") + if point_y == 1: + return 0, 1 + u = (point_y**2 - 1) % p + v = ((point_y**2 % p) * d + 1) % p + try: + v_inv = v.inverse(p) + x2 = (u * v_inv) % p + point_x = Integer._tonelli_shanks(x2, p) + if (point_x & 1) != x_lsb: + point_x = p - point_x + except ValueError: + raise ValueError("Invalid Ed25519 public key") + return point_x, point_y + + +def _import_ed448_public_key(encoded): + """Import an Ed448 ECC public key, encoded as raw bytes as described + in RFC8032_. + + Args: + encoded (bytes): + The Ed448 public key to import. It must be 57 bytes long. + + Returns: + :class:`EccKey` : a new ECC key object + + Raises: + ValueError: when the given key cannot be parsed. + + .. _RFC8032: https://datatracker.ietf.org/doc/html/rfc8032 + """ -def import_key(encoded, passphrase=None): + if len(encoded) != 57: + raise ValueError("Incorrect length. Only Ed448 public keys are supported.") + + p = Integer(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff) # 2**448 - 2**224 - 1 + d = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffff6756 + + y = encoded[:56] + x_lsb = bord(encoded[56]) >> 7 + point_y = Integer.from_bytes(y, byteorder='little') + if point_y >= p: + raise ValueError("Invalid Ed448 key (y)") + if point_y == 1: + return 0, 1 + + u = (point_y**2 - 1) % p + v = ((point_y**2 % p) * d - 1) % p + try: + v_inv = v.inverse(p) + x2 = (u * v_inv) % p + point_x = Integer._tonelli_shanks(x2, p) + if (point_x & 1) != x_lsb: + point_x = p - point_x + except ValueError: + raise ValueError("Invalid Ed448 public key") + return point_x, point_y + + +def import_key(encoded, passphrase=None, curve_name=None): """Import an ECC key (public or private). Args: encoded (bytes or multi-line string): The ECC key to import. + The function will try to automatically detect the right format. - An ECC **public** key can be: + Supported formats for an ECC **public** key: - - An X.509 certificate, binary (DER) or ASCII (PEM) - - An X.509 ``subjectPublicKeyInfo``, binary (DER) or ASCII (PEM) - - An OpenSSH line (e.g. the content of ``~/.ssh/id_ecdsa``, ASCII) + * X.509 certificate: binary (DER) or ASCII (PEM). + * X.509 ``subjectPublicKeyInfo``: binary (DER) or ASCII (PEM). + * SEC1_ (or X9.62), as ``bytes``. NIST P curves only. + You must also provide the ``curve_name`` (with a value from the `ECC table`_) + * OpenSSH line, defined in RFC5656_ and RFC8709_ (ASCII). + This is normally the content of files like ``~/.ssh/id_ecdsa.pub``. - An ECC **private** key can be: + Supported formats for an ECC **private** key: - - In binary format (DER, see section 3 of `RFC5915`_ or `PKCS#8`_) - - In ASCII format (PEM or `OpenSSH 6.5+`_) + * A binary ``ECPrivateKey`` structure, as defined in `RFC5915`_ (DER). + NIST P curves only. + * A `PKCS#8`_ structure (or the more recent Asymmetric Key Package, RFC5958_): binary (DER) or ASCII (PEM). + * `OpenSSH 6.5`_ and newer versions (ASCII). Private keys can be in the clear or password-protected. @@ -1099,9 +1688,21 @@ def import_key(encoded, passphrase=None): passphrase (byte string): The passphrase to use for decrypting a private key. - Encryption may be applied protected at the PEM level or at the PKCS#8 level. + Encryption may be applied protected at the PEM level (not recommended) + or at the PKCS#8 level (recommended). This parameter is ignored if the key in input is not encrypted. + curve_name (string): + For a SEC1 encoding only. This is the name of the curve, + as defined in the `ECC table`_. + + .. note:: + + To import EdDSA private and public keys, when encoded as raw ``bytes``, use: + + * :func:`Cryptodome.Signature.eddsa.import_public_key`, or + * :func:`Cryptodome.Signature.eddsa.import_private_key`. + Returns: :class:`EccKey` : a new ECC key object @@ -1109,11 +1710,15 @@ def import_key(encoded, passphrase=None): ValueError: when the given key cannot be parsed (possibly because the pass phrase is wrong). - .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt - .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt - .. _RFC5915: http://www.ietf.org/rfc/rfc5915.txt - .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt - .. _`OpenSSH 6.5+`: https://flak.tedunangst.com/post/new-openssh-key-format-and-bcrypt-pbkdf + .. _RFC1421: https://datatracker.ietf.org/doc/html/rfc1421 + .. _RFC1423: https://datatracker.ietf.org/doc/html/rfc1423 + .. _RFC5915: https://datatracker.ietf.org/doc/html/rfc5915 + .. _RFC5656: https://datatracker.ietf.org/doc/html/rfc5656 + .. _RFC8709: https://datatracker.ietf.org/doc/html/rfc8709 + .. _RFC5958: https://datatracker.ietf.org/doc/html/rfc5958 + .. _`PKCS#8`: https://datatracker.ietf.org/doc/html/rfc5208 + .. _`OpenSSH 6.5`: https://flak.tedunangst.com/post/new-openssh-key-format-and-bcrypt-pbkdf + .. _SEC1: https://www.secg.org/sec1-v2.pdf """ from Cryptodome.IO import PEM @@ -1135,12 +1740,11 @@ def import_key(encoded, passphrase=None): # Remove any EC PARAMETERS section # Ignore its content because the curve type must be already given in the key - if sys.version_info[:2] != (2, 6): - ecparams_start = "-----BEGIN EC PARAMETERS-----" - ecparams_end = "-----END EC PARAMETERS-----" - text_encoded = re.sub(ecparams_start + ".*?" + ecparams_end, "", - text_encoded, - flags=re.DOTALL) + ecparams_start = "-----BEGIN EC PARAMETERS-----" + ecparams_end = "-----END EC PARAMETERS-----" + text_encoded = re.sub(ecparams_start + ".*?" + ecparams_end, "", + text_encoded, + flags=re.DOTALL) der_encoded, marker, enc_flag = PEM.decode(text_encoded, passphrase) if enc_flag: @@ -1154,13 +1758,19 @@ def import_key(encoded, passphrase=None): return result # OpenSSH - if encoded.startswith(b'ecdsa-sha2-'): + if encoded.startswith((b'ecdsa-sha2-', b'ssh-ed25519')): return _import_openssh_public(encoded) # DER if len(encoded) > 0 and bord(encoded[0]) == 0x30: return _import_der(encoded, passphrase) + # SEC1 + if len(encoded) > 0 and bord(encoded[0]) in b'\x02\x03\x04': + if curve_name is None: + raise ValueError("No curve name was provided") + return _import_public_der(encoded, curve_name=curve_name) + raise ValueError("ECC key format is not supported") diff --git a/frozen_deps/Cryptodome/PublicKey/ECC.pyi b/frozen_deps/Cryptodome/PublicKey/ECC.pyi index b38b337..b0bfbec 100644 --- a/frozen_deps/Cryptodome/PublicKey/ECC.pyi +++ b/frozen_deps/Cryptodome/PublicKey/ECC.pyi @@ -38,7 +38,7 @@ class EccKey(object): @property def pointQ(self) -> EccPoint: ... def public_key(self) -> EccKey: ... - def export_key(self, **kwargs: Union[str, bytes, bool]) -> str: ... + def export_key(self, **kwargs: Union[str, bytes, bool]) -> Union[str,bytes]: ... _Curve = NamedTuple("_Curve", [('p', Integer), @@ -51,7 +51,7 @@ _Curve = NamedTuple("_Curve", [('p', Integer), ('oid', str), ('context', Any), ('desc', str), - ('openssh', str), + ('openssh', Union[str, None]), ]) _curves : Dict[str, _Curve] @@ -59,4 +59,8 @@ _curves : Dict[str, _Curve] def generate(**kwargs: Union[str, RNG]) -> EccKey: ... def construct(**kwargs: Union[str, int]) -> EccKey: ... -def import_key(encoded: Union[bytes, str], passphrase: Optional[str]=None) -> EccKey: ... +def import_key(encoded: Union[bytes, str], + passphrase: Optional[str]=None, + curve_name:Optional[str]=None) -> EccKey: ... +def _import_ed25519_public_key(encoded: bytes) -> EccKey: ... +def _import_ed448_public_key(encoded: bytes) -> EccKey: ... diff --git a/frozen_deps/Cryptodome/PublicKey/RSA.py b/frozen_deps/Cryptodome/PublicKey/RSA.py index 27331ca..bafe036 100644 --- a/frozen_deps/Cryptodome/PublicKey/RSA.py +++ b/frozen_deps/Cryptodome/PublicKey/RSA.py @@ -37,7 +37,7 @@ import struct from Cryptodome import Random from Cryptodome.Util.py3compat import tobytes, bord, tostr -from Cryptodome.Util.asn1 import DerSequence +from Cryptodome.Util.asn1 import DerSequence, DerNull from Cryptodome.Math.Numbers import Integer from Cryptodome.Math.Primality import (test_probable_prime, @@ -69,7 +69,9 @@ class RsaKey(object): :vartype q: integer :ivar u: Chinese remainder component (:math:`p^{-1} \text{mod } q`) - :vartype q: integer + :vartype u: integer + + :undocumented: exportKey, publickey """ def __init__(self, **kwargs): @@ -164,7 +166,7 @@ class RsaKey(object): m2 = pow(cp, self._dq, self._q) h = ((m2 - m1) * self._u) % self._q mp = h * self._p + m1 - # Step 4: Compute m = m**(r-1) mod n + # Step 4: Compute m = m' * (r**(-1)) mod n result = (r.inverse(self._n) * mp) % self._n # Verify no faults occurred if ciphertext != pow(result, self._e, self._n): @@ -182,7 +184,7 @@ class RsaKey(object): def can_sign(self): # legacy return True - def publickey(self): + def public_key(self): """A matching RSA public key. Returns: @@ -337,19 +339,22 @@ class RsaKey(object): if format == 'PEM' and protection is None: key_type = 'PRIVATE KEY' - binary_key = PKCS8.wrap(binary_key, oid, None) + binary_key = PKCS8.wrap(binary_key, oid, None, + key_params=DerNull()) else: key_type = 'ENCRYPTED PRIVATE KEY' if not protection: protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' binary_key = PKCS8.wrap(binary_key, oid, - passphrase, protection) + passphrase, protection, + key_params=DerNull()) passphrase = None else: key_type = "PUBLIC KEY" binary_key = _create_subject_public_key_info(oid, DerSequence([self.n, - self.e]) + self.e]), + DerNull() ) if format == 'DER': @@ -364,6 +369,7 @@ class RsaKey(object): # Backward compatibility exportKey = export_key + publickey = public_key # Methods defined in PyCryptodome that we don't support anymore def sign(self, M, K): diff --git a/frozen_deps/Cryptodome/PublicKey/RSA.pyi b/frozen_deps/Cryptodome/PublicKey/RSA.pyi index e4d0369..d436acf 100644 --- a/frozen_deps/Cryptodome/PublicKey/RSA.pyi +++ b/frozen_deps/Cryptodome/PublicKey/RSA.pyi @@ -24,7 +24,7 @@ class RsaKey(object): def has_private(self) -> bool: ... def can_encrypt(self) -> bool: ... # legacy def can_sign(self) -> bool:... # legacy - def publickey(self) -> RsaKey: ... + def public_key(self) -> RsaKey: ... def __eq__(self, other: object) -> bool: ... def __ne__(self, other: object) -> bool: ... def __getstate__(self) -> None: ... @@ -35,6 +35,7 @@ class RsaKey(object): # Backward compatibility exportKey = export_key + publickey = public_key def generate(bits: int, randfunc: Optional[RNG]=None, e: Optional[int]=65537) -> RsaKey: ... def construct(rsa_components: Union[Tuple[int, int], # n, e diff --git a/frozen_deps/Cryptodome/PublicKey/__init__.py b/frozen_deps/Cryptodome/PublicKey/__init__.py index 4d019bf..99b67a4 100644 --- a/frozen_deps/Cryptodome/PublicKey/__init__.py +++ b/frozen_deps/Cryptodome/PublicKey/__init__.py @@ -60,17 +60,16 @@ def _expand_subject_public_key_info(encoded): return algo_oid.value, spk, algo_params -def _create_subject_public_key_info(algo_oid, secret_key, params=None): +def _create_subject_public_key_info(algo_oid, public_key, params): if params is None: - params = DerNull() - - spki = DerSequence([ - DerSequence([ - DerObjectId(algo_oid), - params]), - DerBitString(secret_key) - ]) + algorithm = DerSequence([DerObjectId(algo_oid)]) + else: + algorithm = DerSequence([DerObjectId(algo_oid), params]) + + spki = DerSequence([algorithm, + DerBitString(public_key) + ]) return spki.encode() diff --git a/frozen_deps/Cryptodome/PublicKey/_ec_ws.abi3.so b/frozen_deps/Cryptodome/PublicKey/_ec_ws.abi3.so Binary files differnew file mode 100755 index 0000000..b6fd404 --- /dev/null +++ b/frozen_deps/Cryptodome/PublicKey/_ec_ws.abi3.so diff --git a/frozen_deps/Cryptodome/PublicKey/_ed25519.abi3.so b/frozen_deps/Cryptodome/PublicKey/_ed25519.abi3.so Binary files differnew file mode 100755 index 0000000..bd8bcc5 --- /dev/null +++ b/frozen_deps/Cryptodome/PublicKey/_ed25519.abi3.so diff --git a/frozen_deps/Cryptodome/PublicKey/_ed448.abi3.so b/frozen_deps/Cryptodome/PublicKey/_ed448.abi3.so Binary files differnew file mode 100755 index 0000000..ee47399 --- /dev/null +++ b/frozen_deps/Cryptodome/PublicKey/_ed448.abi3.so diff --git a/frozen_deps/Cryptodome/PublicKey/_x25519.abi3.so b/frozen_deps/Cryptodome/PublicKey/_x25519.abi3.so Binary files differnew file mode 100755 index 0000000..bbdc726 --- /dev/null +++ b/frozen_deps/Cryptodome/PublicKey/_x25519.abi3.so diff --git a/frozen_deps/Cryptodome/Signature/DSS.py b/frozen_deps/Cryptodome/Signature/DSS.py index 3dcbeb4..67f23ac 100644 --- a/frozen_deps/Cryptodome/Signature/DSS.py +++ b/frozen_deps/Cryptodome/Signature/DSS.py @@ -31,15 +31,15 @@ # POSSIBILITY OF SUCH DAMAGE. # =================================================================== -__all__ = ['new'] - - from Cryptodome.Util.asn1 import DerSequence from Cryptodome.Util.number import long_to_bytes from Cryptodome.Math.Numbers import Integer from Cryptodome.Hash import HMAC from Cryptodome.PublicKey.ECC import EccKey +from Cryptodome.PublicKey.DSA import DsaKey + +__all__ = ['DssSigScheme', 'new'] class DssSigScheme(object): @@ -75,24 +75,23 @@ class DssSigScheme(object): raise NotImplementedError("To be provided by subclasses") def sign(self, msg_hash): - """Produce the DSA/ECDSA signature of a message. + """Compute the DSA/ECDSA signature of a message. - :parameter msg_hash: + Args: + msg_hash (hash object): The hash that was carried out over the message. The object belongs to the :mod:`Cryptodome.Hash` package. + Under mode ``'fips-186-3'``, the hash must be a FIPS + approved secure hash (SHA-2 or SHA-3). - Under mode *'fips-186-3'*, the hash must be a FIPS - approved secure hash (SHA-1 or a member of the SHA-2 family), - of cryptographic strength appropriate for the DSA key. - For instance, a 3072/256 DSA key can only be used - in combination with SHA-512. - :type msg_hash: hash object - - :return: The signature as a *byte string* + :return: The signature as ``bytes`` :raise ValueError: if the hash algorithm is incompatible to the (EC)DSA key :raise TypeError: if the (EC)DSA key has no private half """ + if not self._key.has_private(): + raise TypeError("Private key is needed to sign") + if not self._valid_hash(msg_hash): raise ValueError("Hash is not sufficiently strong") @@ -106,7 +105,7 @@ class DssSigScheme(object): # Encode the signature into a single byte string if self._encoding == 'binary': output = b"".join([long_to_bytes(x, self._order_bytes) - for x in sig_pair]) + for x in sig_pair]) else: # Dss-sig ::= SEQUENCE { # r INTEGER, @@ -123,20 +122,15 @@ class DssSigScheme(object): def verify(self, msg_hash, signature): """Check if a certain (EC)DSA signature is authentic. - :parameter msg_hash: + Args: + msg_hash (hash object): The hash that was carried out over the message. This is an object belonging to the :mod:`Cryptodome.Hash` module. + Under mode ``'fips-186-3'``, the hash must be a FIPS + approved secure hash (SHA-2 or SHA-3). - Under mode *'fips-186-3'*, the hash must be a FIPS - approved secure hash (SHA-1 or a member of the SHA-2 family), - of cryptographic strength appropriate for the DSA key. - For instance, a 3072/256 DSA key can only be used in - combination with SHA-512. - :type msg_hash: hash object - - :parameter signature: - The signature that needs to be validated - :type signature: byte string + signature (``bytes``): + The signature that needs to be validated. :raise ValueError: if the signature is not authentic """ @@ -294,85 +288,77 @@ class FipsEcDsaSigScheme(DssSigScheme): randfunc=self._randfunc) def _valid_hash(self, msg_hash): - """Verify that SHA-[23] (256|384|512) bits are used to - match the security of P-256 (128 bits), P-384 (192 bits) - or P-521 (256 bits)""" + """Verify that the strength of the hash matches or exceeds + the strength of the EC. We fail if the hash is too weak.""" modulus_bits = self._key.pointQ.size_in_bits() - sha256 = ( "2.16.840.1.101.3.4.2.1", "2.16.840.1.101.3.4.2.8" ) - sha384 = ( "2.16.840.1.101.3.4.2.2", "2.16.840.1.101.3.4.2.9" ) - sha512 = ( "2.16.840.1.101.3.4.2.3", "2.16.840.1.101.3.4.2.10") - - if msg_hash.oid in sha256: - return modulus_bits <= 256 - elif msg_hash.oid in sha384: - return modulus_bits <= 384 - else: - return msg_hash.oid in sha512 + # SHS: SHA-2, SHA-3, truncated SHA-512 + sha224 = ("2.16.840.1.101.3.4.2.4", "2.16.840.1.101.3.4.2.7", "2.16.840.1.101.3.4.2.5") + sha256 = ("2.16.840.1.101.3.4.2.1", "2.16.840.1.101.3.4.2.8", "2.16.840.1.101.3.4.2.6") + sha384 = ("2.16.840.1.101.3.4.2.2", "2.16.840.1.101.3.4.2.9") + sha512 = ("2.16.840.1.101.3.4.2.3", "2.16.840.1.101.3.4.2.10") + shs = sha224 + sha256 + sha384 + sha512 + + try: + result = msg_hash.oid in shs + except AttributeError: + result = False + return result def new(key, mode, encoding='binary', randfunc=None): - """Create a signature object :class:`DSS_SigScheme` that + """Create a signature object :class:`DssSigScheme` that can perform (EC)DSA signature or verification. .. note:: Refer to `NIST SP 800 Part 1 Rev 4`_ (or newer release) for an overview of the recommended key lengths. - :parameter key: - The key to use for computing the signature (*private* keys only) - or verifying one: it must be either - :class:`Cryptodome.PublicKey.DSA` or :class:`Cryptodome.PublicKey.ECC`. - - For DSA keys, let ``L`` and ``N`` be the bit lengths of the modulus ``p`` - and of ``q``: the pair ``(L,N)`` must appear in the following list, - in compliance to section 4.2 of `FIPS 186-4`_: - - - (1024, 160) *legacy only; do not create new signatures with this* - - (2048, 224) *deprecated; do not create new signatures with this* - - (2048, 256) - - (3072, 256) + Args: + key (:class:`Cryptodome.PublicKey.DSA` or :class:`Cryptodome.PublicKey.ECC`): + The key to use for computing the signature (*private* keys only) + or for verifying one. + For DSA keys, let ``L`` and ``N`` be the bit lengths of the modulus ``p`` + and of ``q``: the pair ``(L,N)`` must appear in the following list, + in compliance to section 4.2 of `FIPS 186-4`_: - For ECC, only keys over P-256, P384, and P-521 are accepted. - :type key: - a key object + - (1024, 160) *legacy only; do not create new signatures with this* + - (2048, 224) *deprecated; do not create new signatures with this* + - (2048, 256) + - (3072, 256) - :parameter mode: - The parameter can take these values: + For ECC, only keys over P-224, P-256, P-384, and P-521 are accepted. - - *'fips-186-3'*. The signature generation is randomized and carried out - according to `FIPS 186-3`_: the nonce ``k`` is taken from the RNG. - - *'deterministic-rfc6979'*. The signature generation is not - randomized. See RFC6979_. - :type mode: - string + mode (string): + The parameter can take these values: - :parameter encoding: - How the signature is encoded. This value determines the output of - :meth:`sign` and the input to :meth:`verify`. + - ``'fips-186-3'``. The signature generation is randomized and carried out + according to `FIPS 186-3`_: the nonce ``k`` is taken from the RNG. + - ``'deterministic-rfc6979'``. The signature generation is not + randomized. See RFC6979_. - The following values are accepted: + encoding (string): + How the signature is encoded. This value determines the output of + :meth:`sign` and the input to :meth:`verify`. - - *'binary'* (default), the signature is the raw concatenation - of ``r`` and ``s``. It is defined in the IEEE P.1363 standard. + The following values are accepted: - For DSA, the size in bytes of the signature is ``N/4`` bytes - (e.g. 64 for ``N=256``). + - ``'binary'`` (default), the signature is the raw concatenation + of ``r`` and ``s``. It is defined in the IEEE P.1363 standard. + For DSA, the size in bytes of the signature is ``N/4`` bytes + (e.g. 64 for ``N=256``). + For ECDSA, the signature is always twice the length of a point + coordinate (e.g. 64 bytes for P-256). - For ECDSA, the signature is always twice the length of a point - coordinate (e.g. 64 bytes for P-256). + - ``'der'``, the signature is a ASN.1 DER SEQUENCE + with two INTEGERs (``r`` and ``s``). It is defined in RFC3279_. + The size of the signature is variable. - - *'der'*, the signature is a ASN.1 DER SEQUENCE - with two INTEGERs (``r`` and ``s``). It is defined in RFC3279_. - The size of the signature is variable. - :type encoding: string - - :parameter randfunc: - A function that returns random *byte strings*, of a given length. - If omitted, the internal RNG is used. - Only applicable for the *'fips-186-3'* mode. - :type randfunc: callable + randfunc (callable): + A function that returns random ``bytes``, of a given length. + If omitted, the internal RNG is used. + Only applicable for the *'fips-186-3'* mode. .. _FIPS 186-3: http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf .. _FIPS 186-4: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf @@ -393,9 +379,13 @@ def new(key, mode, encoding='binary', randfunc=None): if isinstance(key, EccKey): order = key._curve.order private_key_attr = 'd' - else: + if key._curve.name == "ed25519": + raise ValueError("ECC key is not on a NIST P curve") + elif isinstance(key, DsaKey): order = Integer(key.q) private_key_attr = 'x' + else: + raise ValueError("Unsupported key type " + str(type(key))) if key.has_private(): private_key = getattr(key, private_key_attr) diff --git a/frozen_deps/Cryptodome/Signature/__init__.py b/frozen_deps/Cryptodome/Signature/__init__.py index da028a5..11ca64c 100644 --- a/frozen_deps/Cryptodome/Signature/__init__.py +++ b/frozen_deps/Cryptodome/Signature/__init__.py @@ -33,4 +33,4 @@ A collection of standardized protocols to carry out digital signatures. """ -__all__ = ['PKCS1_v1_5', 'PKCS1_PSS', 'DSS', 'pkcs1_15', 'pss'] +__all__ = ['PKCS1_v1_5', 'PKCS1_PSS', 'DSS', 'pkcs1_15', 'pss', 'eddsa'] diff --git a/frozen_deps/Cryptodome/Signature/eddsa.py b/frozen_deps/Cryptodome/Signature/eddsa.py new file mode 100644 index 0000000..e80a866 --- /dev/null +++ b/frozen_deps/Cryptodome/Signature/eddsa.py @@ -0,0 +1,341 @@ +# =================================================================== +# +# Copyright (c) 2022, Legrandin <[email protected]> +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# =================================================================== + +from Cryptodome.Math.Numbers import Integer + +from Cryptodome.Hash import SHA512, SHAKE256 +from Cryptodome.Util.py3compat import bchr, is_bytes +from Cryptodome.PublicKey.ECC import (EccKey, + construct, + _import_ed25519_public_key, + _import_ed448_public_key) + + +def import_public_key(encoded): + """Import an EdDSA ECC public key, when encoded as raw ``bytes`` as described + in RFC8032. + + Args: + encoded (bytes): + The EdDSA public key to import. + It must be 32 bytes for Ed25519, and 57 bytes for Ed448. + + Returns: + :class:`Cryptodome.PublicKey.EccKey` : a new ECC key object. + + Raises: + ValueError: when the given key cannot be parsed. + """ + + if len(encoded) == 32: + x, y = _import_ed25519_public_key(encoded) + curve_name = "Ed25519" + elif len(encoded) == 57: + x, y = _import_ed448_public_key(encoded) + curve_name = "Ed448" + else: + raise ValueError("Not an EdDSA key (%d bytes)" % len(encoded)) + return construct(curve=curve_name, point_x=x, point_y=y) + + +def import_private_key(encoded): + """Import an EdDSA ECC private key, when encoded as raw ``bytes`` as described + in RFC8032. + + Args: + encoded (bytes): + The EdDSA private key to import. + It must be 32 bytes for Ed25519, and 57 bytes for Ed448. + + Returns: + :class:`Cryptodome.PublicKey.EccKey` : a new ECC key object. + + Raises: + ValueError: when the given key cannot be parsed. + """ + + if len(encoded) == 32: + curve_name = "ed25519" + elif len(encoded) == 57: + curve_name = "ed448" + else: + raise ValueError("Incorrect length. Only EdDSA private keys are supported.") + + # Note that the private key is truly a sequence of random bytes, + # so we cannot check its correctness in any way. + + return construct(seed=encoded, curve=curve_name) + + +class EdDSASigScheme(object): + """An EdDSA signature object. + Do not instantiate directly. + Use :func:`Cryptodome.Signature.eddsa.new`. + """ + + def __init__(self, key, context): + """Create a new EdDSA object. + + Do not instantiate this object directly, + use `Cryptodome.Signature.DSS.new` instead. + """ + + self._key = key + self._context = context + self._A = key._export_eddsa() + self._order = key._curve.order + + def can_sign(self): + """Return ``True`` if this signature object can be used + for signing messages.""" + + return self._key.has_private() + + def sign(self, msg_or_hash): + """Compute the EdDSA signature of a message. + + Args: + msg_or_hash (bytes or a hash object): + The message to sign (``bytes``, in case of *PureEdDSA*) or + the hash that was carried out over the message (hash object, for *HashEdDSA*). + + The hash object must be :class:`Cryptodome.Hash.SHA512` for Ed25519, + and :class:`Cryptodome.Hash.SHAKE256` object for Ed448. + + :return: The signature as ``bytes``. It is always 64 bytes for Ed25519, and 114 bytes for Ed448. + :raise TypeError: if the EdDSA key has no private half + """ + + if not self._key.has_private(): + raise TypeError("Private key is needed to sign") + + if self._key._curve.name == "ed25519": + ph = isinstance(msg_or_hash, SHA512.SHA512Hash) + if not (ph or is_bytes(msg_or_hash)): + raise TypeError("'msg_or_hash' must be bytes of a SHA-512 hash") + eddsa_sign_method = self._sign_ed25519 + + elif self._key._curve.name == "ed448": + ph = isinstance(msg_or_hash, SHAKE256.SHAKE256_XOF) + if not (ph or is_bytes(msg_or_hash)): + raise TypeError("'msg_or_hash' must be bytes of a SHAKE256 hash") + eddsa_sign_method = self._sign_ed448 + + else: + raise ValueError("Incorrect curve for EdDSA") + + return eddsa_sign_method(msg_or_hash, ph) + + def _sign_ed25519(self, msg_or_hash, ph): + + if self._context or ph: + flag = int(ph) + # dom2(flag, self._context) + dom2 = b'SigEd25519 no Ed25519 collisions' + bchr(flag) + \ + bchr(len(self._context)) + self._context + else: + dom2 = b'' + + PHM = msg_or_hash.digest() if ph else msg_or_hash + + # See RFC 8032, section 5.1.6 + + # Step 2 + r_hash = SHA512.new(dom2 + self._key._prefix + PHM).digest() + r = Integer.from_bytes(r_hash, 'little') % self._order + # Step 3 + R_pk = EccKey(point=r * self._key._curve.G)._export_eddsa() + # Step 4 + k_hash = SHA512.new(dom2 + R_pk + self._A + PHM).digest() + k = Integer.from_bytes(k_hash, 'little') % self._order + # Step 5 + s = (r + k * self._key.d) % self._order + + return R_pk + s.to_bytes(32, 'little') + + def _sign_ed448(self, msg_or_hash, ph): + + flag = int(ph) + # dom4(flag, self._context) + dom4 = b'SigEd448' + bchr(flag) + \ + bchr(len(self._context)) + self._context + + PHM = msg_or_hash.read(64) if ph else msg_or_hash + + # See RFC 8032, section 5.2.6 + + # Step 2 + r_hash = SHAKE256.new(dom4 + self._key._prefix + PHM).read(114) + r = Integer.from_bytes(r_hash, 'little') % self._order + # Step 3 + R_pk = EccKey(point=r * self._key._curve.G)._export_eddsa() + # Step 4 + k_hash = SHAKE256.new(dom4 + R_pk + self._A + PHM).read(114) + k = Integer.from_bytes(k_hash, 'little') % self._order + # Step 5 + s = (r + k * self._key.d) % self._order + + return R_pk + s.to_bytes(57, 'little') + + def verify(self, msg_or_hash, signature): + """Check if an EdDSA signature is authentic. + + Args: + msg_or_hash (bytes or a hash object): + The message to verify (``bytes``, in case of *PureEdDSA*) or + the hash that was carried out over the message (hash object, for *HashEdDSA*). + + The hash object must be :class:`Cryptodome.Hash.SHA512` object for Ed25519, + and :class:`Cryptodome.Hash.SHAKE256` for Ed448. + + signature (``bytes``): + The signature that needs to be validated. + It must be 64 bytes for Ed25519, and 114 bytes for Ed448. + + :raise ValueError: if the signature is not authentic + """ + + if self._key._curve.name == "ed25519": + ph = isinstance(msg_or_hash, SHA512.SHA512Hash) + if not (ph or is_bytes(msg_or_hash)): + raise TypeError("'msg_or_hash' must be bytes of a SHA-512 hash") + eddsa_verify_method = self._verify_ed25519 + + elif self._key._curve.name == "ed448": + ph = isinstance(msg_or_hash, SHAKE256.SHAKE256_XOF) + if not (ph or is_bytes(msg_or_hash)): + raise TypeError("'msg_or_hash' must be bytes of a SHAKE256 hash") + eddsa_verify_method = self._verify_ed448 + + else: + raise ValueError("Incorrect curve for EdDSA") + + return eddsa_verify_method(msg_or_hash, signature, ph) + + def _verify_ed25519(self, msg_or_hash, signature, ph): + + if len(signature) != 64: + raise ValueError("The signature is not authentic (length)") + + if self._context or ph: + flag = int(ph) + dom2 = b'SigEd25519 no Ed25519 collisions' + bchr(flag) + \ + bchr(len(self._context)) + self._context + else: + dom2 = b'' + + PHM = msg_or_hash.digest() if ph else msg_or_hash + + # Section 5.1.7 + + # Step 1 + try: + R = import_public_key(signature[:32]).pointQ + except ValueError: + raise ValueError("The signature is not authentic (R)") + s = Integer.from_bytes(signature[32:], 'little') + if s > self._order: + raise ValueError("The signature is not authentic (S)") + # Step 2 + k_hash = SHA512.new(dom2 + signature[:32] + self._A + PHM).digest() + k = Integer.from_bytes(k_hash, 'little') % self._order + # Step 3 + point1 = s * 8 * self._key._curve.G + # OPTIMIZE: with double-scalar multiplication, with no SCA + # countermeasures because it is public values + point2 = 8 * R + k * 8 * self._key.pointQ + if point1 != point2: + raise ValueError("The signature is not authentic") + + def _verify_ed448(self, msg_or_hash, signature, ph): + + if len(signature) != 114: + raise ValueError("The signature is not authentic (length)") + + flag = int(ph) + # dom4(flag, self._context) + dom4 = b'SigEd448' + bchr(flag) + \ + bchr(len(self._context)) + self._context + + PHM = msg_or_hash.read(64) if ph else msg_or_hash + + # Section 5.2.7 + + # Step 1 + try: + R = import_public_key(signature[:57]).pointQ + except ValueError: + raise ValueError("The signature is not authentic (R)") + s = Integer.from_bytes(signature[57:], 'little') + if s > self._order: + raise ValueError("The signature is not authentic (S)") + # Step 2 + k_hash = SHAKE256.new(dom4 + signature[:57] + self._A + PHM).read(114) + k = Integer.from_bytes(k_hash, 'little') % self._order + # Step 3 + point1 = s * 8 * self._key._curve.G + # OPTIMIZE: with double-scalar multiplication, with no SCA + # countermeasures because it is public values + point2 = 8 * R + k * 8 * self._key.pointQ + if point1 != point2: + raise ValueError("The signature is not authentic") + + +def new(key, mode, context=None): + """Create a signature object :class:`EdDSASigScheme` that + can perform or verify an EdDSA signature. + + Args: + key (:class:`Cryptodome.PublicKey.ECC` object: + The key to use for computing the signature (*private* keys only) + or for verifying one. + The key must be on the curve ``Ed25519`` or ``Ed448``. + + mode (string): + This parameter must be ``'rfc8032'``. + + context (bytes): + Up to 255 bytes of `context <https://datatracker.ietf.org/doc/html/rfc8032#page-41>`_, + which is a constant byte string to segregate different protocols or + different applications of the same key. + """ + + if not isinstance(key, EccKey) or not key._is_eddsa(): + raise ValueError("EdDSA can only be used with EdDSA keys") + + if mode != 'rfc8032': + raise ValueError("Mode must be 'rfc8032'") + + if context is None: + context = b'' + elif len(context) > 255: + raise ValueError("Context for EdDSA must not be longer than 255 bytes") + + return EdDSASigScheme(key, context) diff --git a/frozen_deps/Cryptodome/Signature/eddsa.pyi b/frozen_deps/Cryptodome/Signature/eddsa.pyi new file mode 100644 index 0000000..bf985c4 --- /dev/null +++ b/frozen_deps/Cryptodome/Signature/eddsa.pyi @@ -0,0 +1,21 @@ +from typing import Union, Optional +from typing_extensions import Protocol +from Cryptodome.PublicKey.ECC import EccKey + +class Hash(Protocol): + def digest(self) -> bytes: ... + +class XOF(Protocol): + def read(self, len: int) -> bytes: ... + +def import_public_key(encoded: bytes) -> EccKey: ... +def import_private_key(encoded: bytes) -> EccKey: ... + +class EdDSASigScheme(object): + + def __init__(self, key: EccKey, context: bytes) -> None: ... + def can_sign(self) -> bool: ... + def sign(self, msg_or_hash: Union[bytes, Hash, XOF]) -> bytes: ... + def verify(self, msg_or_hash: Union[bytes, Hash, XOF], signature: bytes) -> None: ... + +def new(key: EccKey, mode: bytes, context: Optional[bytes]=None) -> EdDSASigScheme: ... diff --git a/frozen_deps/Cryptodome/Signature/pkcs1_15.py b/frozen_deps/Cryptodome/Signature/pkcs1_15.py index f572f85..ae9257e 100644 --- a/frozen_deps/Cryptodome/Signature/pkcs1_15.py +++ b/frozen_deps/Cryptodome/Signature/pkcs1_15.py @@ -202,7 +202,7 @@ def _EMSA_PKCS1_V1_5_ENCODE(msg_hash, emLen, with_hash_parameters=True): # We need at least 11 bytes for the remaining data: 3 fixed bytes and # at least 8 bytes of padding). if emLen<len(digestInfo)+11: - raise TypeError("Selected hash algorith has a too long digest (%d bytes)." % len(digest)) + raise TypeError("Selected hash algorithm has a too long digest (%d bytes)." % len(digest)) PS = b'\xFF' * (emLen - len(digestInfo) - 3) return b'\x00\x01' + PS + b'\x00' + digestInfo diff --git a/frozen_deps/Cryptodome/Util/Counter.py b/frozen_deps/Cryptodome/Util/Counter.py index 423f91f..c67bc95 100644 --- a/frozen_deps/Cryptodome/Util/Counter.py +++ b/frozen_deps/Cryptodome/Util/Counter.py @@ -45,6 +45,7 @@ def new(nbits, prefix=b"", suffix=b"", initial_value=1, little_endian=False, all used. initial_value (integer): The initial value of the counter. Default value is 1. + Its length in bits must not exceed the argument ``nbits``. little_endian (boolean): If ``True``, the counter number will be encoded in little endian format. If ``False`` (default), in big endian format. @@ -61,6 +62,12 @@ def new(nbits, prefix=b"", suffix=b"", initial_value=1, little_endian=False, all if (nbits % 8) != 0: raise ValueError("'nbits' must be a multiple of 8") + iv_bl = initial_value.bit_length() + if iv_bl > nbits: + raise ValueError("Initial value takes %d bits but it is longer than " + "the counter (%d bits)" % + (iv_bl, nbits)) + # Ignore wraparound return {"counter_len": nbits // 8, "prefix": prefix, diff --git a/frozen_deps/Cryptodome/Util/Padding.py b/frozen_deps/Cryptodome/Util/Padding.py index 1c353d1..b525475 100644 --- a/frozen_deps/Cryptodome/Util/Padding.py +++ b/frozen_deps/Cryptodome/Util/Padding.py @@ -82,6 +82,8 @@ def unpad(padded_data, block_size, style='pkcs7'): """ pdata_len = len(padded_data) + if pdata_len == 0: + raise ValueError("Zero-length input cannot be unpadded") if pdata_len % block_size: raise ValueError("Input data is not padded") if style in ('pkcs7', 'x923'): diff --git a/frozen_deps/Cryptodome/Util/_cpuid_c.abi3.so b/frozen_deps/Cryptodome/Util/_cpuid_c.abi3.so Binary files differnew file mode 100755 index 0000000..60f1e26 --- /dev/null +++ b/frozen_deps/Cryptodome/Util/_cpuid_c.abi3.so diff --git a/frozen_deps/Cryptodome/Util/_raw_api.py b/frozen_deps/Cryptodome/Util/_raw_api.py index 9423738..c2e0187 100644 --- a/frozen_deps/Cryptodome/Util/_raw_api.py +++ b/frozen_deps/Cryptodome/Util/_raw_api.py @@ -28,6 +28,7 @@ # POSSIBILITY OF SUCH DAMAGE. # =================================================================== +import os import abc import sys from Cryptodome.Util.py3compat import byte_string @@ -50,10 +51,7 @@ else: extension_suffixes = machinery.EXTENSION_SUFFIXES # Which types with buffer interface we support (apart from byte strings) -if sys.version_info[0] == 2 and sys.version_info[1] < 7: - _buffer_type = (bytearray) -else: - _buffer_type = (bytearray, memoryview) +_buffer_type = (bytearray, memoryview) class _VoidPointer(object): @@ -69,9 +67,6 @@ class _VoidPointer(object): try: - if sys.version_info[0] == 2 and sys.version_info[1] < 7: - raise ImportError("CFFI is only supported with Python 2.7+") - # Starting from v2.18, pycparser (used by cffi for in-line ABI mode) # stops working correctly when PYOPTIMIZE==2 or the parameter -OO is # passed. In that case, we fall back to ctypes. @@ -98,7 +93,10 @@ try: @cdecl, the C function declarations. """ - lib = ffi.dlopen(name) + if hasattr(ffi, "RTLD_DEEPBIND") and not os.getenv('PYCRYPTODOME_DISABLE_DEEPBIND'): + lib = ffi.dlopen(name, ffi.RTLD_DEEPBIND) + else: + lib = ffi.dlopen(name) ffi.cdef(cdecl) return lib @@ -108,6 +106,7 @@ try: c_ulonglong = c_ulong c_uint = c_ulong + c_ubyte = c_ulong def c_size_t(x): """Convert a Python integer to size_t""" @@ -171,6 +170,11 @@ except ImportError: null_pointer = None cached_architecture = [] + def c_ubyte(c): + if not (0 <= c < 256): + raise OverflowError() + return ctypes.c_ubyte(c) + def load_lib(name, cdecl): if not cached_architecture: # platform.architecture() creates a subprocess, so caching the @@ -193,12 +197,7 @@ except ImportError: # ---- Get raw pointer --- - if sys.version_info[0] == 2 and sys.version_info[1] == 6: - # ctypes in 2.6 does not define c_ssize_t. Replacing it - # with c_size_t keeps the structure correctely laid out - _c_ssize_t = c_size_t - else: - _c_ssize_t = ctypes.c_ssize_t + _c_ssize_t = ctypes.c_ssize_t _PyBUF_SIMPLE = 0 _PyObject_GetBuffer = ctypes.pythonapi.PyObject_GetBuffer @@ -207,7 +206,7 @@ except ImportError: _c_ssize_p = ctypes.POINTER(_c_ssize_t) # See Include/object.h for CPython - # and https://github.com/pallets/click/blob/master/click/_winconsole.py + # and https://github.com/pallets/click/blob/master/src/click/_winconsole.py class _Py_buffer(ctypes.Structure): _fields_ = [ ('buf', c_void_p), @@ -235,7 +234,7 @@ except ImportError: buf = _Py_buffer() _PyObject_GetBuffer(obj, byref(buf), _PyBUF_SIMPLE) try: - buffer_type = c_ubyte * buf.len + buffer_type = ctypes.c_ubyte * buf.len return buffer_type.from_address(buf.buf) finally: _PyBuffer_Release(byref(buf)) @@ -260,7 +259,6 @@ except ImportError: return VoidPointer_ctypes() backend = "ctypes" - del ctypes class SmartPointer(object): @@ -301,27 +299,21 @@ def load_pycryptodome_raw_lib(name, cdecl): for ext in extension_suffixes: try: filename = basename + ext - return load_lib(pycryptodome_filename(dir_comps, filename), - cdecl) + full_name = pycryptodome_filename(dir_comps, filename) + if not os.path.isfile(full_name): + attempts.append("Not found '%s'" % filename) + continue + return load_lib(full_name, cdecl) except OSError as exp: - attempts.append("Trying '%s': %s" % (filename, str(exp))) + attempts.append("Cannot load '%s': %s" % (filename, str(exp))) raise OSError("Cannot load native module '%s': %s" % (name, ", ".join(attempts))) -if sys.version_info[:2] != (2, 6): - - def is_buffer(x): - """Return True if object x supports the buffer interface""" - return isinstance(x, (bytes, bytearray, memoryview)) - - def is_writeable_buffer(x): - return (isinstance(x, bytearray) or - (isinstance(x, memoryview) and not x.readonly)) - -else: +def is_buffer(x): + """Return True if object x supports the buffer interface""" + return isinstance(x, (bytes, bytearray, memoryview)) - def is_buffer(x): - return isinstance(x, (bytes, bytearray)) - def is_writeable_buffer(x): - return isinstance(x, bytearray) +def is_writeable_buffer(x): + return (isinstance(x, bytearray) or + (isinstance(x, memoryview) and not x.readonly)) diff --git a/frozen_deps/Cryptodome/Util/_strxor.abi3.so b/frozen_deps/Cryptodome/Util/_strxor.abi3.so Binary files differnew file mode 100755 index 0000000..c028978 --- /dev/null +++ b/frozen_deps/Cryptodome/Util/_strxor.abi3.so diff --git a/frozen_deps/Cryptodome/Util/asn1.py b/frozen_deps/Cryptodome/Util/asn1.py index 18e080c..a88f087 100644 --- a/frozen_deps/Cryptodome/Util/asn1.py +++ b/frozen_deps/Cryptodome/Util/asn1.py @@ -137,7 +137,7 @@ class DerObject(object): self._tag_octet = 0xA0 | self._convertTag(explicit) self._inner_tag_octet = 0x20 * constructed | asn1Id return - + self._tag_octet = 0x20 * constructed | asn1Id def _convertTag(self, tag): @@ -442,7 +442,7 @@ class DerSequence(DerObject): only_non_negative (boolean): If ``True``, negative integers are not counted in. """ - + items = [x for x in self._seq if _is_number(x, only_non_negative)] return len(items) @@ -704,21 +704,20 @@ class DerBitString(DerObject): An example of encoding is: >>> from Cryptodome.Util.asn1 import DerBitString - >>> from binascii import hexlify, unhexlify - >>> bs_der = DerBitString(b'\\xaa') - >>> bs_der.value += b'\\xbb' - >>> print hexlify(bs_der.encode()) + >>> bs_der = DerBitString(b'\\xAA') + >>> bs_der.value += b'\\xBB' + >>> print(bs_der.encode().hex()) - which will show ``040300aabb``, the DER encoding for the bit string + which will show ``030300aabb``, the DER encoding for the bit string ``b'\\xAA\\xBB'``. For decoding: - >>> s = unhexlify(b'040300aabb') + >>> s = bytes.fromhex('030300aabb') >>> try: >>> bs_der = DerBitString() >>> bs_der.decode(s) - >>> print hexlify(bs_der.value) + >>> print(bs_der.value.hex()) >>> except ValueError: >>> print "Not a valid DER BIT STRING" @@ -751,7 +750,7 @@ class DerBitString(DerObject): def encode(self): """Return the DER BIT STRING, fully encoded as a - binary string.""" + byte string.""" # Add padding count byte self.payload = b'\x00' + self.value diff --git a/frozen_deps/Cryptodome/Util/number.py b/frozen_deps/Cryptodome/Util/number.py index 0367fdc..5af85a3 100644 --- a/frozen_deps/Cryptodome/Util/number.py +++ b/frozen_deps/Cryptodome/Util/number.py @@ -28,7 +28,7 @@ import math import sys import struct from Cryptodome import Random -from Cryptodome.Util.py3compat import _memoryview, iter_range +from Cryptodome.Util.py3compat import iter_range # Backward compatibility _fastmath = None @@ -141,14 +141,19 @@ def inverse(u, v): def getPrime(N, randfunc=None): """Return a random N-bit prime number. + N must be an integer larger than 1. If randfunc is omitted, then :meth:`Random.get_random_bytes` is used. """ if randfunc is None: randfunc = Random.get_random_bytes - number=getRandomNBitInteger(N, randfunc) | 1 - while (not isPrime(number, randfunc=randfunc)): - number=number+2 + if N < 2: + raise ValueError("N must be larger than 1") + + while True: + number = getRandomNBitInteger(N, randfunc) | 1 + if isPrime(number, randfunc=randfunc): + break return number @@ -375,46 +380,72 @@ def isPrime(N, false_positive_prob=1e-6, randfunc=None): import struct def long_to_bytes(n, blocksize=0): - """Convert an integer to a byte string. + """Convert a positive integer to a byte string using big endian encoding. - In Python 3.2+, use the native method instead:: + If :data:`blocksize` is absent or zero, the byte string will + be of minimal length. - >>> n.to_bytes(blocksize, 'big') + Otherwise, the length of the byte string is guaranteed to be a multiple + of :data:`blocksize`. If necessary, zeroes (``\\x00``) are added at the left. - For instance:: + .. note:: + In Python 3, if you are sure that :data:`n` can fit into + :data:`blocksize` bytes, you can simply use the native method instead:: - >>> n = 80 - >>> n.to_bytes(2, 'big') - b'\x00P' + >>> n.to_bytes(blocksize, 'big') - If the optional :data:`blocksize` is provided and greater than zero, - the byte string is padded with binary zeros (on the front) so that - the total length of the output is a multiple of blocksize. + For instance:: - If :data:`blocksize` is zero or not provided, the byte string will - be of minimal length. + >>> n = 80 + >>> n.to_bytes(2, 'big') + b'\\x00P' + + However, and unlike this ``long_to_bytes()`` function, + an ``OverflowError`` exception is raised if :data:`n` does not fit. """ - # after much testing, this algorithm was deemed to be the fastest - s = b'' - n = int(n) + + if n < 0 or blocksize < 0: + raise ValueError("Values must be non-negative") + + result = [] pack = struct.pack - while n > 0: - s = pack('>I', n & 0xffffffff) + s + + # Fill the first block independently from the value of n + bsr = blocksize + while bsr >= 8: + result.insert(0, pack('>Q', n & 0xFFFFFFFFFFFFFFFF)) + n = n >> 64 + bsr -= 8 + + while bsr >= 4: + result.insert(0, pack('>I', n & 0xFFFFFFFF)) n = n >> 32 - # strip off leading zeros - for i in range(len(s)): - if s[i] != b'\x00'[0]: - break + bsr -= 4 + + while bsr > 0: + result.insert(0, pack('>B', n & 0xFF)) + n = n >> 8 + bsr -= 1 + + if n == 0: + if len(result) == 0: + bresult = b'\x00' + else: + bresult = b''.join(result) else: - # only happens when n == 0 - s = b'\x00' - i = 0 - s = s[i:] - # add back some pad bytes. this could be done more efficiently w.r.t. the - # de-padding being done above, but sigh... - if blocksize > 0 and len(s) % blocksize: - s = (blocksize - len(s) % blocksize) * b'\x00' + s - return s + # The encoded number exceeds the block size + while n > 0: + result.insert(0, pack('>Q', n & 0xFFFFFFFFFFFFFFFF)) + n = n >> 64 + result[0] = result[0].lstrip(b'\x00') + bresult = b''.join(result) + # bresult has minimum length here + if blocksize > 0: + target_len = ((len(bresult) - 1) // blocksize + 1) * blocksize + bresult = b'\x00' * (target_len - len(bresult)) + bresult + + return bresult + def bytes_to_long(s): """Convert a byte string to a long integer (big endian). @@ -439,7 +470,7 @@ def bytes_to_long(s): if sys.version_info[0:3] < (2, 7, 4): if isinstance(s, bytearray): s = bytes(s) - elif isinstance(s, _memoryview): + elif isinstance(s, memoryview): s = s.tobytes() length = len(s) diff --git a/frozen_deps/Cryptodome/Util/py3compat.py b/frozen_deps/Cryptodome/Util/py3compat.py index 40ef752..9a982e9 100644 --- a/frozen_deps/Cryptodome/Util/py3compat.py +++ b/frozen_deps/Cryptodome/Util/py3compat.py @@ -78,6 +78,8 @@ if sys.version_info[0] == 2: return s elif isinstance(s, bytearray): return bytes(s) + elif isinstance(s, memoryview): + return s.tobytes() else: return ''.join(s) def tostr(bs): @@ -85,17 +87,11 @@ if sys.version_info[0] == 2: def byte_string(s): return isinstance(s, str) - # In Pyton 2.x, StringIO is a stand-alone module - from StringIO import StringIO as BytesIO + from StringIO import StringIO + BytesIO = StringIO from sys import maxint - if sys.version_info[1] < 7: - import types - _memoryview = types.NoneType - else: - _memoryview = memoryview - iter_range = xrange def is_native_int(x): @@ -104,8 +100,15 @@ if sys.version_info[0] == 2: def is_string(x): return isinstance(x, basestring) + def is_bytes(x): + return isinstance(x, str) or \ + isinstance(x, bytearray) or \ + isinstance(x, memoryview) + ABC = abc.ABCMeta('ABC', (object,), {'__slots__': ()}) + FileNotFoundError = IOError + else: def b(s): return s.encode("latin-1") # utf-8 would cause some side-effects we don't want @@ -125,6 +128,8 @@ else: return bytes(s) elif isinstance(s,str): return s.encode(encoding) + elif isinstance(s, memoryview): + return s.tobytes() else: return bytes([s]) def tostr(bs): @@ -132,12 +137,10 @@ else: def byte_string(s): return isinstance(s, bytes) - # In Python 3.x, StringIO is a sub-module of io from io import BytesIO + from io import StringIO from sys import maxsize as maxint - _memoryview = memoryview - iter_range = range def is_native_int(x): @@ -146,14 +149,21 @@ else: def is_string(x): return isinstance(x, str) + def is_bytes(x): + return isinstance(x, bytes) or \ + isinstance(x, bytearray) or \ + isinstance(x, memoryview) + from abc import ABC + FileNotFoundError = FileNotFoundError + def _copy_bytes(start, end, seq): """Return an immutable copy of a sequence (byte string, byte array, memoryview) in a certain interval [start:seq]""" - if isinstance(seq, _memoryview): + if isinstance(seq, memoryview): return seq[start:end].tobytes() elif isinstance(seq, bytearray): return bytes(seq[start:end]) diff --git a/frozen_deps/Cryptodome/Util/py3compat.pyi b/frozen_deps/Cryptodome/Util/py3compat.pyi index 3297dc0..74e04a2 100644 --- a/frozen_deps/Cryptodome/Util/py3compat.pyi +++ b/frozen_deps/Cryptodome/Util/py3compat.pyi @@ -13,23 +13,21 @@ def bytestring(x: Any) -> bool: ... def is_native_int(s: Any) -> bool: ... def is_string(x: Any) -> bool: ... +def is_bytes(x: Any) -> bool: ... def BytesIO(b: bytes) -> IO[bytes]: ... +def StringIO(s: str) -> IO[str]: ... if sys.version_info[0] == 2: from sys import maxint iter_range = xrange - if sys.version_info[1] < 7: - import types - _memoryview = types.NoneType - else: - _memoryview = memoryview - else: from sys import maxsize as maxint iter_range = range - _memoryview = memoryview +class FileNotFoundError: + def __init__(self, err: int, msg: str, filename: str) -> None: + pass def _copy_bytes(start: Optional[int], end: Optional[int], seq: Buffer) -> bytes: ... diff --git a/frozen_deps/Cryptodome/Util/strxor.py b/frozen_deps/Cryptodome/Util/strxor.py index 91fb4c9..6b16155 100644 --- a/frozen_deps/Cryptodome/Util/strxor.py +++ b/frozen_deps/Cryptodome/Util/strxor.py @@ -32,7 +32,8 @@ from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, c_size_t, create_string_buffer, get_raw_buffer, c_uint8_ptr, is_writeable_buffer) -_raw_strxor = load_pycryptodome_raw_lib("Cryptodome.Util._strxor", +_raw_strxor = load_pycryptodome_raw_lib( + "Cryptodome.Util._strxor", """ void strxor(const uint8_t *in1, const uint8_t *in2, @@ -45,33 +46,38 @@ _raw_strxor = load_pycryptodome_raw_lib("Cryptodome.Util._strxor", def strxor(term1, term2, output=None): - """XOR two byte strings. - + """From two byte strings of equal length, + create a third one which is the byte-by-byte XOR of the two. + Args: term1 (bytes/bytearray/memoryview): - The first term of the XOR operation. + The first byte string to XOR. term2 (bytes/bytearray/memoryview): - The second term of the XOR operation. + The second byte string to XOR. output (bytearray/memoryview): - The location where the result must be written to. + The location where the result will be written to. + It must have the same length as ``term1`` and ``term2``. If ``None``, the result is returned. :Return: - If ``output`` is ``None``, a new ``bytes`` string with the result. + If ``output`` is ``None``, a new byte string with the result. Otherwise ``None``. + + .. note:: + ``term1`` and ``term2`` must have the same length. """ if len(term1) != len(term2): raise ValueError("Only byte strings of equal length can be xored") - + if output is None: result = create_string_buffer(len(term1)) else: # Note: output may overlap with either input result = output - + if not is_writeable_buffer(output): raise TypeError("output must be a bytearray or a writeable memoryview") - + if len(term1) != len(output): raise ValueError("output must have the same length as the input" " (%d bytes)" % len(term1)) @@ -88,15 +94,19 @@ def strxor(term1, term2, output=None): def strxor_c(term, c, output=None): - """XOR a byte string with a repeated sequence of characters. + """From a byte string, create a second one of equal length + where each byte is XOR-red with the same value. Args: - term(bytes/bytearray/memoryview): - The first term of the XOR operation. - c (bytes): - The byte that makes up the second term of the XOR operation. - output (None or bytearray/memoryview): - If not ``None``, the location where the result is stored into. + term(bytes/bytearray/memoryview): + The byte string to XOR. + c (int): + Every byte in the string will be XOR-ed with this value. + It must be between 0 and 255 (included). + output (None or bytearray/memoryview): + The location where the result will be written to. + It must have the same length as ``term``. + If ``None``, the result is returned. Return: If ``output`` is ``None``, a new ``bytes`` string with the result. @@ -105,16 +115,16 @@ def strxor_c(term, c, output=None): if not 0 <= c < 256: raise ValueError("c must be in range(256)") - + if output is None: result = create_string_buffer(len(term)) else: # Note: output may overlap with either input result = output - + if not is_writeable_buffer(output): raise TypeError("output must be a bytearray or a writeable memoryview") - + if len(term) != len(output): raise ValueError("output must have the same length as the input" " (%d bytes)" % len(term)) @@ -134,4 +144,3 @@ def strxor_c(term, c, output=None): def _strxor_direct(term1, term2, result): """Very fast XOR - check conditions!""" _raw_strxor.strxor(term1, term2, result, c_size_t(len(term1))) - diff --git a/frozen_deps/Cryptodome/__init__.py b/frozen_deps/Cryptodome/__init__.py index bb08e39..9c2f83b 100644 --- a/frozen_deps/Cryptodome/__init__.py +++ b/frozen_deps/Cryptodome/__init__.py @@ -1,6 +1,6 @@ __all__ = ['Cipher', 'Hash', 'Protocol', 'PublicKey', 'Util', 'Signature', 'IO', 'Math'] -version_info = (3, 9, '9') +version_info = (3, 15, '0') __version__ = ".".join([str(x) for x in version_info]) |