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