Skip to content

Commit 95bbbed

Browse files
committed
add support for encoding
1 parent 8e3f653 commit 95bbbed

File tree

2 files changed

+83
-0
lines changed

2 files changed

+83
-0
lines changed

src/ecdsa/der.py

+26
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,32 @@ def encode_constructed(tag, value):
1616
return int2byte(0xA0 + tag) + encode_length(len(value)) + value
1717

1818

19+
def encode_implicit(tag, value, cls="context-specific"):
20+
"""
21+
Encode and IMPLICIT value using :term:`DER`.
22+
23+
:param int tag: the tag value to encode, must be between 0 an 31 inclusive
24+
:param bytes value: the data to encode
25+
:param str cls: the class of the tag to encode: "application",
26+
"context-specific", or "private"
27+
:rtype: bytes
28+
"""
29+
if cls not in ("application", "context-specific", "private"):
30+
raise ValueError("invalid tag class")
31+
if tag > 31:
32+
raise ValueError("Long tags not supported")
33+
34+
if cls == "application":
35+
tag_class = 0b01000000
36+
elif cls == "context-specific":
37+
tag_class = 0b10000000
38+
else:
39+
assert cls == "private"
40+
tag_class = 0b11000000
41+
42+
return int2byte(tag_class + tag) + encode_length(len(value)) + value
43+
44+
1945
def encode_integer(r):
2046
assert r >= 0 # can't support negative numbers yet
2147
h = ("%x" % r).encode()

src/ecdsa/test_der.py

+57
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
remove_implicit,
2626
remove_octet_string,
2727
remove_sequence,
28+
encode_implicit,
2829
)
2930

3031

@@ -463,6 +464,62 @@ def test_with_constructed(self):
463464

464465
self.assertIn("wanted type primitive, got 0xa6 tag", str(e.exception))
465466

467+
def test_encode_decode(self):
468+
data = b"some longish string"
469+
470+
tag, body, rest = remove_implicit(
471+
encode_implicit(6, data, "application"),
472+
"application"
473+
)
474+
475+
self.assertEqual(tag, 6)
476+
self.assertEqual(body, data)
477+
self.assertEqual(rest, b"")
478+
479+
480+
class TestEncodeImplicit(unittest.TestCase):
481+
@classmethod
482+
def setUpClass(cls):
483+
cls.data = b"\x0a\x0b"
484+
# data with application tag class
485+
cls.data_application = b"\x46\x02\x0a\x0b"
486+
# data with context-specific tag class
487+
cls.data_context_specific = b"\x86\x02\x0a\x0b"
488+
# data with private tag class
489+
cls.data_private = b"\xc6\x02\x0a\x0b"
490+
491+
def test_encode_with_default_class(self):
492+
ret = encode_implicit(6, self.data)
493+
494+
self.assertEqual(ret, self.data_context_specific)
495+
496+
def test_encode_with_application_class(self):
497+
ret = encode_implicit(6, self.data, "application")
498+
499+
self.assertEqual(ret, self.data_application)
500+
501+
def test_encode_with_context_specific_class(self):
502+
ret = encode_implicit(6, self.data, "context-specific")
503+
504+
self.assertEqual(ret, self.data_context_specific)
505+
506+
def test_encode_with_private_class(self):
507+
ret = encode_implicit(6, self.data, "private")
508+
509+
self.assertEqual(ret, self.data_private)
510+
511+
def test_encode_with_invalid_class(self):
512+
with self.assertRaises(ValueError) as e:
513+
encode_implicit(6, self.data, "foobar")
514+
515+
self.assertIn("invalid tag class", str(e.exception))
516+
517+
def test_encode_with_too_large_tag(self):
518+
with self.assertRaises(ValueError) as e:
519+
encode_implicit(32, self.data)
520+
521+
self.assertIn("Long tags not supported", str(e.exception))
522+
466523

467524
class TestRemoveOctetString(unittest.TestCase):
468525
def test_simple(self):

0 commit comments

Comments
 (0)