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