Skip to content

Commit 9f5252e

Browse files
committed
Adds to DER format of ECDSA signature with extended ECDSA-Sig-Value and ECDSA-Full-R.
1 parent d547d92 commit 9f5252e

File tree

8 files changed

+887
-41
lines changed

8 files changed

+887
-41
lines changed

src/ecdsa/der.py

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,20 @@ def encode_number(n):
151151
return b"".join([int2byte(d) for d in b128_digits])
152152

153153

154+
def encode_boolean(b):
155+
"""
156+
Encodes BOOLEAN acording to ASN.1 DER format.
157+
The ASN.1 BOOLEAN type has two possible values: TRUE and FALSE.
158+
True is encoded as ff", False is encoded as a zero.
159+
160+
:param boolean b: the boolean value to be encoded
161+
:return: a byte string
162+
:rtype: bytes
163+
"""
164+
165+
return b"\x01" + encode_length(1) + (b"\xff" if b else b"\x00")
166+
167+
154168
def is_sequence(string):
155169
return string and string[:1] == b"\x30"
156170

@@ -240,6 +254,45 @@ def remove_octet_string(string):
240254
return body, rest
241255

242256

257+
def remove_boolean(string):
258+
"""
259+
Removes the ASN.1 BOOLEAN type.
260+
For BOOLEAN types, in DER FALSE is always encoded as zero
261+
and TRUE is always encoded as ff.
262+
263+
:param bytes string: the boolean value to be encoded
264+
:return: a boolean value and the rest of the string
265+
:rtype: tuple(boolean, bytes)
266+
"""
267+
if not string:
268+
raise UnexpectedDER("Empty string is an invalid encoding of a boolean")
269+
if string[:1] != b"\x01":
270+
n = str_idx_as_int(string, 0)
271+
raise UnexpectedDER("wanted type 'boolean' (0x01), got 0x%02x" % n)
272+
length, lengthlength = read_length(string[1:])
273+
if not length:
274+
raise UnexpectedDER("Invalid length of bit string, can't be 0")
275+
body = string[1 + lengthlength : 1 + lengthlength + length]
276+
rest = string[1 + lengthlength + length :]
277+
if not body:
278+
raise UnexpectedDER("Empty BOOLEAN value")
279+
if length != 1:
280+
raise UnexpectedDER(
281+
"The contents octets of boolean shall consist of a single octet."
282+
)
283+
if body == b"\x00":
284+
return False, rest
285+
# the workaround due to instrumental, that
286+
# saves the binary data as UTF-8 string
287+
# (0xff is an invalid start byte)
288+
if isinstance(body, text_type):
289+
body = body.encode("utf-8")
290+
num = int(binascii.hexlify(body), 16)
291+
if num == 0xFF:
292+
return True, rest
293+
raise UnexpectedDER("Invalid encoding of BOOLEAN.")
294+
295+
243296
def remove_object(string):
244297
if not string:
245298
raise UnexpectedDER(
@@ -298,8 +351,7 @@ def remove_integer(string):
298351
smsb = str_idx_as_int(numberbytes, 1)
299352
if smsb < 0x80:
300353
raise UnexpectedDER(
301-
"Invalid encoding of integer, unnecessary "
302-
"zero padding bytes"
354+
"Invalid encoding of integer, unnecessary zero padding bytes"
303355
)
304356
return int(binascii.hexlify(numberbytes), 16), rest
305357

@@ -399,8 +451,8 @@ def remove_bitstring(string, expect_unused=_sentry):
399451
raise UnexpectedDER("Empty string does not encode a bitstring")
400452
if expect_unused is _sentry:
401453
warnings.warn(
402-
"Legacy call convention used, expect_unused= needs to be"
403-
" specified",
454+
"Legacy call convention used, "
455+
"expect_unused= needs to be specified",
404456
DeprecationWarning,
405457
)
406458
num = str_idx_as_int(string, 0)

src/ecdsa/ecdsa.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
modified as part of the python-ecdsa package.
6565
"""
6666

67+
import sys
6768
import warnings
6869
from six import int2byte
6970
from . import ellipticcurve
@@ -192,10 +193,22 @@ def verifies(self, hash, signature):
192193
n = G.order()
193194
r = signature.r
194195
s = signature.s
195-
if r < 1 or r > n - 1:
196-
return False
197196
if s < 1 or s > n - 1:
198197
return False
198+
199+
if sys.version_info < (3, 0): # pragma: no branch
200+
# memoryview was introduced in py 2.7
201+
byte_objects = set((bytearray, bytes))
202+
else:
203+
byte_objects = set((bytearray, bytes, memoryview))
204+
if type(r) in byte_objects:
205+
point = ellipticcurve.AbstractPoint.from_bytes(
206+
self.generator.curve(), r
207+
)
208+
r = point[0] % n
209+
210+
if r < 1 or r > n - 1:
211+
return False
199212
c = numbertheory.inverse_mod(s, n)
200213
u1 = (hash * c) % n
201214
u2 = (r * c) % n
@@ -231,7 +244,7 @@ def __ne__(self, other):
231244
"""Return False if the points are identical, True otherwise."""
232245
return not self == other
233246

234-
def sign(self, hash, random_k):
247+
def sign(self, hash, random_k, accelerate=False):
235248
"""Return a signature for the provided hash, using the provided
236249
random nonce. It is absolutely vital that random_k be an unpredictable
237250
number in the range [1, self.public_key.point.order()-1]. If
@@ -267,6 +280,8 @@ def sign(self, hash, random_k):
267280
) % n
268281
if s == 0:
269282
raise RSZeroError("amazingly unlucky random number s")
283+
if accelerate:
284+
return Signature(p1, s)
270285
return Signature(r, s)
271286

