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: #include "nsNSSASN1Object.h" michael@0: #include "nsIComponentManager.h" michael@0: #include "secasn1.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsIMutableArray.h" michael@0: #include "nsArrayUtils.h" michael@0: #include "nsXPCOMCID.h" michael@0: michael@0: NS_IMPL_ISUPPORTS(nsNSSASN1Sequence, nsIASN1Sequence, nsIASN1Object) michael@0: NS_IMPL_ISUPPORTS(nsNSSASN1PrintableItem, nsIASN1PrintableItem, nsIASN1Object) michael@0: michael@0: // This function is used to interpret an integer that michael@0: // was encoded in a DER buffer. This function is used michael@0: // when converting a DER buffer into a nsIASN1Object michael@0: // structure. This interprets the buffer in data michael@0: // as defined by the DER (Distinguised Encoding Rules) of michael@0: // ASN1. michael@0: static int michael@0: getInteger256(unsigned char *data, unsigned int nb) michael@0: { michael@0: int val; michael@0: michael@0: switch (nb) { michael@0: case 1: michael@0: val = data[0]; michael@0: break; michael@0: case 2: michael@0: val = (data[0] << 8) | data[1]; michael@0: break; michael@0: case 3: michael@0: val = (data[0] << 16) | (data[1] << 8) | data[2]; michael@0: break; michael@0: case 4: michael@0: val = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; michael@0: break; michael@0: default: michael@0: return -1; michael@0: } michael@0: michael@0: return val; michael@0: } michael@0: michael@0: // This function is used to retrieve the lenght of a DER encoded michael@0: // item. It looks to see if this a multibyte length and then michael@0: // interprets the buffer accordingly to get the actual length value. michael@0: // This funciton is used mostly while parsing the DER headers. michael@0: // michael@0: // A DER encoded item has the following structure: michael@0: // michael@0: // michael@0: static int32_t michael@0: getDERItemLength(unsigned char *data, unsigned char *end, michael@0: unsigned long *bytesUsed, bool *indefinite) michael@0: { michael@0: unsigned char lbyte = *data++; michael@0: int32_t length = -1; michael@0: michael@0: *indefinite = false; michael@0: if (lbyte >= 0x80) { michael@0: // Multibyte length michael@0: unsigned nb = (unsigned) (lbyte & 0x7f); michael@0: if (nb > 4) { michael@0: return -1; michael@0: } michael@0: if (nb > 0) { michael@0: michael@0: if ((data+nb) > end) { michael@0: return -1; michael@0: } michael@0: length = getInteger256(data, nb); michael@0: if (length < 0) michael@0: return -1; michael@0: } else { michael@0: *indefinite = true; michael@0: length = 0; michael@0: } michael@0: *bytesUsed = nb+1; michael@0: } else { michael@0: length = lbyte; michael@0: *bytesUsed = 1; michael@0: } michael@0: return length; michael@0: } michael@0: michael@0: static nsresult michael@0: buildASN1ObjectFromDER(unsigned char *data, michael@0: unsigned char *end, michael@0: nsIASN1Sequence *parent) michael@0: { michael@0: nsresult rv; michael@0: nsCOMPtr sequence; michael@0: nsCOMPtr printableItem; michael@0: nsCOMPtr asn1Obj; michael@0: nsCOMPtr parentObjects; michael@0: michael@0: NS_ENSURE_ARG_POINTER(parent); michael@0: if (data >= end) michael@0: return NS_OK; michael@0: michael@0: unsigned char code, tagnum; michael@0: michael@0: // A DER item has the form of |tag|len|data michael@0: // tag is one byte and describes the type of element michael@0: // we are dealing with. michael@0: // len is a DER encoded int telling us how long the data is michael@0: // data is a buffer that is len bytes long and has to be michael@0: // interpreted according to its type. michael@0: unsigned long bytesUsed; michael@0: bool indefinite; michael@0: int32_t len; michael@0: uint32_t type; michael@0: michael@0: rv = parent->GetASN1Objects(getter_AddRefs(parentObjects)); michael@0: if (NS_FAILED(rv) || !parentObjects) michael@0: return NS_ERROR_FAILURE; michael@0: while (data < end) { michael@0: code = *data; michael@0: tagnum = code & SEC_ASN1_TAGNUM_MASK; michael@0: michael@0: /* michael@0: * NOTE: This code does not (yet) handle the high-tag-number form! michael@0: */ michael@0: if (tagnum == SEC_ASN1_HIGH_TAG_NUMBER) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: data++; michael@0: len = getDERItemLength(data, end, &bytesUsed, &indefinite); michael@0: data += bytesUsed; michael@0: if ((len < 0) || ((data+len) > end)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: if (code & SEC_ASN1_CONSTRUCTED) { michael@0: if (len > 0 || indefinite) { michael@0: sequence = new nsNSSASN1Sequence(); michael@0: switch (code & SEC_ASN1_CLASS_MASK) { michael@0: case SEC_ASN1_UNIVERSAL: michael@0: type = tagnum; michael@0: break; michael@0: case SEC_ASN1_APPLICATION: michael@0: type = nsIASN1Object::ASN1_APPLICATION; michael@0: break; michael@0: case SEC_ASN1_CONTEXT_SPECIFIC: michael@0: type = nsIASN1Object::ASN1_CONTEXT_SPECIFIC; michael@0: break; michael@0: case SEC_ASN1_PRIVATE: michael@0: type = nsIASN1Object::ASN1_PRIVATE; michael@0: break; michael@0: default: michael@0: NS_ERROR("Bad DER"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: sequence->SetTag(tagnum); michael@0: sequence->SetType(type); michael@0: rv = buildASN1ObjectFromDER(data, (len == 0) ? end : data + len, michael@0: sequence); michael@0: asn1Obj = sequence; michael@0: } michael@0: } else { michael@0: printableItem = new nsNSSASN1PrintableItem(); michael@0: michael@0: asn1Obj = printableItem; michael@0: asn1Obj->SetType(tagnum); michael@0: asn1Obj->SetTag(tagnum); michael@0: printableItem->SetData((char*)data, len); michael@0: } michael@0: data += len; michael@0: parentObjects->AppendElement(asn1Obj, false); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: CreateFromDER(unsigned char *data, michael@0: unsigned int len, michael@0: nsIASN1Object **retval) michael@0: { michael@0: nsCOMPtr sequence = new nsNSSASN1Sequence; michael@0: *retval = nullptr; michael@0: michael@0: nsresult rv = buildASN1ObjectFromDER(data, data+len, sequence); michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // The actual object will be the first element inserted michael@0: // into the sequence of the sequence variable we created. michael@0: nsCOMPtr elements; michael@0: michael@0: sequence->GetASN1Objects(getter_AddRefs(elements)); michael@0: nsCOMPtr asn1Obj = do_QueryElementAt(elements, 0); michael@0: *retval = asn1Obj; michael@0: if (!*retval) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: NS_ADDREF(*retval); michael@0: michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: nsNSSASN1Sequence::nsNSSASN1Sequence() : mType(0), michael@0: mTag(0), michael@0: mIsValidContainer(true), michael@0: mIsExpanded(true) michael@0: { michael@0: /* member initializers and constructor code */ michael@0: } michael@0: michael@0: nsNSSASN1Sequence::~nsNSSASN1Sequence() michael@0: { michael@0: /* destructor code */ michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSASN1Sequence::GetASN1Objects(nsIMutableArray * *aASN1Objects) michael@0: { michael@0: if (!mASN1Objects) { michael@0: mASN1Objects = do_CreateInstance(NS_ARRAY_CONTRACTID); michael@0: } michael@0: *aASN1Objects = mASN1Objects; michael@0: NS_IF_ADDREF(*aASN1Objects); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSASN1Sequence::SetASN1Objects(nsIMutableArray * aASN1Objects) michael@0: { michael@0: mASN1Objects = aASN1Objects; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSASN1Sequence::GetTag(uint32_t *aTag) michael@0: { michael@0: *aTag = mTag; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSASN1Sequence::SetTag(uint32_t aTag) michael@0: { michael@0: mTag = aTag; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSASN1Sequence::GetType(uint32_t *aType) michael@0: { michael@0: *aType = mType; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSASN1Sequence::SetType(uint32_t aType) michael@0: { michael@0: mType = aType; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSASN1Sequence::GetDisplayName(nsAString &aDisplayName) michael@0: { michael@0: aDisplayName = mDisplayName; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSASN1Sequence::SetDisplayName(const nsAString &aDisplayName) michael@0: { michael@0: mDisplayName = aDisplayName; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSASN1Sequence::GetDisplayValue(nsAString &aDisplayValue) michael@0: { michael@0: aDisplayValue = mDisplayValue; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSASN1Sequence::SetDisplayValue(const nsAString &aDisplayValue) michael@0: { michael@0: mDisplayValue = aDisplayValue; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSASN1Sequence::GetIsValidContainer(bool *aIsValidContainer) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aIsValidContainer); michael@0: *aIsValidContainer = mIsValidContainer; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSASN1Sequence::SetIsValidContainer(bool aIsValidContainer) michael@0: { michael@0: mIsValidContainer = aIsValidContainer; michael@0: SetIsExpanded(mIsValidContainer); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSASN1Sequence::GetIsExpanded(bool *aIsExpanded) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aIsExpanded); michael@0: *aIsExpanded = mIsExpanded; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSASN1Sequence::SetIsExpanded(bool aIsExpanded) michael@0: { michael@0: mIsExpanded = aIsExpanded; michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsNSSASN1PrintableItem::nsNSSASN1PrintableItem() : mType(0), michael@0: mTag(0), michael@0: mData(nullptr), michael@0: mLen(0) michael@0: { michael@0: /* member initializers and constructor code */ michael@0: } michael@0: michael@0: nsNSSASN1PrintableItem::~nsNSSASN1PrintableItem() michael@0: { michael@0: /* destructor code */ michael@0: if (mData) michael@0: nsMemory::Free(mData); michael@0: } michael@0: michael@0: /* readonly attribute wstring value; */ michael@0: NS_IMETHODIMP michael@0: nsNSSASN1PrintableItem::GetDisplayValue(nsAString &aValue) michael@0: { michael@0: aValue = mValue; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSASN1PrintableItem::SetDisplayValue(const nsAString &aValue) michael@0: { michael@0: mValue = aValue; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSASN1PrintableItem::GetTag(uint32_t *aTag) michael@0: { michael@0: *aTag = mTag; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSASN1PrintableItem::SetTag(uint32_t aTag) michael@0: { michael@0: mTag = aTag; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSASN1PrintableItem::GetType(uint32_t *aType) michael@0: { michael@0: *aType = mType; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSASN1PrintableItem::SetType(uint32_t aType) michael@0: { michael@0: mType = aType; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSASN1PrintableItem::SetData(char *data, uint32_t len) michael@0: { michael@0: if (len > 0) { michael@0: if (mLen < len) { michael@0: unsigned char* newData = (unsigned char*)nsMemory::Realloc(mData, len); michael@0: if (!newData) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: mData = newData; michael@0: } michael@0: michael@0: memcpy(mData, data, len); michael@0: } else if (len == 0) { michael@0: if (mData) { michael@0: nsMemory::Free(mData); michael@0: mData = nullptr; michael@0: } michael@0: } michael@0: mLen = len; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSASN1PrintableItem::GetData(char **outData, uint32_t *outLen) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(outData); michael@0: NS_ENSURE_ARG_POINTER(outLen); michael@0: michael@0: *outData = (char*)mData; michael@0: *outLen = mLen; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* attribute wstring displayName; */ michael@0: NS_IMETHODIMP michael@0: nsNSSASN1PrintableItem::GetDisplayName(nsAString &aDisplayName) michael@0: { michael@0: aDisplayName = mDisplayName; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSASN1PrintableItem::SetDisplayName(const nsAString &aDisplayName) michael@0: { michael@0: mDisplayName = aDisplayName; michael@0: return NS_OK; michael@0: } michael@0: