xref: /openbmc/qemu/crypto/der.c (revision c9f8004b)
1 /*
2  * QEMU Crypto ASN.1 DER decoder
3  *
4  * Copyright (c) 2022 Bytedance
5  * Author: lei he <helei.sig11@bytedance.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #include "qemu/osdep.h"
23 #include "crypto/der.h"
24 
25 enum QCryptoDERTypeTag {
26     QCRYPTO_DER_TYPE_TAG_BOOL = 0x1,
27     QCRYPTO_DER_TYPE_TAG_INT = 0x2,
28     QCRYPTO_DER_TYPE_TAG_BIT_STR = 0x3,
29     QCRYPTO_DER_TYPE_TAG_OCT_STR = 0x4,
30     QCRYPTO_DER_TYPE_TAG_OCT_NULL = 0x5,
31     QCRYPTO_DER_TYPE_TAG_OCT_OID = 0x6,
32     QCRYPTO_DER_TYPE_TAG_SEQ = 0x10,
33     QCRYPTO_DER_TYPE_TAG_SET = 0x11,
34 };
35 
36 #define QCRYPTO_DER_CONSTRUCTED_MASK 0x20
37 #define QCRYPTO_DER_SHORT_LEN_MASK 0x80
38 
39 static uint8_t qcrypto_der_peek_byte(const uint8_t **data, size_t *dlen)
40 {
41     return **data;
42 }
43 
44 static void qcrypto_der_cut_nbytes(const uint8_t **data,
45                                    size_t *dlen,
46                                    size_t nbytes)
47 {
48     *data += nbytes;
49     *dlen -= nbytes;
50 }
51 
52 static uint8_t qcrypto_der_cut_byte(const uint8_t **data, size_t *dlen)
53 {
54     uint8_t val = qcrypto_der_peek_byte(data, dlen);
55 
56     qcrypto_der_cut_nbytes(data, dlen, 1);
57 
58     return val;
59 }
60 
61 static int qcrypto_der_invoke_callback(QCryptoDERDecodeCb cb, void *ctx,
62                                        const uint8_t *value, size_t vlen,
63                                        Error **errp)
64 {
65     if (!cb) {
66         return 0;
67     }
68 
69     return cb(ctx, value, vlen, errp);
70 }
71 
72 static int qcrypto_der_extract_definite_data(const uint8_t **data, size_t *dlen,
73                                              QCryptoDERDecodeCb cb, void *ctx,
74                                              Error **errp)
75 {
76     const uint8_t *value;
77     size_t vlen = 0;
78     uint8_t byte_count = qcrypto_der_cut_byte(data, dlen);
79 
80     /* short format of definite-length */
81     if (!(byte_count & QCRYPTO_DER_SHORT_LEN_MASK)) {
82         if (byte_count > *dlen) {
83             error_setg(errp, "Invalid content length: %u", byte_count);
84             return -1;
85         }
86 
87         value = *data;
88         vlen = byte_count;
89         qcrypto_der_cut_nbytes(data, dlen, vlen);
90 
91         if (qcrypto_der_invoke_callback(cb, ctx, value, vlen, errp) != 0) {
92             return -1;
93         }
94         return vlen;
95     }
96 
97     /* Ignore highest bit */
98     byte_count &= ~QCRYPTO_DER_SHORT_LEN_MASK;
99 
100     /*
101      * size_t is enough to store the value of length, although the DER
102      * encoding standard supports larger length.
103      */
104     if (byte_count > sizeof(size_t)) {
105         error_setg(errp, "Invalid byte count of content length: %u",
106                    byte_count);
107         return -1;
108     }
109 
110     if (byte_count > *dlen) {
111         error_setg(errp, "Invalid content length: %u", byte_count);
112         return -1;
113     }
114     while (byte_count--) {
115         vlen <<= 8;
116         vlen += qcrypto_der_cut_byte(data, dlen);
117     }
118 
119     if (vlen > *dlen) {
120         error_setg(errp, "Invalid content length: %zu", vlen);
121         return -1;
122     }
123 
124     value = *data;
125     qcrypto_der_cut_nbytes(data, dlen, vlen);
126 
127     if (qcrypto_der_invoke_callback(cb, ctx, value, vlen, errp) != 0) {
128         return -1;
129     }
130     return vlen;
131 }
132 
133 static int qcrypto_der_extract_data(const uint8_t **data, size_t *dlen,
134                                     QCryptoDERDecodeCb cb, void *ctx,
135                                     Error **errp)
136 {
137     uint8_t val;
138     if (*dlen < 1) {
139         error_setg(errp, "Need more data");
140         return -1;
141     }
142     val = qcrypto_der_peek_byte(data, dlen);
143 
144     /* must use definite length format */
145     if (val == QCRYPTO_DER_SHORT_LEN_MASK) {
146         error_setg(errp, "Only definite length format is allowed");
147         return -1;
148     }
149 
150     return qcrypto_der_extract_definite_data(data, dlen, cb, ctx, errp);
151 }
152 
153 int qcrypto_der_decode_int(const uint8_t **data, size_t *dlen,
154                            QCryptoDERDecodeCb cb, void *ctx, Error **errp)
155 {
156     uint8_t tag;
157     if (*dlen < 1) {
158         error_setg(errp, "Need more data");
159         return -1;
160     }
161     tag = qcrypto_der_cut_byte(data, dlen);
162 
163     /* INTEGER must encoded in primitive-form */
164     if (tag != QCRYPTO_DER_TYPE_TAG_INT) {
165         error_setg(errp, "Invalid integer type tag: %u", tag);
166         return -1;
167     }
168 
169     return qcrypto_der_extract_data(data, dlen, cb, ctx, errp);
170 }
171 
172 int qcrypto_der_decode_seq(const uint8_t **data, size_t *dlen,
173                            QCryptoDERDecodeCb cb, void *ctx, Error **errp)
174 {
175     uint8_t tag;
176     if (*dlen < 1) {
177         error_setg(errp, "Need more data");
178         return -1;
179     }
180     tag = qcrypto_der_cut_byte(data, dlen);
181 
182     /* SEQUENCE must use constructed form */
183     if (tag != (QCRYPTO_DER_TYPE_TAG_SEQ | QCRYPTO_DER_CONSTRUCTED_MASK)) {
184         error_setg(errp, "Invalid type sequence tag: %u", tag);
185         return -1;
186     }
187 
188     return qcrypto_der_extract_data(data, dlen, cb, ctx, errp);
189 }
190