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
      (if (= len 0)
118
          (make-bytevector 0)
119
          (let ((ans (make-bytevector (- len 1))))
120
            (bytevector-copy! bv (+ pos 1) ans 0 (- len 1))
121
            ans)))
122
    (lambda (value)
123
      (if (= (bytevector-length value) 0)
124
          (make-bytevector 0)
125
          (let ((ans (make-bytevector (+ (bytevector-length value) 1))))
126
            (bytevector-u8-set! ans 0 0)
127
            (bytevector-copy! value 0 ans 1 (bytevector-length value))
128
            ans)))
129
    (lambda (value)
130
      (apply bv-append value))))
131
132
(define asn1:octetstring
133
  (make-asn1-type
134
    "octetstring" 4 #t #t
135
    (lambda (bv pos len)
136
      (let ((ans (make-bytevector len)))
137
        (bytevector-copy! bv pos ans 0 len)
138
        ans))
139
    (lambda (value)
140
      value)
141
    (lambda (value)
142
      (apply bv-append value))))
143
144
(define asn1:object-identifier
145
  (make-asn1-type
146
    "object-identifier" 6 #t #f
147
    (lambda (bv pos len)
148
      (when (= len 0)
149
        (throw 'invalid-object-identifier))
150
      (let loop ((len len) (pos pos) (value '()) (new? #t))
151
        (if (<= len 0)
152
            (if (= len 0)
153
                (let* ((vals (reverse value))
154
                       (first-identifier (car vals)))
155
                  (append
156
                    (cond
157
                      ((< first-identifier 40) (list 0 (modulo first-identifier 40)))
158
                      ((< first-identifier 80) (list 1 (modulo first-identifier 40)))
159
                      (else (list 2 (- first-identifier 80))))
160
                    (cdr vals)))
161
                (throw 'value-too-long))
162
            (let* ((val (bytevector-u8-ref bv pos))
163
                   (cont? (> val #x80))
164
                   (val (logand #x7f val)))
165
              (if new?
166
                (loop (- len 1) (+ pos 1)
167
                      (cons val value)
168
                      (not cont?))
169
                (loop (- len 1) (+ pos 1)
170
                      (cons (+ val (* (car value) 128)) (cdr value))
171
                      (not cont?)))))))
172
    (lambda (value)
173
      (match value
174
        ((x y rest ...)
175
         (let ((first-byte (+ (* x 40) y)))
176
           (apply bv-append
177
             (map
178
               (lambda (identifier)
179
                 (define (id-len identifier)
180
                   (if (< identifier 128)
181
                       1
182
                       (+ 1 (id-len (quotient identifier 128)))))
183
                 (let ((bv (make-bytevector (id-len identifier))))
184
                   (let loop ((pos (- (bytevector-length bv) 1)) (id identifier))
185
                     (if (= pos (- (bytevector-length bv) 1))
186
                       (begin
187
                         (bytevector-u8-set! bv pos (modulo id 128))
188
                         (loop (- pos 1) (quotient id 128)))
189
                       (if (= pos -1)
190
                           bv
191
                           (begin
192
                             (bytevector-u8-set! bv pos (+ #x80 (modulo id 128)))
193
                             (loop (- pos 1) (quotient id 128))))))))
194
               (cons first-byte rest)))))
195
        (_ (throw 'invalid-identifier))))
196
    (lambda _ (throw 'primitive-type))))
197
198
;; Note that a sequence depends on the types of its contents
199
(define* (asn1:sequence . types)
200
  (make-asn1-type
201
    "sequence" #x10 #f #t
202
    (lambda (bv pos len)
203
      (let loop ((result '()) (types types) (rest-len len) (pos pos))
204
        (match types
205
          (()
206
           (unless (equal? rest-len 0)
207
             (throw 'invalid-sequence-type))
208
           (reverse result))
209
          ((type types ...)
210
           (match (decode-asn1-aux bv pos type)
211
             ((value . npos)
212
              (loop (cons value result) types (- rest-len (- npos pos)) npos)))))))
213
    (lambda (value)
214
      (unless (equal? (length value) (length types))
215
        (throw 'incompatible-type-and-value-sizes))
216
      (apply
217
        bv-append
218
        (map encode-asn1 types value)))
219
    (lambda (value) value)))
220
221
222
;; Now these functions are related to parsing or producing DER-encoded values
223
;; as bytevectors.
224
225
(define (decode-asn1-aux bv pos type)
226
  "Return a value that corresponds to the DER-encoded value of @var{type}
227
in @var{bv} at @var{pos}.  The return value is a pair of the decoded
228
value and the new position after reading the bytevector."
229
  (define (decode-len bv pos)
230
    (let ((first-byte (bytevector-u8-ref bv pos)))
231
      (if (> first-byte 127)
232
        (let ((num-bytes (- first-byte 128)))
233
          (let loop ((pos (+ pos 1)) (num num-bytes) (result 0))
234
            (if (eq? num 0)
235
                result
236
                (loop (+ pos 1) (- num 1)
237
                      (+ (bytevector-u8-ref bv pos) (* 256 result))))))
238
        first-byte)))
239
240
  (define (lenlen bv pos)
241
    (let ((first-byte (bytevector-u8-ref bv pos)))
242
      (if (> first-byte 127)
243
          (- first-byte 127); one for the size byte too, so 127 not 128
244
          1)))
245
246
  (define (constructed? type-byte)
247
    (not (eq? (logand type-byte #x20) 0)))
248
249
  (define (decode-asn1-array bv pos type)
250
    (let loop ((value '()) (pos pos))
251
      (if (= (bytevector-u8-ref bv pos) 0)
252
          (begin
253
            (unless (= (bytevector-u8-ref bv (+ pos 1)) 0)
254
              (throw 'invalid-EOC-length))
255
            (cons ((asn1-type-aggregator type) (reverse value)) (+ pos 2)))
256
          (let* ((res (decode-asn1-aux bv pos type))
257
                 (val (car res))
258
                 (pos (cdr res)))
259
            (loop (cons val value) pos)))))
260
261
  (let* ((type-byte (bytevector-u8-ref bv pos))
262
         (constructed? (constructed? type-byte))
263
         (pos (+ pos 1))
264
         (len-byte (bytevector-u8-ref bv pos))
265
         (len (decode-len bv pos))
266
         (pos (+ pos (lenlen bv pos))))
267
    (unless (equal? (- type-byte (if constructed? #x20 0)) (asn1-type-type type))
268
      (throw 'invalid-type-byte type-byte type))
269
    (when (and constructed? (not (asn1-type-constructed? type)))
270
      (throw 'invalid-constructed-type))
271
    (when (and (not constructed?) (not (asn1-type-primitive? type)))
272
      (throw 'invalid-primitive-type))
273
    (if (and constructed? (equal? len-byte #x80))
274
      (decode-asn1-array bv pos type)
275
      (cons ((asn1-type-parser type) bv pos len) (+ pos len)))))
276
277
(define (decode-asn1 bv type)
278
  "Decode the content of @var{bv}, a DER record of @var{type}"
279
  (match (decode-asn1-aux bv 0 type)
280
    ((value . pos) value)))
281
282
(define (encode-asn1 type value)
283
  "Encode the @var{value} of @var{type} into a DER record represented by a
284
bytevector."
285
  (let* ((type-byte (asn1-type-type type))
286
         (type-byte (if (not (asn1-type-primitive? type))
287
                        (+ #x20 type-byte)
288
                        type-byte))
289
         (value ((asn1-type-producer type) value))
290
         (len (encode-len (bytevector-length value)))
291
         (type-bv (make-bytevector 1)))
292
    (bytevector-u8-set! type-bv 0 type-byte)
293
    (bv-append type-bv len value)))
294
295
(define (bv-append . bvs)
296
  "Append multiple bytevectors in a single bytevector."
297
  (match bvs
298
    (() (make-bytevector 0))
299
    ((bv bvs ...)
300
     (let* ((bvs (apply bv-append bvs))
301
            (len1 (bytevector-length bv))
302
            (len2 (bytevector-length bvs))
303
            (result (make-bytevector (+ len1 len2))))
304
       (bytevector-copy! bv 0 result 0 len1)
305
       (bytevector-copy! bvs 0 result len1 len2)
306
       result))))
307
308
(define (encode-len len)
309
  "Encode the length in a bytevector, following the ASN.1 specification: on 7
310
bits if it fits, or the first byte has its most significant bit set, and the 7
311
remaining bits represent the number of bytes needed to represent the length."
312
  (define (nbytes len)
313
    "Return the number of bytes needed to represent a number"
314
    (if (< len 256) 1 (+ 1 (nbytes (quotient len 256)))))
315
316
  (let ((bv (make-bytevector (+ 1 (if (> len 127) (nbytes len) 0)))))
317
    (bytevector-u8-set! bv 0 (if (> len 127) (+ 128 (nbytes len)) len))
318
    (when (> len 127)
319
      (let loop ((val len) (pos (- (bytevector-length bv) 1)))
320
        (when (> val 0)
321
          (bytevector-u8-set! bv pos (modulo val 256))
322
          (loop (quotient val 256) (- pos 1)))))
323
    bv))
324