Skip to content

Commit 3538d44

Browse files
authored
Merge pull request #143 from tomato42/bytes-support
Bytes-like object support
2 parents 4c92d31 + cc5d5ae commit 3538d44

File tree

8 files changed

+453
-54
lines changed

8 files changed

+453
-54
lines changed

.travis.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ before_install:
6060
TRAVIS_COMMIT_RANGE=$PR_FIRST^..$TRAVIS_COMMIT
6161
fi
6262
# sanity check current commit
63-
- git rev-parse HEAD
63+
- BRANCH=$(git rev-parse HEAD)
6464
- echo "TRAVIS_COMMIT_RANGE=$TRAVIS_COMMIT_RANGE"
6565
- git fetch origin master:refs/remotes/origin/master
6666

@@ -78,16 +78,16 @@ install:
7878
script:
7979
- if [[ $TOX_ENV ]]; then tox -e $TOX_ENV; fi
8080
- tox -e speed
81-
- cp diff-instrumental.py diff-instrumental-2.py
8281
- |
8382
if [[ $INSTRUMENTAL && $TRAVIS_PULL_REQUEST != "false" ]]; then
8483
git checkout $PR_FIRST^
8584
# exclude the super slow test_malformed_sigs.py, until #127 is merged
8685
files="$(ls src/ecdsa/test*.py | grep -v test_malformed_sigs.py)"
8786
instrumental -t ecdsa -i 'test.*|.*_version' `which pytest` $files
8887
instrumental -f .instrumental.cov -s
89-
instrumental -f .instrumental.cov -s | python diff-instrumental-2.py --save .diff-instrumental
90-
git checkout $TRAVIS_COMMIT
88+
instrumental -f .instrumental.cov -s | python diff-instrumental.py --save .diff-instrumental
89+
git checkout $BRANCH
90+
files="$(ls src/ecdsa/test*.py | grep -v test_malformed_sigs.py)"
9191
instrumental -t ecdsa -i 'test.*|.*_version' `which pytest` $files
9292
instrumental -f .instrumental.cov -sr
9393
fi
@@ -98,11 +98,11 @@ script:
9898
instrumental -t ecdsa -i 'test.*|.*_version' `which pytest` $files
9999
instrumental -f .instrumental.cov -s
100100
# just log the values when merging
101-
instrumental -f .instrumental.cov -s | python diff-instrumental-2.py
101+
instrumental -f .instrumental.cov -s | python diff-instrumental.py
102102
fi
103103
- |
104104
if [[ $INSTRUMENTAL && $TRAVIS_PULL_REQUEST != "false" ]]; then
105-
instrumental -f .instrumental.cov -s | python diff-instrumental-2.py --read .diff-instrumental --fail-under 70 --max-difference -0.1
105+
instrumental -f .instrumental.cov -s | python diff-instrumental.py --read .diff-instrumental --fail-under 70 --max-difference -0.1
106106
fi
107107
after_success:
108108
- if [[ -z $INSTRUMENTAL ]]; then coveralls; fi

src/ecdsa/_compat.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""
2+
Common functions for providing cross-python version compatibility.
3+
"""
4+
import sys
5+
from six import integer_types
6+
7+
8+
def str_idx_as_int(string, index):
9+
"""Take index'th byte from string, return as integer"""
10+
val = string[index]
11+
if isinstance(val, integer_types):
12+
return val
13+
return ord(val)
14+
15+
16+
if sys.version_info < (3, 0):
17+
def normalise_bytes(buffer_object):
18+
"""Cast the input into array of bytes."""
19+
return buffer(buffer_object)
20+
21+
def hmac_compat(ret):
22+
return ret
23+
24+
else:
25+
if sys.version_info < (3, 4):
26+
# on python 3.3 hmac.hmac.update() accepts only bytes, on newer
27+
# versions it does accept memoryview() also
28+
def hmac_compat(data):
29+
if not isinstance(data, bytes):
30+
return bytes(data)
31+
return data
32+
else:
33+
def hmac_compat(data):
34+
return data
35+
36+
def normalise_bytes(buffer_object):
37+
"""Cast the input into array of bytes."""
38+
return memoryview(buffer_object).cast('B')

src/ecdsa/der.py

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
import binascii
44
import base64
55
import warnings
6-
from six import int2byte, b, integer_types, text_type
6+
from six import int2byte, b, text_type
7+
from ._compat import str_idx_as_int
78

89

910
class UnexpectedDER(Exception):
@@ -20,7 +21,7 @@ def encode_integer(r):
2021
if len(h) % 2:
2122
h = b("0") + h
2223
s = binascii.unhexlify(h)
23-
num = s[0] if isinstance(s[0], integer_types) else ord(s[0])
24+
num = str_idx_as_int(s, 0)
2425
if num <= 0x7f:
2526
return b("\x02") + int2byte(len(s)) + s
2627
else:
@@ -83,7 +84,7 @@ def encode_bitstring(s, unused=_sentry):
8384
if unused:
8485
if not s:
8586
raise ValueError("unused is non-zero but s is empty")
86-
last = s[-1] if isinstance(s[-1], integer_types) else ord(s[-1])
87+
last = str_idx_as_int(s, -1)
8788
if last & (2 ** unused - 1):
8889
raise ValueError("unused bits must be zeros in DER")
8990
encoded_unused = int2byte(unused)
@@ -121,7 +122,7 @@ def encode_number(n):
121122

122123

123124
def remove_constructed(string):
124-
s0 = string[0] if isinstance(string[0], integer_types) else ord(string[0])
125+
s0 = str_idx_as_int(string, 0)
125126
if (s0 & 0xe0) != 0xa0:
126127
raise UnexpectedDER("wanted type 'constructed tag' (0xa0-0xbf), "
127128
"got 0x%02x" % s0)
@@ -135,9 +136,8 @@ def remove_constructed(string):
135136
def remove_sequence(string):
136137
if not string:
137138
raise UnexpectedDER("Empty string does not encode a sequence")
138-
if not string.startswith(b("\x30")):
139-
n = string[0] if isinstance(string[0], integer_types) else \
140-
ord(string[0])
139+
if string[:1] != b"\x30":
140+
n = str_idx_as_int(string, 0)
141141
raise UnexpectedDER("wanted type 'sequence' (0x30), got 0x%02x" % n)
142142
length, lengthlength = read_length(string[1:])
143143
if length > len(string) - 1 - lengthlength:
@@ -147,8 +147,8 @@ def remove_sequence(string):
147147

148148

149149
def remove_octet_string(string):
150-
if not string.startswith(b("\x04")):
151-
n = string[0] if isinstance(string[0], integer_types) else ord(string[0])
150+
if string[:1] != b"\x04":
151+
n = str_idx_as_int(string, 0)
152152
raise UnexpectedDER("wanted type 'octetstring' (0x04), got 0x%02x" % n)
153153
length, llen = read_length(string[1:])
154154
body = string[1+llen:1+llen+length]
@@ -157,8 +157,8 @@ def remove_octet_string(string):
157157

158158

