asn1.c revision 189251
1189251Ssam/*
2189251Ssam * ASN.1 DER parsing
3189251Ssam * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
4189251Ssam *
5189251Ssam * This program is free software; you can redistribute it and/or modify
6189251Ssam * it under the terms of the GNU General Public License version 2 as
7189251Ssam * published by the Free Software Foundation.
8189251Ssam *
9189251Ssam * Alternatively, this software may be distributed under the terms of BSD
10189251Ssam * license.
11189251Ssam *
12189251Ssam * See README and COPYING for more details.
13189251Ssam */
14189251Ssam
15189251Ssam#include "includes.h"
16189251Ssam
17189251Ssam#include "common.h"
18189251Ssam
19189251Ssam#ifdef CONFIG_INTERNAL_X509
20189251Ssam
21189251Ssam#include "asn1.h"
22189251Ssam
23189251Ssamint asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr)
24189251Ssam{
25189251Ssam	const u8 *pos, *end;
26189251Ssam	u8 tmp;
27189251Ssam
28189251Ssam	os_memset(hdr, 0, sizeof(*hdr));
29189251Ssam	pos = buf;
30189251Ssam	end = buf + len;
31189251Ssam
32189251Ssam	hdr->identifier = *pos++;
33189251Ssam	hdr->class = hdr->identifier >> 6;
34189251Ssam	hdr->constructed = !!(hdr->identifier & (1 << 5));
35189251Ssam
36189251Ssam	if ((hdr->identifier & 0x1f) == 0x1f) {
37189251Ssam		hdr->tag = 0;
38189251Ssam		do {
39189251Ssam			if (pos >= end) {
40189251Ssam				wpa_printf(MSG_DEBUG, "ASN.1: Identifier "
41189251Ssam					   "underflow");
42189251Ssam				return -1;
43189251Ssam			}
44189251Ssam			tmp = *pos++;
45189251Ssam			wpa_printf(MSG_MSGDUMP, "ASN.1: Extended tag data: "
46189251Ssam				   "0x%02x", tmp);
47189251Ssam			hdr->tag = (hdr->tag << 7) | (tmp & 0x7f);
48189251Ssam		} while (tmp & 0x80);
49189251Ssam	} else
50189251Ssam		hdr->tag = hdr->identifier & 0x1f;
51189251Ssam
52189251Ssam	tmp = *pos++;
53189251Ssam	if (tmp & 0x80) {
54189251Ssam		if (tmp == 0xff) {
55189251Ssam			wpa_printf(MSG_DEBUG, "ASN.1: Reserved length "
56189251Ssam				   "value 0xff used");
57189251Ssam			return -1;
58189251Ssam		}
59189251Ssam		tmp &= 0x7f; /* number of subsequent octets */
60189251Ssam		hdr->length = 0;
61189251Ssam		if (tmp > 4) {
62189251Ssam			wpa_printf(MSG_DEBUG, "ASN.1: Too long length field");
63189251Ssam			return -1;
64189251Ssam		}
65189251Ssam		while (tmp--) {
66189251Ssam			if (pos >= end) {
67189251Ssam				wpa_printf(MSG_DEBUG, "ASN.1: Length "
68189251Ssam					   "underflow");
69189251Ssam				return -1;
70189251Ssam			}
71189251Ssam			hdr->length = (hdr->length << 8) | *pos++;
72189251Ssam		}
73189251Ssam	} else {
74189251Ssam		/* Short form - length 0..127 in one octet */
75189251Ssam		hdr->length = tmp;
76189251Ssam	}
77189251Ssam
78189251Ssam	if (end < pos || hdr->length > (unsigned int) (end - pos)) {
79189251Ssam		wpa_printf(MSG_DEBUG, "ASN.1: Contents underflow");
80189251Ssam		return -1;
81189251Ssam	}
82189251Ssam
83189251Ssam	hdr->payload = pos;
84189251Ssam	return 0;
85189251Ssam}
86189251Ssam
87189251Ssam
88189251Ssamint asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid,
89189251Ssam		 const u8 **next)
90189251Ssam{
91189251Ssam	struct asn1_hdr hdr;
92189251Ssam	const u8 *pos, *end;
93189251Ssam	unsigned long val;
94189251Ssam	u8 tmp;
95189251Ssam
96189251Ssam	os_memset(oid, 0, sizeof(*oid));
97189251Ssam
98189251Ssam	if (asn1_get_next(buf, len, &hdr) < 0 || hdr.length == 0)
99189251Ssam		return -1;
100189251Ssam
101189251Ssam	if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OID) {
102189251Ssam		wpa_printf(MSG_DEBUG, "ASN.1: Expected OID - found class %d "
103189251Ssam			   "tag 0x%x", hdr.class, hdr.tag);
104189251Ssam		return -1;
105189251Ssam	}
106189251Ssam
107189251Ssam	pos = hdr.payload;
108189251Ssam	end = hdr.payload + hdr.length;
109189251Ssam	*next = end;
110189251Ssam
111189251Ssam	while (pos < end) {
112189251Ssam		val = 0;
113189251Ssam
114189251Ssam		do {
115189251Ssam			if (pos >= end)
116189251Ssam				return -1;
117189251Ssam			tmp = *pos++;
118189251Ssam			val = (val << 7) | (tmp & 0x7f);
119189251Ssam		} while (tmp & 0x80);
120189251Ssam
121189251Ssam		if (oid->len >= ASN1_MAX_OID_LEN) {
122189251Ssam			wpa_printf(MSG_DEBUG, "ASN.1: Too long OID value");
123189251Ssam			return -1;
124189251Ssam		}
125189251Ssam		if (oid->len == 0) {
126189251Ssam			/*
127189251Ssam			 * The first octet encodes the first two object
128189251Ssam			 * identifier components in (X*40) + Y formula.
129189251Ssam			 * X = 0..2.
130189251Ssam			 */
131189251Ssam			oid->oid[0] = val / 40;
132189251Ssam			if (oid->oid[0] > 2)
133189251Ssam				oid->oid[0] = 2;
134189251Ssam			oid->oid[1] = val - oid->oid[0] * 40;
135189251Ssam			oid->len = 2;
136189251Ssam		} else
137189251Ssam			oid->oid[oid->len++] = val;
138189251Ssam	}
139189251Ssam
140189251Ssam	return 0;
141189251Ssam}
142189251Ssam
143189251Ssam
144189251Ssamvoid asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len)
145189251Ssam{
146189251Ssam	char *pos = buf;
147189251Ssam	size_t i;
148189251Ssam	int ret;
149189251Ssam
150189251Ssam	if (len == 0)
151189251Ssam		return;
152189251Ssam
153189251Ssam	buf[0] = '\0';
154189251Ssam
155189251Ssam	for (i = 0; i < oid->len; i++) {
156189251Ssam		ret = os_snprintf(pos, buf + len - pos,
157189251Ssam				  "%s%lu",
158189251Ssam				  i == 0 ? "" : ".", oid->oid[i]);
159189251Ssam		if (ret < 0 || ret >= buf + len - pos)
160189251Ssam			break;
161189251Ssam		pos += ret;
162189251Ssam	}
163189251Ssam	buf[len - 1] = '\0';
164189251Ssam}
165189251Ssam
166189251Ssam
167189251Ssamstatic u8 rotate_bits(u8 octet)
168189251Ssam{
169189251Ssam	int i;
170189251Ssam	u8 res;
171189251Ssam
172189251Ssam	res = 0;
173189251Ssam	for (i = 0; i < 8; i++) {
174189251Ssam		res <<= 1;
175189251Ssam		if (octet & 1)
176189251Ssam			res |= 1;
177189251Ssam		octet >>= 1;
178189251Ssam	}
179189251Ssam
180189251Ssam	return res;
181189251Ssam}
182189251Ssam
183189251Ssam
184189251Ssamunsigned long asn1_bit_string_to_long(const u8 *buf, size_t len)
185189251Ssam{
186189251Ssam	unsigned long val = 0;
187189251Ssam	const u8 *pos = buf;
188189251Ssam
189189251Ssam	/* BER requires that unused bits are zero, so we can ignore the number
190189251Ssam	 * of unused bits */
191189251Ssam	pos++;
192189251Ssam
193189251Ssam	if (len >= 2)
194189251Ssam		val |= rotate_bits(*pos++);
195189251Ssam	if (len >= 3)
196189251Ssam		val |= ((unsigned long) rotate_bits(*pos++)) << 8;
197189251Ssam	if (len >= 4)
198189251Ssam		val |= ((unsigned long) rotate_bits(*pos++)) << 16;
199189251Ssam	if (len >= 5)
200189251Ssam		val |= ((unsigned long) rotate_bits(*pos++)) << 24;
201189251Ssam	if (len >= 6)
202189251Ssam		wpa_printf(MSG_DEBUG, "X509: %s - some bits ignored "
203189251Ssam			   "(BIT STRING length %lu)",
204189251Ssam			   __func__, (unsigned long) len);
205189251Ssam
206189251Ssam	return val;
207189251Ssam}
208189251Ssam
209189251Ssam#endif /* CONFIG_INTERNAL_X509 */
210