[esnacc-dev] [RFC 1/2] python: Introduce Python library
Aaron Conole
aconole at bytheb.org
Tue Jan 10 22:13:07 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>
---
py-lib/.gitignore | 1 +
py-lib/esnacc/__init__.py | 7 ++
py-lib/esnacc/asn_base.py | 160 ++++++++++++++++++++++++++++
py-lib/esnacc/asn_bool.py | 53 ++++++++++
py-lib/esnacc/asn_buffer.py | 124 ++++++++++++++++++++++
py-lib/esnacc/asn_ints.py | 202 ++++++++++++++++++++++++++++++++++++
py-lib/esnacc/asn_list.py | 89 ++++++++++++++++
py-lib/esnacc/asn_octs.py | 110 ++++++++++++++++++++
py-lib/esnacc/asn_useful.py | 35 +++++++
py-lib/esnacctests/__init__.py | 7 ++
py-lib/esnacctests/asn_ints_test.py | 192 ++++++++++++++++++++++++++++++++++
11 files changed, 980 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/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..3611fc3
--- /dev/null
+++ b/py-lib/esnacc/asn_base.py
@@ -0,0 +1,160 @@
+# 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)
+ fnl |= (tag & 0xff)
+ return fnl
+
+ 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(chr(TAG_CODE))
+ asnbuf.PutBuf(newbuf.buf)
+ return asnlen
+
+ def BDec(self, asnbuf, asnlen):
+ bufTag = ord(asnbuf.Buffer()[0])
+
+ 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%02x expected [0x%02x|0x%02x]" %
+ (bufTag, PRIM_TAG, CONS_TAG))
+
+ asnbuf.swallow(1)
+ asnlen += 1
+ 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..f1f6c1b
--- /dev/null
+++ b/py-lib/esnacc/asn_ints.py
@@ -0,0 +1,202 @@
+# 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
+
+
+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
diff --git a/py-lib/esnacc/asn_list.py b/py-lib/esnacc/asn_list.py
new file mode 100644
index 0000000..3cb18f2
--- /dev/null
+++ b/py-lib/esnacc/asn_list.py
@@ -0,0 +1,89 @@
+# 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 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 AsnSequenceOf.BEncContent(self, asnbuf)
+
+ def BDecContent(self, asnbuf, contentlen):
+ ret = AsnSequenceOf.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()
+ return ret
diff --git a/py-lib/esnacc/asn_octs.py b/py-lib/esnacc/asn_octs.py
new file mode 100644
index 0000000..71e4f40
--- /dev/null
+++ b/py-lib/esnacc/asn_octs.py
@@ -0,0 +1,110 @@
+# 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 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)
+
+ def __str__(self):
+ if self.octs is not None:
+ return ''.join(self.octs)
+ return ''
+
+
+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..4258940
--- /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