xref: /dragonfly/contrib/wpa_supplicant/src/tls/asn1.c (revision 3a84a4273475ed07d0ab1c2dfeffdfedef35d9cd)
1 /*
2  * ASN.1 DER parsing
3  * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8 
9 #include "includes.h"
10 
11 #include "common.h"
12 #include "asn1.h"
13 
14 struct asn1_oid asn1_sha1_oid = {
15           .oid = { 1, 3, 14, 3, 2, 26 },
16           .len = 6
17 };
18 
19 struct asn1_oid asn1_sha256_oid = {
20           .oid = { 2, 16, 840, 1, 101, 3, 4, 2, 1 },
21           .len = 9
22 };
23 
24 
asn1_valid_der_boolean(struct asn1_hdr * hdr)25 static int asn1_valid_der_boolean(struct asn1_hdr *hdr)
26 {
27           /* Enforce DER requirements for a single way of encoding a BOOLEAN */
28           if (hdr->length != 1) {
29                     wpa_printf(MSG_DEBUG, "ASN.1: Unexpected BOOLEAN length (%u)",
30                                  hdr->length);
31                     return 0;
32           }
33 
34           if (hdr->payload[0] != 0 && hdr->payload[0] != 0xff) {
35                     wpa_printf(MSG_DEBUG,
36                                  "ASN.1: Invalid BOOLEAN value 0x%x (DER requires 0 or 0xff)",
37                                  hdr->payload[0]);
38                     return 0;
39           }
40 
41           return 1;
42 }
43 
44 
asn1_valid_der(struct asn1_hdr * hdr)45 static int asn1_valid_der(struct asn1_hdr *hdr)
46 {
47           if (hdr->class != ASN1_CLASS_UNIVERSAL)
48                     return 1;
49           if (hdr->tag == ASN1_TAG_BOOLEAN && !asn1_valid_der_boolean(hdr))
50                     return 0;
51           return 1;
52 }
53 
54 
asn1_get_next(const u8 * buf,size_t len,struct asn1_hdr * hdr)55 int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr)
56 {
57           const u8 *pos, *end;
58           u8 tmp;
59 
60           os_memset(hdr, 0, sizeof(*hdr));
61           pos = buf;
62           end = buf + len;
63 
64           if (pos >= end) {
65                     wpa_printf(MSG_DEBUG, "ASN.1: No room for Identifier");
66                     return -1;
67           }
68           hdr->identifier = *pos++;
69           hdr->class = hdr->identifier >> 6;
70           hdr->constructed = !!(hdr->identifier & (1 << 5));
71 
72           if ((hdr->identifier & 0x1f) == 0x1f) {
73                     hdr->tag = 0;
74                     do {
75                               if (pos >= end) {
76                                         wpa_printf(MSG_DEBUG, "ASN.1: Identifier "
77                                                      "underflow");
78                                         return -1;
79                               }
80                               tmp = *pos++;
81                               wpa_printf(MSG_MSGDUMP, "ASN.1: Extended tag data: "
82                                            "0x%02x", tmp);
83                               hdr->tag = (hdr->tag << 7) | (tmp & 0x7f);
84                     } while (tmp & 0x80);
85           } else
86                     hdr->tag = hdr->identifier & 0x1f;
87 
88           if (pos >= end) {
89                     wpa_printf(MSG_DEBUG, "ASN.1: No room for Length");
90                     return -1;
91           }
92           tmp = *pos++;
93           if (tmp & 0x80) {
94                     if (tmp == 0xff) {
95                               wpa_printf(MSG_DEBUG, "ASN.1: Reserved length "
96                                            "value 0xff used");
97                               return -1;
98                     }
99                     tmp &= 0x7f; /* number of subsequent octets */
100                     hdr->length = 0;
101                     if (tmp > 4) {
102                               wpa_printf(MSG_DEBUG, "ASN.1: Too long length field");
103                               return -1;
104                     }
105                     while (tmp--) {
106                               if (pos >= end) {
107                                         wpa_printf(MSG_DEBUG, "ASN.1: Length "
108                                                      "underflow");
109                                         return -1;
110                               }
111                               hdr->length = (hdr->length << 8) | *pos++;
112                     }
113           } else {
114                     /* Short form - length 0..127 in one octet */
115                     hdr->length = tmp;
116           }
117 
118           if (end < pos || hdr->length > (unsigned int) (end - pos)) {
119                     wpa_printf(MSG_DEBUG, "ASN.1: Contents underflow");
120                     return -1;
121           }
122 
123           hdr->payload = pos;
124 
125           return asn1_valid_der(hdr) ? 0 : -1;
126 }
127 
128 
asn1_parse_oid(const u8 * buf,size_t len,struct asn1_oid * oid)129 int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid)
130 {
131           const u8 *pos, *end;
132           unsigned long val;
133           u8 tmp;
134 
135           os_memset(oid, 0, sizeof(*oid));
136 
137           pos = buf;
138           end = buf + len;
139 
140           while (pos < end) {
141                     val = 0;
142 
143                     do {
144                               if (pos >= end)
145                                         return -1;
146                               tmp = *pos++;
147                               val = (val << 7) | (tmp & 0x7f);
148                     } while (tmp & 0x80);
149 
150                     if (oid->len >= ASN1_MAX_OID_LEN) {
151                               wpa_printf(MSG_DEBUG, "ASN.1: Too long OID value");
152                               return -1;
153                     }
154                     if (oid->len == 0) {
155                               /*
156                                * The first octet encodes the first two object
157                                * identifier components in (X*40) + Y formula.
158                                * X = 0..2.
159                                */
160                               oid->oid[0] = val / 40;
161                               if (oid->oid[0] > 2)
162                                         oid->oid[0] = 2;
163                               oid->oid[1] = val - oid->oid[0] * 40;
164                               oid->len = 2;
165                     } else
166                               oid->oid[oid->len++] = val;
167           }
168 
169           return 0;
170 }
171 
172 
asn1_get_oid(const u8 * buf,size_t len,struct asn1_oid * oid,const u8 ** next)173 int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid,
174                      const u8 **next)
175 {
176           struct asn1_hdr hdr;
177 
178           if (asn1_get_next(buf, len, &hdr) < 0 || hdr.length == 0)
179                     return -1;
180 
181           if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OID) {
182                     wpa_printf(MSG_DEBUG, "ASN.1: Expected OID - found class %d "
183                                  "tag 0x%x", hdr.class, hdr.tag);
184                     return -1;
185           }
186 
187           *next = hdr.payload + hdr.length;
188 
189           return asn1_parse_oid(hdr.payload, hdr.length, oid);
190 }
191 
192 
asn1_oid_to_str(const struct asn1_oid * oid,char * buf,size_t len)193 void asn1_oid_to_str(const struct asn1_oid *oid, char *buf, size_t len)
194 {
195           char *pos = buf;
196           size_t i;
197           int ret;
198 
199           if (len == 0)
200                     return;
201 
202           buf[0] = '\0';
203 
204           for (i = 0; i < oid->len; i++) {
205                     ret = os_snprintf(pos, buf + len - pos,
206                                           "%s%lu",
207                                           i == 0 ? "" : ".", oid->oid[i]);
208                     if (os_snprintf_error(buf + len - pos, ret))
209                               break;
210                     pos += ret;
211           }
212           buf[len - 1] = '\0';
213 }
214 
215 
rotate_bits(u8 octet)216 static u8 rotate_bits(u8 octet)
217 {
218           int i;
219           u8 res;
220 
221           res = 0;
222           for (i = 0; i < 8; i++) {
223                     res <<= 1;
224                     if (octet & 1)
225                               res |= 1;
226                     octet >>= 1;
227           }
228 
229           return res;
230 }
231 
232 
asn1_bit_string_to_long(const u8 * buf,size_t len)233 unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len)
234 {
235           unsigned long val = 0;
236           const u8 *pos = buf;
237 
238           /* BER requires that unused bits are zero, so we can ignore the number
239            * of unused bits */
240           pos++;
241 
242           if (len >= 2)
243                     val |= rotate_bits(*pos++);
244           if (len >= 3)
245                     val |= ((unsigned long) rotate_bits(*pos++)) << 8;
246           if (len >= 4)
247                     val |= ((unsigned long) rotate_bits(*pos++)) << 16;
248           if (len >= 5)
249                     val |= ((unsigned long) rotate_bits(*pos++)) << 24;
250           if (len >= 6)
251                     wpa_printf(MSG_DEBUG, "X509: %s - some bits ignored "
252                                  "(BIT STRING length %lu)",
253                                  __func__, (unsigned long) len);
254 
255           return val;
256 }
257 
258 
asn1_oid_equal(const struct asn1_oid * a,const struct asn1_oid * b)259 int asn1_oid_equal(const struct asn1_oid *a, const struct asn1_oid *b)
260 {
261           size_t i;
262 
263           if (a->len != b->len)
264                     return 0;
265 
266           for (i = 0; i < a->len; i++) {
267                     if (a->oid[i] != b->oid[i])
268                               return 0;
269           }
270 
271           return 1;
272 }
273