diff options
author | Legrandin <gooksankoo@hoiptorrow.mailexpire.com> | 2012-06-05 19:58:19 +0200 |
---|---|---|
committer | Dwayne Litzenberger <dlitz@dlitz.net> | 2013-07-14 21:16:46 -0700 |
commit | 5a0ee14e9904335cb90c0dd7a4e10f1523435c52 (patch) | |
tree | e4f4c2e8b07b6bccb955912bb3e8a302e5ccaf0a | |
parent | af9b41cc4b0a58dd87f56e334a8d478f238f074d (diff) | |
download | pycrypto-5a0ee14e9904335cb90c0dd7a4e10f1523435c52.tar.gz |
Refactoring of the asn1 module
The following changes are included:
- Decoding is a much simpler operation. The internal
logic is based on stream of binary data, and not
on string indexing anymore. Additionally,
decoding used to look like this:
bitmap = DerObject()
bitmap.decode(input_buffer, True)
if bitmap.isType('BIT STRING'):
... proceed with parsing ...
else:
... error ...
Whereas now, it is cleaner and more compact:
bitmap = DerBitString()
bitmap.decode(input_buffer)
Any error condition will lead to an exception.
- isType() method has been removed because of the above.
- Added examples and documentation
- Added support IMPLICIT tags
- Added support for negative INTEGERs
- Added DerSetOf ASN.1 class
- DerObjectID can be initialized from the dotted representation of
the Object ID.
- DerBitString has a new member 'value' to hold the binary
string. The member 'payload' should not be accessed anymore.
- DerObjectID has a new member 'value' to hold the dotted representation
of the Object ID string. The member 'payload' should not be accessed
anymore.
- Added operator += to DER SEQUENCE. Now it is possible to do:
my_str = DerOctetString(b'ZYZ')
seq = DerSequence()
seq += 0
seq += my_str.encode()
- Update to test cases
-rw-r--r-- | lib/Crypto/SelfTest/Util/test_asn1.py | 848 | ||||
-rw-r--r-- | lib/Crypto/Util/asn1.py | 945 | ||||
-rw-r--r-- | lib/Crypto/Util/py3compat.py | 6 |
3 files changed, 1387 insertions, 412 deletions
diff --git a/lib/Crypto/SelfTest/Util/test_asn1.py b/lib/Crypto/SelfTest/Util/test_asn1.py index bbd0a39..58d12b4 100644 --- a/lib/Crypto/SelfTest/Util/test_asn1.py +++ b/lib/Crypto/SelfTest/Util/test_asn1.py @@ -28,262 +28,622 @@ import unittest import sys from Crypto.Util.py3compat import * -from Crypto.Util.asn1 import DerSequence, DerObject +from Crypto.Util.asn1 import * +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * class DerObjectTests(unittest.TestCase): - def testObjEncode1(self): - # No payload - der = DerObject(b('\x33')) - self.assertEquals(der.encode(), b('\x33\x00')) - # Small payload - der.payload = b('\x45') - self.assertEquals(der.encode(), b('\x33\x01\x45')) - # Invariant - self.assertEquals(der.encode(), b('\x33\x01\x45')) - # Initialize with numerical tag - der = DerObject(b(0x33)) - der.payload = b('\x45') - self.assertEquals(der.encode(), b('\x33\x01\x45')) - - def testObjEncode2(self): - # Known types - der = DerObject('SEQUENCE') - self.assertEquals(der.encode(), b('\x30\x00')) - der = DerObject('BIT STRING') - self.assertEquals(der.encode(), b('\x03\x00')) - - def testObjEncode3(self): - # Long payload - der = DerObject(b('\x34')) - der.payload = b("0")*128 - self.assertEquals(der.encode(), b('\x34\x81\x80' + "0"*128)) - - def testObjDecode1(self): - # Decode short payload - der = DerObject() - der.decode(b('\x20\x02\x01\x02')) - self.assertEquals(der.payload, b("\x01\x02")) - self.assertEquals(der.typeTag, 0x20) - - def testObjDecode2(self): - # Decode short payload - der = DerObject() - der.decode(b('\x22\x81\x80' + "1"*128)) - self.assertEquals(der.payload, b("1")*128) - self.assertEquals(der.typeTag, 0x22) + def testObjInit1(self): + # Fail with invalid tag format (must be 1 byte) + self.assertRaises(ValueError, DerObject, b('\x00\x99')) + # Fail with invalid implicit tag (must be <0x1F) + self.assertRaises(ValueError, DerObject, 0x1F) + # ------ + + def testObjEncode1(self): + # No payload + der = DerObject(b('\x02')) + self.assertEquals(der.encode(), b('\x02\x00')) + # Small payload (primitive) + der.payload = b('\x45') + self.assertEquals(der.encode(), b('\x02\x01\x45')) + # Invariant + self.assertEquals(der.encode(), b('\x02\x01\x45')) + # Initialize with numerical tag + der = DerObject(0x04) + der.payload = b('\x45') + self.assertEquals(der.encode(), b('\x04\x01\x45')) + # Initialize with constructed type + der = DerObject(b('\x10'), constructed=True) + self.assertEquals(der.encode(), b('\x30\x00')) + + def testObjEncode2(self): + # Initialize with payload + der = DerObject(0x03, b('\x12\x12')) + self.assertEquals(der.encode(), b('\x03\x02\x12\x12')) + + def testObjEncode3(self): + # Long payload + der = DerObject(b('\x10')) + der.payload = b("0")*128 + self.assertEquals(der.encode(), b('\x10\x81\x80' + "0"*128)) + + def testObjEncode4(self): + # Implicit tags (constructed) + der = DerObject(0x10, implicit=1, constructed=True) + der.payload = b('ppll') + self.assertEquals(der.encode(), b('\xa1\x04ppll')) + # Implicit tags (primitive) + der = DerObject(0x02, implicit=0x1E, constructed=False) + der.payload = b('ppll') + self.assertEquals(der.encode(), b('\x9E\x04ppll')) + + # ----- + + def testObjDecode1(self): + # Decode short payload + der = DerObject(0x02) + der.decode(b('\x02\x02\x01\x02')) + self.assertEquals(der.payload, b("\x01\x02")) + self.assertEquals(der._idOctet, 0x02) + + def testObjDecode2(self): + # Decode long payload + der = DerObject(0x02) + der.decode(b('\x02\x81\x80' + "1"*128)) + self.assertEquals(der.payload, b("1")*128) + self.assertEquals(der._idOctet, 0x02) + + def testObjDecode3(self): + # Decode payload with too much data gives error + der = DerObject(0x02) + self.assertRaises(ValueError, der.decode, b('\x02\x02\x01\x02\xFF')) + # Decode payload with too little data gives error + der = DerObject(0x02) + self.assertRaises(EOFError, der.decode, b('\x02\x02\x01')) + + def testObjDecode4(self): + # Decode implicit tag (primitive) + der = DerObject(0x02, constructed=False, implicit=0xF) + self.assertRaises(ValueError, der.decode, b('\x02\x02\x01\x02')) + der.decode(b('\x8F\x01\x00')) + self.assertEquals(der.payload, b('\x00')) + # Decode implicit tag (constructed) + der = DerObject(0x02, constructed=True, implicit=0xF) + self.assertRaises(ValueError, der.decode, b('\x02\x02\x01\x02')) + der.decode(b('\xAF\x01\x00')) + self.assertEquals(der.payload, b('\x00')) + + def testObjDecode5(self): + # Decode payload with unexpected tag gives error + der = DerObject(0x02) + self.assertRaises(ValueError, der.decode, b('\x03\x02\x01\x02')) + + def testObjDecode6(self): + # Arbitrary DER object + der = DerObject() + der.decode(b('\x65\x01\x88')) + self.assertEquals(der._idOctet, 0x65) + self.assertEquals(der.payload, b('\x88')) + +class DerIntegerTests(unittest.TestCase): + + def testInit1(self): + der = newDerInteger(1) + self.assertEquals(der.encode(), b('\x02\x01\x01')) + + def testEncode1(self): + # Single-byte integers + # Value 0 + der = DerInteger(0) + self.assertEquals(der.encode(), b('\x02\x01\x00')) + # Value 1 + der = DerInteger(1) + self.assertEquals(der.encode(), b('\x02\x01\x01')) + # Value 127 + der = DerInteger(127) + self.assertEquals(der.encode(), b('\x02\x01\x7F')) + + def testEncode2(self): + # Multi-byte integers + # Value 128 + der = DerInteger(128) + self.assertEquals(der.encode(), b('\x02\x02\x00\x80')) + # Value 0x180 + der = DerInteger(0x180L) + self.assertEquals(der.encode(), b('\x02\x02\x01\x80')) + # One very long integer + der = DerInteger(2L**2048) + self.assertEquals(der.encode(), + b('\x02\x82\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00')) + + def testEncode3(self): + # Negative integers + # Value -1 + der = DerInteger(-1) + self.assertEquals(der.encode(), b('\x02\x01\xFF')) + # Value -128 + der = DerInteger(-128) + self.assertEquals(der.encode(), b('\x02\x01\x80')) + # Value + der = DerInteger(-87873) + self.assertEquals(der.encode(), b('\x02\x03\xFE\xA8\xBF')) + + # ----- + + def testDecode1(self): + # Single-byte integer + der = DerInteger() + # Value 0 + der.decode(b('\x02\x01\x00')) + self.assertEquals(der.value, 0) + # Value 1 + der.decode(b('\x02\x01\x01')) + self.assertEquals(der.value, 1) + # Value 127 + der.decode(b('\x02\x01\x7F')) + self.assertEquals(der.value, 127) + + def testDecode2(self): + # Multi-byte integer + der = DerInteger() + # Value 0x180L + der.decode(b('\x02\x02\x01\x80')) + self.assertEquals(der.value,0x180L) + # One very long integer + der.decode( + b('\x02\x82\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00')) + self.assertEquals(der.value,2L**2048) + + def testDecode3(self): + # Negative integer + der = DerInteger() + # Value -1 + der.decode(b('\x02\x01\xFF')) + self.assertEquals(der.value, -1) + # Value -32768 + der.decode(b('\x02\x02\x80\x00')) + self.assertEquals(der.value, -32768) + + def testDecode5(self): + # We still accept BER integer format + der = DerInteger() + # Redundant leading zeroes + der.decode(b('\x02\x02\x00\x01')) + self.assertEquals(der.value, 1) + # Redundant leading 0xFF + der.decode(b('\x02\x02\xFF\xFF')) + self.assertEquals(der.value, -1) + # Empty payload + der.decode(b('\x02\x00')) + self.assertEquals(der.value, 0) + + def testErrDecode1(self): + # Wide length field + der = DerInteger() + self.assertRaises(ValueError, der.decode, b('\x02\x81\x01\x01')) + class DerSequenceTests(unittest.TestCase): - def testEncode1(self): - # Empty sequence - der = DerSequence() - self.assertEquals(der.encode(), b('0\x00')) - self.failIf(der.hasOnlyInts()) - # One single-byte integer (zero) - der.append(0) - self.assertEquals(der.encode(), b('0\x03\x02\x01\x00')) - self.failUnless(der.hasOnlyInts()) - # Invariant - self.assertEquals(der.encode(), b('0\x03\x02\x01\x00')) - - def testEncode2(self): - # One single-byte integer (non-zero) - der = DerSequence() - der.append(127) - self.assertEquals(der.encode(), b('0\x03\x02\x01\x7f')) - # Indexing - der[0] = 1 - self.assertEquals(len(der),1) - self.assertEquals(der[0],1) - self.assertEquals(der[-1],1) - self.assertEquals(der.encode(), b('0\x03\x02\x01\x01')) - # - der[:] = [1] - self.assertEquals(len(der),1) - self.assertEquals(der[0],1) - self.assertEquals(der.encode(), b('0\x03\x02\x01\x01')) - - def testEncode3(self): - # One multi-byte integer (non-zero) - der = DerSequence() - der.append(0x180L) - self.assertEquals(der.encode(), b('0\x04\x02\x02\x01\x80')) - - def testEncode4(self): - # One very long integer - der = DerSequence() - der.append(2**2048) - self.assertEquals(der.encode(), b('0\x82\x01\x05')+ - b('\x02\x82\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00')) - - def testEncode5(self): - # One single-byte integer (looks negative) - der = DerSequence() - der.append(0xFFL) - self.assertEquals(der.encode(), b('0\x04\x02\x02\x00\xff')) - - def testEncode6(self): - # Two integers - der = DerSequence() - der.append(0x180L) - der.append(0xFFL) - self.assertEquals(der.encode(), b('0\x08\x02\x02\x01\x80\x02\x02\x00\xff')) - self.failUnless(der.hasOnlyInts()) - # - der.append(0x01) - der[1:] = [9,8] - self.assertEquals(len(der),3) - self.assertEqual(der[1:],[9,8]) - self.assertEqual(der[1:-1],[9]) - self.assertEquals(der.encode(), b('0\x0A\x02\x02\x01\x80\x02\x01\x09\x02\x01\x08')) - - def testEncode7(self): - # One integer and another type (no matter what it is) - der = DerSequence() - der.append(0x180L) - der.append(b('\x00\x02\x00\x00')) - self.assertEquals(der.encode(), b('0\x08\x02\x02\x01\x80\x00\x02\x00\x00')) - self.failIf(der.hasOnlyInts()) - - #### - - def testDecode1(self): - # Empty sequence - der = DerSequence() - der.decode(b('0\x00')) - self.assertEquals(len(der),0) - # One single-byte integer (zero) - der.decode(b('0\x03\x02\x01\x00')) - self.assertEquals(len(der),1) - self.assertEquals(der[0],0) - # Invariant - der.decode(b('0\x03\x02\x01\x00')) - self.assertEquals(len(der),1) - self.assertEquals(der[0],0) - - def testDecode2(self): - # One single-byte integer (non-zero) - der = DerSequence() - der.decode(b('0\x03\x02\x01\x7f')) - self.assertEquals(len(der),1) - self.assertEquals(der[0],127) - - def testDecode3(self): - # One multi-byte integer (non-zero) - der = DerSequence() - der.decode(b('0\x04\x02\x02\x01\x80')) - self.assertEquals(len(der),1) - self.assertEquals(der[0],0x180L) - - def testDecode4(self): - # One very long integer - der = DerSequence() - der.decode(b('0\x82\x01\x05')+ - b('\x02\x82\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00')) - self.assertEquals(len(der),1) - self.assertEquals(der[0],2**2048) - - def testDecode5(self): - # One single-byte integer (looks negative) - der = DerSequence() - der.decode(b('0\x04\x02\x02\x00\xff')) - self.assertEquals(len(der),1) - self.assertEquals(der[0],0xFFL) - - def testDecode6(self): - # Two integers - der = DerSequence() - der.decode(b('0\x08\x02\x02\x01\x80\x02\x02\x00\xff')) - self.assertEquals(len(der),2) - self.assertEquals(der[0],0x180L) - self.assertEquals(der[1],0xFFL) - - def testDecode7(self): - # One integer and 2 other types - der = DerSequence() - der.decode(b('0\x0A\x02\x02\x01\x80\x24\x02\xb6\x63\x12\x00')) - self.assertEquals(len(der),3) - self.assertEquals(der[0],0x180L) - self.assertEquals(der[1],b('\x24\x02\xb6\x63')) - self.assertEquals(der[2],b('\x12\x00')) - - def testDecode8(self): - # Only 2 other types - der = DerSequence() - der.decode(b('0\x06\x24\x02\xb6\x63\x12\x00')) - self.assertEquals(len(der),2) - self.assertEquals(der[0],b('\x24\x02\xb6\x63')) - self.assertEquals(der[1],b('\x12\x00')) - - def testErrDecode1(self): - # Not a sequence - der = DerSequence() - self.assertRaises(ValueError, der.decode, b('')) - self.assertRaises(ValueError, der.decode, b('\x00')) - self.assertRaises(ValueError, der.decode, b('\x30')) - - def testErrDecode2(self): - # Wrong payload type - der = DerSequence() - self.assertRaises(ValueError, der.decode, b('\x30\x00\x00'), True) - - def testErrDecode3(self): - # Wrong length format - der = DerSequence() - self.assertRaises(ValueError, der.decode, b('\x30\x04\x02\x01\x01\x00')) - self.assertRaises(ValueError, der.decode, b('\x30\x81\x03\x02\x01\x01')) - self.assertRaises(ValueError, der.decode, b('\x30\x04\x02\x81\x01\x01')) - - def testErrDecode4(self): - # Wrong integer format - der = DerSequence() - # Multi-byte encoding for zero - #self.assertRaises(ValueError, der.decode, '\x30\x04\x02\x02\x00\x00') - # Negative integer - self.assertRaises(ValueError, der.decode, b('\x30\x04\x02\x01\xFF')) + def testInit1(self): + der = newDerSequence(1, DerInteger(2), '0\x00') + self.assertEquals(der.encode(), b('0\x08\x02\x01\x01\x02\x01\x020\x00')) + + def testEncode1(self): + # Empty sequence + der = DerSequence() + self.assertEquals(der.encode(), b('0\x00')) + self.failIf(der.hasOnlyInts()) + # One single-byte integer (zero) + der.append(0) + self.assertEquals(der.encode(), b('0\x03\x02\x01\x00')) + self.assertEquals(der.hasInts(),1) + self.assertEquals(der.hasInts(False),1) + self.failUnless(der.hasOnlyInts()) + self.failUnless(der.hasOnlyInts(False)) + # Invariant + self.assertEquals(der.encode(), b('0\x03\x02\x01\x00')) + + def testEncode2(self): + # Indexing + der = DerSequence() + der.append(0) + der[0] = 1 + self.assertEquals(len(der),1) + self.assertEquals(der[0],1) + self.assertEquals(der[-1],1) + self.assertEquals(der.encode(), b('0\x03\x02\x01\x01')) + # + der[:] = [1] + self.assertEquals(len(der),1) + self.assertEquals(der[0],1) + self.assertEquals(der.encode(), b('0\x03\x02\x01\x01')) + + def testEncode3(self): + # One multi-byte integer (non-zero) + der = DerSequence() + der.append(0x180L) + self.assertEquals(der.encode(), b('0\x04\x02\x02\x01\x80')) + + def testEncode4(self): + # One very long integer + der = DerSequence() + der.append(2L**2048) + self.assertEquals(der.encode(), b('0\x82\x01\x05')+ + b('\x02\x82\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00')) + + def testEncode5(self): + der = DerSequence() + der += 1 + der += b('\x30\x00') + self.assertEquals(der.encode(), b('\x30\x05\x02\x01\x01\x30\x00')) + + def testEncode6(self): + # Two positive integers + der = DerSequence() + der.append(0x180L) + der.append(0xFFL) + self.assertEquals(der.encode(), b('0\x08\x02\x02\x01\x80\x02\x02\x00\xff')) + self.failUnless(der.hasOnlyInts()) + self.failUnless(der.hasOnlyInts(False)) + # Two mixed integers + der = DerSequence() + der.append(2) + der.append(-2) + self.assertEquals(der.encode(), b('0\x06\x02\x01\x02\x02\x01\xFE')) + self.assertEquals(der.hasInts(), 1) + self.assertEquals(der.hasInts(False), 2) + self.failIf(der.hasOnlyInts()) + self.failUnless(der.hasOnlyInts(False)) + # + der.append(0x01) + der[1:] = [9,8] + self.assertEquals(len(der),3) + self.assertEqual(der[1:],[9,8]) + self.assertEqual(der[1:-1],[9]) + self.assertEquals(der.encode(), b('0\x09\x02\x01\x02\x02\x01\x09\x02\x01\x08')) + + def testEncode7(self): + # One integer and another type (no matter what it is) + der = DerSequence() + der.append(0x180L) + der.append(b('\x00\x02\x00\x00')) + self.assertEquals(der.encode(), b('0\x08\x02\x02\x01\x80\x00\x02\x00\x00')) + self.failIf(der.hasOnlyInts()) + + #### + + def testDecode1(self): + # Empty sequence + der = DerSequence() + der.decode(b('0\x00')) + self.assertEquals(len(der),0) + # One single-byte integer (zero) + der.decode(b('0\x03\x02\x01\x00')) + self.assertEquals(len(der),1) + self.assertEquals(der[0],0) + # Invariant + der.decode(b('0\x03\x02\x01\x00')) + self.assertEquals(len(der),1) + self.assertEquals(der[0],0) + + def testDecode2(self): + # One single-byte integer (non-zero) + der = DerSequence() + der.decode(b('0\x03\x02\x01\x7f')) + self.assertEquals(len(der),1) + self.assertEquals(der[0],127) + + def testDecode4(self): + # One very long integer + der = DerSequence() + der.decode(b('0\x82\x01\x05')+ + b('\x02\x82\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00')) + self.assertEquals(len(der),1) + self.assertEquals(der[0],2L**2048) + + def testDecode6(self): + # Two integers + der = DerSequence() + der.decode(b('0\x08\x02\x02\x01\x80\x02\x02\x00\xff')) + self.assertEquals(len(der),2) + self.assertEquals(der[0],0x180L) + self.assertEquals(der[1],0xFFL) + + def testDecode7(self): + # One integer and 2 other types + der = DerSequence() + der.decode(b('0\x0A\x02\x02\x01\x80\x24\x02\xb6\x63\x12\x00')) + self.assertEquals(len(der),3) + self.assertEquals(der[0],0x180L) + self.assertEquals(der[1],b('\x24\x02\xb6\x63')) + self.assertEquals(der[2],b('\x12\x00')) + + def testDecode8(self): + # Only 2 other types + der = DerSequence() + der.decode(b('0\x06\x24\x02\xb6\x63\x12\x00')) + self.assertEquals(len(der),2) + self.assertEquals(der[0],b('\x24\x02\xb6\x63')) + self.assertEquals(der[1],b('\x12\x00')) + self.assertEquals(der.hasInts(), 0) + self.assertEquals(der.hasInts(False), 0) + self.failIf(der.hasOnlyInts()) + self.failIf(der.hasOnlyInts(False)) + + def testErrDecode1(self): + # Not a sequence + der = DerSequence() + self.assertRaises(EOFError, der.decode, b('')) + self.assertRaises(ValueError, der.decode, b('\x00')) + self.assertRaises(EOFError, der.decode, b('\x30')) + + def testErrDecode2(self): + der = DerSequence() + # Too much data + self.assertRaises(ValueError, der.decode, b('\x30\x00\x00')) + + def testErrDecode3(self): + # Wrong length format + der = DerSequence() + # Missing length in sub-item + self.assertRaises(EOFError, der.decode, b('\x30\x04\x02\x01\x01\x00')) + # Valid BER, but invalid DER length + self.assertRaises(ValueError, der.decode, b('\x30\x81\x03\x02\x01\x01')) + self.assertRaises(ValueError, der.decode, b('\x30\x04\x02\x81\x01\x01')) + +class DerOctetStringTests(unittest.TestCase): + + def testInit1(self): + der = newDerOctetString(b('\xFF')) + self.assertEquals(der.encode(), b('\x04\x01\xFF')) + + def testEncode1(self): + # Empty sequence + der = DerOctetString() + self.assertEquals(der.encode(), b('\x04\x00')) + # Small payload + der.payload = b('\x01\x02') + self.assertEquals(der.encode(), b('\x04\x02\x01\x02')) + + #### + + def testDecode1(self): + # Empty sequence + der = DerOctetString() + der.decode(b('\x04\x00')) + self.assertEquals(der.payload, b('')) + # Small payload + der.decode(b('\x04\x02\x01\x02')) + self.assertEquals(der.payload, b('\x01\x02')) + + def testErrDecode1(self): + # No leftovers allowed + der = DerOctetString() + self.assertRaises(ValueError, der.decode, b('\x04\x01\x01\xff')) + +class DerNullTests(unittest.TestCase): + + def testEncode1(self): + der = DerNull() + self.assertEquals(der.encode(), b('\x05\x00')) + + #### + + def testDecode1(self): + # Empty sequence + der = DerNull() + der.decode(b('\x05\x00')) + +class DerObjectIdTests(unittest.TestCase): + + def testInit1(self): + der = newDerObjectId("1.1") + self.assertEquals(der.encode(), b('\x06\x01)')) + + def testEncode1(self): + der = DerObjectId('1.2.840.113549.1.1.1') + self.assertEquals(der.encode(), b('\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01')) + # + der = DerObjectId() + der.value = '1.2.840.113549.1.1.1' + self.assertEquals(der.encode(), b('\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01')) + + #### + + def testDecode1(self): + # Empty sequence + der = DerObjectId() + der.decode(b('\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01')) + self.assertEquals(der.value, '1.2.840.113549.1.1.1') + +class DerBitStringTests(unittest.TestCase): + + def testInit1(self): + der = newDerBitString(b("\xFF")) + self.assertEquals(der.encode(), b('\x03\x02\x00\xFF')) + + def testEncode1(self): + # Empty sequence + der = DerBitString() + self.assertEquals(der.encode(), b('\x03\x01\x00')) + # Small payload + der = DerBitString(b('\x01\x02')) + self.assertEquals(der.encode(), b('\x03\x03\x00\x01\x02')) + # Small payload + der = DerBitString() + der.value = b('\x01\x02') + self.assertEquals(der.encode(), b('\x03\x03\x00\x01\x02')) + + #### + + def testDecode1(self): + # Empty sequence + der = DerBitString() + der.decode(b('\x03\x00')) + self.assertEquals(der.value, b('')) + # Small payload + der.decode(b('\x03\x03\x00\x01\x02')) + self.assertEquals(der.value, b('\x01\x02')) + +class DerSetOfTests(unittest.TestCase): + + def testInit1(self): + der = newDerSetOf(DerInteger(1), DerInteger(2)) + self.assertEquals(der.encode(), b('1\x06\x02\x01\x01\x02\x01\x02')) + + def testEncode1(self): + # Empty set + der = DerSetOf() + self.assertEquals(der.encode(), b('1\x00')) + # One single-byte integer (zero) + der.add(0) + self.assertEquals(der.encode(), b('1\x03\x02\x01\x00')) + # Invariant + self.assertEquals(der.encode(), b('1\x03\x02\x01\x00')) + + def testEncode2(self): + # Two integers + der = DerSetOf() + der.add(0x180L) + der.add(0xFFL) + self.assertEquals(der.encode(), b('1\x08\x02\x02\x00\xff\x02\x02\x01\x80')) + # Initialize with integers + der = DerSetOf([0x180L, 0xFFL]) + self.assertEquals(der.encode(), b('1\x08\x02\x02\x00\xff\x02\x02\x01\x80')) + + def testEncode3(self): + # One integer and another type (no matter what it is) + der = DerSetOf() + der.add(0x180L) + self.assertRaises(ValueError, der.add, b('\x00\x02\x00\x00')) + + def testEncode4(self): + # Only non integers + der = DerSetOf() + der.add(b('\x01\x00')) + der.add(b('\x01\x01\x01')) + self.assertEquals(der.encode(), b('1\x05\x01\x00\x01\x01\x01')) + + #### + + def testDecode1(self): + # Empty sequence + der = DerSetOf() + der.decode(b('1\x00')) + self.assertEquals(len(der),0) + # One single-byte integer (zero) + der.decode(b('1\x03\x02\x01\x00')) + self.assertEquals(len(der),1) + self.assertEquals(list(der),[0]) + + def testDecode2(self): + # Two integers + der = DerSetOf() + der.decode(b('1\x08\x02\x02\x01\x80\x02\x02\x00\xff')) + self.assertEquals(len(der),2) + l = list(der) + self.failUnless(0x180 in l) + self.failUnless(0xFF in l) + + def testDecode3(self): + # One integer and 2 other types + der = DerSetOf() + #import pdb; pdb.set_trace() + self.assertRaises(ValueError, der.decode, + b('0\x0A\x02\x02\x01\x80\x24\x02\xb6\x63\x12\x00')) + def testErrDecode1(self): + # No leftovers allowed + der = DerSetOf() + self.assertRaises(ValueError, der.decode, + b('1\x08\x02\x02\x01\x80\x02\x02\x00\xff\xAA')) + def get_tests(config={}): from Crypto.SelfTest.st_common import list_test_cases listTests = [] listTests += list_test_cases(DerObjectTests) + listTests += list_test_cases(DerIntegerTests) listTests += list_test_cases(DerSequenceTests) + listTests += list_test_cases(DerOctetStringTests) + listTests += list_test_cases(DerNullTests) + listTests += list_test_cases(DerObjectIdTests) + listTests += list_test_cases(DerBitStringTests) + listTests += list_test_cases(DerSetOfTests) return listTests if __name__ == '__main__': diff --git a/lib/Crypto/Util/asn1.py b/lib/Crypto/Util/asn1.py index cbeb6e6..0e471a3 100644 --- a/lib/Crypto/Util/asn1.py +++ b/lib/Crypto/Util/asn1.py @@ -19,150 +19,349 @@ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # =================================================================== +""" ASN.1 DER encoding and decoding + +This module provides minimal support for encoding and decoding `ASN.1`_ DER +objects. + +.. _`ASN.1`: ftp://ftp.rsasecurity.com/pub/pkcs/ascii/layman.asc + +""" + +from __future__ import nested_scopes -from Crypto.Util.number import long_to_bytes, bytes_to_long import sys + if sys.version_info[0] == 2 and sys.version_info[1] == 1: from Crypto.Util.py21compat import * from Crypto.Util.py3compat import * +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * + +from Crypto.Util.number import long_to_bytes, bytes_to_long + +__all__ = [ 'DerObject', 'DerInteger', 'DerOctetString', 'DerNull', + 'DerSequence', 'DerObjectId', 'DerBitString', 'DerSetOf', + 'newDerInteger', 'newDerOctetString', 'newDerSequence', + 'newDerObjectId', 'newDerBitString', 'newDerSetOf' ] + +def _isInt(x, onlyNonNegative=False): + test = 0 + try: + test += x + except TypeError: + return False + return not onlyNonNegative or x>=0 + +class BytesIO_EOF(BytesIO): + """This class differs from BytesIO in that an EOFError exception is + raised whenever EOF is reached.""" + + def __init__(self, *params): + BytesIO.__init__(self, *params) + self.setRecord(False) + + def setRecord(self, record): + self._record = record + self._recording = b("") + + def read(self, length): + s = BytesIO.read(self, length) + if len(s)<length: + raise EOFError + if self._record: + self._recording += s + return s + + def read_byte(self): + return self.read(1)[0] -__all__ = [ 'DerObject', 'DerInteger', 'DerOctetString', 'DerNull', 'DerSequence', 'DerObjectId' ] +class NoDerElementError(EOFError): + pass class DerObject(object): """Base class for defining a single DER object. - Instantiate this class ONLY when you have to decode a DER element. + This class should never be directly instantiated. """ - # Known TAG types - typeTags = { 'SEQUENCE': 0x30, 'BIT STRING': 0x03, 'INTEGER': 0x02, - 'OCTET STRING': 0x04, 'NULL': 0x05, 'OBJECT IDENTIFIER': 0x06 } + def __init__(self, asn1Id=None, payload=b(''), implicit=None, constructed=False): + """Initialize the DER object according to a specific ASN.1 type. - def __init__(self, ASN1Type=None, payload=b('')): - """Initialize the DER object according to a specific type. + :Parameters: + asn1Id : integer + The universal DER tag identifier for this object + (e.g. 0x10 for a SEQUENCE). If None, the tag is not known + yet. - The ASN.1 type is either specified as the ASN.1 string (e.g. - 'SEQUENCE'), directly with its numerical tag or with no tag - at all (None).""" - if isInt(ASN1Type) or ASN1Type is None: - self.typeTag = ASN1Type + payload : byte string + The initial payload of the object. + If not specified, the payload is empty. + + implicit : integer + The IMPLICIT tag to use for the encoded object. + It overrides the universal tag *asn1Id*. + + constructed : bool + True when the ASN.1 type is *constructed*. + False when it is *primitive*. + """ + + if asn1Id==None: + self._idOctet = None + return + asn1Id = self._convertTag(asn1Id) + self._implicit = implicit + if implicit: + # In a BER/DER identifier octet: + # * bits 4-0 contain the tag value + # * bit 5 is set if the type is 'construted' + # and unset if 'primitive' + # * bits 7-6 depend on the encoding class + # + # Class | Bit 7, Bit 6 + # universal | 0 0 + # application | 0 1 + # context-spec | 1 0 (default for IMPLICIT) + # private | 1 1 + # + self._idOctet = 0x80 | self._convertTag(implicit) else: - if len(ASN1Type)==1: - self.typeTag = ord(ASN1Type) - else: - self.typeTag = self.typeTags.get(ASN1Type) + self._idOctet = asn1Id + if constructed: + self._idOctet |= 0x20 self.payload = payload - def isType(self, ASN1Type): - return self.typeTags[ASN1Type]==self.typeTag - - def _lengthOctets(self, payloadLen): - """Return a byte string that encodes the given payload length (in - bytes) in a format suitable for a DER length tag (L). + def _convertTag(self, tag): + """Check if *tag* is a real DER tag. + Convert it from a character to number if necessary. + """ + if not _isInt(tag): + if len(tag)==1: + tag = bord(tag[0]) + # Ensure that tag is a low tag + if not (_isInt(tag) and 0 <= tag < 0x1F): + raise ValueError("Wrong DER tag") + return tag + + def _lengthOctets(self): + """Build length octets according to the current object's payload. + + Return a byte string that encodes the payload length (in + bytes) in a format suitable for DER length octets (L). """ + payloadLen = len(self.payload) if payloadLen>127: encoding = long_to_bytes(payloadLen) return bchr(len(encoding)+128) + encoding return bchr(payloadLen) def encode(self): - """Return a complete DER element, fully encoded as a TLV.""" - return bchr(self.typeTag) + self._lengthOctets(len(self.payload)) + self.payload + """Return this DER element, fully encoded as a binary byte string.""" + # Concatenate identifier octets, length octets, + # and contents octets + return bchr(self._idOctet) + self._lengthOctets() + self.payload - def _decodeLen(self, idx, der): - """Given a (part of a) DER element, and an index to the first byte of - a DER length tag (L), return a tuple with the payload size, - and the index of the first byte of the such payload (V). + def _decodeLen(self, s): + """Decode DER length octets from a file.""" - Raises a ValueError exception if the DER length is invalid. - Raises an IndexError exception if the DER element is too short. - """ - length = bord(der[idx]) + length = bord(s.read_byte()) if length<=127: - return (length,idx+1) - payloadLength = bytes_to_long(der[idx+1:idx+1+(length & 0x7F)]) + return length + payloadLength = bytes_to_long(s.read(length & 0x7F)) + # According to DER (but not BER) the long form is used + # only when the length doesn't fit into 7 bits. if payloadLength<=127: - raise ValueError("Not a DER length tag.") - return (payloadLength, idx+1+(length & 0x7F)) + raise ValueError("Not a DER length tag (but still valid BER).") + return payloadLength - def decode(self, derEle, noLeftOvers=0): + def decode(self, derEle): """Decode a complete DER element, and re-initializes this object with it. - @param derEle A complete DER element. It must start with a DER T - tag. - @param noLeftOvers Indicate whether it is acceptable to complete the - parsing of the DER element and find that not all - bytes in derEle have been used. - @return Index of the first unused byte in the given DER element. - - Raises a ValueError exception in case of parsing errors. - Raises an IndexError exception if the DER element is too short. + :Parameters: + derEle : byte string + A complete DER element. + + :Raise ValueError: + In case of parsing errors. + :Raise EOFError: + If the DER element is too short. """ + + s = BytesIO_EOF(derEle) + self._decodeFromStream(s) + # There shouldn't be other bytes left try: - self.typeTag = bord(derEle[0]) - if (self.typeTag & 0x1F)==0x1F: - raise ValueError("Unsupported DER tag") - (length,idx) = self._decodeLen(1, derEle) - if noLeftOvers and len(derEle) != (idx+length): - raise ValueError("Not a DER structure") - self.payload = derEle[idx:idx+length] - except IndexError: - raise ValueError("Not a valid DER SEQUENCE.") - return idx+length + b = s.read_byte() + raise ValueError("Unexpected extra data after the DER structure") + except EOFError: + pass + + def _decodeFromStream(self, s): + """Decode a complete DER element from a file.""" + + try: + idOctet = bord(s.read_byte()) + except EOFError: + raise NoDerElementError + if self._idOctet != None: + if idOctet != self._idOctet: + raise ValueError("Unexpected DER tag") + else: + self._idOctet = idOctet + length = self._decodeLen(s) + self.payload = s.read(length) class DerInteger(DerObject): - def __init__(self, value = 0): - """Class to model an INTEGER DER element. + """Class to model a DER INTEGER. + + An example of encoding is: - Limitation: only non-negative values are supported. + >>> from Crypto.Util.asn1 import DerInteger + >>> from binascii import hexlify, unhexlify + >>> int_der = DerInteger(9) + >>> print hexlify(int_der.encode()) + + which will show ``020109``, the DER encoding of 9. + + And for decoding: + + >>> s = unhexlify(b'020109') + >>> try: + >>> int_der = DerInteger() + >>> int_der.decode(s) + >>> print int_der.value + >>> except (ValueError, EOFError): + >>> print "Not a valid DER INTEGER" + + the output will be ``9``. + """ + + def __init__(self, value=0, implicit=None): + """Initialize the DER object as an INTEGER. + + :Parameters: + value : integer + The value of the integer. + + implicit : integer + The IMPLICIT tag to use for the encoded object. + It overrides the universal tag for INTEGER (2). """ - DerObject.__init__(self, 'INTEGER') - self.value = value + + DerObject.__init__(self, 0x02, b(''), implicit, False) + self.value = value #: The integer value def encode(self): - """Return a complete INTEGER DER element, fully encoded as a TLV.""" - self.payload = long_to_bytes(self.value) - if bord(self.payload[0])>127: + """Return the DER INTEGER, fully encoded as a + binary string.""" + + number = self.value + self.payload = b('') + while True: + self.payload = bchr(number&255) + self.payload + if 128 <= number <= 255: self.payload = bchr(0x00) + self.payload + if -128 <= number <= 255: + break + number >>= 8 return DerObject.encode(self) - def decode(self, derEle, noLeftOvers=0): - """Decode a complete INTEGER DER element, and re-initializes this + def decode(self, derEle): + """Decode a complete DER INTEGER DER, and re-initializes this object with it. - @param derEle A complete INTEGER DER element. It must start with a DER - INTEGER tag. - @param noLeftOvers Indicate whether it is acceptable to complete the - parsing of the DER element and find that not all - bytes in derEle have been used. - @return Index of the first unused byte in the given DER element. - - Raises a ValueError exception if the DER element is not a - valid non-negative INTEGER. - Raises an IndexError exception if the DER element is too short. + :Parameters: + derEle : byte string + A complete INTEGER DER element. + + :Raise ValueError: + In case of parsing errors. + :Raise EOFError: + If the DER element is too short. """ - tlvLength = DerObject.decode(self, derEle, noLeftOvers) - if self.typeTag!=self.typeTags['INTEGER']: - raise ValueError ("Not a DER INTEGER.") - if bord(self.payload[0])>127: - raise ValueError ("Negative INTEGER.") - self.value = bytes_to_long(self.payload) - return tlvLength + DerObject.decode(self, derEle) + + def _decodeFromStream(self, s): + """Decode a complete DER INTEGER from a file.""" + + # Fill up self.payload + DerObject._decodeFromStream(self, s) + + # Derive self.value from self.payload + self.value = 0L + bits = 1 + for i in self.payload: + self.value *= 256 + self.value += bord(i) + bits <<= 8 + if self.payload and bord(self.payload[0]) & 0x80: + self.value -= bits + +def newDerInteger(number): + """Create a DerInteger object, already initialized with an integer.""" + + der = DerInteger(number) + return der class DerSequence(DerObject): - """Class to model a SEQUENCE DER element. + """Class to model a DER SEQUENCE. + + This object behaves like a dynamic Python sequence. - This object behave like a dynamic Python sequence. - Sub-elements that are INTEGERs, look like Python integers. - Any other sub-element is a binary string encoded as the complete DER + Sub-elements that are INTEGERs behave like Python integers. + + Any other sub-element is a binary string encoded as a complete DER sub-element (TLV). + + An example of encoding is: + + >>> from Crypto.Util.asn1 import DerSequence, DerInteger + >>> from binascii import hexlify, unhexlify + >>> obj_der = unhexlify('070102') + >>> seq_der = DerSequence([4]) + >>> seq_der.append(9) + >>> seq_der.append(obj_der.encode()) + >>> print hexlify(seq_der.encode()) + + which will show ``3009020104020109070102``, the DER encoding of the + sequence containing ``4``, ``9``, and the object with payload ``02``. + + For decoding: + + >>> s = unhexlify(b'3009020104020109070102') + >>> try: + >>> seq_der = DerSequence() + >>> seq_der.decode(s) + >>> print len(seq_der) + >>> print seq_der[0] + >>> print seq_der[:] + >>> except (ValueError, EOFError): + >>> print "Not a valid DER SEQUENCE" + + the output will be:: + + 3 + 4 + [4L, 9L, b'\x07\x01\x02'] + """ - def __init__(self, startSeq=None): - """Initialize the SEQUENCE DER object. Always empty - initially.""" - DerObject.__init__(self, 'SEQUENCE') + def __init__(self, startSeq=None, implicit=None): + """Initialize the DER object as a SEQUENCE. + + :Parameters: + startSeq : Python sequence + A sequence whose element are either integers or + other DER objects. + + implicit : integer + The IMPLICIT tag to use for the encoded object. + It overrides the universal tag for SEQUENCE (16). + """ + + DerObject.__init__(self, 0x10, b(''), implicit, True) if startSeq==None: self._seq = [] else: @@ -184,105 +383,517 @@ class DerSequence(DerObject): return self._seq[max(0, i):max(0, j)] def __len__(self): return len(self._seq) + def __iadd__(self, item): + self._seq.append(item) + return self def append(self, item): - return self._seq.append(item) - - def hasInts(self): - """Return the number of items in this sequence that are numbers.""" - return len(filter(isInt, self._seq)) - - def hasOnlyInts(self): - """Return True if all items in this sequence are numbers.""" - return self._seq and self.hasInts()==len(self._seq) + self._seq.append(item) + return self + + def hasInts(self, onlyNonNegative=True): + """Return the number of items in this sequence that are + integers. + + :Parameters: + onlyNonNegative : boolean + If True, negative integers are not counted in. + """ + def _isInt2(x): + return _isInt(x, onlyNonNegative) + return len(filter(_isInt2, self._seq)) + + def hasOnlyInts(self, onlyNonNegative=True): + """Return True if all items in this sequence are integers + or non-negative integers. + + This function returns False is the sequence is empty, + or at least one member is not an integer. + + :Parameters: + onlyNonNegative : boolean + If True, the presence of negative integers + causes the method to return False.""" + return self._seq and self.hasInts(onlyNonNegative)==len(self._seq) def encode(self): - """Return the DER encoding for the ASN.1 SEQUENCE, containing - the non-negative integers and longs added to this object. - - Limitation: Raises a ValueError exception if it some elements - in the sequence are neither Python integers nor complete DER INTEGERs. + """Return this DER SEQUENCE, fully encoded as a + binary string. + + :Raises ValueError: + If some elements in the sequence are neither integers + nor byte strings. """ self.payload = b('') for item in self._seq: + try: + self.payload += item + except TypeError: try: - self.payload += item - except: - try: - self.payload += DerInteger(item).encode() - except: - raise ValueError("Trying to DER encode an unknown object") + self.payload += DerInteger(item).encode() + except TypeError: + raise ValueError("Trying to DER encode an unknown object") return DerObject.encode(self) - def decode(self, derEle, noLeftOvers=0): - """Decode a complete SEQUENCE DER element, and re-initializes this + def decode(self, derEle): + """Decode a complete DER SEQUENCE, and re-initializes this object with it. - @param derEle A complete SEQUENCE DER element. It must start with a DER - SEQUENCE tag. - @param noLeftOvers Indicate whether it is acceptable to complete the - parsing of the DER element and find that not all - bytes in derEle have been used. - @return Index of the first unused byte in the given DER element. + :Parameters: + derEle : byte string + A complete SEQUENCE DER element. + + :Raise ValueError: + In case of parsing errors. + :Raise EOFError: + If the DER element is too short. DER INTEGERs are decoded into Python integers. Any other DER element is not decoded. Its validity is not checked. - - Raises a ValueError exception if the DER element is not a - valid DER SEQUENCE. - Raises an IndexError exception if the DER element is too short. """ + DerObject.decode(self, derEle) + def _decodeFromStream(self, s): + """Decode a complete DER SEQUENCE from a file.""" + self._seq = [] - try: - tlvLength = DerObject.decode(self, derEle, noLeftOvers) - if self.typeTag!=self.typeTags['SEQUENCE']: - raise ValueError("Not a DER SEQUENCE.") - # Scan one TLV at once - idx = 0 - while idx<len(self.payload): - typeTag = bord(self.payload[idx]) - if typeTag==self.typeTags['INTEGER']: - newInteger = DerInteger() - idx += newInteger.decode(self.payload[idx:]) - self._seq.append(newInteger.value) - else: - itemLen,itemIdx = self._decodeLen(idx+1,self.payload) - self._seq.append(self.payload[idx:itemIdx+itemLen]) - idx = itemIdx + itemLen - except IndexError: - raise ValueError("Not a valid DER SEQUENCE.") - return tlvLength + + # Fill up self.payload + DerObject._decodeFromStream(self, s) + + # Add one item at a time to self.seq, by scanning self.payload + p = BytesIO_EOF(self.payload) + while True: + try: + p.setRecord(True) + der = DerObject() + der._decodeFromStream(p) + + # Parse INTEGERs differently + if der._idOctet != 0x02: + self._seq.append(p._recording) + else: + derInt = DerInteger() + derInt.decode(p._recording) + self._seq.append(derInt.value) + + except NoDerElementError: + break + # end + +def newDerSequence(*der_objs): + """Create a DerSequence object, already initialized with all objects + passed as parameters.""" + + der = DerSequence() + for obj in der_objs: + if isinstance(obj, DerObject): + der += obj.encode() + else: + der += obj + return der class DerOctetString(DerObject): - def __init__(self, value = b('')): - DerObject.__init__(self, 'OCTET STRING') - self.payload = value - - def decode(self, derEle, noLeftOvers=0): - p = DerObject.decode(derEle, noLeftOvers) - if not self.isType("OCTET STRING"): - raise ValueError("Not a valid OCTET STRING.") - return p + """Class to model a DER OCTET STRING. + + An example of encoding is: + + >>> from Crypto.Util.asn1 import DerOctetString + >>> from binascii import hexlify, unhexlify + >>> os_der = DerOctetString(b'\\xaa') + >>> os_der.payload += b'\\xbb' + >>> print hexlify(os_der.encode()) + + which will show ``0402aabb``, the DER encoding for the byte string + ``b'\\xAA\\xBB'``. + + For decoding: + + >>> s = unhexlify(b'0402aabb') + >>> try: + >>> os_der = DerOctetString() + >>> os_der.decode(s) + >>> print hexlify(os_der.payload) + >>> except (ValueError, EOFError): + >>> print "Not a valid DER OCTET STRING" + + the output will be ``aabb``. + """ + + def __init__(self, value=b(''), implicit=None): + """Initialize the DER object as an OCTET STRING. + + :Parameters: + value : byte string + The initial payload of the object. + If not specified, the payload is empty. + + implicit : integer + The IMPLICIT tag to use for the encoded object. + It overrides the universal tag for OCTET STRING (4). + """ + DerObject.__init__(self, 0x04, value, implicit, False) + +def newDerOctetString(binstring): + """Create a DerOctetString object, already initialized with the binary + string.""" + + if isinstance(binstring, DerObject): + der = DerOctetString(binstring.encode()) + else: + der = DerOctetString(binstring) + return der class DerNull(DerObject): + """Class to model a DER NULL element.""" + def __init__(self): - DerObject.__init__(self, 'NULL') + """Initialize the DER object as a NULL.""" + + DerObject.__init__(self, 0x05, b(''), False) class DerObjectId(DerObject): - def __init__(self): - DerObject.__init__(self, 'OBJECT IDENTIFIER') + """Class to model a DER OBJECT ID. + + An example of encoding is: + + >>> from Crypto.Util.asn1 import DerObjectId + >>> from binascii import hexlify, unhexlify + >>> oid_der = DerObjectId("1.2") + >>> oid_der.value += ".840.113549.1.1.1" + >>> print hexlify(oid_der.encode()) + + which will show ``06092a864886f70d010101``, the DER encoding for the + RSA Object Identifier ``1.2.840.113549.1.1.1``. + + For decoding: + + >>> s = unhexlify(b'06092a864886f70d010101') + >>> try: + >>> oid_der = DerObjectId() + >>> oid_der.decode(s) + >>> print oid_der.value + >>> except (ValueError, EOFError): + >>> print "Not a valid DER OBJECT ID" + + the output will be ``1.2.840.113549.1.1.1``. + """ + + def __init__(self, value='', implicit=None): + """Initialize the DER object as an OBJECT ID. + + :Parameters: + value : string + The initial Object Identifier (e.g. "1.2.0.0.6.2"). + implicit : integer + The IMPLICIT tag to use for the encoded object. + It overrides the universal tag for OBJECT ID (6). + """ + DerObject.__init__(self, 0x06, b(''), implicit, False) + self.value = value #: The Object ID, a dot separated list of integers + + def encode(self): + """Return the DER OBJECT ID, fully encoded as a + binary string.""" + + comps = map(int,self.value.split(".")) + if len(comps)<2: + raise ValueError("Not a valid Object Identifier string") + self.payload = bchr(40*comps[0]+comps[1]) + for v in comps[2:]: + enc = [] + while v: + enc.insert(0, (v & 0x7F) | 0x80) + v >>= 7 + enc[-1] &= 0x7F + self.payload += b('').join(map(bchr, enc)) + return DerObject.encode(self) + + def decode(self, derEle): + """Decode a complete DER OBJECT ID, and re-initializes this + object with it. + + :Parameters: + derEle : byte string + A complete DER OBJECT ID. + + :Raise ValueError: + In case of parsing errors. + :Raise EOFError: + If the DER element is too short. + """ - def decode(self, derEle, noLeftOvers=0): - p = DerObject.decode(derEle, noLeftOvers) - if not self.isType("OBJECT IDENTIFIER"): - raise ValueError("Not a valid OBJECT IDENTIFIER.") - return p + DerObject.decode(self, derEle) + + def _decodeFromStream(self, s): + """Decode a complete DER OBJECT ID from a file.""" + + # Fill up self.payload + DerObject._decodeFromStream(self, s) + + # Derive self.value from self.payload + p = BytesIO_EOF(self.payload) + comps = list(map(str, divmod(bord(p.read_byte()),40))) + v = 0 + try: + while True: + c = p.read_byte() + v = v*128 + (bord(c) & 0x7F) + if not (bord(c) & 0x80): + comps.append(str(v)) + v = 0 + except EOFError: + pass + self.value = '.'.join(comps) + +def newDerObjectId(dottedstring): + """Create a DerObjectId object, already initialized with the given Object + Identifier (a dotted string).""" + + der = DerObjectId(dottedstring) + return der + +class DerBitString(DerObject): + """Class to model a DER BIT STRING. + + An example of encoding is: + + >>> from Crypto.Util.asn1 import DerBitString + >>> from binascii import hexlify, unhexlify + >>> bs_der = DerBitString(b'\\xaa') + >>> bs_der.value += b'\\xbb' + >>> print hexlify(bs_der.encode()) + + which will show ``040300aabb``, the DER encoding for the bit string + ``b'\\xAA\\xBB'``. + + For decoding: + + >>> s = unhexlify(b'040300aabb') + >>> try: + >>> bs_der = DerBitString() + >>> bs_der.decode(s) + >>> print hexlify(bs_der.value) + >>> except (ValueError, EOFError): + >>> print "Not a valid DER OCTET STRING" + + the output will be ``aabb``. + """ + + def __init__(self, value=b(''), implicit=None): + """Initialize the DER object as a BIT STRING. + + :Parameters: + value : byte string + The initial, packed bit string. + If not specified, the bit string is empty. + implicit : integer + The IMPLICIT tag to use for the encoded object. + It overrides the universal tag for OCTET STRING (3). + """ + DerObject.__init__(self, 0x03, b(''), implicit, False) + self.value = value #: The bitstring value (packed) + + def encode(self): + """Return the DER BIT STRING, fully encoded as a + binary string.""" + + # Add padding count byte + self.payload = b('\x00') + self.value + return DerObject.encode(self) + + def decode(self, derEle): + """Decode a complete DER BIT STRING, and re-initializes this + object with it. + + :Parameters: + derEle : byte string + A complete DER BIT STRING. + + :Raise ValueError: + In case of parsing errors. + :Raise EOFError: + If the DER element is too short. + """ -def isInt(x): - test = 0 - try: - test += x - except TypeError: - return 0 - return 1 + DerObject.decode(self, derEle) + + def _decodeFromStream(self, s): + """Decode a complete DER BIT STRING DER from a file.""" + + # Fill-up self.payload + DerObject._decodeFromStream(self, s) + + if self.payload and bord(self.payload[0])!=0: + raise ValueError("Not a valid BIT STRING") + + # Fill-up self.value + self.value = b('') + # Remove padding count byte + if self.payload: + self.value = self.payload[1:] + +def newDerBitString(binstring): + """Create a DerStringString object, already initialized with the binary + string.""" + + if isinstance(binstring, DerObject): + der = DerBitString(binstring.encode()) + else: + der = DerBitString(binstring) + return der + +class DerSetOf(DerObject): + """Class to model a DER SET OF. + + An example of encoding is: + + >>> from Crypto.Util.asn1 import DerBitString + >>> from binascii import hexlify, unhexlify + >>> so_der = DerSetOf([4,5]) + >>> so_der.add(6) + >>> print hexlify(so_der.encode()) + + which will show ``3109020104020105020106``, the DER encoding + of a SET OF with items 4,5, and 6. + + For decoding: + + >>> s = unhexlify(b'3109020104020105020106') + >>> try: + >>> so_der = DerSetOf() + >>> so_der.decode(s) + >>> print [x for x in so_der] + >>> except (ValueError, EOFError): + >>> print "Not a valid DER SET OF" + + the output will be ``[4L, 5L, 6L]``. + """ + + def __init__(self, startSet=None, implicit=None): + """Initialize the DER object as a SET OF. + + :Parameters: + startSet : container + The initial set of integers or DER encoded objects. + implicit : integer + The IMPLICIT tag to use for the encoded object. + It overrides the universal tag for SET OF (17). + """ + DerObject.__init__(self, 0x11, b(''), implicit, True) + self._seq = [] + self._elemOctet = None + if startSet: + for e in startSet: + self.add(e) + + def __getitem__(self, n): + return self._seq[n] + + def __iter__(self): + return iter(self._seq) + + def __len__(self): + return len(self._seq) + + def add(self, elem): + """Add an element to the set. + + :Parameters: + elem : byte string or integer + An element of the same type of objects already in the set. + It can be an integer or a DER encoded object. + """ + if _isInt(elem): + eo = 0x02 + else: + eo = bord(elem[0]) + if self._elemOctet != eo: + if self._elemOctet: + raise ValueError("New element does not belong to the set") + self._elemOctet = eo + if not elem in self._seq: + self._seq.append(elem) + + def decode(self, derEle): + """Decode a complete SET OF DER element, and re-initializes this + object with it. + + DER INTEGERs are decoded into Python integers. Any other DER + element is left undecoded; its validity is not checked. + + :Parameters: + derEle : byte string + A complete DER BIT SET OF. + + :Raise ValueError: + In case of parsing errors. + :Raise EOFError: + If the DER element is too short. + """ + + DerObject.decode(self, derEle) + + def _decodeFromStream(self, s): + """Decode a complete DER SET OF from a file.""" + + self._seq = [] + + # Fill up self.payload + DerObject._decodeFromStream(self, s) + + # Add one item at a time to self.seq, by scanning self.payload + p = BytesIO_EOF(self.payload) + setIdOctet = -1 + while True: + try: + p.setRecord(True) + der = DerObject() + der._decodeFromStream(p) + + # Verify that all members are of the same type + if setIdOctet < 0: + setIdOctet = der._idOctet + else: + if setIdOctet != der._idOctet: + raise ValueError("Not all elements are of the same DER type") + + # Parse INTEGERs differently + if setIdOctet != 0x02: + self._seq.append(p._recording) + else: + derInt = DerInteger() + derInt.decode(p._recording) + self._seq.append(derInt.value) + + except NoDerElementError: + break + # end + + def encode(self): + """Return this SET OF DER element, fully encoded as a + binary string. + """ + # Elements in the set must be ordered in lexicographic order + ordered = [] + for item in self._seq: + if _isInt(item): + bys = DerInteger(item).encode() + else: + bys = item + ordered.append(bys) + ordered.sort() + self.payload = b('').join(ordered) + return DerObject.encode(self) + +def newDerSetOf(*der_objs): + """Create a DerSequence object, already initialized with all objects + passed as parameters.""" + + der = DerSetOf() + for obj in der_objs: + if isinstance(obj, DerObject): + der.add(obj.encode()) + else: + der.add(obj) + return der diff --git a/lib/Crypto/Util/py3compat.py b/lib/Crypto/Util/py3compat.py index 34e5224..e2ca20f 100644 --- a/lib/Crypto/Util/py3compat.py +++ b/lib/Crypto/Util/py3compat.py @@ -83,6 +83,8 @@ if sys.version_info[0] == 2: return s.encode("latin-1") else: return ''.join(s) + # In Pyton 2.x, StringIO is a stand-alone module + from StringIO import StringIO as BytesIO else: def b(s): return s.encode("latin-1") # utf-8 would cause some side-effects we don't want @@ -103,5 +105,7 @@ else: return s.encode("latin-1") else: return bytes(s) - + # In Pyton 3.x, StringIO is a sub-module of io + from io import BytesIO + # vim:set ts=4 sw=4 sts=4 expandtab: |