Skip to content

Commit

Permalink
Merge pull request #317 from tlsfuzzer/more-docs
Browse files Browse the repository at this point in the history
More tests and docs
  • Loading branch information
tomato42 committed Mar 2, 2023
2 parents 31039b4 + 98312f6 commit 2457dc7
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 21 deletions.
2 changes: 1 addition & 1 deletion src/ecdsa/test_malformed_sigs.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ def st_der():
of a valid DER structure, sequence of valid DER objects or a constructed
encoding of any of the above.
"""
return st.recursive(
return st.recursive( # pragma: no branch
st.just(b"")
| st_der_integer(max_value=2**4096)
| st_der_bit_string(max_size=1024**2)
Expand Down
82 changes: 80 additions & 2 deletions src/ecdsa/test_pyecdsa.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,14 @@
from .keys import SigningKey, VerifyingKey
from .keys import BadSignatureError, MalformedPointError, BadDigestError
from . import util
from .util import sigencode_der, sigencode_strings
from .util import sigdecode_der, sigdecode_strings
from .util import (
sigencode_der,
sigencode_strings,
sigencode_strings_canonize,
sigencode_string_canonize,
sigencode_der_canonize,
)
from .util import sigdecode_der, sigdecode_strings, sigdecode_string
from .util import number_to_string, encoded_oid_ecPublicKey, MalformedSignature
from .curves import Curve, UnknownCurveError
from .curves import (
Expand Down Expand Up @@ -451,6 +457,78 @@ def test_signature_strings(self):
self.assertEqual(type(sig_der), binary_type)
self.assertTrue(pub1.verify(sig_der, data, sigdecode=sigdecode_der))

def test_sigencode_string_canonize_no_change(self):
r = 12
s = 400
order = SECP112r1.order

new_r, new_s = sigdecode_string(
sigencode_string_canonize(r, s, order), order
)

self.assertEqual(r, new_r)
self.assertEqual(s, new_s)

def test_sigencode_string_canonize(self):
r = 12
order = SECP112r1.order
s = order - 10

new_r, new_s = sigdecode_string(
sigencode_string_canonize(r, s, order), order
)

self.assertEqual(r, new_r)
self.assertEqual(order - s, new_s)

def test_sigencode_strings_canonize_no_change(self):
r = 12
s = 400
order = SECP112r1.order

new_r, new_s = sigdecode_strings(
sigencode_strings_canonize(r, s, order), order
)

self.assertEqual(r, new_r)
self.assertEqual(s, new_s)

def test_sigencode_strings_canonize(self):
r = 12
order = SECP112r1.order
s = order - 10

new_r, new_s = sigdecode_strings(
sigencode_strings_canonize(r, s, order), order
)

self.assertEqual(r, new_r)
self.assertEqual(order - s, new_s)

def test_sigencode_der_canonize_no_change(self):
r = 13
s = 200
order = SECP112r1.order

new_r, new_s = sigdecode_der(
sigencode_der_canonize(r, s, order), order
)

self.assertEqual(r, new_r)
self.assertEqual(s, new_s)

def test_sigencode_der_canonize(self):
r = 13
order = SECP112r1.order
s = order - 14

new_r, new_s = sigdecode_der(
sigencode_der_canonize(r, s, order), order
)

self.assertEqual(r, new_r)
self.assertEqual(order - s, new_s)

def test_sig_decode_strings_with_invalid_count(self):
with self.assertRaises(MalformedSignature):
sigdecode_strings([b("one"), b("two"), b("three")], 0xFF)
Expand Down
122 changes: 104 additions & 18 deletions src/ecdsa/util.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
"""
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
Expand All @@ -9,6 +22,7 @@
from . import der
from ._compat import normalise_bytes


# RFC5480:
# The "unrestricted" algorithm identifier is:
# id-ecPublicKey OBJECT IDENTIFIER ::= {
Expand Down Expand Up @@ -221,12 +235,24 @@ def string_to_number_fixedlen(string, order):
return int(binascii.hexlify(string), 16)


# these methods are useful for the sigencode= argument to SK.sign() and the
# sigdecode= argument to VK.verify(), and control how the signature is packed
# or unpacked.
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.
def sigencode_strings(r, s, order):
: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)
Expand All @@ -236,7 +262,7 @@ 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
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
Expand Down Expand Up @@ -264,7 +290,7 @@ def sigencode_der(r, s, order):
s INTEGER
}
It's expected that this function will be used as a `sigencode=` parameter
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
Expand All @@ -278,23 +304,83 @@ def sigencode_der(r, s, order):
return der.encode_sequence(der.encode_integer(r), der.encode_integer(s))


# canonical versions of sigencode methods
# these enforce low S values, by negating the value (modulo the order) if
# above order/2 see CECKey::Sign()
# https://github.com/bitcoin/bitcoin/blob/master/src/key.cpp#L214
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)
Expand All @@ -321,7 +407,7 @@ def sigdecode_string(signature, order):
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=`
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
Expand All @@ -331,7 +417,7 @@ def sigdecode_string(signature, order):
:raises MalformedSignature: when the encoding of the signature is invalid
:return: tuple with decoded 'r' and 's' values of signature
:return: tuple with decoded ``r`` and ``s`` values of signature
:rtype: tuple of ints
"""
signature = normalise_bytes(signature)
Expand All @@ -350,10 +436,10 @@ 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.
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=`
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
Expand All @@ -362,7 +448,7 @@ def sigdecode_strings(rs_strings, order):
:raises MalformedSignature: when the encoding of the signature is invalid
:return: tuple with decoded 'r' and 's' values of signature
:return: tuple with decoded ``r`` and ``s`` values of signature
:rtype: tuple of ints
"""
if not len(rs_strings) == 2:
Expand Down Expand Up @@ -404,7 +490,7 @@ def sigdecode_der(sig_der, order):
s INTEGER
}
It's expected that this function will be used as as the `sigdecode=`
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
Expand All @@ -414,7 +500,7 @@ def sigdecode_der(sig_der, order):
:raises UnexpectedDER: when the encoding of signature is invalid
:return: tuple with decoded 'r' and 's' values of signature
:return: tuple with decoded ``r`` and ``s`` values of signature
:rtype: tuple of ints
"""
sig_der = normalise_bytes(sig_der)
Expand Down

0 comments on commit 2457dc7

Please sign in to comment.