mirror of
https://gitlab.com/MoonTestUse1/AdministrationItDepartmens.git
synced 2025-08-14 00:25:46 +02:00
Initial commit
This commit is contained in:
104
venv/Lib/site-packages/ecdsa/__init__.py
Normal file
104
venv/Lib/site-packages/ecdsa/__init__.py
Normal file
@@ -0,0 +1,104 @@
|
||||
# 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,
|
||||
BadSignatureError,
|
||||
BadDigestError,
|
||||
MalformedPointError,
|
||||
)
|
||||
from .curves import (
|
||||
NIST192p,
|
||||
NIST224p,
|
||||
NIST256p,
|
||||
NIST384p,
|
||||
NIST521p,
|
||||
SECP256k1,
|
||||
BRAINPOOLP160r1,
|
||||
BRAINPOOLP192r1,
|
||||
BRAINPOOLP224r1,
|
||||
BRAINPOOLP256r1,
|
||||
BRAINPOOLP320r1,
|
||||
BRAINPOOLP384r1,
|
||||
BRAINPOOLP512r1,
|
||||
SECP112r1,
|
||||
SECP112r2,
|
||||
SECP128r1,
|
||||
SECP160r1,
|
||||
Ed25519,
|
||||
Ed448,
|
||||
BRAINPOOLP160t1,
|
||||
BRAINPOOLP192t1,
|
||||
BRAINPOOLP224t1,
|
||||
BRAINPOOLP256t1,
|
||||
BRAINPOOLP320t1,
|
||||
BRAINPOOLP384t1,
|
||||
BRAINPOOLP512t1,
|
||||
)
|
||||
from .ecdh import (
|
||||
ECDH,
|
||||
NoKeyError,
|
||||
NoCurveError,
|
||||
InvalidCurveError,
|
||||
InvalidSharedSecretError,
|
||||
)
|
||||
from .der import UnexpectedDER
|
||||
from . import _version
|
||||
|
||||
# This code comes from http://github.com/tlsfuzzer/python-ecdsa
|
||||
__all__ = [
|
||||
"curves",
|
||||
"der",
|
||||
"ecdsa",
|
||||
"ellipticcurve",
|
||||
"keys",
|
||||
"numbertheory",
|
||||
"test_pyecdsa",
|
||||
"util",
|
||||
"six",
|
||||
]
|
||||
|
||||
_hush_pyflakes = [
|
||||
SigningKey,
|
||||
VerifyingKey,
|
||||
BadSignatureError,
|
||||
BadDigestError,
|
||||
MalformedPointError,
|
||||
UnexpectedDER,
|
||||
InvalidCurveError,
|
||||
NoKeyError,
|
||||
InvalidSharedSecretError,
|
||||
ECDH,
|
||||
NoCurveError,
|
||||
NIST192p,
|
||||
NIST224p,
|
||||
NIST256p,
|
||||
NIST384p,
|
||||
NIST521p,
|
||||
SECP256k1,
|
||||
BRAINPOOLP160r1,
|
||||
BRAINPOOLP192r1,
|
||||
BRAINPOOLP224r1,
|
||||
BRAINPOOLP256r1,
|
||||
BRAINPOOLP320r1,
|
||||
BRAINPOOLP384r1,
|
||||
BRAINPOOLP512r1,
|
||||
SECP112r1,
|
||||
SECP112r2,
|
||||
SECP128r1,
|
||||
SECP160r1,
|
||||
Ed25519,
|
||||
Ed448,
|
||||
six.b(""),
|
||||
BRAINPOOLP160t1,
|
||||
BRAINPOOLP192t1,
|
||||
BRAINPOOLP224t1,
|
||||
BRAINPOOLP256t1,
|
||||
BRAINPOOLP320t1,
|
||||
BRAINPOOLP384t1,
|
||||
BRAINPOOLP512t1,
|
||||
]
|
||||
del _hush_pyflakes
|
||||
|
||||
__version__ = _version.get_versions()["version"]
|
138
venv/Lib/site-packages/ecdsa/_compat.py
Normal file
138
venv/Lib/site-packages/ecdsa/_compat.py
Normal file
@@ -0,0 +1,138 @@
|
||||
"""
|
||||
Common functions for providing cross-python version compatibility.
|
||||
"""
|
||||
import sys
|
||||
import re
|
||||
import binascii
|
||||
from six import integer_types
|
||||
|
||||
|
||||
def str_idx_as_int(string, index):
|
||||
"""Take index'th byte from string, return as integer"""
|
||||
val = string[index]
|
||||
if isinstance(val, integer_types):
|
||||
return val
|
||||
return ord(val)
|
||||
|
||||
|
||||
if sys.version_info < (3, 0): # pragma: no branch
|
||||
import platform
|
||||
|
||||
def normalise_bytes(buffer_object):
|
||||
"""Cast the input into array of bytes."""
|
||||
# flake8 runs on py3 where `buffer` indeed doesn't exist...
|
||||
return buffer(buffer_object) # noqa: F821
|
||||
|
||||
def hmac_compat(ret):
|
||||
return ret
|
||||
|
||||
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:
|
||||
|
||||
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 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 not isinstance(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
|
86
venv/Lib/site-packages/ecdsa/_rwlock.py
Normal file
86
venv/Lib/site-packages/ecdsa/_rwlock.py
Normal file
@@ -0,0 +1,86 @@
|
||||
# Copyright Mateusz Kobos, (c) 2011
|
||||
# https://code.activestate.com/recipes/577803-reader-writer-lock-with-priority-for-writers/
|
||||
# released under the MIT licence
|
||||
|
||||
import threading
|
||||
|
||||
|
||||
__author__ = "Mateusz Kobos"
|
||||
|
||||
|
||||
class RWLock:
|
||||
"""
|
||||
Read-Write locking primitive
|
||||
|
||||
Synchronization object used in a solution of so-called second
|
||||
readers-writers problem. In this problem, many readers can simultaneously
|
||||
access a share, and a writer has an exclusive access to this share.
|
||||
Additionally, the following constraints should be met:
|
||||
1) no reader should be kept waiting if the share is currently opened for
|
||||
reading unless a writer is also waiting for the share,
|
||||
2) no writer should be kept waiting for the share longer than absolutely
|
||||
necessary.
|
||||
|
||||
The implementation is based on [1, secs. 4.2.2, 4.2.6, 4.2.7]
|
||||
with a modification -- adding an additional lock (C{self.__readers_queue})
|
||||
-- in accordance with [2].
|
||||
|
||||
Sources:
|
||||
[1] A.B. Downey: "The little book of semaphores", Version 2.1.5, 2008
|
||||
[2] P.J. Courtois, F. Heymans, D.L. Parnas:
|
||||
"Concurrent Control with 'Readers' and 'Writers'",
|
||||
Communications of the ACM, 1971 (via [3])
|
||||
[3] http://en.wikipedia.org/wiki/Readers-writers_problem
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
A lock giving an even higher priority to the writer in certain
|
||||
cases (see [2] for a discussion).
|
||||
"""
|
||||
self.__read_switch = _LightSwitch()
|
||||
self.__write_switch = _LightSwitch()
|
||||
self.__no_readers = threading.Lock()
|
||||
self.__no_writers = threading.Lock()
|
||||
self.__readers_queue = threading.Lock()
|
||||
|
||||
def reader_acquire(self):
|
||||
self.__readers_queue.acquire()
|
||||
self.__no_readers.acquire()
|
||||
self.__read_switch.acquire(self.__no_writers)
|
||||
self.__no_readers.release()
|
||||
self.__readers_queue.release()
|
||||
|
||||
def reader_release(self):
|
||||
self.__read_switch.release(self.__no_writers)
|
||||
|
||||
def writer_acquire(self):
|
||||
self.__write_switch.acquire(self.__no_readers)
|
||||
self.__no_writers.acquire()
|
||||
|
||||
def writer_release(self):
|
||||
self.__no_writers.release()
|
||||
self.__write_switch.release(self.__no_readers)
|
||||
|
||||
|
||||
class _LightSwitch:
|
||||
"""An auxiliary "light switch"-like object. The first thread turns on the
|
||||
"switch", the last one turns it off (see [1, sec. 4.2.2] for details)."""
|
||||
|
||||
def __init__(self):
|
||||
self.__counter = 0
|
||||
self.__mutex = threading.Lock()
|
||||
|
||||
def acquire(self, lock):
|
||||
self.__mutex.acquire()
|
||||
self.__counter += 1
|
||||
if self.__counter == 1:
|
||||
lock.acquire()
|
||||
self.__mutex.release()
|
||||
|
||||
def release(self, lock):
|
||||
self.__mutex.acquire()
|
||||
self.__counter -= 1
|
||||
if self.__counter == 0:
|
||||
lock.release()
|
||||
self.__mutex.release()
|
181
venv/Lib/site-packages/ecdsa/_sha3.py
Normal file
181
venv/Lib/site-packages/ecdsa/_sha3.py
Normal file
@@ -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)
|
21
venv/Lib/site-packages/ecdsa/_version.py
Normal file
21
venv/Lib/site-packages/ecdsa/_version.py
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
# 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.
|
||||
|
||||
import json
|
||||
|
||||
version_json = '''
|
||||
{
|
||||
"date": "2024-04-08T20:59:55+0200",
|
||||
"dirty": false,
|
||||
"error": null,
|
||||
"full-revisionid": "be70016f8911f79e891a65dcfcb602e5ba866ed3",
|
||||
"version": "0.19.0"
|
||||
}
|
||||
''' # END VERSION_JSON
|
||||
|
||||
|
||||
def get_versions():
|
||||
return json.loads(version_json)
|
590
venv/Lib/site-packages/ecdsa/curves.py
Normal file
590
venv/Lib/site-packages/ecdsa/curves.py
Normal file
@@ -0,0 +1,590 @@
|
||||
from __future__ import division
|
||||
|
||||
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__,
|
||||
# will need to mark it as deprecated later
|
||||
__all__ = [
|
||||
"UnknownCurveError",
|
||||
"orderlen",
|
||||
"Curve",
|
||||
"SECP112r1",
|
||||
"SECP112r2",
|
||||
"SECP128r1",
|
||||
"SECP160r1",
|
||||
"NIST192p",
|
||||
"NIST224p",
|
||||
"NIST256p",
|
||||
"NIST384p",
|
||||
"NIST521p",
|
||||
"curves",
|
||||
"find_curve",
|
||||
"curve_by_name",
|
||||
"SECP256k1",
|
||||
"BRAINPOOLP160r1",
|
||||
"BRAINPOOLP160t1",
|
||||
"BRAINPOOLP192r1",
|
||||
"BRAINPOOLP192t1",
|
||||
"BRAINPOOLP224r1",
|
||||
"BRAINPOOLP224t1",
|
||||
"BRAINPOOLP256r1",
|
||||
"BRAINPOOLP256t1",
|
||||
"BRAINPOOLP320r1",
|
||||
"BRAINPOOLP320t1",
|
||||
"BRAINPOOLP384r1",
|
||||
"BRAINPOOLP384t1",
|
||||
"BRAINPOOLP512r1",
|
||||
"BRAINPOOLP512t1",
|
||||
"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
|
||||
|
||||
|
||||
class Curve:
|
||||
def __init__(self, name, curve, generator, oid, openssl_name=None):
|
||||
self.name = name
|
||||
self.openssl_name = openssl_name # maybe None
|
||||
self.curve = curve
|
||||
self.generator = generator
|
||||
self.order = generator.order()
|
||||
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
|
||||
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(
|
||||
"NIST192p",
|
||||
ecdsa.curve_192,
|
||||
ecdsa.generator_192,
|
||||
(1, 2, 840, 10045, 3, 1, 1),
|
||||
"prime192v1",
|
||||
)
|
||||
|
||||
|
||||
NIST224p = Curve(
|
||||
"NIST224p",
|
||||
ecdsa.curve_224,
|
||||
ecdsa.generator_224,
|
||||
(1, 3, 132, 0, 33),
|
||||
"secp224r1",
|
||||
)
|
||||
|
||||
|
||||
NIST256p = Curve(
|
||||
"NIST256p",
|
||||
ecdsa.curve_256,
|
||||
ecdsa.generator_256,
|
||||
(1, 2, 840, 10045, 3, 1, 7),
|
||||
"prime256v1",
|
||||
)
|
||||
|
||||
|
||||
NIST384p = Curve(
|
||||
"NIST384p",
|
||||
ecdsa.curve_384,
|
||||
ecdsa.generator_384,
|
||||
(1, 3, 132, 0, 34),
|
||||
"secp384r1",
|
||||
)
|
||||
|
||||
|
||||
NIST521p = Curve(
|
||||
"NIST521p",
|
||||
ecdsa.curve_521,
|
||||
ecdsa.generator_521,
|
||||
(1, 3, 132, 0, 35),
|
||||
"secp521r1",
|
||||
)
|
||||
|
||||
|
||||
SECP256k1 = Curve(
|
||||
"SECP256k1",
|
||||
ecdsa.curve_secp256k1,
|
||||
ecdsa.generator_secp256k1,
|
||||
(1, 3, 132, 0, 10),
|
||||
"secp256k1",
|
||||
)
|
||||
|
||||
|
||||
BRAINPOOLP160r1 = Curve(
|
||||
"BRAINPOOLP160r1",
|
||||
ecdsa.curve_brainpoolp160r1,
|
||||
ecdsa.generator_brainpoolp160r1,
|
||||
(1, 3, 36, 3, 3, 2, 8, 1, 1, 1),
|
||||
"brainpoolP160r1",
|
||||
)
|
||||
|
||||
|
||||
BRAINPOOLP160t1 = Curve(
|
||||
"BRAINPOOLP160t1",
|
||||
ecdsa.curve_brainpoolp160t1,
|
||||
ecdsa.generator_brainpoolp160t1,
|
||||
(1, 3, 36, 3, 3, 2, 8, 1, 1, 2),
|
||||
"brainpoolP160t1",
|
||||
)
|
||||
|
||||
|
||||
BRAINPOOLP192r1 = Curve(
|
||||
"BRAINPOOLP192r1",
|
||||
ecdsa.curve_brainpoolp192r1,
|
||||
ecdsa.generator_brainpoolp192r1,
|
||||
(1, 3, 36, 3, 3, 2, 8, 1, 1, 3),
|
||||
"brainpoolP192r1",
|
||||
)
|
||||
|
||||
|
||||
BRAINPOOLP192t1 = Curve(
|
||||
"BRAINPOOLP192t1",
|
||||
ecdsa.curve_brainpoolp192t1,
|
||||
ecdsa.generator_brainpoolp192t1,
|
||||
(1, 3, 36, 3, 3, 2, 8, 1, 1, 4),
|
||||
"brainpoolP192t1",
|
||||
)
|
||||
|
||||
|
||||
BRAINPOOLP224r1 = Curve(
|
||||
"BRAINPOOLP224r1",
|
||||
ecdsa.curve_brainpoolp224r1,
|
||||
ecdsa.generator_brainpoolp224r1,
|
||||
(1, 3, 36, 3, 3, 2, 8, 1, 1, 5),
|
||||
"brainpoolP224r1",
|
||||
)
|
||||
|
||||
|
||||
BRAINPOOLP224t1 = Curve(
|
||||
"BRAINPOOLP224t1",
|
||||
ecdsa.curve_brainpoolp224t1,
|
||||
ecdsa.generator_brainpoolp224t1,
|
||||
(1, 3, 36, 3, 3, 2, 8, 1, 1, 6),
|
||||
"brainpoolP224t1",
|
||||
)
|
||||
|
||||
|
||||
BRAINPOOLP256r1 = Curve(
|
||||
"BRAINPOOLP256r1",
|
||||
ecdsa.curve_brainpoolp256r1,
|
||||
ecdsa.generator_brainpoolp256r1,
|
||||
(1, 3, 36, 3, 3, 2, 8, 1, 1, 7),
|
||||
"brainpoolP256r1",
|
||||
)
|
||||
|
||||
|
||||
BRAINPOOLP256t1 = Curve(
|
||||
"BRAINPOOLP256t1",
|
||||
ecdsa.curve_brainpoolp256t1,
|
||||
ecdsa.generator_brainpoolp256t1,
|
||||
(1, 3, 36, 3, 3, 2, 8, 1, 1, 8),
|
||||
"brainpoolP256t1",
|
||||
)
|
||||
|
||||
|
||||
BRAINPOOLP320r1 = Curve(
|
||||
"BRAINPOOLP320r1",
|
||||
ecdsa.curve_brainpoolp320r1,
|
||||
ecdsa.generator_brainpoolp320r1,
|
||||
(1, 3, 36, 3, 3, 2, 8, 1, 1, 9),
|
||||
"brainpoolP320r1",
|
||||
)
|
||||
|
||||
|
||||
BRAINPOOLP320t1 = Curve(
|
||||
"BRAINPOOLP320t1",
|
||||
ecdsa.curve_brainpoolp320t1,
|
||||
ecdsa.generator_brainpoolp320t1,
|
||||
(1, 3, 36, 3, 3, 2, 8, 1, 1, 10),
|
||||
"brainpoolP320t1",
|
||||
)
|
||||
|
||||
|
||||
BRAINPOOLP384r1 = Curve(
|
||||
"BRAINPOOLP384r1",
|
||||
ecdsa.curve_brainpoolp384r1,
|
||||
ecdsa.generator_brainpoolp384r1,
|
||||
(1, 3, 36, 3, 3, 2, 8, 1, 1, 11),
|
||||
"brainpoolP384r1",
|
||||
)
|
||||
|
||||
|
||||
BRAINPOOLP384t1 = Curve(
|
||||
"BRAINPOOLP384t1",
|
||||
ecdsa.curve_brainpoolp384t1,
|
||||
ecdsa.generator_brainpoolp384t1,
|
||||
(1, 3, 36, 3, 3, 2, 8, 1, 1, 12),
|
||||
"brainpoolP384t1",
|
||||
)
|
||||
|
||||
|
||||
BRAINPOOLP512r1 = Curve(
|
||||
"BRAINPOOLP512r1",
|
||||
ecdsa.curve_brainpoolp512r1,
|
||||
ecdsa.generator_brainpoolp512r1,
|
||||
(1, 3, 36, 3, 3, 2, 8, 1, 1, 13),
|
||||
"brainpoolP512r1",
|
||||
)
|
||||
|
||||
|
||||
BRAINPOOLP512t1 = Curve(
|
||||
"BRAINPOOLP512t1",
|
||||
ecdsa.curve_brainpoolp512t1,
|
||||
ecdsa.generator_brainpoolp512t1,
|
||||
(1, 3, 36, 3, 3, 2, 8, 1, 1, 14),
|
||||
"brainpoolP512t1",
|
||||
)
|
||||
|
||||
|
||||
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,
|
||||
NIST256p,
|
||||
NIST384p,
|
||||
NIST521p,
|
||||
SECP256k1,
|
||||
BRAINPOOLP160r1,
|
||||
BRAINPOOLP192r1,
|
||||
BRAINPOOLP224r1,
|
||||
BRAINPOOLP256r1,
|
||||
BRAINPOOLP320r1,
|
||||
BRAINPOOLP384r1,
|
||||
BRAINPOOLP512r1,
|
||||
SECP112r1,
|
||||
SECP112r2,
|
||||
SECP128r1,
|
||||
SECP160r1,
|
||||
Ed25519,
|
||||
Ed448,
|
||||
BRAINPOOLP160t1,
|
||||
BRAINPOOLP192t1,
|
||||
BRAINPOOLP224t1,
|
||||
BRAINPOOLP256t1,
|
||||
BRAINPOOLP320t1,
|
||||
BRAINPOOLP384t1,
|
||||
BRAINPOOLP512t1,
|
||||
]
|
||||
|
||||
|
||||
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
|
||||
raise UnknownCurveError(
|
||||
"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]
|
||||
)
|
||||
)
|
409
venv/Lib/site-packages/ecdsa/der.py
Normal file
409
venv/Lib/site-packages/ecdsa/der.py
Normal file
@@ -0,0 +1,409 @@
|
||||
from __future__ import division
|
||||
|
||||
import binascii
|
||||
import base64
|
||||
import warnings
|
||||
from itertools import chain
|
||||
from six import int2byte, text_type
|
||||
from ._compat import compat26_str, str_idx_as_int
|
||||
|
||||
|
||||
class UnexpectedDER(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def encode_constructed(tag, value):
|
||||
return int2byte(0xA0 + tag) + encode_length(len(value)) + value
|
||||
|
||||
|
||||
def encode_integer(r):
|
||||
assert r >= 0 # can't support negative numbers yet
|
||||
h = ("%x" % r).encode()
|
||||
if len(h) % 2:
|
||||
h = b"0" + h
|
||||
s = binascii.unhexlify(h)
|
||||
num = str_idx_as_int(s, 0)
|
||||
if num <= 0x7F:
|
||||
return b"\x02" + encode_length(len(s)) + s
|
||||
else:
|
||||
# DER integers are two's complement, so if the first byte is
|
||||
# 0x80-0xff then we need an extra 0x00 byte to prevent it from
|
||||
# looking negative.
|
||||
return b"\x02" + encode_length(len(s) + 1) + b"\x00" + s
|
||||
|
||||
|
||||
# sentry object to check if an argument was specified (used to detect
|
||||
# deprecated calling convention)
|
||||
_sentry = object()
|
||||
|
||||
|
||||
def encode_bitstring(s, unused=_sentry):
|
||||
"""
|
||||
Encode a binary string as a BIT STRING using :term:`DER` encoding.
|
||||
|
||||
Note, because there is no native Python object that can encode an actual
|
||||
bit string, this function only accepts byte strings as the `s` argument.
|
||||
The byte string is the actual bit string that will be encoded, padded
|
||||
on the right (least significant bits, looking from big endian perspective)
|
||||
to the first full byte. If the bit string has a bit length that is multiple
|
||||
of 8, then the padding should not be included. For correct DER encoding
|
||||
the padding bits MUST be set to 0.
|
||||
|
||||
Number of bits of padding need to be provided as the `unused` parameter.
|
||||
In case they are specified as None, it means the number of unused bits
|
||||
is already encoded in the string as the first byte.
|
||||
|
||||
The deprecated call convention specifies just the `s` parameters and
|
||||
encodes the number of unused bits as first parameter (same convention
|
||||
as with None).
|
||||
|
||||
Empty string must be encoded with `unused` specified as 0.
|
||||
|
||||
Future version of python-ecdsa will make specifying the `unused` argument
|
||||
mandatory.
|
||||
|
||||
:param s: bytes to encode
|
||||
:type s: bytes like object
|
||||
:param unused: number of bits at the end of `s` that are unused, must be
|
||||
between 0 and 7 (inclusive)
|
||||
:type unused: int or None
|
||||
|
||||
:raises ValueError: when `unused` is too large or too small
|
||||
|
||||
:return: `s` encoded using DER
|
||||
:rtype: bytes
|
||||
"""
|
||||
encoded_unused = b""
|
||||
len_extra = 0
|
||||
if unused is _sentry:
|
||||
warnings.warn(
|
||||
"Legacy call convention used, unused= needs to be specified",
|
||||
DeprecationWarning,
|
||||
)
|
||||
elif unused is not None:
|
||||
if not 0 <= unused <= 7:
|
||||
raise ValueError("unused must be integer between 0 and 7")
|
||||
if unused:
|
||||
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):
|
||||
raise ValueError("unused bits must be zeros in DER")
|
||||
encoded_unused = int2byte(unused)
|
||||
len_extra = 1
|
||||
return b"\x03" + encode_length(len(s) + len_extra) + encoded_unused + s
|
||||
|
||||
|
||||
def encode_octet_string(s):
|
||||
return b"\x04" + encode_length(len(s)) + s
|
||||
|
||||
|
||||
def encode_oid(first, second, *pieces):
|
||||
assert 0 <= first < 2 and 0 <= second <= 39 or first == 2 and 0 <= second
|
||||
body = b"".join(
|
||||
chain(
|
||||
[encode_number(40 * first + second)],
|
||||
(encode_number(p) for p in pieces),
|
||||
)
|
||||
)
|
||||
return b"\x06" + encode_length(len(body)) + body
|
||||
|
||||
|
||||
def encode_sequence(*encoded_pieces):
|
||||
total_len = sum([len(p) for p in encoded_pieces])
|
||||
return b"\x30" + encode_length(total_len) + b"".join(encoded_pieces)
|
||||
|
||||
|
||||
def encode_number(n):
|
||||
b128_digits = []
|
||||
while n:
|
||||
b128_digits.insert(0, (n & 0x7F) | 0x80)
|
||||
n = n >> 7
|
||||
if not b128_digits:
|
||||
b128_digits.append(0)
|
||||
b128_digits[-1] &= 0x7F
|
||||
return b"".join([int2byte(d) for d in b128_digits])
|
||||
|
||||
|
||||
def is_sequence(string):
|
||||
return string and string[:1] == b"\x30"
|
||||
|
||||
|
||||
def remove_constructed(string):
|
||||
s0 = str_idx_as_int(string, 0)
|
||||
if (s0 & 0xE0) != 0xA0:
|
||||
raise UnexpectedDER(
|
||||
"wanted type 'constructed tag' (0xa0-0xbf), got 0x%02x" % s0
|
||||
)
|
||||
tag = s0 & 0x1F
|
||||
length, llen = read_length(string[1:])
|
||||
body = string[1 + llen : 1 + llen + length]
|
||||
rest = string[1 + llen + length :]
|
||||
return tag, body, rest
|
||||
|
||||
|
||||
def remove_sequence(string):
|
||||
if not string:
|
||||
raise UnexpectedDER("Empty string does not encode a sequence")
|
||||
if string[:1] != b"\x30":
|
||||
n = str_idx_as_int(string, 0)
|
||||
raise UnexpectedDER("wanted type 'sequence' (0x30), got 0x%02x" % n)
|
||||
length, lengthlength = read_length(string[1:])
|
||||
if length > len(string) - 1 - lengthlength:
|
||||
raise UnexpectedDER("Length longer than the provided buffer")
|
||||
endseq = 1 + lengthlength + length
|
||||
return string[1 + lengthlength : endseq], string[endseq:]
|
||||
|
||||
|
||||
def remove_octet_string(string):
|
||||
if string[:1] != b"\x04":
|
||||
n = str_idx_as_int(string, 0)
|
||||
raise UnexpectedDER("wanted type 'octetstring' (0x04), got 0x%02x" % n)
|
||||
length, llen = read_length(string[1:])
|
||||
body = string[1 + llen : 1 + llen + length]
|
||||
rest = string[1 + llen + length :]
|
||||
return body, rest
|
||||
|
||||
|
||||
def remove_object(string):
|
||||
if not string:
|
||||
raise UnexpectedDER(
|
||||
"Empty string does not encode an object identifier"
|
||||
)
|
||||
if string[:1] != b"\x06":
|
||||
n = str_idx_as_int(string, 0)
|
||||
raise UnexpectedDER("wanted type 'object' (0x06), got 0x%02x" % n)
|
||||
length, lengthlength = read_length(string[1:])
|
||||
body = string[1 + lengthlength : 1 + lengthlength + length]
|
||||
rest = string[1 + lengthlength + length :]
|
||||
if not body:
|
||||
raise UnexpectedDER("Empty object identifier")
|
||||
if len(body) != length:
|
||||
raise UnexpectedDER(
|
||||
"Length of object identifier longer than the provided buffer"
|
||||
)
|
||||
numbers = []
|
||||
while body:
|
||||
n, ll = read_number(body)
|
||||
numbers.append(n)
|
||||
body = body[ll:]
|
||||
n0 = numbers.pop(0)
|
||||
if n0 < 80:
|
||||
first = n0 // 40
|
||||
else:
|
||||
first = 2
|
||||
second = n0 - (40 * first)
|
||||
numbers.insert(0, first)
|
||||
numbers.insert(1, second)
|
||||
return tuple(numbers), rest
|
||||
|
||||
|
||||
def remove_integer(string):
|
||||
if not string:
|
||||
raise UnexpectedDER(
|
||||
"Empty string is an invalid encoding of an integer"
|
||||
)
|
||||
if string[:1] != b"\x02":
|
||||
n = str_idx_as_int(string, 0)
|
||||
raise UnexpectedDER("wanted type 'integer' (0x02), got 0x%02x" % n)
|
||||
length, llen = read_length(string[1:])
|
||||
if length > len(string) - 1 - llen:
|
||||
raise UnexpectedDER("Length longer than provided buffer")
|
||||
if length == 0:
|
||||
raise UnexpectedDER("0-byte long encoding of integer")
|
||||
numberbytes = string[1 + llen : 1 + llen + length]
|
||||
rest = string[1 + llen + length :]
|
||||
msb = str_idx_as_int(numberbytes, 0)
|
||||
if not msb < 0x80:
|
||||
raise UnexpectedDER("Negative integers are not supported")
|
||||
# check if the encoding is the minimal one (DER requirement)
|
||||
if length > 1 and not msb:
|
||||
# leading zero byte is allowed if the integer would have been
|
||||
# considered a negative number otherwise
|
||||
smsb = str_idx_as_int(numberbytes, 1)
|
||||
if smsb < 0x80:
|
||||
raise UnexpectedDER(
|
||||
"Invalid encoding of integer, unnecessary "
|
||||
"zero padding bytes"
|
||||
)
|
||||
return int(binascii.hexlify(numberbytes), 16), rest
|
||||
|
||||
|
||||
def read_number(string):
|
||||
number = 0
|
||||
llen = 0
|
||||
if str_idx_as_int(string, 0) == 0x80:
|
||||
raise UnexpectedDER("Non minimal encoding of OID subidentifier")
|
||||
# base-128 big endian, with most significant bit set in all but the last
|
||||
# byte
|
||||
while True:
|
||||
if llen >= len(string):
|
||||
raise UnexpectedDER("ran out of length bytes")
|
||||
number = number << 7
|
||||
d = str_idx_as_int(string, llen)
|
||||
number += d & 0x7F
|
||||
llen += 1
|
||||
if not d & 0x80:
|
||||
break
|
||||
return number, llen
|
||||
|
||||
|
||||
def encode_length(l):
|
||||
assert l >= 0
|
||||
if l < 0x80:
|
||||
return int2byte(l)
|
||||
s = ("%x" % l).encode()
|
||||
if len(s) % 2:
|
||||
s = b"0" + s
|
||||
s = binascii.unhexlify(s)
|
||||
llen = len(s)
|
||||
return int2byte(0x80 | llen) + s
|
||||
|
||||
|
||||
def read_length(string):
|
||||
if not string:
|
||||
raise UnexpectedDER("Empty string can't encode valid length value")
|
||||
num = str_idx_as_int(string, 0)
|
||||
if not (num & 0x80):
|
||||
# short form
|
||||
return (num & 0x7F), 1
|
||||
# else long-form: b0&0x7f is number of additional base256 length bytes,
|
||||
# big-endian
|
||||
llen = num & 0x7F
|
||||
if not llen:
|
||||
raise UnexpectedDER("Invalid length encoding, length of length is 0")
|
||||
if llen > len(string) - 1:
|
||||
raise UnexpectedDER("Length of length longer than provided buffer")
|
||||
# verify that the encoding is minimal possible (DER requirement)
|
||||
msb = str_idx_as_int(string, 1)
|
||||
if not msb or llen == 1 and msb < 0x80:
|
||||
raise UnexpectedDER("Not minimal encoding of length")
|
||||
return int(binascii.hexlify(string[1 : 1 + llen]), 16), 1 + llen
|
||||
|
||||
|
||||
def remove_bitstring(string, expect_unused=_sentry):
|
||||
"""
|
||||
Remove a BIT STRING object from `string` following :term:`DER`.
|
||||
|
||||
The `expect_unused` can be used to specify if the bit string should
|
||||
have the amount of unused bits decoded or not. If it's an integer, any
|
||||
read BIT STRING that has number of unused bits different from specified
|
||||
value will cause UnexpectedDER exception to be raised (this is especially
|
||||
useful when decoding BIT STRINGS that have DER encoded object in them;
|
||||
DER encoding is byte oriented, so the unused bits will always equal 0).
|
||||
|
||||
If the `expect_unused` is specified as None, the first element returned
|
||||
will be a tuple, with the first value being the extracted bit string
|
||||
while the second value will be the decoded number of unused bits.
|
||||
|
||||
If the `expect_unused` is unspecified, the decoding of byte with
|
||||
number of unused bits will not be attempted and the bit string will be
|
||||
returned as-is, the callee will be required to decode it and verify its
|
||||
correctness.
|
||||
|
||||
Future version of python will require the `expected_unused` parameter
|
||||
to be specified.
|
||||
|
||||
:param string: string of bytes to extract the BIT STRING from
|
||||
:type string: bytes like object
|
||||
:param expect_unused: number of bits that should be unused in the BIT
|
||||
STRING, or None, to return it to caller
|
||||
:type expect_unused: int or None
|
||||
|
||||
:raises UnexpectedDER: when the encoding does not follow DER.
|
||||
|
||||
:return: a tuple with first element being the extracted bit string and
|
||||
the second being the remaining bytes in the string (if any); if the
|
||||
`expect_unused` is specified as None, the first element of the returned
|
||||
tuple will be a tuple itself, with first element being the bit string
|
||||
as bytes and the second element being the number of unused bits at the
|
||||
end of the byte array as an integer
|
||||
:rtype: tuple
|
||||
"""
|
||||
if not string:
|
||||
raise UnexpectedDER("Empty string does not encode a bitstring")
|
||||
if expect_unused is _sentry:
|
||||
warnings.warn(
|
||||
"Legacy call convention used, expect_unused= needs to be"
|
||||
" specified",
|
||||
DeprecationWarning,
|
||||
)
|
||||
num = str_idx_as_int(string, 0)
|
||||
if string[:1] != b"\x03":
|
||||
raise UnexpectedDER("wanted bitstring (0x03), got 0x%02x" % num)
|
||||
length, llen = read_length(string[1:])
|
||||
if not length:
|
||||
raise UnexpectedDER("Invalid length of bit string, can't be 0")
|
||||
body = string[1 + llen : 1 + llen + length]
|
||||
rest = string[1 + llen + length :]
|
||||
if expect_unused is not _sentry:
|
||||
unused = str_idx_as_int(body, 0)
|
||||
if not 0 <= unused <= 7:
|
||||
raise UnexpectedDER("Invalid encoding of unused bits")
|
||||
if expect_unused is not None and expect_unused != unused:
|
||||
raise UnexpectedDER("Unexpected number of unused bits")
|
||||
body = body[1:]
|
||||
if unused:
|
||||
if not body:
|
||||
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):
|
||||
raise UnexpectedDER("Non zero padding bits in bit string")
|
||||
if expect_unused is None:
|
||||
body = (body, unused)
|
||||
return body, rest
|
||||
|
||||
|
||||
# SEQUENCE([1, STRING(secexp), cont[0], OBJECT(curvename), cont[1], BINTSTRING)
|
||||
|
||||
|
||||
# signatures: (from RFC3279)
|
||||
# ansi-X9-62 OBJECT IDENTIFIER ::= {
|
||||
# iso(1) member-body(2) us(840) 10045 }
|
||||
#
|
||||
# id-ecSigType OBJECT IDENTIFIER ::= {
|
||||
# ansi-X9-62 signatures(4) }
|
||||
# ecdsa-with-SHA1 OBJECT IDENTIFIER ::= {
|
||||
# id-ecSigType 1 }
|
||||
# so 1,2,840,10045,4,1
|
||||
# so 0x42, .. ..
|
||||
|
||||
# Ecdsa-Sig-Value ::= SEQUENCE {
|
||||
# r INTEGER,
|
||||
# s INTEGER }
|
||||
|
||||
# id-public-key-type OBJECT IDENTIFIER ::= { ansi-X9.62 2 }
|
||||
#
|
||||
# id-ecPublicKey OBJECT IDENTIFIER ::= { id-publicKeyType 1 }
|
||||
|
||||
# I think the secp224r1 identifier is (t=06,l=05,v=2b81040021)
|
||||
# secp224r1 OBJECT IDENTIFIER ::= {
|
||||
# iso(1) identified-organization(3) certicom(132) curve(0) 33 }
|
||||
# and the secp384r1 is (t=06,l=05,v=2b81040022)
|
||||
# secp384r1 OBJECT IDENTIFIER ::= {
|
||||
# iso(1) identified-organization(3) certicom(132) curve(0) 34 }
|
||||
|
||||
|
||||
def unpem(pem):
|
||||
if isinstance(pem, text_type): # pragma: no branch
|
||||
pem = pem.encode()
|
||||
|
||||
d = b"".join(
|
||||
[
|
||||
l.strip()
|
||||
for l in pem.split(b"\n")
|
||||
if l and not l.startswith(b"-----")
|
||||
]
|
||||
)
|
||||
return base64.b64decode(d)
|
||||
|
||||
|
||||
def topem(der, name):
|
||||
b64 = base64.b64encode(compat26_str(der))
|
||||
lines = [("-----BEGIN %s-----\n" % name).encode()]
|
||||
lines.extend(
|
||||
[b64[start : start + 76] + b"\n" for start in range(0, len(b64), 76)]
|
||||
)
|
||||
lines.append(("-----END %s-----\n" % name).encode())
|
||||
return b"".join(lines)
|
336
venv/Lib/site-packages/ecdsa/ecdh.py
Normal file
336
venv/Lib/site-packages/ecdsa/ecdh.py
Normal file
@@ -0,0 +1,336 @@
|
||||
"""
|
||||
Class for performing Elliptic-curve Diffie-Hellman (ECDH) operations.
|
||||
"""
|
||||
|
||||
from .util import number_to_string
|
||||
from .ellipticcurve import INFINITY
|
||||
from .keys import SigningKey, VerifyingKey
|
||||
|
||||
|
||||
__all__ = [
|
||||
"ECDH",
|
||||
"NoKeyError",
|
||||
"NoCurveError",
|
||||
"InvalidCurveError",
|
||||
"InvalidSharedSecretError",
|
||||
]
|
||||
|
||||
|
||||
class NoKeyError(Exception):
|
||||
"""ECDH. Key not found but it is needed for operation."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class NoCurveError(Exception):
|
||||
"""ECDH. Curve not set but it is needed for operation."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class InvalidCurveError(Exception):
|
||||
"""
|
||||
ECDH. Raised in case the public and private keys use different curves.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class InvalidSharedSecretError(Exception):
|
||||
"""ECDH. Raised in case the shared secret we obtained is an INFINITY."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ECDH(object):
|
||||
"""
|
||||
Elliptic-curve Diffie-Hellman (ECDH). A key agreement protocol.
|
||||
|
||||
Allows two parties, each having an elliptic-curve public-private key
|
||||
pair, to establish a shared secret over an insecure channel
|
||||
"""
|
||||
|
||||
def __init__(self, curve=None, private_key=None, public_key=None):
|
||||
"""
|
||||
ECDH init.
|
||||
|
||||
Call can be initialised without parameters, then the first operation
|
||||
(loading either key) will set the used curve.
|
||||
All parameters must be ultimately set before shared secret
|
||||
calculation will be allowed.
|
||||
|
||||
:param curve: curve for operations
|
||||
:type curve: Curve
|
||||
:param private_key: `my` private key for ECDH
|
||||
:type private_key: SigningKey
|
||||
:param public_key: `their` public key for ECDH
|
||||
:type public_key: VerifyingKey
|
||||
"""
|
||||
self.curve = curve
|
||||
self.private_key = None
|
||||
self.public_key = None
|
||||
if private_key:
|
||||
self.load_private_key(private_key)
|
||||
if public_key:
|
||||
self.load_received_public_key(public_key)
|
||||
|
||||
def _get_shared_secret(self, remote_public_key):
|
||||
if not self.private_key:
|
||||
raise NoKeyError(
|
||||
"Private key needs to be set to create shared secret"
|
||||
)
|
||||
if not self.public_key:
|
||||
raise NoKeyError(
|
||||
"Public key needs to be set to create shared secret"
|
||||
)
|
||||
if not (
|
||||
self.private_key.curve == self.curve == remote_public_key.curve
|
||||
):
|
||||
raise InvalidCurveError(
|
||||
"Curves for public key and private key is not equal."
|
||||
)
|
||||
|
||||
# shared secret = PUBKEYtheirs * PRIVATEKEYours
|
||||
result = (
|
||||
remote_public_key.pubkey.point
|
||||
* self.private_key.privkey.secret_multiplier
|
||||
)
|
||||
if result == INFINITY:
|
||||
raise InvalidSharedSecretError("Invalid shared secret (INFINITY).")
|
||||
|
||||
return result.x()
|
||||
|
||||
def set_curve(self, key_curve):
|
||||
"""
|
||||
Set the working curve for ecdh operations.
|
||||
|
||||
:param key_curve: curve from `curves` module
|
||||
:type key_curve: Curve
|
||||
"""
|
||||
self.curve = key_curve
|
||||
|
||||
def generate_private_key(self):
|
||||
"""
|
||||
Generate local private key for ecdh operation with curve that was set.
|
||||
|
||||
:raises NoCurveError: Curve must be set before key generation.
|
||||
|
||||
:return: public (verifying) key from this private key.
|
||||
:rtype: VerifyingKey
|
||||
"""
|
||||
if not self.curve:
|
||||
raise NoCurveError("Curve must be set prior to key generation.")
|
||||
return self.load_private_key(SigningKey.generate(curve=self.curve))
|
||||
|
||||
def load_private_key(self, private_key):
|
||||
"""
|
||||
Load private key from SigningKey (keys.py) object.
|
||||
|
||||
Needs to have the same curve as was set with set_curve method.
|
||||
If curve is not set - it sets from this SigningKey
|
||||
|
||||
:param private_key: Initialised SigningKey class
|
||||
:type private_key: SigningKey
|
||||
|
||||
:raises InvalidCurveError: private_key curve not the same as self.curve
|
||||
|
||||
:return: public (verifying) key from this private key.
|
||||
:rtype: VerifyingKey
|
||||
"""
|
||||
if not self.curve:
|
||||
self.curve = private_key.curve
|
||||
if self.curve != private_key.curve:
|
||||
raise InvalidCurveError("Curve mismatch.")
|
||||
self.private_key = private_key
|
||||
return self.private_key.get_verifying_key()
|
||||
|
||||
def load_private_key_bytes(self, private_key):
|
||||
"""
|
||||
Load private key from byte string.
|
||||
|
||||
Uses current curve and checks if the provided key matches
|
||||
the curve of ECDH key agreement.
|
||||
Key loads via from_string method of SigningKey class
|
||||
|
||||
:param private_key: private key in bytes string format
|
||||
:type private_key: :term:`bytes-like object`
|
||||
|
||||
:raises NoCurveError: Curve must be set before loading.
|
||||
|
||||
:return: public (verifying) key from this private key.
|
||||
:rtype: VerifyingKey
|
||||
"""
|
||||
if not self.curve:
|
||||
raise NoCurveError("Curve must be set prior to key load.")
|
||||
return self.load_private_key(
|
||||
SigningKey.from_string(private_key, curve=self.curve)
|
||||
)
|
||||
|
||||
def load_private_key_der(self, private_key_der):
|
||||
"""
|
||||
Load private key from DER byte string.
|
||||
|
||||
Compares the curve of the DER-encoded key with the ECDH set curve,
|
||||
uses the former if unset.
|
||||
|
||||
Note, the only DER format supported is the RFC5915
|
||||
Look at keys.py:SigningKey.from_der()
|
||||
|
||||
:param private_key_der: string with the DER encoding of private ECDSA
|
||||
key
|
||||
:type private_key_der: string
|
||||
|
||||
:raises InvalidCurveError: private_key curve not the same as self.curve
|
||||
|
||||
:return: public (verifying) key from this private key.
|
||||
:rtype: VerifyingKey
|
||||
"""
|
||||
return self.load_private_key(SigningKey.from_der(private_key_der))
|
||||
|
||||
def load_private_key_pem(self, private_key_pem):
|
||||
"""
|
||||
Load private key from PEM string.
|
||||
|
||||
Compares the curve of the DER-encoded key with the ECDH set curve,
|
||||
uses the former if unset.
|
||||
|
||||
Note, the only PEM format supported is the RFC5915
|
||||
Look at keys.py:SigningKey.from_pem()
|
||||
it needs to have `EC PRIVATE KEY` section
|
||||
|
||||
:param private_key_pem: string with PEM-encoded private ECDSA key
|
||||
:type private_key_pem: string
|
||||
|
||||
:raises InvalidCurveError: private_key curve not the same as self.curve
|
||||
|
||||
:return: public (verifying) key from this private key.
|
||||
:rtype: VerifyingKey
|
||||
"""
|
||||
return self.load_private_key(SigningKey.from_pem(private_key_pem))
|
||||
|
||||
def get_public_key(self):
|
||||
"""
|
||||
Provides a public key that matches the local private key.
|
||||
|
||||
Needs to be sent to the remote party.
|
||||
|
||||
:return: public (verifying) key from local private key.
|
||||
:rtype: VerifyingKey
|
||||
"""
|
||||
return self.private_key.get_verifying_key()
|
||||
|
||||
def load_received_public_key(self, public_key):
|
||||
"""
|
||||
Load public key from VerifyingKey (keys.py) object.
|
||||
|
||||
Needs to have the same curve as set as current for ecdh operation.
|
||||
If curve is not set - it sets it from VerifyingKey.
|
||||
|
||||
:param public_key: Initialised VerifyingKey class
|
||||
:type public_key: VerifyingKey
|
||||
|
||||
:raises InvalidCurveError: public_key curve not the same as self.curve
|
||||
"""
|
||||
if not self.curve:
|
||||
self.curve = public_key.curve
|
||||
if self.curve != public_key.curve:
|
||||
raise InvalidCurveError("Curve mismatch.")
|
||||
self.public_key = public_key
|
||||
|
||||
def load_received_public_key_bytes(
|
||||
self, public_key_str, valid_encodings=None
|
||||
):
|
||||
"""
|
||||
Load public key from byte string.
|
||||
|
||||
Uses current curve and checks if key length corresponds to
|
||||
the current curve.
|
||||
Key loads via from_string method of VerifyingKey class
|
||||
|
||||
: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, valid_encodings
|
||||
)
|
||||
)
|
||||
|
||||
def load_received_public_key_der(self, public_key_der):
|
||||
"""
|
||||
Load public key from DER byte string.
|
||||
|
||||
Compares the curve of the DER-encoded key with the ECDH set curve,
|
||||
uses the former if unset.
|
||||
|
||||
Note, the only DER format supported is the RFC5912
|
||||
Look at keys.py:VerifyingKey.from_der()
|
||||
|
||||
:param public_key_der: string with the DER encoding of public ECDSA key
|
||||
:type public_key_der: string
|
||||
|
||||
:raises InvalidCurveError: public_key curve not the same as self.curve
|
||||
"""
|
||||
return self.load_received_public_key(
|
||||
VerifyingKey.from_der(public_key_der)
|
||||
)
|
||||
|
||||
def load_received_public_key_pem(self, public_key_pem):
|
||||
"""
|
||||
Load public key from PEM string.
|
||||
|
||||
Compares the curve of the PEM-encoded key with the ECDH set curve,
|
||||
uses the former if unset.
|
||||
|
||||
Note, the only PEM format supported is the RFC5912
|
||||
Look at keys.py:VerifyingKey.from_pem()
|
||||
|
||||
:param public_key_pem: string with PEM-encoded public ECDSA key
|
||||
:type public_key_pem: string
|
||||
|
||||
:raises InvalidCurveError: public_key curve not the same as self.curve
|
||||
"""
|
||||
return self.load_received_public_key(
|
||||
VerifyingKey.from_pem(public_key_pem)
|
||||
)
|
||||
|
||||
def generate_sharedsecret_bytes(self):
|
||||
"""
|
||||
Generate shared secret from local private key and remote public key.
|
||||
|
||||
The objects needs to have both private key and received public key
|
||||
before generation is allowed.
|
||||
|
||||
:raises InvalidCurveError: public_key curve not the same as self.curve
|
||||
:raises NoKeyError: public_key or private_key is not set
|
||||
|
||||
:return: shared secret
|
||||
:rtype: bytes
|
||||
"""
|
||||
return number_to_string(
|
||||
self.generate_sharedsecret(), self.private_key.curve.curve.p()
|
||||
)
|
||||
|
||||
def generate_sharedsecret(self):
|
||||
"""
|
||||
Generate shared secret from local private key and remote public key.
|
||||
|
||||
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)
|
||||
|
||||
:raises InvalidCurveError: public_key curve not the same as self.curve
|
||||
:raises NoKeyError: public_key or private_key is not set
|
||||
|
||||
:return: shared secret
|
||||
:rtype: int
|
||||
"""
|
||||
return self._get_shared_secret(self.public_key)
|
1094
venv/Lib/site-packages/ecdsa/ecdsa.py
Normal file
1094
venv/Lib/site-packages/ecdsa/ecdsa.py
Normal file
File diff suppressed because it is too large
Load Diff
252
venv/Lib/site-packages/ecdsa/eddsa.py
Normal file
252
venv/Lib/site-packages/ecdsa/eddsa.py
Normal file
@@ -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")
|
1598
venv/Lib/site-packages/ecdsa/ellipticcurve.py
Normal file
1598
venv/Lib/site-packages/ecdsa/ellipticcurve.py
Normal file
File diff suppressed because it is too large
Load Diff
4
venv/Lib/site-packages/ecdsa/errors.py
Normal file
4
venv/Lib/site-packages/ecdsa/errors.py
Normal file
@@ -0,0 +1,4 @@
|
||||
class MalformedPointError(AssertionError):
|
||||
"""Raised in case the encoding of private or public key is malformed."""
|
||||
|
||||
pass
|
1631
venv/Lib/site-packages/ecdsa/keys.py
Normal file
1631
venv/Lib/site-packages/ecdsa/keys.py
Normal file
File diff suppressed because it is too large
Load Diff
835
venv/Lib/site-packages/ecdsa/numbertheory.py
Normal file
835
venv/Lib/site-packages/ecdsa/numbertheory.py
Normal file
@@ -0,0 +1,835 @@
|
||||
#! /usr/bin/env python
|
||||
#
|
||||
# Provide some simple capabilities from number theory.
|
||||
#
|
||||
# Version of 2008.11.14.
|
||||
#
|
||||
# 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 arbitrarily many arguments.
|
||||
|
||||
from __future__ import division
|
||||
|
||||
import sys
|
||||
from six import integer_types, PY2
|
||||
from six.moves import reduce
|
||||
|
||||
try:
|
||||
xrange
|
||||
except NameError:
|
||||
xrange = range
|
||||
try:
|
||||
from gmpy2 import powmod, mpz
|
||||
|
||||
GMPY2 = True
|
||||
GMPY = False
|
||||
except ImportError: # pragma: no branch
|
||||
GMPY2 = False
|
||||
try:
|
||||
from gmpy import mpz
|
||||
|
||||
GMPY = True
|
||||
except ImportError:
|
||||
GMPY = False
|
||||
|
||||
|
||||
if GMPY2 or GMPY: # pragma: no branch
|
||||
integer_types = tuple(integer_types + (type(mpz(1)),))
|
||||
|
||||
|
||||
import math
|
||||
import warnings
|
||||
import random
|
||||
from .util import bit_length
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
"""Base class for exceptions in this module."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class JacobiError(Error):
|
||||
pass
|
||||
|
||||
|
||||
class SquareRootError(Error):
|
||||
pass
|
||||
|
||||
|
||||
class NegativeExponentError(Error):
|
||||
pass
|
||||
|
||||
|
||||
def modular_exp(base, exponent, modulus): # pragma: no cover
|
||||
"""Raise base to exponent, reducing by modulus"""
|
||||
# deprecated in 0.14
|
||||
warnings.warn(
|
||||
"Function is unused in library code. If you use this code, "
|
||||
"change to pow() builtin.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
if exponent < 0:
|
||||
raise NegativeExponentError(
|
||||
"Negative exponents (%d) not allowed" % exponent
|
||||
)
|
||||
return pow(base, exponent, modulus)
|
||||
|
||||
|
||||
def polynomial_reduce_mod(poly, polymod, p):
|
||||
"""Reduce poly by polymod, integer arithmetic modulo p.
|
||||
|
||||
Polynomials are represented as lists of coefficients
|
||||
of increasing powers of x."""
|
||||
|
||||
# This module has been tested only by extensive use
|
||||
# in calculating modular square roots.
|
||||
|
||||
# Just to make this easy, require a monic polynomial:
|
||||
assert polymod[-1] == 1
|
||||
|
||||
assert len(polymod) > 1
|
||||
|
||||
while len(poly) >= len(polymod):
|
||||
if poly[-1] != 0:
|
||||
for i in xrange(2, len(polymod) + 1):
|
||||
poly[-i] = (poly[-i] - poly[-1] * polymod[-i]) % p
|
||||
poly = poly[0:-1]
|
||||
|
||||
return poly
|
||||
|
||||
|
||||
def polynomial_multiply_mod(m1, m2, polymod, p):
|
||||
"""Polynomial multiplication modulo a polynomial over ints mod p.
|
||||
|
||||
Polynomials are represented as lists of coefficients
|
||||
of increasing powers of x."""
|
||||
|
||||
# This is just a seat-of-the-pants implementation.
|
||||
|
||||
# This module has been tested only by extensive use
|
||||
# in calculating modular square roots.
|
||||
|
||||
# Initialize the product to zero:
|
||||
|
||||
prod = (len(m1) + len(m2) - 1) * [0]
|
||||
|
||||
# Add together all the cross-terms:
|
||||
|
||||
for i in xrange(len(m1)):
|
||||
for j in xrange(len(m2)):
|
||||
prod[i + j] = (prod[i + j] + m1[i] * m2[j]) % p
|
||||
|
||||
return polynomial_reduce_mod(prod, polymod, p)
|
||||
|
||||
|
||||
def polynomial_exp_mod(base, exponent, polymod, p):
|
||||
"""Polynomial exponentiation modulo a polynomial over ints mod p.
|
||||
|
||||
Polynomials are represented as lists of coefficients
|
||||
of increasing powers of x."""
|
||||
|
||||
# Based on the Handbook of Applied Cryptography, algorithm 2.227.
|
||||
|
||||
# This module has been tested only by extensive use
|
||||
# in calculating modular square roots.
|
||||
|
||||
assert exponent < p
|
||||
|
||||
if exponent == 0:
|
||||
return [1]
|
||||
|
||||
G = base
|
||||
k = exponent
|
||||
if k % 2 == 1:
|
||||
s = G
|
||||
else:
|
||||
s = [1]
|
||||
|
||||
while k > 1:
|
||||
k = k // 2
|
||||
G = polynomial_multiply_mod(G, G, polymod, p)
|
||||
if k % 2 == 1:
|
||||
s = polynomial_multiply_mod(G, s, polymod, p)
|
||||
|
||||
return s
|
||||
|
||||
|
||||
def jacobi(a, n):
|
||||
"""Jacobi symbol"""
|
||||
|
||||
# Based on the Handbook of Applied Cryptography (HAC), algorithm 2.149.
|
||||
|
||||
# This function has been tested by comparison with a small
|
||||
# table printed in HAC, and by extensive use in calculating
|
||||
# modular square roots.
|
||||
|
||||
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
|
||||
if a == 1:
|
||||
return 1
|
||||
a1, e = a, 0
|
||||
while a1 % 2 == 0:
|
||||
a1, e = a1 // 2, e + 1
|
||||
if e % 2 == 0 or n % 8 == 1 or n % 8 == 7:
|
||||
s = 1
|
||||
else:
|
||||
s = -1
|
||||
if a1 == 1:
|
||||
return s
|
||||
if n % 4 == 3 and a1 % 4 == 3:
|
||||
s = -s
|
||||
return s * jacobi(n % a1, a1)
|
||||
|
||||
|
||||
def square_root_mod_prime(a, p):
|
||||
"""Modular square root of a, mod p, p prime."""
|
||||
|
||||
# Based on the Handbook of Applied Cryptography, algorithms 3.34 to 3.39.
|
||||
|
||||
# This module has been tested for all values in [0,p-1] for
|
||||
# every prime p from 3 to 1229.
|
||||
|
||||
assert 0 <= a < p
|
||||
assert 1 < p
|
||||
|
||||
if a == 0:
|
||||
return 0
|
||||
if p == 2:
|
||||
return a
|
||||
|
||||
jac = jacobi(a, p)
|
||||
if jac == -1:
|
||||
raise SquareRootError("%d has no square root modulo %d" % (a, p))
|
||||
|
||||
if p % 4 == 3:
|
||||
return pow(a, (p + 1) // 4, p)
|
||||
|
||||
if p % 8 == 5:
|
||||
d = pow(a, (p - 1) // 4, p)
|
||||
if d == 1:
|
||||
return pow(a, (p + 3) // 8, p)
|
||||
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
|
||||
range_top = min(0x7FFFFFFF, p)
|
||||
else:
|
||||
range_top = p
|
||||
for b in xrange(2, range_top): # pragma: no branch
|
||||
if jacobi(b * b - 4 * a, p) == -1:
|
||||
f = (a, -b, 1)
|
||||
ff = polynomial_exp_mod((0, 1), (p + 1) // 2, f, p)
|
||||
if ff[1]:
|
||||
raise SquareRootError("p is not prime")
|
||||
return ff[0]
|
||||
# just an assertion
|
||||
raise RuntimeError("No b found.") # pragma: no cover
|
||||
|
||||
|
||||
# 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: # pragma: no branch
|
||||
return 0
|
||||
return powmod(a, -1, m)
|
||||
|
||||
elif GMPY: # pragma: no branch
|
||||
|
||||
def inverse_mod(a, m):
|
||||
"""Inverse of a mod m."""
|
||||
# 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: # 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: # pragma: no branch
|
||||
|
||||
def inverse_mod(a, m):
|
||||
"""Inverse of a mod m."""
|
||||
|
||||
if a == 0: # pragma: no branch
|
||||
return 0
|
||||
|
||||
lm, hm = 1, 0
|
||||
low, high = a % m, m
|
||||
while low > 1: # pragma: no branch
|
||||
r = high // low
|
||||
lm, low, hm, high = hm - lm * r, high - low * r, lm, low
|
||||
|
||||
return lm % m
|
||||
|
||||
|
||||
try:
|
||||
gcd2 = math.gcd
|
||||
except AttributeError:
|
||||
|
||||
def gcd2(a, b):
|
||||
"""Greatest common divisor using Euclid's algorithm."""
|
||||
while a:
|
||||
a, b = b % a, a
|
||||
return b
|
||||
|
||||
|
||||
def gcd(*a):
|
||||
"""Greatest common divisor.
|
||||
|
||||
Usage: gcd([ 2, 4, 6 ])
|
||||
or: gcd(2, 4, 6)
|
||||
"""
|
||||
|
||||
if len(a) > 1:
|
||||
return reduce(gcd2, a)
|
||||
if hasattr(a[0], "__iter__"):
|
||||
return reduce(gcd2, a[0])
|
||||
return a[0]
|
||||
|
||||
|
||||
def lcm2(a, b):
|
||||
"""Least common multiple of two integers."""
|
||||
|
||||
return (a * b) // gcd(a, b)
|
||||
|
||||
|
||||
def lcm(*a):
|
||||
"""Least common multiple.
|
||||
|
||||
Usage: lcm([ 3, 4, 5 ])
|
||||
or: lcm(3, 4, 5)
|
||||
"""
|
||||
|
||||
if len(a) > 1:
|
||||
return reduce(lcm2, a)
|
||||
if hasattr(a[0], "__iter__"):
|
||||
return reduce(lcm2, a[0])
|
||||
return a[0]
|
||||
|
||||
|
||||
def factorization(n):
|
||||
"""Decompose n into a list of (prime,exponent) pairs."""
|
||||
|
||||
assert isinstance(n, integer_types)
|
||||
|
||||
if n < 2:
|
||||
return []
|
||||
|
||||
result = []
|
||||
|
||||
# Test the small primes:
|
||||
|
||||
for d in smallprimes:
|
||||
if d > n:
|
||||
break
|
||||
q, r = divmod(n, d)
|
||||
if r == 0:
|
||||
count = 1
|
||||
while d <= n: # pragma: no branch
|
||||
n = q
|
||||
q, r = divmod(n, d)
|
||||
if r != 0:
|
||||
break
|
||||
count = count + 1
|
||||
result.append((d, count))
|
||||
|
||||
# If n is still greater than the last of our small primes,
|
||||
# it may require further work:
|
||||
|
||||
if n > smallprimes[-1]:
|
||||
if is_prime(n): # If what's left is prime, it's easy:
|
||||
result.append((n, 1))
|
||||
else: # Ugh. Search stupidly for a divisor:
|
||||
d = smallprimes[-1]
|
||||
while 1:
|
||||
d = d + 2 # Try the next divisor.
|
||||
q, r = divmod(n, d)
|
||||
if q < d: # n < d*d means we're done, n = 1 or prime.
|
||||
break
|
||||
if r == 0: # d divides n. How many times?
|
||||
count = 1
|
||||
n = q
|
||||
# As long as d might still divide n,
|
||||
while d <= n: # pragma: no branch
|
||||
q, r = divmod(n, d) # see if it does.
|
||||
if r != 0:
|
||||
break
|
||||
n = q # It does. Reduce n, increase count.
|
||||
count = count + 1
|
||||
result.append((d, count))
|
||||
if n > 1:
|
||||
result.append((n, 1))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def phi(n): # pragma: no cover
|
||||
"""Return the Euler totient function of n."""
|
||||
# deprecated in 0.14
|
||||
warnings.warn(
|
||||
"Function is unused by library code. If you use this code, "
|
||||
"please open an issue in "
|
||||
"https://github.com/tlsfuzzer/python-ecdsa",
|
||||
DeprecationWarning,
|
||||
)
|
||||
|
||||
assert isinstance(n, integer_types)
|
||||
|
||||
if n < 3:
|
||||
return 1
|
||||
|
||||
result = 1
|
||||
ff = factorization(n)
|
||||
for f in ff:
|
||||
e = f[1]
|
||||
if e > 1:
|
||||
result = result * f[0] ** (e - 1) * (f[0] - 1)
|
||||
else:
|
||||
result = result * (f[0] - 1)
|
||||
return result
|
||||
|
||||
|
||||
def carmichael(n): # pragma: no cover
|
||||
"""Return Carmichael function of n.
|
||||
|
||||
Carmichael(n) is the smallest integer x such that
|
||||
m**x = 1 mod n for all m relatively prime to n.
|
||||
"""
|
||||
# deprecated in 0.14
|
||||
warnings.warn(
|
||||
"Function is unused by library code. If you use this code, "
|
||||
"please open an issue in "
|
||||
"https://github.com/tlsfuzzer/python-ecdsa",
|
||||
DeprecationWarning,
|
||||
)
|
||||
|
||||
return carmichael_of_factorized(factorization(n))
|
||||
|
||||
|
||||
def carmichael_of_factorized(f_list): # pragma: no cover
|
||||
"""Return the Carmichael function of a number that is
|
||||
represented as a list of (prime,exponent) pairs.
|
||||
"""
|
||||
# deprecated in 0.14
|
||||
warnings.warn(
|
||||
"Function is unused by library code. If you use this code, "
|
||||
"please open an issue in "
|
||||
"https://github.com/tlsfuzzer/python-ecdsa",
|
||||
DeprecationWarning,
|
||||
)
|
||||
|
||||
if len(f_list) < 1:
|
||||
return 1
|
||||
|
||||
result = carmichael_of_ppower(f_list[0])
|
||||
for i in xrange(1, len(f_list)):
|
||||
result = lcm(result, carmichael_of_ppower(f_list[i]))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def carmichael_of_ppower(pp): # pragma: no cover
|
||||
"""Carmichael function of the given power of the given prime."""
|
||||
# deprecated in 0.14
|
||||
warnings.warn(
|
||||
"Function is unused by library code. If you use this code, "
|
||||
"please open an issue in "
|
||||
"https://github.com/tlsfuzzer/python-ecdsa",
|
||||
DeprecationWarning,
|
||||
)
|
||||
|
||||
p, a = pp
|
||||
if p == 2 and a > 2:
|
||||
return 2 ** (a - 2)
|
||||
else:
|
||||
return (p - 1) * p ** (a - 1)
|
||||
|
||||
|
||||
def order_mod(x, m): # pragma: no cover
|
||||
"""Return the order of x in the multiplicative group mod m."""
|
||||
# deprecated in 0.14
|
||||
warnings.warn(
|
||||
"Function is unused by library code. If you use this code, "
|
||||
"please open an issue in "
|
||||
"https://github.com/tlsfuzzer/python-ecdsa",
|
||||
DeprecationWarning,
|
||||
)
|
||||
|
||||
# Warning: this implementation is not very clever, and will
|
||||
# take a long time if m is very large.
|
||||
|
||||
if m <= 1:
|
||||
return 0
|
||||
|
||||
assert gcd(x, m) == 1
|
||||
|
||||
z = x
|
||||
result = 1
|
||||
while z != 1:
|
||||
z = (z * x) % m
|
||||
result = result + 1
|
||||
return result
|
||||
|
||||
|
||||
def largest_factor_relatively_prime(a, b): # pragma: no cover
|
||||
"""Return the largest factor of a relatively prime to b."""
|
||||
# deprecated in 0.14
|
||||
warnings.warn(
|
||||
"Function is unused by library code. If you use this code, "
|
||||
"please open an issue in "
|
||||
"https://github.com/tlsfuzzer/python-ecdsa",
|
||||
DeprecationWarning,
|
||||
)
|
||||
|
||||
while 1:
|
||||
d = gcd(a, b)
|
||||
if d <= 1:
|
||||
break
|
||||
b = d
|
||||
while 1:
|
||||
q, r = divmod(a, d)
|
||||
if r > 0:
|
||||
break
|
||||
a = q
|
||||
return a
|
||||
|
||||
|
||||
def kinda_order_mod(x, m): # pragma: no cover
|
||||
"""Return the order of x in the multiplicative group mod m',
|
||||
where m' is the largest factor of m relatively prime to x.
|
||||
"""
|
||||
# deprecated in 0.14
|
||||
warnings.warn(
|
||||
"Function is unused by library code. If you use this code, "
|
||||
"please open an issue in "
|
||||
"https://github.com/tlsfuzzer/python-ecdsa",
|
||||
DeprecationWarning,
|
||||
)
|
||||
|
||||
return order_mod(x, largest_factor_relatively_prime(m, x))
|
||||
|
||||
|
||||
def is_prime(n):
|
||||
"""Return True if x is prime, False otherwise.
|
||||
|
||||
We use the Miller-Rabin test, as given in Menezes et al. p. 138.
|
||||
This test is not exact: there are composite values n for which
|
||||
it returns True.
|
||||
|
||||
In testing the odd numbers from 10000001 to 19999999,
|
||||
about 66 composites got past the first test,
|
||||
5 got past the second test, and none got past the third.
|
||||
Since factors of 2, 3, 5, 7, and 11 were detected during
|
||||
preliminary screening, the number of numbers tested by
|
||||
Miller-Rabin was (19999999 - 10000001)*(2/3)*(4/5)*(6/7)
|
||||
= 4.57 million.
|
||||
"""
|
||||
|
||||
# (This is used to study the risk of false positives:)
|
||||
global miller_rabin_test_count
|
||||
|
||||
miller_rabin_test_count = 0
|
||||
|
||||
if n <= smallprimes[-1]:
|
||||
if n in smallprimes:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
# 2310 = 2 * 3 * 5 * 7 * 11
|
||||
if gcd(n, 2310) != 1:
|
||||
return False
|
||||
|
||||
# Choose a number of iterations sufficient to reduce the
|
||||
# probability of accepting a composite below 2**-80
|
||||
# (from Menezes et al. Table 4.4):
|
||||
|
||||
t = 40
|
||||
n_bits = 1 + bit_length(n)
|
||||
assert 11 <= n_bits <= 16384
|
||||
for k, tt in (
|
||||
(100, 27),
|
||||
(150, 18),
|
||||
(200, 15),
|
||||
(250, 12),
|
||||
(300, 9),
|
||||
(350, 8),
|
||||
(400, 7),
|
||||
(450, 6),
|
||||
(550, 5),
|
||||
(650, 4),
|
||||
(850, 3),
|
||||
(1300, 2),
|
||||
):
|
||||
if n_bits < k:
|
||||
break
|
||||
t = tt
|
||||
|
||||
# Run the test t times:
|
||||
|
||||
s = 0
|
||||
r = n - 1
|
||||
while (r % 2) == 0:
|
||||
s = s + 1
|
||||
r = r // 2
|
||||
for i in xrange(t):
|
||||
a = random.choice(smallprimes)
|
||||
y = pow(a, r, n)
|
||||
if y != 1 and y != n - 1:
|
||||
j = 1
|
||||
while j <= s - 1 and y != n - 1:
|
||||
y = pow(y, 2, n)
|
||||
if y == 1:
|
||||
miller_rabin_test_count = i + 1
|
||||
return False
|
||||
j = j + 1
|
||||
if y != n - 1:
|
||||
miller_rabin_test_count = i + 1
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def next_prime(starting_value):
|
||||
"""Return the smallest prime larger than the starting value."""
|
||||
|
||||
if starting_value < 2:
|
||||
return 2
|
||||
result = (starting_value + 1) | 1
|
||||
while not is_prime(result):
|
||||
result = result + 2
|
||||
return result
|
||||
|
||||
|
||||
smallprimes = [
|
||||
2,
|
||||
3,
|
||||
5,
|
||||
7,
|
||||
11,
|
||||
13,
|
||||
17,
|
||||
19,
|
||||
23,
|
||||
29,
|
||||
31,
|
||||
37,
|
||||
41,
|
||||
43,
|
||||
47,
|
||||
53,
|
||||
59,
|
||||
61,
|
||||
67,
|
||||
71,
|
||||
73,
|
||||
79,
|
||||
83,
|
||||
89,
|
||||
97,
|
||||
101,
|
||||
103,
|
||||
107,
|
||||
109,
|
||||
113,
|
||||
127,
|
||||
131,
|
||||
137,
|
||||
139,
|
||||
149,
|
||||
151,
|
||||
157,
|
||||
163,
|
||||
167,
|
||||
173,
|
||||
179,
|
||||
181,
|
||||
191,
|
||||
193,
|
||||
197,
|
||||
199,
|
||||
211,
|
||||
223,
|
||||
227,
|
||||
229,
|
||||
233,
|
||||
239,
|
||||
241,
|
||||
251,
|
||||
257,
|
||||
263,
|
||||
269,
|
||||
271,
|
||||
277,
|
||||
281,
|
||||
283,
|
||||
293,
|
||||
307,
|
||||
311,
|
||||
313,
|
||||
317,
|
||||
331,
|
||||
337,
|
||||
347,
|
||||
349,
|
||||
353,
|
||||
359,
|
||||
367,
|
||||
373,
|
||||
379,
|
||||
383,
|
||||
389,
|
||||
397,
|
||||
401,
|
||||
409,
|
||||
419,
|
||||
421,
|
||||
431,
|
||||
433,
|
||||
439,
|
||||
443,
|
||||
449,
|
||||
457,
|
||||
461,
|
||||
463,
|
||||
467,
|
||||
479,
|
||||
487,
|
||||
491,
|
||||
499,
|
||||
503,
|
||||
509,
|
||||
521,
|
||||
523,
|
||||
541,
|
||||
547,
|
||||
557,
|
||||
563,
|
||||
569,
|
||||
571,
|
||||
577,
|
||||
587,
|
||||
593,
|
||||
599,
|
||||
601,
|
||||
607,
|
||||
613,
|
||||
617,
|
||||
619,
|
||||
631,
|
||||
641,
|
||||
643,
|
||||
647,
|
||||
653,
|
||||
659,
|
||||
661,
|
||||
673,
|
||||
677,
|
||||
683,
|
||||
691,
|
||||
701,
|
||||
709,
|
||||
719,
|
||||
727,
|
||||
733,
|
||||
739,
|
||||
743,
|
||||
751,
|
||||
757,
|
||||
761,
|
||||
769,
|
||||
773,
|
||||
787,
|
||||
797,
|
||||
809,
|
||||
811,
|
||||
821,
|
||||
823,
|
||||
827,
|
||||
829,
|
||||
839,
|
||||
853,
|
||||
857,
|
||||
859,
|
||||
863,
|
||||
877,
|
||||
881,
|
||||
883,
|
||||
887,
|
||||
907,
|
||||
911,
|
||||
919,
|
||||
929,
|
||||
937,
|
||||
941,
|
||||
947,
|
||||
953,
|
||||
967,
|
||||
971,
|
||||
977,
|
||||
983,
|
||||
991,
|
||||
997,
|
||||
1009,
|
||||
1013,
|
||||
1019,
|
||||
1021,
|
||||
1031,
|
||||
1033,
|
||||
1039,
|
||||
1049,
|
||||
1051,
|
||||
1061,
|
||||
1063,
|
||||
1069,
|
||||
1087,
|
||||
1091,
|
||||
1093,
|
||||
1097,
|
||||
1103,
|
||||
1109,
|
||||
1117,
|
||||
1123,
|
||||
1129,
|
||||
1151,
|
||||
1153,
|
||||
1163,
|
||||
1171,
|
||||
1181,
|
||||
1187,
|
||||
1193,
|
||||
1201,
|
||||
1213,
|
||||
1217,
|
||||
1223,
|
||||
1229,
|
||||
]
|
||||
|
||||
miller_rabin_test_count = 0
|
113
venv/Lib/site-packages/ecdsa/rfc6979.py
Normal file
113
venv/Lib/site-packages/ecdsa/rfc6979.py
Normal file
@@ -0,0 +1,113 @@
|
||||
"""
|
||||
RFC 6979:
|
||||
Deterministic Usage of the Digital Signature Algorithm (DSA) and
|
||||
Elliptic Curve Digital Signature Algorithm (ECDSA)
|
||||
|
||||
http://tools.ietf.org/html/rfc6979
|
||||
|
||||
Many thanks to Coda Hale for his implementation in Go language:
|
||||
https://github.com/codahale/rfc6979
|
||||
"""
|
||||
|
||||
import hmac
|
||||
from binascii import hexlify
|
||||
from .util import number_to_string, number_to_string_crop, bit_length
|
||||
from ._compat import hmac_compat
|
||||
|
||||
|
||||
# bit_length was defined in this module previously so keep it for backwards
|
||||
# compatibility, will need to deprecate and remove it later
|
||||
__all__ = ["bit_length", "bits2int", "bits2octets", "generate_k"]
|
||||
|
||||
|
||||
def bits2int(data, qlen):
|
||||
x = int(hexlify(data), 16)
|
||||
l = len(data) * 8
|
||||
|
||||
if l > qlen:
|
||||
return x >> (l - qlen)
|
||||
return x
|
||||
|
||||
|
||||
def bits2octets(data, order):
|
||||
z1 = bits2int(data, bit_length(order))
|
||||
z2 = z1 - order
|
||||
|
||||
if z2 < 0:
|
||||
z2 = z1
|
||||
|
||||
return number_to_string_crop(z2, order)
|
||||
|
||||
|
||||
# https://tools.ietf.org/html/rfc6979#section-3.2
|
||||
def generate_k(order, secexp, hash_func, data, retry_gen=0, extra_entropy=b""):
|
||||
"""
|
||||
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)
|
||||
holen = hash_func().digest_size
|
||||
rolen = (qlen + 7) // 8
|
||||
bx = (
|
||||
hmac_compat(number_to_string(secexp, order)),
|
||||
hmac_compat(bits2octets(data, order)),
|
||||
hmac_compat(extra_entropy),
|
||||
)
|
||||
|
||||
# Step B
|
||||
v = b"\x01" * holen
|
||||
|
||||
# Step C
|
||||
k = b"\x00" * holen
|
||||
|
||||
# Step D
|
||||
|
||||
k = hmac.new(k, digestmod=hash_func)
|
||||
k.update(v + b"\x00")
|
||||
for i in bx:
|
||||
k.update(i)
|
||||
k = k.digest()
|
||||
|
||||
# Step E
|
||||
v = hmac.new(k, v, hash_func).digest()
|
||||
|
||||
# Step F
|
||||
k = hmac.new(k, digestmod=hash_func)
|
||||
k.update(v + b"\x01")
|
||||
for i in bx:
|
||||
k.update(i)
|
||||
k = k.digest()
|
||||
|
||||
# Step G
|
||||
v = hmac.new(k, v, hash_func).digest()
|
||||
|
||||
# Step H
|
||||
while True:
|
||||
# Step H1
|
||||
t = b""
|
||||
|
||||
# Step H2
|
||||
while len(t) < rolen:
|
||||
v = hmac.new(k, v, hash_func).digest()
|
||||
t += v
|
||||
|
||||
# Step H3
|
||||
secret = bits2int(t, qlen)
|
||||
|
||||
if 1 <= secret < order:
|
||||
if retry_gen <= 0:
|
||||
return secret
|
||||
retry_gen -= 1
|
||||
|
||||
k = hmac.new(k, v + b"\x00", hash_func).digest()
|
||||
v = hmac.new(k, v, hash_func).digest()
|
83
venv/Lib/site-packages/ecdsa/ssh.py
Normal file
83
venv/Lib/site-packages/ecdsa/ssh.py
Normal file
@@ -0,0 +1,83 @@
|
||||
import binascii
|
||||
from . import der
|
||||
from ._compat import compat26_str, int_to_bytes
|
||||
|
||||
_SSH_ED25519 = b"ssh-ed25519"
|
||||
_SK_MAGIC = b"openssh-key-v1\0"
|
||||
_NONE = b"none"
|
||||
|
||||
|
||||
def _get_key_type(name):
|
||||
if name == "Ed25519":
|
||||
return _SSH_ED25519
|
||||
else:
|
||||
raise ValueError("Unsupported key type")
|
||||
|
||||
|
||||
class _Serializer:
|
||||
def __init__(self):
|
||||
self.bytes = b""
|
||||
|
||||
def put_raw(self, val):
|
||||
self.bytes += val
|
||||
|
||||
def put_u32(self, val):
|
||||
self.bytes += int_to_bytes(val, length=4, byteorder="big")
|
||||
|
||||
def put_str(self, val):
|
||||
self.put_u32(len(val))
|
||||
self.bytes += val
|
||||
|
||||
def put_pad(self, blklen=8):
|
||||
padlen = blklen - (len(self.bytes) % blklen)
|
||||
self.put_raw(bytearray(range(1, 1 + padlen)))
|
||||
|
||||
def encode(self):
|
||||
return binascii.b2a_base64(compat26_str(self.bytes))
|
||||
|
||||
def tobytes(self):
|
||||
return self.bytes
|
||||
|
||||
def topem(self):
|
||||
return der.topem(self.bytes, "OPENSSH PRIVATE KEY")
|
||||
|
||||
|
||||
def serialize_public(name, pub):
|
||||
serial = _Serializer()
|
||||
ktype = _get_key_type(name)
|
||||
serial.put_str(ktype)
|
||||
serial.put_str(pub)
|
||||
return b" ".join([ktype, serial.encode()])
|
||||
|
||||
|
||||
def serialize_private(name, pub, priv):
|
||||
# encode public part
|
||||
spub = _Serializer()
|
||||
ktype = _get_key_type(name)
|
||||
spub.put_str(ktype)
|
||||
spub.put_str(pub)
|
||||
|
||||
# encode private part
|
||||
spriv = _Serializer()
|
||||
checksum = 0
|
||||
spriv.put_u32(checksum)
|
||||
spriv.put_u32(checksum)
|
||||
spriv.put_raw(spub.tobytes())
|
||||
spriv.put_str(priv + pub)
|
||||
comment = b""
|
||||
spriv.put_str(comment)
|
||||
spriv.put_pad()
|
||||
|
||||
# top-level structure
|
||||
main = _Serializer()
|
||||
main.put_raw(_SK_MAGIC)
|
||||
ciphername = kdfname = _NONE
|
||||
main.put_str(ciphername)
|
||||
main.put_str(kdfname)
|
||||
nokdf = 0
|
||||
main.put_u32(nokdf)
|
||||
nkeys = 1
|
||||
main.put_u32(nkeys)
|
||||
main.put_str(spub.tobytes())
|
||||
main.put_str(spriv.tobytes())
|
||||
return main.topem()
|
361
venv/Lib/site-packages/ecdsa/test_curves.py
Normal file
361
venv/Lib/site-packages/ecdsa/test_curves.py
Normal file
@@ -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),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("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
|
||||
|
||||
|
||||
@pytest.mark.parametrize("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
|
||||
|
||||
|
||||
@pytest.mark.parametrize("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
|
||||
|
||||
|
||||
@pytest.mark.parametrize("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
|
478
venv/Lib/site-packages/ecdsa/test_der.py
Normal file
478
venv/Lib/site-packages/ecdsa/test_der.py
Normal file
@@ -0,0 +1,478 @@
|
||||
# compatibility with Python 2.6, for that we need unittest2 package,
|
||||
# which is not available on 3.3 or 3.4
|
||||
import warnings
|
||||
from binascii import hexlify
|
||||
|
||||
try:
|
||||
import unittest2 as unittest
|
||||
except ImportError:
|
||||
import unittest
|
||||
import sys
|
||||
import hypothesis.strategies as st
|
||||
from hypothesis import given, settings
|
||||
import pytest
|
||||
from ._compat import str_idx_as_int
|
||||
from .curves import NIST256p, NIST224p
|
||||
from .der import (
|
||||
remove_integer,
|
||||
UnexpectedDER,
|
||||
read_length,
|
||||
encode_bitstring,
|
||||
remove_bitstring,
|
||||
remove_object,
|
||||
encode_oid,
|
||||
remove_constructed,
|
||||
remove_octet_string,
|
||||
remove_sequence,
|
||||
)
|
||||
|
||||
|
||||
class TestRemoveInteger(unittest.TestCase):
|
||||
# DER requires the integers to be 0-padded only if they would be
|
||||
# interpreted as negative, check if those errors are detected
|
||||
def test_non_minimal_encoding(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
remove_integer(b"\x02\x02\x00\x01")
|
||||
|
||||
def test_negative_with_high_bit_set(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
remove_integer(b"\x02\x01\x80")
|
||||
|
||||
def test_minimal_with_high_bit_set(self):
|
||||
val, rem = remove_integer(b"\x02\x02\x00\x80")
|
||||
|
||||
self.assertEqual(val, 0x80)
|
||||
self.assertEqual(rem, b"")
|
||||
|
||||
def test_two_zero_bytes_with_high_bit_set(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
remove_integer(b"\x02\x03\x00\x00\xff")
|
||||
|
||||
def test_zero_length_integer(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
remove_integer(b"\x02\x00")
|
||||
|
||||
def test_empty_string(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
remove_integer(b"")
|
||||
|
||||
def test_encoding_of_zero(self):
|
||||
val, rem = remove_integer(b"\x02\x01\x00")
|
||||
|
||||
self.assertEqual(val, 0)
|
||||
self.assertEqual(rem, b"")
|
||||
|
||||
def test_encoding_of_127(self):
|
||||
val, rem = remove_integer(b"\x02\x01\x7f")
|
||||
|
||||
self.assertEqual(val, 127)
|
||||
self.assertEqual(rem, b"")
|
||||
|
||||
def test_encoding_of_128(self):
|
||||
val, rem = remove_integer(b"\x02\x02\x00\x80")
|
||||
|
||||
self.assertEqual(val, 128)
|
||||
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):
|
||||
# DER requires the lengths between 0 and 127 to be encoded using the short
|
||||
# form and lengths above that encoded with minimal number of bytes
|
||||
# necessary
|
||||
def test_zero_length(self):
|
||||
self.assertEqual((0, 1), read_length(b"\x00"))
|
||||
|
||||
def test_two_byte_zero_length(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
read_length(b"\x81\x00")
|
||||
|
||||
def test_two_byte_small_length(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
read_length(b"\x81\x7f")
|
||||
|
||||
def test_long_form_with_zero_length(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
read_length(b"\x80")
|
||||
|
||||
def test_smallest_two_byte_length(self):
|
||||
self.assertEqual((128, 2), read_length(b"\x81\x80"))
|
||||
|
||||
def test_zero_padded_length(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
read_length(b"\x82\x00\x80")
|
||||
|
||||
def test_two_three_byte_length(self):
|
||||
self.assertEqual((256, 3), read_length(b"\x82\x01\x00"))
|
||||
|
||||
def test_empty_string(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
read_length(b"")
|
||||
|
||||
def test_length_overflow(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
read_length(b"\x83\x01\x00")
|
||||
|
||||
|
||||
class TestEncodeBitstring(unittest.TestCase):
|
||||
# DER requires BIT STRINGS to include a number of padding bits in the
|
||||
# encoded byte string, that padding must be between 0 and 7
|
||||
|
||||
def test_old_call_convention(self):
|
||||
"""This is the old way to use the function."""
|
||||
warnings.simplefilter("always")
|
||||
with pytest.warns(DeprecationWarning) as warns:
|
||||
der = encode_bitstring(b"\x00\xff")
|
||||
|
||||
self.assertEqual(len(warns), 1)
|
||||
self.assertIn(
|
||||
"unused= needs to be specified", warns[0].message.args[0]
|
||||
)
|
||||
|
||||
self.assertEqual(der, b"\x03\x02\x00\xff")
|
||||
|
||||
def test_new_call_convention(self):
|
||||
"""This is how it should be called now."""
|
||||
# make sure no warnings are raised
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("error")
|
||||
der = encode_bitstring(b"\xff", 0)
|
||||
|
||||
self.assertEqual(der, b"\x03\x02\x00\xff")
|
||||
|
||||
def test_implicit_unused_bits(self):
|
||||
"""
|
||||
Writing bit string with already included the number of unused bits.
|
||||
"""
|
||||
# make sure no warnings are raised
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("error")
|
||||
der = encode_bitstring(b"\x00\xff", None)
|
||||
|
||||
self.assertEqual(der, b"\x03\x02\x00\xff")
|
||||
|
||||
def test_explicit_unused_bits(self):
|
||||
der = encode_bitstring(b"\xff\xf0", 4)
|
||||
|
||||
self.assertEqual(der, b"\x03\x03\x04\xff\xf0")
|
||||
|
||||
def test_empty_string(self):
|
||||
self.assertEqual(encode_bitstring(b"", 0), b"\x03\x01\x00")
|
||||
|
||||
def test_invalid_unused_count(self):
|
||||
with self.assertRaises(ValueError):
|
||||
encode_bitstring(b"\xff\x00", 8)
|
||||
|
||||
def test_invalid_unused_with_empty_string(self):
|
||||
with self.assertRaises(ValueError):
|
||||
encode_bitstring(b"", 1)
|
||||
|
||||
def test_non_zero_padding_bits(self):
|
||||
with self.assertRaises(ValueError):
|
||||
encode_bitstring(b"\xff", 2)
|
||||
|
||||
|
||||
class TestRemoveBitstring(unittest.TestCase):
|
||||
def test_old_call_convention(self):
|
||||
"""This is the old way to call the function."""
|
||||
warnings.simplefilter("always")
|
||||
with pytest.warns(DeprecationWarning) as warns:
|
||||
bits, rest = remove_bitstring(b"\x03\x02\x00\xff")
|
||||
|
||||
self.assertEqual(len(warns), 1)
|
||||
self.assertIn(
|
||||
"expect_unused= needs to be specified", warns[0].message.args[0]
|
||||
)
|
||||
|
||||
self.assertEqual(bits, b"\x00\xff")
|
||||
self.assertEqual(rest, b"")
|
||||
|
||||
def test_new_call_convention(self):
|
||||
# make sure no warnings are raised
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("error")
|
||||
bits, rest = remove_bitstring(b"\x03\x02\x00\xff", 0)
|
||||
|
||||
self.assertEqual(bits, b"\xff")
|
||||
self.assertEqual(rest, b"")
|
||||
|
||||
def test_implicit_unexpected_unused(self):
|
||||
# make sure no warnings are raised
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("error")
|
||||
bits, rest = remove_bitstring(b"\x03\x02\x00\xff", None)
|
||||
|
||||
self.assertEqual(bits, (b"\xff", 0))
|
||||
self.assertEqual(rest, b"")
|
||||
|
||||
def test_with_padding(self):
|
||||
ret, rest = remove_bitstring(b"\x03\x02\x04\xf0", None)
|
||||
|
||||
self.assertEqual(ret, (b"\xf0", 4))
|
||||
self.assertEqual(rest, b"")
|
||||
|
||||
def test_not_a_bitstring(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
remove_bitstring(b"\x02\x02\x00\xff", None)
|
||||
|
||||
def test_empty_encoding(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
remove_bitstring(b"\x03\x00", None)
|
||||
|
||||
def test_empty_string(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
remove_bitstring(b"", None)
|
||||
|
||||
def test_no_length(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
remove_bitstring(b"\x03", None)
|
||||
|
||||
def test_unexpected_number_of_unused_bits(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
remove_bitstring(b"\x03\x02\x00\xff", 1)
|
||||
|
||||
def test_invalid_encoding_of_unused_bits(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
remove_bitstring(b"\x03\x03\x08\xff\x00", None)
|
||||
|
||||
def test_invalid_encoding_of_empty_string(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
remove_bitstring(b"\x03\x01\x01", None)
|
||||
|
||||
def test_invalid_padding_bits(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
remove_bitstring(b"\x03\x02\x01\xff", None)
|
||||
|
||||
|
||||
class TestStrIdxAsInt(unittest.TestCase):
|
||||
def test_str(self):
|
||||
self.assertEqual(115, str_idx_as_int("str", 0))
|
||||
|
||||
def test_bytes(self):
|
||||
self.assertEqual(115, str_idx_as_int(b"str", 0))
|
||||
|
||||
def test_bytearray(self):
|
||||
self.assertEqual(115, str_idx_as_int(bytearray(b"str"), 0))
|
||||
|
||||
|
||||
class TestEncodeOid(unittest.TestCase):
|
||||
def test_pub_key_oid(self):
|
||||
oid_ecPublicKey = encode_oid(1, 2, 840, 10045, 2, 1)
|
||||
self.assertEqual(hexlify(oid_ecPublicKey), b"06072a8648ce3d0201")
|
||||
|
||||
def test_nist224p_oid(self):
|
||||
self.assertEqual(hexlify(NIST224p.encoded_oid), b"06052b81040021")
|
||||
|
||||
def test_nist256p_oid(self):
|
||||
self.assertEqual(
|
||||
hexlify(NIST256p.encoded_oid), b"06082a8648ce3d030107"
|
||||
)
|
||||
|
||||
def test_large_second_subid(self):
|
||||
# from X.690, section 8.19.5
|
||||
oid = encode_oid(2, 999, 3)
|
||||
self.assertEqual(oid, b"\x06\x03\x88\x37\x03")
|
||||
|
||||
def test_with_two_subids(self):
|
||||
oid = encode_oid(2, 999)
|
||||
self.assertEqual(oid, b"\x06\x02\x88\x37")
|
||||
|
||||
def test_zero_zero(self):
|
||||
oid = encode_oid(0, 0)
|
||||
self.assertEqual(oid, b"\x06\x01\x00")
|
||||
|
||||
def test_with_wrong_types(self):
|
||||
with self.assertRaises((TypeError, AssertionError)):
|
||||
encode_oid(0, None)
|
||||
|
||||
def test_with_small_first_large_second(self):
|
||||
with self.assertRaises(AssertionError):
|
||||
encode_oid(1, 40)
|
||||
|
||||
def test_small_first_max_second(self):
|
||||
oid = encode_oid(1, 39)
|
||||
self.assertEqual(oid, b"\x06\x01\x4f")
|
||||
|
||||
def test_with_invalid_first(self):
|
||||
with self.assertRaises(AssertionError):
|
||||
encode_oid(3, 39)
|
||||
|
||||
|
||||
class TestRemoveObject(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.oid_ecPublicKey = encode_oid(1, 2, 840, 10045, 2, 1)
|
||||
|
||||
def test_pub_key_oid(self):
|
||||
oid, rest = remove_object(self.oid_ecPublicKey)
|
||||
self.assertEqual(rest, b"")
|
||||
self.assertEqual(oid, (1, 2, 840, 10045, 2, 1))
|
||||
|
||||
def test_with_extra_bytes(self):
|
||||
oid, rest = remove_object(self.oid_ecPublicKey + b"more")
|
||||
self.assertEqual(rest, b"more")
|
||||
self.assertEqual(oid, (1, 2, 840, 10045, 2, 1))
|
||||
|
||||
def test_with_large_second_subid(self):
|
||||
# from X.690, section 8.19.5
|
||||
oid, rest = remove_object(b"\x06\x03\x88\x37\x03")
|
||||
self.assertEqual(rest, b"")
|
||||
self.assertEqual(oid, (2, 999, 3))
|
||||
|
||||
def test_with_padded_first_subid(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
remove_object(b"\x06\x02\x80\x00")
|
||||
|
||||
def test_with_padded_second_subid(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
remove_object(b"\x06\x04\x88\x37\x80\x01")
|
||||
|
||||
def test_with_missing_last_byte_of_multi_byte(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
remove_object(b"\x06\x03\x88\x37\x83")
|
||||
|
||||
def test_with_two_subids(self):
|
||||
oid, rest = remove_object(b"\x06\x02\x88\x37")
|
||||
self.assertEqual(rest, b"")
|
||||
self.assertEqual(oid, (2, 999))
|
||||
|
||||
def test_zero_zero(self):
|
||||
oid, rest = remove_object(b"\x06\x01\x00")
|
||||
self.assertEqual(rest, b"")
|
||||
self.assertEqual(oid, (0, 0))
|
||||
|
||||
def test_empty_string(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
remove_object(b"")
|
||||
|
||||
def test_missing_length(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
remove_object(b"\x06")
|
||||
|
||||
def test_empty_oid(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
remove_object(b"\x06\x00")
|
||||
|
||||
def test_empty_oid_overflow(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
remove_object(b"\x06\x01")
|
||||
|
||||
def test_with_wrong_type(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
remove_object(b"\x04\x02\x88\x37")
|
||||
|
||||
def test_with_too_long_length(self):
|
||||
with self.assertRaises(UnexpectedDER):
|
||||
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):
|
||||
"""
|
||||
Hypothesis strategy that returns valid OBJECT IDENTIFIERs as tuples
|
||||
|
||||
:param max_value: maximum value of any single sub-identifier
|
||||
:param max_size: maximum length of the generated OID
|
||||
"""
|
||||
first = draw(st.integers(min_value=0, max_value=2))
|
||||
if first < 2:
|
||||
second = draw(st.integers(min_value=0, max_value=39))
|
||||
else:
|
||||
second = draw(st.integers(min_value=0, max_value=max_value))
|
||||
rest = draw(
|
||||
st.lists(
|
||||
st.integers(min_value=0, max_value=max_value), max_size=max_size
|
||||
)
|
||||
)
|
||||
return (first, second) + tuple(rest)
|
||||
|
||||
|
||||
HYP_SETTINGS = {}
|
||||
|
||||
|
||||
if "--fast" in sys.argv: # pragma: no cover
|
||||
HYP_SETTINGS["max_examples"] = 2
|
||||
|
||||
|
||||
@settings(**HYP_SETTINGS)
|
||||
@given(st_oid())
|
||||
def test_oids(ids):
|
||||
encoded_oid = encode_oid(*ids)
|
||||
decoded_oid, rest = remove_object(encoded_oid)
|
||||
assert rest == b""
|
||||
assert decoded_oid == ids
|
449
venv/Lib/site-packages/ecdsa/test_ecdh.py
Normal file
449
venv/Lib/site-packages/ecdsa/test_ecdh.py
Normal file
@@ -0,0 +1,449 @@
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import subprocess
|
||||
import pytest
|
||||
from binascii import unhexlify
|
||||
|
||||
try:
|
||||
import unittest2 as unittest
|
||||
except ImportError:
|
||||
import unittest
|
||||
|
||||
from .curves import (
|
||||
NIST192p,
|
||||
NIST224p,
|
||||
NIST256p,
|
||||
NIST384p,
|
||||
NIST521p,
|
||||
BRAINPOOLP160r1,
|
||||
SECP112r2,
|
||||
SECP128r1,
|
||||
)
|
||||
from .curves import curves
|
||||
from .ecdh import (
|
||||
ECDH,
|
||||
InvalidCurveError,
|
||||
InvalidSharedSecretError,
|
||||
NoKeyError,
|
||||
NoCurveError,
|
||||
)
|
||||
from .keys import SigningKey, VerifyingKey
|
||||
from .ellipticcurve import CurveEdTw
|
||||
|
||||
|
||||
if "--fast" in sys.argv: # pragma: no cover
|
||||
curves = [SECP112r2, SECP128r1]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"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)
|
||||
|
||||
ecdh2.generate_private_key()
|
||||
ecdh1.load_received_public_key(ecdh2.get_public_key())
|
||||
ecdh2.load_received_public_key(ecdh1.generate_private_key())
|
||||
|
||||
secret1 = ecdh1.generate_sharedsecret_bytes()
|
||||
secret2 = ecdh2.generate_sharedsecret_bytes()
|
||||
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)
|
||||
|
||||
with pytest.raises(NoKeyError):
|
||||
ecdh1.generate_sharedsecret_bytes()
|
||||
|
||||
ecdh1.generate_private_key()
|
||||
|
||||
with pytest.raises(NoKeyError):
|
||||
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()
|
||||
ecdh2 = ECDH(curve=NIST256p)
|
||||
ecdh2.generate_private_key()
|
||||
|
||||
with pytest.raises(InvalidCurveError):
|
||||
ecdh1.load_received_public_key(ecdh2.get_public_key())
|
||||
|
||||
with pytest.raises(InvalidCurveError):
|
||||
ecdh2.load_received_public_key(ecdh1.get_public_key())
|
||||
|
||||
ecdh1.public_key = ecdh2.get_public_key()
|
||||
ecdh2.public_key = ecdh1.get_public_key()
|
||||
|
||||
with pytest.raises(InvalidCurveError):
|
||||
ecdh1.generate_sharedsecret_bytes()
|
||||
|
||||
with pytest.raises(InvalidCurveError):
|
||||
ecdh2.generate_sharedsecret_bytes()
|
||||
|
||||
|
||||
def test_ecdh_invalid_shared_secret_curve():
|
||||
ecdh1 = ECDH(curve=NIST256p)
|
||||
ecdh1.generate_private_key()
|
||||
|
||||
ecdh1.load_received_public_key(
|
||||
SigningKey.generate(NIST256p).get_verifying_key()
|
||||
)
|
||||
|
||||
ecdh1.private_key.privkey.secret_multiplier = ecdh1.private_key.curve.order
|
||||
|
||||
with pytest.raises(InvalidSharedSecretError):
|
||||
ecdh1.generate_sharedsecret_bytes()
|
||||
|
||||
|
||||
# https://github.com/scogliani/ecc-test-vectors/blob/master/ecdh_kat/secp192r1.txt
|
||||
# https://github.com/scogliani/ecc-test-vectors/blob/master/ecdh_kat/secp256r1.txt
|
||||
# https://github.com/coruus/nist-testvectors/blob/master/csrc.nist.gov/groups/STM/cavp/documents/components/ecccdhtestvectors/KAS_ECC_CDH_PrimitiveTest.txt
|
||||
@pytest.mark.parametrize(
|
||||
"curve,privatekey,pubkey,secret",
|
||||
[
|
||||
pytest.param(
|
||||
NIST192p,
|
||||
"f17d3fea367b74d340851ca4270dcb24c271f445bed9d527",
|
||||
"42ea6dd9969dd2a61fea1aac7f8e98edcc896c6e55857cc0"
|
||||
"dfbe5d7c61fac88b11811bde328e8a0d12bf01a9d204b523",
|
||||
"803d8ab2e5b6e6fca715737c3a82f7ce3c783124f6d51cd0",
|
||||
id="NIST192p-1",
|
||||
),
|
||||
pytest.param(
|
||||
NIST192p,
|
||||
"56e853349d96fe4c442448dacb7cf92bb7a95dcf574a9bd5",
|
||||
"deb5712fa027ac8d2f22c455ccb73a91e17b6512b5e030e7"
|
||||
"7e2690a02cc9b28708431a29fb54b87b1f0c14e011ac2125",
|
||||
"c208847568b98835d7312cef1f97f7aa298283152313c29d",
|
||||
id="NIST192p-2",
|
||||
),
|
||||
pytest.param(
|
||||
NIST192p,
|
||||
"c6ef61fe12e80bf56f2d3f7d0bb757394519906d55500949",
|
||||
"4edaa8efc5a0f40f843663ec5815e7762dddc008e663c20f"
|
||||
"0a9f8dc67a3e60ef6d64b522185d03df1fc0adfd42478279",
|
||||
"87229107047a3b611920d6e3b2c0c89bea4f49412260b8dd",
|
||||
id="NIST192p-3",
|
||||
),
|
||||
pytest.param(
|
||||
NIST192p,
|
||||
"e6747b9c23ba7044f38ff7e62c35e4038920f5a0163d3cda",
|
||||
"8887c276edeed3e9e866b46d58d895c73fbd80b63e382e88"
|
||||
"04c5097ba6645e16206cfb70f7052655947dd44a17f1f9d5",
|
||||
"eec0bed8fc55e1feddc82158fd6dc0d48a4d796aaf47d46c",
|
||||
id="NIST192p-4",
|
||||
),
|
||||
pytest.param(
|
||||
NIST192p,
|
||||
"beabedd0154a1afcfc85d52181c10f5eb47adc51f655047d",
|
||||
"0d045f30254adc1fcefa8a5b1f31bf4e739dd327cd18d594"
|
||||
"542c314e41427c08278a08ce8d7305f3b5b849c72d8aff73",
|
||||
"716e743b1b37a2cd8479f0a3d5a74c10ba2599be18d7e2f4",
|
||||
id="NIST192p-5",
|
||||
),
|
||||
pytest.param(
|
||||
NIST192p,
|
||||
"cf70354226667321d6e2baf40999e2fd74c7a0f793fa8699",
|
||||
"fb35ca20d2e96665c51b98e8f6eb3d79113508d8bccd4516"
|
||||
"368eec0d5bfb847721df6aaff0e5d48c444f74bf9cd8a5a7",
|
||||
"f67053b934459985a315cb017bf0302891798d45d0e19508",
|
||||
id="NIST192p-6",
|
||||
),
|
||||
pytest.param(
|
||||
NIST224p,
|
||||
"8346a60fc6f293ca5a0d2af68ba71d1dd389e5e40837942df3e43cbd",
|
||||
"af33cd0629bc7e996320a3f40368f74de8704fa37b8fab69abaae280"
|
||||
"882092ccbba7930f419a8a4f9bb16978bbc3838729992559a6f2e2d7",
|
||||
"7d96f9a3bd3c05cf5cc37feb8b9d5209d5c2597464dec3e9983743e8",
|
||||
id="NIST224p",
|
||||
),
|
||||
pytest.param(
|
||||
NIST256p,
|
||||
"7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534",
|
||||
"700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287"
|
||||
"db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac",
|
||||
"46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b",
|
||||
id="NIST256p-1",
|
||||
),
|
||||
pytest.param(
|
||||
NIST256p,
|
||||
"38f65d6dce47676044d58ce5139582d568f64bb16098d179dbab07741dd5caf5",
|
||||
"809f04289c64348c01515eb03d5ce7ac1a8cb9498f5caa50197e58d43a86a7ae"
|
||||
"b29d84e811197f25eba8f5194092cb6ff440e26d4421011372461f579271cda3",
|
||||
"057d636096cb80b67a8c038c890e887d1adfa4195e9b3ce241c8a778c59cda67",
|
||||
id="NIST256p-2",
|
||||
),
|
||||
pytest.param(
|
||||
NIST256p,
|
||||
"1accfaf1b97712b85a6f54b148985a1bdc4c9bec0bd258cad4b3d603f49f32c8",
|
||||
"a2339c12d4a03c33546de533268b4ad667debf458b464d77443636440ee7fec3"
|
||||
"ef48a3ab26e20220bcda2c1851076839dae88eae962869a497bf73cb66faf536",
|
||||
"2d457b78b4614132477618a5b077965ec90730a8c81a1c75d6d4ec68005d67ec",
|
||||
id="NIST256p-3",
|
||||
),
|
||||
pytest.param(
|
||||
NIST256p,
|
||||
"207c43a79bfee03db6f4b944f53d2fb76cc49ef1c9c4d34d51b6c65c4db6932d",
|
||||
"df3989b9fa55495719b3cf46dccd28b5153f7808191dd518eff0c3cff2b705ed"
|
||||
"422294ff46003429d739a33206c8752552c8ba54a270defc06e221e0feaf6ac4",
|
||||
"96441259534b80f6aee3d287a6bb17b5094dd4277d9e294f8fe73e48bf2a0024",
|
||||
id="NIST256p-4",
|
||||
),
|
||||
pytest.param(
|
||||
NIST256p,
|
||||
"59137e38152350b195c9718d39673d519838055ad908dd4757152fd8255c09bf",
|
||||
"41192d2813e79561e6a1d6f53c8bc1a433a199c835e141b05a74a97b0faeb922"
|
||||
"1af98cc45e98a7e041b01cf35f462b7562281351c8ebf3ffa02e33a0722a1328",
|
||||
"19d44c8d63e8e8dd12c22a87b8cd4ece27acdde04dbf47f7f27537a6999a8e62",
|
||||
id="NIST256p-5",
|
||||
),
|
||||
pytest.param(
|
||||
NIST256p,
|
||||
"f5f8e0174610a661277979b58ce5c90fee6c9b3bb346a90a7196255e40b132ef",
|
||||
"33e82092a0f1fb38f5649d5867fba28b503172b7035574bf8e5b7100a3052792"
|
||||
"f2cf6b601e0a05945e335550bf648d782f46186c772c0f20d3cd0d6b8ca14b2f",
|
||||
"664e45d5bba4ac931cd65d52017e4be9b19a515f669bea4703542a2c525cd3d3",
|
||||
id="NIST256p-6",
|
||||
),
|
||||
pytest.param(
|
||||
NIST384p,
|
||||
"3cc3122a68f0d95027ad38c067916ba0eb8c38894d22e1b1"
|
||||
"5618b6818a661774ad463b205da88cf699ab4d43c9cf98a1",
|
||||
"a7c76b970c3b5fe8b05d2838ae04ab47697b9eaf52e76459"
|
||||
"2efda27fe7513272734466b400091adbf2d68c58e0c50066"
|
||||
"ac68f19f2e1cb879aed43a9969b91a0839c4c38a49749b66"
|
||||
"1efedf243451915ed0905a32b060992b468c64766fc8437a",
|
||||
"5f9d29dc5e31a163060356213669c8ce132e22f57c9a04f4"
|
||||
"0ba7fcead493b457e5621e766c40a2e3d4d6a04b25e533f1",
|
||||
id="NIST384p",
|
||||
),
|
||||
pytest.param(
|
||||
NIST521p,
|
||||
"017eecc07ab4b329068fba65e56a1f8890aa935e57134ae0ffcce802735151f4ea"
|
||||
"c6564f6ee9974c5e6887a1fefee5743ae2241bfeb95d5ce31ddcb6f9edb4d6fc47",
|
||||
"00685a48e86c79f0f0875f7bc18d25eb5fc8c0b07e5da4f4370f3a949034085433"
|
||||
"4b1e1b87fa395464c60626124a4e70d0f785601d37c09870ebf176666877a2046d"
|
||||
"01ba52c56fc8776d9e8f5db4f0cc27636d0b741bbe05400697942e80b739884a83"
|
||||
"bde99e0f6716939e632bc8986fa18dccd443a348b6c3e522497955a4f3c302f676",
|
||||
"005fc70477c3e63bc3954bd0df3ea0d1f41ee21746ed95fc5e1fdf90930d5e1366"
|
||||
"72d72cc770742d1711c3c3a4c334a0ad9759436a4d3c5bf6e74b9578fac148c831",
|
||||
id="NIST521p",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_ecdh_NIST(curve, privatekey, pubkey, secret):
|
||||
ecdh = ECDH(curve=curve)
|
||||
ecdh.load_private_key_bytes(unhexlify(privatekey))
|
||||
ecdh.load_received_public_key_bytes(unhexlify(pubkey))
|
||||
|
||||
sharedsecret = ecdh.generate_sharedsecret_bytes()
|
||||
|
||||
assert sharedsecret == unhexlify(secret)
|
||||
|
||||
|
||||
pem_local_private_key = (
|
||||
"-----BEGIN EC PRIVATE KEY-----\n"
|
||||
"MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n"
|
||||
"BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n"
|
||||
"bA==\n"
|
||||
"-----END EC PRIVATE KEY-----\n"
|
||||
)
|
||||
der_local_private_key = (
|
||||
"305f02010104185ec8420bd6ef9252a942e989043ca29f561fa525770eb1c5a00a06082a864"
|
||||
"8ce3d030101a13403320004b88177d084ef17f5e45639408028360f9f59b4a4d7264e62da06"
|
||||
"51dce47a35a4c5b45cf51593423a8b557b9c2099f36c"
|
||||
)
|
||||
pem_remote_public_key = (
|
||||
"-----BEGIN PUBLIC KEY-----\n"
|
||||
"MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEuIF30ITvF/XkVjlAgCg2D59ZtKTX\n"
|
||||
"Jk5i2gZR3OR6NaTFtFz1FZNCOotVe5wgmfNs\n"
|
||||
"-----END PUBLIC KEY-----\n"
|
||||
)
|
||||
der_remote_public_key = (
|
||||
"3049301306072a8648ce3d020106082a8648ce3d03010103320004b88177d084ef17f5e4563"
|
||||
"9408028360f9f59b4a4d7264e62da0651dce47a35a4c5b45cf51593423a8b557b9c2099f36c"
|
||||
)
|
||||
gshared_secret = "8f457e34982478d1c34b9cd2d0c15911b72dd60d869e2cea"
|
||||
|
||||
|
||||
def test_ecdh_pem():
|
||||
ecdh = ECDH()
|
||||
ecdh.load_private_key_pem(pem_local_private_key)
|
||||
ecdh.load_received_public_key_pem(pem_remote_public_key)
|
||||
|
||||
sharedsecret = ecdh.generate_sharedsecret_bytes()
|
||||
|
||||
assert sharedsecret == unhexlify(gshared_secret)
|
||||
|
||||
|
||||
def test_ecdh_der():
|
||||
ecdh = ECDH()
|
||||
ecdh.load_private_key_der(unhexlify(der_local_private_key))
|
||||
ecdh.load_received_public_key_der(unhexlify(der_remote_public_key))
|
||||
|
||||
sharedsecret = ecdh.generate_sharedsecret_bytes()
|
||||
|
||||
assert sharedsecret == unhexlify(gshared_secret)
|
||||
|
||||
|
||||
# Exception classes used by run_openssl.
|
||||
class RunOpenSslError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def run_openssl(cmd):
|
||||
OPENSSL = "openssl"
|
||||
p = subprocess.Popen(
|
||||
[OPENSSL] + cmd.split(),
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
stdout, ignored = p.communicate()
|
||||
if p.returncode != 0:
|
||||
raise RunOpenSslError(
|
||||
"cmd '%s %s' failed: rc=%s, stdout/err was %s"
|
||||
% (OPENSSL, cmd, p.returncode, stdout)
|
||||
)
|
||||
return stdout.decode()
|
||||
|
||||
|
||||
OPENSSL_SUPPORTED_CURVES = set(
|
||||
c.split(":")[0].strip()
|
||||
for c in run_openssl("ecparam -list_curves").split("\n")
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
@pytest.mark.parametrize(
|
||||
"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)
|
||||
|
||||
try:
|
||||
hlp = run_openssl("pkeyutl -help")
|
||||
if hlp.find("-derive") == 0: # pragma: no cover
|
||||
pytest.skip("system openssl does not support `pkeyutl -derive`")
|
||||
except RunOpenSslError: # pragma: no cover
|
||||
pytest.skip("system openssl could not be executed")
|
||||
|
||||
if os.path.isdir("t"): # pragma: no branch
|
||||
shutil.rmtree("t")
|
||||
os.mkdir("t")
|
||||
run_openssl(
|
||||
"ecparam -name %s -genkey -out t/privkey1.pem" % vcurve.openssl_name
|
||||
)
|
||||
run_openssl(
|
||||
"ecparam -name %s -genkey -out t/privkey2.pem" % vcurve.openssl_name
|
||||
)
|
||||
run_openssl("ec -in t/privkey1.pem -pubout -out t/pubkey1.pem")
|
||||
|
||||
ecdh1 = ECDH(curve=vcurve)
|
||||
ecdh2 = ECDH(curve=vcurve)
|
||||
with open("t/privkey1.pem") as e:
|
||||
key = e.read()
|
||||
ecdh1.load_private_key_pem(key)
|
||||
with open("t/privkey2.pem") as e:
|
||||
key = e.read()
|
||||
ecdh2.load_private_key_pem(key)
|
||||
|
||||
with open("t/pubkey1.pem") as e:
|
||||
key = e.read()
|
||||
vk1 = VerifyingKey.from_pem(key)
|
||||
assert vk1.to_string() == ecdh1.get_public_key().to_string()
|
||||
vk2 = ecdh2.get_public_key()
|
||||
with open("t/pubkey2.pem", "wb") as e:
|
||||
e.write(vk2.to_pem())
|
||||
|
||||
ecdh1.load_received_public_key(vk2)
|
||||
ecdh2.load_received_public_key(vk1)
|
||||
secret1 = ecdh1.generate_sharedsecret_bytes()
|
||||
secret2 = ecdh2.generate_sharedsecret_bytes()
|
||||
|
||||
assert secret1 == secret2
|
||||
|
||||
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()
|
||||
|
||||
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
|
694
venv/Lib/site-packages/ecdsa/test_ecdsa.py
Normal file
694
venv/Lib/site-packages/ecdsa/test_ecdsa.py
Normal file
@@ -0,0 +1,694 @@
|
||||
from __future__ import print_function
|
||||
import sys
|
||||
import hypothesis.strategies as st
|
||||
from hypothesis import given, settings, note, example
|
||||
|
||||
try:
|
||||
import unittest2 as unittest
|
||||
except ImportError:
|
||||
import unittest
|
||||
import pytest
|
||||
from .ecdsa import (
|
||||
Private_key,
|
||||
Public_key,
|
||||
Signature,
|
||||
generator_192,
|
||||
digest_integer,
|
||||
ellipticcurve,
|
||||
point_is_valid,
|
||||
generator_224,
|
||||
generator_256,
|
||||
generator_384,
|
||||
generator_521,
|
||||
generator_secp256k1,
|
||||
curve_192,
|
||||
InvalidPointError,
|
||||
curve_112r2,
|
||||
generator_112r2,
|
||||
int_to_string,
|
||||
)
|
||||
from .ellipticcurve import Point
|
||||
|
||||
|
||||
HYP_SETTINGS = {}
|
||||
# old hypothesis doesn't have the "deadline" setting
|
||||
if sys.version_info > (2, 7): # pragma: no branch
|
||||
# SEC521p is slow, allow long execution for it
|
||||
HYP_SETTINGS["deadline"] = 5000
|
||||
|
||||
|
||||
class TestP192FromX9_62(unittest.TestCase):
|
||||
"""Check test vectors from X9.62"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.d = 651056770906015076056810763456358567190100156695615665659
|
||||
cls.Q = cls.d * generator_192
|
||||
cls.k = 6140507067065001063065065565667405560006161556565665656654
|
||||
cls.R = cls.k * generator_192
|
||||
|
||||
cls.msg = 968236873715988614170569073515315707566766479517
|
||||
cls.pubk = Public_key(generator_192, generator_192 * cls.d)
|
||||
cls.privk = Private_key(cls.pubk, cls.d)
|
||||
cls.sig = cls.privk.sign(cls.msg, cls.k)
|
||||
|
||||
def test_point_multiplication(self):
|
||||
assert self.Q.x() == 0x62B12D60690CDCF330BABAB6E69763B471F994DD702D16A5
|
||||
|
||||
def test_point_multiplication_2(self):
|
||||
assert self.R.x() == 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD
|
||||
assert self.R.y() == 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835
|
||||
|
||||
def test_mult_and_addition(self):
|
||||
u1 = 2563697409189434185194736134579731015366492496392189760599
|
||||
u2 = 6266643813348617967186477710235785849136406323338782220568
|
||||
temp = u1 * generator_192 + u2 * self.Q
|
||||
assert temp.x() == 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD
|
||||
assert temp.y() == 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835
|
||||
|
||||
def test_signature(self):
|
||||
r, s = self.sig.r, self.sig.s
|
||||
assert r == 3342403536405981729393488334694600415596881826869351677613
|
||||
assert s == 5735822328888155254683894997897571951568553642892029982342
|
||||
|
||||
def test_verification(self):
|
||||
assert self.pubk.verifies(self.msg, self.sig)
|
||||
|
||||
def test_rejection(self):
|
||||
assert not self.pubk.verifies(self.msg - 1, self.sig)
|
||||
|
||||
def test_verification_with_regular_point(self):
|
||||
pubk = Public_key(
|
||||
Point(
|
||||
generator_192.curve(),
|
||||
generator_192.x(),
|
||||
generator_192.y(),
|
||||
generator_192.order(),
|
||||
),
|
||||
self.pubk.point,
|
||||
)
|
||||
|
||||
assert pubk.verifies(self.msg, self.sig)
|
||||
|
||||
|
||||
class TestPublicKey(unittest.TestCase):
|
||||
def test_equality_public_keys(self):
|
||||
gen = generator_192
|
||||
x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6
|
||||
y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F
|
||||
point = ellipticcurve.Point(gen.curve(), x, y)
|
||||
pub_key1 = Public_key(gen, point)
|
||||
pub_key2 = Public_key(gen, point)
|
||||
self.assertEqual(pub_key1, pub_key2)
|
||||
|
||||
def test_inequality_public_key(self):
|
||||
gen = generator_192
|
||||
x1 = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6
|
||||
y1 = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F
|
||||
point1 = ellipticcurve.Point(gen.curve(), x1, y1)
|
||||
|
||||
x2 = 0x6A223D00BD22C52833409A163E057E5B5DA1DEF2A197DD15
|
||||
y2 = 0x7B482604199367F1F303F9EF627F922F97023E90EAE08ABF
|
||||
point2 = ellipticcurve.Point(gen.curve(), x2, y2)
|
||||
|
||||
pub_key1 = Public_key(gen, point1)
|
||||
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
|
||||
y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F
|
||||
point = ellipticcurve.Point(gen.curve(), x, y)
|
||||
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
|
||||
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_equality_private_keys(self):
|
||||
pr_key1 = Private_key(self.pub_key, 100)
|
||||
pr_key2 = Private_key(self.pub_key, 100)
|
||||
self.assertEqual(pr_key1, pr_key2)
|
||||
|
||||
def test_inequality_private_keys(self):
|
||||
pr_key1 = Private_key(self.pub_key, 100)
|
||||
pr_key2 = Private_key(self.pub_key, 200)
|
||||
self.assertNotEqual(pr_key1, pr_key2)
|
||||
|
||||
def test_inequality_private_keys_not_implemented(self):
|
||||
pr_key = Private_key(self.pub_key, 100)
|
||||
self.assertNotEqual(pr_key, None)
|
||||
|
||||
|
||||
# Testing point validity, as per ECDSAVS.pdf B.2.2:
|
||||
P192_POINTS = [
|
||||
(
|
||||
generator_192,
|
||||
0xCD6D0F029A023E9AACA429615B8F577ABEE685D8257CC83A,
|
||||
0x00019C410987680E9FB6C0B6ECC01D9A2647C8BAE27721BACDFC,
|
||||
False,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
0x00017F2FCE203639E9EAF9FB50B81FC32776B30E3B02AF16C73B,
|
||||
0x95DA95C5E72DD48E229D4748D4EEE658A9A54111B23B2ADB,
|
||||
False,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
0x4F77F8BC7FCCBADD5760F4938746D5F253EE2168C1CF2792,
|
||||
0x000147156FF824D131629739817EDB197717C41AAB5C2A70F0F6,
|
||||
False,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6,
|
||||
0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F,
|
||||
True,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
0xCDF56C1AA3D8AFC53C521ADF3FFB96734A6A630A4A5B5A70,
|
||||
0x97C1C44A5FB229007B5EC5D25F7413D170068FFD023CAA4E,
|
||||
True,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
0x89009C0DC361C81E99280C8E91DF578DF88CDF4B0CDEDCED,
|
||||
0x27BE44A529B7513E727251F128B34262A0FD4D8EC82377B9,
|
||||
True,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
0x6A223D00BD22C52833409A163E057E5B5DA1DEF2A197DD15,
|
||||
0x7B482604199367F1F303F9EF627F922F97023E90EAE08ABF,
|
||||
True,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
0x6DCCBDE75C0948C98DAB32EA0BC59FE125CF0FB1A3798EDA,
|
||||
0x0001171A3E0FA60CF3096F4E116B556198DE430E1FBD330C8835,
|
||||
False,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
0xD266B39E1F491FC4ACBBBC7D098430931CFA66D55015AF12,
|
||||
0x193782EB909E391A3148B7764E6B234AA94E48D30A16DBB2,
|
||||
False,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
0x9D6DDBCD439BAA0C6B80A654091680E462A7D1D3F1FFEB43,
|
||||
0x6AD8EFC4D133CCF167C44EB4691C80ABFFB9F82B932B8CAA,
|
||||
False,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
0x146479D944E6BDA87E5B35818AA666A4C998A71F4E95EDBC,
|
||||
0xA86D6FE62BC8FBD88139693F842635F687F132255858E7F6,
|
||||
False,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
0xE594D4A598046F3598243F50FD2C7BD7D380EDB055802253,
|
||||
0x509014C0C4D6B536E3CA750EC09066AF39B4C8616A53A923,
|
||||
False,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("generator,x,y,expected", P192_POINTS)
|
||||
def test_point_validity(generator, x, y, expected):
|
||||
"""
|
||||
`generator` defines the curve; is `(x, y)` a point on
|
||||
this curve? `expected` is True if the right answer is Yes.
|
||||
"""
|
||||
assert point_is_valid(generator, x, y) == expected
|
||||
|
||||
|
||||
# Trying signature-verification tests from ECDSAVS.pdf B.2.4:
|
||||
CURVE_192_KATS = [
|
||||
(
|
||||
generator_192,
|
||||
int(
|
||||
"0x84ce72aa8699df436059f052ac51b6398d2511e49631bcb7e71f89c499b9ee"
|
||||
"425dfbc13a5f6d408471b054f2655617cbbaf7937b7c80cd8865cf02c8487d30"
|
||||
"d2b0fbd8b2c4e102e16d828374bbc47b93852f212d5043c3ea720f086178ff79"
|
||||
"8cc4f63f787b9c2e419efa033e7644ea7936f54462dc21a6c4580725f7f0e7d1"
|
||||
"58",
|
||||
16,
|
||||
),
|
||||
0xD9DBFB332AA8E5FF091E8CE535857C37C73F6250FFB2E7AC,
|
||||
0x282102E364FEDED3AD15DDF968F88D8321AA268DD483EBC4,
|
||||
0x64DCA58A20787C488D11D6DD96313F1B766F2D8EFE122916,
|
||||
0x1ECBA28141E84AB4ECAD92F56720E2CC83EB3D22DEC72479,
|
||||
True,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
int(
|
||||
"0x94bb5bacd5f8ea765810024db87f4224ad71362a3c28284b2b9f39fab86db1"
|
||||
"2e8beb94aae899768229be8fdb6c4f12f28912bb604703a79ccff769c1607f5a"
|
||||
"91450f30ba0460d359d9126cbd6296be6d9c4bb96c0ee74cbb44197c207f6db3"
|
||||
"26ab6f5a659113a9034e54be7b041ced9dcf6458d7fb9cbfb2744d999f7dfd63"
|
||||
"f4",
|
||||
16,
|
||||
),
|
||||
0x3E53EF8D3112AF3285C0E74842090712CD324832D4277AE7,
|
||||
0xCC75F8952D30AEC2CBB719FC6AA9934590B5D0FF5A83ADB7,
|
||||
0x8285261607283BA18F335026130BAB31840DCFD9C3E555AF,
|
||||
0x356D89E1B04541AFC9704A45E9C535CE4A50929E33D7E06C,
|
||||
True,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
int(
|
||||
"0xf6227a8eeb34afed1621dcc89a91d72ea212cb2f476839d9b4243c66877911"
|
||||
"b37b4ad6f4448792a7bbba76c63bdd63414b6facab7dc71c3396a73bd7ee14cd"
|
||||
"d41a659c61c99b779cecf07bc51ab391aa3252386242b9853ea7da67fd768d30"
|
||||
"3f1b9b513d401565b6f1eb722dfdb96b519fe4f9bd5de67ae131e64b40e78c42"
|
||||
"dd",
|
||||
16,
|
||||
),
|
||||
0x16335DBE95F8E8254A4E04575D736BEFB258B8657F773CB7,
|
||||
0x421B13379C59BC9DCE38A1099CA79BBD06D647C7F6242336,
|
||||
0x4141BD5D64EA36C5B0BD21EF28C02DA216ED9D04522B1E91,
|
||||
0x159A6AA852BCC579E821B7BB0994C0861FB08280C38DAA09,
|
||||
False,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
int(
|
||||
"0x16b5f93afd0d02246f662761ed8e0dd9504681ed02a253006eb36736b56309"
|
||||
"7ba39f81c8e1bce7a16c1339e345efabbc6baa3efb0612948ae51103382a8ee8"
|
||||
"bc448e3ef71e9f6f7a9676694831d7f5dd0db5446f179bcb737d4a526367a447"
|
||||
"bfe2c857521c7f40b6d7d7e01a180d92431fb0bbd29c04a0c420a57b3ed26ccd"
|
||||
"8a",
|
||||
16,
|
||||
),
|
||||
0xFD14CDF1607F5EFB7B1793037B15BDF4BAA6F7C16341AB0B,
|
||||
0x83FA0795CC6C4795B9016DAC928FD6BAC32F3229A96312C4,
|
||||
0x8DFDB832951E0167C5D762A473C0416C5C15BC1195667DC1,
|
||||
0x1720288A2DC13FA1EC78F763F8FE2FF7354A7E6FDDE44520,
|
||||
False,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
int(
|
||||
"0x08a2024b61b79d260e3bb43ef15659aec89e5b560199bc82cf7c65c77d3919"
|
||||
"2e03b9a895d766655105edd9188242b91fbde4167f7862d4ddd61e5d4ab55196"
|
||||
"683d4f13ceb90d87aea6e07eb50a874e33086c4a7cb0273a8e1c4408f4b846bc"
|
||||
"eae1ebaac1b2b2ea851a9b09de322efe34cebe601653efd6ddc876ce8c2f2072"
|
||||
"fb",
|
||||
16,
|
||||
),
|
||||
0x674F941DC1A1F8B763C9334D726172D527B90CA324DB8828,
|
||||
0x65ADFA32E8B236CB33A3E84CF59BFB9417AE7E8EDE57A7FF,
|
||||
0x9508B9FDD7DAF0D8126F9E2BC5A35E4C6D800B5B804D7796,
|
||||
0x36F2BF6B21B987C77B53BB801B3435A577E3D493744BFAB0,
|
||||
False,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
int(
|
||||
"0x1843aba74b0789d4ac6b0b8923848023a644a7b70afa23b1191829bbe4397c"
|
||||
"e15b629bf21a8838298653ed0c19222b95fa4f7390d1b4c844d96e645537e0aa"
|
||||
"e98afb5c0ac3bd0e4c37f8daaff25556c64e98c319c52687c904c4de7240a1cc"
|
||||
"55cd9756b7edaef184e6e23b385726e9ffcba8001b8f574987c1a3fedaaa83ca"
|
||||
"6d",
|
||||
16,
|
||||
),
|
||||
0x10ECCA1AAD7220B56A62008B35170BFD5E35885C4014A19F,
|
||||
0x04EB61984C6C12ADE3BC47F3C629ECE7AA0A033B9948D686,
|
||||
0x82BFA4E82C0DFE9274169B86694E76CE993FD83B5C60F325,
|
||||
0xA97685676C59A65DBDE002FE9D613431FB183E8006D05633,
|
||||
False,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
int(
|
||||
"0x5a478f4084ddd1a7fea038aa9732a822106385797d02311aeef4d0264f824f"
|
||||
"698df7a48cfb6b578cf3da416bc0799425bb491be5b5ecc37995b85b03420a98"
|
||||
"f2c4dc5c31a69a379e9e322fbe706bbcaf0f77175e05cbb4fa162e0da82010a2"
|
||||
"78461e3e974d137bc746d1880d6eb02aa95216014b37480d84b87f717bb13f76"
|
||||
"e1",
|
||||
16,
|
||||
),
|
||||
0x6636653CB5B894CA65C448277B29DA3AD101C4C2300F7C04,
|
||||
0xFDF1CBB3FC3FD6A4F890B59E554544175FA77DBDBEB656C1,
|
||||
0xEAC2DDECDDFB79931A9C3D49C08DE0645C783A24CB365E1C,
|
||||
0x3549FEE3CFA7E5F93BC47D92D8BA100E881A2A93C22F8D50,
|
||||
False,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
int(
|
||||
"0xc598774259a058fa65212ac57eaa4f52240e629ef4c310722088292d1d4af6"
|
||||
"c39b49ce06ba77e4247b20637174d0bd67c9723feb57b5ead232b47ea452d5d7"
|
||||
"a089f17c00b8b6767e434a5e16c231ba0efa718a340bf41d67ea2d295812ff1b"
|
||||
"9277daacb8bc27b50ea5e6443bcf95ef4e9f5468fe78485236313d53d1c68f6b"
|
||||
"a2",
|
||||
16,
|
||||
),
|
||||
0xA82BD718D01D354001148CD5F69B9EBF38FF6F21898F8AAA,
|
||||
0xE67CEEDE07FC2EBFAFD62462A51E4B6C6B3D5B537B7CAF3E,
|
||||
0x4D292486C620C3DE20856E57D3BB72FCDE4A73AD26376955,
|
||||
0xA85289591A6081D5728825520E62FF1C64F94235C04C7F95,
|
||||
False,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
int(
|
||||
"0xca98ed9db081a07b7557f24ced6c7b9891269a95d2026747add9e9eb80638a"
|
||||
"961cf9c71a1b9f2c29744180bd4c3d3db60f2243c5c0b7cc8a8d40a3f9a7fc91"
|
||||
"0250f2187136ee6413ffc67f1a25e1c4c204fa9635312252ac0e0481d89b6d53"
|
||||
"808f0c496ba87631803f6c572c1f61fa049737fdacce4adff757afed4f05beb6"
|
||||
"58",
|
||||
16,
|
||||
),
|
||||
0x7D3B016B57758B160C4FCA73D48DF07AE3B6B30225126C2F,
|
||||
0x4AF3790D9775742BDE46F8DA876711BE1B65244B2B39E7EC,
|
||||
0x95F778F5F656511A5AB49A5D69DDD0929563C29CBC3A9E62,
|
||||
0x75C87FC358C251B4C83D2DD979FAAD496B539F9F2EE7A289,
|
||||
False,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
int(
|
||||
"0x31dd9a54c8338bea06b87eca813d555ad1850fac9742ef0bbe40dad400e102"
|
||||
"88acc9c11ea7dac79eb16378ebea9490e09536099f1b993e2653cd50240014c9"
|
||||
"0a9c987f64545abc6a536b9bd2435eb5e911fdfde2f13be96ea36ad38df4ae9e"
|
||||
"a387b29cced599af777338af2794820c9cce43b51d2112380a35802ab7e396c9"
|
||||
"7a",
|
||||
16,
|
||||
),
|
||||
0x9362F28C4EF96453D8A2F849F21E881CD7566887DA8BEB4A,
|
||||
0xE64D26D8D74C48A024AE85D982EE74CD16046F4EE5333905,
|
||||
0xF3923476A296C88287E8DE914B0B324AD5A963319A4FE73B,
|
||||
0xF0BAEED7624ED00D15244D8BA2AEDE085517DBDEC8AC65F5,
|
||||
True,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
int(
|
||||
"0xb2b94e4432267c92f9fdb9dc6040c95ffa477652761290d3c7de312283f645"
|
||||
"0d89cc4aabe748554dfb6056b2d8e99c7aeaad9cdddebdee9dbc099839562d90"
|
||||
"64e68e7bb5f3a6bba0749ca9a538181fc785553a4000785d73cc207922f63e8c"
|
||||
"e1112768cb1de7b673aed83a1e4a74592f1268d8e2a4e9e63d414b5d442bd045"
|
||||
"6d",
|
||||
16,
|
||||
),
|
||||
0xCC6FC032A846AAAC25533EB033522824F94E670FA997ECEF,
|
||||
0xE25463EF77A029ECCDA8B294FD63DD694E38D223D30862F1,
|
||||
0x066B1D07F3A40E679B620EDA7F550842A35C18B80C5EBE06,
|
||||
0xA0B0FB201E8F2DF65E2C4508EF303BDC90D934016F16B2DC,
|
||||
False,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
int(
|
||||
"0x4366fcadf10d30d086911de30143da6f579527036937007b337f7282460eae"
|
||||
"5678b15cccda853193ea5fc4bc0a6b9d7a31128f27e1214988592827520b214e"
|
||||
"ed5052f7775b750b0c6b15f145453ba3fee24a085d65287e10509eb5d5f602c4"
|
||||
"40341376b95c24e5c4727d4b859bfe1483d20538acdd92c7997fa9c614f0f839"
|
||||
"d7",
|
||||
16,
|
||||
),
|
||||
0x955C908FE900A996F7E2089BEE2F6376830F76A19135E753,
|
||||
0xBA0C42A91D3847DE4A592A46DC3FDAF45A7CC709B90DE520,
|
||||
0x1F58AD77FC04C782815A1405B0925E72095D906CBF52A668,
|
||||
0xF2E93758B3AF75EDF784F05A6761C9B9A6043C66B845B599,
|
||||
False,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
int(
|
||||
"0x543f8af57d750e33aa8565e0cae92bfa7a1ff78833093421c2942cadf99866"
|
||||
"70a5ff3244c02a8225e790fbf30ea84c74720abf99cfd10d02d34377c3d3b412"
|
||||
"69bea763384f372bb786b5846f58932defa68023136cd571863b304886e95e52"
|
||||
"e7877f445b9364b3f06f3c28da12707673fecb4b8071de06b6e0a3c87da160ce"
|
||||
"f3",
|
||||
16,
|
||||
),
|
||||
0x31F7FA05576D78A949B24812D4383107A9A45BB5FCCDD835,
|
||||
0x8DC0EB65994A90F02B5E19BD18B32D61150746C09107E76B,
|
||||
0xBE26D59E4E883DDE7C286614A767B31E49AD88789D3A78FF,
|
||||
0x8762CA831C1CE42DF77893C9B03119428E7A9B819B619068,
|
||||
False,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
int(
|
||||
"0xd2e8454143ce281e609a9d748014dcebb9d0bc53adb02443a6aac2ffe6cb009f"
|
||||
"387c346ecb051791404f79e902ee333ad65e5c8cb38dc0d1d39a8dc90add502357"
|
||||
"2720e5b94b190d43dd0d7873397504c0c7aef2727e628eb6a74411f2e400c65670"
|
||||
"716cb4a815dc91cbbfeb7cfe8c929e93184c938af2c078584da045e8f8d1",
|
||||
16,
|
||||
),
|
||||
0x66AA8EDBBDB5CF8E28CEB51B5BDA891CAE2DF84819FE25C0,
|
||||
0x0C6BC2F69030A7CE58D4A00E3B3349844784A13B8936F8DA,
|
||||
0xA4661E69B1734F4A71B788410A464B71E7FFE42334484F23,
|
||||
0x738421CF5E049159D69C57A915143E226CAC8355E149AFE9,
|
||||
False,
|
||||
),
|
||||
(
|
||||
generator_192,
|
||||
int(
|
||||
"0x6660717144040f3e2f95a4e25b08a7079c702a8b29babad5a19a87654bc5c5af"
|
||||
"a261512a11b998a4fb36b5d8fe8bd942792ff0324b108120de86d63f65855e5461"
|
||||
"184fc96a0a8ffd2ce6d5dfb0230cbbdd98f8543e361b3205f5da3d500fdc8bac6d"
|
||||
"b377d75ebef3cb8f4d1ff738071ad0938917889250b41dd1d98896ca06fb",
|
||||
16,
|
||||
),
|
||||
0xBCFACF45139B6F5F690A4C35A5FFFA498794136A2353FC77,
|
||||
0x6F4A6C906316A6AFC6D98FE1F0399D056F128FE0270B0F22,
|
||||
0x9DB679A3DAFE48F7CCAD122933ACFE9DA0970B71C94C21C1,
|
||||
0x984C2DB99827576C0A41A5DA41E07D8CC768BC82F18C9DA9,
|
||||
False,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("gen,msg,qx,qy,r,s,expected", CURVE_192_KATS)
|
||||
def test_signature_validity(gen, msg, qx, qy, r, s, expected):
|
||||
"""
|
||||
`msg` = message, `qx` and `qy` represent the base point on
|
||||
elliptic curve of `gen`, `r` and `s` are the signature, and
|
||||
`expected` is True iff the signature is expected to be valid."""
|
||||
pubk = Public_key(gen, ellipticcurve.Point(gen.curve(), qx, qy))
|
||||
with pytest.warns(DeprecationWarning) as warns:
|
||||
msg_dgst = digest_integer(msg)
|
||||
assert len(warns) == 3
|
||||
assert "unused" in warns[0].message.args[0]
|
||||
assert "unused" in warns[1].message.args[0]
|
||||
assert "unused" in warns[2].message.args[0]
|
||||
assert expected == pubk.verifies(msg_dgst, Signature(r, s))
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"gen,msg,qx,qy,r,s,expected", [x for x in CURVE_192_KATS if x[6]]
|
||||
)
|
||||
def test_pk_recovery(gen, msg, r, s, qx, qy, expected):
|
||||
del expected
|
||||
sign = Signature(r, s)
|
||||
with pytest.warns(DeprecationWarning) as warns:
|
||||
msg_dgst = digest_integer(msg)
|
||||
assert len(warns) == 3
|
||||
assert "unused" in warns[0].message.args[0]
|
||||
assert "unused" in warns[1].message.args[0]
|
||||
assert "unused" in warns[2].message.args[0]
|
||||
pks = sign.recover_public_keys(msg_dgst, gen)
|
||||
|
||||
assert pks
|
||||
|
||||
# Test if the signature is valid for all found public keys
|
||||
for pk in pks:
|
||||
q = pk.point
|
||||
test_signature_validity(gen, msg, q.x(), q.y(), r, s, True)
|
||||
|
||||
# Test if the original public key is in the set of found keys
|
||||
original_q = ellipticcurve.Point(gen.curve(), qx, qy)
|
||||
points = [pk.point for pk in pks]
|
||||
assert original_q in points
|
||||
|
||||
|
||||
@st.composite
|
||||
def st_random_gen_key_msg_nonce(draw):
|
||||
"""Hypothesis strategy for test_sig_verify()."""
|
||||
name_gen = {
|
||||
"generator_192": generator_192,
|
||||
"generator_224": generator_224,
|
||||
"generator_256": generator_256,
|
||||
"generator_secp256k1": generator_secp256k1,
|
||||
"generator_384": generator_384,
|
||||
"generator_521": generator_521,
|
||||
}
|
||||
name = draw(st.sampled_from(sorted(name_gen.keys())))
|
||||
note("Generator used: {0}".format(name))
|
||||
generator = name_gen[name]
|
||||
order = int(generator.order()) - 1
|
||||
|
||||
key = draw(st.integers(min_value=1, max_value=order))
|
||||
msg = draw(st.integers(min_value=1, max_value=order))
|
||||
nonce = draw(
|
||||
st.integers(min_value=1, max_value=order)
|
||||
| st.integers(min_value=order >> 1, max_value=order)
|
||||
)
|
||||
return generator, key, msg, nonce
|
||||
|
||||
|
||||
SIG_VER_SETTINGS = dict(HYP_SETTINGS)
|
||||
if "--fast" in sys.argv: # pragma: no cover
|
||||
SIG_VER_SETTINGS["max_examples"] = 1
|
||||
else:
|
||||
SIG_VER_SETTINGS["max_examples"] = 10
|
||||
|
||||
|
||||
@settings(**SIG_VER_SETTINGS)
|
||||
@example((generator_224, 4, 1, 1))
|
||||
@given(st_random_gen_key_msg_nonce())
|
||||
def test_sig_verify(args):
|
||||
"""
|
||||
Check if signing and verification works for arbitrary messages and
|
||||
that signatures for other messages are rejected.
|
||||
"""
|
||||
generator, sec_mult, msg, nonce = args
|
||||
|
||||
pubkey = Public_key(generator, generator * sec_mult)
|
||||
privkey = Private_key(pubkey, sec_mult)
|
||||
|
||||
signature = privkey.sign(msg, nonce)
|
||||
|
||||
assert pubkey.verifies(msg, signature)
|
||||
|
||||
assert not pubkey.verifies(msg - 1, signature)
|
||||
|
||||
|
||||
def test_int_to_string_with_zero():
|
||||
with pytest.warns(DeprecationWarning) as warns:
|
||||
assert int_to_string(0) == b"\x00"
|
||||
|
||||
assert len(warns) == 1
|
||||
assert "unused" in warns[0].message.args[0]
|
1124
venv/Lib/site-packages/ecdsa/test_eddsa.py
Normal file
1124
venv/Lib/site-packages/ecdsa/test_eddsa.py
Normal file
File diff suppressed because it is too large
Load Diff
256
venv/Lib/site-packages/ecdsa/test_ellipticcurve.py
Normal file
256
venv/Lib/site-packages/ecdsa/test_ellipticcurve.py
Normal file
@@ -0,0 +1,256 @@
|
||||
import pytest
|
||||
|
||||
try:
|
||||
import unittest2 as unittest
|
||||
except ImportError:
|
||||
import unittest
|
||||
from hypothesis import given, settings
|
||||
import hypothesis.strategies as st
|
||||
|
||||
try:
|
||||
from hypothesis import HealthCheck
|
||||
|
||||
HC_PRESENT = True
|
||||
except ImportError: # pragma: no cover
|
||||
HC_PRESENT = False
|
||||
from .numbertheory import inverse_mod
|
||||
from .ellipticcurve import CurveFp, INFINITY, Point, CurveEdTw
|
||||
|
||||
|
||||
HYP_SETTINGS = {}
|
||||
if HC_PRESENT: # pragma: no branch
|
||||
HYP_SETTINGS["suppress_health_check"] = [HealthCheck.too_slow]
|
||||
HYP_SETTINGS["deadline"] = 5000
|
||||
|
||||
|
||||
# NIST Curve P-192:
|
||||
p = 6277101735386680763835789423207666416083908700390324961279
|
||||
r = 6277101735386680763835789423176059013767194773182842284081
|
||||
# s = 0x3045ae6fc8422f64ed579528d38120eae12196d5
|
||||
# c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65
|
||||
b = 0x64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1
|
||||
Gx = 0x188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012
|
||||
Gy = 0x07192B95FFC8DA78631011ED6B24CDD573F977A11E794811
|
||||
|
||||
c192 = CurveFp(p, -3, b)
|
||||
p192 = Point(c192, Gx, Gy, r)
|
||||
|
||||
c_23 = CurveFp(23, 1, 1)
|
||||
g_23 = Point(c_23, 13, 7, 7)
|
||||
|
||||
|
||||
HYP_SLOW_SETTINGS = dict(HYP_SETTINGS)
|
||||
HYP_SLOW_SETTINGS["max_examples"] = 2
|
||||
|
||||
|
||||
@settings(**HYP_SLOW_SETTINGS)
|
||||
@given(st.integers(min_value=1, max_value=r - 1))
|
||||
def test_p192_mult_tests(multiple):
|
||||
inv_m = inverse_mod(multiple, r)
|
||||
|
||||
p1 = p192 * multiple
|
||||
assert p1 * inv_m == p192
|
||||
|
||||
|
||||
def add_n_times(point, n):
|
||||
ret = INFINITY
|
||||
i = 0
|
||||
while i <= n:
|
||||
yield ret
|
||||
ret = ret + point
|
||||
i += 1
|
||||
|
||||
|
||||
# From X9.62 I.1 (p. 96):
|
||||
@pytest.mark.parametrize(
|
||||
"p, m, check",
|
||||
[(g_23, n, exp) for n, exp in enumerate(add_n_times(g_23, 8))],
|
||||
ids=["g_23 test with mult {0}".format(i) for i in range(9)],
|
||||
)
|
||||
def test_add_and_mult_equivalence(p, m, check):
|
||||
assert p * m == check
|
||||
|
||||
|
||||
class TestCurve(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.c_23 = CurveFp(23, 1, 1)
|
||||
|
||||
def test_equality_curves(self):
|
||||
self.assertEqual(self.c_23, CurveFp(23, 1, 1))
|
||||
|
||||
def test_inequality_curves(self):
|
||||
c192 = CurveFp(p, -3, b)
|
||||
self.assertNotEqual(self.c_23, c192)
|
||||
|
||||
def test_usability_in_a_hashed_collection_curves(self):
|
||||
{self.c_23: None}
|
||||
|
||||
def test_hashability_curves(self):
|
||||
hash(self.c_23)
|
||||
|
||||
def test_conflation_curves(self):
|
||||
ne1, ne2, ne3 = CurveFp(24, 1, 1), CurveFp(23, 2, 1), CurveFp(23, 1, 2)
|
||||
eq1, eq2, eq3 = CurveFp(23, 1, 1), CurveFp(23, 1, 1), self.c_23
|
||||
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.assertIn(eq2, {eq3: None})
|
||||
|
||||
def test___str__(self):
|
||||
self.assertEqual(str(self.c_23), "CurveFp(p=23, a=1, b=1)")
|
||||
|
||||
def test___str___with_cofactor(self):
|
||||
c = CurveFp(23, 1, 1, 4)
|
||||
self.assertEqual(str(c), "CurveFp(p=23, a=1, b=1, h=4)")
|
||||
|
||||
|
||||
class TestCurveEdTw(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.c_23 = CurveEdTw(23, 1, 1)
|
||||
|
||||
def test___str__(self):
|
||||
self.assertEqual(str(self.c_23), "CurveEdTw(p=23, a=1, d=1)")
|
||||
|
||||
def test___str___with_cofactor(self):
|
||||
c = CurveEdTw(23, 1, 1, 4)
|
||||
self.assertEqual(str(c), "CurveEdTw(p=23, a=1, d=1, h=4)")
|
||||
|
||||
def test_usability_in_a_hashed_collection_curves(self):
|
||||
{self.c_23: None}
|
||||
|
||||
def test_hashability_curves(self):
|
||||
hash(self.c_23)
|
||||
|
||||
|
||||
class TestPoint(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.c_23 = CurveFp(23, 1, 1)
|
||||
cls.g_23 = Point(cls.c_23, 13, 7, 7)
|
||||
|
||||
p = 6277101735386680763835789423207666416083908700390324961279
|
||||
r = 6277101735386680763835789423176059013767194773182842284081
|
||||
# s = 0x3045ae6fc8422f64ed579528d38120eae12196d5
|
||||
# c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65
|
||||
b = 0x64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1
|
||||
Gx = 0x188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012
|
||||
Gy = 0x07192B95FFC8DA78631011ED6B24CDD573F977A11E794811
|
||||
|
||||
cls.c192 = CurveFp(p, -3, b)
|
||||
cls.p192 = Point(cls.c192, Gx, Gy, r)
|
||||
|
||||
def test_p192(self):
|
||||
# Checking against some sample computations presented
|
||||
# in X9.62:
|
||||
d = 651056770906015076056810763456358567190100156695615665659
|
||||
Q = d * self.p192
|
||||
self.assertEqual(
|
||||
Q.x(), 0x62B12D60690CDCF330BABAB6E69763B471F994DD702D16A5
|
||||
)
|
||||
|
||||
k = 6140507067065001063065065565667405560006161556565665656654
|
||||
R = k * self.p192
|
||||
self.assertEqual(
|
||||
R.x(), 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD
|
||||
)
|
||||
self.assertEqual(
|
||||
R.y(), 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835
|
||||
)
|
||||
|
||||
u1 = 2563697409189434185194736134579731015366492496392189760599
|
||||
u2 = 6266643813348617967186477710235785849136406323338782220568
|
||||
temp = u1 * self.p192 + u2 * Q
|
||||
self.assertEqual(
|
||||
temp.x(), 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD
|
||||
)
|
||||
self.assertEqual(
|
||||
temp.y(), 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835
|
||||
)
|
||||
|
||||
def test_double_infinity(self):
|
||||
p1 = INFINITY
|
||||
p3 = p1.double()
|
||||
self.assertEqual(p1, p3)
|
||||
self.assertEqual(p3.x(), p1.x())
|
||||
self.assertEqual(p3.y(), p3.y())
|
||||
|
||||
def test_double(self):
|
||||
x1, y1, x3, y3 = (3, 10, 7, 12)
|
||||
|
||||
p1 = Point(self.c_23, x1, y1)
|
||||
p3 = p1.double()
|
||||
self.assertEqual(p3.x(), x3)
|
||||
self.assertEqual(p3.y(), y3)
|
||||
|
||||
def test_multiply(self):
|
||||
x1, y1, m, x3, y3 = (3, 10, 2, 7, 12)
|
||||
p1 = Point(self.c_23, x1, y1)
|
||||
p3 = p1 * m
|
||||
self.assertEqual(p3.x(), x3)
|
||||
self.assertEqual(p3.y(), y3)
|
||||
|
||||
# Trivial tests from X9.62 B.3:
|
||||
def test_add(self):
|
||||
"""We expect that on curve c, (x1,y1) + (x2, y2 ) = (x3, y3)."""
|
||||
|
||||
x1, y1, x2, y2, x3, y3 = (3, 10, 9, 7, 17, 20)
|
||||
p1 = Point(self.c_23, x1, y1)
|
||||
p2 = Point(self.c_23, x2, y2)
|
||||
p3 = p1 + p2
|
||||
self.assertEqual(p3.x(), x3)
|
||||
self.assertEqual(p3.y(), y3)
|
||||
|
||||
def test_add_as_double(self):
|
||||
"""We expect that on curve c, (x1,y1) + (x2, y2 ) = (x3, y3)."""
|
||||
|
||||
x1, y1, x2, y2, x3, y3 = (3, 10, 3, 10, 7, 12)
|
||||
p1 = Point(self.c_23, x1, y1)
|
||||
p2 = Point(self.c_23, x2, y2)
|
||||
p3 = p1 + p2
|
||||
self.assertEqual(p3.x(), x3)
|
||||
self.assertEqual(p3.y(), y3)
|
||||
|
||||
def test_equality_points(self):
|
||||
self.assertEqual(self.g_23, Point(self.c_23, 13, 7, 7))
|
||||
|
||||
def test_inequality_points(self):
|
||||
c = CurveFp(100, -3, 100)
|
||||
p = Point(c, 100, 100, 100)
|
||||
self.assertNotEqual(self.g_23, p)
|
||||
|
||||
def test_inequality_points_diff_types(self):
|
||||
c = CurveFp(100, -3, 100)
|
||||
self.assertNotEqual(self.g_23, c)
|
||||
|
||||
def test_to_bytes_from_bytes(self):
|
||||
p = Point(self.c_23, 3, 10)
|
||||
|
||||
self.assertEqual(p, Point.from_bytes(self.c_23, p.to_bytes()))
|
||||
|
||||
def test_add_to_neg_self(self):
|
||||
p = Point(self.c_23, 3, 10)
|
||||
|
||||
self.assertEqual(INFINITY, p + (-p))
|
||||
|
||||
def test_add_to_infinity(self):
|
||||
p = Point(self.c_23, 3, 10)
|
||||
|
||||
self.assertIs(p, p + INFINITY)
|
||||
|
||||
def test_mul_infinity_by_scalar(self):
|
||||
self.assertIs(INFINITY, INFINITY * 10)
|
||||
|
||||
def test_mul_by_negative(self):
|
||||
p = Point(self.c_23, 3, 10)
|
||||
|
||||
self.assertEqual(p * -5, (-p) * 5)
|
||||
|
||||
def test_str_infinity(self):
|
||||
self.assertEqual(str(INFINITY), "infinity")
|
||||
|
||||
def test_str_point(self):
|
||||
p = Point(self.c_23, 3, 10)
|
||||
|
||||
self.assertEqual(str(p), "(3,10)")
|
753
venv/Lib/site-packages/ecdsa/test_jacobi.py
Normal file
753
venv/Lib/site-packages/ecdsa/test_jacobi.py
Normal file
@@ -0,0 +1,753 @@
|
||||
import pickle
|
||||
import sys
|
||||
|
||||
try:
|
||||
import unittest2 as unittest
|
||||
except ImportError:
|
||||
import unittest
|
||||
|
||||
import os
|
||||
import signal
|
||||
import pytest
|
||||
import threading
|
||||
import platform
|
||||
import hypothesis.strategies as st
|
||||
from hypothesis import given, assume, settings, example
|
||||
|
||||
from .ellipticcurve import CurveFp, PointJacobi, INFINITY
|
||||
from .ecdsa import (
|
||||
generator_256,
|
||||
curve_256,
|
||||
generator_224,
|
||||
generator_brainpoolp160r1,
|
||||
curve_brainpoolp160r1,
|
||||
generator_112r2,
|
||||
curve_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
|
||||
|
||||
|
||||
SLOW_SETTINGS = {}
|
||||
if "--fast" in sys.argv: # pragma: no cover
|
||||
SLOW_SETTINGS["max_examples"] = 2
|
||||
else:
|
||||
SLOW_SETTINGS["max_examples"] = 10
|
||||
|
||||
|
||||
class TestJacobi(unittest.TestCase):
|
||||
def test___init__(self):
|
||||
curve = object()
|
||||
x = 2
|
||||
y = 3
|
||||
z = 1
|
||||
order = 4
|
||||
pj = PointJacobi(curve, x, y, z, order)
|
||||
|
||||
self.assertEqual(pj.order(), order)
|
||||
self.assertIs(pj.curve(), curve)
|
||||
self.assertEqual(pj.x(), x)
|
||||
self.assertEqual(pj.y(), y)
|
||||
|
||||
def test_add_with_different_curves(self):
|
||||
p_a = PointJacobi.from_affine(generator_256)
|
||||
p_b = PointJacobi.from_affine(generator_224)
|
||||
|
||||
with self.assertRaises(ValueError): # pragma: no branch
|
||||
p_a + p_b
|
||||
|
||||
def test_compare_different_curves(self):
|
||||
self.assertNotEqual(generator_256, generator_224)
|
||||
|
||||
def test_equality_with_non_point(self):
|
||||
pj = PointJacobi.from_affine(generator_256)
|
||||
|
||||
self.assertNotEqual(pj, "value")
|
||||
|
||||
def test_conversion(self):
|
||||
pj = PointJacobi.from_affine(generator_256)
|
||||
pw = pj.to_affine()
|
||||
|
||||
self.assertEqual(generator_256, pw)
|
||||
|
||||
def test_single_double(self):
|
||||
pj = PointJacobi.from_affine(generator_256)
|
||||
pw = generator_256.double()
|
||||
|
||||
pj = pj.double()
|
||||
|
||||
self.assertEqual(pj.x(), pw.x())
|
||||
self.assertEqual(pj.y(), pw.y())
|
||||
|
||||
def test_double_with_zero_point(self):
|
||||
pj = PointJacobi(curve_256, 0, 0, 1)
|
||||
|
||||
pj = pj.double()
|
||||
|
||||
self.assertIs(pj, INFINITY)
|
||||
|
||||
def test_double_with_zero_equivalent_point(self):
|
||||
pj = PointJacobi(curve_256, 0, curve_256.p(), 1)
|
||||
|
||||
pj = pj.double()
|
||||
|
||||
self.assertIs(pj, INFINITY)
|
||||
|
||||
def test_double_with_zero_equivalent_point_non_1_z(self):
|
||||
pj = PointJacobi(curve_256, 0, curve_256.p(), 2)
|
||||
|
||||
pj = pj.double()
|
||||
|
||||
self.assertIs(pj, INFINITY)
|
||||
|
||||
def test_compare_with_affine_point(self):
|
||||
pj = PointJacobi.from_affine(generator_256)
|
||||
pa = pj.to_affine()
|
||||
|
||||
self.assertEqual(pj, pa)
|
||||
self.assertEqual(pa, pj)
|
||||
|
||||
def test_to_affine_with_zero_point(self):
|
||||
pj = PointJacobi(curve_256, 0, 0, 1)
|
||||
|
||||
pa = pj.to_affine()
|
||||
|
||||
self.assertIs(pa, INFINITY)
|
||||
|
||||
def test_add_with_affine_point(self):
|
||||
pj = PointJacobi.from_affine(generator_256)
|
||||
pa = pj.to_affine()
|
||||
|
||||
s = pj + pa
|
||||
|
||||
self.assertEqual(s, pj.double())
|
||||
|
||||
def test_radd_with_affine_point(self):
|
||||
pj = PointJacobi.from_affine(generator_256)
|
||||
pa = pj.to_affine()
|
||||
|
||||
s = pa + pj
|
||||
|
||||
self.assertEqual(s, pj.double())
|
||||
|
||||
def test_add_with_infinity(self):
|
||||
pj = PointJacobi.from_affine(generator_256)
|
||||
|
||||
s = pj + INFINITY
|
||||
|
||||
self.assertEqual(s, pj)
|
||||
|
||||
def test_add_zero_point_to_affine(self):
|
||||
pa = PointJacobi.from_affine(generator_256).to_affine()
|
||||
pj = PointJacobi(curve_256, 0, 0, 1)
|
||||
|
||||
s = pj + pa
|
||||
|
||||
self.assertIs(s, pa)
|
||||
|
||||
def test_multiply_by_zero(self):
|
||||
pj = PointJacobi.from_affine(generator_256)
|
||||
|
||||
pj = pj * 0
|
||||
|
||||
self.assertIs(pj, INFINITY)
|
||||
|
||||
def test_zero_point_multiply_by_one(self):
|
||||
pj = PointJacobi(curve_256, 0, 0, 1)
|
||||
|
||||
pj = pj * 1
|
||||
|
||||
self.assertIs(pj, INFINITY)
|
||||
|
||||
def test_multiply_by_one(self):
|
||||
pj = PointJacobi.from_affine(generator_256)
|
||||
pw = generator_256 * 1
|
||||
|
||||
pj = pj * 1
|
||||
|
||||
self.assertEqual(pj.x(), pw.x())
|
||||
self.assertEqual(pj.y(), pw.y())
|
||||
|
||||
def test_multiply_by_two(self):
|
||||
pj = PointJacobi.from_affine(generator_256)
|
||||
pw = generator_256 * 2
|
||||
|
||||
pj = pj * 2
|
||||
|
||||
self.assertEqual(pj.x(), pw.x())
|
||||
self.assertEqual(pj.y(), pw.y())
|
||||
|
||||
def test_rmul_by_two(self):
|
||||
pj = PointJacobi.from_affine(generator_256)
|
||||
pw = generator_256 * 2
|
||||
|
||||
pj = 2 * pj
|
||||
|
||||
self.assertEqual(pj, pw)
|
||||
|
||||
def test_compare_non_zero_with_infinity(self):
|
||||
pj = PointJacobi.from_affine(generator_256)
|
||||
|
||||
self.assertNotEqual(pj, INFINITY)
|
||||
|
||||
def test_compare_zero_point_with_infinity(self):
|
||||
pj = PointJacobi(curve_256, 0, 0, 1)
|
||||
|
||||
self.assertEqual(pj, INFINITY)
|
||||
|
||||
def test_compare_double_with_multiply(self):
|
||||
pj = PointJacobi.from_affine(generator_256)
|
||||
dbl = pj.double()
|
||||
mlpl = pj * 2
|
||||
|
||||
self.assertEqual(dbl, mlpl)
|
||||
|
||||
@settings(**SLOW_SETTINGS)
|
||||
@given(
|
||||
st.integers(
|
||||
min_value=0, max_value=int(generator_brainpoolp160r1.order() - 1)
|
||||
)
|
||||
)
|
||||
def test_multiplications(self, mul):
|
||||
pj = PointJacobi.from_affine(generator_brainpoolp160r1)
|
||||
pw = pj.to_affine() * mul
|
||||
|
||||
pj = pj * mul
|
||||
|
||||
self.assertEqual((pj.x(), pj.y()), (pw.x(), pw.y()))
|
||||
self.assertEqual(pj, pw)
|
||||
|
||||
@settings(**SLOW_SETTINGS)
|
||||
@given(
|
||||
st.integers(
|
||||
min_value=0, max_value=int(generator_brainpoolp160r1.order() - 1)
|
||||
)
|
||||
)
|
||||
@example(0)
|
||||
@example(int(generator_brainpoolp160r1.order()))
|
||||
def test_precompute(self, mul):
|
||||
precomp = generator_brainpoolp160r1
|
||||
self.assertTrue(precomp._PointJacobi__precompute)
|
||||
pj = PointJacobi.from_affine(generator_brainpoolp160r1)
|
||||
|
||||
a = precomp * mul
|
||||
b = pj * mul
|
||||
|
||||
self.assertEqual(a, b)
|
||||
|
||||
@settings(**SLOW_SETTINGS)
|
||||
@given(
|
||||
st.integers(
|
||||
min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1)
|
||||
),
|
||||
st.integers(
|
||||
min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1)
|
||||
),
|
||||
)
|
||||
@example(3, 3)
|
||||
def test_add_scaled_points(self, a_mul, b_mul):
|
||||
j_g = PointJacobi.from_affine(generator_brainpoolp160r1)
|
||||
a = PointJacobi.from_affine(j_g * a_mul)
|
||||
b = PointJacobi.from_affine(j_g * b_mul)
|
||||
|
||||
c = a + b
|
||||
|
||||
self.assertEqual(c, j_g * (a_mul + b_mul))
|
||||
|
||||
@settings(**SLOW_SETTINGS)
|
||||
@given(
|
||||
st.integers(
|
||||
min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1)
|
||||
),
|
||||
st.integers(
|
||||
min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1)
|
||||
),
|
||||
st.integers(min_value=1, max_value=int(curve_brainpoolp160r1.p() - 1)),
|
||||
)
|
||||
def test_add_one_scaled_point(self, a_mul, b_mul, new_z):
|
||||
j_g = PointJacobi.from_affine(generator_brainpoolp160r1)
|
||||
a = PointJacobi.from_affine(j_g * a_mul)
|
||||
b = PointJacobi.from_affine(j_g * b_mul)
|
||||
|
||||
p = curve_brainpoolp160r1.p()
|
||||
|
||||
assume(inverse_mod(new_z, p))
|
||||
|
||||
new_zz = new_z * new_z % p
|
||||
|
||||
b = PointJacobi(
|
||||
curve_brainpoolp160r1,
|
||||
b.x() * new_zz % p,
|
||||
b.y() * new_zz * new_z % p,
|
||||
new_z,
|
||||
)
|
||||
|
||||
c = a + b
|
||||
|
||||
self.assertEqual(c, j_g * (a_mul + b_mul))
|
||||
|
||||
@pytest.mark.slow
|
||||
@settings(**SLOW_SETTINGS)
|
||||
@given(
|
||||
st.integers(
|
||||
min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1)
|
||||
),
|
||||
st.integers(
|
||||
min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1)
|
||||
),
|
||||
st.integers(min_value=1, max_value=int(curve_brainpoolp160r1.p() - 1)),
|
||||
)
|
||||
@example(1, 1, 1)
|
||||
@example(3, 3, 3)
|
||||
@example(2, int(generator_brainpoolp160r1.order() - 2), 1)
|
||||
@example(2, int(generator_brainpoolp160r1.order() - 2), 3)
|
||||
def test_add_same_scale_points(self, a_mul, b_mul, new_z):
|
||||
j_g = PointJacobi.from_affine(generator_brainpoolp160r1)
|
||||
a = PointJacobi.from_affine(j_g * a_mul)
|
||||
b = PointJacobi.from_affine(j_g * b_mul)
|
||||
|
||||
p = curve_brainpoolp160r1.p()
|
||||
|
||||
assume(inverse_mod(new_z, p))
|
||||
|
||||
new_zz = new_z * new_z % p
|
||||
|
||||
a = PointJacobi(
|
||||
curve_brainpoolp160r1,
|
||||
a.x() * new_zz % p,
|
||||
a.y() * new_zz * new_z % p,
|
||||
new_z,
|
||||
)
|
||||
b = PointJacobi(
|
||||
curve_brainpoolp160r1,
|
||||
b.x() * new_zz % p,
|
||||
b.y() * new_zz * new_z % p,
|
||||
new_z,
|
||||
)
|
||||
|
||||
c = a + b
|
||||
|
||||
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)
|
||||
|
||||
@pytest.mark.slow
|
||||
@settings(**SLOW_SETTINGS)
|
||||
@given(
|
||||
st.integers(
|
||||
min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1)
|
||||
),
|
||||
st.integers(
|
||||
min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1)
|
||||
),
|
||||
st.lists(
|
||||
st.integers(
|
||||
min_value=1, max_value=int(curve_brainpoolp160r1.p() - 1)
|
||||
),
|
||||
min_size=2,
|
||||
max_size=2,
|
||||
unique=True,
|
||||
),
|
||||
)
|
||||
@example(2, 2, [2, 1])
|
||||
@example(2, 2, [2, 3])
|
||||
@example(2, int(generator_brainpoolp160r1.order() - 2), [2, 3])
|
||||
@example(2, int(generator_brainpoolp160r1.order() - 2), [2, 1])
|
||||
def test_add_different_scale_points(self, a_mul, b_mul, new_z):
|
||||
j_g = PointJacobi.from_affine(generator_brainpoolp160r1)
|
||||
a = PointJacobi.from_affine(j_g * a_mul)
|
||||
b = PointJacobi.from_affine(j_g * b_mul)
|
||||
|
||||
p = curve_brainpoolp160r1.p()
|
||||
|
||||
assume(inverse_mod(new_z[0], p))
|
||||
assume(inverse_mod(new_z[1], p))
|
||||
|
||||
new_zz0 = new_z[0] * new_z[0] % p
|
||||
new_zz1 = new_z[1] * new_z[1] % p
|
||||
|
||||
a = PointJacobi(
|
||||
curve_brainpoolp160r1,
|
||||
a.x() * new_zz0 % p,
|
||||
a.y() * new_zz0 * new_z[0] % p,
|
||||
new_z[0],
|
||||
)
|
||||
b = PointJacobi(
|
||||
curve_brainpoolp160r1,
|
||||
b.x() * new_zz1 % p,
|
||||
b.y() * new_zz1 * new_z[1] % p,
|
||||
new_z[1],
|
||||
)
|
||||
|
||||
c = a + b
|
||||
|
||||
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_different_points_same_scale_static(self):
|
||||
j_g = generator_brainpoolp160r1
|
||||
p = curve_brainpoolp160r1.p()
|
||||
a = j_g * 11
|
||||
a.scale()
|
||||
b = j_g * 12
|
||||
z = 13
|
||||
x = PointJacobi(
|
||||
curve_brainpoolp160r1,
|
||||
a.x() * z**2 % p,
|
||||
a.y() * z**3 % p,
|
||||
z,
|
||||
)
|
||||
y = PointJacobi(
|
||||
curve_brainpoolp160r1,
|
||||
b.x() * z**2 % p,
|
||||
b.y() * z**3 % p,
|
||||
z,
|
||||
)
|
||||
|
||||
c = a + b
|
||||
|
||||
self.assertEqual(c, x + y)
|
||||
|
||||
def test_add_same_point_different_scale_second_z_1_static(self):
|
||||
j_g = generator_112r2
|
||||
p = curve_112r2.p()
|
||||
z = 11
|
||||
a = j_g * z
|
||||
a.scale()
|
||||
|
||||
x = PointJacobi(
|
||||
curve_112r2,
|
||||
a.x() * z**2 % p,
|
||||
a.y() * z**3 % p,
|
||||
z,
|
||||
)
|
||||
y = PointJacobi(
|
||||
curve_112r2,
|
||||
a.x(),
|
||||
a.y(),
|
||||
1,
|
||||
)
|
||||
|
||||
c = a + a
|
||||
|
||||
self.assertEqual(c, x + y)
|
||||
|
||||
def test_add_to_infinity_static(self):
|
||||
j_g = generator_112r2
|
||||
|
||||
z = 11
|
||||
a = j_g * z
|
||||
a.scale()
|
||||
|
||||
b = -a
|
||||
|
||||
x = PointJacobi(
|
||||
curve_112r2,
|
||||
a.x(),
|
||||
a.y(),
|
||||
1,
|
||||
)
|
||||
y = PointJacobi(
|
||||
curve_112r2,
|
||||
b.x(),
|
||||
b.y(),
|
||||
1,
|
||||
)
|
||||
|
||||
self.assertEqual(INFINITY, 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)
|
||||
|
||||
self.assertEqual(j_g, j_g.mul_add(1, INFINITY, 1))
|
||||
|
||||
def test_mul_add_same(self):
|
||||
j_g = PointJacobi.from_affine(generator_256)
|
||||
|
||||
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_brainpoolp160r1, True)
|
||||
b = PointJacobi.from_affine(j_g * 255, True)
|
||||
|
||||
self.assertEqual(j_g * 256, j_g + b)
|
||||
self.assertEqual(j_g * (5 + 255 * 7), j_g * 5 + b * 7)
|
||||
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_brainpoolp160r1, True)
|
||||
b = PointJacobi.from_affine(j_g * 255, True)
|
||||
|
||||
self.assertEqual(j_g * 256, j_g + b)
|
||||
self.assertEqual(
|
||||
j_g * (0xFF00 + 255 * 0xF0F0), j_g * 0xFF00 + b * 0xF0F0
|
||||
)
|
||||
self.assertEqual(
|
||||
j_g * (0xFF00 + 255 * 0xF0F0), j_g.mul_add(0xFF00, b, 0xF0F0)
|
||||
)
|
||||
|
||||
def test_mul_add_to_mul(self):
|
||||
j_g = PointJacobi.from_affine(generator_256)
|
||||
|
||||
a = j_g * 3
|
||||
b = j_g.mul_add(2, j_g, 1)
|
||||
|
||||
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)
|
||||
|
||||
w_a = generator_256 * 255
|
||||
w_b = generator_256 * (0xA8 * 0xF0)
|
||||
j_b = j_g * 0xA8
|
||||
|
||||
ret = j_g.mul_add(255, j_b, 0xF0)
|
||||
|
||||
self.assertEqual(ret.to_affine(), w_a + w_b)
|
||||
|
||||
def test_mul_add_large(self):
|
||||
j_g = PointJacobi.from_affine(generator_256)
|
||||
b = PointJacobi.from_affine(j_g * 255)
|
||||
|
||||
self.assertEqual(j_g * 256, j_g + b)
|
||||
self.assertEqual(
|
||||
j_g * (0xFF00 + 255 * 0xF0F0), j_g * 0xFF00 + b * 0xF0F0
|
||||
)
|
||||
self.assertEqual(
|
||||
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_add_with_point_at_infinity(self):
|
||||
pj1 = PointJacobi(curve=CurveFp(23, 1, 1, 1), x=2, y=3, z=1, order=1)
|
||||
x, y, z = pj1._add(2, 3, 1, 5, 5, 0, 23)
|
||||
|
||||
self.assertEqual((x, y, z), (2, 3, 1))
|
||||
|
||||
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)
|
||||
|
||||
@pytest.mark.slow
|
||||
@settings(**NO_OLD_SETTINGS)
|
||||
@pytest.mark.skipif(
|
||||
platform.python_implementation() == "PyPy",
|
||||
reason="threading on PyPy breaks coverage",
|
||||
)
|
||||
@given(st.integers(min_value=1, max_value=10))
|
||||
def test_multithreading(self, thread_num): # pragma: no cover
|
||||
# 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.slow
|
||||
@pytest.mark.skipif(
|
||||
platform.system() == "Windows"
|
||||
or platform.python_implementation() == "PyPy",
|
||||
reason="there are no signals on Windows, and threading breaks coverage"
|
||||
" on PyPy",
|
||||
)
|
||||
def test_multithreading_with_interrupts(self): # pragma: no cover
|
||||
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,
|
||||
)
|
1138
venv/Lib/site-packages/ecdsa/test_keys.py
Normal file
1138
venv/Lib/site-packages/ecdsa/test_keys.py
Normal file
File diff suppressed because it is too large
Load Diff
378
venv/Lib/site-packages/ecdsa/test_malformed_sigs.py
Normal file
378
venv/Lib/site-packages/ecdsa/test_malformed_sigs.py
Normal file
@@ -0,0 +1,378 @@
|
||||
from __future__ import with_statement, division
|
||||
|
||||
import hashlib
|
||||
|
||||
try:
|
||||
from hashlib import algorithms_available
|
||||
except ImportError: # pragma: no cover
|
||||
algorithms_available = [
|
||||
"md5",
|
||||
"sha1",
|
||||
"sha224",
|
||||
"sha256",
|
||||
"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
|
||||
import hypothesis.strategies as st
|
||||
from hypothesis import note, assume, given, settings, example
|
||||
|
||||
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, SECP112r2, SECP128r1
|
||||
from .der import (
|
||||
encode_integer,
|
||||
encode_bitstring,
|
||||
encode_octet_string,
|
||||
encode_oid,
|
||||
encode_sequence,
|
||||
encode_constructed,
|
||||
)
|
||||
from .ellipticcurve import CurveEdTw
|
||||
|
||||
|
||||
example_data = b"some data to sign"
|
||||
"""Since the data is hashed for processing, really any string will do."""
|
||||
|
||||
|
||||
hash_and_size = [
|
||||
(name, hashlib.new(name).digest_size) for name in algorithms_available
|
||||
]
|
||||
"""Pairs of hash names and their output sizes.
|
||||
Needed for pairing with curves as we don't support hashes
|
||||
bigger than order sizes of curves."""
|
||||
|
||||
|
||||
if "--fast" in sys.argv: # pragma: no cover
|
||||
curves = [SECP112r2, SECP128r1]
|
||||
|
||||
|
||||
keys_and_sigs = []
|
||||
"""Name of the curve+hash combination, VerifyingKey and DER signature."""
|
||||
|
||||
|
||||
# for hypothesis strategy shrinking we want smallest curves and hashes first
|
||||
for curve in sorted(curves, key=lambda x: x.baselen):
|
||||
for hash_alg in [
|
||||
name
|
||||
for name, size in sorted(hash_and_size, key=lambda x: x[1])
|
||||
if 0 < size <= curve.baselen
|
||||
]:
|
||||
sk = SigningKey.generate(
|
||||
curve, hashfunc=partial(hashlib.new, hash_alg)
|
||||
)
|
||||
|
||||
keys_and_sigs.append(
|
||||
(
|
||||
"{0} {1}".format(curve, hash_alg),
|
||||
sk.verifying_key,
|
||||
sk.sign(example_data, sigencode=sigencode_der),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# first make sure that the signatures can be verified
|
||||
@pytest.mark.parametrize(
|
||||
"verifying_key,signature",
|
||||
[pytest.param(vk, sig, id=name) for name, vk, sig in keys_and_sigs],
|
||||
)
|
||||
def test_signatures(verifying_key, signature):
|
||||
assert verifying_key.verify(
|
||||
signature, example_data, sigdecode=sigdecode_der
|
||||
)
|
||||
|
||||
|
||||
@st.composite
|
||||
def st_fuzzed_sig(draw, keys_and_sigs): # pragma: no cover
|
||||
"""
|
||||
Hypothesis strategy that generates pairs of VerifyingKey and malformed
|
||||
signatures created by fuzzing of a valid signature.
|
||||
"""
|
||||
name, verifying_key, old_sig = draw(st.sampled_from(keys_and_sigs))
|
||||
note("Configuration: {0}".format(name))
|
||||
|
||||
sig = bytearray(old_sig)
|
||||
|
||||
# decide which bytes should be removed
|
||||
to_remove = draw(
|
||||
st.lists(st.integers(min_value=0, max_value=len(sig) - 1), unique=True)
|
||||
)
|
||||
to_remove.sort()
|
||||
for i in reversed(to_remove):
|
||||
del sig[i]
|
||||
note("Remove bytes: {0}".format(to_remove))
|
||||
|
||||
# decide which bytes of the original signature should be changed
|
||||
xors = None
|
||||
if sig: # pragma: no branch
|
||||
xors = draw(
|
||||
st.dictionaries(
|
||||
st.integers(min_value=0, max_value=len(sig) - 1),
|
||||
st.integers(min_value=1, max_value=255),
|
||||
)
|
||||
)
|
||||
for i, val in xors.items():
|
||||
sig[i] ^= val
|
||||
note("xors: {0}".format(xors))
|
||||
|
||||
# decide where new data should be inserted
|
||||
insert_pos = draw(st.integers(min_value=0, max_value=len(sig)))
|
||||
# NIST521p signature is about 140 bytes long, test slightly longer
|
||||
insert_data = draw(st.binary(max_size=256))
|
||||
|
||||
sig = sig[:insert_pos] + insert_data + sig[insert_pos:]
|
||||
note(
|
||||
"Inserted at position {0} bytes: {1!r}".format(insert_pos, insert_data)
|
||||
)
|
||||
|
||||
sig = bytes(sig)
|
||||
# make sure that there was performed at least one mutation on the data
|
||||
assume(to_remove or xors or insert_data)
|
||||
# and that the mutations didn't cancel each-other out
|
||||
assume(sig != old_sig)
|
||||
|
||||
return verifying_key, sig
|
||||
|
||||
|
||||
params = {}
|
||||
# not supported in hypothesis 2.0.0
|
||||
if sys.version_info >= (2, 7): # pragma: no branch
|
||||
from hypothesis import HealthCheck
|
||||
|
||||
# deadline=5s because NIST521p are slow to verify
|
||||
params["deadline"] = 5000
|
||||
params["suppress_health_check"] = [
|
||||
HealthCheck.data_too_large,
|
||||
HealthCheck.filter_too_much,
|
||||
HealthCheck.too_slow,
|
||||
]
|
||||
if "--fast" in sys.argv: # pragma: no cover
|
||||
params["max_examples"] = 20
|
||||
|
||||
slow_params = dict(params)
|
||||
if "--fast" in sys.argv: # pragma: no cover
|
||||
slow_params["max_examples"] = 1
|
||||
else:
|
||||
slow_params["max_examples"] = 10
|
||||
|
||||
|
||||
@settings(**slow_params)
|
||||
@given(st_fuzzed_sig(keys_and_sigs))
|
||||
def test_fuzzed_der_signatures(args):
|
||||
verifying_key, sig = args
|
||||
|
||||
with pytest.raises(BadSignatureError):
|
||||
verifying_key.verify(sig, example_data, sigdecode=sigdecode_der)
|
||||
|
||||
|
||||
@st.composite
|
||||
def st_random_der_ecdsa_sig_value(draw): # pragma: no cover
|
||||
"""
|
||||
Hypothesis strategy for selecting random values and encoding them
|
||||
to ECDSA-Sig-Value object::
|
||||
|
||||
ECDSA-Sig-Value ::= SEQUENCE {
|
||||
r INTEGER,
|
||||
s INTEGER
|
||||
}
|
||||
"""
|
||||
name, verifying_key, _ = draw(st.sampled_from(keys_and_sigs))
|
||||
note("Configuration: {0}".format(name))
|
||||
order = int(verifying_key.curve.order)
|
||||
|
||||
# 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)
|
||||
# way smaller and slightly bigger
|
||||
r = draw(
|
||||
st.integers(min_value=0, max_value=order << 4)
|
||||
| st.integers(min_value=order >> 2, max_value=order + 1)
|
||||
)
|
||||
s = draw(
|
||||
st.integers(min_value=0, max_value=order << 4)
|
||||
| st.integers(min_value=order >> 2, max_value=order + 1)
|
||||
)
|
||||
|
||||
sig = encode_sequence(encode_integer(r), encode_integer(s))
|
||||
|
||||
return verifying_key, sig
|
||||
|
||||
|
||||
@settings(**slow_params)
|
||||
@given(st_random_der_ecdsa_sig_value())
|
||||
def test_random_der_ecdsa_sig_value(params):
|
||||
"""
|
||||
Check if random values encoded in ECDSA-Sig-Value structure are rejected
|
||||
as signature.
|
||||
"""
|
||||
verifying_key, sig = params
|
||||
|
||||
with pytest.raises(BadSignatureError):
|
||||
verifying_key.verify(sig, example_data, sigdecode=sigdecode_der)
|
||||
|
||||
|
||||
def st_der_integer(*args, **kwargs): # pragma: no cover
|
||||
"""
|
||||
Hypothesis strategy that returns a random positive integer as DER
|
||||
INTEGER.
|
||||
Parameters are passed to hypothesis.strategy.integer.
|
||||
"""
|
||||
if "min_value" not in kwargs: # pragma: no branch
|
||||
kwargs["min_value"] = 0
|
||||
return st.builds(encode_integer, st.integers(*args, **kwargs))
|
||||
|
||||
|
||||
@st.composite
|
||||
def st_der_bit_string(draw, *args, **kwargs): # pragma: no cover
|
||||
"""
|
||||
Hypothesis strategy that returns a random DER BIT STRING.
|
||||
Parameters are passed to hypothesis.strategy.binary.
|
||||
"""
|
||||
data = draw(st.binary(*args, **kwargs))
|
||||
if data:
|
||||
unused = draw(st.integers(min_value=0, max_value=7))
|
||||
data = bytearray(data)
|
||||
data[-1] &= -(2**unused)
|
||||
data = bytes(data)
|
||||
else:
|
||||
unused = 0
|
||||
return encode_bitstring(data, unused)
|
||||
|
||||
|
||||
def st_der_octet_string(*args, **kwargs): # pragma: no cover
|
||||
"""
|
||||
Hypothesis strategy that returns a random DER OCTET STRING object.
|
||||
Parameters are passed to hypothesis.strategy.binary
|
||||
"""
|
||||
return st.builds(encode_octet_string, st.binary(*args, **kwargs))
|
||||
|
||||
|
||||
def st_der_null(): # pragma: no cover
|
||||
"""
|
||||
Hypothesis strategy that returns DER NULL object.
|
||||
"""
|
||||
return st.just(b"\x05\x00")
|
||||
|
||||
|
||||
@st.composite
|
||||
def st_der_oid(draw): # pragma: no cover
|
||||
"""
|
||||
Hypothesis strategy that returns DER OBJECT IDENTIFIER objects.
|
||||
"""
|
||||
first = draw(st.integers(min_value=0, max_value=2))
|
||||
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))
|
||||
rest = draw(
|
||||
st.lists(st.integers(min_value=0, max_value=2**512), max_size=50)
|
||||
)
|
||||
return encode_oid(first, second, *rest)
|
||||
|
||||
|
||||
def st_der(): # pragma: no cover
|
||||
"""
|
||||
Hypothesis strategy that returns random DER structures.
|
||||
|
||||
A valid DER structure is any primitive object, an octet encoding
|
||||
of a valid DER structure, sequence of valid DER objects or a constructed
|
||||
encoding of any of the above.
|
||||
"""
|
||||
return st.recursive( # pragma: no branch
|
||||
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_null()
|
||||
| st_der_oid(),
|
||||
lambda children: st.builds(encode_octet_string, st.one_of(children))
|
||||
| st.builds(lambda x: encode_bitstring(x, 0), st.one_of(children))
|
||||
| st.builds(
|
||||
lambda x: encode_sequence(*x), st.lists(children, max_size=200)
|
||||
)
|
||||
| st.builds(
|
||||
encode_constructed,
|
||||
st.integers(min_value=0, max_value=0x3F),
|
||||
st.one_of(children),
|
||||
),
|
||||
max_leaves=40,
|
||||
)
|
||||
|
||||
|
||||
@settings(**slow_params)
|
||||
@given(st.sampled_from(keys_and_sigs), st_der())
|
||||
def test_random_der_as_signature(params, der):
|
||||
"""Check if random DER structures are rejected as signature"""
|
||||
name, verifying_key, _ = params
|
||||
|
||||
with pytest.raises(BadSignatureError):
|
||||
verifying_key.verify(der, example_data, sigdecode=sigdecode_der)
|
||||
|
||||
|
||||
@settings(**slow_params)
|
||||
@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))
|
||||
)
|
||||
@example(
|
||||
keys_and_sigs[0],
|
||||
encode_sequence(encode_integer(1), encode_integer(1)) + b"\x00",
|
||||
)
|
||||
@example(keys_and_sigs[0], encode_sequence(*[encode_integer(1)] * 3))
|
||||
def test_random_bytes_as_signature(params, der):
|
||||
"""Check if random bytes are rejected as signature"""
|
||||
name, verifying_key, _ = params
|
||||
|
||||
with pytest.raises(BadSignatureError):
|
||||
verifying_key.verify(der, example_data, sigdecode=sigdecode_der)
|
||||
|
||||
|
||||
keys_and_string_sigs = [
|
||||
(
|
||||
name,
|
||||
verifying_key,
|
||||
sigencode_string(
|
||||
*sigdecode_der(sig, verifying_key.curve.order),
|
||||
order=verifying_key.curve.order
|
||||
),
|
||||
)
|
||||
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
|
||||
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(**slow_params)
|
||||
@given(st_fuzzed_sig(keys_and_string_sigs))
|
||||
def test_fuzzed_string_signatures(params):
|
||||
verifying_key, sig = params
|
||||
|
||||
with pytest.raises(BadSignatureError):
|
||||
verifying_key.verify(sig, example_data, sigdecode=sigdecode_string)
|
483
venv/Lib/site-packages/ecdsa/test_numbertheory.py
Normal file
483
venv/Lib/site-packages/ecdsa/test_numbertheory.py
Normal file
@@ -0,0 +1,483 @@
|
||||
import operator
|
||||
from functools import reduce
|
||||
import sys
|
||||
|
||||
try:
|
||||
import unittest2 as unittest
|
||||
except ImportError:
|
||||
import unittest
|
||||
import hypothesis.strategies as st
|
||||
import pytest
|
||||
from hypothesis import given, settings, example
|
||||
|
||||
try:
|
||||
from hypothesis import HealthCheck
|
||||
|
||||
HC_PRESENT = True
|
||||
except ImportError: # pragma: no cover
|
||||
HC_PRESENT = False
|
||||
from .numbertheory import (
|
||||
SquareRootError,
|
||||
JacobiError,
|
||||
factorization,
|
||||
gcd,
|
||||
lcm,
|
||||
jacobi,
|
||||
inverse_mod,
|
||||
is_prime,
|
||||
next_prime,
|
||||
smallprimes,
|
||||
square_root_mod_prime,
|
||||
)
|
||||
|
||||
try:
|
||||
from gmpy2 import mpz
|
||||
except ImportError:
|
||||
try:
|
||||
from gmpy import mpz
|
||||
except ImportError:
|
||||
|
||||
def mpz(x):
|
||||
return x
|
||||
|
||||
|
||||
BIGPRIMES = (
|
||||
999671,
|
||||
999683,
|
||||
999721,
|
||||
999727,
|
||||
999749,
|
||||
999763,
|
||||
999769,
|
||||
999773,
|
||||
999809,
|
||||
999853,
|
||||
999863,
|
||||
999883,
|
||||
999907,
|
||||
999917,
|
||||
999931,
|
||||
999953,
|
||||
999959,
|
||||
999961,
|
||||
999979,
|
||||
999983,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"prime, next_p", [(p, q) for p, q in zip(BIGPRIMES[:-1], BIGPRIMES[1:])]
|
||||
)
|
||||
def test_next_prime(prime, next_p):
|
||||
assert next_prime(prime) == next_p
|
||||
|
||||
|
||||
@pytest.mark.parametrize("val", [-1, 0, 1])
|
||||
def test_next_prime_with_nums_less_2(val):
|
||||
assert next_prime(val) == 2
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
@pytest.mark.parametrize("prime", smallprimes)
|
||||
def test_square_root_mod_prime_for_small_primes(prime):
|
||||
squares = set()
|
||||
for num in range(0, 1 + prime // 2):
|
||||
sq = num * num % prime
|
||||
squares.add(sq)
|
||||
root = square_root_mod_prime(sq, prime)
|
||||
# tested for real with TestNumbertheory.test_square_root_mod_prime
|
||||
assert root * root % prime == sq
|
||||
|
||||
for nonsquare in range(0, prime):
|
||||
if nonsquare in squares:
|
||||
continue
|
||||
with pytest.raises(SquareRootError):
|
||||
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))
|
||||
num = draw(
|
||||
st.integers(min_value=1, max_value=mod - 1).filter(
|
||||
lambda x: gcd(x, mod) == 1
|
||||
)
|
||||
)
|
||||
return num, mod
|
||||
|
||||
|
||||
@st.composite
|
||||
def st_primes(draw, *args, **kwargs):
|
||||
if "min_value" not in kwargs: # pragma: no branch
|
||||
kwargs["min_value"] = 1
|
||||
prime = draw(
|
||||
st.sampled_from(smallprimes)
|
||||
| st.integers(*args, **kwargs).filter(is_prime)
|
||||
)
|
||||
return prime
|
||||
|
||||
|
||||
@st.composite
|
||||
def st_num_square_prime(draw):
|
||||
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
|
||||
|
||||
|
||||
@st.composite
|
||||
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)
|
||||
)
|
||||
# select random prime(s) that will make the common factor of composites
|
||||
com_fac_primes = draw(
|
||||
st.lists(st.sampled_from(primes), min_size=1, max_size=20)
|
||||
)
|
||||
com_fac = reduce(operator.mul, com_fac_primes, 1)
|
||||
|
||||
# select at most 20 lists (returned numbers),
|
||||
# each having at most 30 primes (factors) including none (then the number
|
||||
# will be 1)
|
||||
comp_primes = draw( # pragma: no branch
|
||||
st.integers(min_value=1, max_value=20).flatmap(
|
||||
lambda n: st.lists(
|
||||
st.lists(st.sampled_from(primes), max_size=30),
|
||||
min_size=1,
|
||||
max_size=n,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
return [reduce(operator.mul, nums, 1) * com_fac for nums in comp_primes]
|
||||
|
||||
|
||||
@st.composite
|
||||
def st_comp_no_com_fac(draw):
|
||||
"""
|
||||
Strategy that returns lists of numbers that don't have a common factor.
|
||||
"""
|
||||
primes = draw(
|
||||
st.lists(
|
||||
st_primes(max_value=2**512), min_size=2, max_size=10, unique=True
|
||||
)
|
||||
)
|
||||
# first select the primes that will create the uncommon factor
|
||||
# between returned numbers
|
||||
uncom_fac_primes = draw(
|
||||
st.lists(
|
||||
st.sampled_from(primes),
|
||||
min_size=1,
|
||||
max_size=len(primes) - 1,
|
||||
unique=True,
|
||||
)
|
||||
)
|
||||
uncom_fac = reduce(operator.mul, uncom_fac_primes, 1)
|
||||
|
||||
# then build composites from leftover primes
|
||||
leftover_primes = [i for i in primes if i not in uncom_fac_primes]
|
||||
|
||||
assert leftover_primes
|
||||
assert uncom_fac_primes
|
||||
|
||||
# select at most 20 lists, each having at most 30 primes
|
||||
# selected from the leftover_primes list
|
||||
number_primes = draw( # pragma: no branch
|
||||
st.integers(min_value=1, max_value=20).flatmap(
|
||||
lambda n: st.lists(
|
||||
st.lists(st.sampled_from(leftover_primes), max_size=30),
|
||||
min_size=1,
|
||||
max_size=n,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
numbers = [reduce(operator.mul, nums, 1) for nums in number_primes]
|
||||
|
||||
insert_at = draw(st.integers(min_value=0, max_value=len(numbers)))
|
||||
numbers.insert(insert_at, uncom_fac)
|
||||
return numbers
|
||||
|
||||
|
||||
HYP_SETTINGS = {}
|
||||
if HC_PRESENT: # pragma: no branch
|
||||
HYP_SETTINGS["suppress_health_check"] = [
|
||||
HealthCheck.filter_too_much,
|
||||
HealthCheck.too_slow,
|
||||
]
|
||||
# the factorization() sometimes takes a long time to finish
|
||||
HYP_SETTINGS["deadline"] = 5000
|
||||
|
||||
if "--fast" in sys.argv: # pragma: no cover
|
||||
HYP_SETTINGS["max_examples"] = 20
|
||||
|
||||
|
||||
HYP_SLOW_SETTINGS = dict(HYP_SETTINGS)
|
||||
if "--fast" in sys.argv: # pragma: no cover
|
||||
HYP_SLOW_SETTINGS["max_examples"] = 1
|
||||
else:
|
||||
HYP_SLOW_SETTINGS["max_examples"] = 20
|
||||
|
||||
|
||||
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(mpz(2) ** 2048 + 0x3D5)
|
||||
|
||||
def test_pseudoprime_base_19(self):
|
||||
assert not is_prime(1543267864443420616877677640751301)
|
||||
|
||||
def test_pseudoprime_base_300(self):
|
||||
# F. Arnault "Constructing Carmichael Numbers Which Are Strong
|
||||
# Pseudoprimes to Several Bases". Journal of Symbolic
|
||||
# Computation. 20 (2): 151-161. doi:10.1006/jsco.1995.1042.
|
||||
# Section 4.4 Large Example (a pseudoprime to all bases up to
|
||||
# 300)
|
||||
p = int(
|
||||
"29 674 495 668 685 510 550 154 174 642 905 332 730 "
|
||||
"771 991 799 853 043 350 995 075 531 276 838 753 171 "
|
||||
"770 199 594 238 596 428 121 188 033 664 754 218 345 "
|
||||
"562 493 168 782 883".replace(" ", "")
|
||||
)
|
||||
|
||||
assert is_prime(p)
|
||||
for _ in range(10):
|
||||
if not is_prime(p * (313 * (p - 1) + 1) * (353 * (p - 1) + 1)):
|
||||
break
|
||||
else:
|
||||
assert False, "composite not detected"
|
||||
|
||||
|
||||
class TestNumbertheory(unittest.TestCase):
|
||||
def test_gcd(self):
|
||||
assert gcd(3 * 5 * 7, 3 * 5 * 11, 3 * 5 * 13) == 3 * 5
|
||||
assert gcd([3 * 5 * 7, 3 * 5 * 11, 3 * 5 * 13]) == 3 * 5
|
||||
assert gcd(3) == 3
|
||||
|
||||
@unittest.skipUnless(
|
||||
HC_PRESENT,
|
||||
"Hypothesis 2.0.0 can't be made tolerant of hard to "
|
||||
"meet requirements (like `is_prime()`), the test "
|
||||
"case times-out on it",
|
||||
)
|
||||
@settings(**HYP_SLOW_SETTINGS)
|
||||
@example([877 * 1151, 877 * 1009])
|
||||
@given(st_comp_with_com_fac())
|
||||
def test_gcd_with_com_factor(self, numbers):
|
||||
n = gcd(numbers)
|
||||
assert 1 in numbers or n != 1
|
||||
for i in numbers:
|
||||
assert i % n == 0
|
||||
|
||||
@unittest.skipUnless(
|
||||
HC_PRESENT,
|
||||
"Hypothesis 2.0.0 can't be made tolerant of hard to "
|
||||
"meet requirements (like `is_prime()`), the test "
|
||||
"case times-out on it",
|
||||
)
|
||||
@settings(**HYP_SLOW_SETTINGS)
|
||||
@example([1151, 1069, 1009])
|
||||
@given(st_comp_no_com_fac())
|
||||
def test_gcd_with_uncom_factor(self, numbers):
|
||||
n = gcd(numbers)
|
||||
assert n == 1
|
||||
|
||||
@settings(**HYP_SLOW_SETTINGS)
|
||||
@given(
|
||||
st.lists(
|
||||
st.integers(min_value=1, max_value=2**8192),
|
||||
min_size=1,
|
||||
max_size=20,
|
||||
)
|
||||
)
|
||||
def test_gcd_with_random_numbers(self, numbers):
|
||||
n = gcd(numbers)
|
||||
for i in numbers:
|
||||
# check that at least it's a divider
|
||||
assert i % n == 0
|
||||
|
||||
def test_lcm(self):
|
||||
assert lcm(3, 5 * 3, 7 * 3) == 3 * 5 * 7
|
||||
assert lcm([3, 5 * 3, 7 * 3]) == 3 * 5 * 7
|
||||
assert lcm(3) == 3
|
||||
|
||||
@settings(**HYP_SLOW_SETTINGS)
|
||||
@given(
|
||||
st.lists(
|
||||
st.integers(min_value=1, max_value=2**8192),
|
||||
min_size=1,
|
||||
max_size=20,
|
||||
)
|
||||
)
|
||||
def test_lcm_with_random_numbers(self, numbers):
|
||||
n = lcm(numbers)
|
||||
for i in numbers:
|
||||
assert n % i == 0
|
||||
|
||||
@unittest.skipUnless(
|
||||
HC_PRESENT,
|
||||
"Hypothesis 2.0.0 can't be made tolerant of hard to "
|
||||
"meet requirements (like `is_prime()`), the test "
|
||||
"case times-out on it",
|
||||
)
|
||||
@settings(**HYP_SLOW_SETTINGS)
|
||||
@given(st_num_square_prime())
|
||||
def test_square_root_mod_prime(self, vals):
|
||||
square, prime = vals
|
||||
|
||||
calc = square_root_mod_prime(square, prime)
|
||||
assert calc * calc % prime == square
|
||||
|
||||
@pytest.mark.slow
|
||||
@settings(**HYP_SLOW_SETTINGS)
|
||||
@given(st.integers(min_value=1, max_value=10**12))
|
||||
@example(265399 * 1526929)
|
||||
@example(373297**2 * 553991)
|
||||
def test_factorization(self, num):
|
||||
factors = factorization(num)
|
||||
mult = 1
|
||||
for i in factors:
|
||||
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_SLOW_SETTINGS)
|
||||
@given(st.integers(min_value=3, max_value=1000).filter(lambda x: x % 2))
|
||||
def test_jacobi(self, mod):
|
||||
mod = mpz(mod)
|
||||
if is_prime(mod):
|
||||
squares = set()
|
||||
for root in range(1, mod):
|
||||
root = mpz(root)
|
||||
assert jacobi(root * root, mod) == 1
|
||||
squares.add(root * root % mod)
|
||||
for i in range(1, mod):
|
||||
if i not in squares:
|
||||
i = mpz(i)
|
||||
assert jacobi(i, mod) == -1
|
||||
else:
|
||||
factors = factorization(mod)
|
||||
for a in range(1, mod):
|
||||
c = 1
|
||||
for i in factors:
|
||||
c *= jacobi(a, i[0]) ** i[1]
|
||||
assert c == jacobi(a, mod)
|
||||
|
||||
@settings(**HYP_SLOW_SETTINGS)
|
||||
@given(st_two_nums_rel_prime())
|
||||
def test_inverse_mod(self, nums):
|
||||
num, mod = nums
|
||||
|
||||
inv = inverse_mod(num, mod)
|
||||
|
||||
assert 0 < inv < mod
|
||||
assert num * inv % mod == 1
|
||||
|
||||
def test_inverse_mod_with_zero(self):
|
||||
assert 0 == inverse_mod(0, 11)
|
2522
venv/Lib/site-packages/ecdsa/test_pyecdsa.py
Normal file
2522
venv/Lib/site-packages/ecdsa/test_pyecdsa.py
Normal file
File diff suppressed because it is too large
Load Diff
180
venv/Lib/site-packages/ecdsa/test_rw_lock.py
Normal file
180
venv/Lib/site-packages/ecdsa/test_rw_lock.py
Normal file
@@ -0,0 +1,180 @@
|
||||
# Copyright Mateusz Kobos, (c) 2011
|
||||
# https://code.activestate.com/recipes/577803-reader-writer-lock-with-priority-for-writers/
|
||||
# released under the MIT licence
|
||||
|
||||
try:
|
||||
import unittest2 as unittest
|
||||
except ImportError:
|
||||
import unittest
|
||||
import threading
|
||||
import time
|
||||
import copy
|
||||
from ._rwlock import RWLock
|
||||
|
||||
|
||||
class Writer(threading.Thread):
|
||||
def __init__(
|
||||
self, buffer_, rw_lock, init_sleep_time, sleep_time, to_write
|
||||
):
|
||||
"""
|
||||
@param buffer_: common buffer_ shared by the readers and writers
|
||||
@type buffer_: list
|
||||
@type rw_lock: L{RWLock}
|
||||
@param init_sleep_time: sleep time before doing any action
|
||||
@type init_sleep_time: C{float}
|
||||
@param sleep_time: sleep time while in critical section
|
||||
@type sleep_time: C{float}
|
||||
@param to_write: data that will be appended to the buffer
|
||||
"""
|
||||
threading.Thread.__init__(self)
|
||||
self.__buffer = buffer_
|
||||
self.__rw_lock = rw_lock
|
||||
self.__init_sleep_time = init_sleep_time
|
||||
self.__sleep_time = sleep_time
|
||||
self.__to_write = to_write
|
||||
self.entry_time = None
|
||||
"""Time of entry to the critical section"""
|
||||
self.exit_time = None
|
||||
"""Time of exit from the critical section"""
|
||||
|
||||
def run(self):
|
||||
time.sleep(self.__init_sleep_time)
|
||||
self.__rw_lock.writer_acquire()
|
||||
self.entry_time = time.time()
|
||||
time.sleep(self.__sleep_time)
|
||||
self.__buffer.append(self.__to_write)
|
||||
self.exit_time = time.time()
|
||||
self.__rw_lock.writer_release()
|
||||
|
||||
|
||||
class Reader(threading.Thread):
|
||||
def __init__(self, buffer_, rw_lock, init_sleep_time, sleep_time):
|
||||
"""
|
||||
@param buffer_: common buffer shared by the readers and writers
|
||||
@type buffer_: list
|
||||
@type rw_lock: L{RWLock}
|
||||
@param init_sleep_time: sleep time before doing any action
|
||||
@type init_sleep_time: C{float}
|
||||
@param sleep_time: sleep time while in critical section
|
||||
@type sleep_time: C{float}
|
||||
"""
|
||||
threading.Thread.__init__(self)
|
||||
self.__buffer = buffer_
|
||||
self.__rw_lock = rw_lock
|
||||
self.__init_sleep_time = init_sleep_time
|
||||
self.__sleep_time = sleep_time
|
||||
self.buffer_read = None
|
||||
"""a copy of a the buffer read while in critical section"""
|
||||
self.entry_time = None
|
||||
"""Time of entry to the critical section"""
|
||||
self.exit_time = None
|
||||
"""Time of exit from the critical section"""
|
||||
|
||||
def run(self):
|
||||
time.sleep(self.__init_sleep_time)
|
||||
self.__rw_lock.reader_acquire()
|
||||
self.entry_time = time.time()
|
||||
time.sleep(self.__sleep_time)
|
||||
self.buffer_read = copy.deepcopy(self.__buffer)
|
||||
self.exit_time = time.time()
|
||||
self.__rw_lock.reader_release()
|
||||
|
||||
|
||||
class RWLockTestCase(unittest.TestCase):
|
||||
def test_readers_nonexclusive_access(self):
|
||||
(buffer_, rw_lock, threads) = self.__init_variables()
|
||||
|
||||
threads.append(Reader(buffer_, rw_lock, 0, 0))
|
||||
threads.append(Writer(buffer_, rw_lock, 0.2, 0.4, 1))
|
||||
threads.append(Reader(buffer_, rw_lock, 0.3, 0.3))
|
||||
threads.append(Reader(buffer_, rw_lock, 0.5, 0))
|
||||
|
||||
self.__start_and_join_threads(threads)
|
||||
|
||||
## The third reader should enter after the second one but it should
|
||||
## exit before the second one exits
|
||||
## (i.e. the readers should be in the critical section
|
||||
## at the same time)
|
||||
|
||||
self.assertEqual([], threads[0].buffer_read)
|
||||
self.assertEqual([1], threads[2].buffer_read)
|
||||
self.assertEqual([1], threads[3].buffer_read)
|
||||
self.assertTrue(threads[1].exit_time <= threads[2].entry_time)
|
||||
self.assertTrue(threads[2].entry_time <= threads[3].entry_time)
|
||||
self.assertTrue(threads[3].exit_time < threads[2].exit_time)
|
||||
|
||||
def test_writers_exclusive_access(self):
|
||||
(buffer_, rw_lock, threads) = self.__init_variables()
|
||||
|
||||
threads.append(Writer(buffer_, rw_lock, 0, 0.4, 1))
|
||||
threads.append(Writer(buffer_, rw_lock, 0.1, 0, 2))
|
||||
threads.append(Reader(buffer_, rw_lock, 0.2, 0))
|
||||
|
||||
self.__start_and_join_threads(threads)
|
||||
|
||||
## The second writer should wait for the first one to exit
|
||||
|
||||
self.assertEqual([1, 2], threads[2].buffer_read)
|
||||
self.assertTrue(threads[0].exit_time <= threads[1].entry_time)
|
||||
self.assertTrue(threads[1].exit_time <= threads[2].exit_time)
|
||||
|
||||
def test_writer_priority(self):
|
||||
(buffer_, rw_lock, threads) = self.__init_variables()
|
||||
|
||||
threads.append(Writer(buffer_, rw_lock, 0, 0, 1))
|
||||
threads.append(Reader(buffer_, rw_lock, 0.1, 0.4))
|
||||
threads.append(Writer(buffer_, rw_lock, 0.2, 0, 2))
|
||||
threads.append(Reader(buffer_, rw_lock, 0.3, 0))
|
||||
threads.append(Reader(buffer_, rw_lock, 0.3, 0))
|
||||
|
||||
self.__start_and_join_threads(threads)
|
||||
|
||||
## The second writer should go before the second and the third reader
|
||||
|
||||
self.assertEqual([1], threads[1].buffer_read)
|
||||
self.assertEqual([1, 2], threads[3].buffer_read)
|
||||
self.assertEqual([1, 2], threads[4].buffer_read)
|
||||
self.assertTrue(threads[0].exit_time < threads[1].entry_time)
|
||||
self.assertTrue(threads[1].exit_time <= threads[2].entry_time)
|
||||
self.assertTrue(threads[2].exit_time <= threads[3].entry_time)
|
||||
self.assertTrue(threads[2].exit_time <= threads[4].entry_time)
|
||||
|
||||
def test_many_writers_priority(self):
|
||||
(buffer_, rw_lock, threads) = self.__init_variables()
|
||||
|
||||
threads.append(Writer(buffer_, rw_lock, 0, 0, 1))
|
||||
threads.append(Reader(buffer_, rw_lock, 0.1, 0.6))
|
||||
threads.append(Writer(buffer_, rw_lock, 0.2, 0.1, 2))
|
||||
threads.append(Reader(buffer_, rw_lock, 0.3, 0))
|
||||
threads.append(Reader(buffer_, rw_lock, 0.4, 0))
|
||||
threads.append(Writer(buffer_, rw_lock, 0.5, 0.1, 3))
|
||||
|
||||
self.__start_and_join_threads(threads)
|
||||
|
||||
## The two last writers should go first -- after the first reader and
|
||||
## before the second and the third reader
|
||||
|
||||
self.assertEqual([1], threads[1].buffer_read)
|
||||
self.assertEqual([1, 2, 3], threads[3].buffer_read)
|
||||
self.assertEqual([1, 2, 3], threads[4].buffer_read)
|
||||
self.assertTrue(threads[0].exit_time < threads[1].entry_time)
|
||||
self.assertTrue(threads[1].exit_time <= threads[2].entry_time)
|
||||
self.assertTrue(threads[1].exit_time <= threads[5].entry_time)
|
||||
self.assertTrue(threads[2].exit_time <= threads[3].entry_time)
|
||||
self.assertTrue(threads[2].exit_time <= threads[4].entry_time)
|
||||
self.assertTrue(threads[5].exit_time <= threads[3].entry_time)
|
||||
self.assertTrue(threads[5].exit_time <= threads[4].entry_time)
|
||||
|
||||
@staticmethod
|
||||
def __init_variables():
|
||||
buffer_ = []
|
||||
rw_lock = RWLock()
|
||||
threads = []
|
||||
return (buffer_, rw_lock, threads)
|
||||
|
||||
@staticmethod
|
||||
def __start_and_join_threads(threads):
|
||||
for t in threads:
|
||||
t.start()
|
||||
for t in threads:
|
||||
t.join()
|
111
venv/Lib/site-packages/ecdsa/test_sha3.py
Normal file
111
venv/Lib/site-packages/ecdsa/test_sha3.py
Normal file
@@ -0,0 +1,111 @@
|
||||
try:
|
||||
import unittest2 as unittest
|
||||
except ImportError:
|
||||
import unittest
|
||||
import pytest
|
||||
|
||||
try:
|
||||
from gmpy2 import mpz
|
||||
|
||||
GMPY = True
|
||||
except ImportError: # pragma: no cover
|
||||
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),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("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")
|
||||
|
||||
|
||||
@pytest.mark.skipif(GMPY == False, reason="requires 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"),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("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",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("msg,olen,ohash", SHAKE_256_VECTORS)
|
||||
def test_shake_256(msg, olen, ohash):
|
||||
out = shake_256(msg, olen)
|
||||
assert out == bytearray(ohash)
|
519
venv/Lib/site-packages/ecdsa/util.py
Normal file
519
venv/Lib/site-packages/ecdsa/util.py
Normal file
@@ -0,0 +1,519 @@
|
||||
"""
|
||||
This module includes some utility functions.
|
||||
|
||||
The methods most typically used are the sigencode and sigdecode functions
|
||||
to be used with :func:`~ecdsa.keys.SigningKey.sign` and
|
||||
:func:`~ecdsa.keys.VerifyingKey.verify`
|
||||
respectively. See the :func:`sigencode_strings`, :func:`sigdecode_string`,
|
||||
:func:`sigencode_der`, :func:`sigencode_strings_canonize`,
|
||||
:func:`sigencode_string_canonize`, :func:`sigencode_der_canonize`,
|
||||
:func:`sigdecode_strings`, :func:`sigdecode_string`, and
|
||||
:func:`sigdecode_der` functions.
|
||||
"""
|
||||
|
||||
from __future__ import division
|
||||
|
||||
import os
|
||||
import math
|
||||
import binascii
|
||||
import sys
|
||||
from hashlib import sha256
|
||||
from six import PY2, int2byte, next
|
||||
from . import der
|
||||
from ._compat import normalise_bytes
|
||||
|
||||
|
||||
# RFC5480:
|
||||
# The "unrestricted" algorithm identifier is:
|
||||
# id-ecPublicKey OBJECT IDENTIFIER ::= {
|
||||
# iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 }
|
||||
|
||||
oid_ecPublicKey = (1, 2, 840, 10045, 2, 1)
|
||||
encoded_oid_ecPublicKey = der.encode_oid(*oid_ecPublicKey)
|
||||
|
||||
# RFC5480:
|
||||
# The ECDH algorithm uses the following object identifier:
|
||||
# id-ecDH OBJECT IDENTIFIER ::= {
|
||||
# iso(1) identified-organization(3) certicom(132) schemes(1)
|
||||
# ecdh(12) }
|
||||
|
||||
oid_ecDH = (1, 3, 132, 1, 12)
|
||||
|
||||
# RFC5480:
|
||||
# The ECMQV algorithm uses the following object identifier:
|
||||
# id-ecMQV OBJECT IDENTIFIER ::= {
|
||||
# iso(1) identified-organization(3) certicom(132) schemes(1)
|
||||
# ecmqv(13) }
|
||||
|
||||
oid_ecMQV = (1, 3, 132, 1, 13)
|
||||
|
||||
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):
|
||||
"""Convert a bytestring to string of 0's and 1's"""
|
||||
return "".join(bin(ord(x))[2:].zfill(8) for x in ent_256)
|
||||
|
||||
|
||||
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):
|
||||
return x.bit_length() or 1
|
||||
|
||||
|
||||
def orderlen(order):
|
||||
return (1 + len("%x" % order)) // 2 # bytes
|
||||
|
||||
|
||||
def randrange(order, entropy=None):
|
||||
"""Return a random integer k such that 1 <= k < order, uniformly
|
||||
distributed across that range. Worst case should be a mean of 2 loops at
|
||||
(2**k)+2.
|
||||
|
||||
Note that this function is not declared to be forwards-compatible: we may
|
||||
change the behavior in future releases. The entropy= argument (which
|
||||
should get a callable that behaves like os.urandom) can be used to
|
||||
achieve stability within a given release (for repeatable unit tests), but
|
||||
should not be used as a long-term-compatible key generation algorithm.
|
||||
"""
|
||||
assert order > 1
|
||||
if entropy is None:
|
||||
entropy = os.urandom
|
||||
upper_2 = bit_length(order - 2)
|
||||
upper_256 = upper_2 // 8 + 1
|
||||
while True: # I don't think this needs a counter with bit-wise randrange
|
||||
ent_256 = entropy(upper_256)
|
||||
ent_2 = entropy_to_bits(ent_256)
|
||||
rand_num = int(ent_2[:upper_2], base=2) + 1
|
||||
if 0 < rand_num < order:
|
||||
return rand_num
|
||||
|
||||
|
||||
class PRNG:
|
||||
# this returns a callable which, when invoked with an integer N, will
|
||||
# return N pseudorandom bytes. Note: this is a short-term PRNG, meant
|
||||
# primarily for the needs of randrange_from_seed__trytryagain(), which
|
||||
# only needs to run it a few times per seed. It does not provide
|
||||
# protection against state compromise (forward security).
|
||||
def __init__(self, seed):
|
||||
self.generator = self.block_generator(seed)
|
||||
|
||||
def __call__(self, numbytes):
|
||||
a = [next(self.generator) for i in range(numbytes)]
|
||||
|
||||
if PY2: # pragma: no branch
|
||||
return "".join(a)
|
||||
else:
|
||||
return bytes(a)
|
||||
|
||||
def block_generator(self, seed):
|
||||
counter = 0
|
||||
while True:
|
||||
for byte in sha256(
|
||||
("prng-%d-%s" % (counter, seed)).encode()
|
||||
).digest():
|
||||
yield byte
|
||||
counter += 1
|
||||
|
||||
|
||||
def randrange_from_seed__overshoot_modulo(seed, order):
|
||||
# hash the data, then turn the digest into a number in [1,order).
|
||||
#
|
||||
# We use David-Sarah Hopwood's suggestion: turn it into a number that's
|
||||
# sufficiently larger than the group order, then modulo it down to fit.
|
||||
# This should give adequate (but not perfect) uniformity, and simple
|
||||
# code. There are other choices: try-try-again is the main one.
|
||||
base = PRNG(seed)(2 * orderlen(order))
|
||||
number = (int(binascii.hexlify(base), 16) % (order - 1)) + 1
|
||||
assert 1 <= number < order, (1, number, order)
|
||||
return number
|
||||
|
||||
|
||||
def lsb_of_ones(numbits):
|
||||
return (1 << numbits) - 1
|
||||
|
||||
|
||||
def bits_and_bytes(order):
|
||||
bits = int(math.log(order - 1, 2) + 1)
|
||||
bytes = bits // 8
|
||||
extrabits = bits % 8
|
||||
return bits, bytes, extrabits
|
||||
|
||||
|
||||
# the following randrange_from_seed__METHOD() functions take an
|
||||
# arbitrarily-sized secret seed and turn it into a number that obeys the same
|
||||
# range limits as randrange() above. They are meant for deriving consistent
|
||||
# signing keys from a secret rather than generating them randomly, for
|
||||
# example a protocol in which three signing keys are derived from a master
|
||||
# secret. You should use a uniformly-distributed unguessable seed with about
|
||||
# curve.baselen bytes of entropy. To use one, do this:
|
||||
# seed = os.urandom(curve.baselen) # or other starting point
|
||||
# secexp = ecdsa.util.randrange_from_seed__trytryagain(sed, curve.order)
|
||||
# sk = SigningKey.from_secret_exponent(secexp, curve)
|
||||
|
||||
|
||||
def randrange_from_seed__truncate_bytes(seed, order, hashmod=sha256):
|
||||
# hash the seed, then turn the digest into a number in [1,order), but
|
||||
# don't worry about trying to uniformly fill the range. This will lose,
|
||||
# on average, four bits of entropy.
|
||||
bits, _bytes, extrabits = bits_and_bytes(order)
|
||||
if extrabits:
|
||||
_bytes += 1
|
||||
base = hashmod(seed).digest()[:_bytes]
|
||||
base = "\x00" * (_bytes - len(base)) + base
|
||||
number = 1 + int(binascii.hexlify(base), 16)
|
||||
assert 1 <= number < order
|
||||
return number
|
||||
|
||||
|
||||
def randrange_from_seed__truncate_bits(seed, order, hashmod=sha256):
|
||||
# like string_to_randrange_truncate_bytes, but only lose an average of
|
||||
# half a bit
|
||||
bits = int(math.log(order - 1, 2) + 1)
|
||||
maxbytes = (bits + 7) // 8
|
||||
base = hashmod(seed).digest()[:maxbytes]
|
||||
base = "\x00" * (maxbytes - len(base)) + base
|
||||
topbits = 8 * maxbytes - bits
|
||||
if topbits:
|
||||
base = int2byte(ord(base[0]) & lsb_of_ones(topbits)) + base[1:]
|
||||
number = 1 + int(binascii.hexlify(base), 16)
|
||||
assert 1 <= number < order
|
||||
return number
|
||||
|
||||
|
||||
def randrange_from_seed__trytryagain(seed, order):
|
||||
# figure out exactly how many bits we need (rounded up to the nearest
|
||||
# bit), so we can reduce the chance of looping to less than 0.5 . This is
|
||||
# specified to feed from a byte-oriented PRNG, and discards the
|
||||
# high-order bits of the first byte as necessary to get the right number
|
||||
# of bits. The average number of loops will range from 1.0 (when
|
||||
# order=2**k-1) to 2.0 (when order=2**k+1).
|
||||
assert order > 1
|
||||
bits, bytes, extrabits = bits_and_bytes(order)
|
||||
generate = PRNG(seed)
|
||||
while True:
|
||||
extrabyte = b""
|
||||
if extrabits:
|
||||
extrabyte = int2byte(ord(generate(1)) & lsb_of_ones(extrabits))
|
||||
guess = string_to_number(extrabyte + generate(bytes)) + 1
|
||||
if 1 <= guess < order:
|
||||
return guess
|
||||
|
||||
|
||||
def number_to_string(num, order):
|
||||
l = orderlen(order)
|
||||
fmt_str = "%0" + str(2 * l) + "x"
|
||||
string = binascii.unhexlify((fmt_str % num).encode())
|
||||
assert len(string) == l, (len(string), l)
|
||||
return string
|
||||
|
||||
|
||||
def number_to_string_crop(num, order):
|
||||
l = orderlen(order)
|
||||
fmt_str = "%0" + str(2 * l) + "x"
|
||||
string = binascii.unhexlify((fmt_str % num).encode())
|
||||
return string[:l]
|
||||
|
||||
|
||||
def string_to_number(string):
|
||||
return int(binascii.hexlify(string), 16)
|
||||
|
||||
|
||||
def string_to_number_fixedlen(string, order):
|
||||
l = orderlen(order)
|
||||
assert len(string) == l, (len(string), l)
|
||||
return int(binascii.hexlify(string), 16)
|
||||
|
||||
|
||||
def sigencode_strings(r, s, order):
|
||||
"""
|
||||
Encode the signature to a pair of strings in a tuple
|
||||
|
||||
Encodes signature into raw encoding (:term:`raw encoding`) with the
|
||||
``r`` and ``s`` parts of the signature encoded separately.
|
||||
|
||||
It's expected that this function will be used as a ``sigencode=`` parameter
|
||||
in :func:`ecdsa.keys.SigningKey.sign` method.
|
||||
|
||||
:param int r: first parameter of the signature
|
||||
:param int s: second parameter of the signature
|
||||
:param int order: the order of the curve over which the signature was
|
||||
computed
|
||||
|
||||
:return: raw encoding of ECDSA signature
|
||||
:rtype: tuple(bytes, bytes)
|
||||
"""
|
||||
r_str = number_to_string(r, order)
|
||||
s_str = number_to_string(s, order)
|
||||
return (r_str, s_str)
|
||||
|
||||
|
||||
def sigencode_string(r, s, order):
|
||||
"""
|
||||
Encode the signature to raw format (:term:`raw encoding`)
|
||||
|
||||
It's expected that this function will be used as a ``sigencode=`` parameter
|
||||
in :func:`ecdsa.keys.SigningKey.sign` method.
|
||||
|
||||
:param int r: first parameter of the signature
|
||||
:param int s: second parameter of the signature
|
||||
:param int order: the order of the curve over which the signature was
|
||||
computed
|
||||
|
||||
:return: raw encoding of ECDSA signature
|
||||
:rtype: bytes
|
||||
"""
|
||||
# for any given curve, the size of the signature numbers is
|
||||
# fixed, so just use simple concatenation
|
||||
r_str, s_str = sigencode_strings(r, s, order)
|
||||
return r_str + s_str
|
||||
|
||||
|
||||
def sigencode_der(r, s, order):
|
||||
"""
|
||||
Encode the signature into the ECDSA-Sig-Value structure using :term:`DER`.
|
||||
|
||||
Encodes the signature to the following :term:`ASN.1` structure::
|
||||
|
||||
Ecdsa-Sig-Value ::= SEQUENCE {
|
||||
r INTEGER,
|
||||
s INTEGER
|
||||
}
|
||||
|
||||
It's expected that this function will be used as a ``sigencode=`` parameter
|
||||
in :func:`ecdsa.keys.SigningKey.sign` method.
|
||||
|
||||
:param int r: first parameter of the signature
|
||||
:param int s: second parameter of the signature
|
||||
:param int order: the order of the curve over which the signature was
|
||||
computed
|
||||
|
||||
:return: DER encoding of ECDSA signature
|
||||
:rtype: bytes
|
||||
"""
|
||||
return der.encode_sequence(der.encode_integer(r), der.encode_integer(s))
|
||||
|
||||
|
||||
def sigencode_strings_canonize(r, s, order):
|
||||
"""
|
||||
Encode the signature to a pair of strings in a tuple
|
||||
|
||||
Encodes signature into raw encoding (:term:`raw encoding`) with the
|
||||
``r`` and ``s`` parts of the signature encoded separately.
|
||||
|
||||
Makes sure that the signature is encoded in the canonical format, where
|
||||
the ``s`` parameter is always smaller than ``order / 2``.
|
||||
Most commonly used in bitcoin.
|
||||
|
||||
It's expected that this function will be used as a ``sigencode=`` parameter
|
||||
in :func:`ecdsa.keys.SigningKey.sign` method.
|
||||
|
||||
:param int r: first parameter of the signature
|
||||
:param int s: second parameter of the signature
|
||||
:param int order: the order of the curve over which the signature was
|
||||
computed
|
||||
|
||||
:return: raw encoding of ECDSA signature
|
||||
:rtype: tuple(bytes, bytes)
|
||||
"""
|
||||
if s > order / 2:
|
||||
s = order - s
|
||||
return sigencode_strings(r, s, order)
|
||||
|
||||
|
||||
def sigencode_string_canonize(r, s, order):
|
||||
"""
|
||||
Encode the signature to raw format (:term:`raw encoding`)
|
||||
|
||||
Makes sure that the signature is encoded in the canonical format, where
|
||||
the ``s`` parameter is always smaller than ``order / 2``.
|
||||
Most commonly used in bitcoin.
|
||||
|
||||
It's expected that this function will be used as a ``sigencode=`` parameter
|
||||
in :func:`ecdsa.keys.SigningKey.sign` method.
|
||||
|
||||
:param int r: first parameter of the signature
|
||||
:param int s: second parameter of the signature
|
||||
:param int order: the order of the curve over which the signature was
|
||||
computed
|
||||
|
||||
:return: raw encoding of ECDSA signature
|
||||
:rtype: bytes
|
||||
"""
|
||||
if s > order / 2:
|
||||
s = order - s
|
||||
return sigencode_string(r, s, order)
|
||||
|
||||
|
||||
def sigencode_der_canonize(r, s, order):
|
||||
"""
|
||||
Encode the signature into the ECDSA-Sig-Value structure using :term:`DER`.
|
||||
|
||||
Makes sure that the signature is encoded in the canonical format, where
|
||||
the ``s`` parameter is always smaller than ``order / 2``.
|
||||
Most commonly used in bitcoin.
|
||||
|
||||
Encodes the signature to the following :term:`ASN.1` structure::
|
||||
|
||||
Ecdsa-Sig-Value ::= SEQUENCE {
|
||||
r INTEGER,
|
||||
s INTEGER
|
||||
}
|
||||
|
||||
It's expected that this function will be used as a ``sigencode=`` parameter
|
||||
in :func:`ecdsa.keys.SigningKey.sign` method.
|
||||
|
||||
:param int r: first parameter of the signature
|
||||
:param int s: second parameter of the signature
|
||||
:param int order: the order of the curve over which the signature was
|
||||
computed
|
||||
|
||||
:return: DER encoding of ECDSA signature
|
||||
:rtype: bytes
|
||||
"""
|
||||
if s > order / 2:
|
||||
s = order - s
|
||||
return sigencode_der(r, s, order)
|
||||
|
||||
|
||||
class MalformedSignature(Exception):
|
||||
"""
|
||||
Raised by decoding functions when the signature is malformed.
|
||||
|
||||
Malformed in this context means that the relevant strings or integers
|
||||
do not match what a signature over provided curve would create. Either
|
||||
because the byte strings have incorrect lengths or because the encoded
|
||||
values are too large.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def sigdecode_string(signature, order):
|
||||
"""
|
||||
Decoder for :term:`raw encoding` of ECDSA signatures.
|
||||
|
||||
raw encoding is a simple concatenation of the two integers that comprise
|
||||
the signature, with each encoded using the same amount of bytes depending
|
||||
on curve size/order.
|
||||
|
||||
It's expected that this function will be used as the ``sigdecode=``
|
||||
parameter to the :func:`ecdsa.keys.VerifyingKey.verify` method.
|
||||
|
||||
:param signature: encoded signature
|
||||
:type signature: bytes like object
|
||||
:param order: order of the curve over which the signature was computed
|
||||
:type order: int
|
||||
|
||||
:raises MalformedSignature: when the encoding of the signature is invalid
|
||||
|
||||
:return: tuple with decoded ``r`` and ``s`` values of signature
|
||||
:rtype: tuple of ints
|
||||
"""
|
||||
signature = normalise_bytes(signature)
|
||||
l = orderlen(order)
|
||||
if not len(signature) == 2 * l:
|
||||
raise MalformedSignature(
|
||||
"Invalid length of signature, expected {0} bytes long, "
|
||||
"provided string is {1} bytes long".format(2 * l, len(signature))
|
||||
)
|
||||
r = string_to_number_fixedlen(signature[:l], order)
|
||||
s = string_to_number_fixedlen(signature[l:], order)
|
||||
return r, s
|
||||
|
||||
|
||||
def sigdecode_strings(rs_strings, order):
|
||||
"""
|
||||
Decode the signature from two strings.
|
||||
|
||||
First string needs to be a big endian encoding of ``r``, second needs to
|
||||
be a big endian encoding of the ``s`` parameter of an ECDSA signature.
|
||||
|
||||
It's expected that this function will be used as the ``sigdecode=``
|
||||
parameter to the :func:`ecdsa.keys.VerifyingKey.verify` method.
|
||||
|
||||
:param list rs_strings: list of two bytes-like objects, each encoding one
|
||||
parameter of signature
|
||||
:param int order: order of the curve over which the signature was computed
|
||||
|
||||
:raises MalformedSignature: when the encoding of the signature is invalid
|
||||
|
||||
:return: tuple with decoded ``r`` and ``s`` values of signature
|
||||
:rtype: tuple of ints
|
||||
"""
|
||||
if not len(rs_strings) == 2:
|
||||
raise MalformedSignature(
|
||||
"Invalid number of strings provided: {0}, expected 2".format(
|
||||
len(rs_strings)
|
||||
)
|
||||
)
|
||||
(r_str, s_str) = rs_strings
|
||||
r_str = normalise_bytes(r_str)
|
||||
s_str = normalise_bytes(s_str)
|
||||
l = orderlen(order)
|
||||
if not len(r_str) == l:
|
||||
raise MalformedSignature(
|
||||
"Invalid length of first string ('r' parameter), "
|
||||
"expected {0} bytes long, provided string is {1} "
|
||||
"bytes long".format(l, len(r_str))
|
||||
)
|
||||
if not len(s_str) == l:
|
||||
raise MalformedSignature(
|
||||
"Invalid length of second string ('s' parameter), "
|
||||
"expected {0} bytes long, provided string is {1} "
|
||||
"bytes long".format(l, len(s_str))
|
||||
)
|
||||
r = string_to_number_fixedlen(r_str, order)
|
||||
s = string_to_number_fixedlen(s_str, order)
|
||||
return r, s
|
||||
|
||||
|
||||
def sigdecode_der(sig_der, order):
|
||||
"""
|
||||
Decoder for DER format of ECDSA signatures.
|
||||
|
||||
DER format of signature is one that uses the :term:`ASN.1` :term:`DER`
|
||||
rules to encode it as a sequence of two integers::
|
||||
|
||||
Ecdsa-Sig-Value ::= SEQUENCE {
|
||||
r INTEGER,
|
||||
s INTEGER
|
||||
}
|
||||
|
||||
It's expected that this function will be used as as the ``sigdecode=``
|
||||
parameter to the :func:`ecdsa.keys.VerifyingKey.verify` method.
|
||||
|
||||
:param sig_der: encoded signature
|
||||
:type sig_der: bytes like object
|
||||
:param order: order of the curve over which the signature was computed
|
||||
:type order: int
|
||||
|
||||
:raises UnexpectedDER: when the encoding of signature is invalid
|
||||
|
||||
:return: tuple with decoded ``r`` and ``s`` values of signature
|
||||
:rtype: tuple of ints
|
||||
"""
|
||||
sig_der = normalise_bytes(sig_der)
|
||||
# return der.encode_sequence(der.encode_integer(r), der.encode_integer(s))
|
||||
rs_strings, empty = der.remove_sequence(sig_der)
|
||||
if empty != b"":
|
||||
raise der.UnexpectedDER(
|
||||
"trailing junk after DER sig: %s" % binascii.hexlify(empty)
|
||||
)
|
||||
r, rest = der.remove_integer(rs_strings)
|
||||
s, empty = der.remove_integer(rest)
|
||||
if empty != b"":
|
||||
raise der.UnexpectedDER(
|
||||
"trailing junk after DER numbers: %s" % binascii.hexlify(empty)
|
||||
)
|
||||
return r, s
|
Reference in New Issue
Block a user