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