guile-fediverse/http-signature/asn1.scm

asn1.scm

1
;;;; Copyright (C) 2020 Julien Lepiller <julien@lepiller.eu>
2
;;;; 
3
;;;; This library is free software; you can redistribute it and/or
4
;;;; modify it under the terms of the GNU Lesser General Public
5
;;;; License as published by the Free Software Foundation; either
6
;;;; version 3 of the License, or (at your option) any later version.
7
;;;; 
8
;;;; This library is distributed in the hope that it will be useful,
9
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
10
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11
;;;; Lesser General Public License for more details.
12
;;;; 
13
;;;; You should have received a copy of the GNU Lesser General Public
14
;;;; License along with this library; if not, write to the Free Software
15
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
;;;; 
17
18
(define-module (http-signature asn1)
19
  #:use-module (gcrypt base64)
20
  #:use-module (gcrypt pk-crypto)
21
  #:use-module (ice-9 match)
22
  #:use-module (srfi srfi-1)
23
  #:use-module (srfi srfi-9)
24
  #:use-module (rnrs bytevectors)
25
  #:export (asn1-type?
26
            make-asn1-type
27
            asn1-type-name
28
            asn1-type-type
29
            asn1-type-primitive?
30
            asn1-type-constructed?
31
            asn1-type-parser
32
            asn1-type-producer
33
            asn1-type-aggregator
34
35
            asn1:bool
36
            asn1:null
37
            asn1:int-bv
38
            asn1:bitstring
39
            asn1:octetstring
40
            asn1:object-identifier
41
            asn1:sequence
42
43
            encode-asn1
44
            decode-asn1))
45
46
;; We declare the ASN.1 types here. Each type is associated with its name,
47
;; its type byte, a parser and a producer.  We will use them to either parse
48
;; a DER encoded ASN.1 record, or to produce a DER-encoded ASN.1 record.
49
50
(define-record-type asn1-type
51
  (make-asn1-type name type primitive? constructed? parser producer aggregator)
52
  asn1-type?
53
  (name asn1-type-name)
54
  (type asn1-type-type)
55
  (primitive? asn1-type-primitive?)
56
  (constructed? asn1-type-constructed?)
57
  (parser asn1-type-parser)
58
  (producer asn1-type-producer)
59
  (aggregator asn1-type-aggregator))
