Skip to content

Commit

Permalink
give the same handling to string encoded signatures as to DER
Browse files Browse the repository at this point in the history
Verify that strings of unexpected lengths are rejected with the
same BadSignatureError exception
  • Loading branch information
tomato42 committed Sep 26, 2019
1 parent 3acd27a commit 932b7e3
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 27 deletions.
4 changes: 2 additions & 2 deletions src/ecdsa/keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from .ecdsa import RSZeroError
from .util import string_to_number, number_to_string, randrange
from .util import sigencode_string, sigdecode_string
from .util import oid_ecPublicKey, encoded_oid_ecPublicKey
from .util import oid_ecPublicKey, encoded_oid_ecPublicKey, MalformedSignature
from six import PY3, b
from hashlib import sha1

Expand Down Expand Up @@ -138,7 +138,7 @@ def verify_digest(self, signature, digest, sigdecode=sigdecode_string):
number = string_to_number(digest)
try:
r, s = sigdecode(signature, self.pubkey.order)
except der.UnexpectedDER as e:
except (der.UnexpectedDER, MalformedSignature) as e:
raise BadSignatureError("Malformed formatting of signature", e)
sig = ecdsa.Signature(r, s)
if self.pubkey.verifies(number, sig):
Expand Down
62 changes: 40 additions & 22 deletions src/ecdsa/test_malformed_sigs.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,16 @@
from __future__ import with_statement, division

import unittest
import os
import time
import shutil
import subprocess
import pytest
from binascii import hexlify, unhexlify
from hashlib import sha1, sha256, sha512
import hashlib

from six import b, print_, binary_type
from six import b
from .keys import SigningKey, VerifyingKey
from .keys import BadSignatureError
from . import util
from .util import sigencode_der, sigencode_strings
from .util import sigdecode_der, sigdecode_strings
from .util import sigencode_der, sigencode_string
from .util import sigdecode_der, sigdecode_string
from .curves import curves, NIST256p, NIST521p
from .ellipticcurve import Point
from . import der
from . import rfc6979
import copy

sigs = []
der_sigs = []
example_data = b("some data to sign")

# Just NIST256p with SHA256 is 560 test cases, all curves with all hashes is
Expand All @@ -37,14 +25,14 @@
sigencode=sigencode_der)
for pos in range(len(signature)):
for xor in (1<<i for i in range(8)):
sigs.append(pytest.param(
der_sigs.append(pytest.param(
key.verifying_key, hash_alg,
signature, pos, xor,
id="{0}-{1}-pos-{2}-xor-{3}".format(
curve.name, hash_alg, pos, xor)))


@pytest.mark.parametrize("verifying_key,hash_alg,signature,pos,xor", sigs)
@pytest.mark.parametrize("verifying_key,hash_alg,signature,pos,xor", der_sigs)
def test_fuzzed_der_signatures(verifying_key, hash_alg, signature, pos, xor):
# check if a malformed DER encoded signature causes the same exception
# to be raised irrespective of the type of error
Expand All @@ -59,9 +47,39 @@ def test_fuzzed_der_signatures(verifying_key, hash_alg, signature, pos, xor):
assert True


def __main__():
unittest.main()
####
#
# For string encoded signatures, only the length of string is important
#
####

str_sigs = []

if __name__ == "__main__":
__main__()
#for curve in curves:
for curve in [NIST256p]:
#for hash_alg in ["md5", "sha1", "sha224", "sha256", "sha384", "sha512"]:
for hash_alg in ["sha256"]:
key = SigningKey.generate(curve)
signature = key.sign(example_data, hashfunc=getattr(hashlib, hash_alg),
sigencode=sigencode_string)
for trunc in range(len(signature)):
str_sigs.append(pytest.param(
key.verifying_key, hash_alg,
signature, trunc,
id="{0}-{1}-trunc-{2}".format(
curve.name, hash_alg, trunc)))


@pytest.mark.parametrize("verifying_key,hash_alg,signature,trunc", str_sigs)
def test_truncated_string_signatures(verifying_key, hash_alg, signature, trunc):
# check if a malformed string encoded signature causes the same exception
# to be raised irrespective of the type of error
sig = bytearray(signature)
sig = sig[:trunc]

try:
verifying_key.verify(sig, example_data, getattr(hashlib, hash_alg),
sigdecode_string)
assert False
except BadSignatureError:
assert True
26 changes: 23 additions & 3 deletions src/ecdsa/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,19 +233,39 @@ def sigencode_der_canonize(r, s, order):
return sigencode_der(r, s, order)


class MalformedSignature(Exception):
pass


def sigdecode_string(signature, order):
l = orderlen(order)
assert len(signature) == 2 * l, (len(signature), 2 * l)
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):
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
l = orderlen(order)
assert len(r_str) == l, (len(r_str), l)
assert len(s_str) == l, (len(s_str), l)
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
Expand Down

0 comments on commit 932b7e3

Please sign in to comment.