159159
def remove_object(string):
160-
if not string.startswith(b("\x06")):
161-
n = string[0] if isinstance(string[0], integer_types) else ord(string[0])
160+
if string[:1] != b"\x06":
161+
n = str_idx_as_int(string, 0)
162162
raise UnexpectedDER("wanted type 'object' (0x06), got 0x%02x" % n)
163163
length, lengthlength = read_length(string[1:])
164164
body = string[1+lengthlength:1+lengthlength+length]
@@ -180,9 +180,8 @@ def remove_integer(string):
180180
if not string:
181181
raise UnexpectedDER("Empty string is an invalid encoding of an "
182182
"integer")
183-
if not string.startswith(b("\x02")):
184-
n = string[0] if isinstance(string[0], integer_types) \
185-
else ord(string[0])
183+
if string[:1] != b"\x02":
184+
n = str_idx_as_int(string, 0)
186185
raise UnexpectedDER("wanted type 'integer' (0x02), got 0x%02x" % n)
187186
length, llen = read_length(string[1:])
188187
if length > len(string) - 1 - llen:
@@ -191,16 +190,14 @@ def remove_integer(string):
191190
raise UnexpectedDER("0-byte long encoding of integer")
192191
numberbytes = string[1+llen:1+llen+length]
193192
rest = string[1+llen+length:]
194-
msb = numberbytes[0] if isinstance(numberbytes[0], integer_types) \
195-
else ord(numberbytes[0])
193+
msb = str_idx_as_int(numberbytes, 0)
196194
if not msb < 0x80:
197195
raise UnexpectedDER("Negative integers are not supported")
198196
# check if the encoding is the minimal one (DER requirement)
199197
if length > 1 and not msb:
200198
# leading zero byte is allowed if the integer would have been
201199
# considered a negative number otherwise
202-
smsb = numberbytes[1] if isinstance(numberbytes[1], integer_types) \
203-
else ord(numberbytes[1])
200+
smsb = str_idx_as_int(numberbytes, 1)
204201
if smsb < 0x80:
205202
raise UnexpectedDER("Invalid encoding of integer, unnecessary "
206203
"zero padding bytes")
@@ -215,7 +212,7 @@ def read_number(string):
215212
if llen > len(string):
216213
raise UnexpectedDER("ran out of length bytes")
217214
number = number << 7
218-
d = string[llen] if isinstance(string[llen], integer_types) else ord(string[llen])
215+
d = str_idx_as_int(string, llen)
219216
number += (d & 0x7f)
220217
llen += 1
221218
if not d & 0x80:
@@ -238,7 +235,7 @@ def encode_length(l):
238235
def read_length(string):
239236
if not string:
240237
raise UnexpectedDER("Empty string can't encode valid length value")
241-
num = string[0] if isinstance(string[0], integer_types) else ord(string[0])
238+
num = str_idx_as_int(string, 0)
242239
if not (num & 0x80):
243240
# short form
244241
return (num & 0x7f), 1
@@ -250,7 +247,7 @@ def read_length(string):
250247
if llen > len(string)-1:
251248
raise UnexpectedDER("Length of length longer than provided buffer")
252249
# verify that the encoding is minimal possible (DER requirement)
253-
msb = string[1] if isinstance(string[1], integer_types) else ord(string[1])
250+
msb = str_idx_as_int(string, 1)
254251
if not msb or llen == 1 and msb < 0x80:
255252
raise UnexpectedDER("Not minimal encoding of length")
256253
return int(binascii.hexlify(string[1:1+llen]), 16), 1+llen
@@ -301,17 +298,16 @@ def remove_bitstring(string, expect_unused=_sentry):
301298
warnings.warn("Legacy call convention used, expect_unused= needs to be"
302299
" specified",
303300
DeprecationWarning)
304-
num = string[0] if isinstance(string[0], integer_types) else ord(string[0])
305-
if not string.startswith(b("\x03")):
301+
num = str_idx_as_int(string, 0)
302+
if string[:1] != b"\x03":
306303
raise UnexpectedDER("wanted bitstring (0x03), got 0x%02x" % num)
307304
length, llen = read_length(string[1:])
308305
if not length:
309306
raise UnexpectedDER("Invalid length of bit string, can't be 0")
310307
body = string[1+llen:1+llen+length]
311308
rest = string[1+llen+length:]
312309
if expect_unused is not _sentry:
313-
unused = body[0] if isinstance(body[0], integer_types) \
314-
else ord(body[0])
310+
unused = str_idx_as_int(body, 0)
315311
if not 0 <= unused <= 7:
316312
raise UnexpectedDER("Invalid encoding of unused bits")
317313
if expect_unused is not None and expect_unused != unused:
@@ -320,8 +316,7 @@ def remove_bitstring(string, expect_unused=_sentry):
320316
if unused:
321317
if not body:
322318
raise UnexpectedDER("Invalid encoding of empty bit string")
323-
last = body[-1] if isinstance(body[-1], integer_types) else \
324-
ord(body[-1])
319+
last = str_idx_as_int(body, -1)
325320
# verify that all the unused bits are set to zero (DER requirement)
326321
if last & (2 ** unused - 1):
327322
raise UnexpectedDER("Non zero padding bits in bit string")

0 commit comments

Comments
 (0)