├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── requirements.txt ├── scripts ├── dax └── tinyber_gen ├── setup.cfg ├── setup.py ├── test ├── Makefile ├── README.md ├── handwritten.c ├── run_tests.py ├── setup.py ├── t0.asn ├── t0.py ├── t0_c_test.py ├── t0_gen_test.py ├── t0_python_test.py ├── t0_test.pyx └── tint.c ├── tests ├── __init__.py ├── test_choice.asn1 ├── test_choice.py ├── test_int_match.py └── utils.py └── tinyber ├── __init__.py ├── _codec.pyx ├── ber.py ├── c_nodes.py ├── codec.py ├── data ├── tinyber.c └── tinyber.h ├── gen.py ├── nodes.py ├── py_nodes.py ├── walker.py └── writer.py /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | dist/ 3 | tests/gen_*_ber.py 4 | tinyber/_codec.c 5 | *.pyc 6 | *.egg-info 7 | *.swp 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | sudo: false 3 | python: 4 | - "2.7" 5 | - "3.4" 6 | 7 | install: 8 | - pip install --use-mirrors pep8 pyflakes 9 | - pip install -r requirements.txt 10 | 11 | before_script: 12 | - if [[ $TRAVIS_PYTHON_VERSION == 3* ]]; then 2to3 -n -w --no-diffs tinyber; fi 13 | - pep8 --version 14 | - pep8 scripts tests tinyber 15 | 16 | script: 17 | - python setup.py test 18 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | This software is licensed under the Apache 2 license, quoted below. 2 | 3 | Copyright 2015 Sam Rushing 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); you may not 6 | use this file except in compliance with the License. You may obtain a copy of 7 | the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | License for the specific language governing permissions and limitations under 15 | the License. 16 | 17 | ------------------ 18 | 19 | The output of tinyber is NOT considered a derivative work of tinyber. 20 | Specifically, though the code generation process may embed or include code 21 | files of varying lengths into the final output, this code, as output by 22 | tinyber, do not encumber the resulting output with any license restrictions. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | TinyBER 3 | ======= 4 | 5 | ![tinyber logo](http://www.rps.org/~/media/Exhibitions/2013/June/25/Images%20for%20Science/019_Tardigr_Pm_Craterl_400x2_2010_Nicole_Ottawa.ashx?bc=White&mw=400 "tardigrade") 6 | 7 | TinyBER is a very small, limited ASN.1 BER codec and code generator 8 | meant for use on embedded devices (or anywhere code size is 9 | restricted). The generated code uses fixed-size structures and makes 10 | no calls to malloc or free. 11 | 12 | Install 13 | ------- 14 | 15 | ```shell 16 | $ sudo python setup.py install 17 | ``` 18 | 19 | Usage 20 | ----- 21 | 22 | TinyBER can be used for ad-hoc encoding and decoding of data, but it 23 | also comes with a limited code generator. 24 | 25 | Buffers 26 | ------- 27 | 28 | A simple ``buf_t`` structure is used for both input and output:: 29 | 30 | ```c 31 | typedef struct { 32 | uint8_t * buffer; 33 | unsigned int pos; 34 | unsigned int size; 35 | } buf_t; 36 | ``` 37 | 38 | Encoding 39 | -------- 40 | 41 | Encoding is a little unusual. In the interest of efficiency, data can 42 | be encoded directly into an output buffer - backwards. Because asn.1 43 | structures tend to accumulate in reverse (the Type and Length precede 44 | the Value in the stream), the most efficient way to *encode* them is to 45 | do so in reverse. 46 | 47 | For example, to encode a SEQUENCE of objects there are three steps:: 48 | 49 | ```c 50 | int mark0 = obuf.pos; 51 | CHECK (encode_OCTET_STRING (&obuf, (uint8_t *) "ghi", 3)); 52 | CHECK (encode_OCTET_STRING (&obuf, (uint8_t *) "def", 3)); 53 | CHECK (encode_OCTET_STRING (&obuf, (uint8_t *) "abc", 3)); 54 | CHECK (encode_TLV (&obuf, mark0, TAG_SEQUENCE)); 55 | ``` 56 | 57 | 1. Remember the stream's position (record the value of obuf.pos). 58 | 2. Encode each item of the SEQUENCE in reverse. 59 | 3. Emit the type and length for the entire sequence. 60 | 61 | Note that the ``buf_t`` object is used in a 'predecrement' mode. When 62 | you initialize a buffer for output, its ``pos`` field points to the 63 | *end* of the buffer. As data is written, ``pos`` moves backward. 64 | 65 | 66 | Decoding 67 | -------- 68 | 69 | When decoding an object, first call ``decode_TLV()`` to get the type, 70 | length, and value pointers to the object:: 71 | 72 | ```c 73 | buf_t src; 74 | init_ibuf (&src, data, length); 75 | asn1raw dst; 76 | int r = decode_TLV (&dst, &src); 77 | ``` 78 | 79 | Now examine the type tag - if it is the expected type, then you may 80 | further decode the value. If the value itself makes up a more complex 81 | structure, continue the procedure recursively. 82 | 83 | A simple utility structure, ``asn1raw`` is used to represent a TLV:: 84 | 85 | ```c 86 | typedef struct { 87 | uint8_t type; 88 | int length; 89 | uint8_t * value; 90 | } asn1raw; 91 | ``` 92 | 93 | To decode a 'structured' element (i.e., a SEQUENCE or SET), create an 94 | array of ``asn1raw`` objects, and pass it to ``decode_structured()``:: 95 | 96 | ```c 97 | asn1raw subs[50]; 98 | int n = 50; 99 | int i; 100 | CHECK (decode_structured (ob, &subs[0], &n)); 101 | ``` 102 | 103 | In this example we allow up to 50 sub-elements. If more are present 104 | in the stream an error will be returned. If there are less than 50 105 | the actual number will be set in ``n`` (i.e., ``n`` is an in-out 106 | param). 107 | 108 | Now that you have the metadata for each sub-element, you may 109 | recursively continue decoding each one in turn. (This could be viewed 110 | as a form of recursive-descent parser). 111 | 112 | Limitations 113 | ----------- 114 | 115 | This is not a full BER codec by any stretch: for example it supports 116 | only definite-length (i.e., actual length is always prepended), and as 117 | such it can be used for DER encoding as long as care is taken to 118 | follow the rules. 119 | 120 | It does not support INTEGERs larger than a machine int (int64_t by default). 121 | 122 | Still missing are direct support for SET, APPLICATION, BITSTRING, 123 | OIDs, etc... though if you are familiar with BER they can be 124 | implemented with relative ease. 125 | 126 | Because tinyber requires fixed-sized elements for all structures (to 127 | avoid malloc & free), using recursive (or mutually recursive) types is 128 | impossible:: 129 | 130 | ```asn1 131 | List ::= SEQUENCE { 132 | car INTEGER, 133 | cdr List OPTIONAL 134 | } 135 | ``` 136 | 137 | Tinyber can't make a fixed-sized structure that might hold a 138 | potentially infinite list, so it cannot handle this kind of 139 | construction. 140 | 141 | Code Generation 142 | --------------- 143 | 144 | Included is a code generator, ``tinyber_gen.py``, which can generate 145 | type definitions and BER encoders/decoders for a limited subset of the 146 | ASN.1 specification language (X.680) in C and Python. 147 | 148 | ```text 149 | usage: tinyber_gen [-h] [-o OUTDIR] [-l LANG] [-ns] FILE 150 | 151 | tinyber ASN.1 BER/DER code generator. 152 | 153 | positional arguments: 154 | FILE asn.1 spec 155 | 156 | optional arguments: 157 | -h, --help show this help message and exit 158 | -o OUTDIR, --outdir OUTDIR 159 | output directory (defaults to location of input file) 160 | -l LANG, --lang LANG output language ('c' or 'python') 161 | -ns, --no-standalone [python only] do not insert codec.py into output file. 162 | ``` 163 | 164 | For example:: 165 | 166 | ```bash 167 | beast:tinyber rushing$ python tinyber_gen.py -l c thing.asn1 168 | beast:tinyber rushing$ ls -l thing.[ch] 169 | -rw-r--r-- 1 rushing staff 20240 Jan 20 13:08 thing.c 170 | -rw-r--r-- 1 rushing staff 4939 Jan 20 13:08 thing.h 171 | beast:tinyber rushing$ 172 | ``` 173 | 174 | 175 | The code generator requires the 176 | [asn1ate package](https://github.com/kimgr/asn1ate) to be installed. 177 | ``asn1ate`` is a parser for X.680 designed for use by code generators. 178 | 179 | 180 | Module Design 181 | ------------- 182 | 183 | If your goal is to keep your codec as small as possible, a good approach is 184 | to segregate your packet types into 'server' and 'client' groups. Otherwise 185 | the outermost CHOICE PDU will force the inclusion of both server and client 186 | encoders and decoders on both sides. If you use two different PDU's, you will 187 | get only the encoders and decoders needed for each side. For example:: 188 | 189 | ```asn1 190 | ThingModule DEFINITIONS ::= BEGIN 191 | 192 | ThingClientMessage ::= CHOICE { 193 | login-request [0] LoginRequest, 194 | status-request [1] StatusRequest, 195 | } 196 | 197 | ThingServerMessage ::= CHOICE { 198 | login-reply [0] LoginReply, 199 | status-reply [1] StatusReply 200 | } 201 | ``` 202 | 203 | Licensing 204 | --------- 205 | This software is licensed under the Apache 2 license. However, the output, 206 | which is included into other projects, is not encumbered with any license 207 | restrictions. See the LICENSE.txt for more details. 208 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | asn1ate>=0.5 2 | pyparsing>=2.0.0 3 | -------------------------------------------------------------------------------- /scripts/dax: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- Mode: Python -*- 3 | import sys 4 | from subprocess import Popen, PIPE, STDOUT 5 | import subprocess 6 | for arg in sys.argv[1:]: 7 | p = Popen (['dumpasn1', '-'], stdout=PIPE, stdin=PIPE, stderr=PIPE) 8 | print p.communicate (input=arg.decode ('hex'))[0] 9 | -------------------------------------------------------------------------------- /scripts/tinyber_gen: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- Mode: Python -*- 3 | 4 | from tinyber.gen import main 5 | main() 6 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [pep8] 2 | ignore = W503,E211,E221,E226,E302,E402,E501,E731 3 | max-line-length = 120 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- Mode: Python -*- 2 | 3 | from setuptools import setup, find_packages 4 | from distutils.extension import Extension 5 | 6 | try: 7 | from Cython.Build import cythonize 8 | exts = cythonize ([Extension ("tinyber._codec", ['tinyber/_codec.pyx'])]) 9 | except ImportError: 10 | exts = [] 11 | 12 | setup ( 13 | name = 'tinyber', 14 | version = '0.0.1', 15 | url = "https://github.com/cloudtools/tinyber", 16 | packages = find_packages(), 17 | description = 'ASN.1 code generator for Python and C', 18 | scripts = ['scripts/tinyber_gen', 'scripts/dax'], 19 | package_data = { 20 | 'tinyber': ['data/*.[ch]', 'tinyber/codec.py'], 21 | 'tests': ['*.asn1'], 22 | }, 23 | ext_modules = exts, 24 | test_suite = "tests", 25 | use_2to3 = True, 26 | install_requires = ['asn1ate>=0.5', 'pyparsing>=2.0.0'], 27 | ) 28 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | 2 | CFLAGS = -g -O3 3 | 4 | test: handwritten.c ../tinyber/data/tinyber.c ../tinyber/data/tinyber.h 5 | $(CC) $(CFLAGS) -I ../tinyber/data/ handwritten.c ../tinyber/data/tinyber.c -o handwritten 6 | ./handwritten 7 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | 2 | NOTE 3 | ---- 4 | 5 | This test suite is in the middle of being modified so that it can run under the 6 | standard unittest suite and moved to ``../tests``. 7 | 8 | The Tests 9 | --------- 10 | 11 | The file ``t0.asn`` is meant to cover all the supported features of tinyber. 12 | The file ``t0_gen_test.py`` auto-generates over 3000 tests in an attempt to 13 | cover every possible error case. 14 | 15 | Note: t0_c_test.py requires [shrapnel](https://github.com/ironport/shrapnel) for its BER codec. 16 | 17 | Usage 18 | ----- 19 | 20 | 1. generate t0.[ch] from t0.asn. 21 | 22 | ``tinyber_gen -l c t0.asn`` 23 | 24 | 2. build the cython extension. 25 | 26 | ``python setup.py build_ext --inplace`` 27 | 28 | 3. run the test. 29 | 30 | ``python t0_c_test.py`` 31 | 32 | 33 | 34 | Demo 35 | ---- 36 | 37 | The file 'handwritten.c' is a sample of a hand-written encoding & decoding. 38 | To build it: 39 | 40 | ```bash 41 | $ gcc -O3 -I ../tinyber/data/ handwritten.c ../tinyber/data/tinyber.c -o handwritten 42 | ``` 43 | 44 | or just ``make test``. 45 | -------------------------------------------------------------------------------- /test/handwritten.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "tinyber.h" 8 | 9 | #define CHECK(x) do { if ((x) == -1) { return -1; } } while (0) 10 | 11 | int 12 | decode_structured (const asn1raw_t * src, asn1raw_t * dst, int * n) 13 | { 14 | // create a buffer to iterate through the encoded data 15 | buf_t src0; 16 | init_ibuf (&src0, src->value, src->length); 17 | int i = 0; 18 | while (1) { 19 | if (i > *n) { 20 | // too many elements. 21 | return -1; 22 | } else if (src0.pos == src0.size) { 23 | break; 24 | } 25 | // unwrap TLV for each element. 26 | CHECK (decode_TLV (&dst[i], &src0)); 27 | i++; 28 | } 29 | // tell the caller how many entries were present. 30 | *n = i; 31 | return 0; 32 | } 33 | 34 | 35 | static 36 | void 37 | dump_hex (uint8_t * values, int n) 38 | { 39 | int i; 40 | for (i=0; i < n; i++) { 41 | fprintf (stderr, "%02x", values[i]); 42 | } 43 | } 44 | 45 | static 46 | void 47 | indent (int n) 48 | { 49 | for (int i=0; i < n; i++) { 50 | fprintf (stderr, " "); 51 | } 52 | } 53 | 54 | static 55 | void 56 | dump_tlv (asn1raw_t * ob) 57 | { 58 | fprintf (stderr, "{%d %d ", ob->type, ob->length); 59 | dump_hex (ob->value, ob->length); 60 | fprintf (stderr, "}\n"); 61 | } 62 | 63 | static 64 | void 65 | dump_octet_string (uint8_t * s, unsigned int len) 66 | { 67 | int i; 68 | for (i=0; i < len; i++) { 69 | if (isprint (s[i])) { 70 | fputc (s[i], stderr); 71 | } else { 72 | fprintf (stderr, "\\x"); 73 | dump_hex (s + i, 1); 74 | } 75 | } 76 | } 77 | 78 | int 79 | dump (asn1raw_t * ob, int depth) 80 | { 81 | indent (depth); 82 | //dump_tlv (ob); 83 | indent (depth); 84 | switch (ob->type) { 85 | case TAG_INTEGER: 86 | fprintf (stderr, "%lld\n", decode_INTEGER (ob)); 87 | break; 88 | case TAG_BOOLEAN: 89 | fprintf (stderr, decode_BOOLEAN (ob) ? "TRUE\n" : "FALSE\n"); 90 | break; 91 | case TAG_OCTETSTRING: 92 | fputc ('\'', stderr); 93 | dump_octet_string (ob->value, ob->length); 94 | fprintf (stderr, "\'\n"); 95 | break; 96 | case TAG_SEQUENCE: { 97 | asn1raw_t subs[50]; 98 | int n = 50; 99 | int i; 100 | CHECK (decode_structured (ob, &subs[0], &n)); 101 | indent (depth); 102 | fprintf (stderr, "SEQUENCE {\n"); 103 | for (i=0; i < n; i++) { 104 | CHECK (dump (&subs[i], depth + 1)); 105 | } 106 | indent (depth); 107 | fprintf (stderr, "}\n"); 108 | } 109 | break; 110 | default: 111 | fprintf (stderr, "unhandled tag %d\n", ob->type); 112 | } 113 | return 0; 114 | } 115 | 116 | int 117 | decode_bytes (uint8_t * data, int length) 118 | { 119 | buf_t src; 120 | init_ibuf (&src, data, length); 121 | asn1raw_t dst; 122 | fprintf (stderr, "decode "); 123 | dump_hex (data, length); 124 | fprintf (stderr, "\n"); 125 | int r = decode_TLV (&dst, &src); 126 | if (r) { 127 | fprintf (stderr, "\n *** error decoding at position %d ***\n\n", src.pos); 128 | return -1; 129 | } else { 130 | dump (&dst, 0); 131 | return 0; 132 | } 133 | } 134 | 135 | int 136 | test_decoder (void) 137 | { 138 | fprintf (stderr, "\n--- testing decoder ---\n"); 139 | uint8_t data0[6] = "\x02\x04@\x00\x00\x00"; 140 | decode_bytes (data0, sizeof(data0)); 141 | uint8_t data1[] = "0\t\x02\x01\x00\x02\x01\x01\x02\x01\x02"; 142 | decode_bytes (data1, sizeof(data1)); 143 | uint8_t data2[] = "\x30\x11\x02\x04\xff\x61\x63\x39\x30\x09\x02\x01\x00\x01\x01\x00\x02\x01\x02"; 144 | decode_bytes (data2, sizeof(data2)); 145 | // test zero-length boolean. 146 | uint8_t data3[] = "\x30\x06\x01\x00\x02\x02\x0c\x45"; 147 | decode_bytes (data3, sizeof(data3)); 148 | return 0; 149 | } 150 | 151 | int 152 | test_encoder (void) 153 | { 154 | asn1int_t n = -3141; 155 | asn1bool_t q = 0; 156 | fprintf (stderr, "\n--- testing encoder ---\n"); 157 | uint8_t buffer[512]; 158 | buf_t obuf; 159 | init_obuf (&obuf, buffer, sizeof(buffer)); 160 | 161 | memset (buffer, 0, sizeof(buffer)); 162 | 163 | // [-3141, False, ['abc', 'def', 'ghi'], 3735928559, 'Mary had a little lamb. I ate it with a mint sauce.'] 164 | 165 | n = 0xdeadbeef; 166 | int mark = obuf.pos; 167 | uint8_t msg[] = "Mary had a little lamb. I ate it with a mint sauce."; 168 | CHECK (encode_OCTET_STRING (&obuf, msg, sizeof(msg) - 1)); // elide NUL 169 | CHECK (encode_INTEGER (&obuf, &n)); 170 | 171 | int mark0 = obuf.pos; 172 | CHECK (encode_OCTET_STRING (&obuf, (uint8_t *) "ghi", 3)); 173 | CHECK (encode_OCTET_STRING (&obuf, (uint8_t *) "def", 3)); 174 | CHECK (encode_OCTET_STRING (&obuf, (uint8_t *) "abc", 3)); 175 | CHECK (encode_TLV (&obuf, mark0, TAG_SEQUENCE, FLAG_STRUCTURED)); 176 | 177 | n = -3141; 178 | CHECK (encode_BOOLEAN (&obuf, &q)); 179 | CHECK (encode_INTEGER (&obuf, &n)); 180 | CHECK (encode_TLV (&obuf, mark, TAG_SEQUENCE, FLAG_STRUCTURED)); 181 | 182 | int length = mark - obuf.pos; 183 | fprintf (stderr, "length=%d\n", length); 184 | dump_hex ((buffer + sizeof(buffer)) - length, length); 185 | fprintf (stderr, "\n"); 186 | decode_bytes ((buffer + sizeof(buffer)) - length, length); 187 | return 0; 188 | } 189 | 190 | int 191 | main (int argc, char * argv[]) 192 | { 193 | test_decoder(); 194 | test_encoder(); 195 | } 196 | -------------------------------------------------------------------------------- /test/run_tests.py: -------------------------------------------------------------------------------- 1 | 2 | # this is a first step toward moving this test suite into the 'tests' 3 | # directory where it can be run with unittest. 4 | 5 | # 1) generate t0.[ch] 6 | 7 | # this is based on ../tests/utils.py 8 | 9 | from asn1ate import parser 10 | from asn1ate.sema import * 11 | from tinyber.walker import Walker 12 | 13 | from tinyber.c_nodes import CBackend 14 | from tinyber import c_nodes as nodes 15 | 16 | def generate(infilename, outfilename): 17 | class FakeArgs(object): 18 | no_standalone = False 19 | 20 | import os 21 | with open(infilename) as f: 22 | asn1def = f.read() 23 | 24 | parse_tree = parser.parse_asn1(asn1def) 25 | modules = build_semantic_model(parse_tree) 26 | assert (len(modules) == 1) 27 | 28 | module_name = outfilename 29 | path = "." 30 | args = FakeArgs() 31 | 32 | # pull in the python-specific node implementations 33 | walker = Walker(modules[0], nodes) 34 | walker.walk() 35 | 36 | backend = CBackend(args, walker, module_name, path) 37 | backend.generate_code() 38 | 39 | 40 | generate ('t0.asn', 't0') 41 | 42 | # 2) build the cython extension in place. 43 | from distutils.core import run_setup 44 | run_setup ('setup.py', ['build_ext', '--inplace']) 45 | 46 | # 3) run the test 47 | execfile ("t0_c_test.py") 48 | -------------------------------------------------------------------------------- /test/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | from distutils.extension import Extension 3 | from Cython.Build import cythonize 4 | 5 | ext = [ 6 | Extension ("t0_test", ['t0_test.pyx', 't0.c', 'tinyber.c']) 7 | ] 8 | 9 | setup ( 10 | name = 'tinyber_test', 11 | version = '0.1', 12 | packages = find_packages(), 13 | ext_modules = cythonize (ext) 14 | ) 15 | -------------------------------------------------------------------------------- /test/t0.asn: -------------------------------------------------------------------------------- 1 | -- -*- Mode: asn1; indent-tabs-mode: nil -*- 2 | 3 | -- test module meant to exercise all supported features of tinyber_gen.py 4 | 5 | ThingModule DEFINITIONS ::= BEGIN 6 | 7 | StringThing ::= OCTET STRING SIZE (5) 8 | 9 | ThingMsg ::= CHOICE { 10 | msg-a [0] MsgA, 11 | msg-b [1] MsgB, 12 | msg-c [50] MsgC 13 | } 14 | 15 | Pair ::= SEQUENCE { 16 | a INTEGER (0..255), 17 | b INTEGER (100..200) 18 | } 19 | 20 | Color ::= ENUMERATED { 21 | red (0), 22 | blue (1), 23 | green (2) 24 | } 25 | 26 | MsgA ::= SEQUENCE { 27 | toctet OCTET STRING SIZE (0..10), 28 | t8int INTEGER (0..255), 29 | t16int INTEGER (0..16385), 30 | t32int INTEGER (0..4294967295), 31 | tarray SEQUENCE SIZE (4) OF Pair, 32 | tbool BOOLEAN, 33 | tenum Color 34 | } 35 | 36 | MsgB ::= SEQUENCE { 37 | a INTEGER, 38 | b BOOLEAN, 39 | x SEQUENCE SIZE (0..2) OF BOOLEAN, 40 | y SEQUENCE SIZE (1..2) OF INTEGER (0..255) 41 | } 42 | 43 | MsgC ::= SEQUENCE { 44 | -- test multi-byte length 45 | lstr OCTET STRING SIZE (0..500), 46 | tbool BOOLEAN 47 | } 48 | 49 | 50 | END 51 | -------------------------------------------------------------------------------- /test/t0.py: -------------------------------------------------------------------------------- 1 | # -*- Mode: Python; indent-tabs-mode: nil -*- 2 | 3 | from t0_ber import * 4 | 5 | def HD(s): 6 | return s.decode ('hex') 7 | 8 | data = HD('300602016302016e') 9 | p = Pair() 10 | p.decode (data) 11 | print p.encode().encode('hex') 12 | 13 | tests = [ 14 | HD('6110300e020203e90101ff30003003020101'), 15 | HD('611230100204400000000101ff30003003020101'), 16 | HD('6110300e020203e901010030003003020101'), 17 | HD('61133011020203e901010030003006020101020102'), 18 | HD('61133011020203e901010030030101ff3003020101'), 19 | HD('61163014020203e901010030060101ff0101003003020101'), 20 | HD('603c303a040361626302013202022711020417bc927a3020300602010a020165300602010a020165300602010a020165300602010a0201650101000a0100'), 21 | HD('603c303a040361626302013302022711020417bc927a3020300602010a020165300602010a020165300602010a020165300602010a0201650101000a0100'), 22 | HD('603c303a040361626302013402022711020417bc927a3020300602010a020165300602010a020165300602010a020165300602010a0201650101ff0a0100'), 23 | HD('603c303a040361626302013202022711020417bc927a3020300602010a020165300602010a020165300602010a020165300602010a0201650101000a0101'), 24 | HD('603c303a040361626302013302022711020417bc927a3020300602010a020165300602010a020165300602010a020165300602010a0201650101000a0101'), 25 | HD('603c303a040361626302013402022711020417bc927a3020300602010a020165300602010a020165300602010a020165300602010a0201650101ff0a0101'), 26 | HD('603c303a040361626302013202022711020417bc927a3020300602010a020165300602010a020165300602010a020165300602010a0201650101000a0102'), 27 | HD('603c303a040361626302013302022711020417bc927a3020300602010a020165300602010a020165300602010a020165300602010a0201650101000a0102'), 28 | HD('603c303a040361626302013402022711020417bc927a3020300602010a020165300602010a020165300602010a020165300602010a0201650101ff0a0102'), 29 | ] 30 | 31 | for data in tests: 32 | m = ThingMsg() 33 | m.decode (data) 34 | print m.value 35 | -------------------------------------------------------------------------------- /test/t0_c_test.py: -------------------------------------------------------------------------------- 1 | # -*- Mode: Python -*- 2 | 3 | # run the auto-generated tests on the C codec. 4 | 5 | from coro.asn1.ber import * 6 | from t0_test import try_decode 7 | from t0_gen_test import gen_thingmsg 8 | 9 | class ExpectedGood (Exception): 10 | pass 11 | class ExpectedBad (Exception): 12 | pass 13 | class BadEncoding (Exception): 14 | pass 15 | 16 | # XXX use the unittest framework 17 | 18 | def go(): 19 | n = 0 20 | for tval, good in gen_thingmsg(): 21 | print tval.encode ('hex'), good 22 | r = try_decode (tval) 23 | if not good: 24 | if r != -1: 25 | # it should have been bad, but wasn't. 26 | raise ExpectedBad 27 | elif r == -1: 28 | # it should have been good, but wasn't. 29 | raise ExpectedGood 30 | elif r != tval: 31 | # it was a good decode, but the encoding wasn't identical. 32 | raise BadEncoding 33 | else: 34 | # it's all good. 35 | pass 36 | n += 1 37 | print 'passed %d tests' % (n,) 38 | 39 | go() 40 | 41 | -------------------------------------------------------------------------------- /test/t0_gen_test.py: -------------------------------------------------------------------------------- 1 | 2 | from coro.asn1.ber import * 3 | from t0_test import try_decode 4 | 5 | # this will auto-generate test cases - both good and bad ones - to exhaustively 6 | # cover the tinyber codec generated for t0.asn. 7 | # currently generates 3000+ tests. 8 | 9 | def gen_pair(): 10 | return [ 11 | (SEQUENCE (INTEGER (10), INTEGER (101)), True), 12 | # out of range integer 13 | (SEQUENCE (INTEGER (10), INTEGER (10)), False), 14 | (SEQUENCE (INTEGER (1001), INTEGER (10)), False), 15 | # unwanted negative integer 16 | (SEQUENCE (INTEGER (-5), INTEGER (-6)), False), 17 | # junk 18 | ('asdfasdfasdf', False), 19 | ('\xDE\xAD\xBE\xEF', False), 20 | ('x', False), 21 | # trailing junk 22 | (SEQUENCE (INTEGER (10), INTEGER (101), 'asdfasdf'), False), 23 | (SEQUENCE (INTEGER (10), INTEGER (101), BOOLEAN(True)), False), 24 | ] 25 | 26 | def gen_color(): 27 | return [ 28 | (ENUMERATED (0), True), 29 | (ENUMERATED (1), True), 30 | (ENUMERATED (2), True), 31 | (ENUMERATED (3), False), 32 | (ENUMERATED (4), False), 33 | # bad type 34 | (INTEGER (99), False), 35 | # junk 36 | ('wieuriuwiusdf', False), 37 | ('x', False), 38 | ] 39 | 40 | def gen_msgb(): 41 | gx = SEQUENCE() 42 | gy = SEQUENCE (INTEGER (1)) 43 | return [ 44 | (SEQUENCE (INTEGER (1001), BOOLEAN(True), gx, gy), True), 45 | (SEQUENCE (INTEGER (1<<30), BOOLEAN(True), gx, gy), True), 46 | (SEQUENCE (INTEGER (1001), BOOLEAN(False), gx, gy), True), 47 | (SEQUENCE (INTEGER (1001), BOOLEAN(False), SEQUENCE(), SEQUENCE (INTEGER (1), INTEGER (2))), True), 48 | # exactly one x 49 | (SEQUENCE (INTEGER (1001), BOOLEAN(False), SEQUENCE(BOOLEAN(True)), gy), True), 50 | # exactly two x 51 | (SEQUENCE (INTEGER (1001), BOOLEAN(False), SEQUENCE(BOOLEAN(True), BOOLEAN(False)), gy), True), 52 | # too many x 53 | (SEQUENCE (INTEGER (1001), BOOLEAN(False), SEQUENCE(BOOLEAN(True), BOOLEAN(False), BOOLEAN(True)), gy), False), 54 | # < 1 y 55 | (SEQUENCE (INTEGER (1001), BOOLEAN(False), SEQUENCE(), SEQUENCE()), False), 56 | # out of range in y 57 | (SEQUENCE (INTEGER (1001), BOOLEAN(False), SEQUENCE(), SEQUENCE (INTEGER (1), INTEGER (1001))), False), 58 | # extra data 59 | (SEQUENCE (INTEGER (1001), BOOLEAN(False), SEQUENCE(), BOOLEAN(True), OCTET_STRING ("asdfasdfasdfasdfasdfasdfasdfasdfasdf")), False), 60 | # not enough data 61 | (SEQUENCE (BOOLEAN(False), BOOLEAN(True)), False), 62 | (INTEGER (99), False), 63 | ('ksdjfkjwekrjasdf', False), 64 | ('x', False), 65 | ] 66 | 67 | def gen_msga(): 68 | result = [] 69 | for pair, good_pair in gen_pair(): 70 | for color, good_color in gen_color(): 71 | result.extend ([ 72 | # -- potentially good data --- 73 | (SEQUENCE (OCTET_STRING ('abc'), INTEGER (50), INTEGER (10001), INTEGER (398234234), SEQUENCE (pair,pair,pair,pair), BOOLEAN(False), color), good_pair and good_color), 74 | (SEQUENCE (OCTET_STRING ('abc'), INTEGER (51), INTEGER (10001), INTEGER (398234234), SEQUENCE (pair,pair,pair,pair), BOOLEAN(False), color), good_pair and good_color), 75 | (SEQUENCE (OCTET_STRING ('abc'), INTEGER (52), INTEGER (10001), INTEGER (398234234), SEQUENCE (pair,pair,pair,pair), BOOLEAN(True), color), good_pair and good_color), 76 | # --- known to be bad data --- 77 | # not enough entries 78 | (SEQUENCE (OCTET_STRING ('abc'), INTEGER (52), INTEGER (10001), INTEGER (398234234), SEQUENCE (pair,pair,pair), BOOLEAN(True), color), False), 79 | # bad first type 80 | (SEQUENCE (INTEGER (99), INTEGER (50), INTEGER (10001), INTEGER (398234234), SEQUENCE (pair,pair,pair,pair), BOOLEAN(False), color), False), 81 | # out of range integers... 82 | (SEQUENCE (OCTET_STRING ('abc'), INTEGER (410), INTEGER (10001), INTEGER (398234234), SEQUENCE (pair,pair,pair,pair), BOOLEAN(False), color), False), 83 | (SEQUENCE (OCTET_STRING ('abc'), INTEGER (53), INTEGER (16555), INTEGER (398234234), SEQUENCE (pair,pair,pair,pair), BOOLEAN(False), color), False), 84 | (SEQUENCE (OCTET_STRING ('abc'), INTEGER (54), INTEGER (10001), INTEGER (1<<33), SEQUENCE (pair,pair,pair,pair), BOOLEAN(False), color), False), 85 | # bad type in SEQUENCE OF 86 | (SEQUENCE (OCTET_STRING ('abc'), INTEGER (55), INTEGER (16555), INTEGER (99), SEQUENCE (INTEGER(99)), BOOLEAN(False), color), False), 87 | (SEQUENCE (OCTET_STRING ('abc'), INTEGER (56), INTEGER (16555), INTEGER (99), INTEGER (99), BOOLEAN(False), color), False), 88 | # bad type in place of BOOLEAN 89 | (SEQUENCE (OCTET_STRING ('abc'), INTEGER (57), INTEGER (10001), INTEGER (398234234), SEQUENCE (pair,pair,pair,pair), INTEGER(-9), color), False), 90 | # negative integers in unexpected places 91 | (SEQUENCE (OCTET_STRING ('abc'), INTEGER (58), INTEGER (10001), INTEGER (-1000), SEQUENCE (pair,pair,pair), BOOLEAN(True), color), False), 92 | (SEQUENCE (OCTET_STRING ('abc'), INTEGER (59), INTEGER (-100), INTEGER (-1000), SEQUENCE (pair,pair,pair), BOOLEAN(True), color), False), 93 | (SEQUENCE (OCTET_STRING ('abc'), INTEGER (-20), INTEGER (-100), INTEGER (-1000), SEQUENCE (pair,pair,pair), BOOLEAN(True), color), False), 94 | ]) 95 | 96 | return result 97 | 98 | def gen_msgc(): 99 | return [ 100 | (SEQUENCE (OCTET_STRING (''), BOOLEAN (True)), True), 101 | (SEQUENCE (OCTET_STRING ('x' * 499), BOOLEAN (True)), True), 102 | (SEQUENCE (OCTET_STRING ('x' * 501), BOOLEAN (True)), False), 103 | ] 104 | 105 | def gen_thingmsg(): 106 | result = [] 107 | for msgb, good in gen_msgb(): 108 | result.append ((APPLICATION (1, True, msgb), good),) 109 | # wrong tag 110 | result.append ((APPLICATION (0, True, msgb), False),) 111 | for msga, good in gen_msga(): 112 | result.append ((APPLICATION (0, True, msga), good),) 113 | # bad tag 114 | result.append ((APPLICATION (9, True, msga), False),) 115 | # wrong tag 116 | result.append ((APPLICATION (1, True, msga), False),) 117 | for msgc, good in gen_msgc(): 118 | result.append ((APPLICATION (50, True, msgc), good)) 119 | return result 120 | -------------------------------------------------------------------------------- /test/t0_python_test.py: -------------------------------------------------------------------------------- 1 | # -*- Mode: Python -*- 2 | 3 | # run the auto-generated tests on the C codec. 4 | 5 | from coro.asn1.ber import * 6 | from t0_ber import ThingMsg 7 | from t0_gen_test import gen_thingmsg 8 | 9 | def try_decode (val): 10 | try: 11 | m0 = ThingMsg() 12 | m0.decode (val) 13 | except: 14 | return -1 15 | else: 16 | return m0.encode() 17 | 18 | class ExpectedGood (Exception): 19 | pass 20 | class ExpectedBad (Exception): 21 | pass 22 | class BadEncoding (Exception): 23 | pass 24 | 25 | # XXX use the unittest framework 26 | 27 | def go(): 28 | n = 0 29 | for tval, good in gen_thingmsg(): 30 | print tval.encode ('hex'), good 31 | r = try_decode (tval) 32 | if not good: 33 | if r != -1: 34 | # it should have been bad, but wasn't. 35 | raise ExpectedBad 36 | elif r == -1: 37 | # it should have been good, but wasn't. 38 | raise ExpectedGood 39 | elif r != tval: 40 | # it was a good decode, but the encoding wasn't identical. 41 | raise BadEncoding 42 | else: 43 | # it's all good. 44 | pass 45 | n += 1 46 | print 'passed %d tests' % (n,) 47 | 48 | go() 49 | -------------------------------------------------------------------------------- /test/t0_test.pyx: -------------------------------------------------------------------------------- 1 | # -*- Mode: Cython -*- 2 | 3 | from libc.stdint cimport uint8_t 4 | 5 | cdef extern from "t0.h": 6 | 7 | ctypedef struct ThingMsg_t: 8 | pass 9 | cdef int decode_ThingMsg (ThingMsg_t * dst, buf_t * src) 10 | cdef int encode_ThingMsg (buf_t * dst, const ThingMsg_t * src) 11 | 12 | ctypedef struct buf_t: 13 | uint8_t * buffer 14 | unsigned int pos 15 | unsigned int size 16 | 17 | def try_decode (bytes pkt): 18 | cdef buf_t o 19 | cdef buf_t b 20 | cdef ThingMsg_t msg 21 | cdef uint8_t buffer[1024] 22 | b.buffer = pkt 23 | b.size = len(pkt) 24 | b.pos = 0 25 | cdef int r = decode_ThingMsg (&msg, &b) 26 | if r == 0: 27 | # good decode, now let's encode it. 28 | o.buffer = &buffer[0] 29 | o.size = sizeof(buffer) 30 | o.pos = o.size 31 | r = encode_ThingMsg (&o, &msg) 32 | if r == 0: 33 | return o.buffer[o.pos:o.size] 34 | else: 35 | return r 36 | else: 37 | return r 38 | -------------------------------------------------------------------------------- /test/tint.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include "tinyber.h" 7 | 8 | #define FAILIF(x) do { if (x) { return -1; } } while(0) 9 | #define CHECK(x) FAILIF(-1 == (x)) 10 | 11 | int 12 | main (int argc, char * argv[]) 13 | { 14 | asn1int_t n = 0; 15 | buf_t obuf; 16 | buf_t ibuf; 17 | uint8_t buffer[1024]; 18 | init_obuf (&obuf, buffer, sizeof(buffer)); 19 | 20 | for (int64_t i=INT32_MIN; i <= INT32_MAX; i++) { 21 | 22 | if ((i % 1000000) == 0) { 23 | fprintf (stderr, "."); 24 | } 25 | 26 | init_obuf (&obuf, buffer, sizeof(buffer)); 27 | n = i; 28 | encode_INTEGER (&obuf, &n); 29 | 30 | asn1raw_t tlv; 31 | asn1int_t m; 32 | init_ibuf (&ibuf, obuf.buffer + obuf.pos, obuf.size - obuf.pos); 33 | CHECK (decode_TLV (&tlv, &ibuf)); 34 | FAILIF (tlv.type != TAG_INTEGER); 35 | m = decode_INTEGER (&tlv); 36 | if (m != i) { 37 | exit(1); 38 | } 39 | } 40 | fprintf (stderr, "success.\n"); 41 | exit(0); 42 | } 43 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudtools/tinyber/d20d33341f9e74ba9b699553a8cf6448c167dec9/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_choice.asn1: -------------------------------------------------------------------------------- 1 | TestChoice DEFINITIONS ::= BEGIN 2 | Choice ::= CHOICE { 3 | choice1 [0] Choice1, 4 | choice2 [1] Choice2 5 | } 6 | 7 | Choice1 ::= SEQUENCE { 8 | test1 INTEGER (0..255), 9 | str1 OCTET STRING SIZE (0..129) 10 | } 11 | 12 | Choice2 ::= SEQUENCE { 13 | test2 INTEGER (0..255) 14 | } 15 | END 16 | -------------------------------------------------------------------------------- /tests/test_choice.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from asn1ate import parser 4 | from asn1ate.sema import * 5 | from tinyber.walker import Walker 6 | 7 | from tinyber.py_nodes import PythonBackend as Backend 8 | from tinyber import py_nodes as nodes 9 | 10 | from tests.utils import generate, test_reload 11 | 12 | 13 | class TestBasic(unittest.TestCase): 14 | @classmethod 15 | def setUpClass(cls): 16 | generate("tests/test_choice.asn1", "gen_choice") 17 | 18 | @classmethod 19 | def tearDownClass(cls): 20 | pass 21 | 22 | def test_choice1(self): 23 | test_reload() 24 | import tests.gen_choice_ber 25 | # Test length > 0x80 26 | s = bytearray(0x81) 27 | choice1 = tests.gen_choice_ber.Choice1(test1=255, str1=s) 28 | choice1.encode() 29 | assert(len(choice1.encode()) > 0x80) 30 | 31 | 32 | if __name__ == '__main__': 33 | unittest.main() 34 | -------------------------------------------------------------------------------- /tests/test_int_match.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from tinyber.c_nodes import int_max_size_type 4 | 5 | class TestBasic(unittest.TestCase): 6 | 7 | def test_int8(self): 8 | size = "int8_t" 9 | self.assertEqual(int_max_size_type(-2**7, 0), size) 10 | self.assertEqual(int_max_size_type(-1, 0), size) 11 | self.assertEqual(int_max_size_type(-1, 2**7 - 1), size) 12 | self.assertEqual(int_max_size_type(-2**7, 2**7 - 1), size) 13 | self.assertNotEqual(int_max_size_type(0, 2**7), size) 14 | self.assertNotEqual(int_max_size_type(0, 2**7 - 1), size) 15 | 16 | def test_int16(self): 17 | size = "int16_t" 18 | self.assertEqual(int_max_size_type(-1, 256), size) 19 | self.assertEqual(int_max_size_type(-1, 2**15 - 1), size) 20 | self.assertEqual(int_max_size_type(-2**15, 2**15 - 1), size) 21 | self.assertNotEqual(int_max_size_type(0, 2**15), size) 22 | self.assertNotEqual(int_max_size_type(0, 2**15 - 1), size) 23 | 24 | def test_int32(self): 25 | size = "int32_t" 26 | self.assertEqual(int_max_size_type(-1, 2**16), size) 27 | self.assertEqual(int_max_size_type(-1, 2**31 - 1), size) 28 | self.assertEqual(int_max_size_type(-2**31, 2**31 - 1), size) 29 | self.assertNotEqual(int_max_size_type(-1, 2**31), size) 30 | self.assertNotEqual(int_max_size_type(0, 2**31), size) 31 | 32 | def test_int64(self): 33 | size = "int64_t" 34 | self.assertEqual(int_max_size_type(-1, 2**32), size) 35 | self.assertEqual(int_max_size_type(-1, 2**63 - 1), size) 36 | self.assertEqual(int_max_size_type(-2**63, 2**63 - 1), size) 37 | self.assertNotEqual(int_max_size_type(0, 2**63), size) 38 | with self.assertRaises(NotImplementedError): 39 | int_max_size_type(-1, 2**63) 40 | with self.assertRaises(NotImplementedError): 41 | int_max_size_type(-2**64, 0) 42 | 43 | def test_uint8(self): 44 | size = "uint8_t" 45 | self.assertEqual(int_max_size_type(0, 0), size) 46 | self.assertEqual(int_max_size_type(0, 2**8 - 1), size) 47 | # self.assertNotEqual(int_max_size_type(0, -1), size) 48 | self.assertNotEqual(int_max_size_type(0, 2**8), size) 49 | 50 | def test_uint16(self): 51 | size = "uint16_t" 52 | self.assertEqual(int_max_size_type(0, 256), size) 53 | self.assertEqual(int_max_size_type(0, 2**16 - 1), size) 54 | self.assertNotEqual(int_max_size_type(0, 2**16), size) 55 | 56 | def test_uint32(self): 57 | size = "uint32_t" 58 | self.assertEqual(int_max_size_type(0, 2**16), size) 59 | self.assertEqual(int_max_size_type(0, 2**32 - 1), size) 60 | self.assertNotEqual(int_max_size_type(0, 2**32), size) 61 | 62 | def test_uint64(self): 63 | size = "uint64_t" 64 | self.assertEqual(int_max_size_type(0, 2**32), size) 65 | self.assertEqual(int_max_size_type(0, 2**64 - 1), size) 66 | with self.assertRaises(NotImplementedError): 67 | int_max_size_type(0, 2**64) 68 | 69 | 70 | if __name__ == '__main__': 71 | unittest.main() 72 | -------------------------------------------------------------------------------- /tests/utils.py: -------------------------------------------------------------------------------- 1 | from asn1ate import parser 2 | from asn1ate.sema import * 3 | from tinyber.walker import Walker 4 | 5 | from tinyber.py_nodes import PythonBackend as Backend 6 | from tinyber import py_nodes as nodes 7 | 8 | 9 | def generate(infilename, outfilename): 10 | class FakeArgs(object): 11 | no_standalone = False 12 | 13 | import os 14 | with open(infilename) as f: 15 | asn1def = f.read() 16 | 17 | parse_tree = parser.parse_asn1(asn1def) 18 | modules = build_semantic_model(parse_tree) 19 | assert (len(modules) == 1) 20 | 21 | module_name = outfilename 22 | path = "tests" 23 | args = FakeArgs() 24 | 25 | # pull in the python-specific node implementations 26 | walker = Walker(modules[0], nodes) 27 | walker.walk() 28 | 29 | backend = Backend(args, walker, module_name, path) 30 | backend.generate_code() 31 | 32 | def test_reload(): 33 | import sys 34 | sys.path[:0] = '.' 35 | 36 | # reload tests since we just created a new module 37 | import tests 38 | reload(tests) 39 | -------------------------------------------------------------------------------- /tinyber/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.0.1' 2 | -------------------------------------------------------------------------------- /tinyber/_codec.pyx: -------------------------------------------------------------------------------- 1 | # -*- Mode: Python -*- 2 | 3 | # cython version of codec.py 4 | 5 | # NOTE: the encoder writes into its buffer in *reverse*, with predecrement. 6 | # this makes things much simpler. 7 | 8 | from libc.stdint cimport uint32_t, uint8_t 9 | from cpython cimport PyBytes_FromStringAndSize 10 | from libc.string cimport memcpy 11 | 12 | class DecodingError (Exception): 13 | pass 14 | 15 | class IndefiniteLength (DecodingError): 16 | pass 17 | 18 | class ElementTooLarge (DecodingError): 19 | pass 20 | 21 | class Underflow (DecodingError): 22 | pass 23 | 24 | class UnexpectedType (DecodingError): 25 | pass 26 | 27 | class UnexpectedFlags (DecodingError): 28 | pass 29 | 30 | class ConstraintViolation (DecodingError): 31 | pass 32 | 33 | class BadChoice (DecodingError): 34 | pass 35 | 36 | class ExtraData (DecodingError): 37 | pass 38 | 39 | # flags for BER tags 40 | cdef enum FLAGS: 41 | FLAGS_UNIVERSAL = 0x00 42 | FLAGS_STRUCTURED = 0x20 43 | FLAGS_APPLICATION = 0x40 44 | FLAGS_CONTEXT = 0x80 45 | 46 | # universal BER tags 47 | cdef enum TAGS: 48 | TAGS_BOOLEAN = 0x01 49 | TAGS_INTEGER = 0x02 50 | TAGS_BITSTRING = 0x03 51 | TAGS_OCTET_STRING = 0x04 52 | TAGS_NULL = 0x05 53 | TAGS_OBJID = 0x06 54 | TAGS_OBJDESCRIPTOR = 0x07 55 | TAGS_EXTERNAL = 0x08 56 | TAGS_REAL = 0x09 57 | TAGS_ENUMERATED = 0x0a 58 | TAGS_EMBEDDED_PDV = 0x0b 59 | TAGS_UTF8STRING = 0x0c 60 | TAGS_SEQUENCE = 0x10 61 | TAGS_SET = 0x11 62 | 63 | cdef class Decoder: 64 | cdef readonly bytes data 65 | cdef uint8_t * pdata 66 | cdef uint32_t pos 67 | cdef uint32_t end 68 | 69 | def __init__ (self, bytes data, uint32_t pos=0, uint32_t end=0): 70 | self.data = data 71 | self.pdata = data 72 | self.pos = pos 73 | if end == 0: 74 | end = len(data) 75 | self.end = end 76 | 77 | cdef uint8_t pop_byte (self) except? 255: 78 | cdef uint8_t val 79 | if self.pos + 1 > self.end: 80 | raise Underflow (self) 81 | else: 82 | val = self.pdata[self.pos] 83 | self.pos += 1 84 | return val 85 | 86 | cdef Decoder pop (self, uint32_t nbytes): 87 | cdef Decoder r 88 | if self.pos + nbytes > self.end: 89 | raise Underflow (self) 90 | else: 91 | r = Decoder (self.data, self.pos, self.pos + nbytes) 92 | self.pos += nbytes 93 | return r 94 | 95 | cdef bytes pop_bytes (self, uint32_t nbytes): 96 | if self.pos + nbytes > self.end: 97 | raise Underflow (self) 98 | else: 99 | result = self.data[self.pos:self.pos+nbytes] 100 | self.pos += nbytes 101 | return result 102 | 103 | cpdef done (self): 104 | return self.pos == self.end 105 | 106 | def assert_done (self): 107 | if self.pos != self.end: 108 | raise ExtraData (self) 109 | 110 | cdef uint32_t get_length (self) except? 4294967295: 111 | cdef uint8_t val, lol 112 | cdef uint32_t n 113 | val = self.pop_byte() 114 | if val < 0x80: 115 | # one-byte length 116 | return val 117 | elif val == 0x80: 118 | raise IndefiniteLength (self) 119 | else: 120 | # get length of length 121 | lol = val & 0x7f 122 | if lol > 4: 123 | raise ElementTooLarge (self) 124 | else: 125 | n = 0 126 | while lol: 127 | n = (n << 8) | self.pop_byte() 128 | lol -= 1 129 | return n 130 | 131 | cdef uint32_t get_multibyte_tag (self) except? 4294967295: 132 | cdef uint32_t r = 0 133 | cdef uint8_t val 134 | while 1: 135 | val = self.pop_byte() 136 | r <<= 7 137 | r |= val & 0x7f 138 | if not val & 0x80: 139 | break 140 | return r 141 | 142 | cpdef get_tag (self): 143 | cdef uint8_t b = self.pop_byte() 144 | cdef uint32_t tag = b & 0b00011111 145 | cdef uint8_t flags = b & 0b11100000 146 | if tag == 0b11111: 147 | tag = self.get_multibyte_tag() 148 | return tag, flags 149 | 150 | cdef check (self, uint8_t expected_tag, uint8_t expected_flags=0): 151 | tag, flags = self.get_tag() 152 | if tag != expected_tag: 153 | raise UnexpectedType (tag, expected_tag) 154 | if flags != expected_flags: 155 | raise UnexpectedFlags (flags, expected_flags) 156 | 157 | cpdef next (self, uint8_t expected, uint8_t expected_flags=0): 158 | cdef uint32_t length 159 | self.check (expected, expected_flags) 160 | length = self.get_length() 161 | return self.pop (length) 162 | 163 | cdef get_integer (self, uint32_t length): 164 | # XXX do not declare result as uintXX_t, 165 | # we want to support bignums here. 166 | if length == 0: 167 | return 0 168 | else: 169 | n = self.pop_byte() 170 | length -= 1 171 | if n & 0x80: 172 | # negative 173 | n -= 0x100 174 | else: 175 | while length: 176 | n = n << 8 | self.pop_byte() 177 | length -= 1 178 | return n 179 | 180 | def next_INTEGER (self, min_val, max_val): 181 | self.check (TAGS_INTEGER) 182 | r = self.get_integer (self.get_length()) 183 | if min_val is not None and r < min_val: 184 | raise ConstraintViolation (r, min_val) 185 | if max_val is not None and r > max_val: 186 | raise ConstraintViolation (r, max_val) 187 | return r 188 | 189 | def next_OCTET_STRING (self, min_size, max_size): 190 | self.check (TAGS_OCTET_STRING) 191 | r = self.pop_bytes (self.get_length()) 192 | if min_size is not None and len(r) < min_size: 193 | raise ConstraintViolation (r, min_size) 194 | if max_size is not None and len(r) > max_size: 195 | raise ConstraintViolation (r, max_size) 196 | return r 197 | 198 | def next_BOOLEAN (self): 199 | self.check (TAGS_BOOLEAN) 200 | assert (self.pop_byte() == 1) 201 | return self.pop_byte() != 0 202 | 203 | def next_ENUMERATED (self): 204 | self.check (TAGS_ENUMERATED) 205 | return self.get_integer (self.get_length()) 206 | 207 | def next_APPLICATION (self): 208 | cdef uint32_t tag 209 | cdef uint8_t flags 210 | tag, flags = self.get_tag() 211 | if not flags & FLAGS_APPLICATION: 212 | raise UnexpectedFlags (self, flags, FLAGS_APPLICATION) 213 | else: 214 | return tag, self.pop (self.get_length()) 215 | 216 | cdef class EncoderContext: 217 | cdef Encoder enc 218 | cdef uint32_t tag 219 | cdef uint8_t flags 220 | cdef uint32_t pos 221 | 222 | def __init__ (self, Encoder enc, uint32_t tag, uint8_t flags): 223 | self.enc = enc 224 | self.tag = tag 225 | self.flags = flags 226 | self.pos = enc.pos 227 | 228 | def __enter__ (self): 229 | pass 230 | 231 | def __exit__ (self, t, v, tb): 232 | self.enc.emit_length (self.enc.pos - self.pos) 233 | self.enc.emit_tag (self.tag, self.flags) 234 | 235 | cdef class Encoder: 236 | 237 | cdef bytes buffer 238 | cdef unsigned int size 239 | cdef unsigned int pos 240 | 241 | def __init__ (self, unsigned int size=1024): 242 | self.buffer = PyBytes_FromStringAndSize (NULL, size) 243 | self.size = size 244 | self.pos = 0 245 | 246 | cdef grow (self): 247 | cdef unsigned int data_size = self.pos 248 | cdef unsigned int new_size = (self.size * 3) / 2 # grow by 50% 249 | cdef bytes new_buffer = PyBytes_FromStringAndSize (NULL, new_size) 250 | cdef unsigned char * pnew = new_buffer 251 | cdef unsigned char * pold = self.buffer 252 | # copy old string into place 253 | memcpy (&(pnew[new_size - data_size]), &(pold[self.size - data_size]), data_size) 254 | self.buffer = new_buffer 255 | self.size = new_size 256 | 257 | cdef ensure (self, unsigned int n): 258 | while (self.pos + n) > self.size: 259 | self.grow() 260 | 261 | cdef emit (self, bytearray s): 262 | cdef unsigned int slen = len(s) 263 | cdef unsigned char * pbuf = self.buffer 264 | cdef unsigned char * ps = s 265 | self.ensure (slen) 266 | self.pos += slen 267 | memcpy (&(pbuf[self.size - self.pos]), ps, slen) 268 | 269 | cdef emit_byte (self, uint8_t b): 270 | cdef unsigned char * pbuf = self.buffer 271 | self.ensure (1) 272 | self.pos += 1 273 | pbuf[self.size - self.pos] = b 274 | 275 | def emit_tag (self, uint32_t tag, uint8_t flags): 276 | if tag < 0b11111: 277 | self.emit_byte (tag | flags) 278 | else: 279 | while tag: 280 | if tag < 0x80: 281 | self.emit_byte (tag) 282 | else: 283 | self.emit_byte ((tag & 0x7f) | 0x80) 284 | tag >>= 7 285 | self.emit_byte (0b11111 | flags) 286 | 287 | cdef emit_length (self, unsigned int n): 288 | cdef int c = 0 289 | if n < 0x80: 290 | self.emit_byte (n) 291 | else: 292 | while n: 293 | self.emit_byte (n & 0xff) 294 | n >>= 8 295 | c += 1 296 | self.emit_byte (0x80 | c) 297 | 298 | def TLV (self, tag, flags=0): 299 | return EncoderContext (self, tag, flags) 300 | 301 | def done (self): 302 | return self.buffer[self.size - self.pos : self.size] 303 | 304 | # base types 305 | 306 | # encode an integer, ASN1 style. 307 | # two's complement with the minimum number of bytes. 308 | cdef emit_integer (self, n): 309 | cdef uint8_t byte = 0x80 310 | cdef bint first = 1 311 | n0 = n 312 | n1 = n 313 | while 1: 314 | n1 >>= 8 315 | if n0 == n1: 316 | if n1 == -1 and ((not byte & 0x80) or first): 317 | # negative, but high bit clear 318 | self.emit_byte (0xff) 319 | elif n1 == 0 and byte & 0x80: 320 | # positive, but high bit set 321 | self.emit_byte (0x00) 322 | break 323 | else: 324 | byte = n0 & 0xff 325 | self.emit_byte (byte) 326 | n0 = n1 327 | first = 0 328 | 329 | cpdef emit_INTEGER (self, n): 330 | with self.TLV (TAGS_INTEGER): 331 | self.emit_integer (n) 332 | 333 | cpdef emit_OCTET_STRING (self, s): 334 | with self.TLV (TAGS_OCTET_STRING): 335 | self.emit (s) 336 | 337 | cpdef emit_BOOLEAN (self, v): 338 | with self.TLV (TAGS_BOOLEAN): 339 | if v: 340 | self.emit_byte (b'\xff') 341 | else: 342 | self.emit_byte (b'\x00') 343 | 344 | class ASN1: 345 | value = None 346 | def __init__ (self, value=None): 347 | self.value = value 348 | def encode (self): 349 | cdef Encoder e = Encoder() 350 | self._encode (e) 351 | return e.done() 352 | def decode (self, data): 353 | b = Decoder (data) 354 | self._decode (b) 355 | def __repr__ (self): 356 | return '<%s %r>' % (self.__class__.__name__, self.value) 357 | 358 | class SEQUENCE (ASN1): 359 | __slots__ = () 360 | def __init__ (self, **args): 361 | for k, v in args.iteritems(): 362 | setattr (self, k, v) 363 | def __repr__ (self): 364 | r = [] 365 | for name in self.__slots__: 366 | r.append ('%s=%r' % (name, getattr (self, name))) 367 | return '<%s %s>' % (self.__class__.__name__, ' '.join (r)) 368 | 369 | class CHOICE (ASN1): 370 | tags_f = {} 371 | tags_r = {} 372 | def _decode (self, Decoder src): 373 | cdef uint8_t tag 374 | cdef Decoder src0 375 | tag, src0 = src.next_APPLICATION() 376 | self.value = self.tags_r[tag]() 377 | self.value._decode (src0) 378 | def _encode (self, Encoder dst): 379 | for klass, tag in self.tags_f.iteritems(): 380 | if isinstance (self.value, klass): 381 | with dst.TLV (tag, FLAGS_APPLICATION | FLAGS_STRUCTURED): 382 | self.value._encode (dst) 383 | return 384 | raise BadChoice (self.value) 385 | 386 | class ENUMERATED (ASN1): 387 | tags_f = {} 388 | tags_r = {} 389 | value = 'NoValueDefined' 390 | def _decode (self, Decoder src): 391 | v = src.next_ENUMERATED() 392 | self.value = self.tags_r[v] 393 | def _encode (self, Encoder dst): 394 | with dst.TLV (TAGS_ENUMERATED): 395 | dst.emit_integer (self.tags_f[self.value]) 396 | def __repr__ (self): 397 | return '<%s %s>' % (self.__class__.__name__, self.value) 398 | -------------------------------------------------------------------------------- /tinyber/ber.py: -------------------------------------------------------------------------------- 1 | # -*- Mode: Python -*- 2 | 3 | # how many bytes to represent length (the 'L' in TLV). 4 | 5 | def length_of_length (n): 6 | if n < 0x80: 7 | return 1 8 | else: 9 | r = 1 10 | while n: 11 | n >>= 8 12 | r += 1 13 | return r 14 | 15 | def length_of_integer (n): 16 | i = 0 17 | n0 = n 18 | byte = 0x80 19 | r = 0 20 | while 1: 21 | n >>= 8 22 | if n0 == n: 23 | if n == -1 and ((not byte & 0x80) or (i == 0)): 24 | # negative, but high bit clear 25 | r += 1 26 | i += 1 27 | elif n == 0 and (byte & 0x80): 28 | # positive, but high bit set 29 | r += 1 30 | i += 1 31 | break 32 | else: 33 | byte = n0 & 0xff 34 | r += 1 35 | i += 1 36 | n0 = n 37 | return r 38 | -------------------------------------------------------------------------------- /tinyber/c_nodes.py: -------------------------------------------------------------------------------- 1 | # -*- Mode: Python -*- 2 | 3 | from tinyber import nodes 4 | from tinyber.writer import Writer 5 | import os 6 | import sys 7 | 8 | def csafe (s): 9 | return s.replace ('-', '_') 10 | 11 | def int_max_size_type (min_size, max_size): 12 | if max_size is None: 13 | # unconstrained int type. 14 | return 'asn1int_t' 15 | 16 | # compensate for 2's-complement negative numbers 17 | min_compare = min_size + 1 if min_size < 0 else min_size 18 | max_compare = max_size + 1 if max_size < 0 else max_size 19 | signed = min_size < 0 or max_size < 0 20 | compare = max(abs(min_compare), abs(max_compare)) 21 | if signed: 22 | if compare < 2**7: 23 | return 'int8_t' 24 | elif compare < 2**15: 25 | return 'int16_t' 26 | elif compare < 2**31: 27 | return 'int32_t' 28 | elif compare < 2**63: 29 | return 'int64_t' 30 | else: 31 | if compare < 2**8: 32 | return 'uint8_t' 33 | elif compare < 2**16: 34 | return 'uint16_t' 35 | elif compare < 2**32: 36 | return 'uint32_t' 37 | elif compare < 2**64: 38 | return 'uint64_t' 39 | raise NotImplementedError() 40 | 41 | class c_base_type (nodes.c_base_type): 42 | 43 | def emit (self, out): 44 | type_name, min_size, max_size = self.attrs 45 | if type_name == 'OCTET STRING' or type_name == 'UTF8String': 46 | out.writelines ('struct {') 47 | with out.indent(): 48 | out.writelines ( 49 | 'uint8_t val[%s];' % (max_size,), 50 | 'int len;' 51 | ) 52 | out.write ('}', True) 53 | elif type_name == 'BOOLEAN': 54 | out.write ('asn1bool_t', True) 55 | elif type_name == 'INTEGER': 56 | out.write (int_max_size_type (min_size, max_size), True) 57 | elif type_name == 'NULL': 58 | pass 59 | else: 60 | import pdb 61 | pdb.set_trace() 62 | 63 | def emit_decode (self, out, lval, src): 64 | type_name, min_size, max_size = self.attrs 65 | out.writelines ( 66 | 'TYB_CHECK (decode_TLV (&tlv, %s));' % (src,), 67 | 'TYB_FAILIF (tlv.type != %s);' % (self.tag_map[type_name],), 68 | ) 69 | if type_name == 'OCTET STRING' or type_name == 'UTF8String': 70 | out.writelines ( 71 | 'TYB_FAILIF(tlv.length > %d);' % (max_size,), 72 | 'memcpy ((*%s).val, tlv.value, tlv.length);' % (lval,), 73 | '(*%s).len = tlv.length;' % (lval,), 74 | ) 75 | elif type_name == 'INTEGER': 76 | with out.scope(): 77 | out.writelines ('asn1int_t intval = decode_INTEGER (&tlv);',) 78 | if max_size is not None: 79 | out.writelines ('TYB_FAILIF(intval > %s);' % (max_size,),) 80 | if min_size is not None: 81 | out.writelines ('TYB_FAILIF(intval < %s);' % (min_size,),) 82 | out.writelines ('*(%s) = intval;' % (lval,)) 83 | elif type_name == 'BOOLEAN': 84 | out.writelines ('*(%s) = decode_BOOLEAN (&tlv);' % (lval,),) 85 | elif type_name == 'NULL': 86 | pass 87 | else: 88 | import pdb 89 | pdb.set_trace() 90 | 91 | def emit_encode (self, out, dst, src): 92 | type_name, min_size, max_size = self.attrs 93 | if type_name == 'OCTET STRING' or type_name == 'UTF8String': 94 | out.writelines ('TYB_CHECK (encode_OCTET_STRING (%s, (%s)->val, (%s)->len));' % (dst, src, src)) 95 | elif type_name == 'INTEGER': 96 | with out.scope(): 97 | out.writelines ( 98 | 'asn1int_t intval = *%s;' % (src,), 99 | 'TYB_CHECK (encode_INTEGER (%s, &intval));' % (dst,), 100 | ) 101 | elif type_name == 'BOOLEAN': 102 | with out.scope(): 103 | out.writelines ( 104 | 'asn1bool_t boolval = *%s;' % (src,), 105 | 'TYB_CHECK (encode_BOOLEAN (%s, &boolval));' % (dst,), 106 | ) 107 | elif type_name == 'NULL': 108 | out.writelines ('encode_NULL (%s)' % (dst,)) 109 | else: 110 | import pdb 111 | pdb.set_trace() 112 | 113 | class c_sequence (nodes.c_sequence): 114 | 115 | def emit (self, out): 116 | name, slots = self.attrs 117 | types = self.subs 118 | out.writelines ('struct %s {' % (name,)) 119 | with out.indent(): 120 | for i in range (len (slots)): 121 | slot_name = csafe (slots[i]) 122 | slot_type = types[i] 123 | slot_type.emit (out) 124 | out.write (' %s;' % (slot_name,)) 125 | out.newline() 126 | out.write ('}') 127 | 128 | def emit_decode (self, out, lval, src): 129 | name, slots = self.attrs 130 | types = self.subs 131 | out.writelines ( 132 | 'TYB_CHECK (decode_TLV (&tlv, %s));' % (src,), 133 | 'TYB_FAILIF (tlv.type != TAG_SEQUENCE);', 134 | '{' 135 | ) 136 | with out.indent(): 137 | out.writelines ( 138 | 'buf_t src0;', 139 | 'init_ibuf (&src0, tlv.value, tlv.length);' 140 | ) 141 | for i in range (len (slots)): 142 | out.writelines ('// slot %s' % (slots[i],)) 143 | slot_type = types[i] 144 | slot_type.emit_decode (out, '&(%s->%s)' % (lval, csafe (slots[i])), '&src0') 145 | out.writelines ('TYB_FAILIF (src0.pos != src0.size);') 146 | out.writelines ('}') 147 | 148 | def emit_encode (self, out, dst, src): 149 | name, slots = self.attrs 150 | types = self.subs 151 | out.writelines ('{') 152 | with out.indent(): 153 | out.writelines ('unsigned int mark = %s->pos;' % (dst,)) 154 | for i in reversed (range (len (slots))): 155 | out.writelines ('// slot %s' % (slots[i],)) 156 | slot_type = types[i] 157 | slot_type.emit_encode (out, dst, '&(%s->%s)' % (src, csafe (slots[i]))) 158 | out.writelines ('TYB_CHECK (encode_TLV (%s, mark, TAG_SEQUENCE, FLAG_STRUCTURED));' % (dst,)) 159 | out.writelines ('}') 160 | 161 | class c_sequence_of (nodes.c_sequence_of): 162 | 163 | TAG_NAME = 'TAG_SEQUENCE' 164 | 165 | def emit (self, out): 166 | min_size, max_size, = self.attrs 167 | [seq_type] = self.subs 168 | out.writelines ('struct {') 169 | with out.indent(): 170 | seq_type.emit (out) 171 | out.write (' val[%s];' % (max_size,)) 172 | out.newline() 173 | out.writelines ('int len;') 174 | out.write ('}', True) 175 | 176 | def emit_decode (self, out, lval, src): 177 | min_size, max_size, = self.attrs 178 | [seq_type] = self.subs 179 | out.writelines ('{') 180 | with out.indent(): 181 | out.writelines ( 182 | 'buf_t src1;', 183 | 'int i;', 184 | 'TYB_CHECK (decode_TLV (&tlv, %s));' % (src,), 185 | 'TYB_FAILIF (tlv.type != %s);' % (self.TAG_NAME,), 186 | 'init_ibuf (&src1, tlv.value, tlv.length);', 187 | '(%s)->len = 0;' % (lval,), 188 | 'for (i=0; (src1.pos < src1.size); i++) {', 189 | ) 190 | with out.indent(): 191 | out.writelines ('TYB_FAILIF (i >= %s);' % (max_size,),) 192 | seq_type.emit_decode (out, '%s.val[i]' % (lval,), '&src1') 193 | out.writelines ('(%s)->len = i + 1;' % (lval,)) 194 | out.writelines ('}') 195 | if min_size: 196 | out.writelines ('TYB_FAILIF ((%s)->len < %d);' % (lval, min_size)) 197 | out.writelines ('}') 198 | 199 | def emit_encode (self, out, dst, src): 200 | min_size, max_size, = self.attrs 201 | [seq_type] = self.subs 202 | out.writelines ('{') 203 | with out.indent(): 204 | out.writelines ( 205 | 'int i;', 206 | 'unsigned int mark = %s->pos;' % (dst,), 207 | 'int alen = (%s)->len;' % (src,), 208 | 'for (i=0; i < alen; i++) {', 209 | ) 210 | with out.indent(): 211 | out.writelines ('TYB_FAILIF (i >= %s);' % (max_size,),) 212 | seq_type.emit_encode (out, dst, '&((%s)->val[alen-(i+1)])' % (src,)) 213 | out.writelines ( 214 | '}', 215 | 'TYB_CHECK (encode_TLV (%s, mark, %s, FLAG_STRUCTURED));' % (dst, self.TAG_NAME) 216 | ) 217 | out.writelines ('}') 218 | 219 | # NOTE parent class 220 | class c_set_of (c_sequence_of): 221 | TAG_NAME = 'TAG_SET' 222 | 223 | class c_choice (nodes.c_choice): 224 | 225 | def emit (self, out): 226 | name, slots, tags = self.attrs 227 | out.writelines ('struct %s {' % (name,)) 228 | with out.indent(): 229 | out.writelines ('%s_PR present;' % (name,)) 230 | out.writelines ('union %s_u {' % (name,)) 231 | with out.indent(): 232 | for i in range (len (slots)): 233 | slot_name = csafe (slots[i]) 234 | type_name = self.subs[i].name() 235 | out.writelines ('%s_t %s;' % (type_name, slot_name)) 236 | out.writelines ('} choice;') 237 | out.write ('}') 238 | 239 | def emit_enum (self, out): 240 | name, slots, tags = self.attrs 241 | # first emit the enum for type_PR 242 | out.writelines ('typedef enum {') 243 | with out.indent(): 244 | for i in range (len (slots)): 245 | out.writelines ('%s_PR_%s = %s,' % (name, csafe (slots[i]), tags[i])) 246 | out.writelines ('} %s_PR;' % (name,), '') 247 | 248 | def emit_decode (self, out, lval, src): 249 | name, slots, tags = self.attrs 250 | types = self.subs 251 | out.writelines ('{') 252 | with out.indent(): 253 | out.writelines ( 254 | 'buf_t src0;', 255 | 'TYB_CHECK (decode_TLV (&tlv, %s));' % (src,), 256 | 'init_ibuf (&src0, tlv.value, tlv.length);', 257 | 'switch (tlv.type) {', 258 | ) 259 | with out.indent(): 260 | for i in range (len (slots)): 261 | type_name = types[i].name() 262 | tag_name = csafe (slots[i]) 263 | out.writelines ( 264 | 'case (%s):' % (tags[i],), 265 | ' %s->present = %s_PR_%s;' % (lval, name, tag_name), 266 | ' TYB_CHECK (decode_%s (&(%s->choice.%s), &src0));' % (type_name, lval, tag_name), 267 | ' break;', 268 | ) 269 | out.writelines ( 270 | 'default:', ' return -1;', ' break;' 271 | ) 272 | out.writelines ('}') 273 | out.writelines ('}') 274 | 275 | def emit_encode (self, out, dst, src): 276 | name, slots, tags = self.attrs 277 | types = self.subs 278 | out.writelines ('{') 279 | with out.indent(): 280 | out.writelines ( 281 | 'unsigned int mark = %s->pos;' % (dst,), 282 | 'switch (%s->present) {' % (src,), 283 | ) 284 | with out.indent(): 285 | for i in range (len (slots)): 286 | type_name = types[i].name() 287 | tag_name = csafe (slots[i]) 288 | out.writelines ( 289 | 'case %s:' % (tags[i],), 290 | ' TYB_CHECK (encode_%s (%s, &(%s->choice.%s)));' % (type_name, dst, src, tag_name), 291 | ' break;', 292 | ) 293 | out.writelines ( 294 | 'default:', ' return -1;', ' break;' 295 | ) 296 | out.writelines ( 297 | '}', 298 | 'TYB_CHECK (encode_TLV (%s, mark, %s->present, FLAG_APPLICATION | FLAG_STRUCTURED));' % (dst, src), 299 | ) 300 | out.writelines ('}') 301 | 302 | class c_enumerated (nodes.c_enumerated): 303 | 304 | def emit (self, out): 305 | defname, alts, = self.attrs 306 | if defname is not None: 307 | prefix = '%s_' % (defname,) 308 | else: 309 | prefix = '' 310 | out.write ('enum {\n') 311 | with out.indent(): 312 | for name, val in alts: 313 | if val is not None: 314 | out.writelines ('%s%s = %s,' % (prefix, csafe (name), val)) 315 | else: 316 | out.writelines ('%s%s,' % (prefix, csafe (name))) 317 | out.write ('}') 318 | 319 | def emit_decode (self, out, lval, src): 320 | defname, alts, = self.attrs 321 | out.writelines ('{') 322 | with out.indent(): 323 | out.writelines ( 324 | 'TYB_CHECK (decode_TLV (&tlv, %s));' % (src,), 325 | 'TYB_FAILIF (tlv.type != TAG_ENUMERATED);', 326 | ) 327 | with out.scope(): 328 | out.writelines ( 329 | 'asn1int_t intval = decode_INTEGER (&tlv);', 330 | 'switch (intval) {' 331 | ) 332 | with out.indent(): 333 | for name, val in alts: 334 | out.writelines ('case %s: break;' % (val,)) 335 | out.writelines ('default: return -1;') 336 | out.writelines ('}') 337 | out.writelines ('*%s = intval;' % (lval,)) 338 | out.writelines ('}') 339 | 340 | def emit_encode (self, out, dst, src): 341 | defname, alts, = self.attrs 342 | with out.scope(): 343 | out.writelines ( 344 | 'asn1int_t intval = *%s;' % (src,), 345 | 'TYB_CHECK (encode_ENUMERATED (%s, &intval));' % (dst,), 346 | ) 347 | 348 | class c_defined (nodes.c_defined): 349 | 350 | def emit (self, out): 351 | name, max_size = self.attrs 352 | out.write ('%s_t' % (name,), True) 353 | 354 | def emit_decode (self, out, lval, src): 355 | type_name, max_size = self.attrs 356 | out.writelines ('TYB_CHECK (decode_%s (%s, %s));' % (type_name, lval, src),) 357 | 358 | def emit_encode (self, out, dst, src): 359 | type_name, max_size = self.attrs 360 | out.writelines ('TYB_CHECK (encode_%s (%s, %s));' % (type_name, dst, src),) 361 | 362 | 363 | class CBackend: 364 | 365 | def __init__ (self, args, walker, module_name, path): 366 | self.args = args 367 | self.walker = walker 368 | self.module_name = module_name 369 | self.path = path 370 | self.base_path = os.path.join(path, module_name) 371 | 372 | def gen_decoder (self, type_name, type_decl, node): 373 | # generate a decoder for a type assignment. 374 | sig = 'int decode_%s (%s_t * dst, buf_t * src)' % (type_name, type_name) 375 | self.hout.writelines (sig + ';') 376 | self.cout.writelines (sig, '{') 377 | with self.cout.indent(): 378 | self.cout.writelines ( 379 | 'asn1raw_t tlv;', 380 | ) 381 | node.emit_decode (self.cout, 'dst', 'src') 382 | self.cout.writelines ('return 0;') 383 | self.cout.writelines ('}', '') 384 | 385 | def gen_encoder (self, type_name, type_decl, node): 386 | # generate an encoder for a type assignment 387 | sig = 'int encode_%s (buf_t * dst, const %s_t * src)' % (type_name, type_name) 388 | self.cout.writelines (sig, '{') 389 | self.hout.writelines (sig + ';') 390 | with self.cout.indent(): 391 | node.emit_encode (self.cout, 'dst', 'src') 392 | self.cout.writelines ('return 0;') 393 | self.cout.writelines ('}', '') 394 | 395 | def gen_codec_funs (self, type_name, type_decl, node): 396 | self.gen_decoder (type_name, type_decl, node) 397 | self.gen_encoder (type_name, type_decl, node) 398 | 399 | def copyfiles(self): 400 | import shutil 401 | pkg_dir, _ = os.path.split(__file__) 402 | tinyberc = os.path.join(pkg_dir, "data", "tinyber.c") 403 | tinyberh = os.path.join(pkg_dir, "data", "tinyber.h") 404 | shutil.copy(tinyberc, self.path) 405 | shutil.copy(tinyberh, self.path) 406 | 407 | def generate_code (self): 408 | self.copyfiles() 409 | self.hout = Writer (open (self.base_path + '.h', 'w')) 410 | self.cout = Writer (open (self.base_path + '.c', 'w')) 411 | self.hout.writelines ( 412 | '', 413 | '// generated by %r' % sys.argv, 414 | '// *** do not edit ***', 415 | '', 416 | '#ifndef _%s_H_' % self.module_name.upper(), 417 | '#define _%s_H_' % self.module_name.upper(), 418 | '', 419 | '#include ', 420 | '#include ', 421 | '#include "tinyber.h"', 422 | '', 423 | ) 424 | self.cout.writelines ( 425 | '', 426 | '// generated by %r' % sys.argv, 427 | '// *** do not edit ***', 428 | '', 429 | '#include "%s.h"' % (self.module_name,), 430 | '', 431 | # needed for the inline defs when compiled with inlining disabled. 432 | 'extern void init_obuf (buf_t * self, uint8_t * buffer, unsigned int size);', 433 | 'extern void init_ibuf (buf_t * self, uint8_t * buffer, unsigned int size);', 434 | '', 435 | ) 436 | 437 | self.tag_assignments = self.walker.tag_assignments 438 | 439 | # generate typedefs and prototypes. 440 | out = self.hout 441 | for (type_name, node, type_decl) in self.walker.defined_types: 442 | if isinstance (node, c_choice): 443 | node.emit_enum (out) 444 | out.write ('typedef ') 445 | node.emit (out) 446 | out.writelines ( 447 | ' %s_t;' % (type_name,), 448 | '#define %s_MAX_SIZE %d' % (type_name, node.max_size()), 449 | '' 450 | ) 451 | 452 | for (type_name, node, type_decl) in self.walker.defined_types: 453 | self.gen_codec_funs (type_name, type_decl, node) 454 | 455 | self.hout.writelines ( 456 | '', 457 | '#endif // _%s_H_' % self.module_name.upper() 458 | ) 459 | self.hout.close() 460 | self.cout.close() 461 | -------------------------------------------------------------------------------- /tinyber/codec.py: -------------------------------------------------------------------------------- 1 | # -*- Mode: Python -*- 2 | 3 | # base for python codecs. 4 | 5 | # NOTE: the encoder accumulates in *reverse*. 6 | 7 | 8 | class DecodingError(Exception): 9 | pass 10 | 11 | 12 | class IndefiniteLength(DecodingError): 13 | pass 14 | 15 | 16 | class ElementTooLarge(DecodingError): 17 | pass 18 | 19 | 20 | class Underflow(DecodingError): 21 | pass 22 | 23 | 24 | class UnexpectedType(DecodingError): 25 | pass 26 | 27 | 28 | class UnexpectedFlags(DecodingError): 29 | pass 30 | 31 | 32 | class ConstraintViolation(DecodingError): 33 | pass 34 | 35 | 36 | class BadChoice(DecodingError): 37 | pass 38 | 39 | 40 | class ExtraData(DecodingError): 41 | pass 42 | 43 | 44 | class FLAG: 45 | UNIVERSAL = 0x00 46 | STRUCTURED = 0x20 47 | APPLICATION = 0x40 48 | CONTEXT = 0x80 49 | 50 | 51 | class TAG: 52 | BOOLEAN = 0x01 53 | INTEGER = 0x02 54 | BITSTRING = 0x03 55 | OCTETSTRING = 0x04 56 | NULLTAG = 0x05 57 | OID = 0x06 58 | ENUMERATED = 0x0A 59 | UTF8STRING = 0x0C 60 | SEQUENCE = 0x10 61 | SET = 0x11 62 | 63 | 64 | class Decoder: 65 | 66 | def __init__(self, data, pos=0, end=None): 67 | self.data = data 68 | self.pos = pos 69 | if end is None: 70 | end = len(data) 71 | self.end = end 72 | 73 | def pop_byte(self): 74 | if self.pos + 1 > self.end: 75 | raise Underflow(self) 76 | else: 77 | try: 78 | val = ord(self.data[self.pos]) 79 | except TypeError: 80 | val = self.data[self.pos] 81 | self.pos += 1 82 | return val 83 | 84 | def pop(self, nbytes): 85 | if self.pos + nbytes > self.end: 86 | raise Underflow(self) 87 | else: 88 | r = Decoder(self.data, self.pos, self.pos + nbytes) 89 | self.pos += nbytes 90 | return r 91 | 92 | def pop_bytes(self, nbytes): 93 | if self.pos + nbytes > self.end: 94 | raise Underflow(self) 95 | else: 96 | result = self.data[self.pos:self.pos+nbytes] 97 | self.pos += nbytes 98 | return result 99 | 100 | def done(self): 101 | return self.pos == self.end 102 | 103 | def assert_done(self): 104 | if self.pos != self.end: 105 | raise ExtraData(self) 106 | 107 | def get_length(self): 108 | val = self.pop_byte() 109 | if val < 0x80: 110 | # one-byte length 111 | return val 112 | elif val == 0x80: 113 | raise IndefiniteLength(self) 114 | else: 115 | # get length of length 116 | lol = val & 0x7f 117 | if lol > 4: 118 | raise ElementTooLarge(self) 119 | else: 120 | n = 0 121 | while lol: 122 | n = (n << 8) | self.pop_byte() 123 | lol -= 1 124 | return n 125 | 126 | def get_multibyte_tag(self): 127 | r = 0 128 | while 1: 129 | val = self.pop_byte() 130 | r <<= 7 131 | r |= val & 0x7f 132 | if not val & 0x80: 133 | break 134 | return r 135 | 136 | def get_tag(self): 137 | b = self.pop_byte() 138 | tag = b & 0b11111 139 | flags = b & 0b1100000 140 | if tag == 0b11111: 141 | tag = self.get_multibyte_tag() 142 | return tag, flags 143 | 144 | def check(self, expected_tag, expected_flags=0): 145 | tag, flags = self.get_tag() 146 | if tag != expected_tag: 147 | raise UnexpectedType(tag, expected_tag) 148 | if flags != expected_flags: 149 | raise UnexpectedFlags(flags, expected_flags) 150 | 151 | def next(self, expected, flags=0): 152 | self.check(expected, flags) 153 | length = self.get_length() 154 | return self.pop(length) 155 | 156 | def get_integer(self, length): 157 | if length == 0: 158 | return 0 159 | else: 160 | n = self.pop_byte() 161 | length -= 1 162 | if n & 0x80: 163 | # negative 164 | n -= 0x100 165 | else: 166 | while length: 167 | n = n << 8 | self.pop_byte() 168 | length -= 1 169 | return n 170 | 171 | def next_INTEGER(self, min_val, max_val): 172 | self.check(TAG.INTEGER) 173 | r = self.get_integer(self.get_length()) 174 | if min_val is not None and r < min_val: 175 | raise ConstraintViolation(r, min_val) 176 | if max_val is not None and r > max_val: 177 | raise ConstraintViolation(r, max_val) 178 | return r 179 | 180 | def next_OCTET_STRING(self, min_size, max_size): 181 | self.check(TAG.OCTETSTRING) 182 | r = self.pop_bytes(self.get_length()) 183 | if min_size is not None and len(r) < min_size: 184 | raise ConstraintViolation(r, min_size) 185 | if max_size is not None and len(r) > max_size: 186 | raise ConstraintViolation(r, max_size) 187 | return r 188 | 189 | def next_BOOLEAN(self): 190 | self.check(TAG.BOOLEAN) 191 | assert(self.pop_byte() == 1) 192 | return self.pop_byte() != 0 193 | 194 | def next_ENUMERATED(self): 195 | self.check(TAG.ENUMERATED) 196 | return self.get_integer(self.get_length()) 197 | 198 | def next_APPLICATION(self): 199 | tag, flags = self.get_tag() 200 | if not flags & FLAG.APPLICATION: 201 | raise UnexpectedFlags(self, flags, FLAG.APPLICATION) 202 | else: 203 | return tag, self.pop(self.get_length()) 204 | 205 | 206 | class EncoderContext: 207 | 208 | def __init__(self, enc, tag, flags): 209 | self.enc = enc 210 | self.tag = tag 211 | self.flags = flags 212 | self.pos = enc.length 213 | 214 | def __enter__(self): 215 | pass 216 | 217 | def __exit__(self, t, v, tb): 218 | self.enc.emit_length(self.enc.length - self.pos) 219 | self.enc.emit_tag(self.tag, self.flags) 220 | 221 | 222 | class Encoder: 223 | 224 | def __init__(self): 225 | self.r = [] 226 | self.length = 0 227 | 228 | def _chr(self, x): 229 | return bytearray((x,)) 230 | 231 | def emit(self, data): 232 | self.r.insert(0, data) 233 | self.length += len(data) 234 | 235 | def emit_length(self, n): 236 | if n < 0x80: 237 | self.emit(self._chr(n)) 238 | else: 239 | r = [] 240 | while n: 241 | r.insert(0, self._chr(n & 0xff)) 242 | n >>= 8 243 | r.insert(0, self._chr(0x80 | len(r))) 244 | self.emit(bytearray().join(r)) 245 | 246 | def emit_tag(self, tag, flags=0): 247 | if tag < 0x1f: 248 | self.emit(self._chr(tag | flags)) 249 | else: 250 | while tag: 251 | if tag < 0x80: 252 | self.emit(self._chr(tag)) 253 | else: 254 | self.emit(self._chr((tag & 0x7f) | 0x80)) 255 | tag >>= 7 256 | self.emit(self._chr(0x1f | flags)) 257 | 258 | def TLV(self, tag, flags=0): 259 | return EncoderContext(self, tag, flags) 260 | 261 | def done(self): 262 | return bytearray().join(self.r) 263 | 264 | # base types 265 | 266 | # encode an integer, ASN1 style. 267 | # two's complement with the minimum number of bytes. 268 | def emit_integer(self, n): 269 | i = 0 270 | n0 = n 271 | byte = 0x80 272 | r = [] 273 | while 1: 274 | n >>= 8 275 | if n0 == n: 276 | if n == -1 and ((not byte & 0x80) or i == 0): 277 | # negative, but high bit clear 278 | r.insert(0, self._chr(0xff)) 279 | i = i + 1 280 | elif n == 0 and (byte & 0x80): 281 | # positive, but high bit set 282 | r.insert(0, self._chr(0x00)) 283 | i = i + 1 284 | break 285 | else: 286 | byte = n0 & 0xff 287 | r.insert(0, self._chr(byte)) 288 | i += 1 289 | n0 = n 290 | self.emit(bytearray().join(r)) 291 | 292 | def emit_INTEGER(self, n): 293 | with self.TLV(TAG.INTEGER): 294 | self.emit_integer(n) 295 | 296 | def emit_OCTET_STRING(self, s): 297 | with self.TLV(TAG.OCTETSTRING): 298 | self.emit(s) 299 | 300 | def emit_BOOLEAN(self, v): 301 | with self.TLV(TAG.BOOLEAN): 302 | if v: 303 | self.emit(b'\xff') 304 | else: 305 | self.emit(b'\x00') 306 | 307 | 308 | class ASN1: 309 | value = None 310 | 311 | def __init__(self, value=None): 312 | self.value = value 313 | 314 | def encode(self): 315 | e = Encoder() 316 | self._encode(e) 317 | return e.done() 318 | 319 | def decode(self, data): 320 | b = Decoder(data) 321 | self._decode(b) 322 | 323 | def __repr__(self): 324 | return '<%s %r>' % (self.__class__.__name__, self.value) 325 | 326 | 327 | class SEQUENCE(ASN1): 328 | __slots__ = () 329 | 330 | def __init__(self, **args): 331 | for k, v in args.iteritems(): 332 | setattr(self, k, v) 333 | 334 | def __repr__(self): 335 | r = [] 336 | for name in self.__slots__: 337 | r.append('%s=%r' % (name, getattr(self, name))) 338 | return '<%s %s>' % (self.__class__.__name__, ' '.join(r)) 339 | 340 | 341 | class CHOICE(ASN1): 342 | tags_f = {} 343 | tags_r = {} 344 | 345 | def _decode(self, src): 346 | tag, src = src.next_APPLICATION() 347 | self.value = self.tags_r[tag]() 348 | self.value._decode(src) 349 | 350 | def _encode(self, dst): 351 | for klass, tag in self.tags_f.iteritems(): 352 | if isinstance(self.value, klass): 353 | with dst.TLV(tag, FLAG.APPLICATION | FLAG.STRUCTURED): 354 | self.value._encode(dst) 355 | return 356 | raise BadChoice(self.value) 357 | 358 | 359 | class ENUMERATED(ASN1): 360 | tags_f = {} 361 | tags_r = {} 362 | value = 'NoValueDefined' 363 | 364 | def _decode(self, src): 365 | v = src.next_ENUMERATED() 366 | self.value = self.tags_r[v] 367 | 368 | def _encode(self, dst): 369 | with dst.TLV(TAG.ENUMERATED): 370 | dst.emit_integer(self.tags_f[self.value]) 371 | 372 | def __repr__(self): 373 | return '<%s %s>' % (self.__class__.__name__, self.value) 374 | 375 | 376 | # try to pull in cython version if available. 377 | try: 378 | from tinyber._codec import * 379 | except ImportError: 380 | pass 381 | -------------------------------------------------------------------------------- /tinyber/data/tinyber.c: -------------------------------------------------------------------------------- 1 | // -*- Mode: C -*- 2 | 3 | #include 4 | #include 5 | #include 6 | #include "tinyber.h" 7 | 8 | // -------------------------------------------------------------------------------- 9 | // buffer interface 10 | // -------------------------------------------------------------------------------- 11 | 12 | // output buffers are written in reverse, using predecrement. 13 | // [this is the most efficient way to render BER - you write 14 | // a sub-object first, then prepend its length and type byte]. 15 | 16 | // emit data (of length ) into output buffer . 17 | static 18 | int 19 | emit (buf_t * dst, const uint8_t * src, int n) 20 | { 21 | if (dst->pos < n) { 22 | return -1; 23 | } else { 24 | dst->pos -= n; 25 | memcpy (dst->buffer + dst->pos, src, n); 26 | return 0; 27 | } 28 | } 29 | 30 | // emit a single byte into an output stream. 31 | static 32 | int 33 | emit_byte (buf_t * self, uint8_t b) 34 | { 35 | if (self->pos < 1) { 36 | return -1; 37 | } else { 38 | self->buffer[--self->pos] = b; 39 | return 0; 40 | } 41 | } 42 | 43 | // ensure there are at least bytes of input available. 44 | static 45 | int 46 | ensure_input (const buf_t * self, unsigned int n) 47 | { 48 | if ((self->pos + n) <= self->size) { 49 | return 0; 50 | } else { 51 | return -1; 52 | } 53 | } 54 | 55 | // ensure there are at least bytes of output available. 56 | static 57 | int 58 | ensure_output (const buf_t * self, unsigned int n) 59 | { 60 | if (self->pos >= n) { 61 | return 0; 62 | } else { 63 | return -1; 64 | } 65 | } 66 | 67 | // -------------------------------------------------------------------------------- 68 | // encoder 69 | // -------------------------------------------------------------------------------- 70 | 71 | // how many bytes to represent length (the 'L' in TLV). 72 | static 73 | int 74 | length_of_length (asn1int_t n) 75 | { 76 | if (n < 0x80) { 77 | return 1; 78 | } else { 79 | int r = 1; 80 | while (n) { 81 | n >>= 8; 82 | r += 1; 83 | } 84 | return r; 85 | } 86 | } 87 | 88 | // encode length into a byte buffer. 89 | static 90 | void 91 | encode_length (asn1int_t len, int n, uint8_t * buffer) 92 | { 93 | // caller must ensure room. see length_of_length above. 94 | if (len < 0x80) { 95 | buffer[0] = (uint8_t) len; 96 | } else { 97 | int i; 98 | buffer[0] = 0x80 | ((n-1) & 0x7f); 99 | for (i=1; i < n; i++) { 100 | buffer[n-i] = len & 0xff; 101 | len >>= 8; 102 | } 103 | } 104 | } 105 | 106 | // encode an integer, ASN1 style. 107 | // two's complement with the minimum number of bytes. 108 | static 109 | int 110 | encode_integer (buf_t * o, asn1int_t n) 111 | { 112 | asn1int_t n0 = n; 113 | asn1int_t i = 0; 114 | uint8_t byte = 0x80; // for n==0 115 | uint8_t result[16]; // plenty of room up to uint128_t 116 | while (1) { 117 | n >>= 8; 118 | if (n0 == n) { 119 | if ((n == -1) && (!(byte & 0x80) || (i == 0))) { 120 | // negative, but high bit clear 121 | result[15-i] = 0xff; 122 | i += 1; 123 | } else if ((n == 0) && (byte & 0x80)) { 124 | // positive, but high bit set 125 | result[15-i] = 0x00; 126 | i += 1; 127 | } 128 | break; 129 | } else { 130 | byte = n0 & 0xff; 131 | result[15-i] = byte; 132 | i += 1; 133 | n0 = n; 134 | } 135 | } 136 | // for machine-sized ints, lol is one byte, tag is one byte. 137 | // Note: emit() works in reverse. 138 | TYB_CHECK (emit (o, result + (16 - i), i)); // V 139 | return 0; 140 | } 141 | 142 | static 143 | int 144 | encode_tag (buf_t * o, uint32_t tag, uint8_t flags) 145 | { 146 | if (tag < 0x1f) { 147 | TYB_CHECK (emit_byte (o, tag | flags)); 148 | } else { 149 | while (tag > 0) { 150 | if (tag < 0x80) { 151 | TYB_CHECK (emit_byte (o, tag)); 152 | } else { 153 | TYB_CHECK (emit_byte (o, (tag & 0x7f) | 0x80)); 154 | } 155 | tag >>= 7; 156 | } 157 | TYB_CHECK (emit_byte (o, 0x1f | flags)); 158 | } 159 | return 0; 160 | } 161 | 162 | int 163 | encode_INTEGER (buf_t * o, const asn1int_t * n) 164 | { 165 | unsigned int mark = o->pos; 166 | encode_integer (o, *n); 167 | TYB_CHECK (encode_TLV (o, mark, TAG_INTEGER, FLAG_UNIVERSAL)); 168 | return 0; 169 | } 170 | 171 | int 172 | encode_ENUMERATED (buf_t * o, const asn1int_t * n) 173 | { 174 | unsigned int mark = o->pos; 175 | encode_integer (o, *n); 176 | TYB_CHECK (encode_TLV (o, mark, TAG_ENUMERATED, FLAG_UNIVERSAL)); 177 | return 0; 178 | } 179 | 180 | int 181 | encode_BOOLEAN (buf_t * o, const asn1bool_t * value) 182 | { 183 | static const uint8_t encoded_bool_true[3] = {0x01, 0x01, 0xff}; 184 | static const uint8_t encoded_bool_false[3] = {0x01, 0x01, 0x00}; 185 | if (*value) { 186 | TYB_CHECK (emit (o, encoded_bool_true, sizeof(encoded_bool_true))); 187 | } else { 188 | TYB_CHECK (emit (o, encoded_bool_false, sizeof(encoded_bool_false))); 189 | } 190 | return 0; 191 | } 192 | 193 | int 194 | encode_NULL (buf_t * o) 195 | { 196 | static const uint8_t encoded_null[2] = {0x05, 0x00}; 197 | TYB_CHECK (emit (o, encoded_null, sizeof(encoded_null))); 198 | return 0; 199 | } 200 | 201 | int 202 | encode_OCTET_STRING (buf_t * o, const uint8_t * src, int src_len) 203 | { 204 | int mark = o->pos; 205 | TYB_CHECK (emit (o, src, src_len)); 206 | TYB_CHECK (encode_TLV (o, mark, TAG_OCTETSTRING, FLAG_UNIVERSAL)); 207 | return 0; 208 | } 209 | 210 | // assuming the encoded value has already been emitted (starting at position ), 211 | // emit the length and tag for that value. 212 | int 213 | encode_TLV (buf_t * o, unsigned int mark, uint32_t tag, uint8_t flags) 214 | { 215 | int length = mark - o->pos; 216 | uint8_t encoded_length[6]; 217 | uint8_t lol; 218 | // compute length of length 219 | lol = length_of_length (length); 220 | // ensure room for length 221 | TYB_CHECK (ensure_output (o, lol)); 222 | // encode & emit the length 223 | encode_length (length, lol, encoded_length); 224 | TYB_CHECK (emit (o, encoded_length, lol)); 225 | // emit tag|flags 226 | TYB_CHECK (encode_tag (o, tag, flags)); 227 | return 0; 228 | } 229 | 230 | // -------------------------------------------------------------------------------- 231 | // decoder 232 | // -------------------------------------------------------------------------------- 233 | 234 | int 235 | decode_length (buf_t * src, uint32_t * length) 236 | { 237 | uint8_t lol; 238 | // assure at least one byte [valid for length == 0] 239 | TYB_CHECK (ensure_input (src, 1)); 240 | // 2) get length 241 | if (src->buffer[src->pos] < 0x80) { 242 | // one-byte length 243 | *length = src->buffer[src->pos++]; 244 | return 0; 245 | } else if (src->buffer[src->pos] == 0x80) { 246 | // indefinite length 247 | return -1; 248 | } else { 249 | // long definite length from, lower 7 bits 250 | // give us the number of bytes of length. 251 | lol = src->buffer[src->pos++] & 0x7f; 252 | if (lol > 4) { 253 | // we don't support lengths > 32 bits 254 | return -1; 255 | } else { 256 | TYB_CHECK (ensure_input (src, lol)); 257 | uint8_t i; 258 | uint32_t n=0; 259 | for (i=0; i < lol; i++) { 260 | n = (n << 8) | src->buffer[src->pos++]; 261 | } 262 | *length = n; 263 | return 0; 264 | } 265 | } 266 | } 267 | 268 | int 269 | decode_tag (buf_t * src, uint32_t * tag, uint8_t * flags) 270 | { 271 | uint32_t r = 0; 272 | uint8_t b = 0; 273 | TYB_CHECK (ensure_input (src, 1)); 274 | b = src->buffer[src->pos++]; 275 | *flags = b & 0xe0; 276 | if ((b & 0x1f) < 0x1f) { 277 | // single-byte tag 278 | *tag = b & 0x1f; 279 | return 0; 280 | } else { 281 | // multi-byte tag 282 | while (1) { 283 | // tag is in base128, high bit is continuation flag. 284 | TYB_CHECK (ensure_input (src, 1)); 285 | b = src->buffer[src->pos++]; 286 | if (!(b & 0x80)) { 287 | break; 288 | } else { 289 | r = (r << 7) | (b & 0x7f); 290 | } 291 | } 292 | r = (r << 7) | b; 293 | *tag = r; 294 | return 0; 295 | } 296 | } 297 | 298 | int 299 | decode_TLV (asn1raw_t * dst, buf_t * src) 300 | { 301 | uint32_t tag; 302 | uint8_t flags; 303 | uint32_t length; 304 | TYB_CHECK (decode_tag (src, &tag, &flags)); 305 | TYB_CHECK (decode_length (src, &length)); 306 | TYB_CHECK (ensure_input (src, length)); 307 | dst->type = tag; 308 | dst->flags = flags; 309 | dst->length = length; 310 | dst->value = src->buffer + src->pos; 311 | src->pos += length; 312 | return 0; 313 | } 314 | 315 | asn1int_t 316 | decode_INTEGER (asn1raw_t * src) 317 | { 318 | uint8_t length = src->length; 319 | if (length == 0) { 320 | return 0; 321 | } else { 322 | asn1int_t n; 323 | uint8_t pos = 0; 324 | n = src->value[pos]; 325 | if (n & 0x80) { 326 | // negative 327 | n = n - 0x100; 328 | } 329 | length -= 1; 330 | while (length) { 331 | pos += 1; 332 | n = (n << 8) | src->value[pos]; 333 | length -= 1; 334 | } 335 | return n; 336 | } 337 | } 338 | 339 | int 340 | decode_BOOLEAN (asn1raw_t * src) 341 | { 342 | if (src->length == 0) { 343 | return 0; 344 | } else { 345 | return (src->value[0] == 0xff); 346 | } 347 | } 348 | -------------------------------------------------------------------------------- /tinyber/data/tinyber.h: -------------------------------------------------------------------------------- 1 | // -*- Mode: C -*- 2 | 3 | #ifndef _TINYBER_H_ 4 | #define _TINYBER_H_ 5 | 6 | typedef int64_t asn1int_t; 7 | //typedef int32_t asn1int_t; 8 | typedef uint8_t asn1bool_t; 9 | 10 | typedef enum { 11 | FLAG_UNIVERSAL = 0x00, 12 | FLAG_STRUCTURED = 0x20, 13 | FLAG_APPLICATION = 0x40, 14 | FLAG_CONTEXT = 0x80, 15 | } asn1flag; 16 | 17 | typedef enum { 18 | TAG_BOOLEAN = 0x01, 19 | TAG_INTEGER = 0x02, 20 | TAG_BITSTRING = 0x03, 21 | TAG_OCTETSTRING = 0x04, 22 | TAG_NULLTAG = 0x05, 23 | TAG_OID = 0x06, 24 | TAG_ENUMERATED = 0x0A, 25 | TAG_UTF8STRING = 0x0C, 26 | TAG_SEQUENCE = 0x10, 27 | TAG_SET = 0x11, 28 | } asn1type; 29 | 30 | typedef struct { 31 | uint32_t type; 32 | uint8_t flags; 33 | unsigned int length; 34 | uint8_t * value; 35 | } asn1raw_t; 36 | 37 | typedef struct { 38 | uint8_t * buffer; 39 | unsigned int pos; 40 | unsigned int size; 41 | } buf_t; 42 | 43 | // buffer interface 44 | 45 | inline 46 | void 47 | init_obuf (buf_t * self, uint8_t * buffer, unsigned int size) 48 | { 49 | self->buffer = buffer; 50 | self->pos = size; 51 | self->size = size; 52 | } 53 | 54 | inline 55 | void 56 | init_ibuf (buf_t * self, uint8_t * buffer, unsigned int size) 57 | { 58 | self->buffer = buffer; 59 | self->pos = 0; 60 | self->size = size; 61 | } 62 | 63 | // decoder 64 | int decode_BOOLEAN (asn1raw_t * src); 65 | asn1int_t decode_INTEGER (asn1raw_t * src); 66 | int decode_TLV (asn1raw_t * dst, buf_t * src); 67 | int decode_length (buf_t * src, uint32_t * length); 68 | 69 | // encoder 70 | int encode_TLV (buf_t * o, unsigned int mark, uint32_t tag, uint8_t flags); 71 | int encode_INTEGER (buf_t * o, const asn1int_t * n); 72 | int encode_BOOLEAN (buf_t * o, const asn1bool_t * value); 73 | int encode_OCTET_STRING (buf_t * o, const uint8_t * src, int src_len); 74 | int encode_ENUMERATED (buf_t * o, const asn1int_t * n); 75 | int encode_NULL (buf_t * o); 76 | 77 | //#include 78 | //#define TYB_FAILIF(x) do { if (x) { fprintf (stderr, "*** line %d ***\\n", __LINE__); abort(); } } while(0) 79 | #define TYB_FAILIF(x) do { if (x) { return -1; } } while(0) 80 | #define TYB_CHECK(x) TYB_FAILIF(-1 == (x)) 81 | 82 | #endif // _TINYBER_H_ 83 | -------------------------------------------------------------------------------- /tinyber/gen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- Mode: Python -*- 3 | 4 | import os 5 | 6 | from asn1ate import parser 7 | from asn1ate.sema import build_semantic_model 8 | from tinyber.walker import Walker 9 | 10 | def go (args): 11 | with open (args.file) as f: 12 | asn1def = f.read() 13 | 14 | parse_tree = parser.parse_asn1(asn1def) 15 | modules = build_semantic_model(parse_tree) 16 | assert (len(modules) == 1) 17 | base, ext = os.path.splitext (args.file) 18 | parts = os.path.split (base) 19 | module_name = parts[-1] 20 | if args.outdir: 21 | path = args.outdir 22 | else: 23 | path = "." 24 | 25 | if args.lang == 'python': 26 | from tinyber.py_nodes import PythonBackend as Backend 27 | from tinyber import py_nodes as nodes 28 | elif args.lang == 'c': 29 | from tinyber.c_nodes import CBackend as Backend 30 | from tinyber import c_nodes as nodes 31 | 32 | # pull in the python-specific node implementations 33 | walker = Walker (modules[0], nodes) 34 | walker.walk() 35 | 36 | backend = Backend (args, walker, module_name, path) 37 | backend.generate_code() 38 | 39 | def main(): 40 | import argparse 41 | p = argparse.ArgumentParser (description='tinyber ASN.1 BER/DER code generator.') 42 | p.add_argument ('-o', '--outdir', help="output directory (defaults to location of input file)", default='') 43 | p.add_argument ('-l', '--lang', help="output language ('c' or 'python')", default='c') 44 | p.add_argument ('-ns', '--no-standalone', action='store_true', help="[python only] do not insert codec.py into output file.") 45 | p.add_argument ('file', help="asn.1 spec", metavar="FILE") 46 | args = p.parse_args() 47 | go (args) 48 | -------------------------------------------------------------------------------- /tinyber/nodes.py: -------------------------------------------------------------------------------- 1 | # -*- Mode: Python -*- 2 | 3 | from tinyber.ber import length_of_length, length_of_integer 4 | 5 | class c_node: 6 | 7 | def __init__ (self, kind, attrs, subs): 8 | self.kind = kind 9 | self.attrs = attrs 10 | self.subs = subs 11 | 12 | def dump (self): 13 | if self.subs: 14 | return (self.kind, self.attrs, [x.dump() for x in self.subs]) 15 | else: 16 | return (self.kind, self.attrs) 17 | 18 | # type grammar 19 | # type ::= base_type | sequence | sequence_of | choice | enumerated | defined 20 | # base_type ::= (INTEGER | BOOLEAN | OCTETSTRING), max_size 21 | # sequence ::= (name, type)+ 22 | # sequence_of ::= (type, max_size) 23 | # choice ::= (name, tag, type)+ 24 | # enumerated ::= (name, val)+ 25 | 26 | class c_base_type (c_node): 27 | 28 | def __init__ (self, name, min_size=None, max_size=None): 29 | c_node.__init__ (self, 'base_type', (name, min_size, max_size), []) 30 | 31 | def max_size (self): 32 | type_name, min_size, max_size = self.attrs 33 | if type_name == 'OCTET STRING' or type_name == 'UTF8String': 34 | return 1 + length_of_length (max_size) + max_size 35 | elif type_name == 'BOOLEAN': 36 | return 1 + length_of_length (1) + 1 37 | elif type_name == 'INTEGER': 38 | if max_size is None: 39 | # this actually depends on what integer size is in use (asn1int_t) 40 | max_size = 2**64 41 | loi = length_of_integer (int (max_size)) 42 | return 1 + length_of_length (loi) + loi 43 | elif type_name == 'NULL': 44 | return 2 45 | else: 46 | import pdb 47 | pdb.set_trace() 48 | 49 | tag_map = { 50 | 'OCTET STRING': 'TAG_OCTETSTRING', 51 | 'UTF8String': 'TAG_UTF8STRING', 52 | 'INTEGER': 'TAG_INTEGER', 53 | 'BOOLEAN': 'TAG_BOOLEAN', 54 | 'NULL': 'TAG_NULL', 55 | } 56 | 57 | def tag_name (self): 58 | type_name, min_size, max_size = self.attrs 59 | return self.tag_map[type_name] 60 | 61 | class c_sequence (c_node): 62 | 63 | def __init__ (self, name, pairs): 64 | slots, types = zip(*pairs) 65 | c_node.__init__ (self, 'sequence', (name, slots,), types) 66 | 67 | def max_size (self): 68 | name, slots = self.attrs 69 | types = self.subs 70 | r = 0 71 | for slot_type in types: 72 | r += slot_type.max_size() 73 | return 1 + length_of_length(r) + r 74 | 75 | class c_sequence_of (c_node): 76 | 77 | def __init__ (self, seq_type, min_size, max_size): 78 | c_node.__init__ (self, 'sequence_of', (min_size, max_size,), [seq_type]) 79 | 80 | def max_size (self): 81 | min_size, max_size, = self.attrs 82 | [seq_type] = self.subs 83 | r = seq_type.max_size() * max_size 84 | return 1 + length_of_length(r) + r 85 | 86 | class c_set_of (c_node): 87 | 88 | def __init__ (self, item_type, min_size, max_size): 89 | c_node.__init__ (self, 'set_of', (min_size, max_size,), [item_type]) 90 | 91 | def max_size (self): 92 | min_size, max_size, = self.attrs 93 | [item_type] = self.subs 94 | r = item_type.max_size() * max_size 95 | return 1 + length_of_length(r) + r 96 | 97 | class c_choice (c_node): 98 | 99 | def __init__ (self, name, alts): 100 | names, tags, types = zip(*alts) 101 | c_node.__init__ (self, 'choice', (name, names, tags), types) 102 | 103 | def max_size (self): 104 | name, slots, tags = self.attrs 105 | types = self.subs 106 | r = 0 107 | for slot_type in types: 108 | r = max (r, slot_type.max_size()) 109 | return 1 + length_of_length (r) + r 110 | 111 | class c_enumerated (c_node): 112 | 113 | def __init__ (self, name, pairs): 114 | c_node.__init__ (self, 'enumerated', (name, pairs), []) 115 | 116 | def max_size (self): 117 | _, alts, = self.attrs 118 | max_val = len(alts) 119 | for name, val in alts: 120 | if val is not None: 121 | max_val = max (max_val, int(val)) 122 | loi = length_of_integer (max_val) 123 | return 1 + length_of_length (loi) + loi 124 | 125 | class c_defined (c_node): 126 | 127 | def __init__ (self, name, max_size): 128 | c_node.__init__ (self, 'defined', (name, max_size), []) 129 | 130 | def name (self): 131 | name, max_size = self.attrs 132 | return name 133 | 134 | def max_size (self): 135 | name, max_size = self.attrs 136 | return max_size 137 | -------------------------------------------------------------------------------- /tinyber/py_nodes.py: -------------------------------------------------------------------------------- 1 | # -*- Mode: Python -*- 2 | 3 | from tinyber import nodes 4 | from tinyber.writer import Writer 5 | import os 6 | import sys 7 | 8 | def psafe (s): 9 | return s.replace ('-', '_') 10 | 11 | def emit_pairs(out, tag, pairs, reversed=False): 12 | out.writelines('%s = {' % tag) 13 | with out.indent(): 14 | for x in pairs: 15 | if reversed: 16 | out.writelines("%s: %s," % (x[1], x[0])) 17 | else: 18 | out.writelines("%s: %s," % (x[0], x[1])) 19 | out.writelines('}') 20 | 21 | class c_base_type (nodes.c_base_type): 22 | 23 | def emit (self, out): 24 | pass 25 | 26 | def emit_decode (self, out): 27 | type_name, min_size, max_size = self.attrs 28 | if type_name == 'INTEGER': 29 | out.writelines ('v = src.next_INTEGER(%s, %s)' % (min_size, max_size),) 30 | elif type_name == 'OCTET STRING': 31 | out.writelines ('v = src.next_OCTET_STRING(%s, %s)' % (min_size, max_size),) 32 | elif type_name == 'BOOLEAN': 33 | out.writelines ('v = src.next_BOOLEAN()') 34 | else: 35 | import pdb 36 | pdb.set_trace() 37 | 38 | def emit_encode (self, out, val): 39 | type_name, min_size, max_size = self.attrs 40 | if type_name == 'INTEGER': 41 | out.writelines ('dst.emit_INTEGER(%s)' % (val,)) 42 | elif type_name == 'OCTET STRING': 43 | out.writelines ('dst.emit_OCTET_STRING(%s)' % (val,)) 44 | elif type_name == 'BOOLEAN': 45 | out.writelines ('dst.emit_BOOLEAN(%s)' % (val,)) 46 | else: 47 | import pdb 48 | pdb.set_trace() 49 | 50 | class c_sequence (nodes.c_sequence): 51 | 52 | parent_class = 'SEQUENCE' 53 | 54 | def emit (self, out): 55 | name, slots = self.attrs 56 | out.writelines ('__slots__ = (') 57 | with out.indent(): 58 | for x in slots: 59 | out.writelines("'%s'," % psafe(x)) 60 | out.writelines (')') 61 | 62 | def emit_decode (self, out): 63 | name, slots = self.attrs 64 | types = self.subs 65 | out.writelines ('src = src.next(TAG.SEQUENCE, FLAG.STRUCTURED)') 66 | for i in range (len (slots)): 67 | slot_name = slots[i] 68 | slot_type = types[i] 69 | slot_type.emit_decode (out) 70 | out.writelines ('self.%s = v' % (psafe(slot_name),)) 71 | out.writelines ('src.assert_done()') 72 | 73 | def emit_encode (self, out, val): 74 | name, slots = self.attrs 75 | types = self.subs 76 | out.writelines ('with dst.TLV(TAG.SEQUENCE, FLAG.STRUCTURED):') 77 | with out.indent(): 78 | for i in reversed (range (len (types))): 79 | types[i].emit_encode (out, 'self.%s' % (psafe(slots[i]),)) 80 | 81 | class c_sequence_of (nodes.c_sequence_of): 82 | 83 | def emit (self, out): 84 | min_size, max_size, = self.attrs 85 | [seq_type] = self.subs 86 | 87 | def emit_decode (self, out): 88 | min_size, max_size, = self.attrs 89 | [seq_type] = self.subs 90 | out.writelines ( 91 | 'src, save = src.next(TAG.SEQUENCE, FLAG.STRUCTURED), src', 92 | 'a = []', 93 | 'while not src.done():' 94 | ) 95 | with out.indent(): 96 | seq_type.emit_decode (out) 97 | out.writelines ('a.append(v)') 98 | if min_size is not None and min_size > 0: 99 | out.writelines ('if len(a) < %d:' % (min_size,)) 100 | with out.indent(): 101 | out.writelines ('raise ConstraintViolation(a)') 102 | if max_size is not None: 103 | out.writelines ('if len(a) > %d:' % (max_size,)) 104 | with out.indent(): 105 | out.writelines ('raise ConstraintViolation(a)') 106 | out.writelines ('v, src = a, save') 107 | 108 | def emit_encode (self, out, val): 109 | min_size, max_size, = self.attrs 110 | [seq_type] = self.subs 111 | out.writelines ('with dst.TLV(TAG.SEQUENCE, FLAG.STRUCTURED):') 112 | with out.indent(): 113 | out.writelines ('for v in reversed(%s):' % (val,)) 114 | with out.indent(): 115 | seq_type.emit_encode (out, 'v') 116 | 117 | class c_set_of (nodes.c_sequence_of): 118 | 119 | def emit (self, out): 120 | min_size, max_size, = self.attrs 121 | [item_type] = self.subs 122 | 123 | def emit_decode (self, out): 124 | min_size, max_size, = self.attrs 125 | [item_type] = self.subs 126 | out.writelines ( 127 | 'src, save = src.next(TAG.SET, FLAG.STRUCTURED), src', 128 | 'a = set()', 129 | 'while not src.done():' 130 | ) 131 | with out.indent(): 132 | item_type.emit_decode (out) 133 | out.writelines ('a.add (v)') 134 | out.writelines ("# check constraints") 135 | out.writelines ('v, src = a, save') 136 | 137 | def emit_encode (self, out, val): 138 | min_size, max_size, = self.attrs 139 | [item_type] = self.subs 140 | out.writelines ('with dst.TLV(TAG.SET, FLAG.STRUCTURED):') 141 | with out.indent(): 142 | out.writelines ('for v in %s:' % (val,)) 143 | with out.indent(): 144 | item_type.emit_encode (out, 'v') 145 | 146 | 147 | class c_choice (nodes.c_choice): 148 | 149 | parent_class = 'CHOICE' 150 | nodecoder = True 151 | noencoder = True 152 | 153 | def emit (self, out): 154 | name, slots, tags = self.attrs 155 | types = self.subs 156 | pairs = [] 157 | for i in range (len (slots)): 158 | pairs.append ((types[i].name(), tags[i])) 159 | emit_pairs(out, 'tags_f', pairs) 160 | emit_pairs(out, 'tags_r', pairs, reversed=True) 161 | 162 | class c_enumerated (nodes.c_enumerated): 163 | 164 | def emit (self, out): 165 | defname, alts, = self.attrs 166 | pairs = [] 167 | for name, val in alts: 168 | pairs.append (("'%s'" % name, val)) 169 | emit_pairs(out, 'tags_f', pairs) 170 | emit_pairs(out, 'tags_r', pairs, reversed=True) 171 | 172 | parent_class = 'ENUMERATED' 173 | nodecoder = True 174 | noencoder = True 175 | 176 | class c_defined (nodes.c_defined): 177 | 178 | def emit (self, out): 179 | name, max_size = self.attrs 180 | 181 | def emit_decode (self, out): 182 | type_name, max_size = self.attrs 183 | out.writelines ( 184 | 'v = %s()' % (type_name,), 185 | 'v._decode(src)', 186 | ) 187 | 188 | def emit_encode (self, out, val): 189 | type_name, max_size = self.attrs 190 | out.writelines ('%s._encode(dst)' % (val,)) 191 | 192 | class PythonBackend: 193 | 194 | def __init__ (self, args, walker, module_name, path): 195 | self.args = args 196 | self.walker = walker 197 | self.module_name = module_name 198 | self.path = path 199 | self.base_path = os.path.join(path, module_name) 200 | 201 | def gen_decoder (self, type_name, type_decl, node): 202 | # generate a decoder for a type assignment. 203 | self.out.newline() 204 | self.out.writelines ('def _decode(self, src):') 205 | with self.out.indent(): 206 | node.emit_decode (self.out) 207 | # this line is unecessary (but harmless) on normal defined sequence types 208 | self.out.writelines ('self.value = v') 209 | 210 | def gen_encoder (self, type_name, type_decl, node): 211 | # generate an encoder for a type assignment 212 | self.out.newline() 213 | self.out.writelines ('def _encode(self, dst):') 214 | with self.out.indent(): 215 | node.emit_encode (self.out, 'self.value') 216 | 217 | def gen_codec_funs (self, type_name, type_decl, node): 218 | if not hasattr (node, 'nodecoder'): 219 | self.gen_decoder (type_name, type_decl, node) 220 | if not hasattr (node, 'noencoder'): 221 | self.gen_encoder (type_name, type_decl, node) 222 | 223 | def generate_code (self): 224 | self.out = Writer (open (self.base_path + '_ber.py', 'w'), indent_size=4) 225 | command = os.path.basename(sys.argv[0]) 226 | self.out.writelines ( 227 | '# -*- Mode: Python -*-', 228 | '# generated by: %s %s' % (command, " ".join(sys.argv[1:])), 229 | '# *** do not edit ***', 230 | '', 231 | ) 232 | if self.args.no_standalone: 233 | self.out.writelines ('from tinyber.codec import *', '') 234 | else: 235 | pkg_dir, _ = os.path.split (__file__) 236 | 237 | self.out.writelines ('# --- start codec.py ---', '') 238 | with open(os.path.join(pkg_dir, 'codec.py'), 'r') as infile: 239 | for line in infile: 240 | self.out.writelines (line[:-1]) 241 | self.out.writelines('', '# --- end codec.py ---') 242 | 243 | self.tag_assignments = self.walker.tag_assignments 244 | # generate typedefs and prototypes. 245 | for (type_name, node, type_decl) in self.walker.defined_types: 246 | if hasattr (node, 'parent_class'): 247 | parent_class = node.parent_class 248 | else: 249 | parent_class = 'ASN1' 250 | self.out.newline() 251 | self.out.newline() 252 | self.out.writelines ('class %s(%s):' % (type_name, parent_class)) 253 | with self.out.indent(): 254 | self.out.writelines ( 255 | 'max_size = %d' % (node.max_size()) 256 | ) 257 | node.emit (self.out) 258 | self.gen_codec_funs (type_name, type_decl, node) 259 | self.out.close() 260 | -------------------------------------------------------------------------------- /tinyber/walker.py: -------------------------------------------------------------------------------- 1 | # -*- Mode: Python -*- 2 | 3 | from asn1ate.sema import ( 4 | ChoiceType, ExtensionMarker, SequenceType, SetOfType, SizeConstraint, 5 | SingleValueConstraint, TaggedType, ValueRangeConstraint, ValueListType, 6 | dependency_sort 7 | ) 8 | 9 | class Walker (object): 10 | 11 | def __init__(self, sema_module, nodes): 12 | self.sema_module = sema_module 13 | self.tag_assignments = {} 14 | self.defined_types = [] 15 | self.nodes = nodes 16 | 17 | def gen_ChoiceType (self, ob, name=None): 18 | alts = [] 19 | for c in ob.components: 20 | if isinstance (c, ExtensionMarker): 21 | continue 22 | if not isinstance (c.type_decl, TaggedType): 23 | raise NotImplementedError ("CHOICE elements need a tag") 24 | tag = c.type_decl.class_number 25 | slot_type = self.gen_dispatch (c.type_decl.type_decl) 26 | slot_name = c.identifier 27 | alts.append ((slot_name, tag, slot_type)) 28 | return self.nodes.c_choice (name, alts) 29 | 30 | def gen_SequenceType (self, ob, name=None): 31 | slots = [] 32 | for c in ob.components: 33 | slot_type = self.gen_dispatch (c.type_decl) 34 | slot_name = c.identifier 35 | slots.append ((slot_name, slot_type)) 36 | return self.nodes.c_sequence (name, slots) 37 | 38 | def constraint_get_min_max_size (self, ob): 39 | if isinstance (ob, SizeConstraint): 40 | return self.constraint_get_min_max_size (ob.nested) 41 | elif isinstance (ob, SingleValueConstraint): 42 | return int (ob.value), int (ob.value) 43 | elif isinstance (ob, ValueRangeConstraint): 44 | return int (ob.min_value), int (ob.max_value) 45 | else: 46 | raise NotImplementedError ("testing") 47 | 48 | def gen_SequenceOfType (self, ob): 49 | min_size, max_size = self.constraint_get_min_max_size (ob.size_constraint) 50 | array_type = self.gen_dispatch (ob.type_decl) 51 | return self.nodes.c_sequence_of (array_type, min_size, max_size) 52 | 53 | def gen_TaggedType (self, ob): 54 | # XXX for now, only support [APPLICATION X] SEQUENCE {} 55 | # assert (isinstance (ob.type_decl, SequenceType)) 56 | raise NotImplementedError ('put your tags inside the SEQUENCE definition') 57 | 58 | def gen_TypeAssignment (self, ob): 59 | type_name, type_decl = ob.type_name, ob.type_decl 60 | # strip (and record) tag information if present 61 | if isinstance (type_decl, TaggedType): 62 | tag = type_decl.class_number, type_decl.implicit 63 | type_decl = type_decl.type_decl 64 | self.tag_assignments[type_name] = tag 65 | if isinstance (type_decl, ChoiceType): 66 | node = self.gen_ChoiceType (type_decl, name=type_name) 67 | elif isinstance (type_decl, SequenceType): 68 | node = self.gen_SequenceType (type_decl, name=type_name) 69 | elif isinstance (type_decl, SetOfType): 70 | node = self.gen_SetOfType (type_decl, name=type_name) 71 | elif isinstance (type_decl, ValueListType): 72 | node = self.gen_ValueListType (type_decl, name=type_name) 73 | else: 74 | node = self.gen_dispatch (type_decl) 75 | self.defined_types.append ((type_name, node, type_decl)) 76 | 77 | def gen_SimpleType (self, ob): 78 | if ob.constraint: 79 | min_size, max_size = self.constraint_get_min_max_size (ob.constraint) 80 | else: 81 | min_size, max_size = None, None 82 | return self.nodes.c_base_type (ob.type_name, min_size, max_size) 83 | 84 | def gen_ValueListType (self, ob, name=None): 85 | alts = [] 86 | for sub in ob.named_values: 87 | if sub.value is not None: 88 | alts.append ((sub.identifier, sub.value)) 89 | else: 90 | alts.append ((sub.identifier, None)) 91 | return self.nodes.c_enumerated (name, alts) 92 | 93 | def gen_DefinedType (self, ob): 94 | for type_name, node, type_decl in self.defined_types: 95 | if ob.type_name == type_name: 96 | return self.nodes.c_defined (ob.type_name, node.max_size()) 97 | raise ValueError (ob.type_name) 98 | 99 | def gen_SetOfType (self, ob): 100 | min_size, max_size = self.constraint_get_min_max_size (ob.size_constraint) 101 | item_type = self.gen_dispatch (ob.type_decl) 102 | return self.nodes.c_set_of (item_type, min_size, max_size) 103 | 104 | def gen_dispatch (self, ob): 105 | name = ob.__class__.__name__ 106 | probe = getattr (self, 'gen_%s' % (name,), None) 107 | if not probe: 108 | raise KeyError ("unhandled node type %r" % (name,)) 109 | else: 110 | return probe (ob) 111 | 112 | def walk (self): 113 | assignment_components = dependency_sort (self.sema_module.assignments) 114 | for component in assignment_components: 115 | for assignment in component: 116 | self.gen_dispatch (assignment) 117 | -------------------------------------------------------------------------------- /tinyber/writer.py: -------------------------------------------------------------------------------- 1 | # -*- Mode: Python -*- 2 | 3 | class IndentContext: 4 | 5 | def __init__ (self, writer, scope=False): 6 | self.writer = writer 7 | self.scope = scope 8 | 9 | def __enter__ (self): 10 | if self.scope: 11 | self.writer.writelines ('{') 12 | self.writer.indent_level += 1 13 | 14 | def __exit__ (self, t, v, tb): 15 | self.writer.indent_level -= 1 16 | if self.scope: 17 | self.writer.writelines ('}') 18 | 19 | class Writer: 20 | 21 | def __init__ (self, stream, indent_size=2): 22 | self.stream = stream 23 | self.indent_level = 0 24 | self.base_indent = ' ' * indent_size 25 | 26 | def indent (self): 27 | return IndentContext (self, False) 28 | 29 | def scope (self): 30 | return IndentContext (self, True) 31 | 32 | def writelines (self, *lines): 33 | for line in lines: 34 | self.stream.write (self.base_indent * self.indent_level) 35 | self.stream.write (line) 36 | self.stream.write ('\n') 37 | 38 | def newline (self): 39 | self.stream.write ('\n') 40 | 41 | def write (self, s, indent=False): 42 | if indent: 43 | self.stream.write (self.base_indent * self.indent_level) 44 | self.stream.write (s) 45 | 46 | def close (self): 47 | self.stream.close() 48 | --------------------------------------------------------------------------------