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 | |
parent | be4dc086591c9bced04a507d127c83811c5700c4 (diff) |
v0.1.7
189 files changed, 10692 insertions, 1501 deletions
diff --git a/AppImageBuilder.yml b/AppImageBuilder.yml index 45243f0..1e5cbf5 100644 --- a/AppImageBuilder.yml +++ b/AppImageBuilder.yml @@ -5,7 +5,7 @@ script: - cp bech32.py AppDir/usr/src - cp keytree.py AppDir/usr/src - cp setup.py AppDir/usr/src - - python3.9 -m pip install -t AppDir/usr/src/frozen_deps AppDir/usr/src + - python3.10 -m pip install -t AppDir/usr/src/frozen_deps AppDir/usr/src - rm -rf AppDir/usr/src/frozen_deps/Cryptodome/SelfTest @@ -16,8 +16,8 @@ AppDir: id: org.ted.keytree name: keytree icon: utilities-terminal - version: 0.1.6 - exec: usr/bin/python3.9 + version: 0.1.7 + exec: usr/bin/python3.10 exec_args: $APPDIR/usr/src/keytree.py $@ apt: @@ -28,7 +28,7 @@ AppDir: - sourceline: 'deb [arch=amd64] http://ppa.launchpad.net/deadsnakes/ppa/ubuntu focal main' key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0xBA6932366A755776' include: - - python3.9 + - python3.10 exclude: [] runtime: @@ -1,7 +1,7 @@ keytree.py ========== -- Make sure you have Python = 3.9 +- Make sure you have Python = 3.10 - CD into the cloned repo Examples 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]) diff --git a/frozen_deps/_pysha3.cpython-310-x86_64-linux-gnu.so b/frozen_deps/_pysha3.cpython-310-x86_64-linux-gnu.so Binary files differnew file mode 100755 index 0000000..dd659ee --- /dev/null +++ b/frozen_deps/_pysha3.cpython-310-x86_64-linux-gnu.so diff --git a/frozen_deps/base58-2.1.1.dist-info/COPYING b/frozen_deps/base58-2.1.1.dist-info/COPYING new file mode 100644 index 0000000..342bd62 --- /dev/null +++ b/frozen_deps/base58-2.1.1.dist-info/COPYING @@ -0,0 +1,19 @@ +Copyright (c) 2015 David Keijser + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/frozen_deps/base58-2.1.1.dist-info/INSTALLER b/frozen_deps/base58-2.1.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/frozen_deps/base58-2.1.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/frozen_deps/base58-2.1.1.dist-info/METADATA b/frozen_deps/base58-2.1.1.dist-info/METADATA new file mode 100644 index 0000000..8c2e644 --- /dev/null +++ b/frozen_deps/base58-2.1.1.dist-info/METADATA @@ -0,0 +1,86 @@ +Metadata-Version: 2.1 +Name: base58 +Version: 2.1.1 +Summary: Base58 and Base58Check implementation. +Home-page: https://github.com/keis/base58 +Author: David Keijser +Author-email: [email protected] +License: MIT +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Requires-Python: >=3.5 +Description-Content-Type: text/markdown +License-File: COPYING +Provides-Extra: tests +Requires-Dist: mypy ; extra == 'tests' +Requires-Dist: PyHamcrest (>=2.0.2) ; extra == 'tests' +Requires-Dist: pytest (>=4.6) ; extra == 'tests' +Requires-Dist: pytest-benchmark ; extra == 'tests' +Requires-Dist: pytest-cov ; extra == 'tests' +Requires-Dist: pytest-flake8 ; extra == 'tests' + +# base58 + +[![PyPI Version][pypi-image]](https://pypi.python.org/pypi?name=base58&:action=display) +[![PyPI Downloads][pypi-downloads-image]](https://pypi.python.org/pypi?name=base58&:action=display) +[![Build Status][travis-image]](https://travis-ci.org/keis/base58) +[![Coverage Status][coveralls-image]](https://coveralls.io/r/keis/base58?branch=master) + +Base58 and Base58Check implementation compatible with what is used by the +bitcoin network. Any other alternative alphabet (like the XRP one) can be used. + +Starting from version 2.0.0 **python2 is no longer supported** the 1.x series +will remain supported but no new features will be added. + + +## Command line usage + + $ printf "hello world" | base58 + StV1DL6CwTryKyV + + $ printf "hello world" | base58 -c + 3vQB7B6MrGQZaxCuFg4oh + + $ printf "3vQB7B6MrGQZaxCuFg4oh" | base58 -dc + hello world + + $ printf "4vQB7B6MrGQZaxCuFg4oh" | base58 -dc + Invalid checksum + + +## Module usage + + >>> import base58 + >>> base58.b58encode(b'hello world') + b'StV1DL6CwTryKyV' + >>> base58.b58decode(b'StV1DL6CwTryKyV') + b'hello world' + >>> base58.b58encode_check(b'hello world') + b'3vQB7B6MrGQZaxCuFg4oh' + >>> base58.b58decode_check(b'3vQB7B6MrGQZaxCuFg4oh') + b'hello world' + >>> base58.b58decode_check(b'4vQB7B6MrGQZaxCuFg4oh') + Traceback (most recent call last): + File "<stdin>", line 1, in <module> + File "base58.py", line 89, in b58decode_check + raise ValueError("Invalid checksum") + ValueError: Invalid checksum + # Use another alphabet. Here, using the built-in XRP/Ripple alphabet. + # RIPPLE_ALPHABET is provided as an option for compatibility with existing code + # It is recommended to use XRP_ALPHABET instead + >>> base58.b58encode(b'hello world', alphabet=base58.XRP_ALPHABET) + b'StVrDLaUATiyKyV' + >>> base58.b58decode(b'StVrDLaUATiyKyV', alphabet=base58.XRP_ALPHABET) + b'hello world' + + +[pypi-image]: https://img.shields.io/pypi/v/base58.svg?style=flat +[pypi-downloads-image]: https://img.shields.io/pypi/dm/base58.svg?style=flat +[travis-image]: https://img.shields.io/travis/keis/base58.svg?style=flat +[coveralls-image]: https://img.shields.io/coveralls/keis/base58.svg?style=flat + + diff --git a/frozen_deps/base58-2.1.1.dist-info/RECORD b/frozen_deps/base58-2.1.1.dist-info/RECORD new file mode 100644 index 0000000..e012c40 --- /dev/null +++ b/frozen_deps/base58-2.1.1.dist-info/RECORD @@ -0,0 +1,13 @@ +../../bin/base58,sha256=WWCKVkDRfe64OQtfsxf-b0PQGIclDIn84xO8gkjjWcY,213
+base58-2.1.1.dist-info/COPYING,sha256=z0aU8EC3oxzY7D280LWDpgHA1MN94Ba-eqCgbjpqOlQ,1057
+base58-2.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+base58-2.1.1.dist-info/METADATA,sha256=OMWrHqwTJgXypOcS0Tdx3XBESZ5Y4terXPUBfg07Vd0,3078
+base58-2.1.1.dist-info/RECORD,,
+base58-2.1.1.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
+base58-2.1.1.dist-info/entry_points.txt,sha256=7WwcggBSeBwcC22-LkpqMOCaPdey0nOG3QEaKok403Y,49
+base58-2.1.1.dist-info/top_level.txt,sha256=BVSonMPECDcX_2XqQ7iILRqitlshZNNEmLCEWlpvUvI,7
+base58/__init__.py,sha256=7HvHX-In8vY_AJIGqPYhMRuibYkWNEU0yiquapCDKpw,4059
+base58/__main__.py,sha256=3rysVZfdK6HC2DXk5XdRQntooSxBevh_yO1oi_Pqb4M,1183
+base58/__pycache__/__init__.cpython-310.pyc,,
+base58/__pycache__/__main__.cpython-310.pyc,,
+base58/py.typed,sha256=dcrsqJrcYfTX-ckLFJMTaj6mD8aDe2u0tkQG-ZYxnEg,26
diff --git a/frozen_deps/base58-2.1.1.dist-info/WHEEL b/frozen_deps/base58-2.1.1.dist-info/WHEEL new file mode 100644 index 0000000..5bad85f --- /dev/null +++ b/frozen_deps/base58-2.1.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/frozen_deps/base58-2.1.1.dist-info/entry_points.txt b/frozen_deps/base58-2.1.1.dist-info/entry_points.txt new file mode 100644 index 0000000..dc6d6a2 --- /dev/null +++ b/frozen_deps/base58-2.1.1.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +base58 = base58.__main__:main + diff --git a/frozen_deps/base58-2.1.1.dist-info/top_level.txt b/frozen_deps/base58-2.1.1.dist-info/top_level.txt new file mode 100644 index 0000000..b4c9d71 --- /dev/null +++ b/frozen_deps/base58-2.1.1.dist-info/top_level.txt @@ -0,0 +1 @@ +base58 diff --git a/frozen_deps/base58/__init__.py b/frozen_deps/base58/__init__.py index a86ceb7..929014f 100644 --- a/frozen_deps/base58/__init__.py +++ b/frozen_deps/base58/__init__.py @@ -13,7 +13,7 @@ from functools import lru_cache from hashlib import sha256 from typing import Mapping, Union -__version__ = '2.1.0' +__version__ = '2.1.1' # 58 character alphabet used BITCOIN_ALPHABET = \ @@ -102,7 +102,7 @@ def b58decode_int( decimal = decimal * base + map[char] except KeyError as e: raise ValueError( - "Invalid character <{char}>".format(char=chr(e.args[0])) + "Invalid character {!r}".format(chr(e.args[0])) ) from None return decimal diff --git a/frozen_deps/bin/keytree.py b/frozen_deps/bin/keytree.py index ab38ed6..885299a 100755 --- a/frozen_deps/bin/keytree.py +++ b/frozen_deps/bin/keytree.py @@ -46,7 +46,7 @@ import hashlib import hmac import unicodedata import json -from getpass import getpass +from getpass import getpass as _getpass import bech32 import mnemonic @@ -55,7 +55,16 @@ from ecdsa.ecdsa import generator_secp256k1 from ecdsa.ellipticcurve import INFINITY from base58 import b58encode, b58decode from sha3 import keccak_256 +from uuid import uuid4 from Cryptodome.Cipher import AES +from Cryptodome.Util import Counter + + +def getpass(prompt): + if sys.stdin.isatty(): + return _getpass(prompt) + else: + return sys.stdin.readline() def sha256(data): @@ -215,10 +224,15 @@ def load_from_keystore(filename): with open(filename, "r") as f: try: parsed = json.load(f) + version = parsed['version'] + try: + if parsed['keys'][0]['type'] != 'mnemonic': + raise KeytreeError("not a mnemonic keystore file") + except KeyError: + pass ciphertext = b58decode(parsed['keys'][0]['key'])[:-4] iv = b58decode(parsed['keys'][0]['iv'])[:-4] salt = b58decode(parsed['salt'])[:-4] - tag = b58decode(parsed['pass_hash'])[:-4] passwd = getpass('Enter the password to unlock keystore: ').encode('utf-8') key = hashlib.pbkdf2_hmac( 'sha256', @@ -226,9 +240,14 @@ def load_from_keystore(filename): a = AES.new(key, mode=AES.MODE_GCM, nonce=iv).update(salt) - if tag != sha256(passwd + sha256(passwd + salt)): + if version == '5.0': + tag = b58decode(parsed['pass_hash'])[:-4] + if tag != sha256(passwd + sha256(passwd + salt)): + raise KeytreeError("incorrect keystore password") + try: + return a.decrypt_and_verify(ciphertext[:-16], ciphertext[-16:]).decode('utf-8') + except: raise KeytreeError("incorrect keystore password") - return a.decrypt_and_verify(ciphertext[:-16], ciphertext[-16:]).decode('utf-8') except KeytreeError as e: raise e except: @@ -251,7 +270,7 @@ def save_to_keystore(filename, words): raise KeytreeError("mismatching passwords") iv = os.urandom(12) salt = os.urandom(16) - pass_hash = sha256(passwd + sha256(passwd + salt)) + # pass_hash = sha256(passwd + sha256(passwd + salt)) key = hashlib.pbkdf2_hmac( 'sha256', sha256(passwd + salt), salt, 200000) @@ -261,68 +280,157 @@ def save_to_keystore(filename, words): (c, t) = a.encrypt_and_digest(words.encode('utf-8')) ciphertext = c + t json.dump({ - 'version': "5.0", + 'version': "6.0", + 'activeIndex': 0, 'keys': [ - {'key': cb58encode(ciphertext), 'iv': cb58encode(iv)}], + { + 'key': cb58encode(ciphertext), + 'iv': cb58encode(iv), + 'type': 'mnemonic' + }], 'salt': cb58encode(salt), - 'pass_hash': cb58encode(pass_hash) + # 'pass_hash': cb58encode(pass_hash) }, f) except FileNotFoundError: raise KeytreeError("failed while saving") +# MEW keystore format (version 3.0) +def save_to_mew(priv_keys, n=1 << 18, p=1, r=8, dklen=32): + try: + passwd = getpass('Enter the password for saving (utf-8): ').encode('utf-8') + passwd2 = getpass('Enter the password again (utf-8): ').encode('utf-8') + if passwd != passwd2: + raise KeytreeError("mismatching passwords") + + for priv_key in priv_keys: + addr = get_eth_addr(priv_key.get_verifying_key()) + priv_key = priv_key.to_string() + with open("mew-{}.json".format(addr), "w") as f: + iv = os.urandom(16) + salt = os.urandom(16) + + m = 128 * r * (n + p + 2) + dk = hashlib.scrypt(passwd, salt=salt, + n=n, r=r, p=p, dklen=dklen, maxmem=m) + obj = AES.new(dk[:dklen >> 1], + mode=AES.MODE_CTR, + counter=Counter.new( + 128, + initial_value=int.from_bytes(iv, 'big'))) + enc_pk = obj.encrypt(priv_key) + + # generate MAC + h = keccak_256() + h.update(dk[len(dk) >> 1:]) + h.update(enc_pk) + mac = h.digest() + + crypto = { + 'ciphertext': enc_pk.hex(), + 'cipherparams': {'iv': iv.hex()}, + 'cipher': 'aes-128-ctr', + 'kdf': 'scrypt', + 'kdfparams': {'dklen': dklen, + 'salt': salt.hex(), + 'n': n, + 'r': r, + 'p': p}, + 'mac': mac.hex()} + json.dump({ + 'version': 3, + 'id': str(uuid4()), + 'address': addr, + 'Crypto': crypto}, f) + except FileNotFoundError: + raise KeytreeError("failed while saving") + + +metamask_path = r"44'/60'/0'/0" +avax_path = r"44'/9000'/0'/0" + if __name__ == '__main__': parser = argparse.ArgumentParser(description='Derive BIP32 key pairs from BIP39 mnemonic') - parser.add_argument('--load-keystore', type=str, default=None, help='load mnemonic from a keystore file (AVAX Wallet compatible)') - parser.add_argument('--save-keystore', type=str, default=None, help='save mnemonic to a keystore file (AVAX Wallet compatible)') + parser.add_argument('--load', type=str, default=None, help='load mnemonic from a file (AVAX Wallet compatible)') + parser.add_argument('--save', type=str, default=None, help='save mnemonic to a file (AVAX Wallet compatible)') + parser.add_argument('--export-mew', action='store_true', default=False, help='export keys to MEW keystore files (mnemonic is NOT saved, only keys are saved)') parser.add_argument('--show-private', action='store_true', default=False, help='also show private keys and the mnemonic') - parser.add_argument('--custom-words', action='store_true', default=False, help='use an arbitrary word combination as mnemonic') - parser.add_argument('--account-path', default="44'/9000'/0'/0", help="path prefix for key deriving (e.g. \"0/1'/2\")") + parser.add_argument('--custom', action='store_true', default=False, help='use an arbitrary word combination as mnemonic') + parser.add_argument('--seed', action='store_true', default=False, help='load mnemonic from seed') + parser.add_argument('--path', default=avax_path, help="path prefix for key deriving (e.g. \"{}\" for Metamask)".format(metamask_path)) + parser.add_argument('--metamask', action='store_true', default=False, help="use metamask path for key deriving (synonym to `--path \"{}\"`)".format(metamask_path)) parser.add_argument('--gen-mnemonic', action='store_true', default=False, help='generate a mnemonic (instead of taking an input)') parser.add_argument('--lang', type=str, default="english", help='language for mnemonic words') parser.add_argument('--start-idx', type=int, default=0, help='the start index for keys') parser.add_argument('--end-idx', type=int, default=1, help='the end index for keys (exclusive)') parser.add_argument('--hrp', type=str, default="avax", help='HRP (Human Readable Prefix, defined by Bech32)') - args = parser.parse_args() - + args, unknown = parser.parse_known_args() try: + for arg in unknown: + if len(arg) > 0: + raise KeytreeError("invalid argument: `{}`".format(arg)) try: if args.gen_mnemonic: mgen = mnemonic.Mnemonic(args.lang) words = mgen.generate(256) else: - if args.load_keystore: - words = load_from_keystore(args.load_keystore) - else: + if args.load: + words = load_from_keystore(args.load) + elif not args.seed: words = getpass('Enter the mnemonic: ').strip() - if not args.custom_words: + if not args.custom: mchecker = mnemonic.Mnemonic(args.lang) if not mchecker.check(words): raise KeytreeError("invalid mnemonic") except FileNotFoundError: raise KeytreeError("invalid language") + if args.end_idx < args.start_idx: + args.end_idx = args.start_idx + 1 + if args.seed: + seedstr = getpass('Enter the seed: ').strip() + try: + seed = bytes.fromhex(seedstr) + if len(seed) != 64: + raise ValueError + except ValueError: + raise KeytreeError("invalid seed") + else: + seed = hashlib.pbkdf2_hmac('sha512', unicodedata.normalize('NFKD', words).encode("utf-8"), b"mnemonic", 2048) if args.show_private or args.gen_mnemonic: - print("KEEP THIS PRIVATE: {}".format(words)) - seed = hashlib.pbkdf2_hmac('sha512', unicodedata.normalize('NFKD', words).encode("utf-8"), b"mnemonic", 2048) + if not args.seed: + print("KEEP THIS PRIVATE (mnemonic): {}".format(words)) + print("KEEP THIS PRIVATE (seed): {}".format(seed.hex())) gen = BIP32(seed) if args.start_idx < 0 or args.end_idx < 0: raise KeytreeError("invalid start/end index") + keys = [] for i in range(args.start_idx, args.end_idx): - path = "m/{}/{}".format(args.account_path, i) + path = "m/{}/{}".format(metamask_path if args.metamask else args.path, i) priv = gen.derive(path) + keys.append(priv) pub = priv.get_verifying_key() cpub = pub.to_string(encoding="compressed") if args.show_private: - print("{}.priv(raw/ETH) 0x{}".format(i, priv.to_string().hex())) + print("{}.priv(raw/ETH/AVAX-X) 0x{}".format(i, priv.to_string().hex())) print("{}.priv(BTC) {}".format(i, get_privkey_btc(priv))) - print("{}.addr(AVAX) X-{}".format(i, bech32.bech32_encode(args.hrp, bech32.convertbits(ripemd160(sha256(cpub)), 8, 5)))) + print("{}.addr(AVAX-X/P) {}".format(i, bech32.bech32_encode(args.hrp, bech32.convertbits(ripemd160(sha256(cpub)), 8, 5)))) + + path2 = "m/{}/{}".format(metamask_path, i) + priv2 = gen.derive(path2) + pub2 = priv2.get_verifying_key() + if args.show_private: + print("{}.priv(AVAX-C) 0x{}".format(i, priv2.to_string().hex())) + print("{}.addr(AVAX-C) 0x{}".format(i, get_eth_addr(pub2))) + print("{}.addr(BTC) {}".format(i, get_btc_addr(pub))) - print("{}.addr(ETH) {}".format(i, get_eth_addr(pub))) - if args.save_keystore: - save_to_keystore(args.save_keystore, words) - print("Saved to keystore file: {}".format(args.save_keystore)) + print("{}.addr(ETH) 0x{}".format(i, get_eth_addr(pub))) + if args.export_mew: + save_to_mew(keys) + if args.save: + save_to_keystore(args.save, words) + print("Saved to keystore file: {}".format(args.save)) except KeytreeError as e: sys.stderr.write("error: {}\n".format(str(e))) sys.exit(1) diff --git a/frozen_deps/ecdsa-0.18.0.dist-info/INSTALLER b/frozen_deps/ecdsa-0.18.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/frozen_deps/ecdsa-0.18.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/frozen_deps/ecdsa-0.18.0.dist-info/LICENSE b/frozen_deps/ecdsa-0.18.0.dist-info/LICENSE new file mode 100644 index 0000000..474479a --- /dev/null +++ b/frozen_deps/ecdsa-0.18.0.dist-info/LICENSE @@ -0,0 +1,24 @@ +"python-ecdsa" Copyright (c) 2010 Brian Warner + +Portions written in 2005 by Peter Pearson and placed in the public domain. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/frozen_deps/ecdsa-0.18.0.dist-info/METADATA b/frozen_deps/ecdsa-0.18.0.dist-info/METADATA new file mode 100644 index 0000000..fa1c5fe --- /dev/null +++ b/frozen_deps/ecdsa-0.18.0.dist-info/METADATA @@ -0,0 +1,675 @@ +Metadata-Version: 2.1 +Name: ecdsa +Version: 0.18.0 +Summary: ECDSA cryptographic signature library (pure python) +Home-page: http://github.com/tlsfuzzer/python-ecdsa +Author: Brian Warner +Author-email: [email protected] +License: MIT +Platform: UNKNOWN +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.* +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: six (>=1.9.0) +Provides-Extra: gmpy +Requires-Dist: gmpy ; extra == 'gmpy' +Provides-Extra: gmpy2 +Requires-Dist: gmpy2 ; extra == 'gmpy2' + +# Pure-Python ECDSA and ECDH + +[![Build Status](https://github.com/tlsfuzzer/python-ecdsa/workflows/GitHub%20CI/badge.svg?branch=master)](https://github.com/tlsfuzzer/python-ecdsa/actions?query=workflow%3A%22GitHub+CI%22+branch%3Amaster) +[![Documentation Status](https://readthedocs.org/projects/ecdsa/badge/?version=latest)](https://ecdsa.readthedocs.io/en/latest/?badge=latest) +[![Coverage Status](https://coveralls.io/repos/github/tlsfuzzer/python-ecdsa/badge.svg?branch=master)](https://coveralls.io/github/tlsfuzzer/python-ecdsa?branch=master) +![condition coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/tomato42/9b6ca1f3410207fbeca785a178781651/raw/python-ecdsa-condition-coverage.json) +[![Language grade: Python](https://img.shields.io/lgtm/grade/python/g/tlsfuzzer/python-ecdsa.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/tlsfuzzer/python-ecdsa/context:python) +[![Total alerts](https://img.shields.io/lgtm/alerts/g/tlsfuzzer/python-ecdsa.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/tlsfuzzer/python-ecdsa/alerts/) +[![Latest Version](https://img.shields.io/pypi/v/ecdsa.svg?style=flat)](https://pypi.python.org/pypi/ecdsa/) +![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg?style=flat) + + +This is an easy-to-use implementation of ECC (Elliptic Curve Cryptography) +with support for ECDSA (Elliptic Curve Digital Signature Algorithm), +EdDSA (Edwards-curve Digital Signature Algorithm) and ECDH +(Elliptic Curve Diffie-Hellman), implemented purely in Python, released under +the MIT license. With this library, you can quickly create key pairs (signing +key and verifying key), sign messages, and verify the signatures. You can +also agree on a shared secret key based on exchanged public keys. +The keys and signatures are very short, making them easy to handle and +incorporate into other protocols. + +**NOTE: This library should not be used in production settings, see [Security](#Security) for more details.** + +## Features + +This library provides key generation, signing, verifying, and shared secret +derivation for five +popular NIST "Suite B" GF(p) (_prime field_) curves, with key lengths of 192, +224, 256, 384, and 521 bits. The "short names" for these curves, as known by +the OpenSSL tool (`openssl ecparam -list_curves`), are: `prime192v1`, +`secp224r1`, `prime256v1`, `secp384r1`, and `secp521r1`. It includes the +256-bit curve `secp256k1` used by Bitcoin. There is also support for the +regular (non-twisted) variants of Brainpool curves from 160 to 512 bits. The +"short names" of those curves are: `brainpoolP160r1`, `brainpoolP192r1`, +`brainpoolP224r1`, `brainpoolP256r1`, `brainpoolP320r1`, `brainpoolP384r1`, +`brainpoolP512r1`. Few of the small curves from SEC standard are also +included (mainly to speed-up testing of the library), those are: +`secp112r1`, `secp112r2`, `secp128r1`, and `secp160r1`. +Key generation, siging and verifying is also supported for Ed25519 and +Ed448 curves. +No other curves are included, but it is not too hard to add support for more +curves over prime fields. + +## Dependencies + +This library uses only Python and the 'six' package. It is compatible with +Python 2.6, 2.7, and 3.3+. It also supports execution on alternative +implementations like pypy and pypy3. + +If `gmpy2` or `gmpy` is installed, they will be used for faster arithmetic. +Either of them can be installed after this library is installed, +`python-ecdsa` will detect their presence on start-up and use them +automatically. +You should prefer `gmpy2` on Python3 for optimal performance. + +To run the OpenSSL compatibility tests, the 'openssl' tool must be in your +`PATH`. This release has been tested successfully against OpenSSL 0.9.8o, +1.0.0a, 1.0.2f, 1.1.1d and 3.0.1 (among others). + + +## Installation + +This library is available on PyPI, it's recommended to install it using `pip`: + +``` +pip install ecdsa +``` + +In case higher performance is wanted and using native code is not a problem, +it's possible to specify installation together with `gmpy2`: + +``` +pip install ecdsa[gmpy2] +``` + +or (slower, legacy option): +``` +pip install ecdsa[gmpy] +``` + +## Speed + +The following table shows how long this library takes to generate key pairs +(`keygen`), to sign data (`sign`), to verify those signatures (`verify`), +to derive a shared secret (`ecdh`), and +to verify the signatures with no key-specific precomputation (`no PC verify`). +All those values are in seconds. +For convenience, the inverses of those values are also provided: +how many keys per second can be generated (`keygen/s`), how many signatures +can be made per second (`sign/s`), how many signatures can be verified +per second (`verify/s`), how many shared secrets can be derived per second +(`ecdh/s`), and how many signatures with no key specific +precomputation can be verified per second (`no PC verify/s`). The size of raw +signature (generally the smallest +the way a signature can be encoded) is also provided in the `siglen` column. +Use `tox -e speed` to generate this table on your own computer. +On an Intel Core i7 4790K @ 4.0GHz I'm getting the following performance: + +``` + siglen keygen keygen/s sign sign/s verify verify/s no PC verify no PC verify/s + NIST192p: 48 0.00032s 3134.06 0.00033s 2985.53 0.00063s 1598.36 0.00129s 774.43 + NIST224p: 56 0.00040s 2469.24 0.00042s 2367.88 0.00081s 1233.41 0.00170s 586.66 + NIST256p: 64 0.00051s 1952.73 0.00054s 1867.80 0.00098s 1021.86 0.00212s 471.27 + NIST384p: 96 0.00107s 935.92 0.00111s 904.23 0.00203s 491.77 0.00446s 224.00 + NIST521p: 132 0.00210s 475.52 0.00215s 464.16 0.00398s 251.28 0.00874s 114.39 + SECP256k1: 64 0.00052s 1921.54 0.00054s 1847.49 0.00105s 948.68 0.00210s 477.01 + BRAINPOOLP160r1: 40 0.00025s 4003.88 0.00026s 3845.12 0.00053s 1893.93 0.00105s 949.92 + BRAINPOOLP192r1: 48 0.00033s 3043.97 0.00034s 2975.98 0.00063s 1581.50 0.00135s 742.29 + BRAINPOOLP224r1: 56 0.00041s 2436.44 0.00043s 2315.51 0.00078s 1278.49 0.00180s 556.16 + BRAINPOOLP256r1: 64 0.00053s 1892.49 0.00054s 1846.24 0.00114s 875.64 0.00229s 437.25 + BRAINPOOLP320r1: 80 0.00073s 1361.26 0.00076s 1309.25 0.00143s 699.29 0.00322s 310.49 + BRAINPOOLP384r1: 96 0.00107s 931.29 0.00111s 901.80 0.00230s 434.19 0.00476s 210.20 + BRAINPOOLP512r1: 128 0.00207s 483.41 0.00212s 471.42 0.00425s 235.43 0.00912s 109.61 + SECP112r1: 28 0.00015s 6672.53 0.00016s 6440.34 0.00031s 3265.41 0.00056s 1774.20 + SECP112r2: 28 0.00015s 6697.11 0.00015s 6479.98 0.00028s 3524.72 0.00058s 1716.16 + SECP128r1: 32 0.00018s 5497.65 0.00019s 5272.89 0.00036s 2747.39 0.00072s 1396.16 + SECP160r1: 42 0.00025s 3949.32 0.00026s 3894.45 0.00046s 2153.85 0.00102s 985.07 + Ed25519: 64 0.00076s 1324.48 0.00042s 2405.01 0.00109s 918.05 0.00344s 290.50 + Ed448: 114 0.00176s 569.53 0.00115s 870.94 0.00282s 355.04 0.01024s 97.69 + + ecdh ecdh/s + NIST192p: 0.00104s 964.89 + NIST224p: 0.00134s 748.63 + NIST256p: 0.00170s 587.08 + NIST384p: 0.00352s 283.90 + NIST521p: 0.00717s 139.51 + SECP256k1: 0.00154s 648.40 + BRAINPOOLP160r1: 0.00082s 1220.70 + BRAINPOOLP192r1: 0.00105s 956.75 + BRAINPOOLP224r1: 0.00136s 734.52 + BRAINPOOLP256r1: 0.00178s 563.32 + BRAINPOOLP320r1: 0.00252s 397.23 + BRAINPOOLP384r1: 0.00376s 266.27 + BRAINPOOLP512r1: 0.00733s 136.35 + SECP112r1: 0.00046s 2180.40 + SECP112r2: 0.00045s 2229.14 + SECP128r1: 0.00054s 1868.15 + SECP160r1: 0.00080s 1243.98 +``` + +To test performance with `gmpy2` loaded, use `tox -e speedgmpy2`. +On the same machine I'm getting the following performance with `gmpy2`: +``` + siglen keygen keygen/s sign sign/s verify verify/s no PC verify no PC verify/s + NIST192p: 48 0.00017s 5933.40 0.00017s 5751.70 0.00032s 3125.28 0.00067s 1502.41 + NIST224p: 56 0.00021s 4782.87 0.00022s 4610.05 0.00040s 2487.04 0.00089s 1126.90 + NIST256p: 64 0.00023s 4263.98 0.00024s 4125.16 0.00045s 2200.88 0.00098s 1016.82 + NIST384p: 96 0.00041s 2449.54 0.00042s 2399.96 0.00083s 1210.57 0.00172s 581.43 + NIST521p: 132 0.00071s 1416.07 0.00072s 1389.81 0.00144s 692.93 0.00312s 320.40 + SECP256k1: 64 0.00024s 4245.05 0.00024s 4122.09 0.00045s 2206.40 0.00094s 1068.32 + BRAINPOOLP160r1: 40 0.00014s 6939.17 0.00015s 6681.55 0.00029s 3452.43 0.00057s 1769.81 + BRAINPOOLP192r1: 48 0.00017s 5920.05 0.00017s 5774.36 0.00034s 2979.00 0.00069s 1453.19 + BRAINPOOLP224r1: 56 0.00021s 4732.12 0.00022s 4622.65 0.00041s 2422.47 0.00087s 1149.87 + BRAINPOOLP256r1: 64 0.00024s 4233.02 0.00024s 4115.20 0.00047s 2143.27 0.00098s 1015.60 + BRAINPOOLP320r1: 80 0.00032s 3162.38 0.00032s 3077.62 0.00063s 1598.83 0.00136s 737.34 + BRAINPOOLP384r1: 96 0.00041s 2436.88 0.00042s 2395.62 0.00083s 1202.68 0.00178s 562.85 + BRAINPOOLP512r1: 128 0.00063s 1587.60 0.00064s 1558.83 0.00125s 799.96 0.00281s 355.83 + SECP112r1: 28 0.00009s 11118.66 0.00009s 10775.48 0.00018s 5456.00 0.00033s 3020.83 + SECP112r2: 28 0.00009s 11322.97 0.00009s 10857.71 0.00017s 5748.77 0.00032s 3094.28 + SECP128r1: 32 0.00010s 10078.39 0.00010s 9665.27 0.00019s 5200.58 0.00036s 2760.88 + SECP160r1: 42 0.00015s 6875.51 0.00015s 6647.35 0.00029s 3422.41 0.00057s 1768.35 + Ed25519: 64 0.00030s 3322.56 0.00018s 5568.63 0.00046s 2165.35 0.00153s 654.02 + Ed448: 114 0.00060s 1680.53 0.00039s 2567.40 0.00096s 1036.67 0.00350s 285.62 + + ecdh ecdh/s + NIST192p: 0.00050s 1985.70 + NIST224p: 0.00066s 1524.16 + NIST256p: 0.00071s 1413.07 + NIST384p: 0.00127s 788.89 + NIST521p: 0.00230s 434.85 + SECP256k1: 0.00071s 1409.95 + BRAINPOOLP160r1: 0.00042s 2374.65 + BRAINPOOLP192r1: 0.00051s 1960.01 + BRAINPOOLP224r1: 0.00066s 1518.37 + BRAINPOOLP256r1: 0.00071s 1399.90 + BRAINPOOLP320r1: 0.00100s 997.21 + BRAINPOOLP384r1: 0.00129s 777.51 + BRAINPOOLP512r1: 0.00210s 475.99 + SECP112r1: 0.00022s 4457.70 + SECP112r2: 0.00024s 4252.33 + SECP128r1: 0.00028s 3589.31 + SECP160r1: 0.00043s 2305.02 +``` + +(there's also `gmpy` version, execute it using `tox -e speedgmpy`) + +For comparison, a highly optimised implementation (including curve-specific +assembly for some curves), like the one in OpenSSL 1.1.1d, provides the +following performance numbers on the same machine. +Run `openssl speed ecdsa` and `openssl speed ecdh` to reproduce it: +``` + sign verify sign/s verify/s + 192 bits ecdsa (nistp192) 0.0002s 0.0002s 4785.6 5380.7 + 224 bits ecdsa (nistp224) 0.0000s 0.0001s 22475.6 9822.0 + 256 bits ecdsa (nistp256) 0.0000s 0.0001s 45069.6 14166.6 + 384 bits ecdsa (nistp384) 0.0008s 0.0006s 1265.6 1648.1 + 521 bits ecdsa (nistp521) 0.0003s 0.0005s 3753.1 1819.5 + 256 bits ecdsa (brainpoolP256r1) 0.0003s 0.0003s 2983.5 3333.2 + 384 bits ecdsa (brainpoolP384r1) 0.0008s 0.0007s 1258.8 1528.1 + 512 bits ecdsa (brainpoolP512r1) 0.0015s 0.0012s 675.1 860.1 + + sign verify sign/s verify/s + 253 bits EdDSA (Ed25519) 0.0000s 0.0001s 28217.9 10897.7 + 456 bits EdDSA (Ed448) 0.0003s 0.0005s 3926.5 2147.7 + + op op/s + 192 bits ecdh (nistp192) 0.0002s 4853.4 + 224 bits ecdh (nistp224) 0.0001s 15252.1 + 256 bits ecdh (nistp256) 0.0001s 18436.3 + 384 bits ecdh (nistp384) 0.0008s 1292.7 + 521 bits ecdh (nistp521) 0.0003s 2884.7 + 256 bits ecdh (brainpoolP256r1) 0.0003s 3066.5 + 384 bits ecdh (brainpoolP384r1) 0.0008s 1298.0 + 512 bits ecdh (brainpoolP512r1) 0.0014s 694.8 +``` + +Keys and signature can be serialized in different ways (see Usage, below). +For a NIST192p key, the three basic representations require strings of the +following lengths (in bytes): + + to_string: signkey= 24, verifykey= 48, signature=48 + compressed: signkey=n/a, verifykey= 25, signature=n/a + DER: signkey=106, verifykey= 80, signature=55 + PEM: signkey=278, verifykey=162, (no support for PEM signatures) + +## History + +In 2006, Peter Pearson announced his pure-python implementation of ECDSA in a +[message to sci.crypt][1], available from his [download site][2]. In 2010, +Brian Warner wrote a wrapper around this code, to make it a bit easier and +safer to use. In 2020, Hubert Kario included an implementation of elliptic +curve cryptography that uses Jacobian coordinates internally, improving +performance about 20-fold. You are looking at the README for this wrapper. + +[1]: http://www.derkeiler.com/Newsgroups/sci.crypt/2006-01/msg00651.html +[2]: http://webpages.charter.net/curryfans/peter/downloads.html + +## Testing + +To run the full test suite, do this: + + tox -e coverage + +On an Intel Core i7 4790K @ 4.0GHz, the tests take about 18 seconds to execute. +The test suite uses +[`hypothesis`](https://github.com/HypothesisWorks/hypothesis) so there is some +inherent variability in the test suite execution time. + +One part of `test_pyecdsa.py` and `test_ecdh.py` checks compatibility with +OpenSSL, by running the "openssl" CLI tool, make sure it's in your `PATH` if +you want to test compatibility with it (if OpenSSL is missing, too old, or +doesn't support all the curves supported in upstream releases you will see +skipped tests in the above `coverage` run). + +## Security + +This library was not designed with security in mind. If you are processing +data that needs to be protected we suggest you use a quality wrapper around +OpenSSL. [pyca/cryptography](https://cryptography.io) is one example of such +a wrapper. The primary use-case of this library is as a portable library for +interoperability testing and as a teaching tool. + +**This library does not protect against side-channel attacks.** + +Do not allow attackers to measure how long it takes you to generate a key pair +or sign a message. Do not allow attackers to run code on the same physical +machine when key pair generation or signing is taking place (this includes +virtual machines). Do not allow attackers to measure how much power your +computer uses while generating the key pair or signing a message. Do not allow +attackers to measure RF interference coming from your computer while generating +a key pair or signing a message. Note: just loading the private key will cause +key pair generation. Other operations or attack vectors may also be +vulnerable to attacks. **For a sophisticated attacker observing just one +operation with a private key will be sufficient to completely +reconstruct the private key**. + +Please also note that any Pure-python cryptographic library will be vulnerable +to the same side-channel attacks. This is because Python does not provide +side-channel secure primitives (with the exception of +[`hmac.compare_digest()`][3]), making side-channel secure programming +impossible. + +This library depends upon a strong source of random numbers. Do not use it on +a system where `os.urandom()` does not provide cryptographically secure +random numbers. + +[3]: https://docs.python.org/3/library/hmac.html#hmac.compare_digest + +## Usage + +You start by creating a `SigningKey`. You can use this to sign data, by passing +in data as a byte string and getting back the signature (also a byte string). +You can also ask a `SigningKey` to give you the corresponding `VerifyingKey`. +The `VerifyingKey` can be used to verify a signature, by passing it both the +data string and the signature byte string: it either returns True or raises +`BadSignatureError`. + +```python +from ecdsa import SigningKey +sk = SigningKey.generate() # uses NIST192p +vk = sk.verifying_key +signature = sk.sign(b"message") +assert vk.verify(signature, b"message") +``` + +Each `SigningKey`/`VerifyingKey` is associated with a specific curve, like +NIST192p (the default one). Longer curves are more secure, but take longer to +use, and result in longer keys and signatures. + +```python +from ecdsa import SigningKey, NIST384p +sk = SigningKey.generate(curve=NIST384p) +vk = sk.verifying_key +signature = sk.sign(b"message") +assert vk.verify(signature, b"message") +``` + +The `SigningKey` can be serialized into several different formats: the shortest +is to call `s=sk.to_string()`, and then re-create it with +`SigningKey.from_string(s, curve)` . This short form does not record the +curve, so you must be sure to pass to `from_string()` the same curve you used +for the original key. The short form of a NIST192p-based signing key is just 24 +bytes long. If a point encoding is invalid or it does not lie on the specified +curve, `from_string()` will raise `MalformedPointError`. + +```python +from ecdsa import SigningKey, NIST384p +sk = SigningKey.generate(curve=NIST384p) +sk_string = sk.to_string() +sk2 = SigningKey.from_string(sk_string, curve=NIST384p) +print(sk_string.hex()) +print(sk2.to_string().hex()) +``` + +Note: while the methods are called `to_string()` the type they return is +actually `bytes`, the "string" part is leftover from Python 2. + +`sk.to_pem()` and `sk.to_der()` will serialize the signing key into the same +formats that OpenSSL uses. The PEM file looks like the familiar ASCII-armored +`"-----BEGIN EC PRIVATE KEY-----"` base64-encoded format, and the DER format +is a shorter binary form of the same data. +`SigningKey.from_pem()/.from_der()` will undo this serialization. These +formats include the curve name, so you do not need to pass in a curve +identifier to the deserializer. In case the file is malformed `from_der()` +and `from_pem()` will raise `UnexpectedDER` or` MalformedPointError`. + +```python +from ecdsa import SigningKey, NIST384p +sk = SigningKey.generate(curve=NIST384p) +sk_pem = sk.to_pem() +sk2 = SigningKey.from_pem(sk_pem) +# sk and sk2 are the same key +``` + +Likewise, the `VerifyingKey` can be serialized in the same way: +`vk.to_string()/VerifyingKey.from_string()`, `to_pem()/from_pem()`, and +`to_der()/from_der()`. The same `curve=` argument is needed for +`VerifyingKey.from_string()`. + +```python +from ecdsa import SigningKey, VerifyingKey, NIST384p +sk = SigningKey.generate(curve=NIST384p) +vk = sk.verifying_key +vk_string = vk.to_string() +vk2 = VerifyingKey.from_string(vk_string, curve=NIST384p) +# vk and vk2 are the same key + +from ecdsa import SigningKey, VerifyingKey, NIST384p +sk = SigningKey.generate(curve=NIST384p) +vk = sk.verifying_key +vk_pem = vk.to_pem() +vk2 = VerifyingKey.from_pem(vk_pem) +# vk and vk2 are the same key +``` + +There are a couple of different ways to compute a signature. Fundamentally, +ECDSA takes a number that represents the data being signed, and returns a +pair of numbers that represent the signature. The `hashfunc=` argument to +`sk.sign()` and `vk.verify()` is used to turn an arbitrary string into a +fixed-length digest, which is then turned into a number that ECDSA can sign, +and both sign and verify must use the same approach. The default value is +`hashlib.sha1`, but if you use NIST256p or a longer curve, you can use +`hashlib.sha256` instead. + +There are also multiple ways to represent a signature. The default +`sk.sign()` and `vk.verify()` methods present it as a short string, for +simplicity and minimal overhead. To use a different scheme, use the +`sk.sign(sigencode=)` and `vk.verify(sigdecode=)` arguments. There are helper +functions in the `ecdsa.util` module that can be useful here. + +It is also possible to create a `SigningKey` from a "seed", which is +deterministic. This can be used in protocols where you want to derive +consistent signing keys from some other secret, for example when you want +three separate keys and only want to store a single master secret. You should +start with a uniformly-distributed unguessable seed with about `curve.baselen` +bytes of entropy, and then use one of the helper functions in `ecdsa.util` to +convert it into an integer in the correct range, and then finally pass it +into `SigningKey.from_secret_exponent()`, like this: + +```python +import os +from ecdsa import NIST384p, SigningKey +from ecdsa.util import randrange_from_seed__trytryagain + +def make_key(seed): + secexp = randrange_from_seed__trytryagain(seed, NIST384p.order) + return SigningKey.from_secret_exponent(secexp, curve=NIST384p) + +seed = os.urandom(NIST384p.baselen) # or other starting point +sk1a = make_key(seed) +sk1b = make_key(seed) +# note: sk1a and sk1b are the same key +assert sk1a.to_string() == sk1b.to_string() +sk2 = make_key(b"2-"+seed) # different key +assert sk1a.to_string() != sk2.to_string() +``` + +In case the application will verify a lot of signatures made with a single +key, it's possible to precompute some of the internal values to make +signature verification significantly faster. The break-even point occurs at +about 100 signatures verified. + +To perform precomputation, you can call the `precompute()` method +on `VerifyingKey` instance: +```python +from ecdsa import SigningKey, NIST384p +sk = SigningKey.generate(curve=NIST384p) +vk = sk.verifying_key +vk.precompute() +signature = sk.sign(b"message") +assert vk.verify(signature, b"message") +``` + +Once `precompute()` was called, all signature verifications with this key will +be faster to execute. + +## OpenSSL Compatibility + +To produce signatures that can be verified by OpenSSL tools, or to verify +signatures that were produced by those tools, use: + +```python +# openssl ecparam -name prime256v1 -genkey -out sk.pem +# openssl ec -in sk.pem -pubout -out vk.pem +# echo "data for signing" > data +# openssl dgst -sha256 -sign sk.pem -out data.sig data +# openssl dgst -sha256 -verify vk.pem -signature data.sig data +# openssl dgst -sha256 -prverify sk.pem -signature data.sig data + +import hashlib +from ecdsa import SigningKey, VerifyingKey +from ecdsa.util import sigencode_der, sigdecode_der + +with open("vk.pem") as f: + vk = VerifyingKey.from_pem(f.read()) + +with open("data", "rb") as f: + data = f.read() + +with open("data.sig", "rb") as f: + signature = f.read() + +assert vk.verify(signature, data, hashlib.sha256, sigdecode=sigdecode_der) + +with open("sk.pem") as f: + sk = SigningKey.from_pem(f.read(), hashlib.sha256) + +new_signature = sk.sign_deterministic(data, sigencode=sigencode_der) + +with open("data.sig2", "wb") as f: + f.write(new_signature) + +# openssl dgst -sha256 -verify vk.pem -signature data.sig2 data +``` + +Note: if compatibility with OpenSSL 1.0.0 or earlier is necessary, the +`sigencode_string` and `sigdecode_string` from `ecdsa.util` can be used for +respectively writing and reading the signatures. + +The keys also can be written in format that openssl can handle: + +```python +from ecdsa import SigningKey, VerifyingKey + +with open("sk.pem") as f: + sk = SigningKey.from_pem(f.read()) +with open("sk.pem", "wb") as f: + f.write(sk.to_pem()) + +with open("vk.pem") as f: + vk = VerifyingKey.from_pem(f.read()) +with open("vk.pem", "wb") as f: + f.write(vk.to_pem()) +``` + +## Entropy + +Creating a signing key with `SigningKey.generate()` requires some form of +entropy (as opposed to +`from_secret_exponent`/`from_string`/`from_der`/`from_pem`, +which are deterministic and do not require an entropy source). The default +source is `os.urandom()`, but you can pass any other function that behaves +like `os.urandom` as the `entropy=` argument to do something different. This +may be useful in unit tests, where you want to achieve repeatable results. The +`ecdsa.util.PRNG` utility is handy here: it takes a seed and produces a strong +pseudo-random stream from it: + +```python +from ecdsa.util import PRNG +from ecdsa import SigningKey +rng1 = PRNG(b"seed") +sk1 = SigningKey.generate(entropy=rng1) +rng2 = PRNG(b"seed") +sk2 = SigningKey.generate(entropy=rng2) +# sk1 and sk2 are the same key +``` + +Likewise, ECDSA signature generation requires a random number, and each +signature must use a different one (using the same number twice will +immediately reveal the private signing key). The `sk.sign()` method takes an +`entropy=` argument which behaves the same as `SigningKey.generate(entropy=)`. + +## Deterministic Signatures + +If you call `SigningKey.sign_deterministic(data)` instead of `.sign(data)`, +the code will generate a deterministic signature instead of a random one. +This uses the algorithm from RFC6979 to safely generate a unique `k` value, +derived from the private key and the message being signed. Each time you sign +the same message with the same key, you will get the same signature (using +the same `k`). + +This may become the default in a future version, as it is not vulnerable to +failures of the entropy source. + +## Examples + +Create a NIST192p key pair and immediately save both to disk: + +```python +from ecdsa import SigningKey +sk = SigningKey.generate() +vk = sk.verifying_key +with open("private.pem", "wb") as f: + f.write(sk.to_pem()) +with open("public.pem", "wb") as f: + f.write(vk.to_pem()) +``` + +Load a signing key from disk, use it to sign a message (using SHA-1), and write +the signature to disk: + +```python +from ecdsa import SigningKey +with open("private.pem") as f: + sk = SigningKey.from_pem(f.read()) +with open("message", "rb") as f: + message = f.read() +sig = sk.sign(message) +with open("signature", "wb") as f: + f.write(sig) +``` + +Load the verifying key, message, and signature from disk, and verify the +signature (assume SHA-1 hash): + +```python +from ecdsa import VerifyingKey, BadSignatureError +vk = VerifyingKey.from_pem(open("public.pem").read()) +with open("message", "rb") as f: + message = f.read() +with open("signature", "rb") as f: + sig = f.read() +try: + vk.verify(sig, message) + print "good signature" +except BadSignatureError: + print "BAD SIGNATURE" +``` + +Create a NIST521p key pair: + +```python +from ecdsa import SigningKey, NIST521p +sk = SigningKey.generate(curve=NIST521p) +vk = sk.verifying_key +``` + +Create three independent signing keys from a master seed: + +```python +from ecdsa import NIST192p, SigningKey +from ecdsa.util import randrange_from_seed__trytryagain + +def make_key_from_seed(seed, curve=NIST192p): + secexp = randrange_from_seed__trytryagain(seed, curve.order) + return SigningKey.from_secret_exponent(secexp, curve) + +sk1 = make_key_from_seed("1:%s" % seed) +sk2 = make_key_from_seed("2:%s" % seed) +sk3 = make_key_from_seed("3:%s" % seed) +``` + +Load a verifying key from disk and print it using hex encoding in +uncompressed and compressed format (defined in X9.62 and SEC1 standards): + +```python +from ecdsa import VerifyingKey + +with open("public.pem") as f: + vk = VerifyingKey.from_pem(f.read()) + +print("uncompressed: {0}".format(vk.to_string("uncompressed").hex())) +print("compressed: {0}".format(vk.to_string("compressed").hex())) +``` + +Load a verifying key from a hex string from compressed format, output +uncompressed: + +```python +from ecdsa import VerifyingKey, NIST256p + +comp_str = '022799c0d0ee09772fdd337d4f28dc155581951d07082fb19a38aa396b67e77759' +vk = VerifyingKey.from_string(bytearray.fromhex(comp_str), curve=NIST256p) +print(vk.to_string("uncompressed").hex()) +``` + +ECDH key exchange with remote party: + +```python +from ecdsa import ECDH, NIST256p + +ecdh = ECDH(curve=NIST256p) +ecdh.generate_private_key() +local_public_key = ecdh.get_public_key() +#send `local_public_key` to remote party and receive `remote_public_key` from remote party +with open("remote_public_key.pem") as e: + remote_public_key = e.read() +ecdh.load_received_public_key_pem(remote_public_key) +secret = ecdh.generate_sharedsecret_bytes() +``` + + diff --git a/frozen_deps/ecdsa-0.18.0.dist-info/RECORD b/frozen_deps/ecdsa-0.18.0.dist-info/RECORD new file mode 100644 index 0000000..f4130d1 --- /dev/null +++ b/frozen_deps/ecdsa-0.18.0.dist-info/RECORD @@ -0,0 +1,64 @@ +ecdsa-0.18.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+ecdsa-0.18.0.dist-info/LICENSE,sha256=PsqYRXc9LluMydjBGdNF8ApIBuS9Zg1KPWzfnA6di7I,1147
+ecdsa-0.18.0.dist-info/METADATA,sha256=vesFVMWT6uSeOuNwGGxtBm8nm6GROqNNRO28jr8wWqM,29750
+ecdsa-0.18.0.dist-info/RECORD,,
+ecdsa-0.18.0.dist-info/WHEEL,sha256=Z-nyYpwrcSqxfdux5Mbn_DQ525iP7J2DG3JgGvOYyTQ,110
+ecdsa-0.18.0.dist-info/top_level.txt,sha256=7ovPHfAPyTou19f8gOSbHm6B9dGjTibWolcCB7Zjovs,6
+ecdsa/__init__.py,sha256=m8jWFcZ9E-iiDqVaTy7ABtvEyTJlF58tZ45UAKj3UWo,1637
+ecdsa/__pycache__/__init__.cpython-310.pyc,,
+ecdsa/__pycache__/_compat.cpython-310.pyc,,
+ecdsa/__pycache__/_rwlock.cpython-310.pyc,,
+ecdsa/__pycache__/_sha3.cpython-310.pyc,,
+ecdsa/__pycache__/_version.cpython-310.pyc,,
+ecdsa/__pycache__/curves.cpython-310.pyc,,
+ecdsa/__pycache__/der.cpython-310.pyc,,
+ecdsa/__pycache__/ecdh.cpython-310.pyc,,
+ecdsa/__pycache__/ecdsa.cpython-310.pyc,,
+ecdsa/__pycache__/eddsa.cpython-310.pyc,,
+ecdsa/__pycache__/ellipticcurve.cpython-310.pyc,,
+ecdsa/__pycache__/errors.cpython-310.pyc,,
+ecdsa/__pycache__/keys.cpython-310.pyc,,
+ecdsa/__pycache__/numbertheory.cpython-310.pyc,,
+ecdsa/__pycache__/rfc6979.cpython-310.pyc,,
+ecdsa/__pycache__/test_curves.cpython-310.pyc,,
+ecdsa/__pycache__/test_der.cpython-310.pyc,,
+ecdsa/__pycache__/test_ecdh.cpython-310.pyc,,
+ecdsa/__pycache__/test_ecdsa.cpython-310.pyc,,
+ecdsa/__pycache__/test_eddsa.cpython-310.pyc,,
+ecdsa/__pycache__/test_ellipticcurve.cpython-310.pyc,,
+ecdsa/__pycache__/test_jacobi.cpython-310.pyc,,
+ecdsa/__pycache__/test_keys.cpython-310.pyc,,
+ecdsa/__pycache__/test_malformed_sigs.cpython-310.pyc,,
+ecdsa/__pycache__/test_numbertheory.cpython-310.pyc,,
+ecdsa/__pycache__/test_pyecdsa.cpython-310.pyc,,
+ecdsa/__pycache__/test_rw_lock.cpython-310.pyc,,
+ecdsa/__pycache__/test_sha3.cpython-310.pyc,,
+ecdsa/__pycache__/util.cpython-310.pyc,,
+ecdsa/_compat.py,sha256=EhUF8-sFu1dKKGDibkmItbYm_nKoklSIBgkIburUoAg,4619
+ecdsa/_rwlock.py,sha256=CAwHp2V65ksI8B1UqY7EccK9LaUToiv6pDLVzm44eag,2849
+ecdsa/_sha3.py,sha256=DJs7QLmdkQMU35llyD8HQeAXNvf5sMcujO6oFdScIqI,4747
+ecdsa/_version.py,sha256=YZ3BGOHr1Ltse4LfX7F80J6qmKFA-NS-G2eYUuw2WnU,498
+ecdsa/curves.py,sha256=Na5rpnuADNvWkCTlUGbs9xwVogY6Vl2_3uNzpVGgxtE,14390
+ecdsa/der.py,sha256=OmfH8fojeqfJnasAt7I1P8j_qfwcwl-W4gDx1-cO8M0,14109
+ecdsa/ecdh.py,sha256=Tiirawt5xegVDrY9eS-ATvvfmTIznUyv5fy2k7VnzTk,11011
+ecdsa/ecdsa.py,sha256=LPRHHXNvGyZ67lyM6cWqUuhQceCwoktfPij20UTsdJo,24955
+ecdsa/eddsa.py,sha256=IzsGzoGAefcoYjF7DVjFkX5ZJqiK2LTOmAMe6wyf4UU,7170
+ecdsa/ellipticcurve.py,sha256=HwlFqrihf7Q2GQTLYQ0PeCwsgMhk_GlkZz3I2Xj4-eI,53625
+ecdsa/errors.py,sha256=b4mhnmIpRnEdHzbectHAA5F7O9MtSaI-fYoc13_vBxQ,130
+ecdsa/keys.py,sha256=plsomRHYrN3Z3iigUqKM5An8_6TDk1nJNpFv_cPGAvM,65124
+ecdsa/numbertheory.py,sha256=XWugBG59BxrpvjZm7Ytsnmkv770vK8IkClObThpbvAM,17479
+ecdsa/rfc6979.py,sha256=zwzo33lsZJA9r2dSf7HCliI_yIbw5cJ0Ek9tLdRRO40,2850
+ecdsa/test_curves.py,sha256=l5N-m4Yo5IAy4a8aJMsBamaSlLAfSoYjYqCj1HDEVpU,13081
+ecdsa/test_der.py,sha256=q2mr4HS_JyUxojWTSLJu-MQZiTwaCE7W_VG3rwwPEas,14956
+ecdsa/test_ecdh.py,sha256=hUJXTo_Cr9ji9-EpPvpQf7-TgB97WUj2tWcwU-LqCXc,15238
+ecdsa/test_ecdsa.py,sha256=1clBfDtA0zdF-13BoKXsJffL5K-iFutlMgov0kmGro0,23923
+ecdsa/test_eddsa.py,sha256=Vlv5J0C4zNJu5zzr756qpm0AJmARpzBKCWrhHaF_bR4,32615
+ecdsa/test_ellipticcurve.py,sha256=K6W_EQunOfE-RVSux6d1O7LXzSuAsVk9F1elwyY-rYA,6085
+ecdsa/test_jacobi.py,sha256=JDQeM_JKwPfwWBd4IgqtOp1rboeQNUIPPA334b-nmLQ,18388
+ecdsa/test_keys.py,sha256=n_IYLxG4JwD84dYLDmRjV2A-NqsSrrR1P0XBJOCZsEI,32833
+ecdsa/test_malformed_sigs.py,sha256=hiV2vwzFrIdNIC-inYUJIKboyAAw2TKAIVXtFadyojg,10857
+ecdsa/test_numbertheory.py,sha256=g6hi7NZFKuMSAxJSAYW5sWM7ivSCiw8g5-PeoDyowgY,11619
+ecdsa/test_pyecdsa.py,sha256=WeRujEKpkZzHvEeXNnnhM1QdMH0Lxou7Bl4u8RXY-jM,82757
+ecdsa/test_rw_lock.py,sha256=byv0_FTM90cbuHPCI6__LeQJkHL_zYEeVYIBO8e2LLc,7021
+ecdsa/test_sha3.py,sha256=oKULy5KOTaXjpLXSyuHrB1wjPiQDxB6INp7Tf1EU8Ko,3022
+ecdsa/util.py,sha256=cOEN3_c8p79Dc8a-LcUQP2ctIsYky35jhSWc9hLP1qc,14618
diff --git a/frozen_deps/ecdsa-0.18.0.dist-info/WHEEL b/frozen_deps/ecdsa-0.18.0.dist-info/WHEEL new file mode 100644 index 0000000..01b8fc7 --- /dev/null +++ b/frozen_deps/ecdsa-0.18.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/frozen_deps/ecdsa-0.18.0.dist-info/top_level.txt b/frozen_deps/ecdsa-0.18.0.dist-info/top_level.txt new file mode 100644 index 0000000..aa5efdb --- /dev/null +++ b/frozen_deps/ecdsa-0.18.0.dist-info/top_level.txt @@ -0,0 +1 @@ +ecdsa diff --git a/frozen_deps/ecdsa/__init__.py b/frozen_deps/ecdsa/__init__.py index 4ae0a11..ce8749a 100644 --- a/frozen_deps/ecdsa/__init__.py +++ b/frozen_deps/ecdsa/__init__.py @@ -1,3 +1,6 @@ +# while we don't use six in this file, we did bundle it for a long time, so +# keep as part of module in a virtual way (through __all__) +import six from .keys import ( SigningKey, VerifyingKey, @@ -19,6 +22,12 @@ from .curves import ( BRAINPOOLP320r1, BRAINPOOLP384r1, BRAINPOOLP512r1, + SECP112r1, + SECP112r2, + SECP128r1, + SECP160r1, + Ed25519, + Ed448, ) from .ecdh import ( ECDH, @@ -28,13 +37,9 @@ from .ecdh import ( InvalidSharedSecretError, ) from .der import UnexpectedDER +from . import _version -# This code comes from http://github.com/warner/python-ecdsa -from ._version import get_versions - -__version__ = get_versions()["version"] -del get_versions - +# This code comes from http://github.com/tlsfuzzer/python-ecdsa __all__ = [ "curves", "der", @@ -72,5 +77,14 @@ _hush_pyflakes = [ BRAINPOOLP320r1, BRAINPOOLP384r1, BRAINPOOLP512r1, + SECP112r1, + SECP112r2, + SECP128r1, + SECP160r1, + Ed25519, + Ed448, + six.b(""), ] del _hush_pyflakes + +__version__ = _version.get_versions()["version"] diff --git a/frozen_deps/ecdsa/_compat.py b/frozen_deps/ecdsa/_compat.py index 720360b..83d41a5 100644 --- a/frozen_deps/ecdsa/_compat.py +++ b/frozen_deps/ecdsa/_compat.py @@ -3,6 +3,7 @@ Common functions for providing cross-python version compatibility. """ import sys import re +import binascii from six import integer_types @@ -14,7 +15,8 @@ def str_idx_as_int(string, index): return ord(val) -if sys.version_info < (3, 0): +if sys.version_info < (3, 0): # pragma: no branch + import platform def normalise_bytes(buffer_object): """Cast the input into array of bytes.""" @@ -24,37 +26,128 @@ if sys.version_info < (3, 0): def hmac_compat(ret): return ret - if sys.version_info < (2, 7) or sys.version_info < (2, 7, 4): + if ( + sys.version_info < (2, 7) + or sys.version_info < (2, 7, 4) + or platform.system() == "Java" + ): # pragma: no branch def remove_whitespace(text): """Removes all whitespace from passed in string""" return re.sub(r"\s+", "", text) + def compat26_str(val): + return str(val) + + def bit_length(val): + if val == 0: + return 0 + return len(bin(val)) - 2 + else: def remove_whitespace(text): """Removes all whitespace from passed in string""" return re.sub(r"\s+", "", text, flags=re.UNICODE) + def compat26_str(val): + return val + + def bit_length(val): + """Return number of bits necessary to represent an integer.""" + return val.bit_length() + + def b2a_hex(val): + return binascii.b2a_hex(compat26_str(val)) + + def a2b_hex(val): + try: + return bytearray(binascii.a2b_hex(val)) + except Exception as e: + raise ValueError("base16 error: %s" % e) + + def bytes_to_int(val, byteorder): + """Convert bytes to an int.""" + if not val: + return 0 + if byteorder == "big": + return int(b2a_hex(val), 16) + if byteorder == "little": + return int(b2a_hex(val[::-1]), 16) + raise ValueError("Only 'big' and 'little' endian supported") + + def int_to_bytes(val, length=None, byteorder="big"): + """Return number converted to bytes""" + if length is None: + length = byte_length(val) + if byteorder == "big": + return bytearray( + (val >> i) & 0xFF for i in reversed(range(0, length * 8, 8)) + ) + if byteorder == "little": + return bytearray( + (val >> i) & 0xFF for i in range(0, length * 8, 8) + ) + raise ValueError("Only 'big' or 'little' endian supported") else: - if sys.version_info < (3, 4): + if sys.version_info < (3, 4): # pragma: no branch # on python 3.3 hmac.hmac.update() accepts only bytes, on newer # versions it does accept memoryview() also def hmac_compat(data): - if not isinstance(data, bytes): + if not isinstance(data, bytes): # pragma: no branch return bytes(data) return data + def normalise_bytes(buffer_object): + """Cast the input into array of bytes.""" + if not buffer_object: + return b"" + return memoryview(buffer_object).cast("B") + else: def hmac_compat(data): return data - def normalise_bytes(buffer_object): - """Cast the input into array of bytes.""" - return memoryview(buffer_object).cast("B") + def normalise_bytes(buffer_object): + """Cast the input into array of bytes.""" + return memoryview(buffer_object).cast("B") + + def compat26_str(val): + return val def remove_whitespace(text): """Removes all whitespace from passed in string""" return re.sub(r"\s+", "", text, flags=re.UNICODE) + + def a2b_hex(val): + try: + return bytearray(binascii.a2b_hex(bytearray(val, "ascii"))) + except Exception as e: + raise ValueError("base16 error: %s" % e) + + # pylint: disable=invalid-name + # pylint is stupid here and doesn't notice it's a function, not + # constant + bytes_to_int = int.from_bytes + # pylint: enable=invalid-name + + def bit_length(val): + """Return number of bits necessary to represent an integer.""" + return val.bit_length() + + def int_to_bytes(val, length=None, byteorder="big"): + """Convert integer to bytes.""" + if length is None: + length = byte_length(val) + # for gmpy we need to convert back to native int + if type(val) != int: + val = int(val) + return bytearray(val.to_bytes(length=length, byteorder=byteorder)) + + +def byte_length(val): + """Return number of bytes necessary to represent an integer.""" + length = bit_length(val) + return (length + 7) // 8 diff --git a/frozen_deps/ecdsa/_sha3.py b/frozen_deps/ecdsa/_sha3.py new file mode 100644 index 0000000..2db0058 --- /dev/null +++ b/frozen_deps/ecdsa/_sha3.py @@ -0,0 +1,181 @@ +""" +Implementation of the SHAKE-256 algorithm for Ed448 +""" + +try: + import hashlib + + hashlib.new("shake256").digest(64) + + def shake_256(msg, outlen): + return hashlib.new("shake256", msg).digest(outlen) + +except (TypeError, ValueError): + + from ._compat import bytes_to_int, int_to_bytes + + # From little endian. + def _from_le(s): + return bytes_to_int(s, byteorder="little") + + # Rotate a word x by b places to the left. + def _rol(x, b): + return ((x << b) | (x >> (64 - b))) & (2**64 - 1) + + # Do the SHA-3 state transform on state s. + def _sha3_transform(s): + ROTATIONS = [ + 0, + 1, + 62, + 28, + 27, + 36, + 44, + 6, + 55, + 20, + 3, + 10, + 43, + 25, + 39, + 41, + 45, + 15, + 21, + 8, + 18, + 2, + 61, + 56, + 14, + ] + PERMUTATION = [ + 1, + 6, + 9, + 22, + 14, + 20, + 2, + 12, + 13, + 19, + 23, + 15, + 4, + 24, + 21, + 8, + 16, + 5, + 3, + 18, + 17, + 11, + 7, + 10, + ] + RC = [ + 0x0000000000000001, + 0x0000000000008082, + 0x800000000000808A, + 0x8000000080008000, + 0x000000000000808B, + 0x0000000080000001, + 0x8000000080008081, + 0x8000000000008009, + 0x000000000000008A, + 0x0000000000000088, + 0x0000000080008009, + 0x000000008000000A, + 0x000000008000808B, + 0x800000000000008B, + 0x8000000000008089, + 0x8000000000008003, + 0x8000000000008002, + 0x8000000000000080, + 0x000000000000800A, + 0x800000008000000A, + 0x8000000080008081, + 0x8000000000008080, + 0x0000000080000001, + 0x8000000080008008, + ] + + for rnd in range(0, 24): + # AddColumnParity (Theta) + c = [0] * 5 + d = [0] * 5 + for i in range(0, 25): + c[i % 5] ^= s[i] + for i in range(0, 5): + d[i] = c[(i + 4) % 5] ^ _rol(c[(i + 1) % 5], 1) + for i in range(0, 25): + s[i] ^= d[i % 5] + # RotateWords (Rho) + for i in range(0, 25): + s[i] = _rol(s[i], ROTATIONS[i]) + # PermuteWords (Pi) + t = s[PERMUTATION[0]] + for i in range(0, len(PERMUTATION) - 1): + s[PERMUTATION[i]] = s[PERMUTATION[i + 1]] + s[PERMUTATION[-1]] = t + # NonlinearMixRows (Chi) + for i in range(0, 25, 5): + t = [ + s[i], + s[i + 1], + s[i + 2], + s[i + 3], + s[i + 4], + s[i], + s[i + 1], + ] + for j in range(0, 5): + s[i + j] = t[j] ^ ((~t[j + 1]) & (t[j + 2])) + # AddRoundConstant (Iota) + s[0] ^= RC[rnd] + + # Reinterpret octet array b to word array and XOR it to state s. + def _reinterpret_to_words_and_xor(s, b): + for j in range(0, len(b) // 8): + s[j] ^= _from_le(b[8 * j : 8 * j + 8]) + + # Reinterpret word array w to octet array and return it. + def _reinterpret_to_octets(w): + mp = bytearray() + for j in range(0, len(w)): + mp += int_to_bytes(w[j], 8, byteorder="little") + return mp + + def _sha3_raw(msg, r_w, o_p, e_b): + """Semi-generic SHA-3 implementation""" + r_b = 8 * r_w + s = [0] * 25 + # Handle whole blocks. + idx = 0 + blocks = len(msg) // r_b + for i in range(0, blocks): + _reinterpret_to_words_and_xor(s, msg[idx : idx + r_b]) + idx += r_b + _sha3_transform(s) + # Handle last block padding. + m = bytearray(msg[idx:]) + m.append(o_p) + while len(m) < r_b: + m.append(0) + m[len(m) - 1] |= 128 + # Handle padded last block. + _reinterpret_to_words_and_xor(s, m) + _sha3_transform(s) + # Output. + out = bytearray() + while len(out) < e_b: + out += _reinterpret_to_octets(s[:r_w]) + _sha3_transform(s) + return out[:e_b] + + def shake_256(msg, outlen): + return _sha3_raw(msg, 17, 31, outlen) diff --git a/frozen_deps/ecdsa/_version.py b/frozen_deps/ecdsa/_version.py index 9a3e5fa..96aae17 100644 --- a/frozen_deps/ecdsa/_version.py +++ b/frozen_deps/ecdsa/_version.py @@ -1,5 +1,5 @@ -# This file was generated by 'versioneer.py' (0.17) from +# This file was generated by 'versioneer.py' (0.21) from # revision-control system data, or from the parent directory name of an # unpacked source archive. Distribution tarballs contain a pre-generated copy # of this file. @@ -8,11 +8,11 @@ import json version_json = ''' { - "date": "2020-11-12T20:12:49+0100", + "date": "2022-07-09T14:49:17+0200", "dirty": false, "error": null, - "full-revisionid": "9d5a727c766773b8367f6dd0a49bbda21c18dbe7", - "version": "0.16.1" + "full-revisionid": "341e0d8be9fedf66fbc9a95630b4ed2138343380", + "version": "0.18.0" } ''' # END VERSION_JSON diff --git a/frozen_deps/ecdsa/curves.py b/frozen_deps/ecdsa/curves.py index 9a10380..1119ee5 100644 --- a/frozen_deps/ecdsa/curves.py +++ b/frozen_deps/ecdsa/curves.py @@ -1,7 +1,9 @@ from __future__ import division -from . import der, ecdsa -from .util import orderlen +from six import PY2 +from . import der, ecdsa, ellipticcurve, eddsa +from .util import orderlen, number_to_string, string_to_number +from ._compat import normalise_bytes, bit_length # orderlen was defined in this module previously, so keep it in __all__, @@ -10,6 +12,10 @@ __all__ = [ "UnknownCurveError", "orderlen", "Curve", + "SECP112r1", + "SECP112r2", + "SECP128r1", + "SECP160r1", "NIST192p", "NIST224p", "NIST256p", @@ -17,6 +23,7 @@ __all__ = [ "NIST521p", "curves", "find_curve", + "curve_by_name", "SECP256k1", "BRAINPOOLP160r1", "BRAINPOOLP192r1", @@ -25,9 +32,17 @@ __all__ = [ "BRAINPOOLP320r1", "BRAINPOOLP384r1", "BRAINPOOLP512r1", + "PRIME_FIELD_OID", + "CHARACTERISTIC_TWO_FIELD_OID", + "Ed25519", + "Ed448", ] +PRIME_FIELD_OID = (1, 2, 840, 10045, 1, 1) +CHARACTERISTIC_TWO_FIELD_OID = (1, 2, 840, 10045, 1, 2) + + class UnknownCurveError(Exception): pass @@ -39,15 +54,262 @@ class Curve: self.curve = curve self.generator = generator self.order = generator.order() - self.baselen = orderlen(self.order) - self.verifying_key_length = 2 * self.baselen + if isinstance(curve, ellipticcurve.CurveEdTw): + # EdDSA keys are special in that both private and public + # are the same size (as it's defined only with compressed points) + + # +1 for the sign bit and then round up + self.baselen = (bit_length(curve.p()) + 1 + 7) // 8 + self.verifying_key_length = self.baselen + else: + self.baselen = orderlen(self.order) + self.verifying_key_length = 2 * orderlen(curve.p()) self.signature_length = 2 * self.baselen self.oid = oid - self.encoded_oid = der.encode_oid(*oid) + if oid: + self.encoded_oid = der.encode_oid(*oid) + + def __eq__(self, other): + if isinstance(other, Curve): + return ( + self.curve == other.curve and self.generator == other.generator + ) + return NotImplemented + + def __ne__(self, other): + return not self == other def __repr__(self): return self.name + def to_der(self, encoding=None, point_encoding="uncompressed"): + """Serialise the curve parameters to binary string. + + :param str encoding: the format to save the curve parameters in. + Default is ``named_curve``, with fallback being the ``explicit`` + if the OID is not set for the curve. + :param str point_encoding: the point encoding of the generator when + explicit curve encoding is used. Ignored for ``named_curve`` + format. + + :return: DER encoded ECParameters structure + :rtype: bytes + """ + if encoding is None: + if self.oid: + encoding = "named_curve" + else: + encoding = "explicit" + + if encoding not in ("named_curve", "explicit"): + raise ValueError( + "Only 'named_curve' and 'explicit' encodings supported" + ) + + if encoding == "named_curve": + if not self.oid: + raise UnknownCurveError( + "Can't encode curve using named_curve encoding without " + "associated curve OID" + ) + return der.encode_oid(*self.oid) + elif isinstance(self.curve, ellipticcurve.CurveEdTw): + assert encoding == "explicit" + raise UnknownCurveError( + "Twisted Edwards curves don't support explicit encoding" + ) + + # encode the ECParameters sequence + curve_p = self.curve.p() + version = der.encode_integer(1) + field_id = der.encode_sequence( + der.encode_oid(*PRIME_FIELD_OID), der.encode_integer(curve_p) + ) + curve = der.encode_sequence( + der.encode_octet_string( + number_to_string(self.curve.a() % curve_p, curve_p) + ), + der.encode_octet_string( + number_to_string(self.curve.b() % curve_p, curve_p) + ), + ) + base = der.encode_octet_string(self.generator.to_bytes(point_encoding)) + order = der.encode_integer(self.generator.order()) + seq_elements = [version, field_id, curve, base, order] + if self.curve.cofactor(): + cofactor = der.encode_integer(self.curve.cofactor()) + seq_elements.append(cofactor) + + return der.encode_sequence(*seq_elements) + + def to_pem(self, encoding=None, point_encoding="uncompressed"): + """ + Serialise the curve parameters to the :term:`PEM` format. + + :param str encoding: the format to save the curve parameters in. + Default is ``named_curve``, with fallback being the ``explicit`` + if the OID is not set for the curve. + :param str point_encoding: the point encoding of the generator when + explicit curve encoding is used. Ignored for ``named_curve`` + format. + + :return: PEM encoded ECParameters structure + :rtype: str + """ + return der.topem( + self.to_der(encoding, point_encoding), "EC PARAMETERS" + ) + + @staticmethod + def from_der(data, valid_encodings=None): + """Decode the curve parameters from DER file. + + :param data: the binary string to decode the parameters from + :type data: :term:`bytes-like object` + :param valid_encodings: set of names of allowed encodings, by default + all (set by passing ``None``), supported ones are ``named_curve`` + and ``explicit`` + :type valid_encodings: :term:`set-like object` + """ + if not valid_encodings: + valid_encodings = set(("named_curve", "explicit")) + if not all(i in ["named_curve", "explicit"] for i in valid_encodings): + raise ValueError( + "Only named_curve and explicit encodings supported" + ) + data = normalise_bytes(data) + if not der.is_sequence(data): + if "named_curve" not in valid_encodings: + raise der.UnexpectedDER( + "named_curve curve parameters not allowed" + ) + oid, empty = der.remove_object(data) + if empty: + raise der.UnexpectedDER("Unexpected data after OID") + return find_curve(oid) + + if "explicit" not in valid_encodings: + raise der.UnexpectedDER("explicit curve parameters not allowed") + + seq, empty = der.remove_sequence(data) + if empty: + raise der.UnexpectedDER( + "Unexpected data after ECParameters structure" + ) + # decode the ECParameters sequence + version, rest = der.remove_integer(seq) + if version != 1: + raise der.UnexpectedDER("Unknown parameter encoding format") + field_id, rest = der.remove_sequence(rest) + curve, rest = der.remove_sequence(rest) + base_bytes, rest = der.remove_octet_string(rest) + order, rest = der.remove_integer(rest) + cofactor = None + if rest: + # the ASN.1 specification of ECParameters allows for future + # extensions of the sequence, so ignore the remaining bytes + cofactor, _ = der.remove_integer(rest) + + # decode the ECParameters.fieldID sequence + field_type, rest = der.remove_object(field_id) + if field_type == CHARACTERISTIC_TWO_FIELD_OID: + raise UnknownCurveError("Characteristic 2 curves unsupported") + if field_type != PRIME_FIELD_OID: + raise UnknownCurveError( + "Unknown field type: {0}".format(field_type) + ) + prime, empty = der.remove_integer(rest) + if empty: + raise der.UnexpectedDER( + "Unexpected data after ECParameters.fieldID.Prime-p element" + ) + + # decode the ECParameters.curve sequence + curve_a_bytes, rest = der.remove_octet_string(curve) + curve_b_bytes, rest = der.remove_octet_string(rest) + # seed can be defined here, but we don't parse it, so ignore `rest` + + curve_a = string_to_number(curve_a_bytes) + curve_b = string_to_number(curve_b_bytes) + + curve_fp = ellipticcurve.CurveFp(prime, curve_a, curve_b, cofactor) + + # decode the ECParameters.base point + + base = ellipticcurve.PointJacobi.from_bytes( + curve_fp, + base_bytes, + valid_encodings=("uncompressed", "compressed", "hybrid"), + order=order, + generator=True, + ) + tmp_curve = Curve("unknown", curve_fp, base, None) + + # if the curve matches one of the well-known ones, use the well-known + # one in preference, as it will have the OID and name associated + for i in curves: + if tmp_curve == i: + return i + return tmp_curve + + @classmethod + def from_pem(cls, string, valid_encodings=None): + """Decode the curve parameters from PEM file. + + :param str string: the text string to decode the parameters from + :param valid_encodings: set of names of allowed encodings, by default + all (set by passing ``None``), supported ones are ``named_curve`` + and ``explicit`` + :type valid_encodings: :term:`set-like object` + """ + if not PY2 and isinstance(string, str): # pragma: no branch + string = string.encode() + + ec_param_index = string.find(b"-----BEGIN EC PARAMETERS-----") + if ec_param_index == -1: + raise der.UnexpectedDER("EC PARAMETERS PEM header not found") + + return cls.from_der( + der.unpem(string[ec_param_index:]), valid_encodings + ) + + +# the SEC curves +SECP112r1 = Curve( + "SECP112r1", + ecdsa.curve_112r1, + ecdsa.generator_112r1, + (1, 3, 132, 0, 6), + "secp112r1", +) + + +SECP112r2 = Curve( + "SECP112r2", + ecdsa.curve_112r2, + ecdsa.generator_112r2, + (1, 3, 132, 0, 7), + "secp112r2", +) + + +SECP128r1 = Curve( + "SECP128r1", + ecdsa.curve_128r1, + ecdsa.generator_128r1, + (1, 3, 132, 0, 28), + "secp128r1", +) + + +SECP160r1 = Curve( + "SECP160r1", + ecdsa.curve_160r1, + ecdsa.generator_160r1, + (1, 3, 132, 0, 8), + "secp160r1", +) + # the NIST curves NIST192p = Curve( @@ -167,6 +429,23 @@ BRAINPOOLP512r1 = Curve( ) +Ed25519 = Curve( + "Ed25519", + eddsa.curve_ed25519, + eddsa.generator_ed25519, + (1, 3, 101, 112), +) + + +Ed448 = Curve( + "Ed448", + eddsa.curve_ed448, + eddsa.generator_ed448, + (1, 3, 101, 113), +) + + +# no order in particular, but keep previously added curves first curves = [ NIST192p, NIST224p, @@ -181,10 +460,26 @@ curves = [ BRAINPOOLP320r1, BRAINPOOLP384r1, BRAINPOOLP512r1, + SECP112r1, + SECP112r2, + SECP128r1, + SECP160r1, + Ed25519, + Ed448, ] def find_curve(oid_curve): + """Select a curve based on its OID + + :param tuple[int,...] oid_curve: ASN.1 Object Identifier of the + curve to return, like ``(1, 2, 840, 10045, 3, 1, 7)`` for ``NIST256p``. + + :raises UnknownCurveError: When the oid doesn't match any of the supported + curves + + :rtype: ~ecdsa.curves.Curve + """ for c in curves: if c.oid == oid_curve: return c @@ -192,3 +487,27 @@ def find_curve(oid_curve): "I don't know about the curve with oid %s." "I only know about these: %s" % (oid_curve, [c.name for c in curves]) ) + + +def curve_by_name(name): + """Select a curve based on its name. + + Returns a :py:class:`~ecdsa.curves.Curve` object with a ``name`` name. + Note that ``name`` is case-sensitve. + + :param str name: Name of the curve to return, like ``NIST256p`` or + ``prime256v1`` + + :raises UnknownCurveError: When the name doesn't match any of the supported + curves + + :rtype: ~ecdsa.curves.Curve + """ + for c in curves: + if name == c.name or (c.openssl_name and name == c.openssl_name): + return c + raise UnknownCurveError( + "Curve with name {0!r} unknown, only curves supported: {1}".format( + name, [c.name for c in curves] + ) + ) diff --git a/frozen_deps/ecdsa/der.py b/frozen_deps/ecdsa/der.py index 8c1de9b..8b27941 100644 --- a/frozen_deps/ecdsa/der.py +++ b/frozen_deps/ecdsa/der.py @@ -87,7 +87,7 @@ def encode_bitstring(s, unused=_sentry): if not s: raise ValueError("unused is non-zero but s is empty") last = str_idx_as_int(s, -1) - if last & (2 ** unused - 1): + if last & (2**unused - 1): raise ValueError("unused bits must be zeros in DER") encoded_unused = int2byte(unused) len_extra = 1 @@ -348,7 +348,7 @@ def remove_bitstring(string, expect_unused=_sentry): raise UnexpectedDER("Invalid encoding of empty bit string") last = str_idx_as_int(body, -1) # verify that all the unused bits are set to zero (DER requirement) - if last & (2 ** unused - 1): + if last & (2**unused - 1): raise UnexpectedDER("Non zero padding bits in bit string") if expect_unused is None: body = (body, unused) @@ -386,7 +386,7 @@ def remove_bitstring(string, expect_unused=_sentry): def unpem(pem): - if isinstance(pem, text_type): + if isinstance(pem, text_type): # pragma: no branch pem = pem.encode() d = b("").join( diff --git a/frozen_deps/ecdsa/ecdh.py b/frozen_deps/ecdsa/ecdh.py index 9173279..7f697d9 100644 --- a/frozen_deps/ecdsa/ecdh.py +++ b/frozen_deps/ecdsa/ecdh.py @@ -116,7 +116,7 @@ class ECDH(object): :raises NoCurveError: Curve must be set before key generation. :return: public (verifying) key from this private key. - :rtype: VerifyingKey object + :rtype: VerifyingKey """ if not self.curve: raise NoCurveError("Curve must be set prior to key generation.") @@ -135,7 +135,7 @@ class ECDH(object): :raises InvalidCurveError: private_key curve not the same as self.curve :return: public (verifying) key from this private key. - :rtype: VerifyingKey object + :rtype: VerifyingKey """ if not self.curve: self.curve = private_key.curve @@ -158,7 +158,7 @@ class ECDH(object): :raises NoCurveError: Curve must be set before loading. :return: public (verifying) key from this private key. - :rtype: VerifyingKey object + :rtype: VerifyingKey """ if not self.curve: raise NoCurveError("Curve must be set prior to key load.") @@ -183,7 +183,7 @@ class ECDH(object): :raises InvalidCurveError: private_key curve not the same as self.curve :return: public (verifying) key from this private key. - :rtype: VerifyingKey object + :rtype: VerifyingKey """ return self.load_private_key(SigningKey.from_der(private_key_der)) @@ -204,7 +204,7 @@ class ECDH(object): :raises InvalidCurveError: private_key curve not the same as self.curve :return: public (verifying) key from this private key. - :rtype: VerifyingKey object + :rtype: VerifyingKey """ return self.load_private_key(SigningKey.from_pem(private_key_pem)) @@ -215,8 +215,8 @@ class ECDH(object): Needs to be sent to the remote party. :return: public (verifying) key from local private key. - :rtype: VerifyingKey object - """ + :rtype: VerifyingKey + """ return self.private_key.get_verifying_key() def load_received_public_key(self, public_key): @@ -237,7 +237,9 @@ class ECDH(object): raise InvalidCurveError("Curve mismatch.") self.public_key = public_key - def load_received_public_key_bytes(self, public_key_str): + def load_received_public_key_bytes( + self, public_key_str, valid_encodings=None + ): """ Load public key from byte string. @@ -247,9 +249,16 @@ class ECDH(object): :param public_key_str: public key in bytes string format :type public_key_str: :term:`bytes-like object` + :param valid_encodings: list of acceptable point encoding formats, + supported ones are: :term:`uncompressed`, :term:`compressed`, + :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` + name). All formats by default (specified with ``None``). + :type valid_encodings: :term:`set-like object` """ return self.load_received_public_key( - VerifyingKey.from_string(public_key_str, self.curve) + VerifyingKey.from_string( + public_key_str, self.curve, valid_encodings + ) ) def load_received_public_key_der(self, public_key_der): @@ -301,10 +310,10 @@ class ECDH(object): :raises NoKeyError: public_key or private_key is not set :return: shared secret - :rtype: byte string + :rtype: bytes """ return number_to_string( - self.generate_sharedsecret(), self.private_key.curve.order + self.generate_sharedsecret(), self.private_key.curve.curve.p() ) def generate_sharedsecret(self): @@ -314,9 +323,9 @@ class ECDH(object): The objects needs to have both private key and received public key before generation is allowed. - It's the same for local and remote party. - shared secret(local private key, remote public key ) == - shared secret (local public key, remote private key) + It's the same for local and remote party, + shared secret(local private key, remote public key) == + shared secret(local public key, remote private key) :raises InvalidCurveError: public_key curve not the same as self.curve :raises NoKeyError: public_key or private_key is not set diff --git a/frozen_deps/ecdsa/ecdsa.py b/frozen_deps/ecdsa/ecdsa.py index d785a45..3328281 100644 --- a/frozen_deps/ecdsa/ecdsa.py +++ b/frozen_deps/ecdsa/ecdsa.py @@ -1,56 +1,67 @@ #! /usr/bin/env python """ -Implementation of Elliptic-Curve Digital Signatures. +Low level implementation of Elliptic-Curve Digital Signatures. + +.. note :: + You're most likely looking for the :py:class:`~ecdsa.keys` module. + This is a low-level implementation of the ECDSA that operates on + integers, not byte strings. + +NOTE: This a low level implementation of ECDSA, for normal applications +you should be looking at the keys.py module. Classes and methods for elliptic-curve signatures: private keys, public keys, signatures, -NIST prime-modulus curves with modulus lengths of -192, 224, 256, 384, and 521 bits. +and definitions of prime-modulus curves. Example: - # (In real-life applications, you would probably want to - # protect against defects in SystemRandom.) - from random import SystemRandom - randrange = SystemRandom().randrange +.. code-block:: python - # Generate a public/private key pair using the NIST Curve P-192: + # (In real-life applications, you would probably want to + # protect against defects in SystemRandom.) + from random import SystemRandom + randrange = SystemRandom().randrange - g = generator_192 - n = g.order() - secret = randrange( 1, n ) - pubkey = Public_key( g, g * secret ) - privkey = Private_key( pubkey, secret ) + # Generate a public/private key pair using the NIST Curve P-192: - # Signing a hash value: + g = generator_192 + n = g.order() + secret = randrange( 1, n ) + pubkey = Public_key( g, g * secret ) + privkey = Private_key( pubkey, secret ) - hash = randrange( 1, n ) - signature = privkey.sign( hash, randrange( 1, n ) ) + # Signing a hash value: - # Verifying a signature for a hash value: + hash = randrange( 1, n ) + signature = privkey.sign( hash, randrange( 1, n ) ) - if pubkey.verifies( hash, signature ): - print_("Demo verification succeeded.") - else: - print_("*** Demo verification failed.") + # Verifying a signature for a hash value: - # Verification fails if the hash value is modified: + if pubkey.verifies( hash, signature ): + print_("Demo verification succeeded.") + else: + print_("*** Demo verification failed.") - if pubkey.verifies( hash-1, signature ): - print_("**** Demo verification failed to reject tampered hash.") - else: - print_("Demo verification correctly rejected tampered hash.") + # Verification fails if the hash value is modified: -Version of 2009.05.16. + if pubkey.verifies( hash-1, signature ): + print_("**** Demo verification failed to reject tampered hash.") + else: + print_("Demo verification correctly rejected tampered hash.") Revision history: 2005.12.31 - Initial version. + 2008.11.25 - Substantial revisions introducing new classes. + 2009.05.16 - Warn against using random.randrange in real applications. + 2009.05.17 - Use random.SystemRandom by default. -Written in 2005 by Peter Pearson and placed in the public domain. +Originally written in 2005 by Peter Pearson and placed in the public domain, +modified as part of the python-ecdsa package. """ from six import int2byte, b @@ -69,16 +80,26 @@ class InvalidPointError(RuntimeError): class Signature(object): - """ECDSA signature.""" + """ + ECDSA signature. + + :ivar int r: the ``r`` element of the ECDSA signature + :ivar int s: the ``s`` element of the ECDSA signature + """ def __init__(self, r, s): self.r = r self.s = s def recover_public_keys(self, hash, generator): - """Returns two public keys for which the signature is valid - hash is signed hash - generator is the used generator of the signature + """ + Returns two public keys for which the signature is valid + + :param int hash: signed hash + :param AbstractPoint generator: is the generator used in creation + of the signature + :rtype: tuple(Public_key, Public_key) + :return: a pair of public keys that can validate the signature """ curve = generator.curve() n = generator.order() @@ -118,7 +139,7 @@ class Public_key(object): :param bool verify: if True check if point is valid point on curve :raises InvalidPointError: if the point parameters are invalid or - point does not lie on the curve + point does not lay on the curve """ self.curve = generator.curve() @@ -131,7 +152,7 @@ class Public_key(object): "The public point has x or y out of range." ) if verify and not self.curve.contains_point(point.x(), point.y()): - raise InvalidPointError("Point does not lie on the curve") + raise InvalidPointError("Point does not lay on the curve") if not n: raise InvalidPointError("Generator point must have order.") # for curve parameters with base point with cofactor 1, all points @@ -145,11 +166,20 @@ class Public_key(object): raise InvalidPointError("Generator point order is bad.") def __eq__(self, other): + """Return True if the keys are identical, False otherwise. + + Note: for comparison, only placement on the same curve and point + equality is considered, use of the same generator point is not + considered. + """ if isinstance(other, Public_key): - """Return True if the points are identical, False otherwise.""" return self.curve == other.curve and self.point == other.point return NotImplemented + def __ne__(self, other): + """Return False if the keys are identical, True otherwise.""" + return not self == other + def verifies(self, hash, signature): """Verify that signature is a valid signature of hash. Return True if the signature is valid. @@ -188,14 +218,18 @@ class Private_key(object): self.secret_multiplier = secret_multiplier def __eq__(self, other): + """Return True if the points are identical, False otherwise.""" if isinstance(other, Private_key): - """Return True if the points are identical, False otherwise.""" return ( self.public_key == other.public_key and self.secret_multiplier == other.secret_multiplier ) return NotImplemented + def __ne__(self, other): + """Return False if the points are identical, True otherwise.""" + return not self == other + def sign(self, hash, random_k): """Return a signature for the provided hash, using the provided random nonce. It is absolutely vital that random_k be an unpredictable @@ -262,7 +296,7 @@ def string_to_int(s): def digest_integer(m): """Convert an integer into a string of bytes, compute - its SHA-1 hash, and convert the result to an integer.""" + its SHA-1 hash, and convert the result to an integer.""" # # I don't expect this function to be used much. I wrote # it in order to be able to duplicate the examples @@ -294,6 +328,77 @@ def point_is_valid(generator, x, y): return True +# secp112r1 curve +_p = int(remove_whitespace("DB7C 2ABF62E3 5E668076 BEAD208B"), 16) +# s = 00F50B02 8E4D696E 67687561 51752904 72783FB1 +_a = int(remove_whitespace("DB7C 2ABF62E3 5E668076 BEAD2088"), 16) +_b = int(remove_whitespace("659E F8BA0439 16EEDE89 11702B22"), 16) +_Gx = int(remove_whitespace("09487239 995A5EE7 6B55F9C2 F098"), 16) +_Gy = int(remove_whitespace("A89C E5AF8724 C0A23E0E 0FF77500"), 16) +_r = int(remove_whitespace("DB7C 2ABF62E3 5E7628DF AC6561C5"), 16) +_h = 1 +curve_112r1 = ellipticcurve.CurveFp(_p, _a, _b, _h) +generator_112r1 = ellipticcurve.PointJacobi( + curve_112r1, _Gx, _Gy, 1, _r, generator=True +) + + +# secp112r2 curve +_p = int(remove_whitespace("DB7C 2ABF62E3 5E668076 BEAD208B"), 16) +# s = 022757A1 114D69E 67687561 51755316 C05E0BD4 +_a = int(remove_whitespace("6127 C24C05F3 8A0AAAF6 5C0EF02C"), 16) +_b = int(remove_whitespace("51DE F1815DB5 ED74FCC3 4C85D709"), 16) +_Gx = int(remove_whitespace("4BA30AB5 E892B4E1 649DD092 8643"), 16) +_Gy = int(remove_whitespace("ADCD 46F5882E 3747DEF3 6E956E97"), 16) +_r = int(remove_whitespace("36DF 0AAFD8B8 D7597CA1 0520D04B"), 16) +_h = 4 +curve_112r2 = ellipticcurve.CurveFp(_p, _a, _b, _h) +generator_112r2 = ellipticcurve.PointJacobi( + curve_112r2, _Gx, _Gy, 1, _r, generator=True +) + + +# secp128r1 curve +_p = int(remove_whitespace("FFFFFFFD FFFFFFFF FFFFFFFF FFFFFFFF"), 16) +# S = 000E0D4D 69E6768 75615175 0CC03A44 73D03679 +# a and b are mod p, so a is equal to p-3, or simply -3 +# _a = -3 +_b = int(remove_whitespace("E87579C1 1079F43D D824993C 2CEE5ED3"), 16) +_Gx = int(remove_whitespace("161FF752 8B899B2D 0C28607C A52C5B86"), 16) +_Gy = int(remove_whitespace("CF5AC839 5BAFEB13 C02DA292 DDED7A83"), 16) +_r = int(remove_whitespace("FFFFFFFE 00000000 75A30D1B 9038A115"), 16) +_h = 1 +curve_128r1 = ellipticcurve.CurveFp(_p, -3, _b, _h) +generator_128r1 = ellipticcurve.PointJacobi( + curve_128r1, _Gx, _Gy, 1, _r, generator=True +) + + +# secp160r1 +_p = int(remove_whitespace("FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 7FFFFFFF"), 16) +# S = 1053CDE4 2C14D696 E6768756 1517533B F3F83345 +# a and b are mod p, so a is equal to p-3, or simply -3 +# _a = -3 +_b = int(remove_whitespace("1C97BEFC 54BD7A8B 65ACF89F 81D4D4AD C565FA45"), 16) +_Gx = int( + remove_whitespace("4A96B568 8EF57328 46646989 68C38BB9 13CBFC82"), + 16, +) +_Gy = int( + remove_whitespace("23A62855 3168947D 59DCC912 04235137 7AC5FB32"), + 16, +) +_r = int( + remove_whitespace("01 00000000 00000000 0001F4C8 F927AED3 CA752257"), + 16, +) +_h = 1 +curve_160r1 = ellipticcurve.CurveFp(_p, -3, _b, _h) +generator_160r1 = ellipticcurve.PointJacobi( + curve_160r1, _Gx, _Gy, 1, _r, generator=True +) + + # NIST Curve P-192: _p = 6277101735386680763835789423207666416083908700390324961279 _r = 6277101735386680763835789423176059013767194773182842284081 diff --git a/frozen_deps/ecdsa/eddsa.py b/frozen_deps/ecdsa/eddsa.py new file mode 100644 index 0000000..9769cfd --- /dev/null +++ b/frozen_deps/ecdsa/eddsa.py @@ -0,0 +1,252 @@ +"""Implementation of Edwards Digital Signature Algorithm.""" + +import hashlib +from ._sha3 import shake_256 +from . import ellipticcurve +from ._compat import ( + remove_whitespace, + bit_length, + bytes_to_int, + int_to_bytes, + compat26_str, +) + +# edwards25519, defined in RFC7748 +_p = 2**255 - 19 +_a = -1 +_d = int( + remove_whitespace( + "370957059346694393431380835087545651895421138798432190163887855330" + "85940283555" + ) +) +_h = 8 + +_Gx = int( + remove_whitespace( + "151122213495354007725011514095885315114540126930418572060461132" + "83949847762202" + ) +) +_Gy = int( + remove_whitespace( + "463168356949264781694283940034751631413079938662562256157830336" + "03165251855960" + ) +) +_r = 2**252 + 0x14DEF9DEA2F79CD65812631A5CF5D3ED + + +def _sha512(data): + return hashlib.new("sha512", compat26_str(data)).digest() + + +curve_ed25519 = ellipticcurve.CurveEdTw(_p, _a, _d, _h, _sha512) +generator_ed25519 = ellipticcurve.PointEdwards( + curve_ed25519, _Gx, _Gy, 1, _Gx * _Gy % _p, _r, generator=True +) + + +# edwards448, defined in RFC7748 +_p = 2**448 - 2**224 - 1 +_a = 1 +_d = -39081 % _p +_h = 4 + +_Gx = int( + remove_whitespace( + "224580040295924300187604334099896036246789641632564134246125461" + "686950415467406032909029192869357953282578032075146446173674602635" + "247710" + ) +) +_Gy = int( + remove_whitespace( + "298819210078481492676017930443930673437544040154080242095928241" + "372331506189835876003536878655418784733982303233503462500531545062" + "832660" + ) +) +_r = 2**446 - 0x8335DC163BB124B65129C96FDE933D8D723A70AADC873D6D54A7BB0D + + +def _shake256(data): + return shake_256(data, 114) + + +curve_ed448 = ellipticcurve.CurveEdTw(_p, _a, _d, _h, _shake256) +generator_ed448 = ellipticcurve.PointEdwards( + curve_ed448, _Gx, _Gy, 1, _Gx * _Gy % _p, _r, generator=True +) + + +class PublicKey(object): + """Public key for the Edwards Digital Signature Algorithm.""" + + def __init__(self, generator, public_key, public_point=None): + self.generator = generator + self.curve = generator.curve() + self.__encoded = public_key + # plus one for the sign bit and round up + self.baselen = (bit_length(self.curve.p()) + 1 + 7) // 8 + if len(public_key) != self.baselen: + raise ValueError( + "Incorrect size of the public key, expected: {0} bytes".format( + self.baselen + ) + ) + if public_point: + self.__point = public_point + else: + self.__point = ellipticcurve.PointEdwards.from_bytes( + self.curve, public_key + ) + + def __eq__(self, other): + if isinstance(other, PublicKey): + return ( + self.curve == other.curve and self.__encoded == other.__encoded + ) + return NotImplemented + + def __ne__(self, other): + return not self == other + + @property + def point(self): + return self.__point + + @point.setter + def point(self, other): + if self.__point != other: + raise ValueError("Can't change the coordinates of the point") + self.__point = other + + def public_point(self): + return self.__point + + def public_key(self): + return self.__encoded + + def verify(self, data, signature): + """Verify a Pure EdDSA signature over data.""" + data = compat26_str(data) + if len(signature) != 2 * self.baselen: + raise ValueError( + "Invalid signature length, expected: {0} bytes".format( + 2 * self.baselen + ) + ) + R = ellipticcurve.PointEdwards.from_bytes( + self.curve, signature[: self.baselen] + ) + S = bytes_to_int(signature[self.baselen :], "little") + if S >= self.generator.order(): + raise ValueError("Invalid signature") + + dom = bytearray() + if self.curve == curve_ed448: + dom = bytearray(b"SigEd448" + b"\x00\x00") + + k = bytes_to_int( + self.curve.hash_func(dom + R.to_bytes() + self.__encoded + data), + "little", + ) + + if self.generator * S != self.__point * k + R: + raise ValueError("Invalid signature") + + return True + + +class PrivateKey(object): + """Private key for the Edwards Digital Signature Algorithm.""" + + def __init__(self, generator, private_key): + self.generator = generator + self.curve = generator.curve() + # plus one for the sign bit and round up + self.baselen = (bit_length(self.curve.p()) + 1 + 7) // 8 + if len(private_key) != self.baselen: + raise ValueError( + "Incorrect size of private key, expected: {0} bytes".format( + self.baselen + ) + ) + self.__private_key = bytes(private_key) + self.__h = bytearray(self.curve.hash_func(private_key)) + self.__public_key = None + + a = self.__h[: self.baselen] + a = self._key_prune(a) + scalar = bytes_to_int(a, "little") + self.__s = scalar + + @property + def private_key(self): + return self.__private_key + + def __eq__(self, other): + if isinstance(other, PrivateKey): + return ( + self.curve == other.curve + and self.__private_key == other.__private_key + ) + return NotImplemented + + def __ne__(self, other): + return not self == other + + def _key_prune(self, key): + # make sure the key is not in a small subgroup + h = self.curve.cofactor() + if h == 4: + h_log = 2 + elif h == 8: + h_log = 3 + else: + raise ValueError("Only cofactor 4 and 8 curves supported") + key[0] &= ~((1 << h_log) - 1) + + # ensure the highest bit is set but no higher + l = bit_length(self.curve.p()) + if l % 8 == 0: + key[-1] = 0 + key[-2] |= 0x80 + else: + key[-1] = key[-1] & (1 << (l % 8)) - 1 | 1 << (l % 8) - 1 + return key + + def public_key(self): + """Generate the public key based on the included private key""" + if self.__public_key: + return self.__public_key + + public_point = self.generator * self.__s + + self.__public_key = PublicKey( + self.generator, public_point.to_bytes(), public_point + ) + + return self.__public_key + + def sign(self, data): + """Perform a Pure EdDSA signature over data.""" + data = compat26_str(data) + A = self.public_key().public_key() + + prefix = self.__h[self.baselen :] + + dom = bytearray() + if self.curve == curve_ed448: + dom = bytearray(b"SigEd448" + b"\x00\x00") + + r = bytes_to_int(self.curve.hash_func(dom + prefix + data), "little") + R = (self.generator * r).to_bytes() + + k = bytes_to_int(self.curve.hash_func(dom + R + A + data), "little") + k %= self.generator.order() + + S = (r + k * self.__s) % self.generator.order() + + return R + int_to_bytes(S, self.baselen, "little") diff --git a/frozen_deps/ecdsa/ellipticcurve.py b/frozen_deps/ecdsa/ellipticcurve.py index 25565df..d6f7146 100644 --- a/frozen_deps/ecdsa/ellipticcurve.py +++ b/frozen_deps/ecdsa/ellipticcurve.py @@ -25,13 +25,12 @@ # Signature checking (5.4.2): # - Verify that r and s are in [1,n-1]. # -# Version of 2008.11.25. -# # Revision history: # 2005.12.31 - Initial version. # 2008.11.25 - Change CurveFp.is_on to contains_point. # # Written in 2005 by Peter Pearson and placed in the public domain. +# Modified extensively as part of python-ecdsa. from __future__ import division @@ -39,7 +38,7 @@ try: from gmpy2 import mpz GMPY = True -except ImportError: +except ImportError: # pragma: no branch try: from gmpy import mpz @@ -50,14 +49,19 @@ except ImportError: from six import python_2_unicode_compatible from . import numbertheory -from ._rwlock import RWLock +from ._compat import normalise_bytes, int_to_bytes, bit_length, bytes_to_int +from .errors import MalformedPointError +from .util import orderlen, string_to_number, number_to_string @python_2_unicode_compatible class CurveFp(object): - """Elliptic Curve over the field of integers modulo a prime.""" + """ + :term:`Short Weierstrass Elliptic Curve <short Weierstrass curve>` over a + prime field. + """ - if GMPY: + if GMPY: # pragma: no branch def __init__(self, p, a, b, h=None): """ @@ -75,7 +79,7 @@ class CurveFp(object): # gmpy with it self.__h = h - else: + else: # pragma: no branch def __init__(self, p, a, b, h=None): """ @@ -92,17 +96,25 @@ class CurveFp(object): self.__h = h def __eq__(self, other): + """Return True if other is an identical curve, False otherwise. + + Note: the value of the cofactor of the curve is not taken into account + when comparing curves, as it's derived from the base point and + intrinsic curve characteristic (but it's complex to compute), + only the prime and curve parameters are considered. + """ if isinstance(other, CurveFp): - """Return True if the curves are identical, False otherwise.""" + p = self.__p return ( self.__p == other.__p - and self.__a == other.__a - and self.__b == other.__b + and self.__a % p == other.__a % p + and self.__b % p == other.__b % p ) return NotImplemented def __ne__(self, other): - return not (self == other) + """Return False if other is an identical curve, True otherwise.""" + return not self == other def __hash__(self): return hash((self.__p, self.__a, self.__b)) @@ -132,9 +144,356 @@ class CurveFp(object): ) -class PointJacobi(object): +class CurveEdTw(object): + """Parameters for a Twisted Edwards Elliptic Curve""" + + if GMPY: # pragma: no branch + + def __init__(self, p, a, d, h=None, hash_func=None): + """ + The curve of points satisfying a*x^2 + y^2 = 1 + d*x^2*y^2 (mod p). + + h is the cofactor of the curve. + hash_func is the hash function associated with the curve + (like SHA-512 for Ed25519) + """ + self.__p = mpz(p) + self.__a = mpz(a) + self.__d = mpz(d) + self.__h = h + self.__hash_func = hash_func + + else: + + def __init__(self, p, a, d, h=None, hash_func=None): + """ + The curve of points satisfying a*x^2 + y^2 = 1 + d*x^2*y^2 (mod p). + + h is the cofactor of the curve. + hash_func is the hash function associated with the curve + (like SHA-512 for Ed25519) + """ + self.__p = p + self.__a = a + self.__d = d + self.__h = h + self.__hash_func = hash_func + + def __eq__(self, other): + """Returns True if other is an identical curve.""" + if isinstance(other, CurveEdTw): + p = self.__p + return ( + self.__p == other.__p + and self.__a % p == other.__a % p + and self.__d % p == other.__d % p + ) + return NotImplemented + + def __ne__(self, other): + """Return False if the other is an identical curve, True otherwise.""" + return not self == other + + def __hash__(self): + return hash((self.__p, self.__a, self.__d)) + + def contains_point(self, x, y): + """Is the point (x, y) on this curve?""" + return ( + self.__a * x * x + y * y - 1 - self.__d * x * x * y * y + ) % self.__p == 0 + + def p(self): + return self.__p + + def a(self): + return self.__a + + def d(self): + return self.__d + + def hash_func(self, data): + return self.__hash_func(data) + + def cofactor(self): + return self.__h + + def __str__(self): + return "CurveEdTw(p={0}, a={1}, d={2}, h={3})".format( + self.__p, + self.__a, + self.__d, + self.__h, + ) + + +class AbstractPoint(object): + """Class for common methods of elliptic curve points.""" + + @staticmethod + def _from_raw_encoding(data, raw_encoding_length): + """ + Decode public point from :term:`raw encoding`. + + :term:`raw encoding` is the same as the :term:`uncompressed` encoding, + but without the 0x04 byte at the beginning. + """ + # real assert, from_bytes() should not call us with different length + assert len(data) == raw_encoding_length + xs = data[: raw_encoding_length // 2] + ys = data[raw_encoding_length // 2 :] + # real assert, raw_encoding_length is calculated by multiplying an + # integer by two so it will always be even + assert len(xs) == raw_encoding_length // 2 + assert len(ys) == raw_encoding_length // 2 + coord_x = string_to_number(xs) + coord_y = string_to_number(ys) + + return coord_x, coord_y + + @staticmethod + def _from_compressed(data, curve): + """Decode public point from compressed encoding.""" + if data[:1] not in (b"\x02", b"\x03"): + raise MalformedPointError("Malformed compressed point encoding") + + is_even = data[:1] == b"\x02" + x = string_to_number(data[1:]) + p = curve.p() + alpha = (pow(x, 3, p) + (curve.a() * x) + curve.b()) % p + try: + beta = numbertheory.square_root_mod_prime(alpha, p) + except numbertheory.Error as e: + raise MalformedPointError( + "Encoding does not correspond to a point on curve", e + ) + if is_even == bool(beta & 1): + y = p - beta + else: + y = beta + return x, y + + @classmethod + def _from_hybrid(cls, data, raw_encoding_length, validate_encoding): + """Decode public point from hybrid encoding.""" + # real assert, from_bytes() should not call us with different types + assert data[:1] in (b"\x06", b"\x07") + + # primarily use the uncompressed as it's easiest to handle + x, y = cls._from_raw_encoding(data[1:], raw_encoding_length) + + # but validate if it's self-consistent if we're asked to do that + if validate_encoding and ( + y & 1 + and data[:1] != b"\x07" + or (not y & 1) + and data[:1] != b"\x06" + ): + raise MalformedPointError("Inconsistent hybrid point encoding") + + return x, y + + @classmethod + def _from_edwards(cls, curve, data): + """Decode a point on an Edwards curve.""" + data = bytearray(data) + p = curve.p() + # add 1 for the sign bit and then round up + exp_len = (bit_length(p) + 1 + 7) // 8 + if len(data) != exp_len: + raise MalformedPointError("Point length doesn't match the curve.") + x_0 = (data[-1] & 0x80) >> 7 + + data[-1] &= 0x80 - 1 + + y = bytes_to_int(data, "little") + if GMPY: + y = mpz(y) + + x2 = ( + (y * y - 1) + * numbertheory.inverse_mod(curve.d() * y * y - curve.a(), p) + % p + ) + + try: + x = numbertheory.square_root_mod_prime(x2, p) + except numbertheory.Error as e: + raise MalformedPointError( + "Encoding does not correspond to a point on curve", e + ) + + if x % 2 != x_0: + x = -x % p + + return x, y + + @classmethod + def from_bytes( + cls, curve, data, validate_encoding=True, valid_encodings=None + ): + """ + Initialise the object from byte encoding of a point. + + The method does accept and automatically detect the type of point + encoding used. It supports the :term:`raw encoding`, + :term:`uncompressed`, :term:`compressed`, and :term:`hybrid` encodings. + + Note: generally you will want to call the ``from_bytes()`` method of + either a child class, PointJacobi or Point. + + :param data: single point encoding of the public key + :type data: :term:`bytes-like object` + :param curve: the curve on which the public key is expected to lay + :type curve: ~ecdsa.ellipticcurve.CurveFp + :param validate_encoding: whether to verify that the encoding of the + point is self-consistent, defaults to True, has effect only + on ``hybrid`` encoding + :type validate_encoding: bool + :param valid_encodings: list of acceptable point encoding formats, + supported ones are: :term:`uncompressed`, :term:`compressed`, + :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` + name). All formats by default (specified with ``None``). + :type valid_encodings: :term:`set-like object` + + :raises `~ecdsa.errors.MalformedPointError`: if the public point does + not lay on the curve or the encoding is invalid + + :return: x and y coordinates of the encoded point + :rtype: tuple(int, int) + """ + if not valid_encodings: + valid_encodings = set( + ["uncompressed", "compressed", "hybrid", "raw"] + ) + if not all( + i in set(("uncompressed", "compressed", "hybrid", "raw")) + for i in valid_encodings + ): + raise ValueError( + "Only uncompressed, compressed, hybrid or raw encoding " + "supported." + ) + data = normalise_bytes(data) + + if isinstance(curve, CurveEdTw): + return cls._from_edwards(curve, data) + + key_len = len(data) + raw_encoding_length = 2 * orderlen(curve.p()) + if key_len == raw_encoding_length and "raw" in valid_encodings: + coord_x, coord_y = cls._from_raw_encoding( + data, raw_encoding_length + ) + elif key_len == raw_encoding_length + 1 and ( + "hybrid" in valid_encodings or "uncompressed" in valid_encodings + ): + if data[:1] in (b"\x06", b"\x07") and "hybrid" in valid_encodings: + coord_x, coord_y = cls._from_hybrid( + data, raw_encoding_length, validate_encoding + ) + elif data[:1] == b"\x04" and "uncompressed" in valid_encodings: + coord_x, coord_y = cls._from_raw_encoding( + data[1:], raw_encoding_length + ) + else: + raise MalformedPointError( + "Invalid X9.62 encoding of the public point" + ) + elif ( + key_len == raw_encoding_length // 2 + 1 + and "compressed" in valid_encodings + ): + coord_x, coord_y = cls._from_compressed(data, curve) + else: + raise MalformedPointError( + "Length of string does not match lengths of " + "any of the enabled ({0}) encodings of the " + "curve.".format(", ".join(valid_encodings)) + ) + return coord_x, coord_y + + def _raw_encode(self): + """Convert the point to the :term:`raw encoding`.""" + prime = self.curve().p() + x_str = number_to_string(self.x(), prime) + y_str = number_to_string(self.y(), prime) + return x_str + y_str + + def _compressed_encode(self): + """Encode the point into the compressed form.""" + prime = self.curve().p() + x_str = number_to_string(self.x(), prime) + if self.y() & 1: + return b"\x03" + x_str + return b"\x02" + x_str + + def _hybrid_encode(self): + """Encode the point into the hybrid form.""" + raw_enc = self._raw_encode() + if self.y() & 1: + return b"\x07" + raw_enc + return b"\x06" + raw_enc + + def _edwards_encode(self): + """Encode the point according to RFC8032 encoding.""" + self.scale() + x, y, p = self.x(), self.y(), self.curve().p() + + # add 1 for the sign bit and then round up + enc_len = (bit_length(p) + 1 + 7) // 8 + y_str = int_to_bytes(y, enc_len, "little") + if x % 2: + y_str[-1] |= 0x80 + return y_str + + def to_bytes(self, encoding="raw"): + """ + Convert the point to a byte string. + + The method by default uses the :term:`raw encoding` (specified + by `encoding="raw"`. It can also output points in :term:`uncompressed`, + :term:`compressed`, and :term:`hybrid` formats. + + For points on Edwards curves `encoding` is ignored and only the + encoding defined in RFC 8032 is supported. + + :return: :term:`raw encoding` of a public on the curve + :rtype: bytes + """ + assert encoding in ("raw", "uncompressed", "compressed", "hybrid") + curve = self.curve() + if isinstance(curve, CurveEdTw): + return self._edwards_encode() + elif encoding == "raw": + return self._raw_encode() + elif encoding == "uncompressed": + return b"\x04" + self._raw_encode() + elif encoding == "hybrid": + return self._hybrid_encode() + else: + return self._compressed_encode() + + @staticmethod + def _naf(mult): + """Calculate non-adjacent form of number.""" + ret = [] + while mult: + if mult % 2: + nd = mult % 4 + if nd >= 2: + nd -= 4 + ret.append(nd) + mult -= nd + else: + ret.append(0) + mult //= 2 + return ret + + +class PointJacobi(AbstractPoint): """ - Point on an elliptic curve. Uses Jacobi coordinates. + Point on a short Weierstrass elliptic curve. Uses Jacobi coordinates. In Jacobian coordinates, there are three parameters, X, Y and Z. They correspond to affine parameters 'x' and 'y' like so: @@ -158,88 +517,115 @@ class PointJacobi(object): generator=True :param bool generator: the point provided is a curve generator, as such, it will be commonly used with scalar multiplication. This will - cause to precompute multiplication table for it + cause to precompute multiplication table generation for it """ + super(PointJacobi, self).__init__() self.__curve = curve - # since it's generally better (faster) to use scaled points vs unscaled - # ones, use writer-biased RWLock for locking: - self._update_lock = RWLock() - if GMPY: - self.__x = mpz(x) - self.__y = mpz(y) - self.__z = mpz(z) + if GMPY: # pragma: no branch + self.__coords = (mpz(x), mpz(y), mpz(z)) self.__order = order and mpz(order) - else: - self.__x = x - self.__y = y - self.__z = z + else: # pragma: no branch + self.__coords = (x, y, z) self.__order = order self.__generator = generator self.__precompute = [] - def _maybe_precompute(self): - if self.__generator: - # since we lack promotion of read-locks to write-locks, we do a - # "acquire-read-lock, check, acquire-write-lock plus recheck" cycle - try: - self._update_lock.reader_acquire() - if self.__precompute: - return - finally: - self._update_lock.reader_release() - - try: - self._update_lock.writer_acquire() - if self.__precompute: - return - order = self.__order - assert order - i = 1 - order *= 2 - doubler = PointJacobi( - self.__curve, self.__x, self.__y, self.__z, order - ) - order *= 2 - self.__precompute.append((doubler.x(), doubler.y())) + @classmethod + def from_bytes( + cls, + curve, + data, + validate_encoding=True, + valid_encodings=None, + order=None, + generator=False, + ): + """ + Initialise the object from byte encoding of a point. + + The method does accept and automatically detect the type of point + encoding used. It supports the :term:`raw encoding`, + :term:`uncompressed`, :term:`compressed`, and :term:`hybrid` encodings. + + :param data: single point encoding of the public key + :type data: :term:`bytes-like object` + :param curve: the curve on which the public key is expected to lay + :type curve: ~ecdsa.ellipticcurve.CurveFp + :param validate_encoding: whether to verify that the encoding of the + point is self-consistent, defaults to True, has effect only + on ``hybrid`` encoding + :type validate_encoding: bool + :param valid_encodings: list of acceptable point encoding formats, + supported ones are: :term:`uncompressed`, :term:`compressed`, + :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` + name). All formats by default (specified with ``None``). + :type valid_encodings: :term:`set-like object` + :param int order: the point order, must be non zero when using + generator=True + :param bool generator: the point provided is a curve generator, as + such, it will be commonly used with scalar multiplication. This + will cause to precompute multiplication table generation for it + + :raises `~ecdsa.errors.MalformedPointError`: if the public point does + not lay on the curve or the encoding is invalid - while i < order: - i *= 2 - doubler = doubler.double().scale() - self.__precompute.append((doubler.x(), doubler.y())) + :return: Point on curve + :rtype: PointJacobi + """ + coord_x, coord_y = super(PointJacobi, cls).from_bytes( + curve, data, validate_encoding, valid_encodings + ) + return PointJacobi(curve, coord_x, coord_y, 1, order, generator) - finally: - self._update_lock.writer_release() + def _maybe_precompute(self): + if not self.__generator or self.__precompute: + return + + # since this code will execute just once, and it's fully deterministic, + # depend on atomicity of the last assignment to switch from empty + # self.__precompute to filled one and just ignore the unlikely + # situation when two threads execute it at the same time (as it won't + # lead to inconsistent __precompute) + order = self.__order + assert order + precompute = [] + i = 1 + order *= 2 + coord_x, coord_y, coord_z = self.__coords + doubler = PointJacobi(self.__curve, coord_x, coord_y, coord_z, order) + order *= 2 + precompute.append((doubler.x(), doubler.y())) + + while i < order: + i *= 2 + doubler = doubler.double().scale() + precompute.append((doubler.x(), doubler.y())) + + self.__precompute = precompute def __getstate__(self): - try: - self._update_lock.reader_acquire() - state = self.__dict__.copy() - finally: - self._update_lock.reader_release() - del state["_update_lock"] + # while this code can execute at the same time as _maybe_precompute() + # is updating the __precompute or scale() is updating the __coords, + # there is no requirement for consistency between __coords and + # __precompute + state = self.__dict__.copy() return state def __setstate__(self, state): self.__dict__.update(state) - self._update_lock = RWLock() def __eq__(self, other): - """Compare two points with each-other.""" - try: - self._update_lock.reader_acquire() - if other is INFINITY: - return not self.__y or not self.__z - x1, y1, z1 = self.__x, self.__y, self.__z - finally: - self._update_lock.reader_release() + """Compare for equality two points with each-other. + + Note: only points that lay on the same curve can be equal. + """ + x1, y1, z1 = self.__coords + if other is INFINITY: + return not y1 or not z1 if isinstance(other, Point): x2, y2, z2 = other.x(), other.y(), 1 elif isinstance(other, PointJacobi): - try: - other._update_lock.reader_acquire() - x2, y2, z2 = other.__x, other.__y, other.__z - finally: - other._update_lock.reader_release() + x2, y2, z2 = other.__coords else: return NotImplemented if self.__curve != other.curve(): @@ -256,6 +642,10 @@ class PointJacobi(object): y1 * zz2 * z2 - y2 * zz1 * z1 ) % p == 0 + def __ne__(self, other): + """Compare for inequality two points with each-other.""" + return not self == other + def order(self): """Return the order of the point. @@ -276,17 +666,12 @@ class PointJacobi(object): call x() and y() on the returned instance. Or call `scale()` and then x() and y() on the returned instance. """ - try: - self._update_lock.reader_acquire() - if self.__z == 1: - return self.__x - x = self.__x - z = self.__z - finally: - self._update_lock.reader_release() + x, _, z = self.__coords + if z == 1: + return x p = self.__curve.p() z = numbertheory.inverse_mod(z, p) - return x * z ** 2 % p + return x * z**2 % p def y(self): """ @@ -297,17 +682,12 @@ class PointJacobi(object): call x() and y() on the returned instance. Or call `scale()` and then x() and y() on the returned instance. """ - try: - self._update_lock.reader_acquire() - if self.__z == 1: - return self.__y - y = self.__y - z = self.__z - finally: - self._update_lock.reader_release() + _, y, z = self.__coords + if z == 1: + return y p = self.__curve.p() z = numbertheory.inverse_mod(z, p) - return y * z ** 3 % p + return y * z**3 % p def scale(self): """ @@ -315,37 +695,28 @@ class PointJacobi(object): Modifies point in place, returns self. """ - try: - self._update_lock.reader_acquire() - if self.__z == 1: - return self - finally: - self._update_lock.reader_release() + x, y, z = self.__coords + if z == 1: + return self - try: - self._update_lock.writer_acquire() - # scaling already scaled point is safe (as inverse of 1 is 1) and - # quick so we don't need to optimise for the unlikely event when - # two threads hit the lock at the same time - p = self.__curve.p() - z_inv = numbertheory.inverse_mod(self.__z, p) - zz_inv = z_inv * z_inv % p - self.__x = self.__x * zz_inv % p - self.__y = self.__y * zz_inv * z_inv % p - # we are setting the z last so that the check above will return - # true only after all values were already updated - self.__z = 1 - finally: - self._update_lock.writer_release() + # scaling is deterministic, so even if two threads execute the below + # code at the same time, they will set __coords to the same value + p = self.__curve.p() + z_inv = numbertheory.inverse_mod(z, p) + zz_inv = z_inv * z_inv % p + x = x * zz_inv % p + y = y * zz_inv * z_inv % p + self.__coords = (x, y, 1) return self def to_affine(self): """Return point in affine form.""" - if not self.__y or not self.__z: + _, y, z = self.__coords + if not y or not z: return INFINITY self.scale() - # after point is scaled, it's immutable, so no need to perform locking - return Point(self.__curve, self.__x, self.__y, self.__order) + x, y, z = self.__coords + return Point(self.__curve, x, y, self.__order) @staticmethod def from_affine(point, generator=False): @@ -359,7 +730,8 @@ class PointJacobi(object): point.curve(), point.x(), point.y(), 1, point.order(), generator ) - # plese note that all the methods that use the equations from hyperelliptic + # please note that all the methods that use the equations from + # hyperelliptic # are formatted in a way to maximise performance. # Things that make code faster: multiplying instead of taking to the power # (`xx = x * x; xxxx = xx * xx % p` is faster than `xxxx = x**4 % p` and @@ -389,7 +761,7 @@ class PointJacobi(object): """Add a point to itself, arbitrary z.""" if Z1 == 1: return self._double_with_z_1(X1, Y1, p, a) - if not Z1: + if not Y1 or not Z1: return 0, 0, 1 # after: # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-2007-bl @@ -409,17 +781,13 @@ class PointJacobi(object): def double(self): """Add a point to itself.""" - if not self.__y: + X1, Y1, Z1 = self.__coords + + if not Y1: return INFINITY p, a = self.__curve.p(), self.__curve.a() - try: - self._update_lock.reader_acquire() - X1, Y1, Z1 = self.__x, self.__y, self.__z - finally: - self._update_lock.reader_release() - X3, Y3, Z3 = self._double(X1, Y1, Z1, p, a) if not Y3 or not Z3: @@ -438,7 +806,7 @@ class PointJacobi(object): if not H and not r: return self._double_with_z_1(X1, Y1, p, self.__curve.a()) V = X1 * I - X3 = (r ** 2 - J - 2 * V) % p + X3 = (r**2 - J - 2 * V) % p Y3 = (r * (V - X3) - 2 * Y1 * J) % p Z3 = 2 * H % p return X3, Y3, Z3 @@ -532,16 +900,9 @@ class PointJacobi(object): raise ValueError("The other point is on different curve") p = self.__curve.p() - try: - self._update_lock.reader_acquire() - X1, Y1, Z1 = self.__x, self.__y, self.__z - finally: - self._update_lock.reader_release() - try: - other._update_lock.reader_acquire() - X2, Y2, Z2 = other.__x, other.__y, other.__z - finally: - other._update_lock.reader_release() + X1, Y1, Z1 = self.__coords + X2, Y2, Z2 = other.__coords + X3, Y3, Z3 = self._add(X1, Y1, Z1, X2, Y2, Z2, p) if not Y3 or not Z3: @@ -571,25 +932,9 @@ class PointJacobi(object): return INFINITY return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) - @staticmethod - def _naf(mult): - """Calculate non-adjacent form of number.""" - ret = [] - while mult: - if mult % 2: - nd = mult % 4 - if nd >= 2: - nd = nd - 4 - ret += [nd] - mult -= nd - else: - ret += [0] - mult //= 2 - return ret - def __mul__(self, other): """Multiply point by an integer.""" - if not self.__y or not other: + if not self.__coords[1] or not other: return INFINITY if other == 1: return self @@ -601,8 +946,7 @@ class PointJacobi(object): return self._mul_precompute(other) self = self.scale() - # once scaled, point is immutable, not need to lock - X2, Y2 = self.__x, self.__y + X2, Y2, _ = self.__coords X3, Y3, Z3 = 0, 0, 1 p, a = self.__curve.p(), self.__curve.a() _double = self._double @@ -621,29 +965,20 @@ class PointJacobi(object): return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) - @staticmethod - def _leftmost_bit(x): - """Return integer with the same magnitude as x but only one bit set""" - assert x > 0 - result = 1 - while result <= x: - result = 2 * result - return result // 2 - def mul_add(self, self_mul, other, other_mul): """ Do two multiplications at the same time, add results. calculates self*self_mul + other*other_mul """ - if other is INFINITY or other_mul == 0: + if other == INFINITY or other_mul == 0: return self * self_mul if self_mul == 0: return other * other_mul if not isinstance(other, PointJacobi): other = PointJacobi.from_affine(other) # when the points have precomputed answers, then multiplying them alone - # is faster (as it uses NAF) + # is faster (as it uses NAF and no point doublings) self._maybe_precompute() other._maybe_precompute() if self.__precompute and other.__precompute: @@ -653,32 +988,75 @@ class PointJacobi(object): self_mul = self_mul % self.__order other_mul = other_mul % self.__order - i = self._leftmost_bit(max(self_mul, other_mul)) * 2 + # (X3, Y3, Z3) is the accumulator X3, Y3, Z3 = 0, 0, 1 p, a = self.__curve.p(), self.__curve.a() - self = self.scale() - # after scaling, point is immutable, no need for locking - X1, Y1 = self.__x, self.__y - other = other.scale() - X2, Y2 = other.__x, other.__y - both = self + other - if both is INFINITY: - X4, Y4 = 0, 0 - else: - both.scale() - X4, Y4 = both.__x, both.__y + + # as we have 6 unique points to work with, we can't scale all of them, + # but do scale the ones that are used most often + self.scale() + X1, Y1, Z1 = self.__coords + other.scale() + X2, Y2, Z2 = other.__coords + _double = self._double _add = self._add - while i > 1: + + # with NAF we have 3 options: no add, subtract, add + # so with 2 points, we have 9 combinations: + # 0, -A, +A, -B, -A-B, +A-B, +B, -A+B, +A+B + # so we need 4 combined points: + mAmB_X, mAmB_Y, mAmB_Z = _add(X1, -Y1, Z1, X2, -Y2, Z2, p) + pAmB_X, pAmB_Y, pAmB_Z = _add(X1, Y1, Z1, X2, -Y2, Z2, p) + mApB_X, mApB_Y, mApB_Z = _add(X1, -Y1, Z1, X2, Y2, Z2, p) + pApB_X, pApB_Y, pApB_Z = _add(X1, Y1, Z1, X2, Y2, Z2, p) + # when the self and other sum to infinity, we need to add them + # one by one to get correct result but as that's very unlikely to + # happen in regular operation, we don't need to optimise this case + if not pApB_Y or not pApB_Z: + return self * self_mul + other * other_mul + + # gmp object creation has cumulatively higher overhead than the + # speedup we get from calculating the NAF using gmp so ensure use + # of int() + self_naf = list(reversed(self._naf(int(self_mul)))) + other_naf = list(reversed(self._naf(int(other_mul)))) + # ensure that the lists are the same length (zip() will truncate + # longer one otherwise) + if len(self_naf) < len(other_naf): + self_naf = [0] * (len(other_naf) - len(self_naf)) + self_naf + elif len(self_naf) > len(other_naf): + other_naf = [0] * (len(self_naf) - len(other_naf)) + other_naf + + for A, B in zip(self_naf, other_naf): X3, Y3, Z3 = _double(X3, Y3, Z3, p, a) - i = i // 2 - if self_mul & i and other_mul & i: - X3, Y3, Z3 = _add(X3, Y3, Z3, X4, Y4, 1, p) - elif self_mul & i: - X3, Y3, Z3 = _add(X3, Y3, Z3, X1, Y1, 1, p) - elif other_mul & i: - X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, 1, p) + # conditions ordered from most to least likely + if A == 0: + if B == 0: + pass + elif B < 0: + X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, Z2, p) + else: + assert B > 0 + X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, Z2, p) + elif A < 0: + if B == 0: + X3, Y3, Z3 = _add(X3, Y3, Z3, X1, -Y1, Z1, p) + elif B < 0: + X3, Y3, Z3 = _add(X3, Y3, Z3, mAmB_X, mAmB_Y, mAmB_Z, p) + else: + assert B > 0 + X3, Y3, Z3 = _add(X3, Y3, Z3, mApB_X, mApB_Y, mApB_Z, p) + else: + assert A > 0 + if B == 0: + X3, Y3, Z3 = _add(X3, Y3, Z3, X1, Y1, Z1, p) + elif B < 0: + X3, Y3, Z3 = _add(X3, Y3, Z3, pAmB_X, pAmB_Y, pAmB_Z, p) + else: + assert B > 0 + X3, Y3, Z3 = _add(X3, Y3, Z3, pApB_X, pApB_Y, pApB_Z, p) if not Y3 or not Z3: return INFINITY @@ -687,21 +1065,17 @@ class PointJacobi(object): def __neg__(self): """Return negated point.""" - try: - self._update_lock.reader_acquire() - return PointJacobi( - self.__curve, self.__x, -self.__y, self.__z, self.__order - ) - finally: - self._update_lock.reader_release() + x, y, z = self.__coords + return PointJacobi(self.__curve, x, -y, z, self.__order) -class Point(object): - """A point on an elliptic curve. Altering x and y is forbidding, - but they can be read by the x() and y() methods.""" +class Point(AbstractPoint): + """A point on a short Weierstrass elliptic curve. Altering x and y is + forbidden, but they can be read by the x() and y() methods.""" def __init__(self, curve, x, y, order=None): """curve, x, y, order; order (optional) is the order of this point.""" + super(Point, self).__init__() self.__curve = curve if GMPY: self.__x = x and mpz(x) @@ -720,8 +1094,54 @@ class Point(object): if curve and curve.cofactor() != 1 and order: assert self * order == INFINITY + @classmethod + def from_bytes( + cls, + curve, + data, + validate_encoding=True, + valid_encodings=None, + order=None, + ): + """ + Initialise the object from byte encoding of a point. + + The method does accept and automatically detect the type of point + encoding used. It supports the :term:`raw encoding`, + :term:`uncompressed`, :term:`compressed`, and :term:`hybrid` encodings. + + :param data: single point encoding of the public key + :type data: :term:`bytes-like object` + :param curve: the curve on which the public key is expected to lay + :type curve: ~ecdsa.ellipticcurve.CurveFp + :param validate_encoding: whether to verify that the encoding of the + point is self-consistent, defaults to True, has effect only + on ``hybrid`` encoding + :type validate_encoding: bool + :param valid_encodings: list of acceptable point encoding formats, + supported ones are: :term:`uncompressed`, :term:`compressed`, + :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` + name). All formats by default (specified with ``None``). + :type valid_encodings: :term:`set-like object` + :param int order: the point order, must be non zero when using + generator=True + + :raises `~ecdsa.errors.MalformedPointError`: if the public point does + not lay on the curve or the encoding is invalid + + :return: Point on curve + :rtype: Point + """ + coord_x, coord_y = super(Point, cls).from_bytes( + curve, data, validate_encoding, valid_encodings + ) + return Point(curve, coord_x, coord_y, order) + def __eq__(self, other): - """Return True if the points are identical, False otherwise.""" + """Return True if the points are identical, False otherwise. + + Note: only points that lay on the same curve can be equal. + """ if isinstance(other, Point): return ( self.__curve == other.__curve @@ -730,6 +1150,10 @@ class Point(object): ) return NotImplemented + def __ne__(self, other): + """Returns False if points are identical, True otherwise.""" + return not self == other + def __neg__(self): return Point(self.__curve, self.__x, self.__curve.p() - self.__y) @@ -843,5 +1267,318 @@ class Point(object): return self.__order +class PointEdwards(AbstractPoint): + """Point on Twisted Edwards curve. + + Internally represents the coordinates on the curve using four parameters, + X, Y, Z, T. They correspond to affine parameters 'x' and 'y' like so: + + x = X / Z + y = Y / Z + x*y = T / Z + """ + + def __init__(self, curve, x, y, z, t, order=None, generator=False): + """ + Initialise a point that uses the extended coordinates internally. + """ + super(PointEdwards, self).__init__() + self.__curve = curve + if GMPY: # pragma: no branch + self.__coords = (mpz(x), mpz(y), mpz(z), mpz(t)) + self.__order = order and mpz(order) + else: # pragma: no branch + self.__coords = (x, y, z, t) + self.__order = order + self.__generator = generator + self.__precompute = [] + + @classmethod + def from_bytes( + cls, + curve, + data, + validate_encoding=None, + valid_encodings=None, + order=None, + generator=False, + ): + """ + Initialise the object from byte encoding of a point. + + `validate_encoding` and `valid_encodings` are provided for + compatibility with Weierstrass curves, they are ignored for Edwards + points. + + :param data: single point encoding of the public key + :type data: :term:`bytes-like object` + :param curve: the curve on which the public key is expected to lay + :type curve: ecdsa.ellipticcurve.CurveEdTw + :param None validate_encoding: Ignored, encoding is always validated + :param None valid_encodings: Ignored, there is just one encoding + supported + :param int order: the point order, must be non zero when using + generator=True + :param bool generator: Flag to mark the point as a curve generator, + this will cause the library to pre-compute some values to + make repeated usages of the point much faster + + :raises `~ecdsa.errors.MalformedPointError`: if the public point does + not lay on the curve or the encoding is invalid + + :return: Initialised point on an Edwards curve + :rtype: PointEdwards + """ + coord_x, coord_y = super(PointEdwards, cls).from_bytes( + curve, data, validate_encoding, valid_encodings + ) + return PointEdwards( + curve, coord_x, coord_y, 1, coord_x * coord_y, order, generator + ) + + def _maybe_precompute(self): + if not self.__generator or self.__precompute: + return self.__precompute + + # since this code will execute just once, and it's fully deterministic, + # depend on atomicity of the last assignment to switch from empty + # self.__precompute to filled one and just ignore the unlikely + # situation when two threads execute it at the same time (as it won't + # lead to inconsistent __precompute) + order = self.__order + assert order + precompute = [] + i = 1 + order *= 2 + coord_x, coord_y, coord_z, coord_t = self.__coords + prime = self.__curve.p() + + doubler = PointEdwards( + self.__curve, coord_x, coord_y, coord_z, coord_t, order + ) + # for "protection" against Minerva we need 1 or 2 more bits depending + # on order bit size, but it's easier to just calculate one + # point more always + order *= 4 + + while i < order: + doubler = doubler.scale() + coord_x, coord_y = doubler.x(), doubler.y() + coord_t = coord_x * coord_y % prime + precompute.append((coord_x, coord_y, coord_t)) + + i *= 2 + doubler = doubler.double() + + self.__precompute = precompute + return self.__precompute + + def x(self): + """Return affine x coordinate.""" + X1, _, Z1, _ = self.__coords + if Z1 == 1: + return X1 + p = self.__curve.p() + z_inv = numbertheory.inverse_mod(Z1, p) + return X1 * z_inv % p + + def y(self): + """Return affine y coordinate.""" + _, Y1, Z1, _ = self.__coords + if Z1 == 1: + return Y1 + p = self.__curve.p() + z_inv = numbertheory.inverse_mod(Z1, p) + return Y1 * z_inv % p + + def curve(self): + """Return the curve of the point.""" + return self.__curve + + def order(self): + return self.__order + + def scale(self): + """ + Return point scaled so that z == 1. + + Modifies point in place, returns self. + """ + X1, Y1, Z1, _ = self.__coords + if Z1 == 1: + return self + + p = self.__curve.p() + z_inv = numbertheory.inverse_mod(Z1, p) + x = X1 * z_inv % p + y = Y1 * z_inv % p + t = x * y % p + self.__coords = (x, y, 1, t) + return self + + def __eq__(self, other): + """Compare for equality two points with each-other. + + Note: only points on the same curve can be equal. + """ + x1, y1, z1, t1 = self.__coords + if other is INFINITY: + return not x1 or not t1 + if isinstance(other, PointEdwards): + x2, y2, z2, t2 = other.__coords + else: + return NotImplemented + if self.__curve != other.curve(): + return False + p = self.__curve.p() + + # cross multiply to eliminate divisions + xn1 = x1 * z2 % p + xn2 = x2 * z1 % p + yn1 = y1 * z2 % p + yn2 = y2 * z1 % p + return xn1 == xn2 and yn1 == yn2 + + def __ne__(self, other): + """Compare for inequality two points with each-other.""" + return not self == other + + def _add(self, X1, Y1, Z1, T1, X2, Y2, Z2, T2, p, a): + """add two points, assume sane parameters.""" + # after add-2008-hwcd-2 + # from https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html + # NOTE: there are more efficient formulas for Z1 or Z2 == 1 + A = X1 * X2 % p + B = Y1 * Y2 % p + C = Z1 * T2 % p + D = T1 * Z2 % p + E = D + C + F = ((X1 - Y1) * (X2 + Y2) + B - A) % p + G = B + a * A + H = D - C + if not H: + return self._double(X1, Y1, Z1, T1, p, a) + X3 = E * F % p + Y3 = G * H % p + T3 = E * H % p + Z3 = F * G % p + + return X3, Y3, Z3, T3 + + def __add__(self, other): + """Add point to another.""" + if other == INFINITY: + return self + if ( + not isinstance(other, PointEdwards) + or self.__curve != other.__curve + ): + raise ValueError("The other point is on a different curve.") + + p, a = self.__curve.p(), self.__curve.a() + X1, Y1, Z1, T1 = self.__coords + X2, Y2, Z2, T2 = other.__coords + + X3, Y3, Z3, T3 = self._add(X1, Y1, Z1, T1, X2, Y2, Z2, T2, p, a) + + if not X3 or not T3: + return INFINITY + return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order) + + def __radd__(self, other): + """Add other to self.""" + return self + other + + def _double(self, X1, Y1, Z1, T1, p, a): + """Double the point, assume sane parameters.""" + # after "dbl-2008-hwcd" + # from https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html + # NOTE: there are more efficient formulas for Z1 == 1 + A = X1 * X1 % p + B = Y1 * Y1 % p + C = 2 * Z1 * Z1 % p + D = a * A % p + E = ((X1 + Y1) * (X1 + Y1) - A - B) % p + G = D + B + F = G - C + H = D - B + X3 = E * F % p + Y3 = G * H % p + T3 = E * H % p + Z3 = F * G % p + + return X3, Y3, Z3, T3 + + def double(self): + """Return point added to itself.""" + X1, Y1, Z1, T1 = self.__coords + + if not X1 or not T1: + return INFINITY + + p, a = self.__curve.p(), self.__curve.a() + + X3, Y3, Z3, T3 = self._double(X1, Y1, Z1, T1, p, a) + + if not X3 or not T3: + return INFINITY + return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order) + + def __rmul__(self, other): + """Multiply point by an integer.""" + return self * other + + def _mul_precompute(self, other): + """Multiply point by integer with precomputation table.""" + X3, Y3, Z3, T3, p, a = 0, 1, 1, 0, self.__curve.p(), self.__curve.a() + _add = self._add + for X2, Y2, T2 in self.__precompute: + rem = other % 4 + if rem == 0 or rem == 2: + other //= 2 + elif rem == 3: + other = (other + 1) // 2 + X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, -X2, Y2, 1, -T2, p, a) + else: + assert rem == 1 + other = (other - 1) // 2 + X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, X2, Y2, 1, T2, p, a) + + if not X3 or not T3: + return INFINITY + + return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order) + + def __mul__(self, other): + """Multiply point by an integer.""" + X2, Y2, Z2, T2 = self.__coords + if not X2 or not T2 or not other: + return INFINITY + if other == 1: + return self + if self.__order: + # order*2 as a "protection" for Minerva + other = other % (self.__order * 2) + if self._maybe_precompute(): + return self._mul_precompute(other) + + X3, Y3, Z3, T3 = 0, 1, 1, 0 # INFINITY in extended coordinates + p, a = self.__curve.p(), self.__curve.a() + _double = self._double + _add = self._add + + for i in reversed(self._naf(other)): + X3, Y3, Z3, T3 = _double(X3, Y3, Z3, T3, p, a) + if i < 0: + X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, -X2, Y2, Z2, -T2, p, a) + elif i > 0: + X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, X2, Y2, Z2, T2, p, a) + + if not X3 or not T3: + return INFINITY + + return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order) + + # This one point is the Point At Infinity for all purposes: INFINITY = Point(None, None, None) diff --git a/frozen_deps/ecdsa/errors.py b/frozen_deps/ecdsa/errors.py new file mode 100644 index 0000000..0184c05 --- /dev/null +++ b/frozen_deps/ecdsa/errors.py @@ -0,0 +1,4 @@ +class MalformedPointError(AssertionError): + """Raised in case the encoding of private or public key is malformed.""" + + pass diff --git a/frozen_deps/ecdsa/keys.py b/frozen_deps/ecdsa/keys.py index a6fc13f..2b7d316 100644 --- a/frozen_deps/ecdsa/keys.py +++ b/frozen_deps/ecdsa/keys.py @@ -1,80 +1,16 @@ """ Primary classes for performing signing and verification operations. - -.. glossary:: - - raw encoding - Conversion of public, private keys and signatures (which in - mathematical sense are integers or pairs of integers) to strings of - bytes that does not use any special tags or encoding rules. - For any given curve, all keys of the same type or signatures will be - encoded to byte strings of the same length. In more formal sense, - the integers are encoded as big-endian, constant length byte strings, - where the string length is determined by the curve order (e.g. - for NIST256p the order is 256 bits long, so the private key will be 32 - bytes long while public key will be 64 bytes long). The encoding of a - single integer is zero-padded on the left if the numerical value is - low. In case of public keys and signatures, which are comprised of two - integers, the integers are simply concatenated. - - uncompressed - The most common formatting specified in PKIX standards. Specified in - X9.62 and SEC1 standards. The only difference between it and - :term:`raw encoding` is the prepending of a 0x04 byte. Thus an - uncompressed NIST256p public key encoding will be 65 bytes long. - - compressed - The public point representation that uses half of bytes of the - :term:`uncompressed` encoding (rounded up). It uses the first byte of - the encoding to specify the sign of the y coordinate and encodes the - x coordinate as-is. The first byte of the encoding is equal to - 0x02 or 0x03. Compressed encoding of NIST256p public key will be 33 - bytes long. - - hybrid - A combination of :term:`uncompressed` and :term:`compressed` encodings. - Both x and y coordinates are stored just as in :term:`compressed` - encoding, but the first byte reflects the sign of the y coordinate. The - first byte of the encoding will be equal to 0x06 or 0x7. Hybrid - encoding of NIST256p public key will be 65 bytes long. - - PEM - The acronym stands for Privacy Enhanced Email, but currently it is used - primarily as the way to encode :term:`DER` objects into text that can - be either easily copy-pasted or transferred over email. - It uses headers like ``-----BEGIN <type of contents>-----`` and footers - like ``-----END <type of contents>-----`` to separate multiple - types of objects in the same file or the object from the surrounding - comments. The actual object stored is base64 encoded. - - DER - Distinguished Encoding Rules, the way to encode :term:`ASN.1` objects - deterministically and uniquely into byte strings. - - ASN.1 - Abstract Syntax Notation 1 is a standard description language for - specifying serialisation and deserialisation of data structures in a - portable and cross-platform way. - - bytes-like object - All the types that implement the buffer protocol. That includes - ``str`` (only on python2), ``bytes``, ``bytesarray``, ``array.array` - and ``memoryview`` of those objects. - Please note that ``array.array` serialisation (converting it to byte - string) is endianess dependant! Signature computed over ``array.array`` - of integers on a big-endian system will not be verified on a - little-endian system and vice-versa. """ import binascii from hashlib import sha1 +import os from six import PY2, b -from . import ecdsa +from . import ecdsa, eddsa from . import der from . import rfc6979 from . import ellipticcurve -from .curves import NIST192p, find_curve -from .numbertheory import square_root_mod_prime, SquareRootError +from .curves import NIST192p, Curve, Ed25519, Ed448 from .ecdsa import RSZeroError from .util import string_to_number, number_to_string, randrange from .util import sigencode_string, sigdecode_string, bit_length @@ -86,6 +22,8 @@ from .util import ( MalformedSignature, ) from ._compat import normalise_bytes +from .errors import MalformedPointError +from .ellipticcurve import PointJacobi, CurveEdTw __all__ = [ @@ -118,23 +56,49 @@ class BadDigestError(Exception): pass -class MalformedPointError(AssertionError): - """Raised in case the encoding of private or public key is malformed.""" +def _truncate_and_convert_digest(digest, curve, allow_truncate): + """Truncates and converts digest to an integer.""" + if not allow_truncate: + if len(digest) > curve.baselen: + raise BadDigestError( + "this curve ({0}) is too short " + "for the length of your digest ({1})".format( + curve.name, 8 * len(digest) + ) + ) + else: + digest = digest[: curve.baselen] + number = string_to_number(digest) + if allow_truncate: + max_length = bit_length(curve.order) + # we don't use bit_length(number) as that truncates leading zeros + length = len(digest) * 8 + + # See NIST FIPS 186-4: + # + # When the length of the output of the hash function is greater + # than N (i.e., the bit length of q), then the leftmost N bits of + # the hash function output block shall be used in any calculation + # using the hash function output during the generation or + # verification of a digital signature. + # + # as such, we need to shift-out the low-order bits: + number >>= max(0, length - max_length) - pass + return number class VerifyingKey(object): """ Class for handling keys that can verify signatures (public keys). - :ivar ecdsa.curves.Curve curve: The Curve over which all the cryptographic - operations will take place + :ivar `~ecdsa.curves.Curve` ~.curve: The Curve over which all the + cryptographic operations will take place :ivar default_hashfunc: the function that will be used for hashing the data. Should implement the same API as hashlib.sha1 :vartype default_hashfunc: callable :ivar pubkey: the actual public key - :vartype pubkey: ecdsa.ecdsa.Public_key + :vartype pubkey: ~ecdsa.ecdsa.Public_key """ def __init__(self, _error__please_use_generate=None): @@ -149,8 +113,12 @@ class VerifyingKey(object): def __repr__(self): pub_key = self.to_string("compressed") + if self.default_hashfunc: + hash_name = self.default_hashfunc().name + else: + hash_name = "None" return "VerifyingKey.from_string({0!r}, {1!r}, {2})".format( - pub_key, self.curve, self.default_hashfunc().name + pub_key, self.curve, hash_name ) def __eq__(self, other): @@ -159,6 +127,10 @@ class VerifyingKey(object): return self.curve == other.curve and self.pubkey == other.pubkey return NotImplemented + def __ne__(self, other): + """Return False if the points are identical, True otherwise.""" + return not self == other + @classmethod def from_public_point( cls, point, curve=NIST192p, hashfunc=sha1, validate_point=True @@ -169,25 +141,27 @@ class VerifyingKey(object): This is a low-level method, generally you will not want to use it. :param point: The point to wrap around, the actual public key - :type point: ecdsa.ellipticcurve.Point + :type point: ~ecdsa.ellipticcurve.AbstractPoint :param curve: The curve on which the point needs to reside, defaults to NIST192p - :type curve: ecdsa.curves.Curve + :type curve: ~ecdsa.curves.Curve :param hashfunc: The default hash function that will be used for verification, needs to implement the same interface - as hashlib.sha1 + as :py:class:`hashlib.sha1` :type hashfunc: callable - :type bool validate_point: whether to check if the point lies on curve + :type bool validate_point: whether to check if the point lays on curve should always be used if the public point is not a result of our own calculation - :raises MalformedPointError: if the public point does not lie on the + :raises MalformedPointError: if the public point does not lay on the curve :return: Initialised VerifyingKey object :rtype: VerifyingKey """ self = cls(_error__please_use_generate=True) + if isinstance(curve.curve, CurveEdTw): + raise ValueError("Method incompatible with Edwards curves") if not isinstance(point, ellipticcurve.PointJacobi): point = ellipticcurve.PointJacobi.from_affine(point) self.curve = curve @@ -197,7 +171,7 @@ class VerifyingKey(object): curve.generator, point, validate_point ) except ecdsa.InvalidPointError: - raise MalformedPointError("Point does not lie on the curve") + raise MalformedPointError("Point does not lay on the curve") self.pubkey.order = curve.order return self @@ -220,90 +194,45 @@ class VerifyingKey(object): (if set to False) or if it should be delayed to the time of first use (when set to True) """ - self.pubkey.point = ellipticcurve.PointJacobi.from_affine( - self.pubkey.point, True - ) + if isinstance(self.curve.curve, CurveEdTw): + pt = self.pubkey.point + self.pubkey.point = ellipticcurve.PointEdwards( + pt.curve(), + pt.x(), + pt.y(), + 1, + pt.x() * pt.y(), + self.curve.order, + generator=True, + ) + else: + self.pubkey.point = ellipticcurve.PointJacobi.from_affine( + self.pubkey.point, True + ) # as precomputation in now delayed to the time of first use of the # point and we were asked specifically to precompute now, make # sure the precomputation is performed now to preserve the behaviour if not lazy: self.pubkey.point * 2 - @staticmethod - def _from_raw_encoding(string, curve): - """ - Decode public point from :term:`raw encoding`. - - :term:`raw encoding` is the same as the :term:`uncompressed` encoding, - but without the 0x04 byte at the beginning. - """ - order = curve.order - # real assert, from_string() should not call us with different length - assert len(string) == curve.verifying_key_length - xs = string[: curve.baselen] - ys = string[curve.baselen :] - if len(xs) != curve.baselen: - raise MalformedPointError("Unexpected length of encoded x") - if len(ys) != curve.baselen: - raise MalformedPointError("Unexpected length of encoded y") - x = string_to_number(xs) - y = string_to_number(ys) - - return ellipticcurve.PointJacobi(curve.curve, x, y, 1, order) - - @staticmethod - def _from_compressed(string, curve): - """Decode public point from compressed encoding.""" - if string[:1] not in (b("\x02"), b("\x03")): - raise MalformedPointError("Malformed compressed point encoding") - - is_even = string[:1] == b("\x02") - x = string_to_number(string[1:]) - order = curve.order - p = curve.curve.p() - alpha = (pow(x, 3, p) + (curve.curve.a() * x) + curve.curve.b()) % p - try: - beta = square_root_mod_prime(alpha, p) - except SquareRootError as e: - raise MalformedPointError( - "Encoding does not correspond to a point on curve", e - ) - if is_even == bool(beta & 1): - y = p - beta - else: - y = beta - return ellipticcurve.PointJacobi(curve.curve, x, y, 1, order) - - @classmethod - def _from_hybrid(cls, string, curve, validate_point): - """Decode public point from hybrid encoding.""" - # real assert, from_string() should not call us with different types - assert string[:1] in (b("\x06"), b("\x07")) - - # primarily use the uncompressed as it's easiest to handle - point = cls._from_raw_encoding(string[1:], curve) - - # but validate if it's self-consistent if we're asked to do that - if validate_point and ( - point.y() & 1 - and string[:1] != b("\x07") - or (not point.y() & 1) - and string[:1] != b("\x06") - ): - raise MalformedPointError("Inconsistent hybrid point encoding") - - return point - @classmethod def from_string( - cls, string, curve=NIST192p, hashfunc=sha1, validate_point=True + cls, + string, + curve=NIST192p, + hashfunc=sha1, + validate_point=True, + valid_encodings=None, ): """ Initialise the object from byte encoding of public key. The method does accept and automatically detect the type of point encoding used. It supports the :term:`raw encoding`, - :term:`uncompressed`, :term:`compressed` and :term:`hybrid` encodings. + :term:`uncompressed`, :term:`compressed`, and :term:`hybrid` encodings. + It also works with the native encoding of Ed25519 and Ed448 public + keys (technically those are compressed, but encoded differently than + in other signature systems). Note, while the method is named "from_string" it's a misnomer from Python 2 days when there were no binary strings. In Python 3 the @@ -311,46 +240,54 @@ class VerifyingKey(object): :param string: single point encoding of the public key :type string: :term:`bytes-like object` - :param curve: the curve on which the public key is expected to lie - :type curve: ecdsa.curves.Curve + :param curve: the curve on which the public key is expected to lay + :type curve: ~ecdsa.curves.Curve :param hashfunc: The default hash function that will be used for - verification, needs to implement the same interface as hashlib.sha1 + verification, needs to implement the same interface as + hashlib.sha1. Ignored for EdDSA. :type hashfunc: callable - :param validate_point: whether to verify that the point lies on the - provided curve or not, defaults to True + :param validate_point: whether to verify that the point lays on the + provided curve or not, defaults to True. Ignored for EdDSA. :type validate_point: bool - - :raises MalformedPointError: if the public point does not lie on the + :param valid_encodings: list of acceptable point encoding formats, + supported ones are: :term:`uncompressed`, :term:`compressed`, + :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` + name). All formats by default (specified with ``None``). + Ignored for EdDSA. + :type valid_encodings: :term:`set-like object` + + :raises MalformedPointError: if the public point does not lay on the curve or the encoding is invalid :return: Initialised VerifyingKey object :rtype: VerifyingKey """ - string = normalise_bytes(string) - sig_len = len(string) - if sig_len == curve.verifying_key_length: - point = cls._from_raw_encoding(string, curve) - elif sig_len == curve.verifying_key_length + 1: - if string[:1] in (b("\x06"), b("\x07")): - point = cls._from_hybrid(string, curve, validate_point) - elif string[:1] == b("\x04"): - point = cls._from_raw_encoding(string[1:], curve) - else: - raise MalformedPointError( - "Invalid X9.62 encoding of the public point" - ) - elif sig_len == curve.baselen + 1: - point = cls._from_compressed(string, curve) - else: - raise MalformedPointError( - "Length of string does not match lengths of " - "any of the supported encodings of {0} " - "curve.".format(curve.name) - ) + if isinstance(curve.curve, CurveEdTw): + self = cls(_error__please_use_generate=True) + self.curve = curve + self.default_hashfunc = None # ignored for EdDSA + try: + self.pubkey = eddsa.PublicKey(curve.generator, string) + except ValueError: + raise MalformedPointError("Malformed point for the curve") + return self + + point = PointJacobi.from_bytes( + curve.curve, + string, + validate_encoding=validate_point, + valid_encodings=valid_encodings, + ) return cls.from_public_point(point, curve, hashfunc, validate_point) @classmethod - def from_pem(cls, string, hashfunc=sha1): + def from_pem( + cls, + string, + hashfunc=sha1, + valid_encodings=None, + valid_curve_encodings=None, + ): """ Initialise from public key stored in :term:`PEM` format. @@ -359,19 +296,40 @@ class VerifyingKey(object): See the :func:`~VerifyingKey.from_der()` method for details of the format supported. - Note: only a single PEM object encoding is supported in provided + Note: only a single PEM object decoding is supported in provided string. :param string: text with PEM-encoded public ECDSA key :type string: str + :param valid_encodings: list of allowed point encodings. + By default :term:`uncompressed`, :term:`compressed`, and + :term:`hybrid`. To read malformed files, include + :term:`raw encoding` with ``raw`` in the list. + :type valid_encodings: :term:`set-like object` + :param valid_curve_encodings: list of allowed encoding formats + for curve parameters. By default (``None``) all are supported: + ``named_curve`` and ``explicit``. + :type valid_curve_encodings: :term:`set-like object` + :return: Initialised VerifyingKey object :rtype: VerifyingKey """ - return cls.from_der(der.unpem(string), hashfunc=hashfunc) + return cls.from_der( + der.unpem(string), + hashfunc=hashfunc, + valid_encodings=valid_encodings, + valid_curve_encodings=valid_curve_encodings, + ) @classmethod - def from_der(cls, string, hashfunc=sha1): + def from_der( + cls, + string, + hashfunc=sha1, + valid_encodings=None, + valid_curve_encodings=None, + ): """ Initialise the key stored in :term:`DER` format. @@ -396,10 +354,21 @@ class VerifyingKey(object): :param string: binary string with the DER encoding of public ECDSA key :type string: bytes-like object + :param valid_encodings: list of allowed point encodings. + By default :term:`uncompressed`, :term:`compressed`, and + :term:`hybrid`. To read malformed files, include + :term:`raw encoding` with ``raw`` in the list. + :type valid_encodings: :term:`set-like object` + :param valid_curve_encodings: list of allowed encoding formats + for curve parameters. By default (``None``) all are supported: + ``named_curve`` and ``explicit``. + :type valid_curve_encodings: :term:`set-like object` :return: Initialised VerifyingKey object :rtype: VerifyingKey """ + if valid_encodings is None: + valid_encodings = set(["uncompressed", "compressed", "hybrid"]) string = normalise_bytes(string) # [[oid_ecPublicKey,oid_curve], point_str_bitstring] s1, empty = der.remove_sequence(string) @@ -410,18 +379,22 @@ class VerifyingKey(object): s2, point_str_bitstring = der.remove_sequence(s1) # s2 = oid_ecPublicKey,oid_curve oid_pk, rest = der.remove_object(s2) - oid_curve, empty = der.remove_object(rest) - if empty != b"": - raise der.UnexpectedDER( - "trailing junk after DER pubkey objects: %s" - % binascii.hexlify(empty) - ) + if oid_pk in (Ed25519.oid, Ed448.oid): + if oid_pk == Ed25519.oid: + curve = Ed25519 + else: + assert oid_pk == Ed448.oid + curve = Ed448 + point_str, empty = der.remove_bitstring(point_str_bitstring, 0) + if empty: + raise der.UnexpectedDER("trailing junk after public key") + return cls.from_string(point_str, curve, None) if not oid_pk == oid_ecPublicKey: raise der.UnexpectedDER( "Unexpected object identifier in DER " "encoding: {0!r}".format(oid_pk) ) - curve = find_curve(oid_curve) + curve = Curve.from_der(rest, valid_curve_encodings) point_str, empty = der.remove_bitstring(point_str_bitstring, 0) if empty != b"": raise der.UnexpectedDER( @@ -431,11 +404,22 @@ class VerifyingKey(object): # raw encoding of point is invalid in DER files if len(point_str) == curve.verifying_key_length: raise der.UnexpectedDER("Malformed encoding of public point") - return cls.from_string(point_str, curve, hashfunc=hashfunc) + return cls.from_string( + point_str, + curve, + hashfunc=hashfunc, + valid_encodings=valid_encodings, + ) @classmethod def from_public_key_recovery( - cls, signature, data, curve, hashfunc=sha1, sigdecode=sigdecode_string + cls, + signature, + data, + curve, + hashfunc=sha1, + sigdecode=sigdecode_string, + allow_truncate=True, ): """ Return keys that can be used as verifiers of the provided signature. @@ -448,7 +432,7 @@ class VerifyingKey(object): :param data: the data to be hashed for signature verification :type data: bytes-like object :param curve: the curve over which the signature was performed - :type curve: ecdsa.curves.Curve + :type curve: ~ecdsa.curves.Curve :param hashfunc: The default hash function that will be used for verification, needs to implement the same interface as hashlib.sha1 :type hashfunc: callable @@ -458,15 +442,25 @@ class VerifyingKey(object): a tuple with two integers, "r" as the first one and "s" as the second one. See :func:`ecdsa.util.sigdecode_string` and :func:`ecdsa.util.sigdecode_der` for examples. + :param bool allow_truncate: if True, the provided hashfunc can generate + values larger than the bit size of the order of the curve, the + extra bits (at the end of the digest) will be truncated. :type sigdecode: callable :return: Initialised VerifyingKey objects :rtype: list of VerifyingKey """ + if isinstance(curve.curve, CurveEdTw): + raise ValueError("Method unsupported for Edwards curves") data = normalise_bytes(data) digest = hashfunc(data).digest() return cls.from_public_key_recovery_with_digest( - signature, digest, curve, hashfunc=hashfunc, sigdecode=sigdecode + signature, + digest, + curve, + hashfunc=hashfunc, + sigdecode=sigdecode, + allow_truncate=allow_truncate, ) @classmethod @@ -477,6 +471,7 @@ class VerifyingKey(object): curve, hashfunc=sha1, sigdecode=sigdecode_string, + allow_truncate=False, ): """ Return keys that can be used as verifiers of the provided signature. @@ -489,7 +484,7 @@ class VerifyingKey(object): :param digest: the hash value of the message signed by the signature :type digest: bytes-like object :param curve: the curve over which the signature was performed - :type curve: ecdsa.curves.Curve + :type curve: ~ecdsa.curves.Curve :param hashfunc: The default hash function that will be used for verification, needs to implement the same interface as hashlib.sha1 :type hashfunc: callable @@ -500,17 +495,24 @@ class VerifyingKey(object): second one. See :func:`ecdsa.util.sigdecode_string` and :func:`ecdsa.util.sigdecode_der` for examples. :type sigdecode: callable - + :param bool allow_truncate: if True, the provided hashfunc can generate + values larger than the bit size of the order of the curve (and + the length of provided `digest`), the extra bits (at the end of the + digest) will be truncated. :return: Initialised VerifyingKey object :rtype: VerifyingKey """ + if isinstance(curve.curve, CurveEdTw): + raise ValueError("Method unsupported for Edwards curves") generator = curve.generator r, s = sigdecode(signature, generator.order()) sig = ecdsa.Signature(r, s) digest = normalise_bytes(digest) - digest_as_number = string_to_number(digest) + digest_as_number = _truncate_and_convert_digest( + digest, curve, allow_truncate + ) pks = sig.recover_public_keys(digest_as_number, generator) # Transforms the ecdsa.Public_key object into a VerifyingKey @@ -519,30 +521,6 @@ class VerifyingKey(object): ] return verifying_keys - def _raw_encode(self): - """Convert the public key to the :term:`raw encoding`.""" - order = self.pubkey.order - x_str = number_to_string(self.pubkey.point.x(), order) - y_str = number_to_string(self.pubkey.point.y(), order) - return x_str + y_str - - def _compressed_encode(self): - """Encode the public point into the compressed form.""" - order = self.pubkey.order - x_str = number_to_string(self.pubkey.point.x(), order) - if self.pubkey.point.y() & 1: - return b("\x03") + x_str - else: - return b("\x02") + x_str - - def _hybrid_encode(self): - """Encode the public point into the hybrid form.""" - raw_enc = self._raw_encode() - if self.pubkey.point.y() & 1: - return b("\x07") + raw_enc - else: - return b("\x06") + raw_enc - def to_string(self, encoding="raw"): """ Convert the public key to a byte string. @@ -564,16 +542,11 @@ class VerifyingKey(object): :rtype: bytes """ assert encoding in ("raw", "uncompressed", "compressed", "hybrid") - if encoding == "raw": - return self._raw_encode() - elif encoding == "uncompressed": - return b("\x04") + self._raw_encode() - elif encoding == "hybrid": - return self._hybrid_encode() - else: - return self._compressed_encode() + return self.pubkey.point.to_bytes(encoding) - def to_pem(self, point_encoding="uncompressed"): + def to_pem( + self, point_encoding="uncompressed", curve_parameters_encoding=None + ): """ Convert the public key to the :term:`PEM` format. @@ -587,6 +560,9 @@ class VerifyingKey(object): of public keys. "uncompressed" is most portable, "compressed" is smallest. "hybrid" is uncommon and unsupported by most implementations, it is as big as "uncompressed". + :param str curve_parameters_encoding: the encoding for curve parameters + to use, by default tries to use ``named_curve`` encoding, + if that is not possible, falls back to ``explicit`` encoding. :return: portable encoding of the public key :rtype: bytes @@ -594,9 +570,14 @@ class VerifyingKey(object): .. warning:: The PEM is encoded to US-ASCII, it needs to be re-encoded if the system is incompatible (e.g. uses UTF-16) """ - return der.topem(self.to_der(point_encoding), "PUBLIC KEY") + return der.topem( + self.to_der(point_encoding, curve_parameters_encoding), + "PUBLIC KEY", + ) - def to_der(self, point_encoding="uncompressed"): + def to_der( + self, point_encoding="uncompressed", curve_parameters_encoding=None + ): """ Convert the public key to the :term:`DER` format. @@ -608,6 +589,9 @@ class VerifyingKey(object): of public keys. "uncompressed" is most portable, "compressed" is smallest. "hybrid" is uncommon and unsupported by most implementations, it is as big as "uncompressed". + :param str curve_parameters_encoding: the encoding for curve parameters + to use, by default tries to use ``named_curve`` encoding, + if that is not possible, falls back to ``explicit`` encoding. :return: DER encoding of the public key :rtype: bytes @@ -615,9 +599,15 @@ class VerifyingKey(object): if point_encoding == "raw": raise ValueError("raw point_encoding not allowed in DER") point_str = self.to_string(point_encoding) + if isinstance(self.curve.curve, CurveEdTw): + return der.encode_sequence( + der.encode_sequence(der.encode_oid(*self.curve.oid)), + der.encode_bitstring(bytes(point_str), 0), + ) return der.encode_sequence( der.encode_sequence( - encoded_oid_ecPublicKey, self.curve.encoded_oid + encoded_oid_ecPublicKey, + self.curve.to_der(curve_parameters_encoding, point_encoding), ), # 0 is the number of unused bits in the # bit string @@ -643,10 +633,10 @@ class VerifyingKey(object): as the `sigdecode` parameter. :param signature: encoding of the signature - :type signature: sigdecode method dependant + :type signature: sigdecode method dependent :param data: data signed by the `signature`, will be hashed using `hashfunc`, if specified, or default hash function - :type data: bytes like object + :type data: :term:`bytes-like object` :param hashfunc: The default hash function that will be used for verification, needs to implement the same interface as hashlib.sha1 :type hashfunc: callable @@ -671,6 +661,12 @@ class VerifyingKey(object): # signature doesn't have to be a bytes-like-object so don't normalise # it, the decoders will do that data = normalise_bytes(data) + if isinstance(self.curve.curve, CurveEdTw): + signature = normalise_bytes(signature) + try: + return self.pubkey.verify(data, signature) + except (ValueError, MalformedPointError) as e: + raise BadSignatureError("Signature verification failed", e) hashfunc = hashfunc or self.default_hashfunc digest = hashfunc(data).digest() @@ -692,9 +688,9 @@ class VerifyingKey(object): as the `sigdecode` parameter. :param signature: encoding of the signature - :type signature: sigdecode method dependant + :type signature: sigdecode method dependent :param digest: raw hash value that the signature authenticates. - :type digest: bytes like object + :type digest: :term:`bytes-like object` :param sigdecode: Callable to define the way the signature needs to be decoded to an object, needs to handle `signature` as the first parameter, the curve order (an int) as the second and return @@ -717,27 +713,11 @@ class VerifyingKey(object): # signature doesn't have to be a bytes-like-object so don't normalise # it, the decoders will do that digest = normalise_bytes(digest) - if not allow_truncate and len(digest) > self.curve.baselen: - raise BadDigestError( - "this curve (%s) is too short " - "for your digest (%d)" % (self.curve.name, 8 * len(digest)) - ) - number = string_to_number(digest) - if allow_truncate: - max_length = bit_length(self.curve.order) - # we don't use bit_length(number) as that truncates leading zeros - length = len(digest) * 8 - - # See NIST FIPS 186-4: - # - # When the length of the output of the hash function is greater - # than N (i.e., the bit length of q), then the leftmost N bits of - # the hash function output block shall be used in any calculation - # using the hash function output during the generation or - # verification of a digital signature. - # - # as such, we need to shift-out the low-order bits: - number >>= max(0, length - max_length) + number = _truncate_and_convert_digest( + digest, + self.curve, + allow_truncate, + ) try: r, s = sigdecode(signature, self.pubkey.order) @@ -753,14 +733,14 @@ class SigningKey(object): """ Class for handling keys that can create signatures (private keys). - :ivar ecdsa.curves.Curve curve: The Curve over which all the cryptographic - operations will take place + :ivar `~ecdsa.curves.Curve` curve: The Curve over which all the + cryptographic operations will take place :ivar default_hashfunc: the function that will be used for hashing the - data. Should implement the same API as hashlib.sha1 + data. Should implement the same API as :py:class:`hashlib.sha1` :ivar int baselen: the length of a :term:`raw encoding` of private key - :ivar ecdsa.keys.VerifyingKey verifying_key: the public key + :ivar `~ecdsa.keys.VerifyingKey` verifying_key: the public key associated with this private key - :ivar ecdsa.ecdsa.Private_key privkey: the actual private key + :ivar `~ecdsa.ecdsa.Private_key` privkey: the actual private key """ def __init__(self, _error__please_use_generate=None): @@ -783,6 +763,37 @@ class SigningKey(object): ) return NotImplemented + def __ne__(self, other): + """Return False if the points are identical, True otherwise.""" + return not self == other + + @classmethod + def _twisted_edwards_keygen(cls, curve, entropy): + """Generate a private key on a Twisted Edwards curve.""" + if not entropy: + entropy = os.urandom + random = entropy(curve.baselen) + private_key = eddsa.PrivateKey(curve.generator, random) + public_key = private_key.public_key() + + verifying_key = VerifyingKey.from_string( + public_key.public_key(), curve + ) + + self = cls(_error__please_use_generate=True) + self.curve = curve + self.default_hashfunc = None + self.baselen = curve.baselen + self.privkey = private_key + self.verifying_key = verifying_key + return self + + @classmethod + def _weierstrass_keygen(cls, curve, entropy, hashfunc): + """Generate a private key on a Weierstrass curve.""" + secexp = randrange(curve.order, entropy) + return cls.from_secret_exponent(secexp, curve, hashfunc) + @classmethod def generate(cls, curve=NIST192p, entropy=None, hashfunc=sha1): """ @@ -790,7 +801,7 @@ class SigningKey(object): :param curve: The curve on which the point needs to reside, defaults to NIST192p - :type curve: ecdsa.curves.Curve + :type curve: ~ecdsa.curves.Curve :param entropy: Source of randomness for generating the private keys, should provide cryptographically secure random numbers if the keys need to be secure. Uses os.urandom() by default. @@ -803,8 +814,9 @@ class SigningKey(object): :return: Initialised SigningKey object :rtype: SigningKey """ - secexp = randrange(curve.order, entropy) - return cls.from_secret_exponent(secexp, curve, hashfunc) + if isinstance(curve.curve, CurveEdTw): + return cls._twisted_edwards_keygen(curve, entropy) + return cls._weierstrass_keygen(curve, entropy, hashfunc) @classmethod def from_secret_exponent(cls, secexp, curve=NIST192p, hashfunc=sha1): @@ -817,7 +829,7 @@ class SigningKey(object): :param int secexp: secret multiplier (the actual private key in ECDSA). Needs to be an integer between 1 and the curve order. :param curve: The curve on which the point needs to reside - :type curve: ecdsa.curves.Curve + :type curve: ~ecdsa.curves.Curve :param hashfunc: The default hash function that will be used for signing, needs to implement the same interface as hashlib.sha1 @@ -831,6 +843,11 @@ class SigningKey(object): :return: Initialised SigningKey object :rtype: SigningKey """ + if isinstance(curve.curve, CurveEdTw): + raise ValueError( + "Edwards keys don't support setting the secret scalar " + "(exponent) directly" + ) self = cls(_error__please_use_generate=True) self.curve = curve self.default_hashfunc = hashfunc @@ -862,9 +879,9 @@ class SigningKey(object): In Python 3, the expected type is `bytes`. :param string: the raw encoding of the private key - :type string: bytes like object + :type string: :term:`bytes-like object` :param curve: The curve on which the point needs to reside - :type curve: ecdsa.curves.Curve + :type curve: ~ecdsa.curves.Curve :param hashfunc: The default hash function that will be used for signing, needs to implement the same interface as hashlib.sha1 @@ -879,16 +896,27 @@ class SigningKey(object): :rtype: SigningKey """ string = normalise_bytes(string) + if len(string) != curve.baselen: raise MalformedPointError( "Invalid length of private key, received {0}, " "expected {1}".format(len(string), curve.baselen) ) + if isinstance(curve.curve, CurveEdTw): + self = cls(_error__please_use_generate=True) + self.curve = curve + self.default_hashfunc = None # Ignored for EdDSA + self.baselen = curve.baselen + self.privkey = eddsa.PrivateKey(curve.generator, string) + self.verifying_key = VerifyingKey.from_string( + self.privkey.public_key().public_key(), curve + ) + return self secexp = string_to_number(string) return cls.from_secret_exponent(secexp, curve, hashfunc) @classmethod - def from_pem(cls, string, hashfunc=sha1): + def from_pem(cls, string, hashfunc=sha1, valid_curve_encodings=None): """ Initialise from key stored in :term:`PEM` format. @@ -908,6 +936,11 @@ class SigningKey(object): :param string: text with PEM-encoded private ECDSA key :type string: str + :param valid_curve_encodings: list of allowed encoding formats + for curve parameters. By default (``None``) all are supported: + ``named_curve`` and ``explicit``. + :type valid_curve_encodings: :term:`set-like object` + :raises MalformedPointError: if the length of encoding doesn't match the provided curve or the encoded values is too large @@ -918,7 +951,7 @@ class SigningKey(object): :return: Initialised SigningKey object :rtype: SigningKey """ - if not PY2 and isinstance(string, str): + if not PY2 and isinstance(string, str): # pragma: no branch string = string.encode() # The privkey pem may have multiple sections, commonly it also has @@ -928,10 +961,14 @@ class SigningKey(object): if private_key_index == -1: private_key_index = string.index(b"-----BEGIN PRIVATE KEY-----") - return cls.from_der(der.unpem(string[private_key_index:]), hashfunc) + return cls.from_der( + der.unpem(string[private_key_index:]), + hashfunc, + valid_curve_encodings, + ) @classmethod - def from_der(cls, string, hashfunc=sha1): + def from_der(cls, string, hashfunc=sha1, valid_curve_encodings=None): """ Initialise from key stored in :term:`DER` format. @@ -952,14 +989,14 @@ class SigningKey(object): `publicKey` field is ignored completely (errors, if any, in it will be undetected). - The only format supported for the `parameters` field is the named - curve method. Explicit encoding of curve parameters is not supported. + Two formats are supported for the `parameters` field: the named + curve and the explicit encoding of curve parameters. In the legacy ssleay format, this implementation requires the optional `parameters` field to get the curve name. In PKCS #8 format, the curve is part of the PrivateKeyAlgorithmIdentifier. The PKCS #8 format includes an ECPrivateKey object as the `privateKey` - field within a larger structure: + field within a larger structure:: OneAsymmetricKey ::= SEQUENCE { version Version, @@ -975,7 +1012,12 @@ class SigningKey(object): in them will not be detected. :param string: binary string with DER-encoded private ECDSA key - :type string: bytes like object + :type string: :term:`bytes-like object` + :param valid_curve_encodings: list of allowed encoding formats + for curve parameters. By default (``None``) all are supported: + ``named_curve`` and ``explicit``. + Ignored for EdDSA. + :type valid_curve_encodings: :term:`set-like object` :raises MalformedPointError: if the length of encoding doesn't match the provided curve or the encoded values is too large @@ -1010,13 +1052,44 @@ class SigningKey(object): sequence, s = der.remove_sequence(s) algorithm_oid, algorithm_identifier = der.remove_object(sequence) - curve_oid, empty = der.remove_object(algorithm_identifier) - curve = find_curve(curve_oid) + + if algorithm_oid in (Ed25519.oid, Ed448.oid): + if algorithm_identifier: + raise der.UnexpectedDER( + "Non NULL parameters for a EdDSA key" + ) + key_str_der, s = der.remove_octet_string(s) + + # As RFC5958 describe, there are may be optional Attributes + # and Publickey. Don't raise error if something after + # Privatekey + + # TODO parse attributes or validate publickey + # if s: + # raise der.UnexpectedDER( + # "trailing junk inside the privateKey" + # ) + key_str, s = der.remove_octet_string(key_str_der) + if s: + raise der.UnexpectedDER( + "trailing junk after the encoded private key" + ) + + if algorithm_oid == Ed25519.oid: + curve = Ed25519 + else: + assert algorithm_oid == Ed448.oid + curve = Ed448 + + return cls.from_string(key_str, curve, None) if algorithm_oid not in (oid_ecPublicKey, oid_ecDH, oid_ecMQV): raise der.UnexpectedDER( "unexpected algorithm identifier '%s'" % (algorithm_oid,) ) + + curve = Curve.from_der(algorithm_identifier, valid_curve_encodings) + if empty != b"": raise der.UnexpectedDER( "unexpected data after algorithm identifier: %s" @@ -1053,13 +1126,7 @@ class SigningKey(object): raise der.UnexpectedDER( "expected tag 0 in DER privkey, got %d" % tag ) - curve_oid, empty = der.remove_object(curve_oid_str) - if empty != b(""): - raise der.UnexpectedDER( - "trailing junk after DER privkey " - "curve_oid: %s" % binascii.hexlify(empty) - ) - curve = find_curve(curve_oid) + curve = Curve.from_der(curve_oid_str, valid_curve_encodings) # we don't actually care about the following fields # @@ -1091,11 +1158,18 @@ class SigningKey(object): :return: raw encoding of private key :rtype: bytes """ + if isinstance(self.curve.curve, CurveEdTw): + return bytes(self.privkey.private_key) secexp = self.privkey.secret_multiplier s = number_to_string(secexp, self.privkey.order) return s - def to_pem(self, point_encoding="uncompressed", format="ssleay"): + def to_pem( + self, + point_encoding="uncompressed", + format="ssleay", + curve_parameters_encoding=None, + ): """ Convert the private key to the :term:`PEM` format. @@ -1109,6 +1183,11 @@ class SigningKey(object): :param str point_encoding: format to use for encoding public point :param str format: either ``ssleay`` (default) or ``pkcs8`` + :param str curve_parameters_encoding: format of encoded curve + parameters, default depends on the curve, if the curve has + an associated OID, ``named_curve`` format will be used, + if no OID is associated with the curve, the fallback of + ``explicit`` parameters will be used. :return: PEM encoded private key :rtype: bytes @@ -1119,9 +1198,26 @@ class SigningKey(object): # TODO: "BEGIN ECPARAMETERS" assert format in ("ssleay", "pkcs8") header = "EC PRIVATE KEY" if format == "ssleay" else "PRIVATE KEY" - return der.topem(self.to_der(point_encoding, format), header) + return der.topem( + self.to_der(point_encoding, format, curve_parameters_encoding), + header, + ) + + def _encode_eddsa(self): + """Create a PKCS#8 encoding of EdDSA keys.""" + ec_private_key = der.encode_octet_string(self.to_string()) + return der.encode_sequence( + der.encode_integer(0), + der.encode_sequence(der.encode_oid(*self.curve.oid)), + der.encode_octet_string(ec_private_key), + ) - def to_der(self, point_encoding="uncompressed", format="ssleay"): + def to_der( + self, + point_encoding="uncompressed", + format="ssleay", + curve_parameters_encoding=None, + ): """ Convert the private key to the :term:`DER` format. @@ -1131,7 +1227,15 @@ class SigningKey(object): The public key will be included in the generated string. :param str point_encoding: format to use for encoding public point - :param str format: either ``ssleay`` (default) or ``pkcs8`` + Ignored for EdDSA + :param str format: either ``ssleay`` (default) or ``pkcs8``. + EdDSA keys require ``pkcs8``. + :param str curve_parameters_encoding: format of encoded curve + parameters, default depends on the curve, if the curve has + an associated OID, ``named_curve`` format will be used, + if no OID is associated with the curve, the fallback of + ``explicit`` parameters will be used. + Ignored for EdDSA. :return: DER encoded private key :rtype: bytes @@ -1141,15 +1245,27 @@ class SigningKey(object): if point_encoding == "raw": raise ValueError("raw encoding not allowed in DER") assert format in ("ssleay", "pkcs8") + if isinstance(self.curve.curve, CurveEdTw): + if format != "pkcs8": + raise ValueError("Only PKCS#8 format supported for EdDSA keys") + return self._encode_eddsa() encoded_vk = self.get_verifying_key().to_string(point_encoding) - # the 0 in encode_bitstring specifies the number of unused bits - # in the `encoded_vk` string - ec_private_key = der.encode_sequence( + priv_key_elems = [ der.encode_integer(1), der.encode_octet_string(self.to_string()), - der.encode_constructed(0, self.curve.encoded_oid), - der.encode_constructed(1, der.encode_bitstring(encoded_vk, 0)), + ] + if format == "ssleay": + priv_key_elems.append( + der.encode_constructed( + 0, self.curve.to_der(curve_parameters_encoding) + ) + ) + # the 0 in encode_bitstring specifies the number of unused bits + # in the `encoded_vk` string + priv_key_elems.append( + der.encode_constructed(1, der.encode_bitstring(encoded_vk, 0)) ) + ec_private_key = der.encode_sequence(*priv_key_elems) if format == "ssleay": return ec_private_key @@ -1159,7 +1275,8 @@ class SigningKey(object): # top-level structure. der.encode_integer(1), der.encode_sequence( - der.encode_oid(*oid_ecPublicKey), self.curve.encoded_oid + der.encode_oid(*oid_ecPublicKey), + self.curve.to_der(curve_parameters_encoding), ), der.encode_octet_string(ec_private_key), ) @@ -1184,20 +1301,27 @@ class SigningKey(object): extra_entropy=b"", ): """ - Create signature over data using the deterministic RFC6679 algorithm. + Create signature over data. - The data will be hashed using the `hashfunc` function before signing. + For Weierstrass curves it uses the deterministic RFC6979 algorithm. + For Edwards curves it uses the standard EdDSA algorithm. + + For ECDSA the data will be hashed using the `hashfunc` function before + signing. + For EdDSA the data will be hashed with the hash associated with the + curve (SHA-512 for Ed25519 and SHAKE-256 for Ed448). This is the recommended method for performing signatures when hashing of data is necessary. :param data: data to be hashed and computed signature over - :type data: bytes like object + :type data: :term:`bytes-like object` :param hashfunc: hash function to use for computing the signature, if unspecified, the default hash function selected during object initialisation will be used (see `VerifyingKey.default_hashfunc`). The object needs to implement the same interface as hashlib.sha1. + Ignored with EdDSA. :type hashfunc: callable :param sigencode: function used to encode the signature. The function needs to accept three parameters: the two integers @@ -1205,16 +1329,22 @@ class SigningKey(object): signature was computed. It needs to return an encoded signature. See `ecdsa.util.sigencode_string` and `ecdsa.util.sigencode_der` as examples of such functions. + Ignored with EdDSA. :type sigencode: callable :param extra_entropy: additional data that will be fed into the random number generator used in the RFC6979 process. Entirely optional. - :type extra_entropy: bytes like object + Ignored with EdDSA. + :type extra_entropy: :term:`bytes-like object` :return: encoded signature over `data` - :rtype: bytes or sigencode function dependant type + :rtype: bytes or sigencode function dependent type """ hashfunc = hashfunc or self.default_hashfunc data = normalise_bytes(data) + + if isinstance(self.curve.curve, CurveEdTw): + return self.privkey.sign(data) + extra_entropy = normalise_bytes(extra_entropy) digest = hashfunc(data).digest() @@ -1235,7 +1365,7 @@ class SigningKey(object): allow_truncate=False, ): """ - Create signature for digest using the deterministic RFC6679 algorithm. + Create signature for digest using the deterministic RFC6979 algorithm. `digest` should be the output of cryptographically secure hash function like SHA256 or SHA-3-256. @@ -1244,32 +1374,36 @@ class SigningKey(object): hashing of data is necessary. :param digest: hash of data that will be signed - :type digest: bytes like object + :type digest: :term:`bytes-like object` :param hashfunc: hash function to use for computing the random "k" value from RFC6979 process, if unspecified, the default hash function selected during object initialisation will be used (see - `VerifyingKey.default_hashfunc`). The object needs to implement - the same interface as hashlib.sha1. + :attr:`.VerifyingKey.default_hashfunc`). The object needs to + implement + the same interface as :func:`~hashlib.sha1` from :py:mod:`hashlib`. :type hashfunc: callable :param sigencode: function used to encode the signature. The function needs to accept three parameters: the two integers that are the signature and the order of the curve over which the signature was computed. It needs to return an encoded signature. - See `ecdsa.util.sigencode_string` and `ecdsa.util.sigencode_der` + See :func:`~ecdsa.util.sigencode_string` and + :func:`~ecdsa.util.sigencode_der` as examples of such functions. :type sigencode: callable :param extra_entropy: additional data that will be fed into the random number generator used in the RFC6979 process. Entirely optional. - :type extra_entropy: bytes like object + :type extra_entropy: :term:`bytes-like object` :param bool allow_truncate: if True, the provided digest can have bigger bit-size than the order of the curve, the extra bits (at the end of the digest) will be truncated. Use it when signing SHA-384 output using NIST256p or in similar situations. :return: encoded signature for the `digest` hash - :rtype: bytes or sigencode function dependant type + :rtype: bytes or sigencode function dependent type """ + if isinstance(self.curve.curve, CurveEdTw): + raise ValueError("Method unsupported for Edwards curves") secexp = self.privkey.secret_multiplier hashfunc = hashfunc or self.default_hashfunc digest = normalise_bytes(digest) @@ -1311,7 +1445,11 @@ class SigningKey(object): allow_truncate=True, ): """ - Create signature over data using the probabilistic ECDSA algorithm. + Create signature over data. + + Uses the probabilistic ECDSA algorithm for Weierstrass curves + (NIST256p, etc.) and the deterministic EdDSA algorithm for the + Edwards curves (Ed25519, Ed448). This method uses the standard ECDSA algorithm that requires a cryptographically secure random number generator. @@ -1320,46 +1458,62 @@ class SigningKey(object): method instead of this one. :param data: data that will be hashed for signing - :type data: bytes like object - :param callable entropy: randomness source, os.urandom by default - :param hashfunc: hash function to use for hashing the provided `data`. + :type data: :term:`bytes-like object` + :param callable entropy: randomness source, :func:`os.urandom` by + default. Ignored with EdDSA. + :param hashfunc: hash function to use for hashing the provided + ``data``. If unspecified the default hash function selected during object initialisation will be used (see - `VerifyingKey.default_hashfunc`). - Should behave like hashlib.sha1. The output length of the + :attr:`.VerifyingKey.default_hashfunc`). + Should behave like :func:`~hashlib.sha1` from :py:mod:`hashlib`. + The output length of the hash (in bytes) must not be longer than the length of the curve order (rounded up to the nearest byte), so using SHA256 with NIST256p is ok, but SHA256 with NIST192p is not. (In the 2**-96ish unlikely event of a hash output larger than the curve order, the hash will effectively be wrapped mod n). - Use hashfunc=hashlib.sha1 to match openssl's -ecdsa-with-SHA1 mode, - or hashfunc=hashlib.sha256 for openssl-1.0.0's -ecdsa-with-SHA256. + If you want to explicitly allow use of large hashes with small + curves set the ``allow_truncate`` to ``True``. + Use ``hashfunc=hashlib.sha1`` to match openssl's + ``-ecdsa-with-SHA1`` mode, + or ``hashfunc=hashlib.sha256`` for openssl-1.0.0's + ``-ecdsa-with-SHA256``. + Ignored for EdDSA :type hashfunc: callable :param sigencode: function used to encode the signature. The function needs to accept three parameters: the two integers that are the signature and the order of the curve over which the signature was computed. It needs to return an encoded signature. - See `ecdsa.util.sigencode_string` and `ecdsa.util.sigencode_der` + See :func:`~ecdsa.util.sigencode_string` and + :func:`~ecdsa.util.sigencode_der` as examples of such functions. + Ignored for EdDSA :type sigencode: callable :param int k: a pre-selected nonce for calculating the signature. In typical use cases, it should be set to None (the default) to allow its generation from an entropy source. - :param bool allow_truncate: if True, the provided digest can have + Ignored for EdDSA. + :param bool allow_truncate: if ``True``, the provided digest can have bigger bit-size than the order of the curve, the extra bits (at the end of the digest) will be truncated. Use it when signing SHA-384 output using NIST256p or in similar situations. True by default. + Ignored for EdDSA. - :raises RSZeroError: in the unlikely event when "r" parameter or - "s" parameter is equal 0 as that would leak the key. Calee should - try a better entropy source or different 'k' in such case. + :raises RSZeroError: in the unlikely event when *r* parameter or + *s* parameter of the created signature is equal 0, as that would + leak the key. Caller should try a better entropy source, retry with + different ``k``, or use the + :func:`~SigningKey.sign_deterministic` in such case. :return: encoded signature of the hash of `data` - :rtype: bytes or sigencode function dependant type + :rtype: bytes or sigencode function dependent type """ hashfunc = hashfunc or self.default_hashfunc data = normalise_bytes(data) + if isinstance(self.curve.curve, CurveEdTw): + return self.sign_deterministic(data) h = hashfunc(data).digest() return self.sign_digest(h, entropy, sigencode, k, allow_truncate) @@ -1384,7 +1538,7 @@ class SigningKey(object): instead of this one. :param digest: hash value that will be signed - :type digest: bytes like object + :type digest: :term:`bytes-like object` :param callable entropy: randomness source, os.urandom by default :param sigencode: function used to encode the signature. The function needs to accept three parameters: the two integers @@ -1402,21 +1556,22 @@ class SigningKey(object): SHA-384 output using NIST256p or in similar situations. :raises RSZeroError: in the unlikely event when "r" parameter or - "s" parameter is equal 0 as that would leak the key. Calee should - try a better entropy source in such case. + "s" parameter of the created signature is equal 0, as that would + leak the key. Caller should try a better entropy source, retry with + different 'k', or use the + :func:`~SigningKey.sign_digest_deterministic` in such case. :return: encoded signature for the `digest` hash - :rtype: bytes or sigencode function dependant type + :rtype: bytes or sigencode function dependent type """ + if isinstance(self.curve.curve, CurveEdTw): + raise ValueError("Method unsupported for Edwards curves") digest = normalise_bytes(digest) - if allow_truncate: - digest = digest[: self.curve.baselen] - if len(digest) > self.curve.baselen: - raise BadDigestError( - "this curve (%s) is too short " - "for your digest (%d)" % (self.curve.name, 8 * len(digest)) - ) - number = string_to_number(digest) + number = _truncate_and_convert_digest( + digest, + self.curve, + allow_truncate, + ) r, s = self.sign_number(number, entropy, k) return sigencode(r, s, self.privkey.order) @@ -1435,12 +1590,16 @@ class SigningKey(object): it will be selected at random using the entropy source. :raises RSZeroError: in the unlikely event when "r" parameter or - "s" parameter is equal 0 as that would leak the key. Calee should - try a different 'k' in such case. + "s" parameter of the created signature is equal 0, as that would + leak the key. Caller should try a better entropy source, retry with + different 'k', or use the + :func:`~SigningKey.sign_digest_deterministic` in such case. :return: the "r" and "s" parameters of the signature :rtype: tuple of ints """ + if isinstance(self.curve.curve, CurveEdTw): + raise ValueError("Method unsupported for Edwards curves") order = self.privkey.order if k is not None: diff --git a/frozen_deps/ecdsa/numbertheory.py b/frozen_deps/ecdsa/numbertheory.py index e5cc888..d3500c7 100644 --- a/frozen_deps/ecdsa/numbertheory.py +++ b/frozen_deps/ecdsa/numbertheory.py @@ -7,10 +7,11 @@ # Written in 2005 and 2006 by Peter Pearson and placed in the public domain. # Revision history: # 2008.11.14: Use pow(base, exponent, modulus) for modular_exp. -# Make gcd and lcm accept arbitrarly many arguments. +# Make gcd and lcm accept arbitrarily many arguments. from __future__ import division +import sys from six import integer_types, PY2 from six.moves import reduce @@ -42,6 +43,10 @@ class Error(Exception): pass +class JacobiError(Error): + pass + + class SquareRootError(Error): pass @@ -153,8 +158,10 @@ def jacobi(a, n): # table printed in HAC, and by extensive use in calculating # modular square roots. - assert n >= 3 - assert n % 2 == 1 + if not n >= 3: + raise JacobiError("n must be larger than 2") + if not n % 2 == 1: + raise JacobiError("n must be odd") a = a % n if a == 0: return 0 @@ -201,9 +208,8 @@ def square_root_mod_prime(a, p): d = pow(a, (p - 1) // 4, p) if d == 1: return pow(a, (p + 3) // 8, p) - if d == p - 1: - return (2 * a * pow(4 * a, (p - 5) // 8, p)) % p - raise RuntimeError("Shouldn't get here.") + assert d == p - 1 + return (2 * a * pow(4 * a, (p - 5) // 8, p)) % p if PY2: # xrange on python2 can take integers representable as C long only @@ -214,53 +220,63 @@ def square_root_mod_prime(a, p): if jacobi(b * b - 4 * a, p) == -1: f = (a, -b, 1) ff = polynomial_exp_mod((0, 1), (p + 1) // 2, f, p) - assert ff[1] == 0 + if ff[1]: + raise SquareRootError("p is not prime") return ff[0] raise RuntimeError("No b found.") -if GMPY2: +# because all the inverse_mod code is arch/environment specific, and coveralls +# expects it to execute equal number of times, we need to waive it by +# adding the "no branch" pragma to all branches +if GMPY2: # pragma: no branch def inverse_mod(a, m): """Inverse of a mod m.""" - if a == 0: + if a == 0: # pragma: no branch return 0 return powmod(a, -1, m) - -elif GMPY: +elif GMPY: # pragma: no branch def inverse_mod(a, m): """Inverse of a mod m.""" - # while libgmp likely does support inverses modulo, it is accessible - # only using the native `pow()` function, and `pow()` sanity checks - # the parameters before passing them on to underlying implementation - # on Python2 - if a == 0: + # while libgmp does support inverses modulo, it is accessible + # only using the native `pow()` function, and `pow()` in gmpy sanity + # checks the parameters before passing them on to underlying + # implementation + if a == 0: # pragma: no branch return 0 a = mpz(a) m = mpz(m) lm, hm = mpz(1), mpz(0) low, high = a % m, m - while low > 1: + while low > 1: # pragma: no branch r = high // low lm, low, hm, high = hm - lm * r, high - low * r, lm, low return lm % m +elif sys.version_info >= (3, 8): # pragma: no branch + + def inverse_mod(a, m): + """Inverse of a mod m.""" + if a == 0: # pragma: no branch + return 0 + return pow(a, -1, m) -else: +else: # pragma: no branch def inverse_mod(a, m): """Inverse of a mod m.""" - if a == 0: + if a == 0: # pragma: no branch return 0 lm, hm = 1, 0 low, high = a % m, m - while low > 1: + while low > 1: # pragma: no branch r = high // low lm, low, hm, high = hm - lm * r, high - low * r, lm, low @@ -321,7 +337,6 @@ def factorization(n): return [] result = [] - d = 2 # Test the small primes: @@ -374,7 +389,7 @@ def phi(n): # pragma: no cover warnings.warn( "Function is unused by library code. If you use this code, " "please open an issue in " - "https://github.com/warner/python-ecdsa", + "https://github.com/tlsfuzzer/python-ecdsa", DeprecationWarning, ) @@ -404,7 +419,7 @@ def carmichael(n): # pragma: no cover warnings.warn( "Function is unused by library code. If you use this code, " "please open an issue in " - "https://github.com/warner/python-ecdsa", + "https://github.com/tlsfuzzer/python-ecdsa", DeprecationWarning, ) @@ -419,7 +434,7 @@ def carmichael_of_factorized(f_list): # pragma: no cover warnings.warn( "Function is unused by library code. If you use this code, " "please open an issue in " - "https://github.com/warner/python-ecdsa", + "https://github.com/tlsfuzzer/python-ecdsa", DeprecationWarning, ) @@ -439,7 +454,7 @@ def carmichael_of_ppower(pp): # pragma: no cover warnings.warn( "Function is unused by library code. If you use this code, " "please open an issue in " - "https://github.com/warner/python-ecdsa", + "https://github.com/tlsfuzzer/python-ecdsa", DeprecationWarning, ) @@ -456,7 +471,7 @@ def order_mod(x, m): # pragma: no cover warnings.warn( "Function is unused by library code. If you use this code, " "please open an issue in " - "https://github.com/warner/python-ecdsa", + "https://github.com/tlsfuzzer/python-ecdsa", DeprecationWarning, ) @@ -482,7 +497,7 @@ def largest_factor_relatively_prime(a, b): # pragma: no cover warnings.warn( "Function is unused by library code. If you use this code, " "please open an issue in " - "https://github.com/warner/python-ecdsa", + "https://github.com/tlsfuzzer/python-ecdsa", DeprecationWarning, ) @@ -507,7 +522,7 @@ def kinda_order_mod(x, m): # pragma: no cover warnings.warn( "Function is unused by library code. If you use this code, " "please open an issue in " - "https://github.com/warner/python-ecdsa", + "https://github.com/tlsfuzzer/python-ecdsa", DeprecationWarning, ) diff --git a/frozen_deps/ecdsa/rfc6979.py b/frozen_deps/ecdsa/rfc6979.py index 1e577c0..0728b5a 100644 --- a/frozen_deps/ecdsa/rfc6979.py +++ b/frozen_deps/ecdsa/rfc6979.py @@ -42,14 +42,17 @@ def bits2octets(data, order): # https://tools.ietf.org/html/rfc6979#section-3.2 def generate_k(order, secexp, hash_func, data, retry_gen=0, extra_entropy=b""): """ - order - order of the DSA generator used in the signature - secexp - secure exponent (private key) in numeric form - hash_func - reference to the same hash function used for generating - hash - data - hash in binary form of the signing data - retry_gen - int - how many good 'k' values to skip before returning - extra_entropy - extra added data in binary form as per section-3.6 of - rfc6979 + Generate the ``k`` value - the nonce for DSA. + + :param int order: order of the DSA generator used in the signature + :param int secexp: secure exponent (private key) in numeric form + :param hash_func: reference to the same hash function used for generating + hash, like :py:class:`hashlib.sha1` + :param bytes data: hash in binary form of the signing data + :param int retry_gen: how many good 'k' values to skip before returning + :param bytes extra_entropy: additional added data in binary form as per + section-3.6 of rfc6979 + :rtype: int """ qlen = bit_length(order) diff --git a/frozen_deps/ecdsa/test_curves.py b/frozen_deps/ecdsa/test_curves.py new file mode 100644 index 0000000..93b6c9b --- /dev/null +++ b/frozen_deps/ecdsa/test_curves.py @@ -0,0 +1,361 @@ +try: + import unittest2 as unittest +except ImportError: + import unittest + +import base64 +import pytest +from .curves import ( + Curve, + NIST256p, + curves, + UnknownCurveError, + PRIME_FIELD_OID, + curve_by_name, +) +from .ellipticcurve import CurveFp, PointJacobi, CurveEdTw +from . import der +from .util import number_to_string + + +class TestParameterEncoding(unittest.TestCase): + @classmethod + def setUpClass(cls): + # minimal, but with cofactor (excludes seed when compared to + # OpenSSL output) + cls.base64_params = ( + "MIHgAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP/////////" + "//////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12K" + "o6k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEQQRrF9Hy4SxCR/i85uVjpEDyd" + "wN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1" + "AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQE=" + ) + + def test_from_pem(self): + pem_params = ( + "-----BEGIN EC PARAMETERS-----\n" + "MIHgAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP/////////\n" + "//////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12K\n" + "o6k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEQQRrF9Hy4SxCR/i85uVjpEDyd\n" + "wN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1\n" + "AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQE=\n" + "-----END EC PARAMETERS-----\n" + ) + curve = Curve.from_pem(pem_params) + + self.assertIs(curve, NIST256p) + + def test_from_pem_with_explicit_when_explicit_disabled(self): + pem_params = ( + "-----BEGIN EC PARAMETERS-----\n" + "MIHgAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP/////////\n" + "//////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12K\n" + "o6k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEQQRrF9Hy4SxCR/i85uVjpEDyd\n" + "wN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1\n" + "AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQE=\n" + "-----END EC PARAMETERS-----\n" + ) + with self.assertRaises(der.UnexpectedDER) as e: + Curve.from_pem(pem_params, ["named_curve"]) + + self.assertIn("explicit curve parameters not", str(e.exception)) + + def test_from_pem_with_named_curve_with_named_curve_disabled(self): + pem_params = ( + "-----BEGIN EC PARAMETERS-----\n" + "BggqhkjOPQMBBw==\n" + "-----END EC PARAMETERS-----\n" + ) + with self.assertRaises(der.UnexpectedDER) as e: + Curve.from_pem(pem_params, ["explicit"]) + + self.assertIn("named_curve curve parameters not", str(e.exception)) + + def test_from_pem_with_wrong_header(self): + pem_params = ( + "-----BEGIN PARAMETERS-----\n" + "MIHgAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP/////////\n" + "//////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12K\n" + "o6k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEQQRrF9Hy4SxCR/i85uVjpEDyd\n" + "wN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1\n" + "AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQE=\n" + "-----END PARAMETERS-----\n" + ) + with self.assertRaises(der.UnexpectedDER) as e: + Curve.from_pem(pem_params) + + self.assertIn("PARAMETERS PEM header", str(e.exception)) + + def test_to_pem(self): + pem_params = ( + b"-----BEGIN EC PARAMETERS-----\n" + b"BggqhkjOPQMBBw==\n" + b"-----END EC PARAMETERS-----\n" + ) + encoding = NIST256p.to_pem() + + self.assertEqual(pem_params, encoding) + + def test_compare_with_different_object(self): + self.assertNotEqual(NIST256p, 256) + + def test_named_curve_params_der(self): + encoded = NIST256p.to_der() + + # just the encoding of the NIST256p OID (prime256v1) + self.assertEqual(b"\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07", encoded) + + def test_verify_that_default_is_named_curve_der(self): + encoded_default = NIST256p.to_der() + encoded_named = NIST256p.to_der("named_curve") + + self.assertEqual(encoded_default, encoded_named) + + def test_encoding_to_explicit_params(self): + encoded = NIST256p.to_der("explicit") + + self.assertEqual(encoded, bytes(base64.b64decode(self.base64_params))) + + def test_encoding_to_unsupported_type(self): + with self.assertRaises(ValueError) as e: + NIST256p.to_der("unsupported") + + self.assertIn("Only 'named_curve'", str(e.exception)) + + def test_encoding_to_explicit_compressed_params(self): + encoded = NIST256p.to_der("explicit", "compressed") + + compressed_base_point = ( + "MIHAAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP//////////" + "/////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12Ko6" + "k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEIQNrF9Hy4SxCR/i85uVjpEDydwN9" + "gS3rM6D0oTlF2JjClgIhAP////8AAAAA//////////+85vqtpxeehPO5ysL8YyVR" + "AgEB" + ) + + self.assertEqual( + encoded, bytes(base64.b64decode(compressed_base_point)) + ) + + def test_decoding_explicit_from_openssl(self): + # generated with openssl 1.1.1k using + # openssl ecparam -name P-256 -param_enc explicit -out /tmp/file.pem + p256_explicit = ( + "MIH3AgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP//////////" + "/////zBbBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12Ko6" + "k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsDFQDEnTYIhucEk2pmeOETnSa3gZ9+" + "kARBBGsX0fLhLEJH+Lzm5WOkQPJ3A32BLeszoPShOUXYmMKWT+NC4v4af5uO5+tK" + "fA+eFivOM1drMV7Oy7ZAaDe/UfUCIQD/////AAAAAP//////////vOb6racXnoTz" + "ucrC/GMlUQIBAQ==" + ) + + decoded = Curve.from_der(bytes(base64.b64decode(p256_explicit))) + + self.assertEqual(NIST256p, decoded) + + def test_decoding_well_known_from_explicit_params(self): + curve = Curve.from_der(bytes(base64.b64decode(self.base64_params))) + + self.assertIs(curve, NIST256p) + + def test_decoding_with_incorrect_valid_encodings(self): + with self.assertRaises(ValueError) as e: + Curve.from_der(b"", ["explicitCA"]) + + self.assertIn("Only named_curve", str(e.exception)) + + def test_compare_curves_with_different_generators(self): + curve_fp = CurveFp(23, 1, 7) + base_a = PointJacobi(curve_fp, 13, 3, 1, 9, generator=True) + base_b = PointJacobi(curve_fp, 1, 20, 1, 9, generator=True) + + curve_a = Curve("unknown", curve_fp, base_a, None) + curve_b = Curve("unknown", curve_fp, base_b, None) + + self.assertNotEqual(curve_a, curve_b) + + def test_default_encode_for_custom_curve(self): + curve_fp = CurveFp(23, 1, 7) + base_point = PointJacobi(curve_fp, 13, 3, 1, 9, generator=True) + + curve = Curve("unknown", curve_fp, base_point, None) + + encoded = curve.to_der() + + decoded = Curve.from_der(encoded) + + self.assertEqual(curve, decoded) + + expected = "MCECAQEwDAYHKoZIzj0BAQIBFzAGBAEBBAEHBAMEDQMCAQk=" + + self.assertEqual(encoded, bytes(base64.b64decode(expected))) + + def test_named_curve_encode_for_custom_curve(self): + curve_fp = CurveFp(23, 1, 7) + base_point = PointJacobi(curve_fp, 13, 3, 1, 9, generator=True) + + curve = Curve("unknown", curve_fp, base_point, None) + + with self.assertRaises(UnknownCurveError) as e: + curve.to_der("named_curve") + + self.assertIn("Can't encode curve", str(e.exception)) + + def test_try_decoding_binary_explicit(self): + sect113r1_explicit = ( + "MIGRAgEBMBwGByqGSM49AQIwEQIBcQYJKoZIzj0BAgMCAgEJMDkEDwAwiCUMpufH" + "/mSc6Fgg9wQPAOi+5NPiJgdEGIvg6ccjAxUAEOcjqxTWluZ2h1YVF1b+v4/LSakE" + "HwQAnXNhbzX0qxQH1zViwQ8ApSgwJ3lY7oTRMV7TGIYCDwEAAAAAAAAA2czsijnl" + "bwIBAg==" + ) + + with self.assertRaises(UnknownCurveError) as e: + Curve.from_der(base64.b64decode(sect113r1_explicit)) + + self.assertIn("Characteristic 2 curves unsupported", str(e.exception)) + + def test_decode_malformed_named_curve(self): + bad_der = der.encode_oid(*NIST256p.oid) + der.encode_integer(1) + + with self.assertRaises(der.UnexpectedDER) as e: + Curve.from_der(bad_der) + + self.assertIn("Unexpected data after OID", str(e.exception)) + + def test_decode_malformed_explicit_garbage_after_ECParam(self): + bad_der = bytes( + base64.b64decode(self.base64_params) + ) + der.encode_integer(1) + + with self.assertRaises(der.UnexpectedDER) as e: + Curve.from_der(bad_der) + + self.assertIn("Unexpected data after ECParameters", str(e.exception)) + + def test_decode_malformed_unknown_version_number(self): + bad_der = der.encode_sequence(der.encode_integer(2)) + + with self.assertRaises(der.UnexpectedDER) as e: + Curve.from_der(bad_der) + + self.assertIn("Unknown parameter encoding format", str(e.exception)) + + def test_decode_malformed_unknown_field_type(self): + curve_p = NIST256p.curve.p() + bad_der = der.encode_sequence( + der.encode_integer(1), + der.encode_sequence( + der.encode_oid(1, 2, 3), der.encode_integer(curve_p) + ), + der.encode_sequence( + der.encode_octet_string( + number_to_string(NIST256p.curve.a() % curve_p, curve_p) + ), + der.encode_octet_string( + number_to_string(NIST256p.curve.b(), curve_p) + ), + ), + der.encode_octet_string( + NIST256p.generator.to_bytes("uncompressed") + ), + der.encode_integer(NIST256p.generator.order()), + ) + + with self.assertRaises(UnknownCurveError) as e: + Curve.from_der(bad_der) + + self.assertIn("Unknown field type: (1, 2, 3)", str(e.exception)) + + def test_decode_malformed_garbage_after_prime(self): + curve_p = NIST256p.curve.p() + bad_der = der.encode_sequence( + der.encode_integer(1), + der.encode_sequence( + der.encode_oid(*PRIME_FIELD_OID), + der.encode_integer(curve_p), + der.encode_integer(1), + ), + der.encode_sequence( + der.encode_octet_string( + number_to_string(NIST256p.curve.a() % curve_p, curve_p) + ), + der.encode_octet_string( + number_to_string(NIST256p.curve.b(), curve_p) + ), + ), + der.encode_octet_string( + NIST256p.generator.to_bytes("uncompressed") + ), + der.encode_integer(NIST256p.generator.order()), + ) + + with self.assertRaises(der.UnexpectedDER) as e: + Curve.from_der(bad_der) + + self.assertIn("Prime-p element", str(e.exception)) + + +class TestCurveSearching(unittest.TestCase): + def test_correct_name(self): + c = curve_by_name("NIST256p") + self.assertIs(c, NIST256p) + + def test_openssl_name(self): + c = curve_by_name("prime256v1") + self.assertIs(c, NIST256p) + + def test_unknown_curve(self): + with self.assertRaises(UnknownCurveError) as e: + curve_by_name("foo bar") + + self.assertIn( + "name 'foo bar' unknown, only curves supported: " + "['NIST192p', 'NIST224p'", + str(e.exception), + ) + + def test_with_None_as_parameter(self): + with self.assertRaises(UnknownCurveError) as e: + curve_by_name(None) + + self.assertIn( + "name None unknown, only curves supported: " + "['NIST192p', 'NIST224p'", + str(e.exception), + ) + + [email protected]("curve", curves, ids=[i.name for i in curves]) +def test_curve_params_encode_decode_named(curve): + ret = Curve.from_der(curve.to_der("named_curve")) + + assert curve == ret + + [email protected]("curve", curves, ids=[i.name for i in curves]) +def test_curve_params_encode_decode_explicit(curve): + if isinstance(curve.curve, CurveEdTw): + with pytest.raises(UnknownCurveError): + curve.to_der("explicit") + else: + ret = Curve.from_der(curve.to_der("explicit")) + + assert curve == ret + + [email protected]("curve", curves, ids=[i.name for i in curves]) +def test_curve_params_encode_decode_default(curve): + ret = Curve.from_der(curve.to_der()) + + assert curve == ret + + [email protected]("curve", curves, ids=[i.name for i in curves]) +def test_curve_params_encode_decode_explicit_compressed(curve): + if isinstance(curve.curve, CurveEdTw): + with pytest.raises(UnknownCurveError): + curve.to_der("explicit", "compressed") + else: + ret = Curve.from_der(curve.to_der("explicit", "compressed")) + + assert curve == ret diff --git a/frozen_deps/ecdsa/test_der.py b/frozen_deps/ecdsa/test_der.py index 746d927..0ca5bd7 100644 --- a/frozen_deps/ecdsa/test_der.py +++ b/frozen_deps/ecdsa/test_der.py @@ -9,7 +9,7 @@ except ImportError: import unittest from six import b import hypothesis.strategies as st -from hypothesis import given, example +from hypothesis import given import pytest from ._compat import str_idx_as_int from .curves import NIST256p, NIST224p @@ -21,6 +21,9 @@ from .der import ( remove_bitstring, remove_object, encode_oid, + remove_constructed, + remove_octet_string, + remove_sequence, ) @@ -39,7 +42,7 @@ class TestRemoveInteger(unittest.TestCase): val, rem = remove_integer(b("\x02\x02\x00\x80")) self.assertEqual(val, 0x80) - self.assertFalse(rem) + self.assertEqual(rem, b"") def test_two_zero_bytes_with_high_bit_set(self): with self.assertRaises(UnexpectedDER): @@ -57,19 +60,31 @@ class TestRemoveInteger(unittest.TestCase): val, rem = remove_integer(b("\x02\x01\x00")) self.assertEqual(val, 0) - self.assertFalse(rem) + self.assertEqual(rem, b"") def test_encoding_of_127(self): val, rem = remove_integer(b("\x02\x01\x7f")) self.assertEqual(val, 127) - self.assertFalse(rem) + self.assertEqual(rem, b"") def test_encoding_of_128(self): val, rem = remove_integer(b("\x02\x02\x00\x80")) self.assertEqual(val, 128) - self.assertFalse(rem) + self.assertEqual(rem, b"") + + def test_wrong_tag(self): + with self.assertRaises(UnexpectedDER) as e: + remove_integer(b"\x01\x02\x00\x80") + + self.assertIn("wanted type 'integer'", str(e.exception)) + + def test_wrong_length(self): + with self.assertRaises(UnexpectedDER) as e: + remove_integer(b"\x02\x03\x00\x80") + + self.assertIn("Length longer", str(e.exception)) class TestReadLength(unittest.TestCase): @@ -368,8 +383,72 @@ class TestRemoveObject(unittest.TestCase): remove_object(b"\x06\x03\x88\x37") +class TestRemoveConstructed(unittest.TestCase): + def test_simple(self): + data = b"\xa1\x02\xff\xaa" + + tag, body, rest = remove_constructed(data) + + self.assertEqual(tag, 0x01) + self.assertEqual(body, b"\xff\xaa") + self.assertEqual(rest, b"") + + def test_with_malformed_tag(self): + data = b"\x01\x02\xff\xaa" + + with self.assertRaises(UnexpectedDER) as e: + remove_constructed(data) + + self.assertIn("constructed tag", str(e.exception)) + + +class TestRemoveOctetString(unittest.TestCase): + def test_simple(self): + data = b"\x04\x03\xaa\xbb\xcc" + body, rest = remove_octet_string(data) + self.assertEqual(body, b"\xaa\xbb\xcc") + self.assertEqual(rest, b"") + + def test_with_malformed_tag(self): + data = b"\x03\x03\xaa\xbb\xcc" + with self.assertRaises(UnexpectedDER) as e: + remove_octet_string(data) + + self.assertIn("octetstring", str(e.exception)) + + +class TestRemoveSequence(unittest.TestCase): + def test_simple(self): + data = b"\x30\x02\xff\xaa" + body, rest = remove_sequence(data) + self.assertEqual(body, b"\xff\xaa") + self.assertEqual(rest, b"") + + def test_with_empty_string(self): + with self.assertRaises(UnexpectedDER) as e: + remove_sequence(b"") + + self.assertIn("Empty string", str(e.exception)) + + def test_with_wrong_tag(self): + data = b"\x20\x02\xff\xaa" + + with self.assertRaises(UnexpectedDER) as e: + remove_sequence(data) + + self.assertIn("wanted type 'sequence'", str(e.exception)) + + def test_with_wrong_length(self): + data = b"\x30\x03\xff\xaa" + + with self.assertRaises(UnexpectedDER) as e: + remove_sequence(data) + + self.assertIn("Length longer", str(e.exception)) + + @st.composite -def st_oid(draw, max_value=2 ** 512, max_size=50): +def st_oid(draw, max_value=2**512, max_size=50): """ Hypothesis strategy that returns valid OBJECT IDENTIFIERs as tuples diff --git a/frozen_deps/ecdsa/test_ecdh.py b/frozen_deps/ecdsa/test_ecdh.py index caf6835..872d4d1 100644 --- a/frozen_deps/ecdsa/test_ecdh.py +++ b/frozen_deps/ecdsa/test_ecdh.py @@ -2,18 +2,41 @@ import os import shutil import subprocess import pytest -from binascii import hexlify, unhexlify - -from .curves import NIST192p, NIST224p, NIST256p, NIST384p, NIST521p +from binascii import unhexlify + +try: + import unittest2 as unittest +except ImportError: + import unittest + +from .curves import ( + NIST192p, + NIST224p, + NIST256p, + NIST384p, + NIST521p, + BRAINPOOLP160r1, +) from .curves import curves -from .ecdh import ECDH, InvalidCurveError, InvalidSharedSecretError, NoKeyError +from .ecdh import ( + ECDH, + InvalidCurveError, + InvalidSharedSecretError, + NoKeyError, + NoCurveError, +) from .keys import SigningKey, VerifyingKey +from .ellipticcurve import CurveEdTw @pytest.mark.parametrize( - "vcurve", curves, ids=[curve.name for curve in curves] + "vcurve", + curves, + ids=[curve.name for curve in curves], ) def test_ecdh_each(vcurve): + if isinstance(vcurve.curve, CurveEdTw): + pytest.skip("ECDH is not supported for Edwards curves") ecdh1 = ECDH(curve=vcurve) ecdh2 = ECDH(curve=vcurve) @@ -26,6 +49,19 @@ def test_ecdh_each(vcurve): assert secret1 == secret2 +def test_ecdh_both_keys_present(): + key1 = SigningKey.generate(BRAINPOOLP160r1) + key2 = SigningKey.generate(BRAINPOOLP160r1) + + ecdh1 = ECDH(BRAINPOOLP160r1, key1, key2.verifying_key) + ecdh2 = ECDH(private_key=key2, public_key=key1.verifying_key) + + secret1 = ecdh1.generate_sharedsecret_bytes() + secret2 = ecdh2.generate_sharedsecret_bytes() + + assert secret1 == secret2 + + def test_ecdh_no_public_key(): ecdh1 = ECDH(curve=NIST192p) @@ -38,6 +74,44 @@ def test_ecdh_no_public_key(): ecdh1.generate_sharedsecret_bytes() +class TestECDH(unittest.TestCase): + def test_load_key_from_wrong_curve(self): + ecdh1 = ECDH() + ecdh1.set_curve(NIST192p) + + key1 = SigningKey.generate(BRAINPOOLP160r1) + + with self.assertRaises(InvalidCurveError) as e: + ecdh1.load_private_key(key1) + + self.assertIn("Curve mismatch", str(e.exception)) + + def test_generate_without_curve(self): + ecdh1 = ECDH() + + with self.assertRaises(NoCurveError) as e: + ecdh1.generate_private_key() + + self.assertIn("Curve must be set", str(e.exception)) + + def test_load_bytes_without_curve_set(self): + ecdh1 = ECDH() + + with self.assertRaises(NoCurveError) as e: + ecdh1.load_private_key_bytes(b"\x01" * 32) + + self.assertIn("Curve must be set", str(e.exception)) + + def test_set_curve_from_received_public_key(self): + ecdh1 = ECDH() + + key1 = SigningKey.generate(BRAINPOOLP160r1) + + ecdh1.load_received_public_key(key1.verifying_key) + + self.assertEqual(ecdh1.curve, BRAINPOOLP160r1) + + def test_ecdh_wrong_public_key_curve(): ecdh1 = ECDH(curve=NIST192p) ecdh1.generate_private_key() @@ -293,25 +367,27 @@ OPENSSL_SUPPORTED_CURVES = set( @pytest.mark.parametrize( - "vcurve", curves, ids=[curve.name for curve in curves] + "vcurve", + curves, + ids=[curve.name for curve in curves], ) def test_ecdh_with_openssl(vcurve): + if isinstance(vcurve.curve, CurveEdTw): + pytest.skip("Edwards curves are not supported for ECDH") + assert vcurve.openssl_name if vcurve.openssl_name not in OPENSSL_SUPPORTED_CURVES: pytest.skip("system openssl does not support " + vcurve.openssl_name) - return try: hlp = run_openssl("pkeyutl -help") - if hlp.find("-derive") == 0: + if hlp.find("-derive") == 0: # pragma: no cover pytest.skip("system openssl does not support `pkeyutl -derive`") - return - except RunOpenSslError: - pytest.skip("system openssl does not support `pkeyutl -derive`") - return + except RunOpenSslError: # pragma: no cover + pytest.skip("system openssl could not be executed") - if os.path.isdir("t"): + if os.path.isdir("t"): # pragma: no branch shutil.rmtree("t") os.mkdir("t") run_openssl( @@ -346,25 +422,20 @@ def test_ecdh_with_openssl(vcurve): assert secret1 == secret2 - try: - run_openssl( - "pkeyutl -derive -inkey t/privkey1.pem -peerkey t/pubkey2.pem -out t/secret1" - ) - run_openssl( - "pkeyutl -derive -inkey t/privkey2.pem -peerkey t/pubkey1.pem -out t/secret2" - ) - except RunOpenSslError: - pytest.skip("system openssl does not support `pkeyutl -derive`") - return + run_openssl( + "pkeyutl -derive -inkey t/privkey1.pem -peerkey t/pubkey2.pem -out t/secret1" + ) + run_openssl( + "pkeyutl -derive -inkey t/privkey2.pem -peerkey t/pubkey1.pem -out t/secret2" + ) with open("t/secret1", "rb") as e: ssl_secret1 = e.read() with open("t/secret1", "rb") as e: ssl_secret2 = e.read() - if len(ssl_secret1) != vk1.curve.baselen: - pytest.skip("system openssl does not support `pkeyutl -derive`") - return + assert len(ssl_secret1) == vk1.curve.verifying_key_length // 2 + assert len(secret1) == vk1.curve.verifying_key_length // 2 assert ssl_secret1 == ssl_secret2 assert secret1 == ssl_secret1 diff --git a/frozen_deps/ecdsa/test_ecdsa.py b/frozen_deps/ecdsa/test_ecdsa.py index e656b88..dbc4a6e 100644 --- a/frozen_deps/ecdsa/test_ecdsa.py +++ b/frozen_deps/ecdsa/test_ecdsa.py @@ -21,6 +21,11 @@ from .ecdsa import ( generator_384, generator_521, generator_secp256k1, + curve_192, + InvalidPointError, + curve_112r2, + generator_112r2, + int_to_string, ) @@ -96,6 +101,20 @@ class TestPublicKey(unittest.TestCase): pub_key2 = Public_key(gen, point2) self.assertNotEqual(pub_key1, pub_key2) + def test_inequality_different_curves(self): + gen = generator_192 + x1 = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 + y1 = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F + point1 = ellipticcurve.Point(gen.curve(), x1, y1) + + x2 = 0x722BA0FB6B8FC8898A4C6AB49E66 + y2 = 0x2B7344BB57A7ABC8CA0F1A398C7D + point2 = ellipticcurve.Point(generator_112r2.curve(), x2, y2) + + pub_key1 = Public_key(gen, point1) + pub_key2 = Public_key(generator_112r2, point2) + self.assertNotEqual(pub_key1, pub_key2) + def test_inequality_public_key_not_implemented(self): gen = generator_192 x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 @@ -104,6 +123,106 @@ class TestPublicKey(unittest.TestCase): pub_key = Public_key(gen, point) self.assertNotEqual(pub_key, None) + def test_public_key_with_generator_without_order(self): + gen = ellipticcurve.PointJacobi( + generator_192.curve(), generator_192.x(), generator_192.y(), 1 + ) + + x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 + y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F + point = ellipticcurve.Point(gen.curve(), x, y) + + with self.assertRaises(InvalidPointError) as e: + Public_key(gen, point) + + self.assertIn("Generator point must have order", str(e.exception)) + + def test_public_point_on_curve_not_scalar_multiple_of_base_point(self): + x = 2 + y = 0xBE6AA4938EF7CFE6FE29595B6B00 + # we need a curve with cofactor != 1 + point = ellipticcurve.PointJacobi(curve_112r2, x, y, 1) + + self.assertTrue(curve_112r2.contains_point(x, y)) + + with self.assertRaises(InvalidPointError) as e: + Public_key(generator_112r2, point) + + self.assertIn("Generator point order", str(e.exception)) + + def test_point_is_valid_with_not_scalar_multiple_of_base_point(self): + x = 2 + y = 0xBE6AA4938EF7CFE6FE29595B6B00 + + self.assertFalse(point_is_valid(generator_112r2, x, y)) + + # the tests to verify the extensiveness of tests in ecdsa.ecdsa + # if PointJacobi gets modified to calculate the x and y mod p the tests + # below will need to use a fake/mock object + def test_invalid_point_x_negative(self): + pt = ellipticcurve.PointJacobi(curve_192, -1, 0, 1) + + with self.assertRaises(InvalidPointError) as e: + Public_key(generator_192, pt) + + self.assertIn("The public point has x or y", str(e.exception)) + + def test_invalid_point_x_equal_p(self): + pt = ellipticcurve.PointJacobi(curve_192, curve_192.p(), 0, 1) + + with self.assertRaises(InvalidPointError) as e: + Public_key(generator_192, pt) + + self.assertIn("The public point has x or y", str(e.exception)) + + def test_invalid_point_y_negative(self): + pt = ellipticcurve.PointJacobi(curve_192, 0, -1, 1) + + with self.assertRaises(InvalidPointError) as e: + Public_key(generator_192, pt) + + self.assertIn("The public point has x or y", str(e.exception)) + + def test_invalid_point_y_equal_p(self): + pt = ellipticcurve.PointJacobi(curve_192, 0, curve_192.p(), 1) + + with self.assertRaises(InvalidPointError) as e: + Public_key(generator_192, pt) + + self.assertIn("The public point has x or y", str(e.exception)) + + +class TestPublicKeyVerifies(unittest.TestCase): + # test all the different ways that a signature can be publicly invalid + @classmethod + def setUpClass(cls): + gen = generator_192 + x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 + y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F + point = ellipticcurve.Point(gen.curve(), x, y) + + cls.pub_key = Public_key(gen, point) + + def test_sig_with_r_zero(self): + sig = Signature(0, 1) + + self.assertFalse(self.pub_key.verifies(1, sig)) + + def test_sig_with_r_order(self): + sig = Signature(generator_192.order(), 1) + + self.assertFalse(self.pub_key.verifies(1, sig)) + + def test_sig_with_s_zero(self): + sig = Signature(1, 0) + + self.assertFalse(self.pub_key.verifies(1, sig)) + + def test_sig_with_s_order(self): + sig = Signature(1, generator_192.order()) + + self.assertFalse(self.pub_key.verifies(1, sig)) + class TestPrivateKey(unittest.TestCase): @classmethod @@ -536,3 +655,7 @@ def test_sig_verify(args): assert pubkey.verifies(msg, signature) assert not pubkey.verifies(msg - 1, signature) + + +def test_int_to_string_with_zero(): + assert int_to_string(0) == b"\x00" diff --git a/frozen_deps/ecdsa/test_eddsa.py b/frozen_deps/ecdsa/test_eddsa.py new file mode 100644 index 0000000..7a09ad7 --- /dev/null +++ b/frozen_deps/ecdsa/test_eddsa.py @@ -0,0 +1,1079 @@ +import pickle +import hashlib +import pytest + +try: + import unittest2 as unittest +except ImportError: + import unittest +from hypothesis import given, settings, example +import hypothesis.strategies as st +from .ellipticcurve import PointEdwards, INFINITY, CurveEdTw +from .eddsa import ( + generator_ed25519, + curve_ed25519, + generator_ed448, + curve_ed448, + PrivateKey, + PublicKey, +) +from .ecdsa import generator_256, curve_256 +from .errors import MalformedPointError +from ._compat import a2b_hex, compat26_str + + +class TestA2B_Hex(unittest.TestCase): + def test_invalid_input(self): + with self.assertRaises(ValueError): + a2b_hex("abcdefghi") + + +def test_ed25519_curve_compare(): + assert curve_ed25519 != curve_256 + + +def test_ed25519_and_ed448_compare(): + assert curve_ed448 != curve_ed25519 + + +def test_ed25519_and_custom_curve_compare(): + a = CurveEdTw(curve_ed25519.p(), -curve_ed25519.a(), 1) + + assert curve_ed25519 != a + + +def test_ed25519_and_almost_exact_curve_compare(): + a = CurveEdTw(curve_ed25519.p(), curve_ed25519.a(), 1) + + assert curve_ed25519 != a + + +def test_ed25519_and_same_curve_params(): + a = CurveEdTw(curve_ed25519.p(), curve_ed25519.a(), curve_ed25519.d()) + + assert curve_ed25519 == a + assert not (curve_ed25519 != a) + + +def test_ed25519_contains_point(): + g = generator_ed25519 + assert curve_ed25519.contains_point(g.x(), g.y()) + + +def test_ed25519_contains_point_bad(): + assert not curve_ed25519.contains_point(1, 1) + + +def test_ed25519_double(): + a = generator_ed25519 + + z = a.double() + + assert isinstance(z, PointEdwards) + + x2 = int( + "24727413235106541002554574571675588834622768167397638456726423" + "682521233608206" + ) + y2 = int( + "15549675580280190176352668710449542251549572066445060580507079" + "593062643049417" + ) + + b = PointEdwards(curve_ed25519, x2, y2, 1, x2 * y2) + + assert z == b + assert a != b + + +def test_ed25519_add_as_double(): + a = generator_ed25519 + + z = a + a + + assert isinstance(z, PointEdwards) + + b = generator_ed25519.double() + + assert z == b + + +def test_ed25519_double_infinity(): + a = PointEdwards(curve_ed25519, 0, 1, 1, 0) + + z = a.double() + + assert z is INFINITY + + +def test_ed25519_double_badly_encoded_infinity(): + # invalid point, mostly to make instrumental happy + a = PointEdwards(curve_ed25519, 1, 1, 1, 0) + + z = a.double() + + assert z is INFINITY + + +def test_ed25519_eq_with_different_z(): + x = generator_ed25519.x() + y = generator_ed25519.y() + p = curve_ed25519.p() + + a = PointEdwards(curve_ed25519, x * 2 % p, y * 2 % p, 2, x * y * 2 % p) + b = PointEdwards(curve_ed25519, x * 3 % p, y * 3 % p, 3, x * y * 3 % p) + + assert a == b + + assert not (a != b) + + +def test_ed25519_eq_against_infinity(): + assert generator_ed25519 != INFINITY + + +def test_ed25519_eq_encoded_infinity_against_infinity(): + a = PointEdwards(curve_ed25519, 0, 1, 1, 0) + assert a == INFINITY + + +def test_ed25519_eq_bad_encode_of_infinity_against_infinity(): + # technically incorrect encoding of the point at infinity, but we check + # both X and T, so verify that just T==0 works + a = PointEdwards(curve_ed25519, 1, 1, 1, 0) + assert a == INFINITY + + +def test_ed25519_eq_against_non_Edwards_point(): + assert generator_ed25519 != generator_256 + + +def test_ed25519_eq_against_negated_point(): + g = generator_ed25519 + neg = PointEdwards(curve_ed25519, -g.x(), g.y(), 1, -g.x() * g.y()) + assert g != neg + + +def test_ed25519_eq_x_different_y(): + # not points on the curve, but __eq__ doesn't care + a = PointEdwards(curve_ed25519, 1, 1, 1, 1) + b = PointEdwards(curve_ed25519, 1, 2, 1, 2) + + assert a != b + + +def test_ed25519_test_normalisation_and_scaling(): + x = generator_ed25519.x() + y = generator_ed25519.y() + p = curve_ed25519.p() + + a = PointEdwards(curve_ed25519, x * 11 % p, y * 11 % p, 11, x * y * 11 % p) + + assert a.x() == x + assert a.y() == y + + a.scale() + + assert a.x() == x + assert a.y() == y + + a.scale() # second execution should be a noop + + assert a.x() == x + assert a.y() == y + + +def test_ed25519_add_three_times(): + a = generator_ed25519 + + z = a + a + a + + x3 = int( + "468967334644549386571235445953867877890461982801326656862413" + "21779790909858396" + ) + y3 = int( + "832484377853344397649037712036920113830141722629755531674120" + "2210403726505172" + ) + + b = PointEdwards(curve_ed25519, x3, y3, 1, x3 * y3) + + assert z == b + + +def test_ed25519_add_to_infinity(): + # generator * (order-1) + x1 = int( + "427838232691226969392843410947554224151809796397784248136826" + "78720006717057747" + ) + y1 = int( + "463168356949264781694283940034751631413079938662562256157830" + "33603165251855960" + ) + inf_m_1 = PointEdwards(curve_ed25519, x1, y1, 1, x1 * y1) + + inf = inf_m_1 + generator_ed25519 + + assert inf is INFINITY + + +def test_ed25519_add_and_mul_equivalence(): + g = generator_ed25519 + + assert g + g == g * 2 + assert g + g + g == g * 3 + + +def test_ed25519_add_literal_infinity(): + g = generator_ed25519 + z = g + INFINITY + + assert z == g + + +def test_ed25519_add_infinity(): + inf = PointEdwards(curve_ed25519, 0, 1, 1, 0) + g = generator_ed25519 + z = g + inf + + assert z == g + + z = inf + g + + assert z == g + + +class TestEd25519(unittest.TestCase): + def test_add_wrong_curves(self): + with self.assertRaises(ValueError) as e: + generator_ed25519 + generator_ed448 + + self.assertIn("different curve", str(e.exception)) + + def test_add_wrong_point_type(self): + with self.assertRaises(ValueError) as e: + generator_ed25519 + generator_256 + + self.assertIn("different curve", str(e.exception)) + + +def test_ed25519_mul_to_order_min_1(): + x1 = int( + "427838232691226969392843410947554224151809796397784248136826" + "78720006717057747" + ) + y1 = int( + "463168356949264781694283940034751631413079938662562256157830" + "33603165251855960" + ) + inf_m_1 = PointEdwards(curve_ed25519, x1, y1, 1, x1 * y1) + + assert generator_ed25519 * (generator_ed25519.order() - 1) == inf_m_1 + + +def test_ed25519_mul_to_infinity(): + assert generator_ed25519 * generator_ed25519.order() == INFINITY + + +def test_ed25519_mul_to_infinity_plus_1(): + g = generator_ed25519 + assert g * (g.order() + 1) == g + + +def test_ed25519_mul_and_add(): + g = generator_ed25519 + a = g * 128 + b = g * 64 + g * 64 + + assert a == b + + +def test_ed25519_mul_and_add_2(): + g = generator_ed25519 + + a = g * 123 + b = g * 120 + g * 3 + + assert a == b + + +def test_ed25519_mul_infinity(): + inf = PointEdwards(curve_ed25519, 0, 1, 1, 0) + + z = inf * 11 + + assert z == INFINITY + + +def test_ed25519_mul_by_zero(): + z = generator_ed25519 * 0 + + assert z == INFINITY + + +def test_ed25519_mul_by_one(): + z = generator_ed25519 * 1 + + assert z == generator_ed25519 + + +def test_ed25519_mul_custom_point(): + # verify that multiplication without order set works + + g = generator_ed25519 + + a = PointEdwards(curve_ed25519, g.x(), g.y(), 1, g.x() * g.y()) + + z = a * 11 + + assert z == g * 11 + + +def test_ed25519_pickle(): + g = generator_ed25519 + assert pickle.loads(pickle.dumps(g)) == g + + +def test_ed448_eq_against_different_curve(): + assert generator_ed25519 != generator_ed448 + + +def test_ed448_double(): + g = generator_ed448 + z = g.double() + + assert isinstance(z, PointEdwards) + + x2 = int( + "4845591495304045936995492052586696895690942404582120401876" + "6013278705691214670908136440114445572635086627683154494739" + "7859048262938744149" + ) + y2 = int( + "4940887598674337276743026725267350893505445523037277237461" + "2648447308771911703729389009346215770388834286503647778745" + "3078312060500281069" + ) + + b = PointEdwards(curve_ed448, x2, y2, 1, x2 * y2) + + assert z == b + assert g != b + + +def test_ed448_add_as_double(): + g = generator_ed448 + z = g + g + + b = g.double() + + assert z == b + + +def test_ed448_mul_as_double(): + g = generator_ed448 + z = g * 2 + b = g.double() + + assert z == b + + +def test_ed448_add_to_infinity(): + # generator * (order - 1) + x1 = int( + "5022586839996825903617194737881084981068517190547539260353" + "6473749366191269932473977736719082931859264751085238669719" + "1187378895383117729" + ) + y1 = int( + "2988192100784814926760179304439306734375440401540802420959" + "2824137233150618983587600353687865541878473398230323350346" + "2500531545062832660" + ) + inf_m_1 = PointEdwards(curve_ed448, x1, y1, 1, x1 * y1) + + inf = inf_m_1 + generator_ed448 + + assert inf is INFINITY + + +def test_ed448_mul_to_infinity(): + g = generator_ed448 + inf = g * g.order() + + assert inf is INFINITY + + +def test_ed448_mul_to_infinity_plus_1(): + g = generator_ed448 + + z = g * (g.order() + 1) + + assert z == g + + +def test_ed448_add_and_mul_equivalence(): + g = generator_ed448 + + assert g + g == g * 2 + assert g + g + g == g * 3 + + +def test_ed25519_encode(): + g = generator_ed25519 + g_bytes = g.to_bytes() + assert len(g_bytes) == 32 + exp_bytes = ( + b"\x58\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" + b"\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" + ) + assert g_bytes == exp_bytes + + +def test_ed25519_decode(): + exp_bytes = ( + b"\x58\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" + b"\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" + ) + a = PointEdwards.from_bytes(curve_ed25519, exp_bytes) + + assert a == generator_ed25519 + + +class TestEdwardsMalformed(unittest.TestCase): + def test_invalid_point(self): + exp_bytes = ( + b"\x78\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" + b"\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" + ) + with self.assertRaises(MalformedPointError): + PointEdwards.from_bytes(curve_ed25519, exp_bytes) + + def test_invalid_length(self): + exp_bytes = ( + b"\x58\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" + b"\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" + b"\x66" + ) + with self.assertRaises(MalformedPointError) as e: + PointEdwards.from_bytes(curve_ed25519, exp_bytes) + + self.assertIn("length", str(e.exception)) + + def test_ed448_invalid(self): + exp_bytes = b"\xff" * 57 + with self.assertRaises(MalformedPointError): + PointEdwards.from_bytes(curve_ed448, exp_bytes) + + +def test_ed448_encode(): + g = generator_ed448 + g_bytes = g.to_bytes() + assert len(g_bytes) == 57 + exp_bytes = ( + b"\x14\xfa\x30\xf2\x5b\x79\x08\x98\xad\xc8\xd7\x4e\x2c\x13\xbd" + b"\xfd\xc4\x39\x7c\xe6\x1c\xff\xd3\x3a\xd7\xc2\xa0\x05\x1e\x9c" + b"\x78\x87\x40\x98\xa3\x6c\x73\x73\xea\x4b\x62\xc7\xc9\x56\x37" + b"\x20\x76\x88\x24\xbc\xb6\x6e\x71\x46\x3f\x69\x00" + ) + assert g_bytes == exp_bytes + + +def test_ed448_decode(): + exp_bytes = ( + b"\x14\xfa\x30\xf2\x5b\x79\x08\x98\xad\xc8\xd7\x4e\x2c\x13\xbd" + b"\xfd\xc4\x39\x7c\xe6\x1c\xff\xd3\x3a\xd7\xc2\xa0\x05\x1e\x9c" + b"\x78\x87\x40\x98\xa3\x6c\x73\x73\xea\x4b\x62\xc7\xc9\x56\x37" + b"\x20\x76\x88\x24\xbc\xb6\x6e\x71\x46\x3f\x69\x00" + ) + + a = PointEdwards.from_bytes(curve_ed448, exp_bytes) + + assert a == generator_ed448 + + +class TestEdDSAEquality(unittest.TestCase): + def test_equal_public_points(self): + key1 = PublicKey(generator_ed25519, b"\x01" * 32) + key2 = PublicKey(generator_ed25519, b"\x01" * 32) + + self.assertEqual(key1, key2) + self.assertFalse(key1 != key2) + + def test_unequal_public_points(self): + key1 = PublicKey(generator_ed25519, b"\x01" * 32) + key2 = PublicKey(generator_ed25519, b"\x03" * 32) + + self.assertNotEqual(key1, key2) + + def test_unequal_to_string(self): + key1 = PublicKey(generator_ed25519, b"\x01" * 32) + key2 = b"\x01" * 32 + + self.assertNotEqual(key1, key2) + + def test_unequal_publickey_curves(self): + key1 = PublicKey(generator_ed25519, b"\x01" * 32) + key2 = PublicKey(generator_ed448, b"\x03" * 56 + b"\x00") + + self.assertNotEqual(key1, key2) + self.assertTrue(key1 != key2) + + def test_equal_private_keys(self): + key1 = PrivateKey(generator_ed25519, b"\x01" * 32) + key2 = PrivateKey(generator_ed25519, b"\x01" * 32) + + self.assertEqual(key1, key2) + self.assertFalse(key1 != key2) + + def test_unequal_private_keys(self): + key1 = PrivateKey(generator_ed25519, b"\x01" * 32) + key2 = PrivateKey(generator_ed25519, b"\x02" * 32) + + self.assertNotEqual(key1, key2) + self.assertTrue(key1 != key2) + + def test_unequal_privatekey_to_string(self): + key1 = PrivateKey(generator_ed25519, b"\x01" * 32) + key2 = b"\x01" * 32 + + self.assertNotEqual(key1, key2) + + def test_unequal_privatekey_curves(self): + key1 = PrivateKey(generator_ed25519, b"\x01" * 32) + key2 = PrivateKey(generator_ed448, b"\x01" * 57) + + self.assertNotEqual(key1, key2) + + +class TestInvalidEdDSAInputs(unittest.TestCase): + def test_wrong_length_of_private_key(self): + with self.assertRaises(ValueError): + PrivateKey(generator_ed25519, b"\x01" * 31) + + def test_wrong_length_of_public_key(self): + with self.assertRaises(ValueError): + PublicKey(generator_ed25519, b"\x01" * 33) + + def test_wrong_cofactor_curve(self): + ed_c = curve_ed25519 + + def _hash(data): + return hashlib.new("sha512", compat26_str(data)).digest() + + curve = CurveEdTw(ed_c.p(), ed_c.a(), ed_c.d(), 1, _hash) + g = generator_ed25519 + fake_gen = PointEdwards(curve, g.x(), g.y(), 1, g.x() * g.y()) + + with self.assertRaises(ValueError) as e: + PrivateKey(fake_gen, g.to_bytes()) + + self.assertIn("cofactor", str(e.exception)) + + def test_invalid_signature_length(self): + key = PublicKey(generator_ed25519, b"\x01" * 32) + + with self.assertRaises(ValueError) as e: + key.verify(b"", b"\x01" * 65) + + self.assertIn("length", str(e.exception)) + + def test_changing_public_key(self): + key = PublicKey(generator_ed25519, b"\x01" * 32) + + g = key.point + + new_g = PointEdwards(curve_ed25519, g.x(), g.y(), 1, g.x() * g.y()) + + key.point = new_g + + self.assertEqual(g, key.point) + + def test_changing_public_key_to_different_point(self): + key = PublicKey(generator_ed25519, b"\x01" * 32) + + with self.assertRaises(ValueError) as e: + key.point = generator_ed25519 + + self.assertIn("coordinates", str(e.exception)) + + def test_invalid_s_value(self): + key = PublicKey( + generator_ed25519, + b"\xd7\x5a\x98\x01\x82\xb1\x0a\xb7\xd5\x4b\xfe\xd3\xc9\x64\x07\x3a" + b"\x0e\xe1\x72\xf3\xda\xa6\x23\x25\xaf\x02\x1a\x68\xf7\x07\x51\x1a", + ) + sig_valid = bytearray( + b"\xe5\x56\x43\x00\xc3\x60\xac\x72\x90\x86\xe2\xcc\x80\x6e\x82\x8a" + b"\x84\x87\x7f\x1e\xb8\xe5\xd9\x74\xd8\x73\xe0\x65\x22\x49\x01\x55" + b"\x5f\xb8\x82\x15\x90\xa3\x3b\xac\xc6\x1e\x39\x70\x1c\xf9\xb4\x6b" + b"\xd2\x5b\xf5\xf0\x59\x5b\xbe\x24\x65\x51\x41\x43\x8e\x7a\x10\x0b" + ) + + self.assertTrue(key.verify(b"", sig_valid)) + + sig_invalid = bytearray(sig_valid) + sig_invalid[-1] = 0xFF + + with self.assertRaises(ValueError): + key.verify(b"", sig_invalid) + + def test_invalid_r_value(self): + key = PublicKey( + generator_ed25519, + b"\xd7\x5a\x98\x01\x82\xb1\x0a\xb7\xd5\x4b\xfe\xd3\xc9\x64\x07\x3a" + b"\x0e\xe1\x72\xf3\xda\xa6\x23\x25\xaf\x02\x1a\x68\xf7\x07\x51\x1a", + ) + sig_valid = bytearray( + b"\xe5\x56\x43\x00\xc3\x60\xac\x72\x90\x86\xe2\xcc\x80\x6e\x82\x8a" + b"\x84\x87\x7f\x1e\xb8\xe5\xd9\x74\xd8\x73\xe0\x65\x22\x49\x01\x55" + b"\x5f\xb8\x82\x15\x90\xa3\x3b\xac\xc6\x1e\x39\x70\x1c\xf9\xb4\x6b" + b"\xd2\x5b\xf5\xf0\x59\x5b\xbe\x24\x65\x51\x41\x43\x8e\x7a\x10\x0b" + ) + + self.assertTrue(key.verify(b"", sig_valid)) + + sig_invalid = bytearray(sig_valid) + sig_invalid[0] = 0xE0 + + with self.assertRaises(ValueError): + key.verify(b"", sig_invalid) + + +HYP_SETTINGS = dict() +HYP_SETTINGS["max_examples"] = 10 + + +@settings(**HYP_SETTINGS) +@example(1) +@example(5) # smallest multiple that requires changing sign of x +@given(st.integers(min_value=1, max_value=int(generator_ed25519.order() - 1))) +def test_ed25519_encode_decode(multiple): + a = generator_ed25519 * multiple + + b = PointEdwards.from_bytes(curve_ed25519, a.to_bytes()) + + assert a == b + + +@settings(**HYP_SETTINGS) +@example(1) +@example(2) # smallest multiple that requires changing the sign of x +@given(st.integers(min_value=1, max_value=int(generator_ed448.order() - 1))) +def test_ed448_encode_decode(multiple): + a = generator_ed448 * multiple + + b = PointEdwards.from_bytes(curve_ed448, a.to_bytes()) + + assert a == b + + +@settings(**HYP_SETTINGS) +@example(1) +@example(2) +@given(st.integers(min_value=1, max_value=int(generator_ed25519.order()) - 1)) +def test_ed25519_mul_precompute_vs_naf(multiple): + """Compare multiplication with and without precomputation.""" + g = generator_ed25519 + new_g = PointEdwards(curve_ed25519, g.x(), g.y(), 1, g.x() * g.y()) + + assert g * multiple == multiple * new_g + + +# Test vectors from RFC 8032 +TEST_VECTORS = [ + # TEST 1 + ( + generator_ed25519, + "9d61b19deffd5a60ba844af492ec2cc4" "4449c5697b326919703bac031cae7f60", + "d75a980182b10ab7d54bfed3c964073a" "0ee172f3daa62325af021a68f707511a", + "", + "e5564300c360ac729086e2cc806e828a" + "84877f1eb8e5d974d873e06522490155" + "5fb8821590a33bacc61e39701cf9b46b" + "d25bf5f0595bbe24655141438e7a100b", + ), + # TEST 2 + ( + generator_ed25519, + "4ccd089b28ff96da9db6c346ec114e0f" "5b8a319f35aba624da8cf6ed4fb8a6fb", + "3d4017c3e843895a92b70aa74d1b7ebc" "9c982ccf2ec4968cc0cd55f12af4660c", + "72", + "92a009a9f0d4cab8720e820b5f642540" + "a2b27b5416503f8fb3762223ebdb69da" + "085ac1e43e15996e458f3613d0f11d8c" + "387b2eaeb4302aeeb00d291612bb0c00", + ), + # TEST 3 + ( + generator_ed25519, + "c5aa8df43f9f837bedb7442f31dcb7b1" "66d38535076f094b85ce3a2e0b4458f7", + "fc51cd8e6218a1a38da47ed00230f058" "0816ed13ba3303ac5deb911548908025", + "af82", + "6291d657deec24024827e69c3abe01a3" + "0ce548a284743a445e3680d7db5ac3ac" + "18ff9b538d16f290ae67f760984dc659" + "4a7c15e9716ed28dc027beceea1ec40a", + ), + # TEST 1024 + ( + generator_ed25519, + "f5e5767cf153319517630f226876b86c" "8160cc583bc013744c6bf255f5cc0ee5", + "278117fc144c72340f67d0f2316e8386" "ceffbf2b2428c9c51fef7c597f1d426e", + "08b8b2b733424243760fe426a4b54908" + "632110a66c2f6591eabd3345e3e4eb98" + "fa6e264bf09efe12ee50f8f54e9f77b1" + "e355f6c50544e23fb1433ddf73be84d8" + "79de7c0046dc4996d9e773f4bc9efe57" + "38829adb26c81b37c93a1b270b20329d" + "658675fc6ea534e0810a4432826bf58c" + "941efb65d57a338bbd2e26640f89ffbc" + "1a858efcb8550ee3a5e1998bd177e93a" + "7363c344fe6b199ee5d02e82d522c4fe" + "ba15452f80288a821a579116ec6dad2b" + "3b310da903401aa62100ab5d1a36553e" + "06203b33890cc9b832f79ef80560ccb9" + "a39ce767967ed628c6ad573cb116dbef" + "efd75499da96bd68a8a97b928a8bbc10" + "3b6621fcde2beca1231d206be6cd9ec7" + "aff6f6c94fcd7204ed3455c68c83f4a4" + "1da4af2b74ef5c53f1d8ac70bdcb7ed1" + "85ce81bd84359d44254d95629e9855a9" + "4a7c1958d1f8ada5d0532ed8a5aa3fb2" + "d17ba70eb6248e594e1a2297acbbb39d" + "502f1a8c6eb6f1ce22b3de1a1f40cc24" + "554119a831a9aad6079cad88425de6bd" + "e1a9187ebb6092cf67bf2b13fd65f270" + "88d78b7e883c8759d2c4f5c65adb7553" + "878ad575f9fad878e80a0c9ba63bcbcc" + "2732e69485bbc9c90bfbd62481d9089b" + "eccf80cfe2df16a2cf65bd92dd597b07" + "07e0917af48bbb75fed413d238f5555a" + "7a569d80c3414a8d0859dc65a46128ba" + "b27af87a71314f318c782b23ebfe808b" + "82b0ce26401d2e22f04d83d1255dc51a" + "ddd3b75a2b1ae0784504df543af8969b" + "e3ea7082ff7fc9888c144da2af58429e" + "c96031dbcad3dad9af0dcbaaaf268cb8" + "fcffead94f3c7ca495e056a9b47acdb7" + "51fb73e666c6c655ade8297297d07ad1" + "ba5e43f1bca32301651339e22904cc8c" + "42f58c30c04aafdb038dda0847dd988d" + "cda6f3bfd15c4b4c4525004aa06eeff8" + "ca61783aacec57fb3d1f92b0fe2fd1a8" + "5f6724517b65e614ad6808d6f6ee34df" + "f7310fdc82aebfd904b01e1dc54b2927" + "094b2db68d6f903b68401adebf5a7e08" + "d78ff4ef5d63653a65040cf9bfd4aca7" + "984a74d37145986780fc0b16ac451649" + "de6188a7dbdf191f64b5fc5e2ab47b57" + "f7f7276cd419c17a3ca8e1b939ae49e4" + "88acba6b965610b5480109c8b17b80e1" + "b7b750dfc7598d5d5011fd2dcc5600a3" + "2ef5b52a1ecc820e308aa342721aac09" + "43bf6686b64b2579376504ccc493d97e" + "6aed3fb0f9cd71a43dd497f01f17c0e2" + "cb3797aa2a2f256656168e6c496afc5f" + "b93246f6b1116398a346f1a641f3b041" + "e989f7914f90cc2c7fff357876e506b5" + "0d334ba77c225bc307ba537152f3f161" + "0e4eafe595f6d9d90d11faa933a15ef1" + "369546868a7f3a45a96768d40fd9d034" + "12c091c6315cf4fde7cb68606937380d" + "b2eaaa707b4c4185c32eddcdd306705e" + "4dc1ffc872eeee475a64dfac86aba41c" + "0618983f8741c5ef68d3a101e8a3b8ca" + "c60c905c15fc910840b94c00a0b9d0", + "0aab4c900501b3e24d7cdf4663326a3a" + "87df5e4843b2cbdb67cbf6e460fec350" + "aa5371b1508f9f4528ecea23c436d94b" + "5e8fcd4f681e30a6ac00a9704a188a03", + ), + # TEST SHA(abc) + ( + generator_ed25519, + "833fe62409237b9d62ec77587520911e" "9a759cec1d19755b7da901b96dca3d42", + "ec172b93ad5e563bf4932c70e1245034" "c35467ef2efd4d64ebf819683467e2bf", + "ddaf35a193617abacc417349ae204131" + "12e6fa4e89a97ea20a9eeee64b55d39a" + "2192992a274fc1a836ba3c23a3feebbd" + "454d4423643ce80e2a9ac94fa54ca49f", + "dc2a4459e7369633a52b1bf277839a00" + "201009a3efbf3ecb69bea2186c26b589" + "09351fc9ac90b3ecfdfbc7c66431e030" + "3dca179c138ac17ad9bef1177331a704", + ), + # Blank + ( + generator_ed448, + "6c82a562cb808d10d632be89c8513ebf" + "6c929f34ddfa8c9f63c9960ef6e348a3" + "528c8a3fcc2f044e39a3fc5b94492f8f" + "032e7549a20098f95b", + "5fd7449b59b461fd2ce787ec616ad46a" + "1da1342485a70e1f8a0ea75d80e96778" + "edf124769b46c7061bd6783df1e50f6c" + "d1fa1abeafe8256180", + "", + "533a37f6bbe457251f023c0d88f976ae" + "2dfb504a843e34d2074fd823d41a591f" + "2b233f034f628281f2fd7a22ddd47d78" + "28c59bd0a21bfd3980ff0d2028d4b18a" + "9df63e006c5d1c2d345b925d8dc00b41" + "04852db99ac5c7cdda8530a113a0f4db" + "b61149f05a7363268c71d95808ff2e65" + "2600", + ), + # 1 octet + ( + generator_ed448, + "c4eab05d357007c632f3dbb48489924d" + "552b08fe0c353a0d4a1f00acda2c463a" + "fbea67c5e8d2877c5e3bc397a659949e" + "f8021e954e0a12274e", + "43ba28f430cdff456ae531545f7ecd0a" + "c834a55d9358c0372bfa0c6c6798c086" + "6aea01eb00742802b8438ea4cb82169c" + "235160627b4c3a9480", + "03", + "26b8f91727bd62897af15e41eb43c377" + "efb9c610d48f2335cb0bd0087810f435" + "2541b143c4b981b7e18f62de8ccdf633" + "fc1bf037ab7cd779805e0dbcc0aae1cb" + "cee1afb2e027df36bc04dcecbf154336" + "c19f0af7e0a6472905e799f1953d2a0f" + "f3348ab21aa4adafd1d234441cf807c0" + "3a00", + ), + # 11 octets + ( + generator_ed448, + "cd23d24f714274e744343237b93290f5" + "11f6425f98e64459ff203e8985083ffd" + "f60500553abc0e05cd02184bdb89c4cc" + "d67e187951267eb328", + "dcea9e78f35a1bf3499a831b10b86c90" + "aac01cd84b67a0109b55a36e9328b1e3" + "65fce161d71ce7131a543ea4cb5f7e9f" + "1d8b00696447001400", + "0c3e544074ec63b0265e0c", + "1f0a8888ce25e8d458a21130879b840a" + "9089d999aaba039eaf3e3afa090a09d3" + "89dba82c4ff2ae8ac5cdfb7c55e94d5d" + "961a29fe0109941e00b8dbdeea6d3b05" + "1068df7254c0cdc129cbe62db2dc957d" + "bb47b51fd3f213fb8698f064774250a5" + "028961c9bf8ffd973fe5d5c206492b14" + "0e00", + ), + # 12 octets + ( + generator_ed448, + "258cdd4ada32ed9c9ff54e63756ae582" + "fb8fab2ac721f2c8e676a72768513d93" + "9f63dddb55609133f29adf86ec9929dc" + "cb52c1c5fd2ff7e21b", + "3ba16da0c6f2cc1f30187740756f5e79" + "8d6bc5fc015d7c63cc9510ee3fd44adc" + "24d8e968b6e46e6f94d19b945361726b" + "d75e149ef09817f580", + "64a65f3cdedcdd66811e2915", + "7eeeab7c4e50fb799b418ee5e3197ff6" + "bf15d43a14c34389b59dd1a7b1b85b4a" + "e90438aca634bea45e3a2695f1270f07" + "fdcdf7c62b8efeaf00b45c2c96ba457e" + "b1a8bf075a3db28e5c24f6b923ed4ad7" + "47c3c9e03c7079efb87cb110d3a99861" + "e72003cbae6d6b8b827e4e6c143064ff" + "3c00", + ), + # 13 octets + ( + generator_ed448, + "7ef4e84544236752fbb56b8f31a23a10" + "e42814f5f55ca037cdcc11c64c9a3b29" + "49c1bb60700314611732a6c2fea98eeb" + "c0266a11a93970100e", + "b3da079b0aa493a5772029f0467baebe" + "e5a8112d9d3a22532361da294f7bb381" + "5c5dc59e176b4d9f381ca0938e13c6c0" + "7b174be65dfa578e80", + "64a65f3cdedcdd66811e2915e7", + "6a12066f55331b6c22acd5d5bfc5d712" + "28fbda80ae8dec26bdd306743c5027cb" + "4890810c162c027468675ecf645a8317" + "6c0d7323a2ccde2d80efe5a1268e8aca" + "1d6fbc194d3f77c44986eb4ab4177919" + "ad8bec33eb47bbb5fc6e28196fd1caf5" + "6b4e7e0ba5519234d047155ac727a105" + "3100", + ), + # 64 octets + ( + generator_ed448, + "d65df341ad13e008567688baedda8e9d" + "cdc17dc024974ea5b4227b6530e339bf" + "f21f99e68ca6968f3cca6dfe0fb9f4fa" + "b4fa135d5542ea3f01", + "df9705f58edbab802c7f8363cfe5560a" + "b1c6132c20a9f1dd163483a26f8ac53a" + "39d6808bf4a1dfbd261b099bb03b3fb5" + "0906cb28bd8a081f00", + "bd0f6a3747cd561bdddf4640a332461a" + "4a30a12a434cd0bf40d766d9c6d458e5" + "512204a30c17d1f50b5079631f64eb31" + "12182da3005835461113718d1a5ef944", + "554bc2480860b49eab8532d2a533b7d5" + "78ef473eeb58c98bb2d0e1ce488a98b1" + "8dfde9b9b90775e67f47d4a1c3482058" + "efc9f40d2ca033a0801b63d45b3b722e" + "f552bad3b4ccb667da350192b61c508c" + "f7b6b5adadc2c8d9a446ef003fb05cba" + "5f30e88e36ec2703b349ca229c267083" + "3900", + ), + # 256 octets + ( + generator_ed448, + "2ec5fe3c17045abdb136a5e6a913e32a" + "b75ae68b53d2fc149b77e504132d3756" + "9b7e766ba74a19bd6162343a21c8590a" + "a9cebca9014c636df5", + "79756f014dcfe2079f5dd9e718be4171" + "e2ef2486a08f25186f6bff43a9936b9b" + "fe12402b08ae65798a3d81e22e9ec80e" + "7690862ef3d4ed3a00", + "15777532b0bdd0d1389f636c5f6b9ba7" + "34c90af572877e2d272dd078aa1e567c" + "fa80e12928bb542330e8409f31745041" + "07ecd5efac61ae7504dabe2a602ede89" + "e5cca6257a7c77e27a702b3ae39fc769" + "fc54f2395ae6a1178cab4738e543072f" + "c1c177fe71e92e25bf03e4ecb72f47b6" + "4d0465aaea4c7fad372536c8ba516a60" + "39c3c2a39f0e4d832be432dfa9a706a6" + "e5c7e19f397964ca4258002f7c0541b5" + "90316dbc5622b6b2a6fe7a4abffd9610" + "5eca76ea7b98816af0748c10df048ce0" + "12d901015a51f189f3888145c03650aa" + "23ce894c3bd889e030d565071c59f409" + "a9981b51878fd6fc110624dcbcde0bf7" + "a69ccce38fabdf86f3bef6044819de11", + "c650ddbb0601c19ca11439e1640dd931" + "f43c518ea5bea70d3dcde5f4191fe53f" + "00cf966546b72bcc7d58be2b9badef28" + "743954e3a44a23f880e8d4f1cfce2d7a" + "61452d26da05896f0a50da66a239a8a1" + "88b6d825b3305ad77b73fbac0836ecc6" + "0987fd08527c1a8e80d5823e65cafe2a" + "3d00", + ), + # 1023 octets + ( + generator_ed448, + "872d093780f5d3730df7c212664b37b8" + "a0f24f56810daa8382cd4fa3f77634ec" + "44dc54f1c2ed9bea86fafb7632d8be19" + "9ea165f5ad55dd9ce8", + "a81b2e8a70a5ac94ffdbcc9badfc3feb" + "0801f258578bb114ad44ece1ec0e799d" + "a08effb81c5d685c0c56f64eecaef8cd" + "f11cc38737838cf400", + "6ddf802e1aae4986935f7f981ba3f035" + "1d6273c0a0c22c9c0e8339168e675412" + "a3debfaf435ed651558007db4384b650" + "fcc07e3b586a27a4f7a00ac8a6fec2cd" + "86ae4bf1570c41e6a40c931db27b2faa" + "15a8cedd52cff7362c4e6e23daec0fbc" + "3a79b6806e316efcc7b68119bf46bc76" + "a26067a53f296dafdbdc11c77f7777e9" + "72660cf4b6a9b369a6665f02e0cc9b6e" + "dfad136b4fabe723d2813db3136cfde9" + "b6d044322fee2947952e031b73ab5c60" + "3349b307bdc27bc6cb8b8bbd7bd32321" + "9b8033a581b59eadebb09b3c4f3d2277" + "d4f0343624acc817804728b25ab79717" + "2b4c5c21a22f9c7839d64300232eb66e" + "53f31c723fa37fe387c7d3e50bdf9813" + "a30e5bb12cf4cd930c40cfb4e1fc6225" + "92a49588794494d56d24ea4b40c89fc0" + "596cc9ebb961c8cb10adde976a5d602b" + "1c3f85b9b9a001ed3c6a4d3b1437f520" + "96cd1956d042a597d561a596ecd3d173" + "5a8d570ea0ec27225a2c4aaff26306d1" + "526c1af3ca6d9cf5a2c98f47e1c46db9" + "a33234cfd4d81f2c98538a09ebe76998" + "d0d8fd25997c7d255c6d66ece6fa56f1" + "1144950f027795e653008f4bd7ca2dee" + "85d8e90f3dc315130ce2a00375a318c7" + "c3d97be2c8ce5b6db41a6254ff264fa6" + "155baee3b0773c0f497c573f19bb4f42" + "40281f0b1f4f7be857a4e59d416c06b4" + "c50fa09e1810ddc6b1467baeac5a3668" + "d11b6ecaa901440016f389f80acc4db9" + "77025e7f5924388c7e340a732e554440" + "e76570f8dd71b7d640b3450d1fd5f041" + "0a18f9a3494f707c717b79b4bf75c984" + "00b096b21653b5d217cf3565c9597456" + "f70703497a078763829bc01bb1cbc8fa" + "04eadc9a6e3f6699587a9e75c94e5bab" + "0036e0b2e711392cff0047d0d6b05bd2" + "a588bc109718954259f1d86678a579a3" + "120f19cfb2963f177aeb70f2d4844826" + "262e51b80271272068ef5b3856fa8535" + "aa2a88b2d41f2a0e2fda7624c2850272" + "ac4a2f561f8f2f7a318bfd5caf969614" + "9e4ac824ad3460538fdc25421beec2cc" + "6818162d06bbed0c40a387192349db67" + "a118bada6cd5ab0140ee273204f628aa" + "d1c135f770279a651e24d8c14d75a605" + "9d76b96a6fd857def5e0b354b27ab937" + "a5815d16b5fae407ff18222c6d1ed263" + "be68c95f32d908bd895cd76207ae7264" + "87567f9a67dad79abec316f683b17f2d" + "02bf07e0ac8b5bc6162cf94697b3c27c" + "d1fea49b27f23ba2901871962506520c" + "392da8b6ad0d99f7013fbc06c2c17a56" + "9500c8a7696481c1cd33e9b14e40b82e" + "79a5f5db82571ba97bae3ad3e0479515" + "bb0e2b0f3bfcd1fd33034efc6245eddd" + "7ee2086ddae2600d8ca73e214e8c2b0b" + "db2b047c6a464a562ed77b73d2d841c4" + "b34973551257713b753632efba348169" + "abc90a68f42611a40126d7cb21b58695" + "568186f7e569d2ff0f9e745d0487dd2e" + "b997cafc5abf9dd102e62ff66cba87", + "e301345a41a39a4d72fff8df69c98075" + "a0cc082b802fc9b2b6bc503f926b65bd" + "df7f4c8f1cb49f6396afc8a70abe6d8a" + "ef0db478d4c6b2970076c6a0484fe76d" + "76b3a97625d79f1ce240e7c576750d29" + "5528286f719b413de9ada3e8eb78ed57" + "3603ce30d8bb761785dc30dbc320869e" + "1a00", + ), +] + + + "generator,private_key,public_key,message,signature", + TEST_VECTORS, +) +def test_vectors(generator, private_key, public_key, message, signature): + private_key = a2b_hex(private_key) + public_key = a2b_hex(public_key) + message = a2b_hex(message) + signature = a2b_hex(signature) + + sig_key = PrivateKey(generator, private_key) + ver_key = PublicKey(generator, public_key) + + assert sig_key.public_key().public_key() == ver_key.public_key() + + gen_sig = sig_key.sign(message) + + assert gen_sig == signature + + assert ver_key.verify(message, signature) diff --git a/frozen_deps/ecdsa/test_ellipticcurve.py b/frozen_deps/ecdsa/test_ellipticcurve.py index def53b2..85faef4 100644 --- a/frozen_deps/ecdsa/test_ellipticcurve.py +++ b/frozen_deps/ecdsa/test_ellipticcurve.py @@ -1,5 +1,4 @@ import pytest -from six import print_ try: import unittest2 as unittest @@ -96,7 +95,7 @@ class TestCurve(unittest.TestCase): self.assertEqual(len(set((c_23, eq1, eq2, eq3))), 1) self.assertEqual(len(set((c_23, ne1, ne2, ne3))), 4) self.assertDictEqual({c_23: None}, {eq1: None}) - self.assertTrue(eq2 in {eq3: None}) + self.assertIn(eq2, {eq3: None}) class TestPoint(unittest.TestCase): diff --git a/frozen_deps/ecdsa/test_jacobi.py b/frozen_deps/ecdsa/test_jacobi.py index 43ed6c1..1f52804 100644 --- a/frozen_deps/ecdsa/test_jacobi.py +++ b/frozen_deps/ecdsa/test_jacobi.py @@ -5,18 +5,31 @@ try: except ImportError: import unittest +import os +import sys +import signal +import pytest +import threading +import platform import hypothesis.strategies as st from hypothesis import given, assume, settings, example -from .ellipticcurve import CurveFp, Point, PointJacobi, INFINITY +from .ellipticcurve import CurveFp, PointJacobi, INFINITY from .ecdsa import ( generator_256, curve_256, generator_224, generator_brainpoolp160r1, curve_brainpoolp160r1, + generator_112r2, ) from .numbertheory import inverse_mod +from .util import randrange + + +NO_OLD_SETTINGS = {} +if sys.version_info > (2, 7): # pragma: no branch + NO_OLD_SETTINGS["deadline"] = 5000 class TestJacobi(unittest.TestCase): @@ -210,7 +223,8 @@ class TestJacobi(unittest.TestCase): @example(0) @example(int(generator_brainpoolp160r1.order())) def test_precompute(self, mul): - precomp = PointJacobi.from_affine(generator_brainpoolp160r1, True) + precomp = generator_brainpoolp160r1 + self.assertTrue(precomp._PointJacobi__precompute) pj = PointJacobi.from_affine(generator_brainpoolp160r1) a = precomp * mul @@ -311,6 +325,29 @@ class TestJacobi(unittest.TestCase): self.assertEqual(c, j_g * (a_mul + b_mul)) + def test_add_same_scale_points_static(self): + j_g = generator_brainpoolp160r1 + p = curve_brainpoolp160r1.p() + a = j_g * 11 + a.scale() + z1 = 13 + x = PointJacobi( + curve_brainpoolp160r1, + a.x() * z1**2 % p, + a.y() * z1**3 % p, + z1, + ) + y = PointJacobi( + curve_brainpoolp160r1, + a.x() * z1**2 % p, + a.y() * z1**3 % p, + z1, + ) + + c = a + a + + self.assertEqual(c, x + y) + @settings(max_examples=14) @given( st.integers( @@ -362,11 +399,40 @@ class TestJacobi(unittest.TestCase): self.assertEqual(c, j_g * (a_mul + b_mul)) + def test_add_different_scale_points_static(self): + j_g = generator_brainpoolp160r1 + p = curve_brainpoolp160r1.p() + a = j_g * 11 + a.scale() + z1 = 13 + x = PointJacobi( + curve_brainpoolp160r1, + a.x() * z1**2 % p, + a.y() * z1**3 % p, + z1, + ) + z2 = 29 + y = PointJacobi( + curve_brainpoolp160r1, + a.x() * z2**2 % p, + a.y() * z2**3 % p, + z2, + ) + + c = a + a + + self.assertEqual(c, x + y) + def test_add_point_3_times(self): j_g = PointJacobi.from_affine(generator_256) self.assertEqual(j_g * 3, j_g + j_g + j_g) + def test_mul_without_order(self): + j_g = PointJacobi(curve_256, generator_256.x(), generator_256.y(), 1) + + self.assertEqual(j_g * generator_256.order(), INFINITY) + def test_mul_add_inf(self): j_g = PointJacobi.from_affine(generator_256) @@ -378,7 +444,7 @@ class TestJacobi(unittest.TestCase): self.assertEqual(j_g * 2, j_g.mul_add(1, j_g, 1)) def test_mul_add_precompute(self): - j_g = PointJacobi.from_affine(generator_256, True) + j_g = PointJacobi.from_affine(generator_brainpoolp160r1, True) b = PointJacobi.from_affine(j_g * 255, True) self.assertEqual(j_g * 256, j_g + b) @@ -386,7 +452,7 @@ class TestJacobi(unittest.TestCase): self.assertEqual(j_g * (5 + 255 * 7), j_g.mul_add(5, b, 7)) def test_mul_add_precompute_large(self): - j_g = PointJacobi.from_affine(generator_256, True) + j_g = PointJacobi.from_affine(generator_brainpoolp160r1, True) b = PointJacobi.from_affine(j_g * 255, True) self.assertEqual(j_g * 256, j_g + b) @@ -405,6 +471,21 @@ class TestJacobi(unittest.TestCase): self.assertEqual(a, b) + def test_mul_add_differnt(self): + j_g = PointJacobi.from_affine(generator_256) + + w_a = j_g * 2 + + self.assertEqual(j_g.mul_add(1, w_a, 1), j_g * 3) + + def test_mul_add_slightly_different(self): + j_g = PointJacobi.from_affine(generator_256) + + w_a = j_g * 2 + w_b = j_g * 3 + + self.assertEqual(w_a.mul_add(1, w_b, 3), w_a * 1 + w_b * 3) + def test_mul_add(self): j_g = PointJacobi.from_affine(generator_256) @@ -428,11 +509,149 @@ class TestJacobi(unittest.TestCase): j_g * (0xFF00 + 255 * 0xF0F0), j_g.mul_add(0xFF00, b, 0xF0F0) ) + def test_mul_add_with_infinity_as_result(self): + j_g = PointJacobi.from_affine(generator_256) + + order = generator_256.order() + + b = PointJacobi.from_affine(generator_256 * 256) + + self.assertEqual(j_g.mul_add(order % 256, b, order // 256), INFINITY) + + def test_mul_add_without_order(self): + j_g = PointJacobi(curve_256, generator_256.x(), generator_256.y(), 1) + + order = generator_256.order() + + w_b = generator_256 * 34 + w_b.scale() + + b = PointJacobi(curve_256, w_b.x(), w_b.y(), 1) + + self.assertEqual(j_g.mul_add(order % 34, b, order // 34), INFINITY) + + def test_mul_add_with_doubled_negation_of_itself(self): + j_g = PointJacobi.from_affine(generator_256 * 17) + + dbl_neg = 2 * (-j_g) + + self.assertEqual(j_g.mul_add(4, dbl_neg, 2), INFINITY) + def test_equality(self): pj1 = PointJacobi(curve=CurveFp(23, 1, 1, 1), x=2, y=3, z=1, order=1) pj2 = PointJacobi(curve=CurveFp(23, 1, 1, 1), x=2, y=3, z=1, order=1) self.assertEqual(pj1, pj2) + def test_equality_with_invalid_object(self): + j_g = PointJacobi.from_affine(generator_256) + + self.assertNotEqual(j_g, 12) + + def test_equality_with_wrong_curves(self): + p_a = PointJacobi.from_affine(generator_256) + p_b = PointJacobi.from_affine(generator_224) + + self.assertNotEqual(p_a, p_b) + def test_pickle(self): pj = PointJacobi(curve=CurveFp(23, 1, 1, 1), x=2, y=3, z=1, order=1) self.assertEqual(pickle.loads(pickle.dumps(pj)), pj) + + @settings(**NO_OLD_SETTINGS) + @given(st.integers(min_value=1, max_value=10)) + def test_multithreading(self, thread_num): + # ensure that generator's precomputation table is filled + generator_112r2 * 2 + + # create a fresh point that doesn't have a filled precomputation table + gen = generator_112r2 + gen = PointJacobi(gen.curve(), gen.x(), gen.y(), 1, gen.order(), True) + + self.assertEqual(gen._PointJacobi__precompute, []) + + def runner(generator): + order = generator.order() + for _ in range(10): + generator * randrange(order) + + threads = [] + for _ in range(thread_num): + threads.append(threading.Thread(target=runner, args=(gen,))) + + for t in threads: + t.start() + + runner(gen) + + for t in threads: + t.join() + + self.assertEqual( + gen._PointJacobi__precompute, + generator_112r2._PointJacobi__precompute, + ) + + @pytest.mark.skipif( + platform.system() == "Windows", + reason="there are no signals on Windows", + ) + def test_multithreading_with_interrupts(self): + thread_num = 10 + # ensure that generator's precomputation table is filled + generator_112r2 * 2 + + # create a fresh point that doesn't have a filled precomputation table + gen = generator_112r2 + gen = PointJacobi(gen.curve(), gen.x(), gen.y(), 1, gen.order(), True) + + self.assertEqual(gen._PointJacobi__precompute, []) + + def runner(generator): + order = generator.order() + for _ in range(50): + generator * randrange(order) + + def interrupter(barrier_start, barrier_end, lock_exit): + # wait until MainThread can handle KeyboardInterrupt + barrier_start.release() + barrier_end.acquire() + os.kill(os.getpid(), signal.SIGINT) + lock_exit.release() + + threads = [] + for _ in range(thread_num): + threads.append(threading.Thread(target=runner, args=(gen,))) + + barrier_start = threading.Lock() + barrier_start.acquire() + barrier_end = threading.Lock() + barrier_end.acquire() + lock_exit = threading.Lock() + lock_exit.acquire() + + threads.append( + threading.Thread( + target=interrupter, + args=(barrier_start, barrier_end, lock_exit), + ) + ) + + for t in threads: + t.start() + + with self.assertRaises(KeyboardInterrupt): + # signal to interrupter that we can now handle the signal + barrier_start.acquire() + barrier_end.release() + runner(gen) + # use the lock to ensure we never go past the scope of + # assertRaises before the os.kill is called + lock_exit.acquire() + + for t in threads: + t.join() + + self.assertEqual( + gen._PointJacobi__precompute, + generator_112r2._PointJacobi__precompute, + ) diff --git a/frozen_deps/ecdsa/test_keys.py b/frozen_deps/ecdsa/test_keys.py index 406a5bf..25386b1 100644 --- a/frozen_deps/ecdsa/test_keys.py +++ b/frozen_deps/ecdsa/test_keys.py @@ -8,14 +8,19 @@ try: except NameError: buffer = memoryview +import os import array -import six -import sys import pytest import hashlib -from .keys import VerifyingKey, SigningKey -from .der import unpem +from .keys import VerifyingKey, SigningKey, MalformedPointError +from .der import ( + unpem, + UnexpectedDER, + encode_sequence, + encode_oid, + encode_bitstring, +) from .util import ( sigencode_string, sigencode_der, @@ -24,7 +29,9 @@ from .util import ( sigdecode_der, sigdecode_strings, ) -from .curves import NIST256p +from .curves import NIST256p, Curve, BRAINPOOLP160r1, Ed25519, Ed448 +from .ellipticcurve import Point, PointJacobi, CurveFp, INFINITY +from .ecdsa import generator_brainpoolp160r1 class TestVerifyingKeyFromString(unittest.TestCase): @@ -150,6 +157,55 @@ class TestVerifyingKeyFromDer(unittest.TestCase): ) cls.vk2 = VerifyingKey.from_pem(key_str) + cls.sk2 = SigningKey.generate(vk.curve) + + def test_load_key_with_explicit_parameters(self): + pub_key_str = ( + "-----BEGIN PUBLIC KEY-----\n" + "MIIBSzCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAA\n" + "AAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA////\n" + "///////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSd\n" + "NgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5\n" + "RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA\n" + "//////////+85vqtpxeehPO5ysL8YyVRAgEBA0IABIr1UkgYs5jmbFc7it1/YI2X\n" + "T//IlaEjMNZft1owjqpBYH2ErJHk4U5Pp4WvWq1xmHwIZlsH7Ig4KmefCfR6SmU=\n" + "-----END PUBLIC KEY-----" + ) + pk = VerifyingKey.from_pem(pub_key_str) + + pk_exp = VerifyingKey.from_string( + b"\x04\x8a\xf5\x52\x48\x18\xb3\x98\xe6\x6c\x57\x3b\x8a\xdd\x7f" + b"\x60\x8d\x97\x4f\xff\xc8\x95\xa1\x23\x30\xd6\x5f\xb7\x5a\x30" + b"\x8e\xaa\x41\x60\x7d\x84\xac\x91\xe4\xe1\x4e\x4f\xa7\x85\xaf" + b"\x5a\xad\x71\x98\x7c\x08\x66\x5b\x07\xec\x88\x38\x2a\x67\x9f" + b"\x09\xf4\x7a\x4a\x65", + curve=NIST256p, + ) + self.assertEqual(pk, pk_exp) + + def test_load_key_with_explicit_with_explicit_disabled(self): + pub_key_str = ( + "-----BEGIN PUBLIC KEY-----\n" + "MIIBSzCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAA\n" + "AAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA////\n" + "///////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSd\n" + "NgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5\n" + "RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA\n" + "//////////+85vqtpxeehPO5ysL8YyVRAgEBA0IABIr1UkgYs5jmbFc7it1/YI2X\n" + "T//IlaEjMNZft1owjqpBYH2ErJHk4U5Pp4WvWq1xmHwIZlsH7Ig4KmefCfR6SmU=\n" + "-----END PUBLIC KEY-----" + ) + with self.assertRaises(UnexpectedDER): + VerifyingKey.from_pem( + pub_key_str, valid_curve_encodings=["named_curve"] + ) + + def test_load_key_with_disabled_format(self): + with self.assertRaises(MalformedPointError) as e: + VerifyingKey.from_der(self.key_bytes, valid_encodings=["raw"]) + + self.assertIn("enabled (raw) encodings", str(e.exception)) + def test_custom_hashfunc(self): vk = VerifyingKey.from_der(self.key_bytes, hashlib.sha256) @@ -201,6 +257,218 @@ class TestVerifyingKeyFromDer(unittest.TestCase): def test_inequality_on_verifying_keys_not_implemented(self): self.assertNotEqual(self.vk, None) + def test_VerifyingKey_inequality_on_same_curve(self): + self.assertNotEqual(self.vk, self.sk2.verifying_key) + + def test_SigningKey_inequality_on_same_curve(self): + self.assertNotEqual(self.sk, self.sk2) + + def test_inequality_on_wrong_types(self): + self.assertNotEqual(self.vk, self.sk) + + def test_from_public_point_old(self): + pj = self.vk.pubkey.point + point = Point(pj.curve(), pj.x(), pj.y()) + + vk = VerifyingKey.from_public_point(point, self.vk.curve) + + self.assertEqual(vk, self.vk) + + def test_ed25519_VerifyingKey_repr__(self): + sk = SigningKey.from_string(Ed25519.generator.to_bytes(), Ed25519) + string = repr(sk.verifying_key) + + self.assertEqual( + "VerifyingKey.from_string(" + "bytearray(b'K\\x0c\\xfbZH\\x8e\\x8c\\x8c\\x07\\xee\\xda\\xfb" + "\\xe1\\x97\\xcd\\x90\\x18\\x02\\x15h]\\xfe\\xbe\\xcbB\\xba\\xe6r" + "\\x10\\xae\\xf1P'), Ed25519, None)", + string, + ) + + def test_edwards_from_public_point(self): + point = Ed25519.generator + with self.assertRaises(ValueError) as e: + VerifyingKey.from_public_point(point, Ed25519) + + self.assertIn("incompatible with Edwards", str(e.exception)) + + def test_edwards_precompute_no_side_effect(self): + sk = SigningKey.from_string(Ed25519.generator.to_bytes(), Ed25519) + vk = sk.verifying_key + vk2 = VerifyingKey.from_string(vk.to_string(), Ed25519) + vk.precompute() + + self.assertEqual(vk, vk2) + + def test_parse_malfomed_eddsa_der_pubkey(self): + der_str = encode_sequence( + encode_sequence(encode_oid(*Ed25519.oid)), + encode_bitstring(bytes(Ed25519.generator.to_bytes()), 0), + encode_bitstring(b"\x00", 0), + ) + + with self.assertRaises(UnexpectedDER) as e: + VerifyingKey.from_der(der_str) + + self.assertIn("trailing junk after public key", str(e.exception)) + + def test_edwards_from_public_key_recovery(self): + with self.assertRaises(ValueError) as e: + VerifyingKey.from_public_key_recovery(b"", b"", Ed25519) + + self.assertIn("unsupported for Edwards", str(e.exception)) + + def test_edwards_from_public_key_recovery_with_digest(self): + with self.assertRaises(ValueError) as e: + VerifyingKey.from_public_key_recovery_with_digest( + b"", b"", Ed25519 + ) + + self.assertIn("unsupported for Edwards", str(e.exception)) + + def test_load_ed25519_from_pem(self): + vk_pem = ( + "-----BEGIN PUBLIC KEY-----\n" + "MCowBQYDK2VwAyEAIwBQ0NZkIiiO41WJfm5BV42u3kQm7lYnvIXmCy8qy2U=\n" + "-----END PUBLIC KEY-----\n" + ) + + vk = VerifyingKey.from_pem(vk_pem) + + self.assertIsInstance(vk.curve, Curve) + self.assertIs(vk.curve, Ed25519) + + vk_str = ( + b"\x23\x00\x50\xd0\xd6\x64\x22\x28\x8e\xe3\x55\x89\x7e\x6e\x41\x57" + b"\x8d\xae\xde\x44\x26\xee\x56\x27\xbc\x85\xe6\x0b\x2f\x2a\xcb\x65" + ) + + vk_2 = VerifyingKey.from_string(vk_str, Ed25519) + + self.assertEqual(vk, vk_2) + + def test_export_ed255_to_pem(self): + vk_str = ( + b"\x23\x00\x50\xd0\xd6\x64\x22\x28\x8e\xe3\x55\x89\x7e\x6e\x41\x57" + b"\x8d\xae\xde\x44\x26\xee\x56\x27\xbc\x85\xe6\x0b\x2f\x2a\xcb\x65" + ) + + vk = VerifyingKey.from_string(vk_str, Ed25519) + + vk_pem = ( + b"-----BEGIN PUBLIC KEY-----\n" + b"MCowBQYDK2VwAyEAIwBQ0NZkIiiO41WJfm5BV42u3kQm7lYnvIXmCy8qy2U=\n" + b"-----END PUBLIC KEY-----\n" + ) + + self.assertEqual(vk_pem, vk.to_pem()) + + def test_ed25519_export_import(self): + sk = SigningKey.generate(Ed25519) + vk = sk.verifying_key + + vk2 = VerifyingKey.from_pem(vk.to_pem()) + + self.assertEqual(vk, vk2) + + def test_ed25519_sig_verify(self): + vk_pem = ( + "-----BEGIN PUBLIC KEY-----\n" + "MCowBQYDK2VwAyEAIwBQ0NZkIiiO41WJfm5BV42u3kQm7lYnvIXmCy8qy2U=\n" + "-----END PUBLIC KEY-----\n" + ) + + vk = VerifyingKey.from_pem(vk_pem) + + data = b"data\n" + + # signature created by OpenSSL 3.0.0 beta1 + sig = ( + b"\x64\x47\xab\x6a\x33\xcd\x79\x45\xad\x98\x11\x6c\xb9\xf2\x20\xeb" + b"\x90\xd6\x50\xe3\xc7\x8f\x9f\x60\x10\xec\x75\xe0\x2f\x27\xd3\x96" + b"\xda\xe8\x58\x7f\xe0\xfe\x46\x5c\x81\xef\x50\xec\x29\x9f\xae\xd5" + b"\xad\x46\x3c\x91\x68\x83\x4d\xea\x8d\xa8\x19\x04\x04\x79\x03\x0b" + ) + + self.assertTrue(vk.verify(sig, data)) + + def test_ed448_from_pem(self): + pem_str = ( + "-----BEGIN PUBLIC KEY-----\n" + "MEMwBQYDK2VxAzoAeQtetSu7CMEzE+XWB10Bg47LCA0giNikOxHzdp+tZ/eK/En0\n" + "dTdYD2ll94g58MhSnBiBQB9A1MMA\n" + "-----END PUBLIC KEY-----\n" + ) + + vk = VerifyingKey.from_pem(pem_str) + + self.assertIsInstance(vk.curve, Curve) + self.assertIs(vk.curve, Ed448) + + vk_str = ( + b"\x79\x0b\x5e\xb5\x2b\xbb\x08\xc1\x33\x13\xe5\xd6\x07\x5d\x01\x83" + b"\x8e\xcb\x08\x0d\x20\x88\xd8\xa4\x3b\x11\xf3\x76\x9f\xad\x67\xf7" + b"\x8a\xfc\x49\xf4\x75\x37\x58\x0f\x69\x65\xf7\x88\x39\xf0\xc8\x52" + b"\x9c\x18\x81\x40\x1f\x40\xd4\xc3\x00" + ) + + vk2 = VerifyingKey.from_string(vk_str, Ed448) + + self.assertEqual(vk, vk2) + + def test_ed448_to_pem(self): + vk_str = ( + b"\x79\x0b\x5e\xb5\x2b\xbb\x08\xc1\x33\x13\xe5\xd6\x07\x5d\x01\x83" + b"\x8e\xcb\x08\x0d\x20\x88\xd8\xa4\x3b\x11\xf3\x76\x9f\xad\x67\xf7" + b"\x8a\xfc\x49\xf4\x75\x37\x58\x0f\x69\x65\xf7\x88\x39\xf0\xc8\x52" + b"\x9c\x18\x81\x40\x1f\x40\xd4\xc3\x00" + ) + vk = VerifyingKey.from_string(vk_str, Ed448) + + vk_pem = ( + b"-----BEGIN PUBLIC KEY-----\n" + b"MEMwBQYDK2VxAzoAeQtetSu7CMEzE+XWB10Bg47LCA0giNikOxHzdp+tZ/eK/En0\n" + b"dTdYD2ll94g58MhSnBiBQB9A1MMA\n" + b"-----END PUBLIC KEY-----\n" + ) + + self.assertEqual(vk_pem, vk.to_pem()) + + def test_ed448_export_import(self): + sk = SigningKey.generate(Ed448) + vk = sk.verifying_key + + vk2 = VerifyingKey.from_pem(vk.to_pem()) + + self.assertEqual(vk, vk2) + + def test_ed448_sig_verify(self): + pem_str = ( + "-----BEGIN PUBLIC KEY-----\n" + "MEMwBQYDK2VxAzoAeQtetSu7CMEzE+XWB10Bg47LCA0giNikOxHzdp+tZ/eK/En0\n" + "dTdYD2ll94g58MhSnBiBQB9A1MMA\n" + "-----END PUBLIC KEY-----\n" + ) + + vk = VerifyingKey.from_pem(pem_str) + + data = b"data\n" + + # signature created by OpenSSL 3.0.0 beta1 + sig = ( + b"\x68\xed\x2c\x70\x35\x22\xca\x1c\x35\x03\xf3\xaa\x51\x33\x3d\x00" + b"\xc0\xae\xb0\x54\xc5\xdc\x7f\x6f\x30\x57\xb4\x1d\xcb\xe9\xec\xfa" + b"\xc8\x45\x3e\x51\xc1\xcb\x60\x02\x6a\xd0\x43\x11\x0b\x5f\x9b\xfa" + b"\x32\x88\xb2\x38\x6b\xed\xac\x09\x00\x78\xb1\x7b\x5d\x7e\xf8\x16" + b"\x31\xdd\x1b\x3f\x98\xa0\xce\x19\xe7\xd8\x1c\x9f\x30\xac\x2f\xd4" + b"\x1e\x55\xbf\x21\x98\xf6\x4c\x8c\xbe\x81\xa5\x2d\x80\x4c\x62\x53" + b"\x91\xd5\xee\x03\x30\xc6\x17\x66\x4b\x9e\x0c\x8d\x40\xd0\xad\xae" + b"\x0a\x00" + ) + + self.assertTrue(vk.verify(sig, data)) + class TestSigningKey(unittest.TestCase): """ @@ -237,6 +505,50 @@ class TestSigningKey(unittest.TestCase): ) cls.sk2 = SigningKey.from_pem(prv_key_str) + def test_decoding_explicit_curve_parameters(self): + prv_key_str = ( + "-----BEGIN PRIVATE KEY-----\n" + "MIIBeQIBADCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAAB\n" + "AAAAAAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA\n" + "///////////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMV\n" + "AMSdNgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg\n" + "9KE5RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8A\n" + "AAAA//////////+85vqtpxeehPO5ysL8YyVRAgEBBG0wawIBAQQgIXtREfUmR16r\n" + "ZbmvDGD2lAEFPZa2DLPyz0czSja58yChRANCAASK9VJIGLOY5mxXO4rdf2CNl0//\n" + "yJWhIzDWX7daMI6qQWB9hKyR5OFOT6eFr1qtcZh8CGZbB+yIOCpnnwn0ekpl\n" + "-----END PRIVATE KEY-----\n" + ) + + sk = SigningKey.from_pem(prv_key_str) + + sk2 = SigningKey.from_string( + b"\x21\x7b\x51\x11\xf5\x26\x47\x5e\xab\x65\xb9\xaf\x0c\x60\xf6" + b"\x94\x01\x05\x3d\x96\xb6\x0c\xb3\xf2\xcf\x47\x33\x4a\x36\xb9" + b"\xf3\x20", + curve=NIST256p, + ) + + self.assertEqual(sk, sk2) + + def test_decoding_explicit_curve_parameters_with_explicit_disabled(self): + prv_key_str = ( + "-----BEGIN PRIVATE KEY-----\n" + "MIIBeQIBADCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAAB\n" + "AAAAAAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA\n" + "///////////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMV\n" + "AMSdNgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg\n" + "9KE5RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8A\n" + "AAAA//////////+85vqtpxeehPO5ysL8YyVRAgEBBG0wawIBAQQgIXtREfUmR16r\n" + "ZbmvDGD2lAEFPZa2DLPyz0czSja58yChRANCAASK9VJIGLOY5mxXO4rdf2CNl0//\n" + "yJWhIzDWX7daMI6qQWB9hKyR5OFOT6eFr1qtcZh8CGZbB+yIOCpnnwn0ekpl\n" + "-----END PRIVATE KEY-----\n" + ) + + with self.assertRaises(UnexpectedDER): + SigningKey.from_pem( + prv_key_str, valid_curve_encodings=["named_curve"] + ) + def test_equality_on_signing_keys(self): sk = SigningKey.from_secret_exponent( self.sk1.privkey.secret_multiplier, self.sk1.curve @@ -244,6 +556,15 @@ class TestSigningKey(unittest.TestCase): self.assertEqual(self.sk1, sk) self.assertEqual(self.sk1_pkcs8, sk) + def test_verify_with_empty_message(self): + sig = self.sk1.sign(b"") + + self.assertTrue(sig) + + vk = self.sk1.verifying_key + + self.assertTrue(vk.verify(sig, b"")) + def test_verify_with_precompute(self): sig = self.sk1.sign(b"message") @@ -276,6 +597,143 @@ class TestSigningKey(unittest.TestCase): def test_inequality_on_signing_keys_not_implemented(self): self.assertNotEqual(self.sk1, None) + def test_ed25519_from_pem(self): + pem_str = ( + "-----BEGIN PRIVATE KEY-----\n" + "MC4CAQAwBQYDK2VwBCIEIDS6x9FO1PG8T4xIPg8Zd0z8uL6sVGZFEZrX17gHC/XU\n" + "-----END PRIVATE KEY-----\n" + ) + + sk = SigningKey.from_pem(pem_str) + + sk_str = SigningKey.from_string( + b"\x34\xBA\xC7\xD1\x4E\xD4\xF1\xBC\x4F\x8C\x48\x3E\x0F\x19\x77\x4C" + b"\xFC\xB8\xBE\xAC\x54\x66\x45\x11\x9A\xD7\xD7\xB8\x07\x0B\xF5\xD4", + Ed25519, + ) + + self.assertEqual(sk, sk_str) + + def test_ed25519_to_pem(self): + sk = SigningKey.from_string( + b"\x34\xBA\xC7\xD1\x4E\xD4\xF1\xBC\x4F\x8C\x48\x3E\x0F\x19\x77\x4C" + b"\xFC\xB8\xBE\xAC\x54\x66\x45\x11\x9A\xD7\xD7\xB8\x07\x0B\xF5\xD4", + Ed25519, + ) + + pem_str = ( + b"-----BEGIN PRIVATE KEY-----\n" + b"MC4CAQAwBQYDK2VwBCIEIDS6x9FO1PG8T4xIPg8Zd0z8uL6sVGZFEZrX17gHC/XU\n" + b"-----END PRIVATE KEY-----\n" + ) + + self.assertEqual(sk.to_pem(format="pkcs8"), pem_str) + + def test_ed25519_to_and_from_pem(self): + sk = SigningKey.generate(Ed25519) + + decoded = SigningKey.from_pem(sk.to_pem(format="pkcs8")) + + self.assertEqual(sk, decoded) + + def test_ed448_from_pem(self): + pem_str = ( + "-----BEGIN PRIVATE KEY-----\n" + "MEcCAQAwBQYDK2VxBDsEOTyFuXqFLXgJlV8uDqcOw9nG4IqzLiZ/i5NfBDoHPzmP\n" + "OP0JMYaLGlTzwovmvCDJ2zLaezu9NLz9aQ==\n" + "-----END PRIVATE KEY-----\n" + ) + sk = SigningKey.from_pem(pem_str) + + sk_str = SigningKey.from_string( + b"\x3C\x85\xB9\x7A\x85\x2D\x78\x09\x95\x5F\x2E\x0E\xA7\x0E\xC3\xD9" + b"\xC6\xE0\x8A\xB3\x2E\x26\x7F\x8B\x93\x5F\x04\x3A\x07\x3F\x39\x8F" + b"\x38\xFD\x09\x31\x86\x8B\x1A\x54\xF3\xC2\x8B\xE6\xBC\x20\xC9\xDB" + b"\x32\xDA\x7B\x3B\xBD\x34\xBC\xFD\x69", + Ed448, + ) + + self.assertEqual(sk, sk_str) + + def test_ed448_to_pem(self): + sk = SigningKey.from_string( + b"\x3C\x85\xB9\x7A\x85\x2D\x78\x09\x95\x5F\x2E\x0E\xA7\x0E\xC3\xD9" + b"\xC6\xE0\x8A\xB3\x2E\x26\x7F\x8B\x93\x5F\x04\x3A\x07\x3F\x39\x8F" + b"\x38\xFD\x09\x31\x86\x8B\x1A\x54\xF3\xC2\x8B\xE6\xBC\x20\xC9\xDB" + b"\x32\xDA\x7B\x3B\xBD\x34\xBC\xFD\x69", + Ed448, + ) + pem_str = ( + b"-----BEGIN PRIVATE KEY-----\n" + b"MEcCAQAwBQYDK2VxBDsEOTyFuXqFLXgJlV8uDqcOw9nG4IqzLiZ/i5NfBDoHPzmP\n" + b"OP0JMYaLGlTzwovmvCDJ2zLaezu9NLz9aQ==\n" + b"-----END PRIVATE KEY-----\n" + ) + + self.assertEqual(sk.to_pem(format="pkcs8"), pem_str) + + def test_ed448_encode_decode(self): + sk = SigningKey.generate(Ed448) + + decoded = SigningKey.from_pem(sk.to_pem(format="pkcs8")) + + self.assertEqual(decoded, sk) + + +class TestTrivialCurve(unittest.TestCase): + @classmethod + def setUpClass(cls): + # To test what happens with r or s in signing happens to be zero we + # need to find a scalar that creates one of the points on a curve that + # has x coordinate equal to zero. + # Even for secp112r2 curve that's non trivial so use this toy + # curve, for which we can iterate over all points quickly + curve = CurveFp(163, 84, 58) + gen = PointJacobi(curve, 2, 87, 1, 167, generator=True) + + cls.toy_curve = Curve("toy_p8", curve, gen, (1, 2, 0)) + + cls.sk = SigningKey.from_secret_exponent( + 140, + cls.toy_curve, + hashfunc=hashlib.sha1, + ) + + def test_generator_sanity(self): + gen = self.toy_curve.generator + + self.assertEqual(gen * gen.order(), INFINITY) + + def test_public_key_sanity(self): + self.assertEqual(self.sk.verifying_key.to_string(), b"\x98\x1e") + + def test_deterministic_sign(self): + sig = self.sk.sign_deterministic(b"message") + + self.assertEqual(sig, b"-.") + + self.assertTrue(self.sk.verifying_key.verify(sig, b"message")) + + def test_deterministic_sign_random_message(self): + msg = os.urandom(32) + sig = self.sk.sign_deterministic(msg) + self.assertEqual(len(sig), 2) + self.assertTrue(self.sk.verifying_key.verify(sig, msg)) + + def test_deterministic_sign_that_rises_R_zero_error(self): + # the raised RSZeroError is caught and handled internally by + # sign_deterministic methods + msg = b"\x00\x4f" + sig = self.sk.sign_deterministic(msg) + self.assertEqual(sig, b"\x36\x9e") + self.assertTrue(self.sk.verifying_key.verify(sig, msg)) + + def test_deterministic_sign_that_rises_S_zero_error(self): + msg = b"\x01\x6d" + sig = self.sk.sign_deterministic(msg) + self.assertEqual(sig, b"\x49\x6c") + self.assertTrue(self.sk.verifying_key.verify(sig, msg)) + # test VerifyingKey.verify() prv_key_str = ( @@ -452,3 +910,50 @@ def test_SigningKey_with_unlikely_value(): vk = sk.verifying_key sig = sk.sign(b"hello") assert vk.verify(sig, b"hello") + + +def test_SigningKey_with_custom_curve_old_point(): + generator = generator_brainpoolp160r1 + generator = Point( + generator.curve(), + generator.x(), + generator.y(), + generator.order(), + ) + + curve = Curve( + "BRAINPOOLP160r1", + generator.curve(), + generator, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 1), + ) + + sk = SigningKey.from_secret_exponent(12, curve) + + sk2 = SigningKey.from_secret_exponent(12, BRAINPOOLP160r1) + + assert sk.privkey == sk2.privkey + + +def test_VerifyingKey_inequality_with_different_curves(): + sk1 = SigningKey.from_secret_exponent(2, BRAINPOOLP160r1) + sk2 = SigningKey.from_secret_exponent(2, NIST256p) + + assert sk1.verifying_key != sk2.verifying_key + + +def test_VerifyingKey_inequality_with_different_secret_points(): + sk1 = SigningKey.from_secret_exponent(2, BRAINPOOLP160r1) + sk2 = SigningKey.from_secret_exponent(3, BRAINPOOLP160r1) + + assert sk1.verifying_key != sk2.verifying_key + + +def test_SigningKey_from_pem_pkcs8v2_EdDSA(): + pem = """-----BEGIN PRIVATE KEY----- + MFMCAQEwBQYDK2VwBCIEICc2F2ag1n1QP0jY+g9qWx5sDkx0s/HdNi3cSRHw+zsI + oSMDIQA+HQ2xCif8a/LMWR2m5HaCm5I2pKe/cc8OiRANMHxjKQ== + -----END PRIVATE KEY-----""" + + sk = SigningKey.from_pem(pem) + assert sk.curve == Ed25519 diff --git a/frozen_deps/ecdsa/test_malformed_sigs.py b/frozen_deps/ecdsa/test_malformed_sigs.py index 4895cea..8e1b611 100644 --- a/frozen_deps/ecdsa/test_malformed_sigs.py +++ b/frozen_deps/ecdsa/test_malformed_sigs.py @@ -13,10 +13,17 @@ except ImportError: # pragma: no cover "sha384", "sha512", ] +# skip algorithms broken by change to OpenSSL 3.0 and early versions +# of hashlib that list algorithms that require the legacy provider to work +# https://bugs.python.org/issue38820 +algorithms_available = [ + i + for i in algorithms_available + if i not in ("mdc2", "md2", "md4", "whirlpool", "ripemd160") +] from functools import partial import pytest import sys -from six import binary_type import hypothesis.strategies as st from hypothesis import note, assume, given, settings, example @@ -24,7 +31,7 @@ from .keys import SigningKey from .keys import BadSignatureError from .util import sigencode_der, sigencode_string from .util import sigdecode_der, sigdecode_string -from .curves import curves, NIST256p +from .curves import curves from .der import ( encode_integer, encode_bitstring, @@ -33,6 +40,7 @@ from .der import ( encode_sequence, encode_constructed, ) +from .ellipticcurve import CurveEdTw example_data = b"some data to sign" @@ -174,7 +182,7 @@ def st_random_der_ecdsa_sig_value(draw): note("Configuration: {0}".format(name)) order = int(verifying_key.curve.order) - # the encode_integer doesn't suport negative numbers, would be nice + # the encode_integer doesn't support negative numbers, would be nice # to generate them too, but we have coverage for remove_integer() # verifying that it doesn't accept them, so meh. # Test all numbers around the ones that can show up (around order) @@ -227,7 +235,7 @@ def st_der_bit_string(draw, *args, **kwargs): if data: unused = draw(st.integers(min_value=0, max_value=7)) data = bytearray(data) - data[-1] &= -(2 ** unused) + data[-1] &= -(2**unused) data = bytes(data) else: unused = 0 @@ -258,9 +266,9 @@ def st_der_oid(draw): if first < 2: second = draw(st.integers(min_value=0, max_value=39)) else: - second = draw(st.integers(min_value=0, max_value=2 ** 512)) + second = draw(st.integers(min_value=0, max_value=2**512)) rest = draw( - st.lists(st.integers(min_value=0, max_value=2 ** 512), max_size=50) + st.lists(st.integers(min_value=0, max_value=2**512), max_size=50) ) return encode_oid(first, second, *rest) @@ -275,9 +283,9 @@ def st_der(): """ return st.recursive( st.just(b"") - | st_der_integer(max_value=2 ** 4096) - | st_der_bit_string(max_size=1024 ** 2) - | st_der_octet_string(max_size=1024 ** 2) + | st_der_integer(max_value=2**4096) + | st_der_bit_string(max_size=1024**2) + | st_der_octet_string(max_size=1024**2) | st_der_null() | st_der_oid(), lambda children: st.builds( @@ -307,7 +315,7 @@ def test_random_der_as_signature(params, der): @settings(**params) -@given(st.sampled_from(keys_and_sigs), st.binary(max_size=1024 ** 2)) +@given(st.sampled_from(keys_and_sigs), st.binary(max_size=1024**2)) @example( keys_and_sigs[0], encode_sequence(encode_integer(0), encode_integer(0)) ) @@ -334,6 +342,7 @@ keys_and_string_sigs = [ ), ) for name, verifying_key, sig in keys_and_sigs + if not isinstance(verifying_key.curve.curve, CurveEdTw) ] """ Name of the curve+hash combination, VerifyingKey and signature as a @@ -341,6 +350,17 @@ byte string. """ +keys_and_string_sigs += [ + ( + name, + verifying_key, + sig, + ) + for name, verifying_key, sig in keys_and_sigs + if isinstance(verifying_key.curve.curve, CurveEdTw) +] + + @settings(**params) @given(st_fuzzed_sig(keys_and_string_sigs)) def test_fuzzed_string_signatures(params): diff --git a/frozen_deps/ecdsa/test_numbertheory.py b/frozen_deps/ecdsa/test_numbertheory.py index 4912c57..8bc787f 100644 --- a/frozen_deps/ecdsa/test_numbertheory.py +++ b/frozen_deps/ecdsa/test_numbertheory.py @@ -1,7 +1,5 @@ import operator -from six import print_ from functools import reduce -import operator try: import unittest2 as unittest @@ -19,6 +17,7 @@ except ImportError: # pragma: no cover HC_PRESENT = False from .numbertheory import ( SquareRootError, + JacobiError, factorization, gcd, lcm, @@ -84,11 +83,61 @@ def test_square_root_mod_prime_for_small_primes(prime): square_root_mod_prime(nonsquare, prime) +def test_square_root_mod_prime_for_2(): + a = square_root_mod_prime(1, 2) + assert a == 1 + + +def test_square_root_mod_prime_for_small_prime(): + root = square_root_mod_prime(98**2 % 101, 101) + assert root * root % 101 == 9 + + +def test_square_root_mod_prime_for_p_congruent_5(): + p = 13 + assert p % 8 == 5 + + root = square_root_mod_prime(3, p) + assert root * root % p == 3 + + +def test_square_root_mod_prime_for_p_congruent_5_large_d(): + p = 29 + assert p % 8 == 5 + + root = square_root_mod_prime(4, p) + assert root * root % p == 4 + + +class TestSquareRootModPrime(unittest.TestCase): + def test_power_of_2_p(self): + with self.assertRaises(JacobiError): + square_root_mod_prime(12, 32) + + def test_no_square(self): + with self.assertRaises(SquareRootError) as e: + square_root_mod_prime(12, 31) + + self.assertIn("no square root", str(e.exception)) + + def test_non_prime(self): + with self.assertRaises(SquareRootError) as e: + square_root_mod_prime(12, 33) + + self.assertIn("p is not prime", str(e.exception)) + + def test_non_prime_with_negative(self): + with self.assertRaises(SquareRootError) as e: + square_root_mod_prime(697 - 1, 697) + + self.assertIn("p is not prime", str(e.exception)) + + @st.composite def st_two_nums_rel_prime(draw): # 521-bit is the biggest curve we operate on, use 1024 for a bit # of breathing space - mod = draw(st.integers(min_value=2, max_value=2 ** 1024)) + mod = draw(st.integers(min_value=2, max_value=2**1024)) num = draw( st.integers(min_value=1, max_value=mod - 1).filter( lambda x: gcd(x, mod) == 1 @@ -110,7 +159,7 @@ def st_primes(draw, *args, **kwargs): @st.composite def st_num_square_prime(draw): - prime = draw(st_primes(max_value=2 ** 1024)) + prime = draw(st_primes(max_value=2**1024)) num = draw(st.integers(min_value=0, max_value=1 + prime // 2)) sq = num * num % prime return sq, prime @@ -122,7 +171,7 @@ def st_comp_with_com_fac(draw): Strategy that returns lists of numbers, all having a common factor. """ primes = draw( - st.lists(st_primes(max_value=2 ** 512), min_size=1, max_size=10) + st.lists(st_primes(max_value=2**512), min_size=1, max_size=10) ) # select random prime(s) that will make the common factor of composites com_fac_primes = draw( @@ -153,7 +202,7 @@ def st_comp_no_com_fac(draw): """ primes = draw( st.lists( - st_primes(max_value=2 ** 512), min_size=2, max_size=10, unique=True + st_primes(max_value=2**512), min_size=2, max_size=10, unique=True ) ) # first select the primes that will create the uncommon factor @@ -207,6 +256,38 @@ HYP_SLOW_SETTINGS = dict(HYP_SETTINGS) HYP_SLOW_SETTINGS["max_examples"] = 10 +class TestIsPrime(unittest.TestCase): + def test_very_small_prime(self): + assert is_prime(23) + + def test_very_small_composite(self): + assert not is_prime(22) + + def test_small_prime(self): + assert is_prime(123456791) + + def test_special_composite(self): + assert not is_prime(10261) + + def test_medium_prime_1(self): + # nextPrime[2^256] + assert is_prime(2**256 + 0x129) + + def test_medium_prime_2(self): + # nextPrime(2^256+0x129) + assert is_prime(2**256 + 0x12D) + + def test_medium_trivial_composite(self): + assert not is_prime(2**256 + 0x130) + + def test_medium_non_trivial_composite(self): + assert not is_prime(2**256 + 0x12F) + + def test_large_prime(self): + # nextPrime[2^2048] + assert is_prime(2**2048 + 0x3D5) + + class TestNumbertheory(unittest.TestCase): def test_gcd(self): assert gcd(3 * 5 * 7, 3 * 5 * 11, 3 * 5 * 13) == 3 * 5 @@ -241,7 +322,7 @@ class TestNumbertheory(unittest.TestCase): @given( st.lists( - st.integers(min_value=1, max_value=2 ** 8192), + st.integers(min_value=1, max_value=2**8192), min_size=1, max_size=20, ) @@ -259,7 +340,7 @@ class TestNumbertheory(unittest.TestCase): @given( st.lists( - st.integers(min_value=1, max_value=2 ** 8192), + st.integers(min_value=1, max_value=2**8192), min_size=1, max_size=20, ) @@ -284,9 +365,9 @@ class TestNumbertheory(unittest.TestCase): assert calc * calc % prime == square @settings(**HYP_SETTINGS) - @given(st.integers(min_value=1, max_value=10 ** 12)) + @given(st.integers(min_value=1, max_value=10**12)) @example(265399 * 1526929) - @example(373297 ** 2 * 553991) + @example(373297**2 * 553991) def test_factorization(self, num): factors = factorization(num) mult = 1 @@ -294,6 +375,32 @@ class TestNumbertheory(unittest.TestCase): mult *= i[0] ** i[1] assert mult == num + def test_factorisation_smallprimes(self): + exp = 101 * 103 + assert 101 in smallprimes + assert 103 in smallprimes + factors = factorization(exp) + mult = 1 + for i in factors: + mult *= i[0] ** i[1] + assert mult == exp + + def test_factorisation_not_smallprimes(self): + exp = 1231 * 1237 + assert 1231 not in smallprimes + assert 1237 not in smallprimes + factors = factorization(exp) + mult = 1 + for i in factors: + mult *= i[0] ** i[1] + assert mult == exp + + def test_jacobi_with_zero(self): + assert jacobi(0, 3) == 0 + + def test_jacobi_with_one(self): + assert jacobi(1, 3) == 1 + @settings(**HYP_SETTINGS) @given(st.integers(min_value=3, max_value=1000).filter(lambda x: x % 2)) def test_jacobi(self, mod): diff --git a/frozen_deps/ecdsa/test_pyecdsa.py b/frozen_deps/ecdsa/test_pyecdsa.py index 65b6716..d61f508 100644 --- a/frozen_deps/ecdsa/test_pyecdsa.py +++ b/frozen_deps/ecdsa/test_pyecdsa.py @@ -5,7 +5,7 @@ try: except ImportError: import unittest import os -import time +import sys import shutil import subprocess import pytest @@ -26,6 +26,10 @@ from .util import sigdecode_der, sigdecode_strings from .util import number_to_string, encoded_oid_ecPublicKey, MalformedSignature from .curves import Curve, UnknownCurveError from .curves import ( + SECP112r1, + SECP112r2, + SECP128r1, + SECP160r1, NIST192p, NIST224p, NIST256p, @@ -39,6 +43,8 @@ from .curves import ( BRAINPOOLP320r1, BRAINPOOLP384r1, BRAINPOOLP512r1, + Ed25519, + Ed448, curves, ) from .ecdsa import ( @@ -136,16 +142,12 @@ class ECDSA(unittest.TestCase): BRAINPOOLP384r1, BRAINPOOLP512r1, ): - start = time.time() priv = SigningKey.generate(curve=curve) pub1 = priv.get_verifying_key() - keygen_time = time.time() - start pub2 = VerifyingKey.from_string(pub1.to_string(), curve) self.assertEqual(pub1.to_string(), pub2.to_string()) self.assertEqual(len(pub1.to_string()), curve.verifying_key_length) - start = time.time() sig = priv.sign(b("data")) - sign_time = time.time() - start self.assertEqual(len(sig), curve.signature_length) def test_serialize(self): @@ -310,8 +312,15 @@ class ECDSA(unittest.TestCase): def order(self): return 123456789 + class FakeCurveFp: + def p(self): + return int( + "6525534529039240705020950546962731340" + "4541085228058844382513856749047873406763" + ) + badcurve = Curve( - "unknown", None, FakeGenerator(), (1, 2, 3, 4, 5, 6), None + "unknown", FakeCurveFp(), FakeGenerator(), (1, 2, 3, 4, 5, 6), None ) badpub.curve = badcurve badder = badpub.to_der() @@ -616,7 +625,7 @@ class ECDSA(unittest.TestCase): def test_public_key_recovery(self): # Create keys - curve = NIST256p + curve = BRAINPOOLP160r1 sk = SigningKey.generate(curve=curve) vk = sk.get_verifying_key() @@ -642,14 +651,14 @@ class ECDSA(unittest.TestCase): ) # Test if original vk is the list of recovered keys - self.assertTrue( - vk.pubkey.point - in [recovered_vk.pubkey.point for recovered_vk in recovered_vks] + self.assertIn( + vk.pubkey.point, + [recovered_vk.pubkey.point for recovered_vk in recovered_vks], ) def test_public_key_recovery_with_custom_hash(self): # Create keys - curve = NIST256p + curve = BRAINPOOLP160r1 sk = SigningKey.generate(curve=curve, hashfunc=sha256) vk = sk.get_verifying_key() @@ -660,7 +669,7 @@ class ECDSA(unittest.TestCase): # Recover verifying keys recovered_vks = VerifyingKey.from_public_key_recovery( - signature, data, curve, hashfunc=sha256 + signature, data, curve, hashfunc=sha256, allow_truncate=True ) # Test if each pk is valid @@ -673,9 +682,9 @@ class ECDSA(unittest.TestCase): self.assertEqual(sha256, recovered_vk.default_hashfunc) # Test if original vk is the list of recovered keys - self.assertTrue( - vk.pubkey.point - in [recovered_vk.pubkey.point for recovered_vk in recovered_vks] + self.assertIn( + vk.pubkey.point, + [recovered_vk.pubkey.point for recovered_vk in recovered_vks], ) def test_encoding(self): @@ -715,6 +724,71 @@ class ECDSA(unittest.TestCase): from_uncompressed = VerifyingKey.from_string(b("\x06") + enc) self.assertEqual(from_uncompressed.pubkey.point, vk.pubkey.point) + def test_uncompressed_decoding_as_only_alowed(self): + enc = b( + "\x04" + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) + vk = VerifyingKey.from_string(enc, valid_encodings=("uncompressed",)) + sk = SigningKey.from_secret_exponent(123456789) + + self.assertEqual(vk, sk.verifying_key) + + def test_raw_decoding_with_blocked_format(self): + enc = b( + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) + with self.assertRaises(MalformedPointError) as exp: + VerifyingKey.from_string(enc, valid_encodings=("hybrid",)) + + self.assertIn("hybrid", str(exp.exception)) + + def test_decoding_with_unknown_format(self): + with self.assertRaises(ValueError) as e: + VerifyingKey.from_string(b"", valid_encodings=("raw", "foobar")) + + self.assertIn("Only uncompressed, compressed", str(e.exception)) + + def test_uncompressed_decoding_with_blocked_format(self): + enc = b( + "\x04" + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) + with self.assertRaises(MalformedPointError) as exp: + VerifyingKey.from_string(enc, valid_encodings=("hybrid",)) + + self.assertIn("Invalid X9.62 encoding", str(exp.exception)) + + def test_hybrid_decoding_with_blocked_format(self): + enc = b( + "\x06" + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) + with self.assertRaises(MalformedPointError) as exp: + VerifyingKey.from_string(enc, valid_encodings=("uncompressed",)) + + self.assertIn("Invalid X9.62 encoding", str(exp.exception)) + + def test_compressed_decoding_with_blocked_format(self): + enc = b( + "\x02" + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + )[:25] + with self.assertRaises(MalformedPointError) as exp: + VerifyingKey.from_string(enc, valid_encodings=("hybrid", "raw")) + + self.assertIn("(hybrid, raw)", str(exp.exception)) + def test_decoding_with_malformed_uncompressed(self): enc = b( "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" @@ -866,6 +940,34 @@ class OpenSSL(unittest.TestCase): # sig: 5:OpenSSL->python 6:python->OpenSSL @pytest.mark.skipif( + "secp112r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp112r1", + ) + def test_from_openssl_secp112r1(self): + return self.do_test_from_openssl(SECP112r1) + + @pytest.mark.skipif( + "secp112r2" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp112r2", + ) + def test_from_openssl_secp112r2(self): + return self.do_test_from_openssl(SECP112r2) + + @pytest.mark.skipif( + "secp128r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp128r1", + ) + def test_from_openssl_secp128r1(self): + return self.do_test_from_openssl(SECP128r1) + + @pytest.mark.skipif( + "secp160r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp160r1", + ) + def test_from_openssl_secp160r1(self): + return self.do_test_from_openssl(SECP160r1) + + @pytest.mark.skipif( "prime192v1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support prime192v1", ) @@ -1030,6 +1132,34 @@ class OpenSSL(unittest.TestCase): self.assertEqual(sk, sk_from_p8) @pytest.mark.skipif( + "secp112r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp112r1", + ) + def test_to_openssl_secp112r1(self): + self.do_test_to_openssl(SECP112r1) + + @pytest.mark.skipif( + "secp112r2" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp112r2", + ) + def test_to_openssl_secp112r2(self): + self.do_test_to_openssl(SECP112r2) + + @pytest.mark.skipif( + "secp128r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp128r1", + ) + def test_to_openssl_secp128r1(self): + self.do_test_to_openssl(SECP128r1) + + @pytest.mark.skipif( + "secp160r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp160r1", + ) + def test_to_openssl_secp160r1(self): + self.do_test_to_openssl(SECP160r1) + + @pytest.mark.skipif( "prime192v1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support prime192v1", ) @@ -1191,6 +1321,17 @@ class OpenSSL(unittest.TestCase): % mdarg ) + with open("t/privkey-explicit.pem", "wb") as e: + e.write(sk.to_pem(curve_parameters_encoding="explicit")) + run_openssl( + "dgst %s -sign t/privkey-explicit.pem -out t/data.sig2 t/data.txt" + % mdarg + ) + run_openssl( + "dgst %s -verify t/pubkey.pem -signature t/data.sig2 t/data.txt" + % mdarg + ) + with open("t/privkey-p8.pem", "wb") as e: e.write(sk.to_pem(format="pkcs8")) run_openssl( @@ -1202,6 +1343,133 @@ class OpenSSL(unittest.TestCase): % mdarg ) + with open("t/privkey-p8-explicit.pem", "wb") as e: + e.write( + sk.to_pem(format="pkcs8", curve_parameters_encoding="explicit") + ) + run_openssl( + "dgst %s -sign t/privkey-p8-explicit.pem -out t/data.sig3 t/data.txt" + % mdarg + ) + run_openssl( + "dgst %s -verify t/pubkey.pem -signature t/data.sig3 t/data.txt" + % mdarg + ) + + OPENSSL_SUPPORTED_TYPES = set() + try: + if "-rawin" in run_openssl("pkeyutl -help"): + OPENSSL_SUPPORTED_TYPES = set( + c.lower() + for c in ("ED25519", "ED448") + if c in run_openssl("list -public-key-methods") + ) + except SubprocessError: + pass + + def do_eddsa_test_to_openssl(self, curve): + curvename = curve.name.upper() + + if os.path.isdir("t"): + shutil.rmtree("t") + os.mkdir("t") + + sk = SigningKey.generate(curve=curve) + vk = sk.get_verifying_key() + + data = b"data" + with open("t/pubkey.der", "wb") as e: + e.write(vk.to_der()) + with open("t/pubkey.pem", "wb") as e: + e.write(vk.to_pem()) + + sig = sk.sign(data) + + with open("t/data.sig", "wb") as e: + e.write(sig) + with open("t/data.txt", "wb") as e: + e.write(data) + with open("t/baddata.txt", "wb") as e: + e.write(data + b"corrupt") + + with self.assertRaises(SubprocessError): + run_openssl( + "pkeyutl -verify -pubin -inkey t/pubkey.pem -rawin " + "-in t/baddata.txt -sigfile t/data.sig" + ) + run_openssl( + "pkeyutl -verify -pubin -inkey t/pubkey.pem -rawin " + "-in t/data.txt -sigfile t/data.sig" + ) + + shutil.rmtree("t") + + # in practice at least OpenSSL 3.0.0 is needed to make EdDSA signatures + # earlier versions support EdDSA only in X.509 certificates + @pytest.mark.skipif( + "ed25519" not in OPENSSL_SUPPORTED_TYPES, + reason="system openssl does not support signing with Ed25519", + ) + def test_to_openssl_ed25519(self): + return self.do_eddsa_test_to_openssl(Ed25519) + + @pytest.mark.skipif( + "ed448" not in OPENSSL_SUPPORTED_TYPES, + reason="system openssl does not support signing with Ed448", + ) + def test_to_openssl_ed448(self): + return self.do_eddsa_test_to_openssl(Ed448) + + def do_eddsa_test_from_openssl(self, curve): + curvename = curve.name + + if os.path.isdir("t"): + shutil.rmtree("t") + os.mkdir("t") + + data = b"data" + + run_openssl( + "genpkey -algorithm {0} -outform PEM -out t/privkey.pem".format( + curvename + ) + ) + run_openssl( + "pkey -outform PEM -pubout -in t/privkey.pem -out t/pubkey.pem" + ) + + with open("t/data.txt", "wb") as e: + e.write(data) + run_openssl( + "pkeyutl -sign -inkey t/privkey.pem " + "-rawin -in t/data.txt -out t/data.sig" + ) + + with open("t/data.sig", "rb") as e: + sig = e.read() + with open("t/pubkey.pem", "rb") as e: + vk = VerifyingKey.from_pem(e.read()) + + self.assertIs(vk.curve, curve) + + vk.verify(sig, data) + + shutil.rmtree("t") + + @pytest.mark.skipif( + "ed25519" not in OPENSSL_SUPPORTED_TYPES, + reason="system openssl does not support signing with Ed25519", + ) + def test_from_openssl_ed25519(self): + return self.do_eddsa_test_from_openssl(Ed25519) + + @pytest.mark.skipif( + "ed448" not in OPENSSL_SUPPORTED_TYPES, + reason="system openssl does not support signing with Ed448", + ) + def test_from_openssl_ed448(self): + return self.do_eddsa_test_from_openssl(Ed448) + class TooSmallCurve(unittest.TestCase): OPENSSL_SUPPORTED_CURVES = set( @@ -1215,7 +1483,6 @@ class TooSmallCurve(unittest.TestCase): ) def test_sign_too_small_curve_dont_allow_truncate_raises(self): sk = SigningKey.generate(curve=NIST192p) - vk = sk.get_verifying_key() data = b("data") with self.assertRaises(BadDigestError): sk.sign( @@ -1319,13 +1586,13 @@ class Util(unittest.TestCase): for i in range(1000): seed = "seed-%d" % i for order in ( - 2 ** 8 - 2, - 2 ** 8 - 1, - 2 ** 8, - 2 ** 8 + 1, - 2 ** 8 + 2, - 2 ** 16 - 1, - 2 ** 16 + 1, + 2**8 - 2, + 2**8 - 1, + 2**8, + 2**8 + 1, + 2**8 + 2, + 2**16 - 1, + 2**16 + 1, ): n = tta(seed, order) self.assertTrue(1 <= n < order, (1, n, order)) @@ -1335,24 +1602,35 @@ class Util(unittest.TestCase): b("6fa59d73bf0446ae8743cf748fc5ac11d5585a90356417e97155c3bc"), ) - @given(st.integers(min_value=0, max_value=10 ** 200)) + def test_trytryagain_single(self): + tta = util.randrange_from_seed__trytryagain + order = 2**8 - 2 + seed = b"text" + n = tta(seed, order) + # known issue: https://github.com/warner/python-ecdsa/issues/221 + if sys.version_info < (3, 0): # pragma: no branch + self.assertEqual(n, 228) + else: + self.assertEqual(n, 18) + + @given(st.integers(min_value=0, max_value=10**200)) def test_randrange(self, i): # util.randrange does not provide long-term stability: we might # change the algorithm in the future. entropy = util.PRNG("seed-%d" % i) for order in ( - 2 ** 8 - 2, - 2 ** 8 - 1, - 2 ** 8, - 2 ** 16 - 1, - 2 ** 16 + 1, + 2**8 - 2, + 2**8 - 1, + 2**8, + 2**16 - 1, + 2**16 + 1, ): # that oddball 2**16+1 takes half our runtime n = util.randrange(order, entropy=entropy) self.assertTrue(1 <= n < order, (1, n, order)) def OFF_test_prove_uniformity(self): # pragma: no cover - order = 2 ** 8 - 2 + order = 2**8 - 2 counts = dict([(i, 0) for i in range(1, order)]) assert 0 not in counts assert order not in counts diff --git a/frozen_deps/ecdsa/test_rw_lock.py b/frozen_deps/ecdsa/test_rw_lock.py index d360482..0a84b9c 100644 --- a/frozen_deps/ecdsa/test_rw_lock.py +++ b/frozen_deps/ecdsa/test_rw_lock.py @@ -2,7 +2,10 @@ # https://code.activestate.com/recipes/577803-reader-writer-lock-with-priority-for-writers/ # released under the MIT licence -import unittest +try: + import unittest2 as unittest +except ImportError: + import unittest import threading import time import copy diff --git a/frozen_deps/ecdsa/test_sha3.py b/frozen_deps/ecdsa/test_sha3.py new file mode 100644 index 0000000..2c6bd15 --- /dev/null +++ b/frozen_deps/ecdsa/test_sha3.py @@ -0,0 +1,111 @@ +try: + import unittest2 as unittest +except ImportError: + import unittest +import pytest + +try: + from gmpy2 import mpz + + GMPY = True +except ImportError: + try: + from gmpy import mpz + + GMPY = True + except ImportError: + GMPY = False + +from ._sha3 import shake_256 +from ._compat import bytes_to_int, int_to_bytes + +B2I_VECTORS = [ + (b"\x00\x01", "big", 1), + (b"\x00\x01", "little", 0x0100), + (b"", "big", 0), + (b"\x00", "little", 0), +] + + [email protected]("bytes_in,endian,int_out", B2I_VECTORS) +def test_bytes_to_int(bytes_in, endian, int_out): + out = bytes_to_int(bytes_in, endian) + assert out == int_out + + +class TestBytesToInt(unittest.TestCase): + def test_bytes_to_int_wrong_endian(self): + with self.assertRaises(ValueError): + bytes_to_int(b"\x00", "middle") + + def test_int_to_bytes_wrong_endian(self): + with self.assertRaises(ValueError): + int_to_bytes(0, byteorder="middle") + + [email protected](GMPY == False, reason="requites gmpy or gmpy2") +def test_int_to_bytes_with_gmpy(): + assert int_to_bytes(mpz(1)) == b"\x01" + + +I2B_VECTORS = [ + (0, None, "big", b""), + (0, 1, "big", b"\x00"), + (1, None, "big", b"\x01"), + (0x0100, None, "little", b"\x00\x01"), + (0x0100, 4, "little", b"\x00\x01\x00\x00"), + (1, 4, "big", b"\x00\x00\x00\x01"), +] + + [email protected]("int_in,length,endian,bytes_out", I2B_VECTORS) +def test_int_to_bytes(int_in, length, endian, bytes_out): + out = int_to_bytes(int_in, length, endian) + assert out == bytes_out + + +SHAKE_256_VECTORS = [ + ( + b"Message.", + 32, + b"\x78\xa1\x37\xbb\x33\xae\xe2\x72\xb1\x02\x4f\x39\x43\xe5\xcf\x0c" + b"\x4e\x9c\x72\x76\x2e\x34\x4c\xf8\xf9\xc3\x25\x9d\x4f\x91\x2c\x3a", + ), + ( + b"", + 32, + b"\x46\xb9\xdd\x2b\x0b\xa8\x8d\x13\x23\x3b\x3f\xeb\x74\x3e\xeb\x24" + b"\x3f\xcd\x52\xea\x62\xb8\x1b\x82\xb5\x0c\x27\x64\x6e\xd5\x76\x2f", + ), + ( + b"message", + 32, + b"\x86\x16\xe1\xe4\xcf\xd8\xb5\xf7\xd9\x2d\x43\xd8\x6e\x1b\x14\x51" + b"\xa2\xa6\x5a\xf8\x64\xfc\xb1\x26\xc2\x66\x0a\xb3\x46\x51\xb1\x75", + ), + ( + b"message", + 16, + b"\x86\x16\xe1\xe4\xcf\xd8\xb5\xf7\xd9\x2d\x43\xd8\x6e\x1b\x14\x51", + ), + ( + b"message", + 64, + b"\x86\x16\xe1\xe4\xcf\xd8\xb5\xf7\xd9\x2d\x43\xd8\x6e\x1b\x14\x51" + b"\xa2\xa6\x5a\xf8\x64\xfc\xb1\x26\xc2\x66\x0a\xb3\x46\x51\xb1\x75" + b"\x30\xd6\xba\x2a\x46\x65\xf1\x9d\xf0\x62\x25\xb1\x26\xd1\x3e\xed" + b"\x91\xd5\x0d\xe7\xb9\xcb\x65\xf3\x3a\x46\xae\xd3\x6c\x7d\xc5\xe8", + ), + ( + b"A" * 1024, + 32, + b"\xa5\xef\x7e\x30\x8b\xe8\x33\x64\xe5\x9c\xf3\xb5\xf3\xba\x20\xa3" + b"\x5a\xe7\x30\xfd\xbc\x33\x11\xbf\x83\x89\x50\x82\xb4\x41\xe9\xb3", + ), +] + + [email protected]("msg,olen,ohash", SHAKE_256_VECTORS) +def test_shake_256(msg, olen, ohash): + out = shake_256(msg, olen) + assert out == bytearray(ohash) diff --git a/frozen_deps/ecdsa/util.py b/frozen_deps/ecdsa/util.py index e77d61c..9a56110 100644 --- a/frozen_deps/ecdsa/util.py +++ b/frozen_deps/ecdsa/util.py @@ -33,13 +33,12 @@ oid_ecDH = (1, 3, 132, 1, 12) oid_ecMQV = (1, 3, 132, 1, 13) -if sys.version_info >= (3,): +if sys.version_info >= (3,): # pragma: no branch def entropy_to_bits(ent_256): """Convert a bytestring to string of 0's and 1's""" return bin(int.from_bytes(ent_256, "big"))[2:].zfill(len(ent_256) * 8) - else: def entropy_to_bits(ent_256): @@ -47,12 +46,11 @@ else: return "".join(bin(ord(x))[2:].zfill(8) for x in ent_256) -if sys.version_info < (2, 7): +if sys.version_info < (2, 7): # pragma: no branch # Can't add a method to a built-in type so we are stuck with this def bit_length(x): return len(bin(x)) - 2 - else: def bit_length(x): @@ -99,7 +97,7 @@ class PRNG: def __call__(self, numbytes): a = [next(self.generator) for i in range(numbytes)] - if PY2: + if PY2: # pragma: no branch return "".join(a) else: return bytes(a) diff --git a/frozen_deps/keytree.py-0.2.dist-info/METADATA b/frozen_deps/keytree.py-0.2.dist-info/METADATA index 30904eb..880f6b3 100644 --- a/frozen_deps/keytree.py-0.2.dist-info/METADATA +++ b/frozen_deps/keytree.py-0.2.dist-info/METADATA @@ -6,13 +6,9 @@ Home-page: http://github.com/Determinant/keytree.py Author: Ted Yin Author-email: [email protected] License: MIT -Platform: UNKNOWN Requires-Dist: ecdsa Requires-Dist: base58 Requires-Dist: pysha3 Requires-Dist: pycryptodomex Requires-Dist: mnemonic -UNKNOWN - - diff --git a/frozen_deps/keytree.py-0.2.dist-info/RECORD b/frozen_deps/keytree.py-0.2.dist-info/RECORD index 5f7d2f8..e4d5ef6 100644 --- a/frozen_deps/keytree.py-0.2.dist-info/RECORD +++ b/frozen_deps/keytree.py-0.2.dist-info/RECORD @@ -1,10 +1,11 @@ -../../bin/__pycache__/keytree.cpython-38.pyc,,
-../../bin/keytree.py,sha256=XCCc0ThEVf1BRHtz8LefMmWyhGBp9G8LkxzY51JDkj0,11850
-__pycache__/bech32.cpython-38.pyc,,
+../../bin/__pycache__/keytree.cpython-310.pyc,,
+../../bin/keytree.py,sha256=wcn6oUNgFzfpIot7tCRVjrblmDcGGAX4MkF9xHM4-yU,16467
+__pycache__/bech32.cpython-310.pyc,,
bech32.py,sha256=QZdU7nY1UUzeYfV-5ASwhe3PA3YVVnClyGGrtgJIjsE,4376
keytree.py-0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-keytree.py-0.2.dist-info/METADATA,sha256=bnRg0Bju4Uk768UXWAJ2TAlKIkRFgPP92ClhF1Z2o2w,368
+keytree.py-0.2.dist-info/METADATA,sha256=jp3ESWEdzOw82fECFY9_0qWHwmvYuDwVy9uv0l28o7k,340
keytree.py-0.2.dist-info/RECORD,,
-keytree.py-0.2.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92
-keytree.py-0.2.dist-info/direct_url.json,sha256=6seyiOxGCRXsnt6lrEcpcJtZGEXuBr-KHa0idXAuZDA,71
+keytree.py-0.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+keytree.py-0.2.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
+keytree.py-0.2.dist-info/direct_url.json,sha256=appPoRbvitJQonSqtARctmTb0amTShpFDmrPTT5LEQM,79
keytree.py-0.2.dist-info/top_level.txt,sha256=oESACl4kH99axdjvTQuJATlgqujLpRc8iCwiqjvbI8M,7
diff --git a/frozen_deps/keytree.py-0.2.dist-info/REQUESTED b/frozen_deps/keytree.py-0.2.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/frozen_deps/keytree.py-0.2.dist-info/REQUESTED diff --git a/frozen_deps/keytree.py-0.2.dist-info/WHEEL b/frozen_deps/keytree.py-0.2.dist-info/WHEEL index b552003..becc9a6 100644 --- a/frozen_deps/keytree.py-0.2.dist-info/WHEEL +++ b/frozen_deps/keytree.py-0.2.dist-info/WHEEL @@ -1,5 +1,5 @@ Wheel-Version: 1.0 -Generator: bdist_wheel (0.34.2) +Generator: bdist_wheel (0.37.1) Root-Is-Purelib: true Tag: py3-none-any diff --git a/frozen_deps/keytree.py-0.2.dist-info/direct_url.json b/frozen_deps/keytree.py-0.2.dist-info/direct_url.json index 650a846..78f25ff 100644 --- a/frozen_deps/keytree.py-0.2.dist-info/direct_url.json +++ b/frozen_deps/keytree.py-0.2.dist-info/direct_url.json @@ -1 +1 @@ -{"dir_info": {}, "url": "file:///home/ymf/work/2020_summer/keytree.py"}
\ No newline at end of file +{"dir_info": {}, "url": "file:///home/ymf/work/archive/2020_summer/keytree.py"}
\ No newline at end of file diff --git a/frozen_deps/mnemonic-0.20.dist-info/AUTHORS b/frozen_deps/mnemonic-0.20.dist-info/AUTHORS new file mode 100644 index 0000000..34ed344 --- /dev/null +++ b/frozen_deps/mnemonic-0.20.dist-info/AUTHORS @@ -0,0 +1,2 @@ +Marek Palatinus <[email protected]> +Pavol Rusnak <[email protected]> diff --git a/frozen_deps/mnemonic-0.20.dist-info/INSTALLER b/frozen_deps/mnemonic-0.20.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/frozen_deps/mnemonic-0.20.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/frozen_deps/mnemonic-0.20.dist-info/LICENSE b/frozen_deps/mnemonic-0.20.dist-info/LICENSE new file mode 100644 index 0000000..b135744 --- /dev/null +++ b/frozen_deps/mnemonic-0.20.dist-info/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013-2016 Pavol Rusnak + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/frozen_deps/mnemonic-0.20.dist-info/METADATA b/frozen_deps/mnemonic-0.20.dist-info/METADATA new file mode 100644 index 0000000..6e3f74a --- /dev/null +++ b/frozen_deps/mnemonic-0.20.dist-info/METADATA @@ -0,0 +1,138 @@ +Metadata-Version: 2.1 +Name: mnemonic +Version: 0.20 +Summary: Implementation of Bitcoin BIP-0039 +Home-page: https://github.com/trezor/python-mnemonic +Author: Trezor +Author-email: [email protected] +License: UNKNOWN +Platform: UNKNOWN +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Programming Language :: Python :: 3 +Requires-Python: >=3.5 + +python-mnemonic +=============== + +.. image:: https://badge.fury.io/py/mnemonic.svg + :target: https://badge.fury.io/py/mnemonic + +Reference implementation of BIP-0039: Mnemonic code for generating +deterministic keys + +Abstract +-------- + +This BIP describes the implementation of a mnemonic code or mnemonic sentence -- +a group of easy to remember words -- for the generation of deterministic wallets. + +It consists of two parts: generating the mnenomic, and converting it into a +binary seed. This seed can be later used to generate deterministic wallets using +BIP-0032 or similar methods. + +BIP Paper +--------- + +See https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki +for full specification + +Installation +------------ + +To install this library and its dependencies use: + + ``pip install mnemonic`` + +Usage examples +-------------- + +Import library into python project via: + +.. code-block:: python + + from mnemonic import Mnemonic + +Initialize class instance, picking from available dictionaries: + +- english +- chinese_simplified +- chinese_traditional +- french +- italian +- japanese +- korean +- spanish + +.. code-block:: python + + mnemo = Mnemonic(language) + mnemo = Mnemonic("english") + +Generate word list given the strength (128 - 256): + +.. code-block:: python + + words = mnemo.generate(strength=256) + +Given the word list and custom passphrase (empty in example), generate seed: + +.. code-block:: python + + seed = mnemo.to_seed(words, passphrase="") + +Given the word list, calculate original entropy: + +.. code-block:: python + + entropy = mnemo.to_entropy(words) + +Changelog +========= + +.. default-role:: code + +All notable changes to this project will be documented in this file. + +The format is based on `Keep a Changelog`_, and this project adheres to +`Semantic Versioning`_. + +`0.20`_ - 2021-07-27 +--------------------- + +.. _0.20: https://github.com/trezor/python-mnemonic/compare/v0.19...v0.20 + +Added +~~~~~ + +- Type annotations +- Support for testnet private keys + +Changed +~~~~~~~ + +- Project directory structure was cleaned up +- Language on the `Mnemonic` object is remembered instead of repeatedly detecting + +Removed +~~~~~~~ + +- Support for Python 2.7 and 3.4 was dropped + + + +0.19 - 2019-10-01 +------------------ + +Added +~~~~~ + +- Start of changelog + + +.. _Keep a Changelog: https://keepachangelog.com/en/1.0.0/ +.. _Semantic Versioning: https://semver.org/spec/v2.0.0.html + + diff --git a/frozen_deps/mnemonic-0.20.dist-info/RECORD b/frozen_deps/mnemonic-0.20.dist-info/RECORD new file mode 100644 index 0000000..df6e8d0 --- /dev/null +++ b/frozen_deps/mnemonic-0.20.dist-info/RECORD @@ -0,0 +1,20 @@ +mnemonic-0.20.dist-info/AUTHORS,sha256=5ugihxzh5OoFf-6gBQOnJfgaznUQHX5Vv4L9Th9iGFo,77
+mnemonic-0.20.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+mnemonic-0.20.dist-info/LICENSE,sha256=1ePHxiqE6ABzIB4vblEw6eaAT6BfisT4smoTx9OWlpc,1084
+mnemonic-0.20.dist-info/METADATA,sha256=dXEUuWVHgleZLdLt3xICy8dSoNUs1aFzj4qccHpLoTc,2872
+mnemonic-0.20.dist-info/RECORD,,
+mnemonic-0.20.dist-info/WHEEL,sha256=EVRjI69F5qVjm_YgqcTXPnTAv3BfSUr0WVAHuSP3Xoo,92
+mnemonic-0.20.dist-info/top_level.txt,sha256=XlFgB9xaifTc2HXO7w9EXCvIUHFN_x51trUyPQKBSHI,9
+mnemonic/__init__.py,sha256=xlJLxkmLgN4zK_WNEA067BEEL7tVkIWqC8JZVbLq9o0,55
+mnemonic/__pycache__/__init__.cpython-310.pyc,,
+mnemonic/__pycache__/mnemonic.cpython-310.pyc,,
+mnemonic/mnemonic.py,sha256=x4Gm8j0oeqf2Icu23soMWzBu2fB6GjC5PYcOvqYldRA,10307
+mnemonic/py.typed,sha256=bWew9mHgMy8LqMu7RuqQXFXLBxh2CRx0dUbSx-3wE48,27
+mnemonic/wordlist/chinese_simplified.txt,sha256=XFlCeSvYNAy4snzVkvEBXt9WqMWyYnbuGKSCQo58VyY,8192
+mnemonic/wordlist/chinese_traditional.txt,sha256=QXsms9hQCkrj1ZcX1wEZUttvwvuEuAfz-UrHNOicG18,8192
+mnemonic/wordlist/english.txt,sha256=L17tU6Rye0v4iA2PPxme_JDlhQNkbZ_47_Oi7Tsk29o,13116
+mnemonic/wordlist/french.txt,sha256=uMrsEjGdD_sSfITkLIhmyGpUrJlR_iz7-QLTVVLGXk8,16776
+mnemonic/wordlist/italian.txt,sha256=05LEn9twCiTNH86yN8H2XcwSj2s0qKrLWLWThLXGSMI,16033
+mnemonic/wordlist/japanese.txt,sha256=Lu0K70kikeBhYz162BF_GisD64CinQ5OMResJSjQX_0,26423
+mnemonic/wordlist/korean.txt,sha256=npX4bBZ96I9FDwqvieh_ZiSlf5c8Z7UW4zjo6LiJf2A,37832
+mnemonic/wordlist/spanish.txt,sha256=RoRqWgE50ePLdyk-UhwoZfe824LETo0KBqLNDsukjAs,13996
diff --git a/frozen_deps/mnemonic-0.20.dist-info/WHEEL b/frozen_deps/mnemonic-0.20.dist-info/WHEEL new file mode 100644 index 0000000..83ff02e --- /dev/null +++ b/frozen_deps/mnemonic-0.20.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.35.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/frozen_deps/mnemonic-0.20.dist-info/top_level.txt b/frozen_deps/mnemonic-0.20.dist-info/top_level.txt new file mode 100644 index 0000000..9015942 --- /dev/null +++ b/frozen_deps/mnemonic-0.20.dist-info/top_level.txt @@ -0,0 +1 @@ +mnemonic diff --git a/frozen_deps/mnemonic/__init__.py b/frozen_deps/mnemonic/__init__.py index 47e293d..f8c9e1f 100644 --- a/frozen_deps/mnemonic/__init__.py +++ b/frozen_deps/mnemonic/__init__.py @@ -1 +1,3 @@ -from .mnemonic import Mnemonic # noqa: F401 +from .mnemonic import Mnemonic + +__all__ = ["Mnemonic"] diff --git a/frozen_deps/mnemonic/mnemonic.py b/frozen_deps/mnemonic/mnemonic.py index 935620a..e45111b 100644 --- a/frozen_deps/mnemonic/mnemonic.py +++ b/frozen_deps/mnemonic/mnemonic.py @@ -20,15 +20,15 @@ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -import binascii import bisect import hashlib import hmac import itertools import os -import sys +from typing import AnyStr, List, Optional, Sequence, TypeVar, Union import unicodedata +_T = TypeVar("_T") PBKDF2_ROUNDS = 2048 @@ -37,20 +37,23 @@ class ConfigurationError(Exception): # From <https://stackoverflow.com/questions/212358/binary-search-bisection-in-python/2233940#2233940> -def binary_search(a, x, lo=0, hi=None): # can't use a to specify default for hi +def binary_search( + a: Sequence[_T], + x: _T, + lo: int = 0, + hi: Optional[int] = None, # can't use a to specify default for hi +) -> int: hi = hi if hi is not None else len(a) # hi defaults to len(a) pos = bisect.bisect_left(a, x, lo, hi) # find insertion position return pos if pos != hi and a[pos] == x else -1 # don't walk off the end # Refactored code segments from <https://github.com/keis/base58> -def b58encode(v): +def b58encode(v: bytes) -> str: alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" p, acc = 1, 0 for c in reversed(v): - if sys.version < "3": - c = ord(c) acc += p * c p = p << 8 @@ -62,39 +65,36 @@ def b58encode(v): class Mnemonic(object): - def __init__(self, language): + def __init__(self, language: str): + self.language = language self.radix = 2048 - if sys.version < "3": - with open("%s/%s.txt" % (self._get_directory(), language), "r") as f: - self.wordlist = [w.strip().decode("utf8") for w in f.readlines()] - else: - with open( - "%s/%s.txt" % (self._get_directory(), language), "r", encoding="utf-8" - ) as f: - self.wordlist = [w.strip() for w in f.readlines()] + with open( + "%s/%s.txt" % (self._get_directory(), language), "r", encoding="utf-8" + ) as f: + self.wordlist = [w.strip() for w in f.readlines()] if len(self.wordlist) != self.radix: raise ConfigurationError( "Wordlist should contain %d words, but it contains %d words." % (self.radix, len(self.wordlist)) ) - @classmethod - def _get_directory(cls): + @staticmethod + def _get_directory() -> str: return os.path.join(os.path.dirname(__file__), "wordlist") @classmethod - def list_languages(cls): + def list_languages(cls) -> List[str]: return [ f.split(".")[0] for f in os.listdir(cls._get_directory()) if f.endswith(".txt") ] - @classmethod - def normalize_string(cls, txt): - if isinstance(txt, str if sys.version < "3" else bytes): + @staticmethod + def normalize_string(txt: AnyStr) -> str: + if isinstance(txt, bytes): utxt = txt.decode("utf8") - elif isinstance(txt, unicode if sys.version < "3" else str): # noqa: F821 + elif isinstance(txt, str): utxt = txt else: raise TypeError("String value expected") @@ -102,7 +102,7 @@ class Mnemonic(object): return unicodedata.normalize("NFKD", utxt) @classmethod - def detect_language(cls, code): + def detect_language(cls, code: str) -> str: code = cls.normalize_string(code) first = code.split(" ")[0] languages = cls.list_languages() @@ -114,7 +114,7 @@ class Mnemonic(object): raise ConfigurationError("Language not detected") - def generate(self, strength=128): + def generate(self, strength: int = 128) -> str: if strength not in [128, 160, 192, 224, 256]: raise ValueError( "Strength should be one of the following [128, 160, 192, 224, 256], but it is not (%d)." @@ -123,7 +123,7 @@ class Mnemonic(object): return self.to_mnemonic(os.urandom(strength // 8)) # Adapted from <http://tinyurl.com/oxmn476> - def to_entropy(self, words): + def to_entropy(self, words: Union[List[str], str]) -> bytearray: if not isinstance(words, list): words = words.split(" ") if len(words) not in [12, 15, 18, 21, 24]: @@ -136,7 +136,7 @@ class Mnemonic(object): concatLenBits = len(words) * 11 concatBits = [False] * concatLenBits wordindex = 0 - if self.detect_language(" ".join(words)) == "english": + if self.language == "english": use_binary_search = True else: use_binary_search = False @@ -163,28 +163,18 @@ class Mnemonic(object): entropy[ii] |= 1 << (7 - jj) # Take the digest of the entropy. hashBytes = hashlib.sha256(entropy).digest() - if sys.version < "3": - hashBits = list( - itertools.chain.from_iterable( - ( - [ord(c) & (1 << (7 - i)) != 0 for i in range(8)] - for c in hashBytes - ) - ) - ) - else: - hashBits = list( - itertools.chain.from_iterable( - ([c & (1 << (7 - i)) != 0 for i in range(8)] for c in hashBytes) - ) + hashBits = list( + itertools.chain.from_iterable( + [c & (1 << (7 - i)) != 0 for i in range(8)] for c in hashBytes ) + ) # Check all the checksum bits. for i in range(checksumLengthBits): if concatBits[entropyLengthBits + i] != hashBits[i]: raise ValueError("Failed checksum.") return entropy - def to_mnemonic(self, data): + def to_mnemonic(self, data: bytes) -> str: if len(data) not in [16, 20, 24, 28, 32]: raise ValueError( "Data length should be one of the following: [16, 20, 24, 28, 32], but it is not (%d)." @@ -192,39 +182,39 @@ class Mnemonic(object): ) h = hashlib.sha256(data).hexdigest() b = ( - bin(int(binascii.hexlify(data), 16))[2:].zfill(len(data) * 8) + bin(int.from_bytes(data, byteorder="big"))[2:].zfill(len(data) * 8) + bin(int(h, 16))[2:].zfill(256)[: len(data) * 8 // 32] ) result = [] for i in range(len(b) // 11): idx = int(b[i * 11 : (i + 1) * 11], 2) result.append(self.wordlist[idx]) - if ( - self.detect_language(" ".join(result)) == "japanese" - ): # Japanese must be joined by ideographic space. + if self.language == "japanese": # Japanese must be joined by ideographic space. result_phrase = u"\u3000".join(result) else: result_phrase = " ".join(result) return result_phrase - def check(self, mnemonic): - mnemonic = self.normalize_string(mnemonic).split(" ") + def check(self, mnemonic: str) -> bool: + mnemonic_list = self.normalize_string(mnemonic).split(" ") # list of valid mnemonic lengths - if len(mnemonic) not in [12, 15, 18, 21, 24]: + if len(mnemonic_list) not in [12, 15, 18, 21, 24]: return False try: - idx = map(lambda x: bin(self.wordlist.index(x))[2:].zfill(11), mnemonic) + idx = map( + lambda x: bin(self.wordlist.index(x))[2:].zfill(11), mnemonic_list + ) b = "".join(idx) except ValueError: return False l = len(b) # noqa: E741 d = b[: l // 33 * 32] h = b[-l // 33 :] - nd = binascii.unhexlify(hex(int(d, 2))[2:].rstrip("L").zfill(l // 33 * 8)) + nd = int(d, 2).to_bytes(l // 33 * 4, byteorder="big") nh = bin(int(hashlib.sha256(nd).hexdigest(), 16))[2:].zfill(256)[: l // 33] return h == nh - def expand_word(self, prefix): + def expand_word(self, prefix: str) -> str: if prefix in self.wordlist: return prefix else: @@ -236,21 +226,23 @@ class Mnemonic(object): # this is not a validation routine, just return the input return prefix - def expand(self, mnemonic): + def expand(self, mnemonic: str) -> str: return " ".join(map(self.expand_word, mnemonic.split(" "))) @classmethod - def to_seed(cls, mnemonic, passphrase=""): + def to_seed(cls, mnemonic: str, passphrase: str = "") -> bytes: mnemonic = cls.normalize_string(mnemonic) passphrase = cls.normalize_string(passphrase) passphrase = "mnemonic" + passphrase - mnemonic = mnemonic.encode("utf-8") - passphrase = passphrase.encode("utf-8") - stretched = hashlib.pbkdf2_hmac("sha512", mnemonic, passphrase, PBKDF2_ROUNDS) + mnemonic_bytes = mnemonic.encode("utf-8") + passphrase_bytes = passphrase.encode("utf-8") + stretched = hashlib.pbkdf2_hmac( + "sha512", mnemonic_bytes, passphrase_bytes, PBKDF2_ROUNDS + ) return stretched[:64] - @classmethod - def to_hd_master_key(cls, seed): + @staticmethod + def to_hd_master_key(seed: bytes, testnet: bool = False) -> str: if len(seed) != 64: raise ValueError("Provided seed should have length of 64") @@ -259,6 +251,8 @@ class Mnemonic(object): # Serialization format can be found at: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#Serialization_format xprv = b"\x04\x88\xad\xe4" # Version for private mainnet + if testnet: + xprv = b"\x04\x35\x83\x94" # Version for private testnet xprv += b"\x00" * 9 # Depth, parent fingerprint, and child number xprv += seed[32:] # Chain code xprv += b"\x00" + seed[:32] # Master key @@ -274,15 +268,14 @@ class Mnemonic(object): return b58encode(xprv) -def main(): - import binascii +def main() -> None: import sys if len(sys.argv) > 1: - data = sys.argv[1] + hex_data = sys.argv[1] else: - data = sys.stdin.readline().strip() - data = binascii.unhexlify(data) + hex_data = sys.stdin.readline().strip() + data = bytes.fromhex(hex_data) m = Mnemonic("english") print(m.to_mnemonic(data)) diff --git a/frozen_deps/mnemonic/py.typed b/frozen_deps/mnemonic/py.typed new file mode 100644 index 0000000..1242d43 --- /dev/null +++ b/frozen_deps/mnemonic/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561. diff --git a/frozen_deps/mnemonic/wordlist/french.txt b/frozen_deps/mnemonic/wordlist/french.txt index 8600949..bcf7942 100644 --- a/frozen_deps/mnemonic/wordlist/french.txt +++ b/frozen_deps/mnemonic/wordlist/french.txt @@ -1,4 +1,4 @@ -abaisser +abaisser abandon abdiquer abeille diff --git a/frozen_deps/pycryptodomex-3.15.0.dist-info/AUTHORS.rst b/frozen_deps/pycryptodomex-3.15.0.dist-info/AUTHORS.rst new file mode 100644 index 0000000..79adf3c --- /dev/null +++ b/frozen_deps/pycryptodomex-3.15.0.dist-info/AUTHORS.rst @@ -0,0 +1,50 @@ +Simon Arneaud +Nevins Bartolomeo +Thorsten E. Behrens +Tim Berners-Lee +Frédéric Bertolus +Ian Bicking +Joris Bontje +Antoon Bosselaers +Andrea Bottoni +Jean-Paul Calderone +Sergey Chernov +Geremy Condra +Jan Dittberner +Andrew Eland +Philippe Frycia +Peter Gutmann +Hirendra Hindocha +Nikhil Jhingan +Sebastian Kayser +Ryan Kelly +Andrew M. Kuchling +Piers Lauder +Legrandin +M.-A. Lemburg +Wim Lewis +Darsey C. Litzenberger +Richard Mitchell +Mark Moraes +Lim Chee Siang +Bryan Olson +Wallace Owen +Colin Plumb +Robey Pointer +Lorenz Quack +Sebastian Ramacher +Jeethu Rao +James P. Rutledge +Matt Schreiner +Peter Simmons +Janne Snabb +Tom St. Denis +Anders Sundman +Paul Swartz +Fabrizio Tarizzo +Kevin M. Turner +Barry A. Warsaw +Eric Young +Hannes van Niekerk +Stefan Seering +Koki Takahashi diff --git a/frozen_deps/pycryptodomex-3.15.0.dist-info/INSTALLER b/frozen_deps/pycryptodomex-3.15.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/frozen_deps/pycryptodomex-3.15.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/frozen_deps/pycryptodomex-3.15.0.dist-info/LICENSE.rst b/frozen_deps/pycryptodomex-3.15.0.dist-info/LICENSE.rst new file mode 100644 index 0000000..3008ff7 --- /dev/null +++ b/frozen_deps/pycryptodomex-3.15.0.dist-info/LICENSE.rst @@ -0,0 +1,61 @@ +The source code in PyCryptodome is partially in the public domain +and partially released under the BSD 2-Clause license. + +In either case, there are minimal if no restrictions on the redistribution, +modification and usage of the software. + +Public domain +============= + +All code originating from PyCrypto is free and unencumbered software +released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to <http://unlicense.org> + +BSD license +=========== + +All direct contributions to PyCryptodome are released under the following +license. The copyright of each piece belongs to the respective author. + +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. diff --git a/frozen_deps/pycryptodomex-3.15.0.dist-info/METADATA b/frozen_deps/pycryptodomex-3.15.0.dist-info/METADATA new file mode 100644 index 0000000..8e2c564 --- /dev/null +++ b/frozen_deps/pycryptodomex-3.15.0.dist-info/METADATA @@ -0,0 +1,84 @@ +Metadata-Version: 2.1 +Name: pycryptodomex +Version: 3.15.0 +Summary: Cryptographic library for Python +Home-page: https://www.pycryptodome.org +Author: Helder Eijs +Author-email: [email protected] +License: BSD, Public Domain +Project-URL: Source, https://github.com/Legrandin/pycryptodome/ +Platform: Posix; MacOS X; Windows +Classifier: Development Status :: 5 - Production/Stable +Classifier: License :: OSI Approved :: BSD License +Classifier: License :: OSI Approved :: Apache Software License +Classifier: License :: Public Domain +Classifier: Intended Audience :: Developers +Classifier: Operating System :: Unix +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Topic :: Security :: Cryptography +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* + + +PyCryptodome +============ + +PyCryptodome is a self-contained Python package of low-level +cryptographic primitives. + +It supports Python 2.7, Python 3.5 and newer, and PyPy. + +You can install it with:: + + pip install pycryptodomex + +All modules are installed under the ``Cryptodome`` package. + +Check the pycryptodome_ project for the equivalent library that +works under the ``Crypto`` package. + +PyCryptodome is a fork of PyCrypto. It brings several enhancements +with respect to the last official version of PyCrypto (2.6.1), +for instance: + +* Authenticated encryption modes (GCM, CCM, EAX, SIV, OCB) +* Accelerated AES on Intel platforms via AES-NI +* First class support for PyPy +* Elliptic curves cryptography (NIST P-curves; Ed25519, Ed448) +* Better and more compact API (`nonce` and `iv` attributes for ciphers, + automatic generation of random nonces and IVs, simplified CTR cipher mode, + and more) +* SHA-3 (including SHAKE XOFs) and BLAKE2 hash algorithms +* Salsa20 and ChaCha20 stream ciphers +* scrypt and HKDF +* Deterministic (EC)DSA and EdDSA +* Password-protected PKCS#8 key containers +* Shamir's Secret Sharing scheme +* Random numbers get sourced directly from the OS (and not from a CSPRNG in userspace) +* Simplified install process, including better support for Windows +* Cleaner RSA and DSA key generation (largely based on FIPS 186-4) +* Major clean ups and simplification of the code base + +PyCryptodome is not a wrapper to a separate C library like *OpenSSL*. +To the largest possible extent, algorithms are implemented in pure Python. +Only the pieces that are extremely critical to performance (e.g. block ciphers) +are implemented as C extensions. + +For more information, see the `homepage`_. + +All the code can be downloaded from `GitHub`_. + +.. _pycryptodome: https://pypi.python.org/pypi/pycryptodome +.. _`homepage`: http://www.pycryptodome.org +.. _GitHub: https://github.com/Legrandin/pycryptodome + + diff --git a/frozen_deps/pycryptodomex-3.15.0.dist-info/RECORD b/frozen_deps/pycryptodomex-3.15.0.dist-info/RECORD new file mode 100644 index 0000000..a8a2698 --- /dev/null +++ b/frozen_deps/pycryptodomex-3.15.0.dist-info/RECORD @@ -0,0 +1,512 @@ +Cryptodome/Cipher/AES.py,sha256=0psUOUf5OlLbARevPPEtHCPS6voEf6g2DszHOREhMtU,9553
+Cryptodome/Cipher/AES.pyi,sha256=3ahEn43wmFv37xdmnlibzjWOmRAgSSeiFToFMIoLVUk,1387
+Cryptodome/Cipher/ARC2.py,sha256=NwO42KWBpuTXS5Thx0gsDRmxyrvfiudx7PKed1KR0CI,7026
+Cryptodome/Cipher/ARC2.pyi,sha256=zgMfUY35w1AuEi0apkFuHo_NQOfzsCxuZ9gN1s3oAhg,982
+Cryptodome/Cipher/ARC4.py,sha256=x_8L_KA_pDkoeIOSCQZyVPwhD3na4-xShkPkeNRka58,5168
+Cryptodome/Cipher/ARC4.pyi,sha256=sMw73yZHeonmGx9BhiyA7__4PQJocU04SMRcDjnyJ2Y,431
+Cryptodome/Cipher/Blowfish.py,sha256=K5hF6nb2r8FsHhgLX2irdyUMqg4PCwrAgu68NNhU1ag,5976
+Cryptodome/Cipher/Blowfish.pyi,sha256=kDooazMxY1973SMtPuhNJ9f68PS4cNWynyYa7CoWC48,1018
+Cryptodome/Cipher/CAST.py,sha256=3XDjtVBsia2kPvducB19_FEc3zM7hSK8JKKmK-b8cvU,6087
+Cryptodome/Cipher/CAST.pyi,sha256=XgVk9wOv_V77LbQmm24O2R-PDss8JiHfw11karavKCI,983
+Cryptodome/Cipher/ChaCha20.py,sha256=vs7UgkZyHritK0lXGqUMDpqxFkxGgIzcTqeMYAj1bpM,10780
+Cryptodome/Cipher/ChaCha20.pyi,sha256=_l1xhtOyBmYEHP7Ftmk8EQZpKegX9p3N5tckC_PPve0,762
+Cryptodome/Cipher/ChaCha20_Poly1305.py,sha256=W0tOmBkoNzfwtwgPG9Tw0dUm3sZ8IVhnehTbAc2jRt4,11561
+Cryptodome/Cipher/ChaCha20_Poly1305.pyi,sha256=h1U5ixODzM9NwLpX9oaIJdeQ0ubYeDeY9m6ur05dKCc,1068
+Cryptodome/Cipher/DES.py,sha256=ebgHOqNGjCYlIoFh3W5KXY17aQnvckjhQC9TPEKqPuY,5963
+Cryptodome/Cipher/DES.pyi,sha256=thNZATxZ0Q-vOh_V7QVZu6J_ESvr9mcA67WlQV_nP1A,963
+Cryptodome/Cipher/DES3.py,sha256=qmIagOLhMGG221sC7BAH9xe5lnxk2iyilbeYoC3it4c,6941
+Cryptodome/Cipher/DES3.pyi,sha256=arv4Uhv7lSgAL36B_Vx5DL-s_18Kc9_TejNxftMp4H4,1033
+Cryptodome/Cipher/PKCS1_OAEP.py,sha256=-FrP1ZsCGmi-BizJdTSxFu6Q3np-WT2dB-OHW2l_pxo,8883
+Cryptodome/Cipher/PKCS1_OAEP.pyi,sha256=XRZ5_0v8w2LUYD04Gz3Ekey9PxYBZJnUl-0rVv2q_gY,1183
+Cryptodome/Cipher/PKCS1_v1_5.py,sha256=H3RBr3v8fE3Tev63KfNXWVlNFHvZwnBwtAinSXKLYfY,8173
+Cryptodome/Cipher/PKCS1_v1_5.pyi,sha256=frCjzmSJrlPVp-Z0P_Eef9yYSfBNZXfejxv-017G6Jo,690
+Cryptodome/Cipher/Salsa20.py,sha256=SuzaYhBc_Q7voODDmQ2rCzILndPpmF1n6qitDMmk7no,6369
+Cryptodome/Cipher/Salsa20.pyi,sha256=4vjq_HN8NK7U9VdaaHIgs17-fyW8SRPDZaHy3jKVkto,744
+Cryptodome/Cipher/_ARC4.abi3.so,sha256=EIiTtbeekCNX8Yf88U_Z4IsCGk1KjNuEqCNBTjLGfGM,13768
+Cryptodome/Cipher/_EKSBlowfish.py,sha256=fi6TEeN0bXcznFkyLzsaoLxNmu9iPT-Tmp3CLvJsE0A,5217
+Cryptodome/Cipher/_EKSBlowfish.pyi,sha256=6JhAXsSVbJMYlcudpNwSNEDB9X5NjhfRGPzZvcOc_As,270
+Cryptodome/Cipher/_Salsa20.abi3.so,sha256=VFwPkXZoqkHXwSMyzXBQ-ZWaoteY5pdiwk3GK2KDTrM,26784
+Cryptodome/Cipher/__init__.py,sha256=Cdau3A_ZsmqOFM8yK4Q2C_hFeV2xjd9_fU812_ferXM,2888
+Cryptodome/Cipher/__init__.pyi,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+Cryptodome/Cipher/__pycache__/AES.cpython-310.pyc,,
+Cryptodome/Cipher/__pycache__/ARC2.cpython-310.pyc,,
+Cryptodome/Cipher/__pycache__/ARC4.cpython-310.pyc,,
+Cryptodome/Cipher/__pycache__/Blowfish.cpython-310.pyc,,
+Cryptodome/Cipher/__pycache__/CAST.cpython-310.pyc,,
+Cryptodome/Cipher/__pycache__/ChaCha20.cpython-310.pyc,,
+Cryptodome/Cipher/__pycache__/ChaCha20_Poly1305.cpython-310.pyc,,
+Cryptodome/Cipher/__pycache__/DES.cpython-310.pyc,,
+Cryptodome/Cipher/__pycache__/DES3.cpython-310.pyc,,
+Cryptodome/Cipher/__pycache__/PKCS1_OAEP.cpython-310.pyc,,
+Cryptodome/Cipher/__pycache__/PKCS1_v1_5.cpython-310.pyc,,
+Cryptodome/Cipher/__pycache__/Salsa20.cpython-310.pyc,,
+Cryptodome/Cipher/__pycache__/_EKSBlowfish.cpython-310.pyc,,
+Cryptodome/Cipher/__pycache__/__init__.cpython-310.pyc,,
+Cryptodome/Cipher/__pycache__/_mode_cbc.cpython-310.pyc,,
+Cryptodome/Cipher/__pycache__/_mode_ccm.cpython-310.pyc,,
+Cryptodome/Cipher/__pycache__/_mode_cfb.cpython-310.pyc,,
+Cryptodome/Cipher/__pycache__/_mode_ctr.cpython-310.pyc,,
+Cryptodome/Cipher/__pycache__/_mode_eax.cpython-310.pyc,,
+Cryptodome/Cipher/__pycache__/_mode_ecb.cpython-310.pyc,,
+Cryptodome/Cipher/__pycache__/_mode_gcm.cpython-310.pyc,,
+Cryptodome/Cipher/__pycache__/_mode_ocb.cpython-310.pyc,,
+Cryptodome/Cipher/__pycache__/_mode_ofb.cpython-310.pyc,,
+Cryptodome/Cipher/__pycache__/_mode_openpgp.cpython-310.pyc,,
+Cryptodome/Cipher/__pycache__/_mode_siv.cpython-310.pyc,,
+Cryptodome/Cipher/_chacha20.abi3.so,sha256=EZOsXRmosJzfx2bXftuYvZQNwZ8Bfz_7sNwzDA178yo,28224
+Cryptodome/Cipher/_mode_cbc.py,sha256=cDaa-zgNIbosLHJhwsDtt7YOTE-jzRUi4ODfj95SBsM,10971
+Cryptodome/Cipher/_mode_cbc.pyi,sha256=qH5pEQNuefabeRV-Xz-6AA953MFd_Z20zGeBTwa6t0s,691
+Cryptodome/Cipher/_mode_ccm.py,sha256=zqq91jARvmdPXs58u0uCP-D8ctzsg65Lf8nW9VQYezs,24476
+Cryptodome/Cipher/_mode_ccm.pyi,sha256=ZSs4SOlivIG_JUxknDjQKs4ZYGmkwAO7K2DKcbz_14M,1600
+Cryptodome/Cipher/_mode_cfb.py,sha256=lfIeHxJmyqmBWk84d4z9030OooTSBX1gfaVIaRqIPyE,10821
+Cryptodome/Cipher/_mode_cfb.pyi,sha256=PM0slBBfWdA4Ec0JjM-OJheiZkCFPWAADvLsvKpDRCY,731
+Cryptodome/Cipher/_mode_ctr.py,sha256=vEEFc9kj3_m9cdUjI23V_vxTHtGJVpA35qkfphDKx9M,15870
+Cryptodome/Cipher/_mode_ctr.pyi,sha256=UcZ1zOZlVnTSlka9R1yFo7kaDhreiLKCe7AU05AcAuI,804
+Cryptodome/Cipher/_mode_eax.py,sha256=XXOc-GKbZ2hRagr5I5FihKWaGh8hXP1Si2kY3D85-ls,14543
+Cryptodome/Cipher/_mode_eax.pyi,sha256=VHPtTdA-2btCvRE-4npRtGCrApg7rBNWpHSZV1po8J0,1545
+Cryptodome/Cipher/_mode_ecb.py,sha256=2ciIC6I7cXgGDZW9wdkJ_UKAcxsrHHcv7jzVreLeDqE,8321
+Cryptodome/Cipher/_mode_ecb.pyi,sha256=PgzUPsVY2DRM72wM-h74vCiceNF_yeaBxOA6bt_ZfmA,596
+Cryptodome/Cipher/_mode_gcm.py,sha256=oAOWTQ1qynOv7BfuueXokEF1TghERzKOXEgHKMj50wg,21402
+Cryptodome/Cipher/_mode_gcm.pyi,sha256=5t72QHQS0gDq6wtzYfaVqTxmjBzpUvsQvDaP2DqNvLE,1541
+Cryptodome/Cipher/_mode_ocb.py,sha256=QeHiPunUGWKt4iG8A50xgWV9b3C7qW0uwJPRcta5bC8,19838
+Cryptodome/Cipher/_mode_ocb.pyi,sha256=SXMUa1s1dY-272lktxSOtyOoqLdtPvfNkRXqmXjBE4o,1231
+Cryptodome/Cipher/_mode_ofb.py,sha256=sXdbumASGN-K7ps89z7c-knnMnnYtuO97X7y-VYZvyA,10301
+Cryptodome/Cipher/_mode_ofb.pyi,sha256=mPIZ2e_X-URk-8LBNiZyacfcS3Ei1vgT8YlhyI-0C8k,695
+Cryptodome/Cipher/_mode_openpgp.py,sha256=whIc90qioqxMiL2RCys5-XwfDntDneVYJx4Br6X3zD4,7061
+Cryptodome/Cipher/_mode_openpgp.pyi,sha256=FoLrFqnvxJf0F_npHOgPURfUyGSt6DxyIp2ikoXi-CI,556
+Cryptodome/Cipher/_mode_siv.py,sha256=nRqGJBjqxmKikUpVBLgMN3TGwg2f3boCNtUtGcn9uEU,14094
+Cryptodome/Cipher/_mode_siv.pyi,sha256=syb3kXnyuhoQV6FXvozIjudWCQBCadOb1I2BuV-6Ai0,1261
+Cryptodome/Cipher/_pkcs1_decode.abi3.so,sha256=Gs7MlHLXtYS9vJwtYP9C_SFgixaj3lh_Zh0z1_HPq0I,28096
+Cryptodome/Cipher/_raw_aes.abi3.so,sha256=5O73otJZh11nJmZiDz8NHBOt_6D47Y2k3CYPEYFrz2M,66256
+Cryptodome/Cipher/_raw_aesni.abi3.so,sha256=AO0rOdUByVCWcLCTBh6IuKQbJAC0q8J8WlsR8cMyHPY,101136
+Cryptodome/Cipher/_raw_arc2.abi3.so,sha256=oy1fxEbBQ5iS9QSDAU5ytB8GMq15XTEEIlS_dkXalic,43776
+Cryptodome/Cipher/_raw_blowfish.abi3.so,sha256=cFJp3WClitn38q5Rd-iifcdNTLh-70jQUOwIkgNC2SI,69976
+Cryptodome/Cipher/_raw_cast.abi3.so,sha256=CNaYLRpqkaAj32xIgWHjPih9zgadaBhn8PG-4NKoHDA,42976
+Cryptodome/Cipher/_raw_cbc.abi3.so,sha256=cYJR9e74bnOiwDVFVz3rlVsDcHkl3dyCXauU9htkP_c,20736
+Cryptodome/Cipher/_raw_cfb.abi3.so,sha256=wKD1yOzB3q0mH3zuCu03yYyi5R30ElNXanKCZC_OVpA,25440
+Cryptodome/Cipher/_raw_ctr.abi3.so,sha256=cEjMcn97VwcErB4Wl2GpSgSsd0E5ip0DDIY10c_6qxs,28600
+Cryptodome/Cipher/_raw_des.abi3.so,sha256=zMDcDJtZEHcJ7binMxMuPik4jdUqIbFHTJ8v-gjdVJ8,75672
+Cryptodome/Cipher/_raw_des3.abi3.so,sha256=PXX7XjQTsvfKVWAwAKOLS0Lgphf8rJ-pdFZWRdYl4vg,76480
+Cryptodome/Cipher/_raw_ecb.abi3.so,sha256=DpU9hHt2cIK1sHBO6pfEarydsoygj8mW2EhpBCxMGuw,12440
+Cryptodome/Cipher/_raw_eksblowfish.abi3.so,sha256=WKkPH_kO_ekcYtHIOsLoSGkRFP2ipkj5MUbuW2IQ3Po,166264
+Cryptodome/Cipher/_raw_ocb.abi3.so,sha256=qN5BJM7CWH_JGcLM76corvlAPN1qwx9dAQlXJxgTBHo,37344
+Cryptodome/Cipher/_raw_ofb.abi3.so,sha256=4uEWvDqWhRqHYaxSl0AylXzZpOroiwzBdb2FvruL844,15368
+Cryptodome/Hash/BLAKE2b.py,sha256=WEV8u2cm18T_j9BwjCDPaSJ6AappqpwO1u7mHG6Sgnk,9440
+Cryptodome/Hash/BLAKE2b.pyi,sha256=U4K3mapdYeHVHrlIEgffKV9IfALVbqkOrVbJRujns10,906
+Cryptodome/Hash/BLAKE2s.py,sha256=uBxYbWDZleCmsOvSEmVRXPywTS2TM-qq3oyN4FJp68c,9446
+Cryptodome/Hash/BLAKE2s.pyi,sha256=9jsL4jLQq5_Mb8WM99LPurH1D-FL-gLAeZyBf8QiWt0,739
+Cryptodome/Hash/CMAC.py,sha256=jS4K2sJENptd8U95nUkOo_xIZPB-n-3xhXDIHDDIxsw,10379
+Cryptodome/Hash/CMAC.pyi,sha256=kZXAeUzxQ38nY-aYbIPrZZmROxgja2HnvUz7xuAXuoE,822
+Cryptodome/Hash/HMAC.py,sha256=4OfX8pS1b3a2nGF_yLNk6kh8_WjIGU75GP1nJzAWpmc,7048
+Cryptodome/Hash/HMAC.pyi,sha256=fAyHBEf5Ee6LoiYYTZ9PZpmIRvitU6OriKGfjFUM_4c,624
+Cryptodome/Hash/KMAC128.py,sha256=EuMneuZPXmX3sbCpN9ciVQyVa7p0OaaZg2bpQTif5-g,5957
+Cryptodome/Hash/KMAC128.pyi,sha256=CHcjiaNKjvWQgLXsawb3Vxttxmt_hVK-Dv-5RVs6oOE,903
+Cryptodome/Hash/KMAC256.py,sha256=F6GaAt4qtEiDgKxczWyrXROVFQaLQ2fee3vqs1uxjqo,2910
+Cryptodome/Hash/KMAC256.pyi,sha256=oAeKgyta2iqjV9Yv818xoW5eJ2ixeLP4joUP8XUi2e0,226
+Cryptodome/Hash/KangarooTwelve.py,sha256=VudGHs0siJcBTH1iP_yXaDglCcC-S2mWM-pQjenFDhA,9041
+Cryptodome/Hash/KangarooTwelve.pyi,sha256=shf_g18EQxoJ8O9Kzuah17jw6J-vzmMsuqz1mAUY5WE,572
+Cryptodome/Hash/MD2.py,sha256=mfucNRXYIj8iOXGKEXGN4TIwyHHhIglb2ScSowyg5tc,6123
+Cryptodome/Hash/MD2.pyi,sha256=wa7GSYUpzL27so4YgiJEZo0dO201KyHK87Q20Pvm-bM,492
+Cryptodome/Hash/MD4.py,sha256=FSGY7k8rYayR6PE0hNpICRTa1uNiISVxixIpaC28zzg,6598
+Cryptodome/Hash/MD4.pyi,sha256=7ZtZQEgJCwIswneb0NBov_uL0_Toglh9EPMnLVFGqwo,532
+Cryptodome/Hash/MD5.py,sha256=Cd5wtZ4OBW-O7tvebvObsay_jI6tRtoUfIfkbE0aqMQ,6630
+Cryptodome/Hash/MD5.pyi,sha256=c4MCJHvYTi2YL4hmqEu9ivbSvkBJdR-S2ldUqEpzK8s,492
+Cryptodome/Hash/Poly1305.py,sha256=xESe_sBaJg7jBJE88_S3jWU0uIu8l18NaQjmol_uIeA,8106
+Cryptodome/Hash/Poly1305.pyi,sha256=TSGottirLPIRyivSjZucQB7aPaYfhrUkn8oot6OrmmU,665
+Cryptodome/Hash/RIPEMD.py,sha256=0oRaGuKcTUVTfBZXZm5NZLQAvyPbAhjBpgzN5Kf2a6g,1211
+Cryptodome/Hash/RIPEMD.pyi,sha256=-DzZk9OtiAZE-E2_PCyFz4pHQ3RouoLlUo3Neabf3Sc,98
+Cryptodome/Hash/RIPEMD160.py,sha256=GYDiBSw_D7SCOPA80i3l3mjZsPvmmhZSVVENKwkUs_k,6410
+Cryptodome/Hash/RIPEMD160.pyi,sha256=RQ9yXxjH1BSaU3mwhsCn9-67C0a_Bcv3MDdafQCiuPs,516
+Cryptodome/Hash/SHA.py,sha256=1-O3GFKbm1ht2jV7M9pdKu6GpACsCSnARPHllFcFTAo,1156
+Cryptodome/Hash/SHA.pyi,sha256=RJHp4vuV_19StgE4qxlnIfDltgFjx-L9q6H0tjh-Rk0,169
+Cryptodome/Hash/SHA1.py,sha256=oyz9PdGgaVa90S6O1NPhe7OPq7AvDbJIsH6pICDpTN4,6702
+Cryptodome/Hash/SHA1.pyi,sha256=vNtB_b4MytJq8Io1xufdOO6VL-nMBcCnDPIgJQuNPCM,536
+Cryptodome/Hash/SHA224.py,sha256=RQ3ECOkVJsnh2a5fCPL7Aienpi22lcpsuRNUypkWfBs,6913
+Cryptodome/Hash/SHA224.pyi,sha256=8RsbyIwIfO8Fc_fpWw1MnFw04Z4n-qL0G01qCQZwvx8,544
+Cryptodome/Hash/SHA256.py,sha256=2fhVNPGKrifj49B0ExaxrMCubtDTcQiHarrzN0JPVUA,6909
+Cryptodome/Hash/SHA256.pyi,sha256=zndNEjv6DZOWaOpuoUKsA2hTi2J7-oJFgOQ10sSRnXE,612
+Cryptodome/Hash/SHA384.py,sha256=aDu5NdDbICyrSt2sj0N4fIWwGurEu08WYB5mMiqfW7g,6911
+Cryptodome/Hash/SHA384.pyi,sha256=KIWbD-lBbd7lvWgFquIqUAMaisovey0HV0Nmmq-pvOY,544
+Cryptodome/Hash/SHA3_224.py,sha256=QMB4LKdsBwHios4BZpd5pZB4ARDAv-8_m6iefFERH9I,6191
+Cryptodome/Hash/SHA3_224.pyi,sha256=YNvN-GxVpPK6_-ee0_n-7wgAhq7JzBaBaeGiNdVoQdk,605
+Cryptodome/Hash/SHA3_256.py,sha256=2dt3EToYFzCfB-SLXxLAf6GfeCN31EFoGxUZUudJi74,6191
+Cryptodome/Hash/SHA3_256.pyi,sha256=JlPOiVtEVNJerGWRuBDunXBT19WX_6ObpUuMaX7QdEs,605
+Cryptodome/Hash/SHA3_384.py,sha256=kSg7aQ8Lv4-jfDGplQ31WCTRWqCb_K8X7AOtO5looXM,6286
+Cryptodome/Hash/SHA3_384.pyi,sha256=c3wb0c6RjlcMcK22mV4RZWsDmUA2NszaghLAXIrN8T8,605
+Cryptodome/Hash/SHA3_512.py,sha256=FPtL81mBU7Gi1ije5M2EXwCPj598AoSOyKRrri4GTPU,6143
+Cryptodome/Hash/SHA3_512.pyi,sha256=bZ0WozTD_mQ_5t_z4SWCpCn61YhCVamF501jsQdUjps,605
+Cryptodome/Hash/SHA512.py,sha256=rmSnd1Rka2NCImO0e-suLbzAF9EEXrY1ipnnS8RlG-Q,7732
+Cryptodome/Hash/SHA512.pyi,sha256=VfMzHx-0U4efCyZCrgs_aOz17W8t0ZHL_3uR8zaYzCU,622
+Cryptodome/Hash/SHAKE128.py,sha256=zU7uDVCatsWrg20zbBgWyrlQZCPdffn_p80PhK9-nP4,4773
+Cryptodome/Hash/SHAKE128.pyi,sha256=wLhV8lh8YYWzi7PkhAB3_JQn_hOZNvkiZYg-JjiPpfs,437
+Cryptodome/Hash/SHAKE256.py,sha256=YHXXxK-1cqZv0CRwvTYyEWwf2OfZdVBW6_Jpw8ESOcM,4774
+Cryptodome/Hash/SHAKE256.pyi,sha256=9Uq_FaeYwDx_6dLv331Wv1snnGxA2UhFcUdELHkwU9U,437
+Cryptodome/Hash/TupleHash128.py,sha256=zixd7Pc-uBYkPoqV6PdJfY_hwKRTgDE3CCHhsSG-Dss,4724
+Cryptodome/Hash/TupleHash128.pyi,sha256=fXkKNiV-HubXeCBcBTlAtHlYXwWeQ3iDcOWCs11iIfk,652
+Cryptodome/Hash/TupleHash256.py,sha256=mV6ygJKOY6FUUnYVUFQr3qzxlVT2GC0Wu_4zQjReXwg,2910
+Cryptodome/Hash/TupleHash256.pyi,sha256=esuouWh2HmCu3M4kLjCgu5jrQ87NrBQU5h9o5x21kl0,144
+Cryptodome/Hash/_BLAKE2b.abi3.so,sha256=rk9tPFurUM1tLtloHo59tqMRs7RIvYJGhDLvHQja30Q,21888
+Cryptodome/Hash/_BLAKE2s.abi3.so,sha256=AgnTEq1__qPHvWZnofBb-6Dx4v8jpNA49lwfGmNOUzg,21712
+Cryptodome/Hash/_MD2.abi3.so,sha256=G1xHOoNRg_ovYMYpivT_WWw4tVZcnpb6mGTYbsExZJg,20128
+Cryptodome/Hash/_MD4.abi3.so,sha256=Chb-bqxLPevnCXpxFusErdsgz5QjynMQ8vzg_Q-T0Pw,25576
+Cryptodome/Hash/_MD5.abi3.so,sha256=GNCB_dmGXyuAstTpUekMWZpsTUx_JCTQvhbtxfg_Kuc,31704
+Cryptodome/Hash/_RIPEMD160.abi3.so,sha256=tm5wJWBsY6x9456Ydu3GE8kera119wpNQV4ZC0SGUA8,55608
+Cryptodome/Hash/_SHA1.abi3.so,sha256=HLUzfXUAta_Hc9OhzTkZPgP9sPDLcxq94iei0_8SohE,74416
+Cryptodome/Hash/_SHA224.abi3.so,sha256=nIzcwdudljksQZt4xm9D_Wjy-E6bRcittLF7zFYmJNs,43792
+Cryptodome/Hash/_SHA256.abi3.so,sha256=fPQsB17w0oozWP1RwWWRULvdaE7zNXqzXjjXaA1-Ow4,43872
+Cryptodome/Hash/_SHA384.abi3.so,sha256=x_gHKHoOIEi87bwUjmoyeMcJ462UkpaHEkS-vWpH8BI,50520
+Cryptodome/Hash/_SHA512.abi3.so,sha256=JzPo_PEMB_4QwbaBHWwGi92qYKpynNp9XA5J1AbuigI,50624
+Cryptodome/Hash/__init__.py,sha256=vaCd9NlWJbYWuDUu8QZAt_6DdW0mk1OAmVDvyG7-Izg,1239
+Cryptodome/Hash/__init__.pyi,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+Cryptodome/Hash/__pycache__/BLAKE2b.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/BLAKE2s.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/CMAC.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/HMAC.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/KMAC128.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/KMAC256.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/KangarooTwelve.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/MD2.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/MD4.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/MD5.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/Poly1305.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/RIPEMD.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/RIPEMD160.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/SHA.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/SHA1.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/SHA224.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/SHA256.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/SHA384.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/SHA3_224.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/SHA3_256.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/SHA3_384.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/SHA3_512.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/SHA512.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/SHAKE128.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/SHAKE256.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/TupleHash128.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/TupleHash256.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/__init__.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/cSHAKE128.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/cSHAKE256.cpython-310.pyc,,
+Cryptodome/Hash/__pycache__/keccak.cpython-310.pyc,,
+Cryptodome/Hash/_ghash_clmul.abi3.so,sha256=10DxDhDR3ioWi4_jTZsbXks8Y_WVBKHrwqK5DQwuv4w,50160
+Cryptodome/Hash/_ghash_portable.abi3.so,sha256=dA74EksFW2fzD3kI2W2qXOjPU63U9QrCZdo4VuzkiCo,17432
+Cryptodome/Hash/_keccak.abi3.so,sha256=trhgcIdYGuRwZdWf37Y3maxEAne5I20djgOVgQBod0M,35064
+Cryptodome/Hash/_poly1305.abi3.so,sha256=ijhYY5jdTRZEhtpZNSVhogK8Rygbs1p37hDOwHDX4vI,33360
+Cryptodome/Hash/cSHAKE128.py,sha256=hJea8EFx0wre-IAOxqVb0LMCXrUtzToc6kXWDVv_rAw,6333
+Cryptodome/Hash/cSHAKE128.pyi,sha256=ILenFDiznj1cjIXbjIBvzEqfFOjObKXWyMsOr63awUo,499
+Cryptodome/Hash/cSHAKE256.py,sha256=CEOEmp5VUTahU-dTEjrI7YXR2pUFvpgTS7jok1G7hK8,2210
+Cryptodome/Hash/cSHAKE256.pyi,sha256=MrVXayegTwcQej1ZWv24QDjJtjnUMocs_Z6Li52ejq0,235
+Cryptodome/Hash/keccak.py,sha256=10FAft3funlFws9lcRlB0cKNUC9q6bsgJeuhaTa_5GA,7555
+Cryptodome/Hash/keccak.pyi,sha256=pXAZaNfayZCXMxB7IDFr2F8Hi06_hwFB3GXjNzY7sBM,741
+Cryptodome/IO/PEM.py,sha256=TPPdUiCHLEauwRAIZ1Y0uxtIGzz5Vz5eF5EZsP3dGRc,6972
+Cryptodome/IO/PEM.pyi,sha256=a1G07RQtZvEtXHlybxdDcoTPM3nqMbdONNjzcz5HGtE,303
+Cryptodome/IO/PKCS8.py,sha256=8pbvHQrbZFN7oqaxTp_NiE8SBXO8c8ZCmvhlCc576ck,9086
+Cryptodome/IO/PKCS8.pyi,sha256=2O5vmzNz8oGsXT_iV0qL0HRzlJFfHFX6A5QWLQB3IfU,484
+Cryptodome/IO/_PBES.py,sha256=ZAoljR3_saaEGvNmv1Zjkcp-Up925t6lH0GHbPAzZP4,16352
+Cryptodome/IO/_PBES.pyi,sha256=QWJLbYh7ywy2wlRWnbUQG_hqlv6zfobF5o6FKh7reWA,489
+Cryptodome/IO/__init__.py,sha256=QUvnoDWlmuOGEjxXh_uXHMoSmoPi_nSeh-Et7MSofeg,1540
+Cryptodome/IO/__pycache__/PEM.cpython-310.pyc,,
+Cryptodome/IO/__pycache__/PKCS8.cpython-310.pyc,,
+Cryptodome/IO/__pycache__/_PBES.cpython-310.pyc,,
+Cryptodome/IO/__pycache__/__init__.cpython-310.pyc,,
+Cryptodome/Math/Numbers.py,sha256=2fQR0NxDh4r8rxGGrIahadGXQ5ssQb2nNPuhC1O9S0Q,2042
+Cryptodome/Math/Numbers.pyi,sha256=DBEdhel2f5i097pHa5ZTccxyGf0rKfhXtJP4GiNbP_Q,88
+Cryptodome/Math/Primality.py,sha256=1Y9yDY1B8P3b0cVOxjyctOQs67Ib2Furtvw8QcPWNYI,11387
+Cryptodome/Math/Primality.pyi,sha256=iXAY0gUmciIS_FvH5VJwhQfK-0tDmaH2vcDLHHFyxIE,823
+Cryptodome/Math/_IntegerBase.py,sha256=IhXzQnSDATrbX03Kmo4_FqyOgB2DO3uxbtEXrWqgSd8,10516
+Cryptodome/Math/_IntegerBase.pyi,sha256=Ovo8qweHd6TR7h5506CiwaN9v0L-isgOfUu9YJb6Fr4,3470
+Cryptodome/Math/_IntegerCustom.py,sha256=GI7-e6DTg089x7YzZodhDzxZpKiuRCirMCE1APgecsY,4266
+Cryptodome/Math/_IntegerCustom.pyi,sha256=s9UZigBEgUvHS4IOdt8jXhsZ33O9j19p7lieob1R-EY,135
+Cryptodome/Math/_IntegerGMP.py,sha256=J0TYHwbZHUXWJwB-BTH7B_CCPMYbo6AqN3VIFGi2jok,26755
+Cryptodome/Math/_IntegerGMP.pyi,sha256=UcJOGMYT1d-G0PjbC5ByShFl5oyorFR8h38fFt0uY9s,78
+Cryptodome/Math/_IntegerNative.py,sha256=mg6jDm4kvTMZvWa_LUHV8t1Zgi3M3omO5kzIwGpQeEg,11595
+Cryptodome/Math/_IntegerNative.pyi,sha256=pZaN1xXnB8u7VfrMgp6jqi_jCaJ4x4t0Ecs7qZ_2x-4,81
+Cryptodome/Math/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+Cryptodome/Math/__pycache__/Numbers.cpython-310.pyc,,
+Cryptodome/Math/__pycache__/Primality.cpython-310.pyc,,
+Cryptodome/Math/__pycache__/_IntegerBase.cpython-310.pyc,,
+Cryptodome/Math/__pycache__/_IntegerCustom.cpython-310.pyc,,
+Cryptodome/Math/__pycache__/_IntegerGMP.cpython-310.pyc,,
+Cryptodome/Math/__pycache__/_IntegerNative.cpython-310.pyc,,
+Cryptodome/Math/__pycache__/__init__.cpython-310.pyc,,
+Cryptodome/Math/_modexp.abi3.so,sha256=C8DwJW3oU_w1-hLXRstKQqKoYriSd_2IrO3XTUrrQx0,294464
+Cryptodome/Protocol/KDF.py,sha256=nmwDb1qTj5fHNSWZdLVwl_CyjlbOjM6rUDSl8wNLcUM,19897
+Cryptodome/Protocol/KDF.pyi,sha256=OfuAajDDJIDIny-zMuGsfhqCLZr4x8bZnV5Tonbg00E,1383
+Cryptodome/Protocol/SecretSharing.py,sha256=ZNw_YhVs4TYrJ7075g6WwaiiCJRRmy0fpT_LoEMV3Ww,8794
+Cryptodome/Protocol/SecretSharing.pyi,sha256=-lErV2RvaNPuOA0z4c44WmNSu9irCw_DDb7wPgCS2BY,798
+Cryptodome/Protocol/__init__.py,sha256=eXlh5nJVd6NoXfUjJ-mNGgm5oE8r6MYDBOIHXWdzTPw,1548
+Cryptodome/Protocol/__init__.pyi,sha256=RNdrwMgjt9b9LmckdRkaYYC4PCzNV-1Hi2T3B2MHgds,43
+Cryptodome/Protocol/__pycache__/KDF.cpython-310.pyc,,
+Cryptodome/Protocol/__pycache__/SecretSharing.cpython-310.pyc,,
+Cryptodome/Protocol/__pycache__/__init__.cpython-310.pyc,,
+Cryptodome/Protocol/_scrypt.abi3.so,sha256=sXGTqBkIBy0Yy1-4gJI4dVLNF5_aJ2Nnjre5dtdYFrw,25024
+Cryptodome/PublicKey/DSA.py,sha256=SMeRm30ER5SXkNZoypeV5JZ1SL5rl_H65BZcM56IS8M,22434
+Cryptodome/PublicKey/DSA.pyi,sha256=t6y3t_w_odo5exLTS4K3_d76ObdqfN6R1QHKOff_LA4,1381
+Cryptodome/PublicKey/ECC.py,sha256=LIhFKa0ZpMJV_AR2x4F1DKfEjkn0EI_Gq_OI6u5jZi8,64692
+Cryptodome/PublicKey/ECC.pyi,sha256=xuYWrrPI0Ox7vA1wTfFN4AP5i5t13V9hiMEwS99aTSg,2567
+Cryptodome/PublicKey/ElGamal.py,sha256=qe1JXZRLCnMQO8u892VZA80u92IBXnTq-rV0CcvMKa8,8631
+Cryptodome/PublicKey/ElGamal.pyi,sha256=-s3ty0v_o-8Rq8_nrYh32Vo6ihr8OaSWdc_H7_CVGCo,674
+Cryptodome/PublicKey/RSA.py,sha256=mINA8PTwVuzfK92odBbdEpCjEWHn1uvL1W8k4pIkD3g,29118
+Cryptodome/PublicKey/RSA.pyi,sha256=UpGjHMfe8UvuQPVX6NaMMezyAQw8TmltVxrLeUzr2tc,1865
+Cryptodome/PublicKey/__init__.py,sha256=b7qxFPdI5bbSq5kLLiGtQHtueuDrKhqXAUeZHVyID_Y,3146
+Cryptodome/PublicKey/__init__.pyi,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+Cryptodome/PublicKey/__pycache__/DSA.cpython-310.pyc,,
+Cryptodome/PublicKey/__pycache__/ECC.cpython-310.pyc,,
+Cryptodome/PublicKey/__pycache__/ElGamal.cpython-310.pyc,,
+Cryptodome/PublicKey/__pycache__/RSA.cpython-310.pyc,,
+Cryptodome/PublicKey/__pycache__/__init__.cpython-310.pyc,,
+Cryptodome/PublicKey/__pycache__/_openssh.cpython-310.pyc,,
+Cryptodome/PublicKey/_ec_ws.abi3.so,sha256=a0LjIvJ7c2H7_yt7_1Bpwu7_ehDtFS3XXE_issknhF8,1068008
+Cryptodome/PublicKey/_ed25519.abi3.so,sha256=RkRLQJjk0lPnNl70umyAlbFOGczpS6DWhk5BGxhrd10,578280
+Cryptodome/PublicKey/_ed448.abi3.so,sha256=HnXQi8UZnk49S2XQEiP7Xb-r6-T8w3JQlDQNaPgOC7E,329424
+Cryptodome/PublicKey/_openssh.py,sha256=t8Z8fOJDh64ZXmqpSCqlds-OKb2C5FncQmqCstfGImM,5146
+Cryptodome/PublicKey/_openssh.pyi,sha256=ywCy9UDu2_AQI60ChWxGxyqHiZoYwMKC3TVXJn_ZVIM,324
+Cryptodome/PublicKey/_x25519.abi3.so,sha256=dbOfmyCjJlelCs7aWvVV_GalZOlDTxkenZs0t8MZEFA,124632
+Cryptodome/Random/__init__.py,sha256=EJnd9lTXo5ZFvmgrAzv2wHX8b87SNPO3sDGB3UofVe0,1813
+Cryptodome/Random/__init__.pyi,sha256=ieifhoMB2veKusRRBZWQp6igPri5027VrqfddO5b-WU,367
+Cryptodome/Random/__pycache__/__init__.cpython-310.pyc,,
+Cryptodome/Random/__pycache__/random.cpython-310.pyc,,
+Cryptodome/Random/random.py,sha256=aWcD7vA4eWrRmjIkho-YGxbrVC3anFTzHa_N_7Sds64,5246
+Cryptodome/Random/random.pyi,sha256=Lgo1h6wtyUDhEuroDRyt-eYvPFEgQOo0fxfAE68S2cM,807
+Cryptodome/SelfTest/Cipher/__init__.py,sha256=nI0MW4-BVQHwCwYqgWxa7MhL9OxYmwYSIJcp5qe_n9Y,3708
+Cryptodome/SelfTest/Cipher/__pycache__/__init__.cpython-310.pyc,,
+Cryptodome/SelfTest/Cipher/__pycache__/common.cpython-310.pyc,,
+Cryptodome/SelfTest/Cipher/__pycache__/test_AES.cpython-310.pyc,,
+Cryptodome/SelfTest/Cipher/__pycache__/test_ARC2.cpython-310.pyc,,
+Cryptodome/SelfTest/Cipher/__pycache__/test_ARC4.cpython-310.pyc,,
+Cryptodome/SelfTest/Cipher/__pycache__/test_Blowfish.cpython-310.pyc,,
+Cryptodome/SelfTest/Cipher/__pycache__/test_CAST.cpython-310.pyc,,
+Cryptodome/SelfTest/Cipher/__pycache__/test_CBC.cpython-310.pyc,,
+Cryptodome/SelfTest/Cipher/__pycache__/test_CCM.cpython-310.pyc,,
+Cryptodome/SelfTest/Cipher/__pycache__/test_CFB.cpython-310.pyc,,
+Cryptodome/SelfTest/Cipher/__pycache__/test_CTR.cpython-310.pyc,,
+Cryptodome/SelfTest/Cipher/__pycache__/test_ChaCha20.cpython-310.pyc,,
+Cryptodome/SelfTest/Cipher/__pycache__/test_ChaCha20_Poly1305.cpython-310.pyc,,
+Cryptodome/SelfTest/Cipher/__pycache__/test_DES.cpython-310.pyc,,
+Cryptodome/SelfTest/Cipher/__pycache__/test_DES3.cpython-310.pyc,,
+Cryptodome/SelfTest/Cipher/__pycache__/test_EAX.cpython-310.pyc,,
+Cryptodome/SelfTest/Cipher/__pycache__/test_GCM.cpython-310.pyc,,
+Cryptodome/SelfTest/Cipher/__pycache__/test_OCB.cpython-310.pyc,,
+Cryptodome/SelfTest/Cipher/__pycache__/test_OFB.cpython-310.pyc,,
+Cryptodome/SelfTest/Cipher/__pycache__/test_OpenPGP.cpython-310.pyc,,
+Cryptodome/SelfTest/Cipher/__pycache__/test_SIV.cpython-310.pyc,,
+Cryptodome/SelfTest/Cipher/__pycache__/test_Salsa20.cpython-310.pyc,,
+Cryptodome/SelfTest/Cipher/__pycache__/test_pkcs1_15.cpython-310.pyc,,
+Cryptodome/SelfTest/Cipher/__pycache__/test_pkcs1_oaep.cpython-310.pyc,,
+Cryptodome/SelfTest/Cipher/common.py,sha256=CESk5eoHx2JAa75YyaAeBMHHvQPEUK_v0uAoPbdNXbg,17336
+Cryptodome/SelfTest/Cipher/test_AES.py,sha256=vANeFFPc2fuUWyI7Tj1k6XBvu0STiW1Cv1f78fgi2Bw,71751
+Cryptodome/SelfTest/Cipher/test_ARC2.py,sha256=gH2ig-tNiYbN5_jsv6HseQWwRdkK6Z1RrQPRJ9an468,6474
+Cryptodome/SelfTest/Cipher/test_ARC4.py,sha256=qTb9rtaqd0cnx4soCOheEI6Dz95U8QhwQQbSc9aeAzY,24746
+Cryptodome/SelfTest/Cipher/test_Blowfish.py,sha256=H3L9l_IfNvxzvtxixq4frH5ToLOA1HGnQoF08DHB5LM,7242
+Cryptodome/SelfTest/Cipher/test_CAST.py,sha256=N-JSM-XxnsbaCk2lNcDVIARuM1oKqRKh2cEXzGBSaLI,3291
+Cryptodome/SelfTest/Cipher/test_CBC.py,sha256=uFPMTC2Lb7_q36TNL_wHfRJDlT1jSpKJrAkJDTVe2oU,20222
+Cryptodome/SelfTest/Cipher/test_CCM.py,sha256=ClkcCmUY1STuLkwZFruBgw0qnSzO5M9rXfwH3SmPsWY,37332
+Cryptodome/SelfTest/Cipher/test_CFB.py,sha256=IRdRnbh4PHKVVY8fIlsrnxUuSRuBGOFucg0NueyesbQ,16085
+Cryptodome/SelfTest/Cipher/test_CTR.py,sha256=rP21pAWZ6nBJ2JuEcYptWdEVi47xlS16PaGQuCCdgMk,21334
+Cryptodome/SelfTest/Cipher/test_ChaCha20.py,sha256=yCLZ61H1ovarvEhW-qoeIIDBuEQix--AqFY2QUjAZro,20340
+Cryptodome/SelfTest/Cipher/test_ChaCha20_Poly1305.py,sha256=zmntnmKmEhIhVavSLZLDUVafkGsX7E_5E2voUXvaSlQ,30531
+Cryptodome/SelfTest/Cipher/test_DES.py,sha256=EsgQR9gvvJEdrr58FCD1aUzmyjbLT-FMawoFY_gMqo4,15951
+Cryptodome/SelfTest/Cipher/test_DES3.py,sha256=25EfIOeEgGstrwWK4i5IBCDecuaDbhOdQ4_4d_n52ls,6585
+Cryptodome/SelfTest/Cipher/test_EAX.py,sha256=K8zhU9gfDMqQy24xKgSllnNqeUTNegQZKI8SXcUgEP0,28851
+Cryptodome/SelfTest/Cipher/test_GCM.py,sha256=UDSFocg2zPJufcXLv8Gjwvsx44tizbWJ3Twg2UFRk78,37308
+Cryptodome/SelfTest/Cipher/test_OCB.py,sha256=-6fQfH0Lx8zbuI__EXJfLoQnczeUiE-qeBq5IW1M3lo,28081
+Cryptodome/SelfTest/Cipher/test_OFB.py,sha256=zHzonGcFTQibtsETUlIJtAmui6cgxzug4YpQtGhhFD0,9395
+Cryptodome/SelfTest/Cipher/test_OpenPGP.py,sha256=Q4rOCU0axh_z7U4MSUGda39nujktPlBUaqVVfE5kMhs,8497
+Cryptodome/SelfTest/Cipher/test_SIV.py,sha256=fDE9kmdYmKn0M4bup4LzHCyzIGgEm8YKMf2M2pMSwNA,19967
+Cryptodome/SelfTest/Cipher/test_Salsa20.py,sha256=h8ubtHJCdfGDbxlKPM_pbTPJ3eoribIm36V-ieyL4z0,16607
+Cryptodome/SelfTest/Cipher/test_pkcs1_15.py,sha256=3Gmd9PbhgpvSu8c6YYzJ9ukRwLaHiVr_AX-wsQq3hjg,10972
+Cryptodome/SelfTest/Cipher/test_pkcs1_oaep.py,sha256=3BHUsR_dAJPg5I1VnR1Mb1gJ2MKK-LnK3MSBnThRNZ0,22322
+Cryptodome/SelfTest/Hash/__init__.py,sha256=dm5gAH4OQtUS3NfRVz9XyfdTSBWDw3Jx5iFIhCAHzmk,3805
+Cryptodome/SelfTest/Hash/__pycache__/__init__.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/__pycache__/common.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/__pycache__/test_BLAKE2.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/__pycache__/test_CMAC.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/__pycache__/test_HMAC.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/__pycache__/test_KMAC.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/__pycache__/test_KangarooTwelve.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/__pycache__/test_MD2.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/__pycache__/test_MD4.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/__pycache__/test_MD5.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/__pycache__/test_Poly1305.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/__pycache__/test_RIPEMD160.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/__pycache__/test_SHA1.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/__pycache__/test_SHA224.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/__pycache__/test_SHA256.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/__pycache__/test_SHA384.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/__pycache__/test_SHA3_224.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/__pycache__/test_SHA3_256.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/__pycache__/test_SHA3_384.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/__pycache__/test_SHA3_512.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/__pycache__/test_SHA512.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/__pycache__/test_SHAKE.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/__pycache__/test_TupleHash.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/__pycache__/test_cSHAKE.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/__pycache__/test_keccak.cpython-310.pyc,,
+Cryptodome/SelfTest/Hash/common.py,sha256=_pT1G85L9g04kcFfbXMqhOkzQHDEPbaaOou6b4BFU6k,9906
+Cryptodome/SelfTest/Hash/test_BLAKE2.py,sha256=ezsH9MxKpteMzAJI51vCqRauKdQPqlEhpo0i5y1Yu5Q,16330
+Cryptodome/SelfTest/Hash/test_CMAC.py,sha256=hmOn4XLQJjRbThhejhyTW4EqOQy89q93lUFX3P_IABU,13392
+Cryptodome/SelfTest/Hash/test_HMAC.py,sha256=xqro9TABaXBBCfd8kYK_oAKSYnTlBlR0zshZjy_QsKU,19953
+Cryptodome/SelfTest/Hash/test_KMAC.py,sha256=9GJEEw30275onW7fDSat9A6IEA83GliVdKzyXdYjNtM,11720
+Cryptodome/SelfTest/Hash/test_KangarooTwelve.py,sha256=yi-BwMLLzlpbu7ye7q-omB0zqNdd9l2q9QtNxq9LWJw,10471
+Cryptodome/SelfTest/Hash/test_MD2.py,sha256=ElKzN0tL3U-0TeLVP0e7I8txskHXSA8Ys63aYjLW_BM,2336
+Cryptodome/SelfTest/Hash/test_MD4.py,sha256=9SFHZcbB3M2AhjYtK-Y9w0tWE_cdmeo2QrHvl3f9S4U,2359
+Cryptodome/SelfTest/Hash/test_MD5.py,sha256=Bj6C548u8dz0d-pGxg7JZlezPWuXfD_TBnj7QOJGtxw,3300
+Cryptodome/SelfTest/Hash/test_Poly1305.py,sha256=99gYRntogrqE_dha6UiuFA7-IQiuP5RcLeh7an18N2g,18329
+Cryptodome/SelfTest/Hash/test_RIPEMD160.py,sha256=V1q7YGF0xKe0DeXZhAmtc4hIbbDKhEpjkNfPsNkB-qE,2675
+Cryptodome/SelfTest/Hash/test_SHA1.py,sha256=5TO0WBmJoGUeQBoy9qhF3Oc4UqUQAl4ItuyT2zkTbic,2938
+Cryptodome/SelfTest/Hash/test_SHA224.py,sha256=Dz7sRLuh-AJZXarIZ7Lza4ZUy9tNNT5pUSsAm5Q6Ax0,2541
+Cryptodome/SelfTest/Hash/test_SHA256.py,sha256=pmV4r5WojFZmtVWAIFAl1uO55GO7wMWXCdE_I2e87J0,3637
+Cryptodome/SelfTest/Hash/test_SHA384.py,sha256=6_hL7uoZdQYAEoC0gT2mYuZQuCELHe9ebxcFhQZd4BM,2722
+Cryptodome/SelfTest/Hash/test_SHA3_224.py,sha256=e4ffDcIbVOrsJx3wgSC6GPxcH4jqI5qnWAOrUl4FC1Y,2850
+Cryptodome/SelfTest/Hash/test_SHA3_256.py,sha256=Gekywn54izdEym1g80S9G24cU8_9BS5fTWlZGU-QXuQ,2851
+Cryptodome/SelfTest/Hash/test_SHA3_384.py,sha256=ad0T2zBfNLCLYkLDdefvgFmrAuv-3pNmky8lU0xx40g,2850
+Cryptodome/SelfTest/Hash/test_SHA3_512.py,sha256=-OHjltf_USzOwEZMyJ7mIGnLQg67QUYkcx4OxWhkKN0,2851
+Cryptodome/SelfTest/Hash/test_SHA512.py,sha256=FfpO8kYCnx_fw7s8-FdiK-9245w-X8t49Ca2PG2gfUs,5210
+Cryptodome/SelfTest/Hash/test_SHAKE.py,sha256=kC0-dOsnXMQyofD8y8TnMphXNwMsL4ACEGelUppHrzw,4735
+Cryptodome/SelfTest/Hash/test_TupleHash.py,sha256=aMFuVqU2WfH73nEJoZkbp_ztJR2kV9AnwOi18i9PpaE,8147
+Cryptodome/SelfTest/Hash/test_cSHAKE.py,sha256=X0QOn_DKGU3AS_DBsXF8IEGPSFLWM-Yp-Ia_oIdZRLY,6820
+Cryptodome/SelfTest/Hash/test_keccak.py,sha256=-DjnqvCGwDCQ-VWVfpVP3Dwyfm2Lwk7P1E7_aIydioQ,8909
+Cryptodome/SelfTest/IO/__init__.py,sha256=62l-NkQk9WPrRYoDAbtWKMOc9LT5yAE6ENws7t7-uzU,2002
+Cryptodome/SelfTest/IO/__pycache__/__init__.cpython-310.pyc,,
+Cryptodome/SelfTest/IO/__pycache__/test_PBES.cpython-310.pyc,,
+Cryptodome/SelfTest/IO/__pycache__/test_PKCS8.cpython-310.pyc,,
+Cryptodome/SelfTest/IO/test_PBES.py,sha256=sCOtJYeUCsSYM1NRYpweWzlUgrrc8W4vSHLIdi2s_aE,3469
+Cryptodome/SelfTest/IO/test_PKCS8.py,sha256=5yCEFmxCYSGxD9_G9FKN5QyoQ2f4JoMpaAjSVchXMTU,17621
+Cryptodome/SelfTest/Math/__init__.py,sha256=EkOt_fJnPR9-LS36rE0xm0j1nlj-I0Y9CPnQk1KO29E,2113
+Cryptodome/SelfTest/Math/__pycache__/__init__.cpython-310.pyc,,
+Cryptodome/SelfTest/Math/__pycache__/test_Numbers.cpython-310.pyc,,
+Cryptodome/SelfTest/Math/__pycache__/test_Primality.cpython-310.pyc,,
+Cryptodome/SelfTest/Math/__pycache__/test_modexp.cpython-310.pyc,,
+Cryptodome/SelfTest/Math/test_Numbers.py,sha256=tCXXA2GSkvOk_y-oEJ7mIT1CgmCyDIfKKK6CQydVqoU,30802
+Cryptodome/SelfTest/Math/test_Primality.py,sha256=0V_gzcBWyk1SFYEBcsEgYVaPfw3-UZtcWl0etV_bgvA,4901
+Cryptodome/SelfTest/Math/test_modexp.py,sha256=rsoK86G8jDUNPiVg5uu3c3S6XVN4wz9dl_ty0oajcwI,8135
+Cryptodome/SelfTest/Protocol/__init__.py,sha256=M2Sh9OvDVzEqup__hYYipuAqXvBwEHSooPPz4meBCyo,1763
+Cryptodome/SelfTest/Protocol/__pycache__/__init__.cpython-310.pyc,,
+Cryptodome/SelfTest/Protocol/__pycache__/test_KDF.cpython-310.pyc,,
+Cryptodome/SelfTest/Protocol/__pycache__/test_SecretSharing.cpython-310.pyc,,
+Cryptodome/SelfTest/Protocol/__pycache__/test_rfc1751.cpython-310.pyc,,
+Cryptodome/SelfTest/Protocol/test_KDF.py,sha256=iz5LX9r30DmICJ-BYhsHgHeZLOGTJ-MPncTsr096FL0,34041
+Cryptodome/SelfTest/Protocol/test_SecretSharing.py,sha256=1VHyld5BrNuwZ8NnR55Zsmnex92F1DudMr-z2aMueKA,9701
+Cryptodome/SelfTest/Protocol/test_rfc1751.py,sha256=6QuxUUE-NP8_1tQNj9Macjtc540zk4j85Z7G1Nyy2cI,2220
+Cryptodome/SelfTest/PublicKey/__init__.py,sha256=4mg8NCPsjyaMn6ww46sHNpc2N12E2nTx9bggcIUxD6I,2122
+Cryptodome/SelfTest/PublicKey/__pycache__/__init__.cpython-310.pyc,,
+Cryptodome/SelfTest/PublicKey/__pycache__/test_DSA.cpython-310.pyc,,
+Cryptodome/SelfTest/PublicKey/__pycache__/test_ECC_25519.cpython-310.pyc,,
+Cryptodome/SelfTest/PublicKey/__pycache__/test_ECC_448.cpython-310.pyc,,
+Cryptodome/SelfTest/PublicKey/__pycache__/test_ECC_NIST.cpython-310.pyc,,
+Cryptodome/SelfTest/PublicKey/__pycache__/test_ElGamal.cpython-310.pyc,,
+Cryptodome/SelfTest/PublicKey/__pycache__/test_RSA.cpython-310.pyc,,
+Cryptodome/SelfTest/PublicKey/__pycache__/test_import_DSA.cpython-310.pyc,,
+Cryptodome/SelfTest/PublicKey/__pycache__/test_import_ECC.cpython-310.pyc,,
+Cryptodome/SelfTest/PublicKey/__pycache__/test_import_RSA.cpython-310.pyc,,
+Cryptodome/SelfTest/PublicKey/test_DSA.py,sha256=TMEwgUkYF467S9EHMoapNcl3QY6SAWZb3kiWEI65okk,9636
+Cryptodome/SelfTest/PublicKey/test_ECC_25519.py,sha256=HjSq3QWZk_4qq2p76NOsKhMdqKD4dcdE5gi3gPwkOc0,13464
+Cryptodome/SelfTest/PublicKey/test_ECC_448.py,sha256=mcyh3XmV095T-ogxBRzh0tThmsOLt922EG4S08hZOpo,14693
+Cryptodome/SelfTest/PublicKey/test_ECC_NIST.py,sha256=jQRrcO21-81L1UA7WMS4qprOTpPhvphGkU-DnF3x0Ow,49992
+Cryptodome/SelfTest/PublicKey/test_ElGamal.py,sha256=NaGeuJcKfcm5-E-x-WfbzA8oyfPDjR_94hNkvV0cKOE,8672
+Cryptodome/SelfTest/PublicKey/test_RSA.py,sha256=zZNy_X6BXj83p0cjnxoWvIO4Yrn4G6Kdkz2sNqtzai8,12297
+Cryptodome/SelfTest/PublicKey/test_import_DSA.py,sha256=6fLmKITSoP15dlaKZq1dt38gHpUsjmY6Z4gR0H5Bckc,25521
+Cryptodome/SelfTest/PublicKey/test_import_ECC.py,sha256=_6IxIIzIyjtFkS6wxnU5OoUPYRaD8Kj2ucThTXgEMt8,102846
+Cryptodome/SelfTest/PublicKey/test_import_RSA.py,sha256=4fo3HS8FarHTxLJt1AJHw2OKtwn869_Spdncm_L0Eck,25115
+Cryptodome/SelfTest/Random/__init__.py,sha256=vWmKA--IXzOIszf150wbGq1-OTAVBTI3sqInjutIBWk,1546
+Cryptodome/SelfTest/Random/__pycache__/__init__.cpython-310.pyc,,
+Cryptodome/SelfTest/Random/__pycache__/test_random.cpython-310.pyc,,
+Cryptodome/SelfTest/Random/test_random.py,sha256=VtbtEXdP_6EaxZ_hRL3rP-HrBVBEqb_ANN7l8HfoRFM,7014
+Cryptodome/SelfTest/Signature/__init__.py,sha256=4BxIOB9IMey1d1Xixyl7mRYJYnuzNpXk83KlzGBiEEM,1558
+Cryptodome/SelfTest/Signature/__pycache__/__init__.cpython-310.pyc,,
+Cryptodome/SelfTest/Signature/__pycache__/test_dss.cpython-310.pyc,,
+Cryptodome/SelfTest/Signature/__pycache__/test_eddsa.cpython-310.pyc,,
+Cryptodome/SelfTest/Signature/__pycache__/test_pkcs1_15.cpython-310.pyc,,
+Cryptodome/SelfTest/Signature/__pycache__/test_pss.cpython-310.pyc,,
+Cryptodome/SelfTest/Signature/test_dss.py,sha256=fLe0KW8pFdZH7KBmZaXgELY1ULLeuWpuaA4AandWD90,57130
+Cryptodome/SelfTest/Signature/test_eddsa.py,sha256=h8pFZsdJbUiD0QArIl9fFhfz48ldDM2AEE8Ac5ayPYY,24154
+Cryptodome/SelfTest/Signature/test_pkcs1_15.py,sha256=6pdQ_7oIKlDeaDOx0m5XSSpRI63IuaT7li3f8ntwxSg,13601
+Cryptodome/SelfTest/Signature/test_pss.py,sha256=U25BI7C3NOzmNLTsJB5ESh7aWhO07F49V1CVfaojlRo,15867
+Cryptodome/SelfTest/Util/__init__.py,sha256=0Ov0gHqo91NR0f639IzQMU-UX1pxOm90PI-uUaOJlro,2021
+Cryptodome/SelfTest/Util/__pycache__/__init__.cpython-310.pyc,,
+Cryptodome/SelfTest/Util/__pycache__/test_Counter.cpython-310.pyc,,
+Cryptodome/SelfTest/Util/__pycache__/test_Padding.cpython-310.pyc,,
+Cryptodome/SelfTest/Util/__pycache__/test_asn1.cpython-310.pyc,,
+Cryptodome/SelfTest/Util/__pycache__/test_number.cpython-310.pyc,,
+Cryptodome/SelfTest/Util/__pycache__/test_rfc1751.cpython-310.pyc,,
+Cryptodome/SelfTest/Util/__pycache__/test_strxor.cpython-310.pyc,,
+Cryptodome/SelfTest/Util/test_Counter.py,sha256=gXH9Dej6zfih8cA8DFJK_QIbLINYRenXmDw42vVlBtQ,2292
+Cryptodome/SelfTest/Util/test_Padding.py,sha256=bK0L7PoRRnD8DI-ktOMSNa514mLCYBnZnpyLBhAYmFk,5826
+Cryptodome/SelfTest/Util/test_asn1.py,sha256=dl3GK8IOau460vGumkdDZkQSuFhJKi9Tn8VFMT7xKKE,29347
+Cryptodome/SelfTest/Util/test_number.py,sha256=8NyFZ670B_QA04qBhusfYX-xaDJs_n6qO6Z4r4w6Ku4,8546
+Cryptodome/SelfTest/Util/test_rfc1751.py,sha256=iRu-xLLslb_ktNOPkKs4TAWPljrxDMksFnSqo25q9dA,1121
+Cryptodome/SelfTest/Util/test_strxor.py,sha256=BRVOZIin3ypmyMfdB52fsCbmXu1WMwdLFSx7sw3BUyw,10223
+Cryptodome/SelfTest/__init__.py,sha256=RSlgZ8QkSPoBK34ydUAVliBLhQiVsuX8ZqJaXBOWE18,3688
+Cryptodome/SelfTest/__main__.py,sha256=aQAx7W62ztb2utGTClg3Qgb8iD9zSCnThhil_cIdL84,1506
+Cryptodome/SelfTest/__pycache__/__init__.cpython-310.pyc,,
+Cryptodome/SelfTest/__pycache__/__main__.cpython-310.pyc,,
+Cryptodome/SelfTest/__pycache__/loader.cpython-310.pyc,,
+Cryptodome/SelfTest/__pycache__/st_common.cpython-310.pyc,,
+Cryptodome/SelfTest/loader.py,sha256=8p4YzhCE5KaxhBs5i8kVACuCBQQqqu--tddMbIEkcaQ,6743
+Cryptodome/SelfTest/st_common.py,sha256=XsoFHmR_gylMxGhRJrQHfarNnIT_Mu8t9oAebRPReck,1949
+Cryptodome/Signature/DSS.py,sha256=7jx5uEULjx8P16FBpdhKV94xG5VpZAngKM6LgtTUpjM,15352
+Cryptodome/Signature/DSS.pyi,sha256=zay6LNZ3NIlu42Q63ICT3mZEcz_aVG1rXLOkJ2tfasc,1102
+Cryptodome/Signature/PKCS1_PSS.py,sha256=o3Ky9DF9iI-wpGHDi5vZs7spzFSlANYIkqgqu0zCkAo,2103
+Cryptodome/Signature/PKCS1_PSS.pyi,sha256=fzw5vQvHchfJHvlHEr24CMTY2Gw8_pqsz76jNmMUBlc,280
+Cryptodome/Signature/PKCS1_v1_5.py,sha256=aEzzt1ccFM71pJfG6drwzGZeTI-ntM4-LLxBPyaFXLA,1993
+Cryptodome/Signature/PKCS1_v1_5.pyi,sha256=eqweCPvqayn2xiO9Aqv4Bc38GKOLcca6PazT9T87ufE,157
+Cryptodome/Signature/__init__.py,sha256=nkUODHAHwqmFvemdRLKTFXCY0lh6WdxmFlJuLD0IBfw,1695
+Cryptodome/Signature/__pycache__/DSS.cpython-310.pyc,,
+Cryptodome/Signature/__pycache__/PKCS1_PSS.cpython-310.pyc,,
+Cryptodome/Signature/__pycache__/PKCS1_v1_5.cpython-310.pyc,,
+Cryptodome/Signature/__pycache__/__init__.cpython-310.pyc,,
+Cryptodome/Signature/__pycache__/eddsa.cpython-310.pyc,,
+Cryptodome/Signature/__pycache__/pkcs1_15.cpython-310.pyc,,
+Cryptodome/Signature/__pycache__/pss.cpython-310.pyc,,
+Cryptodome/Signature/eddsa.py,sha256=yD-aOaKDCZyGHKqu0BRS0vqNx-pMsik_fOdryakQkNk,12366
+Cryptodome/Signature/eddsa.pyi,sha256=zhvwZlS3hPcBDg33bk97defwh5h7uMZULaBR01ioSIU,732
+Cryptodome/Signature/pkcs1_15.py,sha256=DUGi4HV7M7BfRsorONganSNTIlS8ql7A5f16OYJTxPI,8750
+Cryptodome/Signature/pkcs1_15.pyi,sha256=k8o74VVp_Zw11VmbhLBxPiU6CzzTm7NbaNwGeWeNn9A,568
+Cryptodome/Signature/pss.py,sha256=1443VYy8pEuvB7t8sWJ9p--pGHnRz2yqChKx3EowkXI,13494
+Cryptodome/Signature/pss.pyi,sha256=O_6YOe-iR4rHIzNnm6vCzcwxVNPGRgfAXhhzp1N9jPE,1044
+Cryptodome/Util/Counter.py,sha256=8qQaeEZGHtAhwGj0A9gJQ9e0ZihtjWpeCSeuS1h9KF8,3110
+Cryptodome/Util/Counter.pyi,sha256=2JrTHJYq263XosQSC_NIP0TufUsTlG7WUr-lRqjJCuA,290
+Cryptodome/Util/Padding.py,sha256=LPtSnQG_f8dT5Gq5IetTWtxcJWeiIQpoUKFDU2yCWbk,4317
+Cryptodome/Util/Padding.pyi,sha256=47R3H2kE66PtKO82eT_Vc5eCSgNe4qOFgqOIPRdlp9c,238
+Cryptodome/Util/RFC1751.py,sha256=zHC63Jx-PqZQ2ebcYGUhaG62vfkFSklQc8MZmapE7SY,21204
+Cryptodome/Util/RFC1751.pyi,sha256=B42LvsE6G786rNEsrhta_BANazgrpb0WoSBPqKyjt5g,159
+Cryptodome/Util/__init__.py,sha256=fsZWRqGXZR2gmM0jxuiogKW3WwzlzKuTRRWYiwtOOd0,1951
+Cryptodome/Util/__pycache__/Counter.cpython-310.pyc,,
+Cryptodome/Util/__pycache__/Padding.cpython-310.pyc,,
+Cryptodome/Util/__pycache__/RFC1751.cpython-310.pyc,,
+Cryptodome/Util/__pycache__/__init__.cpython-310.pyc,,
+Cryptodome/Util/__pycache__/_cpu_features.cpython-310.pyc,,
+Cryptodome/Util/__pycache__/_file_system.cpython-310.pyc,,
+Cryptodome/Util/__pycache__/_raw_api.cpython-310.pyc,,
+Cryptodome/Util/__pycache__/asn1.cpython-310.pyc,,
+Cryptodome/Util/__pycache__/number.cpython-310.pyc,,
+Cryptodome/Util/__pycache__/py3compat.cpython-310.pyc,,
+Cryptodome/Util/__pycache__/strxor.cpython-310.pyc,,
+Cryptodome/Util/_cpu_features.py,sha256=f_JiluwzxhmltMbptIQ8qA03YUdgSNBr3RwcyX9z-xc,1997
+Cryptodome/Util/_cpu_features.pyi,sha256=3wKXZ0Z8llc2uxADvbhz3dHV6YLyRrDujOsabXlffCQ,59
+Cryptodome/Util/_cpuid_c.abi3.so,sha256=LoLZvm6GLA4jOWQ6-FZVnN3uz2MfcoZLqq6YWzaQxyg,12776
+Cryptodome/Util/_file_system.py,sha256=m7HsPgKuKRsTQjgov6Vg02dn7Xsa52xhCLmqLjPIoZg,2183
+Cryptodome/Util/_file_system.pyi,sha256=5QruEWPE4urPtlCT5Eg8tBQyhV9ffBfZIAjmMo727dM,100
+Cryptodome/Util/_raw_api.py,sha256=p_2FjTLxRNw9OwBOHiZ21OUk3Pf4P3z-lmTeZJ-Kp_0,10238
+Cryptodome/Util/_raw_api.pyi,sha256=Ohc2rr6RS-nhs6T5AL1YyQtaqsx6BVrJa092CiwAvNM,906
+Cryptodome/Util/_strxor.abi3.so,sha256=y2-werLAQ_VckIphTn1w1oe1arub39k2Unxn9OzG-cw,14960
+Cryptodome/Util/asn1.py,sha256=bsZSxnt0HAZ3PbMH9-GGOV5kpMkRIVFcwutQx-J7T_k,31689
+Cryptodome/Util/asn1.pyi,sha256=xR4oQKBf4SXiz0IQ_K0lw427jvvgX9SiEXejIu9fdV8,3579
+Cryptodome/Util/number.py,sha256=cMIIREXRr9J_jXogGeuDY_7YGKWOG6DJR5B9t4ta_3Q,95733
+Cryptodome/Util/number.pyi,sha256=ixX1BS8EvvuPXN1_8aosdYHKmtXGB9NlRNVI9T9MAA8,975
+Cryptodome/Util/py3compat.py,sha256=FDpjB6AbLPyx13nRQu5hCyL6m44e81x5kRYbxg5wnNg,5530
+Cryptodome/Util/py3compat.pyi,sha256=lcLAXVV6t4d_y_EsUZOYEYgrOUczczMl_3IawItxYpw,837
+Cryptodome/Util/strxor.py,sha256=gInNJiw-NCqj3ULb7EBCZ_F3vbGSHGxcZrVI3Ot4PIQ,5449
+Cryptodome/Util/strxor.pyi,sha256=OuBvuuK_ezq3eaHY10J89xpER9IQ9wcYzFI7j1tpll0,243
+Cryptodome/__init__.py,sha256=t-JUcMSSnZhIFP8wNWKMkQah8dXuyhttfHa0Qda5GQo,185
+Cryptodome/__init__.pyi,sha256=e5Ea45Jy2RdOr6bmLF9jiS2Bw65WnYTD1NMLJlbGAaw,99
+Cryptodome/__pycache__/__init__.cpython-310.pyc,,
+Cryptodome/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pycryptodomex-3.15.0.dist-info/AUTHORS.rst,sha256=TL3mA8NQgoAcSU73HDEKSMnuix-f4GiSTxtFMy_CS9M,750
+pycryptodomex-3.15.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+pycryptodomex-3.15.0.dist-info/LICENSE.rst,sha256=TgRmDXfBxk6J15U3kZ-4JA-iFISn49sp81iyx_hOoHM,2926
+pycryptodomex-3.15.0.dist-info/METADATA,sha256=uG3EO9qmHt4RI0hfj8sLgo5OxSjUtgxgxu8VHntfrtI,3183
+pycryptodomex-3.15.0.dist-info/RECORD,,
+pycryptodomex-3.15.0.dist-info/WHEEL,sha256=NHVYCelbjQYaRHcLsSKkvd1e6nvZj0EwYHq59C6NexI,111
+pycryptodomex-3.15.0.dist-info/top_level.txt,sha256=eHU9ase6in1ZSBEtTDpl7fwIPION42nbqZ1uFTyccxs,11
diff --git a/frozen_deps/pycryptodomex-3.15.0.dist-info/WHEEL b/frozen_deps/pycryptodomex-3.15.0.dist-info/WHEEL new file mode 100644 index 0000000..59c3897 --- /dev/null +++ b/frozen_deps/pycryptodomex-3.15.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: false +Tag: cp35-abi3-manylinux2010_x86_64 + diff --git a/frozen_deps/pycryptodomex-3.15.0.dist-info/top_level.txt b/frozen_deps/pycryptodomex-3.15.0.dist-info/top_level.txt new file mode 100644 index 0000000..9cbd375 --- /dev/null +++ b/frozen_deps/pycryptodomex-3.15.0.dist-info/top_level.txt @@ -0,0 +1 @@ +Cryptodome diff --git a/frozen_deps/pysha3-1.0.2.dist-info/METADATA b/frozen_deps/pysha3-1.0.2.dist-info/METADATA index 5e11ab7..a2e0aaa 100644 --- a/frozen_deps/pysha3-1.0.2.dist-info/METADATA +++ b/frozen_deps/pysha3-1.0.2.dist-info/METADATA @@ -29,6 +29,7 @@ Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Topic :: Security :: Cryptography +License-File: LICENSE ====== pysha3 @@ -247,5 +248,3 @@ pysha3 0.1 - based on KeccakReferenceAndOptimized-3.2.zip - - diff --git a/frozen_deps/pysha3-1.0.2.dist-info/RECORD b/frozen_deps/pysha3-1.0.2.dist-info/RECORD index 17ecd37..4e27e67 100644 --- a/frozen_deps/pysha3-1.0.2.dist-info/RECORD +++ b/frozen_deps/pysha3-1.0.2.dist-info/RECORD @@ -1,9 +1,9 @@ -__pycache__/sha3.cpython-38.pyc,,
-_pysha3.cpython-38-x86_64-linux-gnu.so,sha256=JM0m-PvpwkIqoUIHO_5AuJYgbybynAEV8mKc7qw-hjA,517664
+__pycache__/sha3.cpython-310.pyc,,
+_pysha3.cpython-310-x86_64-linux-gnu.so,sha256=78P0sVJ96derABo3IAPLSY-6JPc7LmMv76Z4sLKTBb8,381088
pysha3-1.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
pysha3-1.0.2.dist-info/LICENSE,sha256=uAzp2oxCofkQeWJ_u-K_JyEK4Qig_-Xwd9WwjgdsJMg,2409
-pysha3-1.0.2.dist-info/METADATA,sha256=mp8k9OmbwPabv7k2zKNy45GRr2rUJ8auKH5xtoi9wIM,6810
+pysha3-1.0.2.dist-info/METADATA,sha256=dv_VgbGxuacT4ckz_NKW0rQxf4c_G_YpKjdDffjvk-k,6830
pysha3-1.0.2.dist-info/RECORD,,
-pysha3-1.0.2.dist-info/WHEEL,sha256=TpFVeXF_cAlV118WSIPWtjqW7nPvzoOw-49FmS3fDKQ,103
+pysha3-1.0.2.dist-info/WHEEL,sha256=nTy_Z8ivGEB6qFwVLg7_h6rq0Jt0JU5Pz2cL2_FjSMQ,105
pysha3-1.0.2.dist-info/top_level.txt,sha256=FdKZVala00U6bdey3Qbc6yW7Z1rzdaDs8Iet_iwYDP8,13
sha3.py,sha256=QeJrjR0om_CROYj4xnndQXqkkr9Y9R11XsCKKiyYTzs,746
diff --git a/frozen_deps/pysha3-1.0.2.dist-info/WHEEL b/frozen_deps/pysha3-1.0.2.dist-info/WHEEL index d193dea..3f78a79 100644 --- a/frozen_deps/pysha3-1.0.2.dist-info/WHEEL +++ b/frozen_deps/pysha3-1.0.2.dist-info/WHEEL @@ -1,5 +1,5 @@ Wheel-Version: 1.0 -Generator: bdist_wheel (0.34.2) +Generator: bdist_wheel (0.37.1) Root-Is-Purelib: false -Tag: cp38-cp38-linux_x86_64 +Tag: cp310-cp310-linux_x86_64 diff --git a/frozen_deps/six-1.16.0.dist-info/INSTALLER b/frozen_deps/six-1.16.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/frozen_deps/six-1.16.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/frozen_deps/six-1.16.0.dist-info/LICENSE b/frozen_deps/six-1.16.0.dist-info/LICENSE new file mode 100644 index 0000000..de66331 --- /dev/null +++ b/frozen_deps/six-1.16.0.dist-info/LICENSE @@ -0,0 +1,18 @@ +Copyright (c) 2010-2020 Benjamin Peterson + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/frozen_deps/six-1.16.0.dist-info/METADATA b/frozen_deps/six-1.16.0.dist-info/METADATA new file mode 100644 index 0000000..6d7525c --- /dev/null +++ b/frozen_deps/six-1.16.0.dist-info/METADATA @@ -0,0 +1,49 @@ +Metadata-Version: 2.1 +Name: six +Version: 1.16.0 +Summary: Python 2 and 3 compatibility utilities +Home-page: https://github.com/benjaminp/six +Author: Benjamin Peterson +Author-email: [email protected] +License: MIT +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 3 +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Topic :: Software Development :: Libraries +Classifier: Topic :: Utilities +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.* + +.. image:: https://img.shields.io/pypi/v/six.svg + :target: https://pypi.org/project/six/ + :alt: six on PyPI + +.. image:: https://travis-ci.org/benjaminp/six.svg?branch=master + :target: https://travis-ci.org/benjaminp/six + :alt: six on TravisCI + +.. image:: https://readthedocs.org/projects/six/badge/?version=latest + :target: https://six.readthedocs.io/ + :alt: six's documentation on Read the Docs + +.. image:: https://img.shields.io/badge/license-MIT-green.svg + :target: https://github.com/benjaminp/six/blob/master/LICENSE + :alt: MIT License badge + +Six is a Python 2 and 3 compatibility library. It provides utility functions +for smoothing over the differences between the Python versions with the goal of +writing Python code that is compatible on both Python versions. See the +documentation for more information on what is provided. + +Six supports Python 2.7 and 3.3+. It is contained in only one Python +file, so it can be easily copied into your project. (The copyright and license +notice must be retained.) + +Online documentation is at https://six.readthedocs.io/. + +Bugs can be reported to https://github.com/benjaminp/six. The code can also +be found there. + + diff --git a/frozen_deps/six-1.16.0.dist-info/RECORD b/frozen_deps/six-1.16.0.dist-info/RECORD new file mode 100644 index 0000000..ed0b598 --- /dev/null +++ b/frozen_deps/six-1.16.0.dist-info/RECORD @@ -0,0 +1,8 @@ +__pycache__/six.cpython-310.pyc,,
+six-1.16.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+six-1.16.0.dist-info/LICENSE,sha256=i7hQxWWqOJ_cFvOkaWWtI9gq3_YPI5P8J2K2MYXo5sk,1066
+six-1.16.0.dist-info/METADATA,sha256=VQcGIFCAEmfZcl77E5riPCN4v2TIsc_qtacnjxKHJoI,1795
+six-1.16.0.dist-info/RECORD,,
+six-1.16.0.dist-info/WHEEL,sha256=Z-nyYpwrcSqxfdux5Mbn_DQ525iP7J2DG3JgGvOYyTQ,110
+six-1.16.0.dist-info/top_level.txt,sha256=_iVH_iYEtEXnD8nYGQYpYFUvkUW9sEO1GYbkeKSAais,4
+six.py,sha256=TOOfQi7nFGfMrIvtdr6wX4wyHH8M7aknmuLfo2cBBrM,34549
diff --git a/frozen_deps/six-1.16.0.dist-info/WHEEL b/frozen_deps/six-1.16.0.dist-info/WHEEL new file mode 100644 index 0000000..01b8fc7 --- /dev/null +++ b/frozen_deps/six-1.16.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/frozen_deps/six-1.16.0.dist-info/top_level.txt b/frozen_deps/six-1.16.0.dist-info/top_level.txt new file mode 100644 index 0000000..ffe2fce --- /dev/null +++ b/frozen_deps/six-1.16.0.dist-info/top_level.txt @@ -0,0 +1 @@ +six diff --git a/frozen_deps/six.py b/frozen_deps/six.py index 83f6978..4e15675 100644 --- a/frozen_deps/six.py +++ b/frozen_deps/six.py @@ -29,7 +29,7 @@ import sys import types __author__ = "Benjamin Peterson <[email protected]>" -__version__ = "1.15.0" +__version__ = "1.16.0" # Useful for very coarse version differentiation. @@ -71,6 +71,11 @@ else: MAXSIZE = int((1 << 63) - 1) del X +if PY34: + from importlib.util import spec_from_loader +else: + spec_from_loader = None + def _add_doc(func, doc): """Add documentation to a function.""" @@ -186,6 +191,11 @@ class _SixMetaPathImporter(object): return self return None + def find_spec(self, fullname, path, target=None): + if fullname in self.known_modules: + return spec_from_loader(fullname, self) + return None + def __get_module(self, fullname): try: return self.known_modules[fullname] @@ -223,6 +233,12 @@ class _SixMetaPathImporter(object): return None get_source = get_code # same as get_code + def create_module(self, spec): + return self.load_module(spec.name) + + def exec_module(self, module): + pass + _importer = _SixMetaPathImporter(__name__) diff --git a/keytree-0.1.7-x86_64.AppImage b/keytree-0.1.7-x86_64.AppImage Binary files differnew file mode 100755 index 0000000..35c2824 --- /dev/null +++ b/keytree-0.1.7-x86_64.AppImage diff --git a/keytree-0.1.7-x86_64.AppImage.zsync b/keytree-0.1.7-x86_64.AppImage.zsync Binary files differnew file mode 100644 index 0000000..4fa6e4f --- /dev/null +++ b/keytree-0.1.7-x86_64.AppImage.zsync @@ -415,7 +415,7 @@ if __name__ == '__main__': if args.show_private: print("{}.priv(raw/ETH/AVAX-X) 0x{}".format(i, priv.to_string().hex())) print("{}.priv(BTC) {}".format(i, get_privkey_btc(priv))) - print("{}.addr(AVAX) (X/P)-{}".format(i, bech32.bech32_encode(args.hrp, bech32.convertbits(ripemd160(sha256(cpub)), 8, 5)))) + print("{}.addr(AVAX-X/P) {}".format(i, bech32.bech32_encode(args.hrp, bech32.convertbits(ripemd160(sha256(cpub)), 8, 5)))) path2 = "m/{}/{}".format(metamask_path, i) priv2 = gen.derive(path2) @@ -425,7 +425,7 @@ if __name__ == '__main__': print("{}.addr(AVAX-C) 0x{}".format(i, get_eth_addr(pub2))) print("{}.addr(BTC) {}".format(i, get_btc_addr(pub))) - print("{}.addr(ETH) {}".format(i, get_eth_addr(pub))) + print("{}.addr(ETH) 0x{}".format(i, get_eth_addr(pub))) if args.export_mew: save_to_mew(keys) if args.save: |