Coverage report: /home/ellis/comp/core/lib/dat/asn1.lisp

KindCoveredAll%
expression0288 0.0
branch040 0.0
Key
Not instrumented
Conditionalized out
Executed
Not executed
 
Both branches taken
One branch taken
Neither branch taken
1
 ;;; asn1.lisp --- Simple ASN.1 Coding
2
 
3
 ;; Abstract Syntax Notation One
4
 
5
 ;;; Commentary:
6
 
7
 ;; IDL for definiting data structures - joint standard between ITU-T and ISO
8
 ;; used to define a large number of protocols.
9
 
10
 ;; For example see the CRY/SSL/X509 package.
11
 
12
 #| refs
13
  - https://github.com/digitalbazaar/forge/blob/909e312878838f46ba6d70e90264650b05eb8bde/js/asn1.js
14
  - http://www.obj-sys.com/asn1tutorial/node128.html
15
  - https://github.com/deadtrickster/ssl_verify_hostname.erl/blob/master/src/ssl_verify_hostname.erl
16
  - https://golang.org/src/encoding/asn1/asn1.go?m=text
17
 |#
18
 
19
 #|
20
  The most common binary encodings for ASN.1 are BER (Basic Encoding Rules)
21
  and DER (Distinguished Encoding Rules). DER is just a subset of BER that
22
  has stricter requirements for how data must be encoded.
23
 
24
  Each ASN.1 structure has a tag (a byte identifying the ASN.1 structure type)
25
  and a byte array for the value of this ASN1 structure which may be data or a
26
  list of ASN.1 structures.
27
 
28
  Each ASN.1 structure using BER is (Tag-Length-Value):
29
 
30
  | byte 0 | bytes X | bytes Y |
31
  |--------|---------|----------
32
  |  tag   | length  |  value  |
33
 
34
  ASN.1 allows for tags to be of "High-tag-number form" which allows a tag to
35
  be two or more octets, but that is not supported by this class. A tag is
36
  only 1 byte. Bits 1-5 give the tag number (ie the data type within a
37
  particular 'class'), 6 indicates whether or not the ASN.1 value is
38
  constructed from other ASN.1 values, and bits 7 and 8 give the 'class'. If
39
  bits 7 and 8 are both zero, the class is UNIVERSAL. If only bit 7 is set,
40
  then the class is APPLICATION. If only bit 8 is set, then the class is
41
  CONTEXT_SPECIFIC. If both bits 7 and 8 are set, then the class is PRIVATE.
42
  The tag numbers for the data types for the class UNIVERSAL are listed below:
43
 
44
  UNIVERSAL 0 Reserved for use by the encoding rules
45
  UNIVERSAL 1 Boolean type
46
  UNIVERSAL 2 Integer type
47
  UNIVERSAL 3 Bitstring type
48
  UNIVERSAL 4 Octetstring type
49
  UNIVERSAL 5 Null type
50
  UNIVERSAL 6 Object identifier type
51
  UNIVERSAL 7 Object descriptor type
52
  UNIVERSAL 8 External type and Instance-of type
53
  UNIVERSAL 9 Real type
54
  UNIVERSAL 10 Enumerated type
55
  UNIVERSAL 11 Embedded-pdv type
56
  UNIVERSAL 12 UTF8String type
57
  UNIVERSAL 13 Relative object identifier type
58
  UNIVERSAL 14-15 Reserved for future editions
59
  UNIVERSAL 16 Sequence and Sequence-of types
60
  UNIVERSAL 17 Set and Set-of types
61
  UNIVERSAL 18-22, 25-30 Character string types
62
  UNIVERSAL 23-24 Time types
63
 
64
  The length of an ASN.1 structure is specified after the tag identifier.
65
  There is a definite form and an indefinite form. The indefinite form may
66
  be used if the encoding is constructed and not all immediately available.
67
  The indefinite form is encoded using a length byte with only the 8th bit
68
  set. The end of the constructed object is marked using end-of-contents
69
  octets (two zero bytes).
70
 
71
  The definite form looks like this:
72
 
73
  The length may take up 1 or more bytes, it depends on the length of the
74
  value of the ASN.1 structure. DER encoding requires that if the ASN.1
75
  structure has a value that has a length greater than 127, more than 1 byte
76
  will be used to store its length, otherwise just one byte will be used.
77
  This is strict.
78
 
79
  In the case that the length of the ASN.1 value is less than 127, 1 octet
80
  (byte) is used to store the "short form" length. The 8th bit has a value of
81
  0 indicating the length is "short form" and not "long form" and bits 7-1
82
  give the length of the data. (The 8th bit is the left-most, most significant
83
  bit: also known as big endian or network format).
84
 
