Skip to content

Commit dba9f80

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

File tree

2 files changed

+82
-0
lines changed

2 files changed

+82
-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

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

467523
class TestRemoveOctetString(unittest.TestCase):
468524
def test_simple(self):

0 commit comments

Comments
 (0)