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-parser
30
            asn1-type-producer
31
32
            asn1:bool
33
            asn1:null
34
            asn1:sequence
35
36
            encode-asn1
37
            decode-asn1))
38
39
;; We declare the ASN.1 types here. Each type is associated with its name,
40
;; its type byte, a parser and a producer.  We will use them to either parse
41
;; a DER encoded ASN.1 record, or to produce a DER-encoded ASN.1 record.
42
43
(define-record-type asn1-type
44
  (make-asn1-type name type parser producer)
45
  asn1-type?
46
  (name asn1-type-name)
47
  (type asn1-type-type)
48
  (parser asn1-type-parser)
49
  (producer asn1-type-producer))
50
51
(define asn1:bool
52
  (make-asn1-type
53
    "bool" 1
54
    (lambda (bv pos len)
55
      (unless (equal? len 1)
56
        (throw 'invalid-bool-value))
57
      (let ((val (bytevector-u8-ref bv pos)))
58
        (not (equal? val 0))))
59
    (lambda (value)
60
      (let ((bv (make-bytevector 1)))
61
        (bytevector-u8-set! bv 0 (if value #xff #x00))
62
        bv))))
63
64
(define asn1:null
65
  (make-asn1-type
66
    "null" 5
67
    (lambda (bv pos len)
68
      (unless (equal? len 0)
69
        (throw 'invalid-null-value))
70
      #nil)
71
    (lambda (value)
72
      (make-bytevector 0))))
73
74
;; Note that a sequence depends on the types of its contents
75
(define* (asn1:sequence . types)
76
  (make-asn1-type
77
    "sequence" #x30
78
    (lambda (bv pos len)
79
      (let loop ((result '()) (types types) (rest-len len) (pos pos))
80
        (match types
81
          (()
82
           (unless (equal? rest-len 0)
83
             (throw 'invalid-sequence-type))
84
           (reverse result))
85
          ((type types ...)
86
           (match (decode-asn1-aux bv pos type)
87
             ((value . npos)
88
              (loop (cons value result) types (- rest-len (- npos pos)) npos)))))))
89
    (lambda (value)
90
      (unless (equal? (length value) (length types))
91
        (throw 'incompatible-type-and-value-sizes))
92
      (apply
93
        bv-append
94
        (map encode-asn1 types value)))))
95
96
97
;; Now these functions are related to parsing or producing DER-encoded values
98
;; as bytevectors.
99
100
(define (decode-asn1-aux bv pos type)
101
  "Return a value that corresponds to the DER-encoded value of @var{type}
102
in @var{bv} at @var{pos}.  The return value is a pair of the decoded
103
value and the new position after reading the bytevector."
104
  (define (decode-len bv pos)
105
    (let ((first-byte (bytevector-u8-ref bv pos)))
106
      (if (> first-byte 127)
107
        (let ((num-bytes (- first-byte 128)))
108
          (let loop ((pos (+ pos 1)) (num num-bytes) (result 0))
109
            (if (eq? num 0)
110
                result
111
                (loop (+ pos 1) (- num 1)
112
                      (+ (bytevector-u8-ref bv pos) (* 256 result))))))
113
        first-byte)))
114
115
  (define (lenlen bv pos)
116
    (let ((first-byte (bytevector-u8-ref bv pos)))
117
      (if (> first-byte 127)
118
          (- first-byte 127); one for the size byte too, so 127 not 128
119
          1)))
120
121
  (let* ((type-byte (bytevector-u8-ref bv pos))
122
         (pos (+ pos 1))
123
         (len (decode-len bv pos))
124
         (pos (+ pos (lenlen bv pos))))
125
    (unless (equal? type-byte (asn1-type-type type))
126
      (throw 'invalid-type-byte))
127
    (cons ((asn1-type-parser type) bv pos len) (+ pos len))))
128
129
(define (decode-asn1 bv type)
130
  "Decode the content of @var{bv}, a DER record of @var{type}"
131
  (match (decode-asn1-aux bv 0 type)
132
    ((value . pos) value)))
133
134
(define (encode-asn1 type value)
135
  "Encode the @var{value} of @var{type} into a DER record represented by a
136
bytevector."
137
  (let* ((type-byte (asn1-type-type type))
138
         (value ((asn1-type-producer type) value))
139
         (len (encode-len (bytevector-length value)))
140
         (type-bv (make-bytevector 1)))
141
    (bytevector-u8-set! type-bv 0 type-byte)
142
    (pk 'encode type-bv len value)
143
    (bv-append type-bv len value)))
144
145
(define (bv-append . bvs)
146
  "Append multiple bytevectors in a single bytevector."
147
  (match bvs
148
    (() (make-bytevector 0))
149
    ((bv bvs ...)
150
     (let* ((bvs (apply bv-append bvs))
151
            (len1 (bytevector-length bv))
152
            (len2 (bytevector-length bvs))
153
            (result (make-bytevector (+ len1 len2))))
154
       (bytevector-copy! bv 0 result 0 len1)
155
       (bytevector-copy! bvs 0 result len1 len2)
156
       result))))
157
158
(define (encode-len len)
159
  "Encode the length in a bytevector, following the ASN.1 specification: on 7
160
bits if it fits, or the first byte has its most significant bit set, and the 7
161
remaining bits represent the number of bytes needed to represent the length."
162
  (define (nbytes len)
163
    "Return the number of bytes needed to represent a number"
164
    (if (< len 256) 1 (+ 1 (nbytes (quotient len 256)))))
165
166
  (let ((bv (make-bytevector (+ 1 (if (> len 127) (nbytes len) 0)))))
167
    (bytevector-u8-set! bv 0 (if (> len 127) (+ 128 (nbytes len)) len))
168
    (when (> len 127)
169
      (let loop ((val len) (pos (- (bytevector-length bv) 1)))
170
        (when (> val 0)
171
          (bytevector-u8-set! bv pos (modulo val 256))
172
          (loop (quotient val 256) (- pos 1)))))
173
    bv))
174