[esnacc-dev] [PATCH 1/2] python: Introduce Python library

Aaron Conole aconole at bytheb.org
Sat Jan 28 19:52:02 UTC 2017


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 at 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

diff --git a/NEWS b/NEWS
index 4f4ad5d..5e1c2dd 100644
--- a/NEWS
+++ b/NEWS
@@ -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
diff --git a/py-lib/.gitignore b/py-lib/.gitignore
new file mode 100644
index 0000000..0d20b64
--- /dev/null
+++ b/py-lib/.gitignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/py-lib/esnacc/__init__.py b/py-lib/esnacc/__init__.py
new file mode 100644
index 0000000..c274353
--- /dev/null
+++ b/py-lib/esnacc/__init__.py
@@ -0,0 +1,7 @@
+# Copyright (C) 2016, Aaron Conole <aconole at 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...
diff --git a/py-lib/esnacc/asn_base.py b/py-lib/esnacc/asn_base.py
new file mode 100644
index 0000000..5e88247
--- /dev/null
+++ b/py-lib/esnacc/asn_base.py
@@ -0,0 +1,189 @@
+# Copyright (C) 2016, Aaron Conole <aconole at 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
diff --git a/py-lib/esnacc/asn_bool.py b/py-lib/esnacc/asn_bool.py
new file mode 100644
index 0000000..5646e5a
--- /dev/null
+++ b/py-lib/esnacc/asn_bool.py
@@ -0,0 +1,53 @@
+# Copyright (C) 2016, Aaron Conole <aconole at 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'
diff --git a/py-lib/esnacc/asn_buffer.py b/py-lib/esnacc/asn_buffer.py
new file mode 100644
index 0000000..553968a
--- /dev/null
+++ b/py-lib/esnacc/asn_buffer.py
@@ -0,0 +1,124 @@
+# Copyright (C) 2016, Aaron Conole <aconole at 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
diff --git a/py-lib/esnacc/asn_ints.py b/py-lib/esnacc/asn_ints.py
new file mode 100644
index 0000000..db1b77e
--- /dev/null
+++ b/py-lib/esnacc/asn_ints.py
@@ -0,0 +1,356 @@
+# Copyright (C) 2016, Aaron Conole <aconole at 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')
diff --git a/py-lib/esnacc/asn_list.py b/py-lib/esnacc/asn_list.py
new file mode 100644
index 0000000..a77012b
--- /dev/null
+++ b/py-lib/esnacc/asn_list.py
@@ -0,0 +1,98 @@
+# Copyright (C) 2016, Aaron Conole <aconole at 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
diff --git a/py-lib/esnacc/asn_octs.py b/py-lib/esnacc/asn_octs.py
new file mode 100644
index 0000000..f4fec16
--- /dev/null
+++ b/py-lib/esnacc/asn_octs.py
@@ -0,0 +1,112 @@
+# Copyright (C) 2016, Aaron Conole <aconole at 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)
diff --git a/py-lib/esnacc/asn_useful.py b/py-lib/esnacc/asn_useful.py
new file mode 100644
index 0000000..b818d4c
--- /dev/null
+++ b/py-lib/esnacc/asn_useful.py
@@ -0,0 +1,35 @@
+# Copyright (C) 2016, Aaron Conole <aconole at 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)
diff --git a/py-lib/esnacctests/__init__.py b/py-lib/esnacctests/__init__.py
new file mode 100644
index 0000000..c274353
--- /dev/null
+++ b/py-lib/esnacctests/__init__.py
@@ -0,0 +1,7 @@
+# Copyright (C) 2016, Aaron Conole <aconole at 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...
diff --git a/py-lib/esnacctests/asn_ints_test.py b/py-lib/esnacctests/asn_ints_test.py
new file mode 100755
index 0000000..ea4193f
--- /dev/null
+++ b/py-lib/esnacctests/asn_ints_test.py
@@ -0,0 +1,192 @@
+#!/usr/bin/env python
+# Copyright (C) 2016, Aaron Conole <aconole at 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()
-- 
2.7.4




More information about the dev mailing list