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