Add DER (ASN.1) parser.

Julien LepillerThu Jan 30 02:28:05+0100 2020

528f873

Add DER (ASN.1) parser. This will be useful because we will need to parse and generate PEM-encoded values, which are the base64 of a DER encoded record.

http-signature/asn1.scm unknown status 1

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))