michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "secder.h" michael@0: #include "secerr.h" michael@0: michael@0: static PRUint32 michael@0: der_indefinite_length(unsigned char *buf, unsigned char *end) michael@0: { michael@0: PRUint32 len, ret, dataLen; michael@0: unsigned char tag, lenCode; michael@0: int dataLenLen; michael@0: michael@0: len = 0; michael@0: while ( 1 ) { michael@0: if ((buf + 2) > end) { michael@0: return(0); michael@0: } michael@0: michael@0: tag = *buf++; michael@0: lenCode = *buf++; michael@0: len += 2; michael@0: michael@0: if ( ( tag == 0 ) && ( lenCode == 0 ) ) { michael@0: return(len); michael@0: } michael@0: michael@0: if ( lenCode == 0x80 ) { /* indefinite length */ michael@0: ret = der_indefinite_length(buf, end); /* recurse to find length */ michael@0: if (ret == 0) michael@0: return 0; michael@0: len += ret; michael@0: buf += ret; michael@0: } else { /* definite length */ michael@0: if (lenCode & 0x80) { michael@0: /* Length of data is in multibyte format */ michael@0: dataLenLen = lenCode & 0x7f; michael@0: switch (dataLenLen) { michael@0: case 1: michael@0: dataLen = buf[0]; michael@0: break; michael@0: case 2: michael@0: dataLen = (buf[0]<<8)|buf[1]; michael@0: break; michael@0: case 3: michael@0: dataLen = ((unsigned long)buf[0]<<16)|(buf[1]<<8)|buf[2]; michael@0: break; michael@0: case 4: michael@0: dataLen = ((unsigned long)buf[0]<<24)| michael@0: ((unsigned long)buf[1]<<16)|(buf[2]<<8)|buf[3]; michael@0: break; michael@0: default: michael@0: PORT_SetError(SEC_ERROR_BAD_DER); michael@0: return SECFailure; michael@0: } michael@0: } else { michael@0: /* Length of data is in single byte */ michael@0: dataLen = lenCode; michael@0: dataLenLen = 0; michael@0: } michael@0: michael@0: /* skip this item */ michael@0: buf = buf + dataLenLen + dataLen; michael@0: len = len + dataLenLen + dataLen; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* michael@0: ** Capture the next thing in the buffer. michael@0: ** Returns the length of the header and the length of the contents. michael@0: */ michael@0: static SECStatus michael@0: der_capture(unsigned char *buf, unsigned char *end, michael@0: int *header_len_p, PRUint32 *contents_len_p) michael@0: { michael@0: unsigned char *bp; michael@0: unsigned char whole_tag; michael@0: PRUint32 contents_len; michael@0: int tag_number; michael@0: michael@0: if ((buf + 2) > end) { michael@0: *header_len_p = 0; michael@0: *contents_len_p = 0; michael@0: if (buf == end) michael@0: return SECSuccess; michael@0: return SECFailure; michael@0: } michael@0: michael@0: bp = buf; michael@0: michael@0: /* Get tag and verify that it is ok. */ michael@0: whole_tag = *bp++; michael@0: tag_number = whole_tag & DER_TAGNUM_MASK; michael@0: michael@0: /* michael@0: * XXX This code does not (yet) handle the high-tag-number form! michael@0: */ michael@0: if (tag_number == DER_HIGH_TAG_NUMBER) { michael@0: PORT_SetError(SEC_ERROR_BAD_DER); michael@0: return SECFailure; michael@0: } michael@0: michael@0: if ((whole_tag & DER_CLASS_MASK) == DER_UNIVERSAL) { michael@0: /* Check that the universal tag number is one we implement. */ michael@0: switch (tag_number) { michael@0: case DER_BOOLEAN: michael@0: case DER_INTEGER: michael@0: case DER_BIT_STRING: michael@0: case DER_OCTET_STRING: michael@0: case DER_NULL: michael@0: case DER_OBJECT_ID: michael@0: case DER_SEQUENCE: michael@0: case DER_SET: michael@0: case DER_PRINTABLE_STRING: michael@0: case DER_T61_STRING: michael@0: case DER_IA5_STRING: michael@0: case DER_VISIBLE_STRING: michael@0: case DER_UTC_TIME: michael@0: case 0: /* end-of-contents tag */ michael@0: break; michael@0: default: michael@0: PORT_SetError(SEC_ERROR_BAD_DER); michael@0: return SECFailure; michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * Get first byte of length code (might contain entire length, might not). michael@0: */ michael@0: contents_len = *bp++; michael@0: michael@0: /* michael@0: * If the high bit is set, then the length is in multibyte format, michael@0: * or the thing has an indefinite-length. michael@0: */ michael@0: if (contents_len & 0x80) { michael@0: int bytes_of_encoded_len; michael@0: michael@0: bytes_of_encoded_len = contents_len & 0x7f; michael@0: contents_len = 0; michael@0: michael@0: switch (bytes_of_encoded_len) { michael@0: case 4: michael@0: contents_len |= *bp++; michael@0: contents_len <<= 8; michael@0: /* fallthru */ michael@0: case 3: michael@0: contents_len |= *bp++; michael@0: contents_len <<= 8; michael@0: /* fallthru */ michael@0: case 2: michael@0: contents_len |= *bp++; michael@0: contents_len <<= 8; michael@0: /* fallthru */ michael@0: case 1: michael@0: contents_len |= *bp++; michael@0: break; michael@0: michael@0: case 0: michael@0: contents_len = der_indefinite_length (bp, end); michael@0: if (contents_len) michael@0: break; michael@0: /* fallthru */ michael@0: default: michael@0: PORT_SetError(SEC_ERROR_BAD_DER); michael@0: return SECFailure; michael@0: } michael@0: } michael@0: michael@0: if ((bp + contents_len) > end) { michael@0: /* Ran past end of buffer */ michael@0: PORT_SetError(SEC_ERROR_BAD_DER); michael@0: return SECFailure; michael@0: } michael@0: michael@0: *header_len_p = bp - buf; michael@0: *contents_len_p = contents_len; michael@0: michael@0: return SECSuccess; michael@0: } michael@0: michael@0: SECStatus michael@0: DER_Lengths(SECItem *item, int *header_len_p, PRUint32 *contents_len_p) michael@0: { michael@0: return(der_capture(item->data, &item->data[item->len], header_len_p, michael@0: contents_len_p)); michael@0: }