Spaces:
Paused
Paused
| # -*- 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 | |
| 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) | |