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