@@ -6,6 +6,7 @@ Future
* c++-lib: IOManipulator based encoding/decoding
* documentation: Developer's guides
* esnacc-logo: A mascot (and CC-BY license) was added for esnacc
+* py-lib: Introduce a first cut at a python back-end
1.80
new file mode 100644
@@ -0,0 +1 @@
+*.pyc
new file mode 100644
@@ -0,0 +1,7 @@
+# Copyright (C) 2016, Aaron Conole <aconole@bytheb.org>
+#
+# Licensed under the terms of the GNU General Public License
+# agreement (version 2 or greater). Alternately, may be licensed
+# under the terms of the ESNACC Public License.
+#
+# This file intentionally blank...
new file mode 100644
@@ -0,0 +1,189 @@
+# Copyright (C) 2016, Aaron Conole <aconole@bytheb.org>
+#
+# Licensed under the terms of the GNU General Public License
+# agreement (version 2 or greater). Alternately, may be licensed
+# under the terms of the ESNACC Public License.
+#
+# ASN.1 Base type for eSNACC
+
+from asn_buffer import AsnBuf
+from asn_buffer import BDecDefLen
+from asn_buffer import BEncDefLen
+
+class Constraint(object):
+ def __init__(self):
+ pass
+
+ def withinConstraint(self, value):
+ raise NotImplementedError
+
+
+class BERConsts(object):
+ BER_ANY_CLASS = -2
+ BER_NULL_CLASS = -1
+ BER_UNIVERSAL_CLASS = 0
+ BER_APPLICATION_CLASS = 1 << 6
+ BER_CONTEXTUAL_CLASS = 2 << 6
+ BER_PRIVATE_CLASS = 3 << 6
+
+ UNIV = BER_UNIVERSAL_CLASS
+ APPL = BER_APPLICATION_CLASS
+ CNTX = BER_CONTEXTUAL_CLASS
+ PRIV = BER_PRIVATE_CLASS
+
+ BER_ANY_FORM = -2
+ BER_NULL_FORM = -1
+ BER_PRIMITIVE_FORM = 0
+ BER_CONSTRUCTED_FORM = 1 << 5
+
+ PRIM = BER_PRIMITIVE_FORM
+ CONS = BER_CONSTRUCTED_FORM
+
+ @staticmethod
+ def MakeTag(cls, frm, tag):
+ fnl = cls & 0xff
+ fnl |= (frm & 0xff)
+
+ def intcnv(f):
+ return chr(int(f))
+
+ if tag < 31:
+ fnl |= tag & 0xff
+ fnl = chr(fnl)
+ else:
+ tg = intcnv(tag & 0x7f)
+ tag >>= 7
+ while tag:
+ tg = intcnv(0x80|(tag & 0x7f)) + tg
+ tag >>= 7
+ fnl = intcnv(fnl|0x1f) + tg
+
+ return int(fnl.encode('hex'), 16)
+
+ NO_TAG_CODE = 0
+ BOOLEAN_TAG_CODE = 1
+ INTEGER_TAG_CODE = 2
+ BITSTRING_TAG_CODE = 3
+ OCTETSTRING_TAG_CODE = 4
+ NULLTYPE_TAG_CODE = 5
+ OID_TAG_CODE = 6
+ OD_TAG_CODE = 7
+ EXTERNAL_TAG_CODE = 8
+ REAL_TAG_CODE = 9
+ ENUM_TAG_CODE = 10
+ UTF8STRING_TAG_CODE = 12
+ RELATIVE_OID_TAG_CODE = 13
+ SEQ_TAG_CODE = 16
+ SET_TAG_CODE = 17
+ NUMERICSTRING_TAG_CODE = 18
+ PRINTABLESTRING_TAG_CODE = 19
+ TELETEXSTRING_TAG_CODE = 20
+ VIDEOTEXSTRING_TAG_CODE = 21
+ IA5STRING_TAG_CODE = 22
+ UTCTIME_TAG_CODE = 23
+ GENERALIZEDTIME_TAG_CODE = 24
+ GRAPHICSTRING_TAG_CODE = 25
+ VISIBLESTRING_TAG_CODE = 26
+ GENERALSTRING_TAG_CODE = 27
+ UNIVERSALSTRING_TAG_CODE = 28
+ BMPSTRING_TAG_CODE = 30
+
+
+class AsnBase(object):
+
+ BER_CLASS = BERConsts.BER_UNIVERSAL_CLASS
+ BER_FORM = BERConsts.BER_PRIMITIVE_FORM
+
+ def __init__(self):
+ pass
+
+ def typename(self):
+ raise NotImplementedError
+
+ def passesAllConstraints(self, constraints):
+ return True
+
+ def addConstraint(self, constraint):
+ try:
+ self.constraints.append(constraint)
+ except AttributeError:
+ self.constraints = []
+ self.constraints.append(constraint)
+
+ def BEncContent(self, asnbuf):
+ raise NotImplementedError
+
+ def BDecContent(self, asnbuf, contentlen):
+ raise NotImplementedError
+
+ def BEnc(self, asnbuf):
+ try:
+ if len(self.constraints) > 0 and \
+ not self.passesAllConstraints(self.constraints):
+ raise ValueError("Not all constraints passed")
+ except AttributeError:
+ pass
+ except TypeError:
+ pass
+
+ newbuf = AsnBuf()
+ asnlen = self.BEncContent(newbuf)
+ asnlen += BEncDefLen(newbuf, asnlen)
+ TAG_CODE = BERConsts.MakeTag(self.BER_CLASS & 0xff,
+ self.BER_FORM & 0xff,
+ self.BER_TAG & 0xff)
+ asnlen += newbuf.PutBufReverse(TAG_CODE)
+ asnbuf.PutBuf(newbuf.buf)
+ return asnlen
+
+ def BDecTag(self, asnbuf, asnlen):
+ al = 1
+ bufTag = ord(asnbuf.Buffer()[0])
+ lastTag = bufTag
+
+ if (lastTag & 0x1f) != 0x1f:
+ return bufTag, al
+
+ lastTag = ord(asnbuf.Buffer()[al])
+ while lastTag & 0x80:
+ lastTag = ord(asnbuf.Buffer()[al])
+ bufTag <<= 8
+ bufTag |= lastTag
+ al += 1
+ return bufTag, al
+
+ def BDec(self, asnbuf, asnlen):
+ bufTag, al = self.BDecTag(asnbuf, asnlen)
+
+ PRIM_TAG = BERConsts.MakeTag(self.BER_CLASS & 0xff,
+ BERConsts.BER_PRIMITIVE_FORM & 0xff,
+ self.BER_TAG & 0xff)
+ CONS_TAG = BERConsts.MakeTag(self.BER_CLASS & 0xff,
+ BERConsts.BER_CONSTRUCTED_FORM & 0xff,
+ self.BER_TAG & 0xff)
+
+ if bufTag != PRIM_TAG and bufTag != CONS_TAG:
+ raise IOError("Invalid bytes 0x%x expected [0x%x|0x%x]" %
+ (bufTag, PRIM_TAG, CONS_TAG))
+
+ asnbuf.swallow(al)
+ asnlen += al
+ tag_total_len, totalBytesLength = BDecDefLen(asnbuf)
+ asnlen += tag_total_len
+ asnlen += totalBytesLength
+ self.BDecContent(asnbuf, totalBytesLength)
+ return asnlen
+
+ def BEncPdu(self, asnbuf, asnlen):
+ try:
+ asnlen = self.BEnc(asnbuf)
+ except Exception:
+ return False
+ return True, asnlen
+
+ def BDecPdu(self, asnbuf, asnlen):
+ try:
+ asnlen = self.BDec(asnbuf, asnlen)
+ except Exception:
+ return False
+ return True, asnlen
new file mode 100644
@@ -0,0 +1,53 @@
+# Copyright (C) 2016, Aaron Conole <aconole@bytheb.org>
+#
+# Licensed under the terms of the GNU General Public License
+# agreement (version 2 or greater). Alternately, may be licensed
+# under the terms of the ESNACC Public License.
+#
+# ASN.1 Boolean for eSNACC
+
+from asn_base import BERConsts
+from asn_ints import AsnInt
+
+def convert_to_bool(value):
+ if value is None:
+ return False
+
+ if isinstance(value, int) or isinstance(value, float):
+ if value == 0:
+ return False
+ return True
+
+ if isinstance(value, basestring):
+ if value.lower() != "true":
+ return False
+ return True
+
+ return False
+
+class AsnBool(AsnInt):
+
+ BER_TAG = BERConsts.BOOLEAN_TAG_CODE
+
+ def __init__(self, value=None):
+ self.value(value)
+
+ def value(self, newval=None):
+ if convert_to_bool(newval):
+ self.intVal = 0xff
+ else:
+ self.intVal = 0
+ return convert_to_bool(newval)
+
+ def typename(self):
+ return "AsnBool"
+
+ def __int__(self):
+ if convert_to_bool(self.value()):
+ return 0xff
+ return 0
+
+ def __str__(self):
+ if convert_to_bool(self.value()):
+ return 'True'
+ return 'False'
new file mode 100644
@@ -0,0 +1,124 @@
+# Copyright (C) 2016, Aaron Conole <aconole@bytheb.org>
+#
+# Licensed under the terms of the GNU General Public License
+# agreement (version 2 or greater). Alternately, may be licensed
+# under the terms of the ESNACC Public License.
+#
+# ASN.1 Buffer library for eSNACC
+
+
+def ints2octs(listOfInts):
+ return [chr(int(x)) for x in listOfInts]
+
+
+def BEncDefLen(asnbuf, length):
+ if length < 128:
+ asnbuf.PutBufReverse(chr(length & 0xff))
+ return 1
+ elif length < 256:
+ asnbuf.PutBufReverse(chr(length & 0xff))
+ asnbuf.PutBufReverse(chr(0x81))
+ return 2
+ elif length < 65536:
+ asnbuf.PutBufReverse(chr(length & 0xff))
+ asnbuf.PutBufReverse(chr((length & 0xff00) >> 8))
+ asnbuf.PutBufReverse(chr(0x82))
+ return 3
+ elif length < 16777126:
+ asnbuf.PutBufReverse(chr(length & 0xff))
+ asnbuf.PutBufReverse(chr((length & 0xff00) >> 8))
+ asnbuf.PutBufReverse(chr((length & 0xff0000) >> 16))
+ asnbuf.PutBufReverse(chr(0x83))
+ return 4
+ else:
+ asnbuf.PutBufReverse(chr(length & 0xff))
+ asnbuf.PutBufReverse(chr((length & 0xff00) >> 8))
+ asnbuf.PutBufReverse(chr((length & 0xff0000) >> 16))
+ asnbuf.PutBufReverse(chr((length & 0xff000000) >> 24))
+ asnbuf.PutBufReverse(chr(0x84))
+ return 5
+
+
+def BDecDefLen(asnbuf):
+ lenByte = ord(asnbuf.GetByte())
+ if lenByte < 128:
+ return 1, lenByte
+ elif lenByte == 0x80:
+ raise IOError("INDEFINITE length not supported")
+
+ bytesTotal = lenByte & 0x7f
+ lenByte = 0
+ for b in range(bytesTotal):
+ lenByte = lenByte << 8
+ lenByte += ord(asnbuf.GetByte())
+ return bytesTotal, lenByte
+
+
+class AsnBuf(object):
+
+ def __init__(self, bufbytes=None):
+ if bufbytes is None:
+ self.buf = []
+ else:
+ if isinstance(bufbytes, basestring):
+ self.buf = list(bufbytes)
+ elif isinstance(bufbytes, file):
+ self.buf = list(bufbytes.read())
+ else:
+ self.buf = bufbytes
+
+ def __len__(self):
+ return len(self.buf)
+
+ def PutBufReverse(self, listOf):
+ length = 0
+ if isinstance(listOf, list):
+ for x in reversed(listOf):
+ length += self.PutBufReverse(x)
+ else:
+ self.buf.insert(0, listOf)
+ length = 1
+ return length
+
+ def PutBufIntsReverse(self, listOfInts):
+ return self.PutBufReverse(ints2octs(listOfInts))
+
+ def PutBuf(self, listOf):
+ length = 0
+ if isinstance(listOf, list):
+ for x in listOf:
+ length += self.PutBuf(x)
+ else:
+ self.buf.append(listOf)
+ length = 1
+ return length
+
+ def PutBufInts(self, listOfInts):
+ return self.PutBuf(ints2octs(listOfInts))
+
+ def Buffer(self):
+ return self.buf
+
+ def swallow(self, num):
+ if len(self.buf) < num:
+ raise ValueError("Too many bytes to swallow")
+
+ for x in range(num):
+ del self.buf[0]
+
+ def GetByte(self):
+ ret = self.buf[0]
+ self.swallow(1)
+ return ret
+
+ def GetSeg(self, length):
+ ret = []
+
+ if len(self.buf) < length:
+ raise ValueError("Too many bytes specified")
+
+ for x in range(length):
+ ret.append(self.buf[0])
+ del self.buf[0]
+
+ return ret
new file mode 100644
@@ -0,0 +1,356 @@
+# Copyright (C) 2016, Aaron Conole <aconole@bytheb.org>
+#
+# Licensed under the terms of the GNU General Public License
+# agreement (version 2 or greater). Alternately, may be licensed
+# under the terms of the ESNACC Public License.
+#
+# ASN.1 Integer Types for eSNACC
+
+from asn_base import AsnBase
+from asn_base import Constraint
+from asn_base import BERConsts
+from asn_buffer import AsnBuf
+import math
+
+
+class IntegerConstraint(Constraint):
+ def __init__(self, lowerBound=None, upperBound=None):
+ self.lower = lowerBound
+ self.upper = upperBound
+
+ def withinConstraint(self, value):
+ if self.lower is not None and value is not None and value < self.lower:
+ return False
+ if self.upper is not None and value is not None and value > self.upper:
+ return False
+ return True
+
+
+def isfundamental_int_type(value):
+ if isinstance(value, int) or isinstance(value, float) or \
+ isinstance(value, basestring):
+ return True
+ return False
+
+
+class AsnInt(AsnBase):
+
+ BER_TAG = BERConsts.INTEGER_TAG_CODE
+
+ def __init__(self, value=None):
+ self.constraints = None
+ if value is None:
+ self.intVal = 0
+ elif isfundamental_int_type(value):
+ self.intVal = int(value)
+ elif isinstance(value, AsnInt):
+ self.intVal = value.intVal
+ else:
+ raise ValueError("AsnInt - bad value")
+
+ def __bool__(self):
+ if self.intVal == 0:
+ return False
+ return True
+
+ __nonzero__=__bool__
+
+ def __cmp__(self, obj):
+ cmpValA = self.intVal
+ if isinstance(obj, int):
+ cmpValB = obj
+ elif isinstance(obj, AsnInt):
+ cmpValB = obj.intVal
+ elif isinstance(obj, basestring) or isinstance(obj, float):
+ cmpValB = int(obj)
+ else:
+ raise TypeError("Compare with int, or AsnInt")
+ return cmp(cmpValA, cmpValB)
+
+ def value(self, newval=None):
+ if newval is not None:
+ if isinstance(newval, int) or isinstance(newval, float) or \
+ isinstance(newval, basestring):
+ self.intVal = int(newval)
+ elif isinstance(newval, AsnInt):
+ self.intVal = newval.intVal
+ return self.intVal
+
+ def __int__(self):
+ return self.value()
+
+ def __float__(self):
+ return float(self.intVal)
+
+ def __add__(self, obj):
+ if isinstance(obj, AsnInt):
+ return self.intVal + obj.intVal
+ return self.intVal + int(obj)
+
+ def __iadd__(self, obj):
+ if isinstance(obj, AsnInt):
+ self.intVal += obj.intVal
+ else:
+ self.intVal += int(obj)
+ return self
+
+ def __sub__(self, obj):
+ if isinstance(obj, AsnInt):
+ return self.intVal - obj.intVal
+ return self.intVal - int(obj)
+
+ def __isub__(self, obj):
+ if isinstance(obj, AsnInt):
+ self.intVal -= obj.intVal
+ else:
+ self.intVal -= int(obj)
+ return self
+
+ def __mul__(self, obj):
+ if isinstance(obj, AsnInt):
+ return self.intVal * obj.intVal
+ return self.intVal * obj
+
+ def __imul__(self, obj):
+ if isinstance(obj, AsnInt):
+ self.intVal *= obj.intVal
+ else:
+ self.intVal *= obj
+ return self
+
+ def __str__(self):
+ return str(self.value())
+
+ def typename(self):
+ return "AsnInt"
+
+ def passesAllConstraints(self, constraints):
+ if constraints is None:
+ return True
+ if isinstance(constraints, IntegerConstraint):
+ return constraints.withinConstraint(self.intVal)
+ elif isinstance(constraints, list):
+ for x in constraints:
+ if not self.passesAllConstraints(x):
+ return False
+ return True
+
+ def BEncContent(self, asnbuf):
+ if not isinstance(asnbuf, AsnBuf):
+ raise ValueError("Buffer must be esnacc.asn_buf.AsnBuf")
+
+ octets = []
+ value = int(self)
+ while 1:
+ octets.insert(0, value & 0xff)
+ if value == 0 or value == -1:
+ break
+ value = value >> 8
+ if value == 0 and octets[0] & 0x80:
+ octets.insert(0, 0)
+ while len(octets) > 1 and \
+ (octets[0] == 0 and octets[1] & 0x80 == 0 or
+ octets[0] == 0xff and octets[1] & 0x80 != 0):
+ del octets[0]
+
+ asnbuf.PutBufIntsReverse(octets)
+ return len(octets)
+
+ def BDecContent(self, asnbuf, intlen):
+ buf = ord(asnbuf.Buffer()[0])
+ result = 0
+ if buf & 0x80:
+ result = -1
+
+ for x in range(intlen):
+ result <<= 8
+ result |= ord(asnbuf.GetByte())
+ self.intVal = result
+ return intlen
+
+
+class EnumeratedConstraint(Constraint):
+ def __init__(self, listOfEnumerations):
+ self.enums = listOfEnumerations
+
+ def withinConstraint(self, value):
+ if value not in self.enums:
+ return False
+ return True
+
+
+class AsnEnum(AsnInt):
+ BER_TAG = BERConsts.ENUM_TAG_CODE
+
+ def __init__(self, value=None):
+ self.constraints = None
+ if value is not None:
+ self.intVal = value
+
+ def typename(self):
+ return "AsnEnum"
+
+ def passesAllConstraints(self, constraints):
+ if constraints is None:
+ return True
+ if isinstance(constraints, IntegerConstraint) \
+ or isinstance(constraints, EnumeratedConstraint):
+ return constraints.withinConstraint(self.intVal)
+ elif isinstance(constraints, list):
+ for x in constraints:
+ if not self.passesAllConstraints(x):
+ return False
+ return True
+
+
+class AsnReal(AsnBase):
+
+ BER_TAG = BERConsts.REAL_TAG_CODE
+
+ def __bool__(self):
+ if self.floatVal == 0:
+ return False
+ return True
+
+ def __int__(self):
+ return int(self.value())
+
+ def __float__(self):
+ return self.value()
+
+ def __add__(self, obj):
+ if isinstance(obj, AsnReal):
+ return self.floatVal + obj.floatVal
+ return self.floatVal + float(obj)
+
+ def __iadd__(self, obj):
+ if isinstance(obj, AsnReal):
+ self.floatVal += obj.floatVal
+ else:
+ self.floatVal += float(obj)
+ return self
+
+ def __sub__(self, obj):
+ if isinstance(obj, AsnReal):
+ return self.floatVal - obj.floatVal
+ return self.floatVal - float(obj)
+
+ def __isub__(self, obj):
+ if isinstance(obj, AsnReal):
+ self.floatVal -= obj.floatVal
+ else:
+ self.floatVal -= float(obj)
+ return self
+
+ def __mul__(self, obj):
+ if isinstance(obj, AsnReal):
+ return self.floatVal * obj.floatVal
+ return self.floatVal * obj
+
+ def __imul__(self, obj):
+ if isinstance(obj, AsnReal):
+ self.floatVal *= obj.floatVal
+ else:
+ self.floatVal *= obj
+ return self
+
+ def __str__(self):
+ return str(self.value())
+
+ __nonzero__=__bool__
+
+ def __cmp__(self, obj):
+ cmpValA = self.floatVal
+ if isinstance(obj, int):
+ cmpValB = obj
+ elif isinstance(obj, AsnReal):
+ cmpValB = obj.floatVal
+ elif isinstance(obj, basestring) or isinstance(obj, int):
+ cmpValB = int(obj)
+ else:
+ raise TypeError("Compare with float, or AsnReal")
+ return cmp(cmpValA, cmpValB)
+
+ def typename(self):
+ return "AsnReal"
+
+ def value(self, newval=None):
+ if newval is not None:
+ if isinstance(newval, int) or isinstance(newval, float) or \
+ isinstance(newval, basestring):
+ self.floatVal = float(newval)
+ elif isinstance(newval, AsnReal):
+ self.floatVal = newval.floatVal
+ return self.floatVal
+
+ PLUS_INF = float('+inf')
+ MINUS_INF = float('-inf')
+ NOT_A_NUM = float('nan')
+
+ def BEncContent(self, asnbuf):
+ if not isinstance(asnbuf, AsnBuf):
+ raise ValueError("Buffer must be esnacc.asn_buf.AsnBuf")
+
+ if self.floatVal is 0.0:
+ return 0
+
+ if self.floatVal is AsnReal.PLUS_INF:
+ asnbuf.PutBuf(chr(0x40))
+ return 1
+ elif self.floatVal is AsnReal.MINUS_INF:
+ asnbuf.PutBuf(chr(0x41))
+ return 1
+
+ def intcnv(f):
+ return chr(int(f))
+
+ m, e = math.frexp(self.floatVal)
+ # Time to normalize for base2 encoding
+
+ encF = 0x80
+ if m < 0:
+ encF |= 0x40
+
+ while m & 0x1 == 0:
+ m >>= 1
+ e += 1
+
+ scaleF = 0
+ while m & 0x1 == 0:
+ m >>= 1
+ scaleF += 1
+
+ if scaleF > 3:
+ raise ValueError('Scale Factor overflow')
+
+ encE = intcnv(0 & 0xff)
+ encF |= scaleF << 2
+ if e in (0, -1):
+ encE = intcnv(e & 0xff)
+ else:
+ while e not in (0, -1):
+ encE = intcnv(e&0xff) + encE
+ e >>= 8
+ if e == 0 and encE[0] != 0 and int(encE[0]) & 0x80:
+ encE = intcnv(0) + encE
+ if e == -1 and encE[0] != 0 and int(encE[0]) & 0x80:
+ encE = intcnv(0xff) + encE
+ ln = len(encE)
+ if ln > 0xff:
+ raise ValueError('Overflow of real value')
+ if ln > 1:
+ encF |= ln - 1
+ if ln > 3:
+ encE = intcnv(ln & 0xff) + encE
+
+ encV = intcnv(0)
+ while m:
+ encV = intcnv(m & 0xff) + encV
+ m >>= 8
+
+ octs = intcnv(encF) + encE + encV
+ asnbuf.PutBufReverse(octs)
+ return len(octs)
+
+ def BDecContent(self, asnbuf, length):
+ raise NotImplementedError('Not ready yet')
new file mode 100644
@@ -0,0 +1,98 @@
+# Copyright (C) 2016, Aaron Conole <aconole@bytheb.org>
+#
+# Licensed under the terms of the GNU General Public License
+# agreement (version 2 or greater). Alternately, may be licensed
+# under the terms of the ESNACC Public License.
+#
+# ASN.1 Integer Types for eSNACC
+
+from asn_base import AsnBase
+from asn_base import BERConsts
+
+
+class AsnSetOf(AsnBase):
+
+ BER_TAG = BERConsts.SET_TAG_CODE
+ BER_FORM = BERConsts.BER_CONSTRUCTED_FORM
+
+ def __init__(self, elemts=None, expected=None):
+ self.constraints = None
+ self.elemts = []
+ self.expected = expected
+ if expected is None:
+ raise ValueError("Cannot decode a bare typed list: specify a type")
+ if elemts is not None:
+ self.elemts = elemts
+
+ def __getitem__(self, idx):
+ if self.elemts is None or idx > len(self.elemts):
+ raise IndexError("Octet value out of range")
+ return self.elemts[idx]
+
+ def __contains__(self, a):
+ if self.elemts is not None:
+ return a in self.elemts
+ return None
+
+ def __len__(self):
+ if self.elemts is not None:
+ return len(self.elemts)
+ return 0
+
+ def __delitem__(self, idx):
+ if self.elemts is not None:
+ del self.elemts[idx]
+ raise IndexError("Out of range")
+
+ def append(self, item):
+ if isinstance(item, self.expected):
+ self.elemts.append(item)
+ else:
+ raise TypeError("Invalid type, expected: %s" % (self.expected))
+
+ def __str__(self):
+ s = "%s" % self.typename()
+ s += " {"
+ s += ','.join(str(x) for x in self.elemts)
+ s += "}"
+ return s
+
+ def typename(self):
+ return "AsnSetOf"
+
+ def BEncContent(self, asnbuf):
+ enclength = 0
+ for x in reversed(self.elemts):
+ enclength += x.BEnc(asnbuf)
+
+ return enclength
+
+ def BDecContent(self, asnbuf, contentlen):
+ while contentlen > 0:
+ t = self.expected()
+ contentlen -= t.BDec(asnbuf, contentlen)
+
+
+class AsnSequenceOf(AsnSetOf):
+
+ BER_TAG = BERConsts.SEQ_TAG_CODE
+
+ def __init__(self, elemts=None, expected=None):
+ AsnSetOf.__init__(self, elemts, expected)
+
+ def typename(self):
+ return "AsnSequenceOf"
+
+ def BEncContent(self, asnbuf):
+ self.elemts.sort()
+ return AsnSetOf.BEncContent(self, asnbuf)
+
+ def BDecContent(self, asnbuf, contentlen):
+ ret = AsnSetOf.BDecContent(self, asnbuf, contentlen)
+ # Generally, an ASN list is unsorted, but a SEQ implies sorted
+ # - however -, if a remote end decides to give us an unsorted
+ # list, we'll just do the sort here.
+ self.elemts.sort()
+ if ret is None:
+ ret = 0
+ return ret
new file mode 100644
@@ -0,0 +1,112 @@
+# Copyright (C) 2016, Aaron Conole <aconole@bytheb.org>
+#
+# Licensed under the terms of the GNU General Public License
+# agreement (version 2 or greater). Alternately, may be licensed
+# under the terms of the ESNACC Public License.
+#
+# ASN.1 Integer Types for eSNACC
+
+from asn_base import AsnBase
+from asn_base import BERConsts
+from asn_base import Constraint
+
+
+class CharacterValueConstraint(Constraint):
+ def __init__(self, lowerBound=None, upperBound=None, charList=None):
+ self.lower = lowerBound
+ self.upper = upperBound
+ self.chars = charList
+
+ def withinConstraint(self, value):
+ if isinstance(value, list) or isinstance(value, basestring):
+ for x in value:
+ if self.withinConstraint(x):
+ return False
+ if self.lower is not None and value is not None and \
+ value < self.lower:
+ return False
+ if self.upper is not None and value is not None and \
+ value > self.upper:
+ return False
+ if self.chars is not None and value is not None and \
+ value not in self.chars:
+ return False
+ return True
+
+
+class OctetLengthConstraint(Constraint):
+ def __init__(self, length=None):
+ self.length = length
+
+ def withinConstraint(self, value):
+ if isinstance(value, list) or isinstance(value, basestring):
+ if len(value) <= self.length:
+ return True
+ return False
+
+
+class AsnOcts(AsnBase):
+ BER_TAG = BERConsts.OCTETSTRING_TAG_CODE
+
+ def __init__(self, value=None):
+ self.constraints = None
+ self.octs = None
+ if value is not None:
+ self.setData(value)
+
+ def __getitem__(self, idx):
+ if self.octs is None or idx > len(self.octs):
+ raise IndexError("Octet value out of range")
+ return self.octs[idx]
+
+ def __contains__(self, a):
+ if self.octs is not None:
+ return a in self.octs
+ return None
+
+ def __len__(self):
+ if self.octs is not None:
+ return len(self.octs)
+ return 0
+
+ def __delitem__(self, idx):
+ if self.octs is not None:
+ del self.octs[idx]
+ raise IndexError("Out of range")
+
+ def __str__(self):
+ s = "["
+ if self.octs is not None:
+ s += ' '.join(["%02X" % ord(x) for x in self.octs])
+ s += "]"
+ return s
+
+ def typename(self):
+ return "AsnOcts"
+
+ def octs(self):
+ return self.octs
+
+ def setData(self, octets):
+ if isinstance(octets, list):
+ self.octs = octets
+ elif isinstance(octets, basestring):
+ self.octs = []
+ for x in octets:
+ self.octs.append(chr(ord(x)))
+
+ def BEncContent(self, asnbuf):
+ asnbuf.PutBufReverse(self.octs)
+ return len(self.octs)
+
+ def BDecContent(self, asnbuf, contentlen):
+ self.octs = asnbuf.GetSeg(contentlen)
+ return len(self.octs)
+
+
+class AsnString(AsnOcts):
+ def __init__(self, value=None):
+ self.contains = None
+ if value is None:
+ value = ""
+ self.setData(value)
new file mode 100644
@@ -0,0 +1,35 @@
+# Copyright (C) 2016, Aaron Conole <aconole@bytheb.org>
+#
+# Licensed under the terms of the GNU General Public License
+# agreement (version 2 or greater). Alternately, may be licensed
+# under the terms of the ESNACC Public License.
+#
+# ASN.1 'Useful' Types for eSNACC
+# Normally, this should be auto-generated. For now, we'll handcode it
+# (because insanity)
+
+from asn_base import BERConsts
+from asn_octs import AsnOcts
+from asn_octs import CharacterValueConstraint
+
+
+NumericStrConstraint = \
+ CharacterValueConstraint(charList=['0', '1', '2', '3', '4', '5',
+ '6', '7', '8', '9', ' '])
+
+
+class NumericString(AsnOcts):
+
+ BER_TAG = BERConsts.NUMERICSTRING_TAG_CODE
+
+ def __init__(self, value=None):
+ self.constraints = [NumericStrConstraint]
+ self.octs = value
+ if self.octs is not None:
+ self.setData(value)
+
+class IA5String(AsnOcts):
+ BER_TAG = BERConsts.IA5STRING_TAG_CODE
+
+ def __init__(self, value=None):
+ AsnOcts.__init__(self, value)
new file mode 100644
@@ -0,0 +1,7 @@
+# Copyright (C) 2016, Aaron Conole <aconole@bytheb.org>
+#
+# Licensed under the terms of the GNU General Public License
+# agreement (version 2 or greater). Alternately, may be licensed
+# under the terms of the ESNACC Public License.
+#
+# This file intentionally blank...
new file mode 100755
@@ -0,0 +1,192 @@
+#!/usr/bin/env python
+# Copyright (C) 2016, Aaron Conole <aconole@bytheb.org>
+#
+# Licensed under the terms of the GNU General Public License
+# agreement (version 2 or greater). Alternately, may be licensed
+# under the terms of the ESNACC Public License.
+#
+# ESNACC ASN.1 integer tests
+
+import unittest
+if __package__ is None:
+ import sys
+ from os import path
+ sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
+ from esnacc import asn_ints
+ from esnacc import asn_buffer
+else:
+ from ..esnacc import asn_ints
+ from ..esnacc import asn_buffer
+
+class TestAsnInts(unittest.TestCase):
+ def test_operations_default(self):
+ asninteger = asn_ints.AsnInt(1)
+
+ self.assertTrue(asninteger == 1)
+ self.assertTrue(asninteger > 0)
+ self.assertTrue(asninteger < 2)
+
+ asninteger += 1
+
+ self.assertTrue(asninteger == 2)
+ self.assertTrue((asninteger * 1) == 2)
+ self.assertTrue((asninteger - 1) == 1)
+
+ def test_ber_encoder_simple(self):
+ class testVec(unittest.TestCase):
+ def __init__(self, number, length, byteCodes):
+ self.nm = number
+ self.ln = length
+ self.bc = byteCodes
+
+ def run(self):
+ asninteger = asn_ints.AsnInt(self.nm)
+ asnbuf = asn_buffer.AsnBuf()
+
+ self.assertTrue(asninteger.BEnc(asnbuf) == self.ln)
+ byteCodes = asnbuf.Buffer()
+ self.assertTrue(len(byteCodes) == len(self.bc))
+ for x in range(len(byteCodes)):
+ self.assertTrue(self.bc[x] == byteCodes[x])
+ return True
+
+ tests = []
+ tvn65535codes = [chr(0x2), chr(0x3), chr(0xff), chr(0x0), chr(0x1)]
+ tests.append(testVec(-65535, 5, tvn65535codes))
+
+ tvn10codes = [chr(0x2), chr(0x1), chr(0xf6)]
+ tests.append(testVec(-10, 3, tvn10codes))
+
+ tv0codes = [chr(0x2), chr(0x1), chr(0x0)]
+ tests.append(testVec(0, 3, tv0codes))
+
+ tv1codes = [chr(0x2), chr(0x1), chr(0x1)]
+ tests.append(testVec(1, 3, tv1codes))
+
+ tv2codes = [chr(0x2), chr(0x2), chr(0x3f), chr(0xc3)]
+ tests.append(testVec(16323, 4, tv2codes))
+
+ tv3codes = [chr(0x2), chr(02), chr(0x7f), chr(0xff)]
+ tests.append(testVec(32767, 4, tv3codes))
+
+ tv4codes = [chr(0x2), chr(0x3), chr(0x00), chr(0x80), chr(0x00)]
+ tests.append(testVec(32768, 5, tv4codes))
+
+ tv5codes = [chr(0x2), chr(0x3), chr(0x20), chr(0x00), chr(0x00)]
+ tests.append(testVec(2097152, 5, tv5codes))
+
+ tv6codes = [chr(0x2), chr(0x4), chr(0x1), chr(0x0), chr(0x0), chr(0x0)]
+ tests.append(testVec(16777216, 6, tv6codes))
+
+ for x in tests:
+ self.assertTrue(x.run())
+
+ def test_ber_decoder_simple(self):
+ class testVec(unittest.TestCase):
+ def __init__(self, number, length, byteCodes):
+ self.nm = number
+ self.ln = length
+ self.bc = byteCodes
+
+ def run(self):
+ asnbuf = asn_buffer.AsnBuf(self.bc)
+ asninteger = asn_ints.AsnInt()
+ self.assertTrue(asninteger.BDec(asnbuf, 0) == self.ln)
+ self.assertTrue(self.nm == int(asninteger))
+ return True
+
+ tests = []
+ tvn65535codes = [chr(0x2), chr(0x3), chr(0xff), chr(0x0), chr(0x1)]
+ tests.append(testVec(-65535, 5, tvn65535codes))
+
+ tvn10codes = [chr(0x2), chr(0x1), chr(0xf6)]
+ tests.append(testVec(-10, 3, tvn10codes))
+
+ tv0codes = [chr(0x2), chr(0x1), chr(0x0)]
+ tests.append(testVec(0, 3, tv0codes))
+
+ tv1codes = [chr(0x2), chr(0x1), chr(0x1)]
+ tests.append(testVec(1, 3, tv1codes))
+
+ tv2codes = [chr(0x2), chr(0x2), chr(0x3f), chr(0xc3)]
+ tests.append(testVec(16323, 4, tv2codes))
+
+ tv3codes = [chr(0x2), chr(02), chr(0x7f), chr(0xff)]
+ tests.append(testVec(32767, 4, tv3codes))
+
+ tv4codes = [chr(0x2), chr(0x3), chr(0x00), chr(0x80), chr(0x00)]
+ tests.append(testVec(32768, 5, tv4codes))
+
+ tv5codes = [chr(0x2), chr(0x3), chr(0x20), chr(0x00), chr(0x00)]
+ tests.append(testVec(2097152, 5, tv5codes))
+
+ tv6codes = [chr(0x2), chr(0x4), chr(0x1), chr(0x0), chr(0x0), chr(0x0)]
+ tests.append(testVec(16777216, 6, tv6codes))
+
+ for x in tests:
+ self.assertTrue(x.run())
+
+
+ def test_constraints(self):
+ class testVec(unittest.TestCase):
+ def __init__(self, number):
+ self.nm = number
+
+ def run(self):
+ cnstraint_good = asn_ints.IntegerConstraint(self.nm-1, self.nm+1)
+ asninteger = asn_ints.AsnInt(self.nm)
+ asninteger.addConstraint(cnstraint_good)
+
+ buf = asn_buffer.AsnBuf()
+ self.assertTrue(asninteger.BEnc(buf) != 0)
+
+ cnstraint_bad = asn_ints.IntegerConstraint(self.nm+1, self.nm+2)
+ asninteger.addConstraint(cnstraint_bad)
+ with self.assertRaises(ValueError):
+ asninteger.BEnc(buf)
+ return True
+
+ tests = []
+ tests.append(testVec(-65535))
+ tests.append(testVec(-10))
+ tests.append(testVec(0))
+ tests.append(testVec(1))
+ tests.append(testVec(16323))
+ tests.append(testVec(32767))
+ tests.append(testVec(32768))
+ tests.append(testVec(2097152))
+ tests.append(testVec(16777216))
+
+ for x in tests:
+ self.assertTrue(x.run())
+
+class TestAsnEnums(unittest.TestCase):
+ def test_enumeration_encoding(self):
+ class testVec(unittest.TestCase):
+ def __init__(self, number, length, byteCodes):
+ self.nm = number
+ self.ln = length
+ self.bc = byteCodes
+
+ def run(self):
+ asnenum = asn_ints.AsnEnum(self.nm)
+ asnbuf = asn_buffer.AsnBuf()
+
+ self.assertTrue(asnenum.BEnc(asnbuf) == self.ln)
+ byteCodes = asnbuf.Buffer()
+ self.assertTrue(len(byteCodes) == len(self.bc))
+ for x in range(len(byteCodes)):
+ self.assertTrue(self.bc[x] == byteCodes[x])
+ return True
+
+ tests = []
+ tv0codes = [chr(0xa), chr(0x1), chr(0x0)]
+ tests.append(testVec(0, 3, tv0codes))
+
+ for x in tests:
+ self.assertTrue(x.run())
+
+
+if __name__ == '__main__':
+
+ unittest.main()
This commit introduces ASN.1 primitive types for ASN.1 Integer, Enumerated, Octet-String, RelativeOID, OID, Bit-String, and the various collected string types. Additionally, a tiny buffer system is added. Signed-off-by: Aaron Conole <aconole@bytheb.org> --- NEWS | 1 + py-lib/.gitignore | 1 + py-lib/esnacc/__init__.py | 7 + py-lib/esnacc/asn_base.py | 189 +++++++++++++++++++ py-lib/esnacc/asn_bool.py | 53 ++++++ py-lib/esnacc/asn_buffer.py | 124 +++++++++++++ py-lib/esnacc/asn_ints.py | 356 ++++++++++++++++++++++++++++++++++++ py-lib/esnacc/asn_list.py | 98 ++++++++++ py-lib/esnacc/asn_octs.py | 112 ++++++++++++ py-lib/esnacc/asn_useful.py | 35 ++++ py-lib/esnacctests/__init__.py | 7 + py-lib/esnacctests/asn_ints_test.py | 192 +++++++++++++++++++ 12 files changed, 1175 insertions(+) create mode 100644 py-lib/.gitignore create mode 100644 py-lib/esnacc/__init__.py create mode 100644 py-lib/esnacc/asn_base.py create mode 100644 py-lib/esnacc/asn_bool.py create mode 100644 py-lib/esnacc/asn_buffer.py create mode 100644 py-lib/esnacc/asn_ints.py create mode 100644 py-lib/esnacc/asn_list.py create mode 100644 py-lib/esnacc/asn_octs.py create mode 100644 py-lib/esnacc/asn_useful.py create mode 100644 py-lib/esnacctests/__init__.py create mode 100755 py-lib/esnacctests/asn_ints_test.py