85
  In the case that the length of the ASN.1 value is greater than 127, 2 to
86
  127 octets (bytes) are used to store the "long form" length. The first
87
  byte's 8th bit is set to 1 to indicate the length is "long form." Bits 7-1
88
  give the number of additional octets. All following octets are in base 256
89
  with the most significant digit first (typical big-endian binary unsigned
90
  integer storage). So, for instance, if the length of a value was 257, the
91
  first byte would be set to:
92
 
93
  10000010 = 130 = 0x82.
94
 
95
  This indicates there are 2 octets (base 256) for the length. The second and
96
  third bytes (the octets just mentioned) would store the length in base 256:
97
 
98
  octet 2: 00000001 = 1 * 256^1 = 256
99
  octet 3: 00000001 = 1 * 256^0 = 1
100
  total = 257
101
 
102
  The algorithm for converting a js integer value of 257 to base-256 is:
103
 
104
  var value = 257;
105
  var bytes = [];
106
  bytes[0] = (value >>> 8) & 0xFF; // most significant byte first
107
  bytes[1] = value & 0xFF;        // least significant byte last
108
 
109
  On the ASN.1 UNIVERSAL Object Identifier (OID) type:
110
 
111
  An OID can be written like: "value1.value2.value3...valueN"
112
 
113
  The DER encoding rules:
114
 
115
  The first byte has the value 40 * value1 + value2.
116
  The following bytes, if any, encode the remaining values. Each value is
117
  encoded in base 128, most significant digit first (big endian), with as
118
  few digits as possible, and the most significant bit of each byte set
119
  to 1 except the last in each value's encoding. For example: Given the