272287

src/ecdsa/keys.py

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1318,6 +1318,7 @@ def sign_deterministic(
13181318
hashfunc=None,
13191319
sigencode=sigencode_string,
13201320
extra_entropy=b"",
1321+
accelerate=False,
13211322
):
13221323
"""
13231324
Create signature over data.
@@ -1354,6 +1355,10 @@ def sign_deterministic(
13541355
number generator used in the RFC6979 process. Entirely optional.
13551356
Ignored with EdDSA.
13561357
:type extra_entropy: :term:`bytes-like object`
1358+
:param accelerate: an indicator for ECDSA sign operation to return
1359+
an ECPoint instead of a number of "r" parameter.
1360+
Applicable only for ECDSA key.
1361+
:type accelerate: boolean
13571362
13581363
:return: encoded signature over `data`
13591364
:rtype: bytes or sigencode function dependent type
@@ -1373,6 +1378,7 @@ def sign_deterministic(
13731378
sigencode=sigencode,
13741379
extra_entropy=extra_entropy,
13751380
allow_truncate=True,
1381+
accelerate=accelerate,
13761382
)
13771383

13781384
def sign_digest_deterministic(
@@ -1382,6 +1388,7 @@ def sign_digest_deterministic(
13821388
sigencode=sigencode_string,
13831389
extra_entropy=b"",
13841390
allow_truncate=False,
1391+
accelerate=False,
13851392
):
13861393
"""
13871394
Create signature for digest using the deterministic RFC6979 algorithm.
@@ -1417,6 +1424,10 @@ def sign_digest_deterministic(
14171424
bigger bit-size than the order of the curve, the extra bits (at
14181425
the end of the digest) will be truncated. Use it when signing
14191426
SHA-384 output using NIST256p or in similar situations.
1427+
:param accelerate: an indicator for ECDSA sign operation to return
1428+
an ECPoint instead of a number of "r" parameter.
1429+
Applicable only for ECDSA key.
1430+
:type accelerate: boolean
14201431
14211432
:return: encoded signature for the `digest` hash
14221433
:rtype: bytes or sigencode function dependent type
@@ -1447,6 +1458,7 @@ def simple_r_s(r, s, order):
14471458
sigencode=simple_r_s,
14481459
k=k,
14491460
allow_truncate=allow_truncate,
1461+
accelerate=accelerate,
14501462
)
14511463
break
14521464
except RSZeroError:
@@ -1462,6 +1474,7 @@ def sign(
14621474
sigencode=sigencode_string,
14631475
k=None,
14641476
allow_truncate=True,
1477+
accelerate=False,
14651478
):
14661479
"""
14671480
Create signature over data.
@@ -1525,6 +1538,10 @@ def sign(
15251538
leak the key. Caller should try a better entropy source, retry with
15261539
different ``k``, or use the
15271540
:func:`~SigningKey.sign_deterministic` in such case.
1541+
:param accelerate: an indicator for ECDSA sign operation to return
1542+
an ECPoint instead of a number of "r" parameter.
1543+
Applicable only for ECDSA key.
1544+
:type accelerate: boolean
15281545
15291546
:return: encoded signature of the hash of `data`
15301547
:rtype: bytes or sigencode function dependent type
@@ -1534,7 +1551,9 @@ def sign(
15341551
if isinstance(self.curve.curve, CurveEdTw):
15351552
return self.sign_deterministic(data)
15361553
h = hashfunc(data).digest()
1537-
return self.sign_digest(h, entropy, sigencode, k, allow_truncate)
1554+
return self.sign_digest(
1555+
h, entropy, sigencode, k, allow_truncate, accelerate
1556+
)
15381557

15391558
def sign_digest(
15401559
self,
@@ -1543,6 +1562,7 @@ def sign_digest(
15431562
sigencode=sigencode_string,
15441563
k=None,
15451564
allow_truncate=False,
1565+
accelerate=False,
15461566
):
15471567
"""
15481568
Create signature over digest using the probabilistic ECDSA algorithm.
@@ -1579,6 +1599,10 @@ def sign_digest(
15791599
leak the key. Caller should try a better entropy source, retry with
15801600
different 'k', or use the
15811601
:func:`~SigningKey.sign_digest_deterministic` in such case.
1602+
:param accelerate: an indicator for ECDSA sign operation to return
1603+
an ECPoint instead of a number of "r" parameter.
1604+
Applicable only for ECDSA key.
1605+
:type accelerate: boolean
15821606
15831607
:return: encoded signature for the `digest` hash
15841608
:rtype: bytes or sigencode function dependent type
@@ -1591,10 +1615,10 @@ def sign_digest(
15911615
self.curve,
15921616
allow_truncate,
15931617
)
1594-
r, s = self.sign_number(number, entropy, k)
1618+
r, s = self.sign_number(number, entropy, k, accelerate)
15951619
return sigencode(r, s, self.privkey.order)
15961620

1597-
def sign_number(self, number, entropy=None, k=None):
1621+
def sign_number(self, number, entropy=None, k=None, accelerate=False):
15981622
"""
15991623
Sign an integer directly.
16001624
@@ -1613,6 +1637,10 @@ def sign_number(self, number, entropy=None, k=None):
16131637
leak the key. Caller should try a better entropy source, retry with
16141638
different 'k', or use the
16151639
:func:`~SigningKey.sign_digest_deterministic` in such case.
1640+
:param accelerate: an indicator for ECDSA sign operation to return
1641+
an ECPoint instead of a number of "r" parameter.
1642+
Applicable only for ECDSA key.
1643+
:type accelerate: boolean
16161644
16171645
:return: the "r" and "s" parameters of the signature
16181646
:rtype: tuple of ints
@@ -1627,5 +1655,5 @@ def sign_number(self, number, entropy=None, k=None):
16271655
_k = randrange(order, entropy)
16281656

16291657
assert 1 <= _k < order
1630-
sig = self.privkey.sign(number, _k)
1658+
sig = self.privkey.sign(number, _k, accelerate)
16311659
return sig.r, sig.s

0 commit comments

Comments
 (0)