summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLegrandin <gooksankoo@hoiptorrow.mailexpire.com>2012-06-05 19:58:19 +0200
committerDwayne Litzenberger <dlitz@dlitz.net>2013-07-14 21:16:46 -0700
commit5a0ee14e9904335cb90c0dd7a4e10f1523435c52 (patch)
treee4f4c2e8b07b6bccb955912bb3e8a302e5ccaf0a
parentaf9b41cc4b0a58dd87f56e334a8d478f238f074d (diff)
downloadpycrypto-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.py848
-rw-r--r--lib/Crypto/Util/asn1.py945
-rw-r--r--lib/Crypto/Util/py3compat.py6
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: