Skip to content

Commit 3427fa2

Browse files
committed
ensure that the encoding is actually the minimal one for length and integer
backport of 2c3db7c
1 parent 563d2ee commit 3427fa2

File tree

3 files changed

+104
-2
lines changed

3 files changed

+104
-2
lines changed

build-requirements-2.6.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
tox
22
coveralls<1.3.0
33
idna<2.8
4+
unittest2

ecdsa/der.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,15 @@ def remove_integer(string):
119119
else ord(numberbytes[0])
120120
if not msb < 0x80:
121121
raise UnexpectedDER("Negative integers are not supported")
122+
# check if the encoding is the minimal one (DER requirement)
123+
if length > 1 and not msb:
124+
# leading zero byte is allowed if the integer would have been
125+
# considered a negative number otherwise
126+
smsb = numberbytes[1] if isinstance(numberbytes[1], integer_types) \
127+
else ord(numberbytes[1])
128+
if smsb < 0x80:
129+
raise UnexpectedDER("Invalid encoding of integer, unnecessary "
130+
"zero padding bytes")
122131
return int(binascii.hexlify(numberbytes), 16), rest
123132

124133
def read_number(string):
@@ -158,9 +167,13 @@ def read_length(string):
158167
# big-endian
159168
llen = num & 0x7f
160169
if not llen:
161-
raise UnexpectedDER("Invalid length encoding, length byte is 0")
170+
raise UnexpectedDER("Invalid length encoding, length of length is 0")
162171
if llen > len(string)-1:
163-
raise UnexpectedDER("ran out of length bytes")
172+
raise UnexpectedDER("Length of length longer than provided buffer")
173+
# verify that the encoding is minimal possible (DER requirement)
174+
msb = string[1] if isinstance(string[1], integer_types) else ord(string[1])
175+
if not msb or llen == 1 and msb < 0x80:
176+
raise UnexpectedDER("Not minimal encoding of length")
164177
return int(binascii.hexlify(string[1:1+llen]), 16), 1+llen
165178

166179
def remove_bitstring(string):

ecdsa/test_der.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
2+
# compatibility with Python 2.6, for that we need unittest2 package,
3+
# which is not available on 3.3 or 3.4
4+
try:
5+
import unittest2 as unittest
6+
except ImportError:
7+
import unittest
8+
from .der import remove_integer, UnexpectedDER, read_length
9+
from six import b
10+
11+
class TestRemoveInteger(unittest.TestCase):
12+
# DER requires the integers to be 0-padded only if they would be
13+
# interpreted as negative, check if those errors are detected
14+
def test_non_minimal_encoding(self):
15+
with self.assertRaises(UnexpectedDER):
16+
remove_integer(b('\x02\x02\x00\x01'))
17+
18+
def test_negative_with_high_bit_set(self):
19+
with self.assertRaises(UnexpectedDER):
20+
remove_integer(b('\x02\x01\x80'))
21+
22+
def test_two_zero_bytes_with_high_bit_set(self):
23+
with self.assertRaises(UnexpectedDER):
24+
remove_integer(b('\x02\x03\x00\x00\xff'))
25+
26+
def test_zero_length_integer(self):
27+
with self.assertRaises(UnexpectedDER):
28+
remove_integer(b('\x02\x00'))
29+
30+
def test_empty_string(self):
31+
with self.assertRaises(UnexpectedDER):
32+
remove_integer(b(''))
33+
34+
def test_encoding_of_zero(self):
35+
val, rem = remove_integer(b('\x02\x01\x00'))
36+
37+
self.assertEqual(val, 0)
38+
self.assertFalse(rem)
39+
40+
def test_encoding_of_127(self):
41+
val, rem = remove_integer(b('\x02\x01\x7f'))
42+
43+
self.assertEqual(val, 127)
44+
self.assertFalse(rem)
45+
46+
def test_encoding_of_128(self):
47+
val, rem = remove_integer(b('\x02\x02\x00\x80'))
48+
49+
self.assertEqual(val, 128)
50+
self.assertFalse(rem)
51+
52+
53+
class TestReadLength(unittest.TestCase):
54+
# DER requires the lengths between 0 and 127 to be encoded using the short
55+
# form and lengths above that encoded with minimal number of bytes
56+
# necessary
57+
def test_zero_length(self):
58+
self.assertEqual((0, 1), read_length(b('\x00')))
59+
60+
def test_two_byte_zero_length(self):
61+
with self.assertRaises(UnexpectedDER):
62+
read_length(b('\x81\x00'))
63+
64+
def test_two_byte_small_length(self):
65+
with self.assertRaises(UnexpectedDER):
66+
read_length(b('\x81\x7f'))
67+
68+
def test_long_form_with_zero_length(self):
69+
with self.assertRaises(UnexpectedDER):
70+
read_length(b('\x80'))
71+
72+
def test_smallest_two_byte_length(self):
73+
self.assertEqual((128, 2), read_length(b('\x81\x80')))
74+
75+
def test_zero_padded_length(self):
76+
with self.assertRaises(UnexpectedDER):
77+
read_length(b('\x82\x00\x80'))
78+
79+
def test_two_three_byte_length(self):
80+
self.assertEqual((256, 3), read_length(b'\x82\x01\x00'))
81+
82+
def test_empty_string(self):
83+
with self.assertRaises(UnexpectedDER):
84+
read_length(b(''))
85+
86+
def test_length_overflow(self):
87+
with self.assertRaises(UnexpectedDER):
88+
read_length(b('\x83\x01\x00'))

0 commit comments

Comments
 (0)