1189251Ssam/*
2189251Ssam * ASN.1 DER parsing
3281806Srpaulo * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
4189251Ssam *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7189251Ssam */
8189251Ssam
9189251Ssam#include "includes.h"
10189251Ssam
11189251Ssam#include "common.h"
12189251Ssam#include "asn1.h"
13189251Ssam
14281806Srpaulostruct asn1_oid asn1_sha1_oid = {
15281806Srpaulo	.oid = { 1, 3, 14, 3, 2, 26 },
16281806Srpaulo	.len = 6
17281806Srpaulo};
18281806Srpaulo
19281806Srpaulostruct asn1_oid asn1_sha256_oid = {
20281806Srpaulo	.oid = { 2, 16, 840, 1, 101, 3, 4, 2, 1 },
21281806Srpaulo	.len = 9
22281806Srpaulo};
23281806Srpaulo
24281806Srpaulo
25351611Scystatic int asn1_valid_der_boolean(struct asn1_hdr *hdr)
26351611Scy{
27351611Scy	/* Enforce DER requirements for a single way of encoding a BOOLEAN */
28351611Scy	if (hdr->length != 1) {
29351611Scy		wpa_printf(MSG_DEBUG, "ASN.1: Unexpected BOOLEAN length (%u)",
30351611Scy			   hdr->length);
31351611Scy		return 0;
32351611Scy	}
33351611Scy
34351611Scy	if (hdr->payload[0] != 0 && hdr->payload[0] != 0xff) {
35351611Scy		wpa_printf(MSG_DEBUG,
36351611Scy			   "ASN.1: Invalid BOOLEAN value 0x%x (DER requires 0 or 0xff)",
37351611Scy			   hdr->payload[0]);
38351611Scy		return 0;
39351611Scy	}
40351611Scy
41351611Scy	return 1;
42351611Scy}
43351611Scy
44351611Scy
45351611Scystatic int asn1_valid_der(struct asn1_hdr *hdr)
46351611Scy{
47351611Scy	if (hdr->class != ASN1_CLASS_UNIVERSAL)
48351611Scy		return 1;
49351611Scy	if (hdr->tag == ASN1_TAG_BOOLEAN && !asn1_valid_der_boolean(hdr))
50351611Scy		return 0;
51351611Scy	return 1;
52351611Scy}
53351611Scy
54351611Scy
55189251Ssamint asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr)
56189251Ssam{
57189251Ssam	const u8 *pos, *end;
58189251Ssam	u8 tmp;
59189251Ssam
60189251Ssam	os_memset(hdr, 0, sizeof(*hdr));
61189251Ssam	pos = buf;
62189251Ssam	end = buf + len;
63189251Ssam
64346981Scy	if (pos >= end) {
65346981Scy		wpa_printf(MSG_DEBUG, "ASN.1: No room for Identifier");
66346981Scy		return -1;
67346981Scy	}
68189251Ssam	hdr->identifier = *pos++;
69189251Ssam	hdr->class = hdr->identifier >> 6;
70189251Ssam	hdr->constructed = !!(hdr->identifier & (1 << 5));
71189251Ssam
72189251Ssam	if ((hdr->identifier & 0x1f) == 0x1f) {
73189251Ssam		hdr->tag = 0;
74189251Ssam		do {
75189251Ssam			if (pos >= end) {
76189251Ssam				wpa_printf(MSG_DEBUG, "ASN.1: Identifier "
77189251Ssam					   "underflow");
78189251Ssam				return -1;
79189251Ssam			}
80189251Ssam			tmp = *pos++;
81189251Ssam			wpa_printf(MSG_MSGDUMP, "ASN.1: Extended tag data: "
82189251Ssam				   "0x%02x", tmp);
83189251Ssam			hdr->tag = (hdr->tag << 7) | (tmp & 0x7f);
84189251Ssam		} while (tmp & 0x80);
85189251Ssam	} else
86189251Ssam		hdr->tag = hdr->identifier & 0x1f;
87189251Ssam
88346981Scy	if (pos >= end) {
89346981Scy		wpa_printf(MSG_DEBUG, "ASN.1: No room for Length");
90346981Scy		return -1;
91346981Scy	}
92189251Ssam	tmp = *pos++;
93189251Ssam	if (tmp & 0x80) {
94189251Ssam		if (tmp == 0xff) {
95189251Ssam			wpa_printf(MSG_DEBUG, "ASN.1: Reserved length "
96189251Ssam				   "value 0xff used");
97189251Ssam			return -1;
98189251Ssam		}
99189251Ssam		tmp &= 0x7f; /* number of subsequent octets */
100189251Ssam		hdr->length = 0;
101189251Ssam		if (tmp > 4) {
102189251Ssam			wpa_printf(MSG_DEBUG, "ASN.1: Too long length field");
103189251Ssam			return -1;
104189251Ssam		}
105189251Ssam		while (tmp--) {
106189251Ssam			if (pos >= end) {
107189251Ssam				wpa_printf(MSG_DEBUG, "ASN.1: Length "
108189251Ssam					   "underflow");
109189251Ssam				return -1;
110189251Ssam			}
111189251Ssam			hdr->length = (hdr->length << 8) | *pos++;
112189251Ssam		}
113189251Ssam	} else {
114189251Ssam		/* Short form - length 0..127 in one octet */
115189251Ssam		hdr->length = tmp;
116189251Ssam	}
117189251Ssam
118189251Ssam	if (end < pos || hdr->length > (unsigned int) (end - pos)) {
119189251Ssam		wpa_printf(MSG_DEBUG, "ASN.1: Contents underflow");
120189251Ssam		return -1;
121189251Ssam	}
122189251Ssam
123189251Ssam	hdr->payload = pos;
124351611Scy
125351611Scy	return asn1_valid_der(hdr) ? 0 : -1;
126189251Ssam}
127189251Ssam
128189251Ssam
129214734Srpauloint asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid)
130189251Ssam{
131189251Ssam	const u8 *pos, *end;
132189251Ssam	unsigned long val;
133189251Ssam	u8 tmp;
134189251Ssam
135189251Ssam	os_memset(oid, 0, sizeof(*oid));
136189251Ssam
137214734Srpaulo	pos = buf;
138214734Srpaulo	end = buf + len;
139189251Ssam
140189251Ssam	while (pos < end) {
141189251Ssam		val = 0;
142189251Ssam
143189251Ssam		do {
144189251Ssam			if (pos >= end)
145189251Ssam				return -1;
146189251Ssam			tmp = *pos++;
147189251Ssam			val = (val << 7) | (tmp & 0x7f);
148189251Ssam		} while (tmp & 0x80);
149189251Ssam
150189251Ssam		if (oid->len >= ASN1_MAX_OID_LEN) {
151189251Ssam			wpa_printf(MSG_DEBUG, "ASN.1: Too long OID value");
152189251Ssam			return -1;
153189251Ssam		}
154189251Ssam		if (oid->len == 0) {
155189251Ssam			/*
156189251Ssam			 * The first octet encodes the first two object
157189251Ssam			 * identifier components in (X*40) + Y formula.
158189251Ssam			 * X = 0..2.
159189251Ssam			 */
160189251Ssam			oid->oid[0] = val / 40;
161189251Ssam			if (oid->oid[0] > 2)
162189251Ssam				oid->oid[0] = 2;
163189251Ssam			oid->oid[1] = val - oid->oid[0] * 40;
164189251Ssam			oid->len = 2;
165189251Ssam		} else
166189251Ssam			oid->oid[oid->len++] = val;
167189251Ssam	}
168189251Ssam
169189251Ssam	return 0;
170189251Ssam}
171189251Ssam
172189251Ssam
173214734Srpauloint asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid,
174214734Srpaulo		 const u8 **next)
175214734Srpaulo{
176214734Srpaulo	struct asn1_hdr hdr;
177214734Srpaulo
178214734Srpaulo	if (asn1_get_next(buf, len, &hdr) < 0 || hdr.length == 0)
179214734Srpaulo		return -1;
180214734Srpaulo
181214734Srpaulo	if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OID) {
182214734Srpaulo		wpa_printf(MSG_DEBUG, "ASN.1: Expected OID - found class %d "
183214734Srpaulo			   "tag 0x%x", hdr.class, hdr.tag);
184214734Srpaulo		return -1;
185214734Srpaulo	}
186214734Srpaulo
187214734Srpaulo	*next = hdr.payload + hdr.length;
188214734Srpaulo
189214734Srpaulo	return asn1_parse_oid(hdr.payload, hdr.length, oid);
190214734Srpaulo}
191214734Srpaulo
192214734Srpaulo
193281806Srpaulovoid asn1_oid_to_str(const struct asn1_oid *oid, char *buf, size_t len)
194189251Ssam{
195189251Ssam	char *pos = buf;
196189251Ssam	size_t i;
197189251Ssam	int ret;
198189251Ssam
199189251Ssam	if (len == 0)
200189251Ssam		return;
201189251Ssam
202189251Ssam	buf[0] = '\0';
203189251Ssam
204189251Ssam	for (i = 0; i < oid->len; i++) {
205189251Ssam		ret = os_snprintf(pos, buf + len - pos,
206189251Ssam				  "%s%lu",
207189251Ssam				  i == 0 ? "" : ".", oid->oid[i]);
208281806Srpaulo		if (os_snprintf_error(buf + len - pos, ret))
209189251Ssam			break;
210189251Ssam		pos += ret;
211189251Ssam	}
212189251Ssam	buf[len - 1] = '\0';
213189251Ssam}
214189251Ssam
215189251Ssam
216189251Ssamstatic u8 rotate_bits(u8 octet)
217189251Ssam{
218189251Ssam	int i;
219189251Ssam	u8 res;
220189251Ssam
221189251Ssam	res = 0;
222189251Ssam	for (i = 0; i < 8; i++) {
223189251Ssam		res <<= 1;
224189251Ssam		if (octet & 1)
225189251Ssam			res |= 1;
226189251Ssam		octet >>= 1;
227189251Ssam	}
228189251Ssam
229189251Ssam	return res;
230189251Ssam}
231189251Ssam
232189251Ssam
233189251Ssamunsigned long asn1_bit_string_to_long(const u8 *buf, size_t len)
234189251Ssam{
235189251Ssam	unsigned long val = 0;
236189251Ssam	const u8 *pos = buf;
237189251Ssam
238189251Ssam	/* BER requires that unused bits are zero, so we can ignore the number
239189251Ssam	 * of unused bits */
240189251Ssam	pos++;
241189251Ssam
242189251Ssam	if (len >= 2)
243189251Ssam		val |= rotate_bits(*pos++);
244189251Ssam	if (len >= 3)
245189251Ssam		val |= ((unsigned long) rotate_bits(*pos++)) << 8;
246189251Ssam	if (len >= 4)
247189251Ssam		val |= ((unsigned long) rotate_bits(*pos++)) << 16;
248189251Ssam	if (len >= 5)
249189251Ssam		val |= ((unsigned long) rotate_bits(*pos++)) << 24;
250189251Ssam	if (len >= 6)
251189251Ssam		wpa_printf(MSG_DEBUG, "X509: %s - some bits ignored "
252189251Ssam			   "(BIT STRING length %lu)",
253189251Ssam			   __func__, (unsigned long) len);
254189251Ssam
255189251Ssam	return val;
256189251Ssam}
257281806Srpaulo
258281806Srpaulo
259281806Srpauloint asn1_oid_equal(const struct asn1_oid *a, const struct asn1_oid *b)
260281806Srpaulo{
261281806Srpaulo	size_t i;
262281806Srpaulo
263281806Srpaulo	if (a->len != b->len)
264281806Srpaulo		return 0;
265281806Srpaulo
266281806Srpaulo	for (i = 0; i < a->len; i++) {
267281806Srpaulo		if (a->oid[i] != b->oid[i])
268281806Srpaulo			return 0;
269281806Srpaulo	}
270281806Srpaulo
271281806Srpaulo	return 1;
272281806Srpaulo}
273