60
61
(define asn1:bool
62
  (make-asn1-type
63
    "bool" 1 #t #f
64
    (lambda (bv pos len)
65
      (unless (equal? len 1)
66
        (throw 'invalid-bool-value))
67
      (let ((val (bytevector-u8-ref bv pos)))
68
        (not (equal? val 0))))
69
    (lambda (value)
70
      (let ((bv (make-bytevector 1)))
71
        (bytevector-u8-set! bv 0 (if value #xff #x00))
72
        bv))
73
    (lambda _ (throw 'primitive-type))))
74
75
(define asn1:null
76
  (make-asn1-type
77
    "null" 5 #t #f
78
    (lambda (bv pos len)
79
      (unless (equal? len 0)
80
        (throw 'invalid-null-value))
81
      #nil)
82
    (lambda (value)
83
      (make-bytevector 0))
84
    (lambda _ (throw 'primitive-type))))
85
86
;(define asn1:int-int
87
;  (make-asn1-type
88
;    "int" 2
89
;    (lambda (bv pos len)
90
;      (let ((ans (make-bytevector len)))
91
;        (bytevector-copy! bv pos ans 0 len)
92
;        (let loop ((num 0) (off 0))
93
;          (if (> off len)
94
;            num
95
;            (loop (+ (* num 256) (bytevector-u8-ref bv off))
96
;                  (+ off 1))))))
97
;    (lambda (value)
98
;      ())))
99
100
(define asn1:int-bv
101
  (make-asn1-type
102
    "int" 2 #t #f
103
    (lambda (bv pos len)
104
      (let ((ans (make-bytevector len)))
105
        (bytevector-copy! bv pos ans 0 len)
106
        ans))
107
    (lambda (value)
108
      value)
109
    (lambda _ (throw 'primitive-type))))
110
111
;(define asn1:enum asn1:int-int)
112
113
(define asn1:bitstring
114
  (make-asn1-type
115
    "bitstring" 3 #t #t
116
    (lambda (bv pos len)
117
      (let ((ans (make-bytevector len)))
118
        (bytevector-copy! bv pos ans 0 len)
119
        ans))
120
    (lambda (value)
121
      value)
122
    (lambda (value)
123
      (apply bv-append value))))
124
125
(define asn1:octetstring
126
  (make-asn1-type
127
    "octetstring" 4 #t #t
128
    (lambda (bv pos len)
129
      (let ((ans (make-bytevector len)))
130
        (bytevector-copy! bv pos ans 0 len)
131
        ans))
132
    (lambda (value)
133
      value)
134
    (lambda (value)
135
      (apply bv-append value))))
136
137
(define asn1:object-identifier
138
  (make-asn1-type
139
    "object-identifier" 6 #t #f
140
    (lambda (bv pos len)
141
      (when (= len 0)
142
        (throw 'invalid-object-identifier))
143
      (let loop ((len len) (pos pos) (value '()) (new? #t))
144
        (if (<= len 0)
145
            (if (= len 0)
146
                (let* ((vals (reverse value))
147
                       (first-identifier (car vals)))
148
                  (append
149
                    (cond
150
                      ((< first-identifier 40) (list 0 (modulo first-identifier 40)))
151
                      ((< first-identifier 80) (list 1 (modulo first-identifier 40)))
152
                      (else (list 2 (- first-identifier 80))))
153
                    (cdr vals)))
154
                (throw 'value-too-long))
155
            (let* ((val (bytevector-u8-ref bv pos))
156
                   (cont? (> val #x80))
157
                   (val (logand #x7f val)))
158
              (if new?
159
                (loop (- len 1) (+ pos 1)
160
                      (cons val value)
161
                      (not cont?))
162
                (loop (- len 1) (+ pos 1)
163
                      (cons (+ val (* (car value) 128)) (cdr value))
164
                      (not cont?)))))))
165
    (lambda (value)
166
      (match value
167
        ((x y rest ...)
168
         (let ((first-byte (+ (* x 40) y)))
169
           (apply bv-append
170
             (map
171
               (lambda (identifier)
172
                 (define (id-len identifier)
173
                   (if (< identifier 128)
174
                       1
175
                       (+ 1 (id-len (quotient identifier 128)))))
176
                 (let ((bv (make-bytevector (id-len identifier))))
177
                   (let loop ((pos (- (bytevector-length bv) 1)) (id identifier))
178
                     (if (= pos (- (bytevector-length bv) 1))
179
                       (begin
180
                         (bytevector-u8-set! bv pos (modulo id 128))
181
                         (loop (- pos 1) (quotient id 128)))
182
                       (if (= pos -1)
183
                           bv
184
                           (begin
185
                             (bytevector-u8-set! bv pos (+ #x80 (modulo id 128)))
186
                             (loop (- pos 1) (quotient id 128))))))))
187
               (cons first-byte rest)))))
188
        (_ (throw 'invalid-identifier))))
189
    (lambda _ (throw 'primitive-type))))
190
191
;; Note that a sequence depends on the types of its contents
192
(define* (asn1:sequence . types)
193
  (make-asn1-type
194
    "sequence" #x10 #f #t
195
    (lambda (bv pos len)
196
      (let loop ((result '()) (types types) (rest-len len) (pos pos))
197
        (match types
198
          (()
199
           (unless (equal? rest-len 0)
200
             (throw 'invalid-sequence-type))
201
           (reverse result))
202
          ((type types ...)
203
           (match (decode-asn1-aux bv pos type)
204
             ((value . npos)
205
              (loop (cons value result) types (- rest-len (- npos pos)) npos)))))))
206
    (lambda (value)
207
      (unless (equal? (length value) (length types))
208
        (throw 'incompatible-type-and-value-sizes))
209
      (apply
210
        bv-append
211
        (map encode-asn1 types value)))
212
    (lambda (value) value)))
213
214
215
;; Now these functions are related to parsing or producing DER-encoded values
216
;; as bytevectors.
217
218
(define (decode-asn1-aux bv pos type)
219
  "Return a value that corresponds to the DER-encoded value of @var{type}
220
in @var{bv} at @var{pos}.  The return value is a pair of the decoded
221
value and the new position after reading the bytevector."
222
  (define (decode-len bv pos)
223
    (let ((first-byte (bytevector-u8-ref bv pos)))
224
      (if (> first-byte 127)
225
        (let ((num-bytes (- first-byte 128)))
226
          (let loop ((pos (+ pos 1)) (num num-bytes) (result 0))
227
            (if (eq? num 0)
228
                result
229
                (loop (+ pos 1) (- num 1)
230
                      (+ (bytevector-u8-ref bv pos) (* 256 result))))))
231
        first-byte)))
232
233
  (define (lenlen bv pos)
234
    (let ((first-byte (bytevector-u8-ref bv pos)))
235
      (if (> first-byte 127)
236
          (- first-byte 127); one for the size byte too, so 127 not 128
237
          1)))
238
239
  (define (constructed? type-byte)
240
    (not (eq? (logand type-byte #x20) 0)))
241
242
  (define (decode-asn1-array bv pos type)
243
    (let loop ((value '()) (pos pos))
244
      (if (= (bytevector-u8-ref bv pos) 0)
245
          (begin
246
            (unless (= (bytevector-u8-ref bv (+ pos 1)) 0)
247
              (throw 'invalid-EOC-length))
248
            (cons ((asn1-type-aggregator type) (reverse value)) (+ pos 2)))
249
          (let* ((res (decode-asn1-aux bv pos type))
250
                 (val (car res))
251
                 (pos (cdr res)))
252
            (loop (cons val value) pos)))))
253
254
  (let* ((type-byte (bytevector-u8-ref bv pos))
255
         (constructed? (constructed? type-byte))
256
         (pos (+ pos 1))
257
         (len-byte (bytevector-u8-ref bv pos))
258
         (len (decode-len bv pos))
259
         (pos (+ pos (lenlen bv pos))))
260
    (unless (equal? (- type-byte (if constructed? #x20 0)) (asn1-type-type type))
261
      (throw 'invalid-type-byte type-byte type))
262
    (when (and constructed? (not (asn1-type-constructed? type)))
263
      (throw 'invalid-constructed-type))
264
    (when (and (not constructed?) (not (asn1-type-primitive? type)))
265
      (throw 'invalid-primitive-type))
266
    (if (and constructed? (equal? len-byte #x80))
267
      (decode-asn1-array bv pos type)
268
      (cons ((asn1-type-parser type) bv pos len) (+ pos len)))))
269
270
(define (decode-asn1 bv type)
271
  "Decode the content of @var{bv}, a DER record of @var{type}"
272
  (match (decode-asn1-aux bv 0 type)
273
    ((value . pos) value)))
274
275
(define (encode-asn1 type value)
276
  "Encode the @var{value} of @var{type} into a DER record represented by a
277
bytevector."
278
  (let* ((type-byte (asn1-type-type type))
279
         (type-byte (if (not (asn1-type-primitive? type))
280
                        (+ #x20 type-byte)
281
                        type-byte))
282
         (value ((asn1-type-producer type) value))
283
         (len (encode-len (bytevector-length value)))
284
         (type-bv (make-bytevector 1)))
285
    (bytevector-u8-set! type-bv 0 type-byte)
286
    (bv-append type-bv len value)))
287
288
(define (bv-append . bvs)
289
  "Append multiple bytevectors in a single bytevector."
290
  (match bvs
291
    (() (make-bytevector 0))
292
    ((bv bvs ...)
293
     (let* ((bvs (apply bv-append bvs))
294
            (len1 (bytevector-length bv))
295
            (len2 (bytevector-length bvs))
296
            (result (make-bytevector (+ len1 len2))))
297
       (bytevector-copy! bv 0 result 0 len1)
298
       (bytevector-copy! bvs 0 result len1 len2)
299
       result))))
300
301
(define (encode-len len)
302
  "Encode the length in a bytevector, following the ASN.1 specification: on 7
303
bits if it fits, or the first byte has its most significant bit set, and the 7
304
remaining bits represent the number of bytes needed to represent the length."
305
  (define (nbytes len)
306
    "Return the number of bytes needed to represent a number"
307
    (if (< len 256) 1 (+ 1 (nbytes (quotient len 256)))))
308
309
  (let ((bv (make-bytevector (+ 1 (if (> len 127) (nbytes len) 0)))))
310
    (bytevector-u8-set! bv 0 (if (> len 127) (+ 128 (nbytes len)) len))
311
    (when (> len 127)
312
      (let loop ((val len) (pos (- (bytevector-length bv) 1)))
313
        (when (> val 0)
314
          (bytevector-u8-set! bv pos (modulo val 256))
315
          (loop (quotient val 256) (- pos 1)))))
316
    bv))
317