| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064 |
- # -*- coding: ascii -*-
- #
- # Util/asn1.py : Minimal support for ASN.1 DER binary encoding.
- #
- # ===================================================================
- # The contents of this file are dedicated to the public domain. To
- # the extent that dedication to the public domain is not available,
- # everyone is granted a worldwide, perpetual, royalty-free,
- # non-exclusive license to exercise all rights associated with the
- # contents of this file for any purpose whatsoever.
- # No rights are reserved.
- #
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- # SOFTWARE.
- # ===================================================================
- import struct
- from Crypto.Util.py3compat import byte_string, bchr, bord
- from Crypto.Util.number import long_to_bytes, bytes_to_long
- __all__ = ['DerObject', 'DerInteger', 'DerBoolean', 'DerOctetString',
- 'DerNull', 'DerSequence', 'DerObjectId', 'DerBitString', 'DerSetOf']
- # Useful references:
- # - https://luca.ntop.org/Teaching/Appunti/asn1.html
- # - https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/
- # - https://www.zytrax.com/tech/survival/asn1.html
- # - https://www.oss.com/asn1/resources/books-whitepapers-pubs/larmouth-asn1-book.pdf
- # - https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
- # - https://misc.daniel-marschall.de/asn.1/oid-converter/online.php
- def _is_number(x, only_non_negative=False):
- test = 0
- try:
- test = x + test
- except TypeError:
- return False
- return not only_non_negative or x >= 0
- class BytesIO_EOF(object):
- """This class differs from BytesIO in that a ValueError exception is
- raised whenever EOF is reached."""
- def __init__(self, initial_bytes):
- self._buffer = initial_bytes
- self._index = 0
- self._bookmark = None
- def set_bookmark(self):
- self._bookmark = self._index
- def data_since_bookmark(self):
- assert self._bookmark is not None
- return self._buffer[self._bookmark:self._index]
- def remaining_data(self):
- return len(self._buffer) - self._index
- def read(self, length):
- new_index = self._index + length
- if new_index > len(self._buffer):
- raise ValueError("Not enough data for DER decoding: expected %d bytes and found %d" % (new_index, len(self._buffer)))
- result = self._buffer[self._index:new_index]
- self._index = new_index
- return result
- def read_byte(self):
- return bord(self.read(1)[0])
- class DerObject(object):
- """Base class for defining a single DER object.
- This class should never be directly instantiated.
- """
- def __init__(self, asn1Id=None, payload=b'', implicit=None,
- constructed=False, explicit=None):
- """Initialize the DER object according to a specific ASN.1 type.
- :Parameters:
- asn1Id : integer or byte
- The universal DER tag number for this object
- (e.g. 0x10 for a SEQUENCE).
- If None, the tag is not known yet.
- payload : byte string
- The initial payload of the object (that it,
- the content octets).
- If not specified, the payload is empty.
- implicit : integer or byte
- The IMPLICIT tag number (< 0x1F) to use for the encoded object.
- It overrides the universal tag *asn1Id*.
- It cannot be combined with the ``explicit`` parameter.
- By default, there is no IMPLICIT tag.
- constructed : bool
- True when the ASN.1 type is *constructed*.
- False when it is *primitive* (default).
- explicit : integer or byte
- The EXPLICIT tag number (< 0x1F) to use for the encoded object.
- It cannot be combined with the ``implicit`` parameter.
- By default, there is no EXPLICIT tag.
- """
- if asn1Id is None:
- # The tag octet will be read in with ``decode``
- self._tag_octet = None
- return
- asn1Id = self._convertTag(asn1Id)
- self.payload = payload
- # In a BER/DER identifier octet:
- # * bits 4-0 contain the tag value
- # * bit 5 is set if the type is 'constructed'
- # 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/EXPLICIT)
- # private | 1 1
- #
- constructed_bit = 0x20 if constructed else 0x00
- if None not in (explicit, implicit):
- raise ValueError("Explicit and implicit tags are"
- " mutually exclusive")
- if implicit is not None:
- # IMPLICIT tag overrides asn1Id
- self._tag_octet = 0x80 | constructed_bit | self._convertTag(implicit)
- elif explicit is not None:
- # 'constructed bit' is always asserted for an EXPLICIT tag
- self._tag_octet = 0x80 | 0x20 | self._convertTag(explicit)
- self._inner_tag_octet = constructed_bit | asn1Id
- else:
- # Neither IMPLICIT nor EXPLICIT
- self._tag_octet = constructed_bit | asn1Id
- def _convertTag(self, tag):
- """Check if *tag* is a real DER tag (5 bits).
- Convert it from a character to number if necessary.
- """
- if not _is_number(tag):
- if len(tag) == 1:
- tag = bord(tag[0])
- # Ensure that tag is a low tag
- if not (_is_number(tag) and 0 <= tag < 0x1F):
- raise ValueError("Wrong DER tag")
- return tag
- @staticmethod
- def _definite_form(length):
- """Build length octets according to BER/DER
- definite form.
- """
- if length > 127:
- encoding = long_to_bytes(length)
- return bchr(len(encoding) + 128) + encoding
- return bchr(length)
- def encode(self):
- """Return this DER element, fully encoded as a binary byte string."""
- # Concatenate identifier octets, length octets,
- # and contents octets
- output_payload = self.payload
- # In case of an EXTERNAL tag, first encode the inner
- # element.
- if hasattr(self, "_inner_tag_octet"):
- output_payload = (bchr(self._inner_tag_octet) +
- self._definite_form(len(self.payload)) +
- self.payload)
- return (bchr(self._tag_octet) +
- self._definite_form(len(output_payload)) +
- output_payload)
- def _decodeLen(self, s):
- """Decode DER length octets from a file."""
- length = s.read_byte()
- if length > 127:
- encoded_length = s.read(length & 0x7F)
- if bord(encoded_length[0]) == 0:
- raise ValueError("Invalid DER: length has leading zero")
- length = bytes_to_long(encoded_length)
- if length <= 127:
- raise ValueError("Invalid DER: length in long form but smaller than 128")
- return length
- def decode(self, der_encoded, strict=False):
- """Decode a complete DER element, and re-initializes this
- object with it.
- Args:
- der_encoded (byte string): A complete DER element.
- Raises:
- ValueError: in case of parsing errors.
- """
- if not byte_string(der_encoded):
- raise ValueError("Input is not a byte string")
- s = BytesIO_EOF(der_encoded)
- self._decodeFromStream(s, strict)
- # There shouldn't be other bytes left
- if s.remaining_data() > 0:
- raise ValueError("Unexpected extra data after the DER structure")
- return self
- def _decodeFromStream(self, s, strict):
- """Decode a complete DER element from a file."""
- idOctet = s.read_byte()
- if self._tag_octet is not None:
- if idOctet != self._tag_octet:
- raise ValueError("Unexpected DER tag")
- else:
- self._tag_octet = idOctet
- length = self._decodeLen(s)
- self.payload = s.read(length)
- # In case of an EXTERNAL tag, further decode the inner
- # element.
- if hasattr(self, "_inner_tag_octet"):
- p = BytesIO_EOF(self.payload)
- inner_octet = p.read_byte()
- if inner_octet != self._inner_tag_octet:
- raise ValueError("Unexpected internal DER tag")
- length = self._decodeLen(p)
- self.payload = p.read(length)
- # There shouldn't be other bytes left
- if p.remaining_data() > 0:
- raise ValueError("Unexpected extra data after the DER structure")
- class DerInteger(DerObject):
- """Class to model a DER INTEGER.
- An example of encoding is::
- >>> 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:
- >>> print "Not a valid DER INTEGER"
- the output will be ``9``.
- :ivar value: The integer value
- :vartype value: integer
- """
- def __init__(self, value=0, implicit=None, explicit=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, 0x02, b'', implicit,
- False, explicit)
- self.value = value # The integer value
- def encode(self):
- """Return the DER INTEGER, fully encoded as a
- binary string."""
- number = self.value
- self.payload = b''
- while True:
- self.payload = bchr(int(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, der_encoded, strict=False):
- """Decode a DER-encoded INTEGER, and re-initializes this
- object with it.
- Args:
- der_encoded (byte string): A complete INTEGER DER element.
- Raises:
- ValueError: in case of parsing errors.
- """
- return DerObject.decode(self, der_encoded, strict=strict)
- def _decodeFromStream(self, s, strict):
- """Decode a complete DER INTEGER from a file."""
- # Fill up self.payload
- DerObject._decodeFromStream(self, s, strict)
- if strict:
- if len(self.payload) == 0:
- raise ValueError("Invalid encoding for DER INTEGER: empty payload")
- if len(self.payload) >= 2 and struct.unpack('>H', self.payload[:2])[0] < 0x80:
- raise ValueError("Invalid encoding for DER INTEGER: leading zero")
- # Derive self.value from self.payload
- self.value = 0
- 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
- class DerBoolean(DerObject):
- """Class to model a DER-encoded BOOLEAN.
- An example of encoding is::
- >>> from Crypto.Util.asn1 import DerBoolean
- >>> bool_der = DerBoolean(True)
- >>> print(bool_der.encode().hex())
- which will show ``0101ff``, the DER encoding of True.
- And for decoding::
- >>> s = bytes.fromhex('0101ff')
- >>> try:
- >>> bool_der = DerBoolean()
- >>> bool_der.decode(s)
- >>> print(bool_der.value)
- >>> except ValueError:
- >>> print "Not a valid DER BOOLEAN"
- the output will be ``True``.
- :ivar value: The boolean value
- :vartype value: boolean
- """
- def __init__(self, value=False, implicit=None, explicit=None):
- """Initialize the DER object as a BOOLEAN.
- Args:
- value (boolean):
- The value of the boolean. Default is False.
- implicit (integer or byte):
- The IMPLICIT tag number (< 0x1F) to use for the encoded object.
- It overrides the universal tag for BOOLEAN (1).
- It cannot be combined with the ``explicit`` parameter.
- By default, there is no IMPLICIT tag.
- explicit (integer or byte):
- The EXPLICIT tag number (< 0x1F) to use for the encoded object.
- It cannot be combined with the ``implicit`` parameter.
- By default, there is no EXPLICIT tag.
- """
- DerObject.__init__(self, 0x01, b'', implicit, False, explicit)
- self.value = value # The boolean value
- def encode(self):
- """Return the DER BOOLEAN, fully encoded as a binary string."""
- self.payload = b'\xFF' if self.value else b'\x00'
- return DerObject.encode(self)
- def decode(self, der_encoded, strict=False):
- """Decode a DER-encoded BOOLEAN, and re-initializes this object with it.
- Args:
- der_encoded (byte string): A DER-encoded BOOLEAN.
- Raises:
- ValueError: in case of parsing errors.
- """
- return DerObject.decode(self, der_encoded, strict)
- def _decodeFromStream(self, s, strict):
- """Decode a DER-encoded BOOLEAN from a file."""
- # Fill up self.payload
- DerObject._decodeFromStream(self, s, strict)
- if len(self.payload) != 1:
- raise ValueError("Invalid encoding for DER BOOLEAN: payload is not 1 byte")
- if bord(self.payload[0]) == 0:
- self.value = False
- elif bord(self.payload[0]) == 0xFF:
- self.value = True
- else:
- raise ValueError("Invalid payload for DER BOOLEAN")
- class DerSequence(DerObject):
- """Class to model a DER SEQUENCE.
- This object behaves like a dynamic Python sequence.
- 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:
- >>> print "Not a valid DER SEQUENCE"
- the output will be::
- 3
- 4
- [4, 9, b'\x07\x01\x02']
- """
- def __init__(self, startSeq=None, implicit=None, explicit=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 or byte
- The IMPLICIT tag number (< 0x1F) to use for the encoded object.
- It overrides the universal tag for SEQUENCE (16).
- It cannot be combined with the ``explicit`` parameter.
- By default, there is no IMPLICIT tag.
- explicit : integer or byte
- The EXPLICIT tag number (< 0x1F) to use for the encoded object.
- It cannot be combined with the ``implicit`` parameter.
- By default, there is no EXPLICIT tag.
- """
- DerObject.__init__(self, 0x10, b'', implicit, True, explicit)
- if startSeq is None:
- self._seq = []
- else:
- self._seq = startSeq
- # A few methods to make it behave like a python sequence
- def __delitem__(self, n):
- del self._seq[n]
- def __getitem__(self, n):
- return self._seq[n]
- def __setitem__(self, key, value):
- self._seq[key] = value
- def __setslice__(self, i, j, sequence):
- self._seq[i:j] = sequence
- def __delslice__(self, i, j):
- del self._seq[i:j]
- def __getslice__(self, i, j):
- 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):
- self._seq.append(item)
- return self
- def insert(self, index, item):
- self._seq.insert(index, item)
- return self
- def hasInts(self, only_non_negative=True):
- """Return the number of items in this sequence that are
- integers.
- Args:
- only_non_negative (boolean):
- If ``True``, negative integers are not counted in.
- """
- items = [x for x in self._seq if _is_number(x, only_non_negative)]
- return len(items)
- def hasOnlyInts(self, only_non_negative=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.
- Args:
- only_non_negative (boolean):
- If ``True``, the presence of negative integers
- causes the method to return ``False``."""
- return self._seq and self.hasInts(only_non_negative) == len(self._seq)
- def encode(self):
- """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:
- if byte_string(item):
- self.payload += item
- elif _is_number(item):
- self.payload += DerInteger(item).encode()
- else:
- self.payload += item.encode()
- return DerObject.encode(self)
- def decode(self, der_encoded, strict=False, nr_elements=None, only_ints_expected=False):
- """Decode a complete DER SEQUENCE, and re-initializes this
- object with it.
- Args:
- der_encoded (byte string):
- A complete SEQUENCE DER element.
- nr_elements (None or integer or list of integers):
- The number of members the SEQUENCE can have
- only_ints_expected (boolean):
- Whether the SEQUENCE is expected to contain only integers.
- strict (boolean):
- Whether decoding must check for strict DER compliancy.
- Raises:
- ValueError: in case of parsing errors.
- DER INTEGERs are decoded into Python integers. Any other DER
- element is not decoded. Its validity is not checked.
- """
- self._nr_elements = nr_elements
- result = DerObject.decode(self, der_encoded, strict=strict)
- if only_ints_expected and not self.hasOnlyInts():
- raise ValueError("Some members are not INTEGERs")
- return result
- def _decodeFromStream(self, s, strict):
- """Decode a complete DER SEQUENCE from a file."""
- self._seq = []
- # Fill up self.payload
- DerObject._decodeFromStream(self, s, strict)
- # Add one item at a time to self.seq, by scanning self.payload
- p = BytesIO_EOF(self.payload)
- while p.remaining_data() > 0:
- p.set_bookmark()
- der = DerObject()
- der._decodeFromStream(p, strict)
- # Parse INTEGERs differently
- if der._tag_octet != 0x02:
- self._seq.append(p.data_since_bookmark())
- else:
- derInt = DerInteger()
- data = p.data_since_bookmark()
- derInt.decode(data, strict=strict)
- self._seq.append(derInt.value)
- ok = True
- if self._nr_elements is not None:
- try:
- ok = len(self._seq) in self._nr_elements
- except TypeError:
- ok = len(self._seq) == self._nr_elements
- if not ok:
- raise ValueError("Unexpected number of members (%d)"
- " in the sequence" % len(self._seq))
- class DerOctetString(DerObject):
- """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:
- >>> print "Not a valid DER OCTET STRING"
- the output will be ``aabb``.
- :ivar payload: The content of the string
- :vartype payload: byte string
- """
- 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)
- class DerNull(DerObject):
- """Class to model a DER NULL element."""
- def __init__(self):
- """Initialize the DER object as a NULL."""
- DerObject.__init__(self, 0x05, b'', None, False)
- class DerObjectId(DerObject):
- """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:
- >>> print "Not a valid DER OBJECT ID"
- the output will be ``1.2.840.113549.1.1.1``.
- :ivar value: The Object ID (OID), a dot separated list of integers
- :vartype value: string
- """
- def __init__(self, value='', implicit=None, explicit=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).
- explicit : integer
- The EXPLICIT tag to use for the encoded object.
- """
- DerObject.__init__(self, 0x06, b'', implicit, False, explicit)
- self.value = value
- def encode(self):
- """Return the DER OBJECT ID, fully encoded as a
- binary string."""
- comps = [int(x) for x in self.value.split(".")]
- if len(comps) < 2:
- raise ValueError("Not a valid Object Identifier string")
- if comps[0] > 2:
- raise ValueError("First component must be 0, 1 or 2")
- if comps[0] < 2 and comps[1] > 39:
- raise ValueError("Second component must be 39 at most")
- subcomps = [40 * comps[0] + comps[1]] + comps[2:]
- encoding = []
- for v in reversed(subcomps):
- encoding.append(v & 0x7F)
- v >>= 7
- while v:
- encoding.append((v & 0x7F) | 0x80)
- v >>= 7
- self.payload = b''.join([bchr(x) for x in reversed(encoding)])
- return DerObject.encode(self)
- def decode(self, der_encoded, strict=False):
- """Decode a complete DER OBJECT ID, and re-initializes this
- object with it.
- Args:
- der_encoded (byte string):
- A complete DER OBJECT ID.
- strict (boolean):
- Whether decoding must check for strict DER compliancy.
- Raises:
- ValueError: in case of parsing errors.
- """
- return DerObject.decode(self, der_encoded, strict)
- def _decodeFromStream(self, s, strict):
- """Decode a complete DER OBJECT ID from a file."""
- # Fill up self.payload
- DerObject._decodeFromStream(self, s, strict)
- # Derive self.value from self.payload
- p = BytesIO_EOF(self.payload)
- subcomps = []
- v = 0
- while p.remaining_data():
- c = p.read_byte()
- v = (v << 7) + (c & 0x7F)
- if not (c & 0x80):
- subcomps.append(v)
- v = 0
- if len(subcomps) == 0:
- raise ValueError("Empty payload")
- if subcomps[0] < 40:
- subcomps[:1] = [0, subcomps[0]]
- elif subcomps[0] < 80:
- subcomps[:1] = [1, subcomps[0] - 40]
- else:
- subcomps[:1] = [2, subcomps[0] - 80]
- self.value = ".".join([str(x) for x in subcomps])
- class DerBitString(DerObject):
- """Class to model a DER BIT STRING.
- An example of encoding is:
- >>> from Crypto.Util.asn1 import DerBitString
- >>> bs_der = DerBitString(b'\\xAA')
- >>> bs_der.value += b'\\xBB'
- >>> print(bs_der.encode().hex())
- which will show ``030300aabb``, the DER encoding for the bit string
- ``b'\\xAA\\xBB'``.
- For decoding:
- >>> s = bytes.fromhex('030300aabb')
- >>> try:
- >>> bs_der = DerBitString()
- >>> bs_der.decode(s)
- >>> print(bs_der.value.hex())
- >>> except ValueError:
- >>> print "Not a valid DER BIT STRING"
- the output will be ``aabb``.
- :ivar value: The content of the string
- :vartype value: byte string
- """
- def __init__(self, value=b'', implicit=None, explicit=None):
- """Initialize the DER object as a BIT STRING.
- :Parameters:
- value : byte string or DER object
- 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 BIT STRING (3).
- explicit : integer
- The EXPLICIT tag to use for the encoded object.
- """
- DerObject.__init__(self, 0x03, b'', implicit, False, explicit)
- # The bitstring value (packed)
- if isinstance(value, DerObject):
- self.value = value.encode()
- else:
- self.value = value
- def encode(self):
- """Return the DER BIT STRING, fully encoded as a
- byte string."""
- # Add padding count byte
- self.payload = b'\x00' + self.value
- return DerObject.encode(self)
- def decode(self, der_encoded, strict=False):
- """Decode a complete DER BIT STRING, and re-initializes this
- object with it.
- Args:
- der_encoded (byte string): a complete DER BIT STRING.
- strict (boolean):
- Whether decoding must check for strict DER compliancy.
- Raises:
- ValueError: in case of parsing errors.
- """
- return DerObject.decode(self, der_encoded, strict)
- def _decodeFromStream(self, s, strict):
- """Decode a complete DER BIT STRING DER from a file."""
- # Fill-up self.payload
- DerObject._decodeFromStream(self, s, strict)
- 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:]
- 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:
- >>> print "Not a valid DER SET OF"
- the output will be ``[4, 5, 6]``.
- """
- 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 = []
- # All elements must be of the same type (and therefore have the
- # same leading octet)
- 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.
- Args:
- 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 _is_number(elem):
- eo = 0x02
- elif isinstance(elem, DerObject):
- eo = self._tag_octet
- else:
- eo = bord(elem[0])
- if self._elemOctet != eo:
- if self._elemOctet is not None:
- raise ValueError("New element does not belong to the set")
- self._elemOctet = eo
- if elem not in self._seq:
- self._seq.append(elem)
- def decode(self, der_encoded, strict=False):
- """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.
- Args:
- der_encoded (byte string): a complete DER BIT SET OF.
- strict (boolean):
- Whether decoding must check for strict DER compliancy.
- Raises:
- ValueError: in case of parsing errors.
- """
- return DerObject.decode(self, der_encoded, strict)
- def _decodeFromStream(self, s, strict):
- """Decode a complete DER SET OF from a file."""
- self._seq = []
- # Fill up self.payload
- DerObject._decodeFromStream(self, s, strict)
- # Add one item at a time to self.seq, by scanning self.payload
- p = BytesIO_EOF(self.payload)
- setIdOctet = -1
- while p.remaining_data() > 0:
- p.set_bookmark()
- der = DerObject()
- der._decodeFromStream(p, strict)
- # Verify that all members are of the same type
- if setIdOctet < 0:
- setIdOctet = der._tag_octet
- else:
- if setIdOctet != der._tag_octet:
- raise ValueError("Not all elements are of the same DER type")
- # Parse INTEGERs differently
- if setIdOctet != 0x02:
- self._seq.append(p.data_since_bookmark())
- else:
- derInt = DerInteger()
- derInt.decode(p.data_since_bookmark(), strict)
- self._seq.append(derInt.value)
- # 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 _is_number(item):
- bys = DerInteger(item).encode()
- elif isinstance(item, DerObject):
- bys = item.encode()
- else:
- bys = item
- ordered.append(bys)
- ordered.sort()
- self.payload = b''.join(ordered)
- return DerObject.encode(self)
|