140 lines
3.7 KiB
Python
140 lines
3.7 KiB
Python
# Author: Trevor Perrin
|
|
# Patch from Google adding getChildBytes()
|
|
#
|
|
# See the LICENSE file for legal information regarding use of this file.
|
|
|
|
"""Abstract Syntax Notation One (ASN.1) parsing"""
|
|
|
|
from .codec import Parser
|
|
|
|
|
|
class ASN1Type(object):
|
|
"""
|
|
Class that represents the ASN.1 type bit octet.
|
|
Consists of a class (universal(0), application(1), context-specific(2)
|
|
or private(3)), boolean value that indicates if a type is constructed or
|
|
primitive and the ASN1 type itself.
|
|
|
|
:vartype bytes: bytearray
|
|
:ivar field: bit octet
|
|
|
|
:vartype tagClass: int
|
|
:ivar tagClass: type's class
|
|
|
|
:vartype isPrimitive: int
|
|
:ivar isPrimitive: equals to 0 if the type is primitive, 1 if not
|
|
|
|
:vartype tagId: int
|
|
:ivar tagId: ANS1 tag number
|
|
"""
|
|
|
|
def __init__(self, tag_class, is_primitive, tag_id):
|
|
self.tag_class = tag_class
|
|
self.is_primitive = is_primitive
|
|
self.tag_id = tag_id
|
|
|
|
|
|
class ASN1Parser(object):
|
|
"""
|
|
Parser and storage of ASN.1 DER encoded objects.
|
|
|
|
:vartype length: int
|
|
:ivar length: length of the value of the tag
|
|
:vartype value: bytearray
|
|
:ivar value: literal value of the tag
|
|
"""
|
|
|
|
def __init__(self, bytes):
|
|
"""Create an object from bytes.
|
|
|
|
:type bytes: bytearray
|
|
:param bytes: DER encoded ASN.1 object
|
|
"""
|
|
p = Parser(bytes)
|
|
|
|
# Get Type
|
|
self.type = self._parse_type(p)
|
|
|
|
#Get Length
|
|
self.length = self._getASN1Length(p)
|
|
|
|
#Get Value
|
|
self.value = p.getFixBytes(self.length)
|
|
|
|
def getChild(self, which):
|
|
"""
|
|
Return n-th child assuming that the object is a SEQUENCE.
|
|
|
|
:type which: int
|
|
:param which: ordinal of the child to return
|
|
|
|
:rtype: ASN1Parser
|
|
:returns: decoded child object
|
|
"""
|
|
return ASN1Parser(self.getChildBytes(which))
|
|
|
|
def getChildCount(self):
|
|
"""
|
|
Return number of children, assuming that the object is a SEQUENCE.
|
|
|
|
:rtype: int
|
|
:returns: number of children in the object
|
|
"""
|
|
p = Parser(self.value)
|
|
count = 0
|
|
while True:
|
|
if p.getRemainingLength() == 0:
|
|
break
|
|
p.skip_bytes(1) # skip Type
|
|
length = self._getASN1Length(p)
|
|
p.skip_bytes(length) # skip value
|
|
count += 1
|
|
return count
|
|
|
|
def getChildBytes(self, which):
|
|
"""
|
|
Return raw encoding of n-th child, assume self is a SEQUENCE
|
|
|
|
:type which: int
|
|
:param which: ordinal of the child to return
|
|
|
|
:rtype: bytearray
|
|
:returns: raw child object
|
|
"""
|
|
p = Parser(self.value)
|
|
for _ in range(which+1):
|
|
markIndex = p.index
|
|
p.skip_bytes(1) # skip Type
|
|
length = self._getASN1Length(p)
|
|
p.skip_bytes(length)
|
|
return p.bytes[markIndex : p.index]
|
|
|
|
@staticmethod
|
|
def _getASN1Length(p):
|
|
"""Decode the ASN.1 DER length field"""
|
|
firstLength = p.get(1)
|
|
if firstLength <= 127:
|
|
return firstLength
|
|
else:
|
|
lengthLength = firstLength & 0x7F
|
|
return p.get(lengthLength)
|
|
|
|
@staticmethod
|
|
def _parse_type(parser):
|
|
"""Decode the ASN.1 DER type field"""
|
|
header = parser.get(1)
|
|
tag_class = (header & 0xc0) >> 6
|
|
tag_is_primitive = (header & 0x20) >> 5
|
|
tag_id = header & 0x1f
|
|
|
|
if tag_id == 0x1f:
|
|
tag_id = 0
|
|
while True:
|
|
value = parser.get(1)
|
|
tag_id += value & 0x7f
|
|
if not value & 0x80:
|
|
break
|
|
tag_id <<= 7
|
|
|
|
asn1type = ASN1Type(tag_class, tag_is_primitive, tag_id)
|
|
return asn1type
|