1189251Ssam/*
2189251Ssam * ASN.1 DER parsing
3189251Ssam * Copyright (c) 2006, 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
14189251Ssamint asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr)
15189251Ssam{
16189251Ssam	const u8 *pos, *end;
17189251Ssam	u8 tmp;
18189251Ssam
19189251Ssam	os_memset(hdr, 0, sizeof(*hdr));
20189251Ssam	pos = buf;
21189251Ssam	end = buf + len;
22189251Ssam
23189251Ssam	hdr->identifier = *pos++;
24189251Ssam	hdr->class = hdr->identifier >> 6;
25189251Ssam	hdr->constructed = !!(hdr->identifier & (1 << 5));
26189251Ssam
27189251Ssam	if ((hdr->identifier & 0x1f) == 0x1f) {
28189251Ssam		hdr->tag = 0;
29189251Ssam		do {
30189251Ssam			if (pos >= end) {
31189251Ssam				wpa_printf(MSG_DEBUG, "ASN.1: Identifier "
32189251Ssam					   "underflow");
33189251Ssam				return -1;
34189251Ssam			}
35189251Ssam			tmp = *pos++;
36189251Ssam			wpa_printf(MSG_MSGDUMP, "ASN.1: Extended tag data: "
37189251Ssam				   "0x%02x", tmp);
38189251Ssam			hdr->tag = (hdr->tag << 7) | (tmp & 0x7f);
39189251Ssam		} while (tmp & 0x80);
40189251Ssam	} else
41189251Ssam		hdr->tag = hdr->identifier & 0x1f;
42189251Ssam
43189251Ssam	tmp = *pos++;
44189251Ssam	if (tmp & 0x80) {
45189251Ssam		if (tmp == 0xff) {
46189251Ssam			wpa_printf(MSG_DEBUG, "ASN.1: Reserved length "
47189251Ssam				   "value 0xff used");
48189251Ssam			return -1;
49189251Ssam		}
50189251Ssam		tmp &= 0x7f; /* number of subsequent octets */
51189251Ssam		hdr->length = 0;
52189251Ssam		if (tmp > 4) {
53189251Ssam			wpa_printf(MSG_DEBUG, "ASN.1: Too long length field");
54189251Ssam			return -1;
55189251Ssam		}
56189251Ssam		while (tmp--) {
57189251Ssam			if (pos >= end) {
58189251Ssam				wpa_printf(MSG_DEBUG, "ASN.1: Length "
59189251Ssam					   "underflow");
60189251Ssam				return -1;
61189251Ssam			}
62189251Ssam			hdr->length = (hdr->length << 8) | *pos++;
63189251Ssam		}
64189251Ssam	} else {
65189251Ssam		/* Short form - length 0..127 in one octet */
66189251Ssam		hdr->length = tmp;
67189251Ssam	}
68189251Ssam
69189251Ssam	if (end < pos || hdr->length > (unsigned int) (end - pos)) {
70189251Ssam		wpa_printf(MSG_DEBUG, "ASN.1: Contents underflow");
71189251Ssam		return -1;
72189251Ssam	}
73189251Ssam
74189251Ssam	hdr->payload = pos;
75189251Ssam	return 0;
76189251Ssam}
77189251Ssam
78189251Ssam
79214734Srpauloint asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid)
80189251Ssam{
81189251Ssam	const u8 *pos, *end;
82189251Ssam	unsigned long val;
83189251Ssam	u8 tmp;
84189251Ssam
85189251Ssam	os_memset(oid, 0, sizeof(*oid));
86189251Ssam
87214734Srpaulo	pos = buf;
88214734Srpaulo	end = buf + len;
89189251Ssam
90189251Ssam	while (pos < end) {
91189251Ssam		val = 0;
92189251Ssam
93189251Ssam		do {
94189251Ssam			if (pos >= end)
95189251Ssam				return -1;
96189251Ssam			tmp = *pos++;
97189251Ssam			val = (val << 7) | (tmp & 0x7f);
98189251Ssam		} while (tmp & 0x80);
99189251Ssam
100189251Ssam		if (oid->len >= ASN1_MAX_OID_LEN) {
101189251Ssam			wpa_printf(MSG_DEBUG, "ASN.1: Too long OID value");
102189251Ssam			return -1;
103189251Ssam		}
104189251Ssam		if (oid->len == 0) {
105189251Ssam			/*
106189251Ssam			 * The first octet encodes the first two object
107189251Ssam			 * identifier components in (X*40) + Y formula.
108189251Ssam			 * X = 0..2.
109189251Ssam			 */
110189251Ssam			oid->oid[0] = val / 40;
111189251Ssam			if (oid->oid[0] > 2)
112189251Ssam				oid->oid[0] = 2;
113189251Ssam			oid->oid[1] = val - oid->oid[0] * 40;
114189251Ssam			oid->len = 2;
115189251Ssam		} else
116189251Ssam			oid->oid[oid->len++] = val;
117189251Ssam	}
118189251Ssam
119189251Ssam	return 0;
120189251Ssam}
121189251Ssam
122189251Ssam
123214734Srpauloint asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid,
124214734Srpaulo		 const u8 **next)
125214734Srpaulo{
126214734Srpaulo	struct asn1_hdr hdr;
127214734Srpaulo
128214734Srpaulo	if (asn1_get_next(buf, len, &hdr) < 0 || hdr.length == 0)
129214734Srpaulo		return -1;
130214734Srpaulo
131214734Srpaulo	if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OID) {
132214734Srpaulo		wpa_printf(MSG_DEBUG, "ASN.1: Expected OID - found class %d "
133214734Srpaulo			   "tag 0x%x", hdr.class, hdr.tag);
134214734Srpaulo		return -1;
135214734Srpaulo	}
136214734Srpaulo
137214734Srpaulo	*next = hdr.payload + hdr.length;
138214734Srpaulo
139214734Srpaulo	return asn1_parse_oid(hdr.payload, hdr.length, oid);
140214734Srpaulo}
141214734Srpaulo
142214734Srpaulo
143189251Ssamvoid asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len)
144189251Ssam{
145189251Ssam	char *pos = buf;
146189251Ssam	size_t i;
147189251Ssam	int ret;
148189251Ssam
149189251Ssam	if (len == 0)
150189251Ssam		return;
151189251Ssam
152189251Ssam	buf[0] = '\0';
153189251Ssam
154189251Ssam	for (i = 0; i < oid->len; i++) {
155189251Ssam		ret = os_snprintf(pos, buf + len - pos,
156189251Ssam				  "%s%lu",
157189251Ssam				  i == 0 ? "" : ".", oid->oid[i]);
158189251Ssam		if (ret < 0 || ret >= buf + len - pos)
159189251Ssam			break;
160189251Ssam		pos += ret;
161189251Ssam	}
162189251Ssam	buf[len - 1] = '\0';
163189251Ssam}
164189251Ssam
165189251Ssam
166189251Ssamstatic u8 rotate_bits(u8 octet)
167189251Ssam{
168189251Ssam	int i;
169189251Ssam	u8 res;
170189251Ssam
171189251Ssam	res = 0;
172189251Ssam	for (i = 0; i < 8; i++) {
173189251Ssam		res <<= 1;
174189251Ssam		if (octet & 1)
175189251Ssam			res |= 1;
176189251Ssam		octet >>= 1;
177189251Ssam	}
178189251Ssam
179189251Ssam	return res;
180189251Ssam}
181189251Ssam
182189251Ssam
183189251Ssamunsigned long asn1_bit_string_to_long(const u8 *buf, size_t len)
184189251Ssam{
185189251Ssam	unsigned long val = 0;
186189251Ssam	const u8 *pos = buf;
187189251Ssam
188189251Ssam	/* BER requires that unused bits are zero, so we can ignore the number
189189251Ssam	 * of unused bits */
190189251Ssam	pos++;
191189251Ssam
192189251Ssam	if (len >= 2)
193189251Ssam		val |= rotate_bits(*pos++);
194189251Ssam	if (len >= 3)
195189251Ssam		val |= ((unsigned long) rotate_bits(*pos++)) << 8;
196189251Ssam	if (len >= 4)
197189251Ssam		val |= ((unsigned long) rotate_bits(*pos++)) << 16;
198189251Ssam	if (len >= 5)
199189251Ssam		val |= ((unsigned long) rotate_bits(*pos++)) << 24;
200189251Ssam	if (len >= 6)
201189251Ssam		wpa_printf(MSG_DEBUG, "X509: %s - some bits ignored "
202189251Ssam			   "(BIT STRING length %lu)",
203189251Ssam			   __func__, (unsigned long) len);
204189251Ssam
205189251Ssam	return val;
206189251Ssam}
207