120
  OID "1.2.840.113549", its DER encoding is (remember each byte except the
121
  last one in each encoding is OR'd with 0x80):
122
 
123
  byte 1: 40 * 1 + 2 = 42 = 0x2A.
124
  bytes 2-3: 128 * 6 + 72 = 840 = 6 72 = 6 72 = 0x0648 = 0x8648
125
  bytes 4-6: 16384 * 6 + 128 * 119 + 13 = 6 119 13 = 0x06770D = 0x86F70D
126
 
127
  The final value is: 0x2A864886F70D.
128
  The full OID (including ASN.1 tag and length of 6 bytes) is:
129
  0x06062A864886F70D
130
 |#
131
 ;;; Code:
132
 (in-package :dat/asn1)
133
 
134
 
135
 (defun copy-to-lisp-vector (src vector count)
136
   (declare (octet-vector vector)
137
            (fixnum count)
138
            (optimize (safety 0) (speed 3)))
139
   (clone-octets-from-alien src vector count))
140
 
141
 (defun asn1-string-octet-vector (asn1-string)
142
   (let* ((data (asn1-string-data asn1-string))
143
          (length (asn1-string-length asn1-string))
144
          (vector (io/static:make-static-vector length)))
145
     (copy-to-lisp-vector data vector length)
146
     vector))
147
 
148
 (definline asn1-iastring-char-p (byte)
149
   (declare (type octet byte)
150
            (optimize (speed 3) (safety 0)))
151
   (< byte #x80))
152
 
153
 (definline asn1-iastring-p (bytes)
154
   (declare (octet-vector bytes)
155
            (optimize (speed 3) (safety 0)))
156
   (every #'asn1-iastring-char-p bytes))
157
 
158
 (defgeneric decode-asn1-string (self type))
159
 
160
 (defmethod decode-asn1-string (self (type (eql #.(v-asn1 :ia5string))))
161
   (let ((bytes (asn1-string-octet-vector self)))
162
     (if (asn1-iastring-p self)
163
         (sb-ext:octets-to-string bytes :external-format :ascii)
164
         (error 'invalid-asn1-string :type #.(v-asn1 :ia5string)))))
165
 
166
 (defun asn1-printable-char-p (byte)
167
   (declare (type (unsigned-byte 8) byte)
168
            (optimize (speed 3) (safety 0)))
169
   (cond
170
     ;; a-z
171
     ((and (>= byte #.(char-code #\a))
172
           (<= byte #.(char-code #\z)))
173
      t)
174
     ;; '-/
175
     ((and (>= byte #.(char-code #\'))
176
           (<= byte #.(char-code #\/)))
177
      t)
178
     ;; 0-9
179
     ((and (>= byte #.(char-code #\0))
180
           (<= byte #.(char-code #\9)))
181
      t)
182
     ;; A-Z
183
     ((and (>= byte #.(char-code #\A))
184
           (<= byte #.(char-code #\Z)))
185
      t)
186
     ;; other
187
     ((= byte #.(char-code #\ )) t)
188
     ((= byte #.(char-code #\:)) t)
189
     ((= byte #.(char-code #\=)) t)
190
     ((= byte #.(char-code #\?)) t)))
191
 
192
 (definline asn1-printable-string-p (bytes)
193
   (declare (octet-vector bytes)
194
            (optimize (speed 3) (safety 0)))
195
   (every #'asn1-printable-char-p bytes))
196
 
197
 (defmethod decode-asn1-string (self (type (eql #.(v-asn1 :printablestring))))
198
   (let* ((bytes (asn1-string-octet-vector self)))
199
     (if (asn1-printable-string-p bytes)
200
         (sb-ext:octets-to-string bytes :external-format :ascii)
201
         (error 'invalid-asn1-string :type #.(v-asn1 :printablestring)))))
202
 
203
 (defmethod decode-asn1-string (self (type (eql #.(v-asn1 :utf8string))))
204
   (let* ((data (asn1-string-data self))
205
          (length (asn1-string-length self))
206
          (vec (make-octets length)))
207
     (clone-octets-from-alien data vec length)
208
     (sb-ext:octets-to-string vec :external-format :utf-8 :end length)))
209
     
210
 
211
 (defmethod decode-asn1-string (self (type (eql #.(v-asn1 :universalstring))))
212
   (let ((len (asn1-string-length self))
213
         (data (asn1-string-data self)))
214
     (if (= 0 (mod len 4))
215
         (let ((vec (make-octets len)))
216
           (clone-octets-from-alien data vec len)
217
           (sb-ext:octets-to-string vec :external-format :utf32))
218
         (error 'invalid-asn1-string :type '+v-asn1-universalstring+))))
219
 
220
 (definline asn1-teletex-char-p (byte)
221
   (declare (octet byte)
222
            (optimize (speed 3) (safety 0)))
223
   (and (>= byte #x20) (< byte #x80)))
224
 
225
 (definline asn1-teletex-string-p (bytes)
226
   (declare (octet-vector bytes)
227
            (optimize (speed 3) (safety 0)))
228
   (every #'asn1-teletex-char-p bytes))
229
 
230
 (defmethod decode-asn1-string (self (type (eql #.(v-asn1 :teletexstring))))
231
   (let ((bytes (asn1-string-octet-vector self)))
232
     (if (asn1-teletex-string-p bytes)
233
         (sb-ext:octets-to-string bytes :external-format :ascii)
234
         (error 'invalid-asn1-string :type #.(v-asn1 :teletexstring)))))
235
 
236
 (defmethod decode-asn1-string (self (type (eql #.(v-asn1 :bmpstring))))
237
   (if (= 0 (mod (length self) 2))
238
       (let* ((data (asn1-string-data self))
239
              (len (asn1-string-length self))
240
              (vec (make-octets len)))
241
         (clone-octets-from-alien data vec len)
242
         (sb-ext:octets-to-string vec :external-format :utf-16/be))
243
       (error 'invalid-asn1-string :type (v-asn1 :bmpstring))))
244
 
245
 (defun try-get-asn1-string-data (asn1-string allowed-types)
246
   (let ((type (asn1-string-type asn1-string)))
247
     (assert (member (v-asn1 (asn1-string-type asn1-string)) allowed-types) nil "Invalid asn1 string type")
248
     (decode-asn1-string asn1-string type)))
249
 
250
 ;; ASN1 Times are represented with ASN1 Strings
251
 (defun decode-asn1-time (asn1-time)
252
   (when (zerop (asn1-time-check asn1-time))
253
     (error "asn1-time is not a syntactically valid ASN1 UTCTime"))
254
   (let ((time-string (sb-ext:octets-to-string (asn1-string-octet-vector asn1-time)
255
                                               :external-format :ascii)))
256
     (let* ((utctime-p (= 1 (asn1-utctime-check asn1-time)))
257
            (year-len (if utctime-p 2 4))
258
            (year-part (parse-integer (subseq time-string 0 year-len)))
259
            (year (if utctime-p
260
                      (if (>= year-part 50)
261
                          (+ 1900 year-part)
262
                          (+ 2000 year-part))
263
                      year-part)))
264
       (flet ((get-element-after-year (position)
265
                (parse-integer
266
                 (subseq time-string
267
                         (+ position year-len)
268
                         (+ position year-len 2)))))
269
         (let ((month  (get-element-after-year 0))
270
               (day    (get-element-after-year 2))
271
               (hour   (get-element-after-year 4))
272
               (minute (get-element-after-year 6))
273
               (second (get-element-after-year 8)))
274
           (encode-universal-time second minute hour day month year 0))))))
275
 
276
 (defmethod deserialize (from (format (eql :asn1)) &key v-asn1)
277
   (decode-asn1-string from (if v-asn1 (v-asn1 v-asn1) (asn1-string-type from))))