--- /dev/null
+/*
+ * Copyright (c) 2003-2004,2011-2012,2014 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+/*
+ * pkcs12SafeBag.cpp : internal representation of various kinds
+ * of P12 SafeBags
+ */
+
+#include "pkcs12SafeBag.h"
+#include "pkcs12Debug.h"
+#include "pkcs12Utils.h"
+#include <string.h>
+#include <assert.h>
+#include <Security/Security.h>
+#include <Security/SecKeyPriv.h>
+#include <Security/SecAsn1Templates.h>
+#include <security_asn1/nssUtils.h>
+
+/*
+ * P12SafeBag, abstract superclass of all safe bags.
+ *
+ * Constructor for decoding. Attr may include friendlyName and localKeyId;
+ * It may also be empty or NULL.
+ */
+P12SafeBag::P12SafeBag(
+ NSS_Attribute **attrs, // including friendlyName, etc.
+ SecNssCoder &coder)
+ : mBagAttrs(coder),
+ mCoder(coder)
+{
+ mFriendlyName.Data = mLocalKeyId.Data = NULL;
+ mFriendlyName.Length = mLocalKeyId.Length = 0;
+
+ /* parse attrs into friendlyName, localKeyId, and generic attrs */
+ unsigned numAttrs = nssArraySize((const void **)attrs);
+ for(unsigned dex=0; dex<numAttrs; dex++) {
+ NSS_Attribute *attr = attrs[dex];
+ unsigned numValues = nssArraySize((const void**)attr->attrValue);
+
+ if(nssCompareCssmData(&attr->attrType,
+ &CSSMOID_PKCS9_FriendlyName)) {
+ /*
+ * BMP string (UniCode). Spec says only one legal value.
+ */
+ if(numValues != 1) {
+ p12ErrorLog("FriendlyName with %u values\n", numValues);
+ /* but let's keep going if we can */
+ if(numValues == 0) {
+ P12_THROW_DECODE;
+ }
+ }
+ if(mCoder.decodeItem(*attr->attrValue[0],
+ kSecAsn1BMPStringTemplate, &mFriendlyName)) {
+ p12ErrorLog("***Error decoding FriendlyName string\n");
+ P12_THROW_DECODE;
+ }
+ }
+ else if(nssCompareCssmData(&attr->attrType,
+ &CSSMOID_PKCS9_LocalKeyId)) {
+ /*
+ * Octet string. Spec says only one legal value.
+ */
+ if(numValues != 1) {
+ p12ErrorLog("LocalKeyId with %u values\n", numValues);
+ /* but let's keep going if we can */
+ if(numValues == 0) {
+ P12_THROW_DECODE;
+ }
+ }
+ if(mCoder.decodeItem(*attr->attrValue[0],
+ kSecAsn1OctetStringTemplate, &mLocalKeyId)) {
+ p12ErrorLog("***Error decoding LocalKeyId\n");
+ P12_THROW_DECODE;
+ }
+ }
+ else {
+ /* others */
+ mBagAttrs.addAttr(*attr);
+ }
+ }
+}
+
+/*
+ * Constructor for encoding. All arguments except for the coder
+ * are optional.
+ */
+P12SafeBag::P12SafeBag(
+ CFStringRef fname,
+ CFDataRef keyId,
+ P12BagAttrs *otherAttrs, // optional
+ SecNssCoder &coder)
+ : mBagAttrs(otherAttrs, coder),
+ mCoder(coder)
+{
+ friendlyName(fname);
+ localKeyId(keyId);
+}
+
+P12SafeBag::~P12SafeBag()
+{
+ /* nothing if everything we allocd is via mCoder */
+}
+
+/*
+ * Setters in CF terms, used when constructing prior
+ * to encoding.
+ */
+void P12SafeBag::friendlyName(
+ CFStringRef fname)
+{
+ CFIndex len = 0;
+
+ if(fname != NULL) {
+ len = CFStringGetLength(fname);
+ }
+ if(len == 0) {
+ mFriendlyName.Data = NULL;
+ mFriendlyName.Length = 0;
+ return;
+ }
+
+ /* convert unicode to byte array */
+ unsigned flen = (unsigned)(len * sizeof(UniChar));
+ mCoder.allocItem(mFriendlyName, flen);
+ unsigned char *cp = mFriendlyName.Data;
+ for(CFIndex dex=0; dex<len; dex++) {
+ UniChar uc = CFStringGetCharacterAtIndex(fname, dex);
+ *cp++ = uc >> 8;
+ *cp++ = uc & 0xff;
+ }
+}
+
+void P12SafeBag::localKeyId(
+ CFDataRef keyId)
+{
+ CFIndex len = 0;
+
+ if(keyId != NULL) {
+ len = CFDataGetLength(keyId);
+ }
+ if(len == 0) {
+ mLocalKeyId.Data = NULL;
+ mLocalKeyId.Length = 0;
+ return;
+ }
+ mCoder.allocItem(mLocalKeyId, len);
+ const UInt8 *cp = CFDataGetBytePtr(keyId);
+ memmove(mLocalKeyId.Data, cp, len);
+}
+
+/*
+ * Copy out all attrs in API form. All incoming ptrs
+ * are optional.
+ */
+void P12SafeBag::copyAllAttrs(
+ CFStringRef *fName,
+ CFDataRef *keyId,
+ P12BagAttrs **bagAttrs)
+{
+ if(fName) {
+ *fName = friendlyName();
+ }
+ if(keyId) {
+ *keyId = localKeyId();
+ }
+ if(bagAttrs) {
+ if(mBagAttrs.numAttrs()) {
+ /* make a copy of our bag attrs */
+ P12BagAttrs *attrs = new P12BagAttrs(&mBagAttrs, mCoder);
+ *bagAttrs = attrs;
+ }
+ else {
+ *bagAttrs = NULL;
+ }
+ }
+}
+
+
+/* getters in CF terms - result is created and returned */
+CFStringRef P12SafeBag::friendlyName()
+{
+ if(mFriendlyName.Data == NULL) {
+ /* not present, no error */
+ return NULL;
+ }
+ /* shouldn't have stored this if it's an odd number of bytes */
+ assert((mFriendlyName.Length & 1) == 0);
+
+ /* convert byte array to unicode */
+ unsigned long strLen = mFriendlyName.Length / 2;
+ UniChar *uc = (UniChar *)malloc(strLen * sizeof(UniChar));
+ const uint8 *inp = mFriendlyName.Data;
+ UniChar *outp = uc;
+ while(inp < (mFriendlyName.Data + mFriendlyName.Length)) {
+ *outp = (((unsigned)inp[0]) << 8) | inp[1];
+ outp++;
+ inp += 2;
+ }
+ CFStringRef cstr = CFStringCreateWithCharacters(NULL, uc, strLen);
+ free(uc);
+ return cstr;
+}
+
+CFDataRef P12SafeBag::localKeyId()
+{
+ if(mLocalKeyId.Data == NULL) {
+ /* not present, no error */
+ return NULL;
+ }
+ return CFDataCreate(NULL, (const UInt8 *)mLocalKeyId.Data,
+ mLocalKeyId.Length);
+}
+
+/*
+ * Get all attrs, including friendlyName and localKeyId,
+ * in preparation for encoding.
+ */
+NSS_Attribute **P12SafeBag::getAllAttrs()
+{
+ unsigned numAttrs = mBagAttrs.numAttrs();
+ if(mFriendlyName.Data) {
+ numAttrs++;
+ }
+ if(mLocalKeyId.Data) {
+ numAttrs++;
+ }
+ NSS_Attribute **attrs =
+ (NSS_Attribute **)p12NssNullArray(numAttrs, mCoder);
+ unsigned outDex=0;
+ for(unsigned i=0; i<mBagAttrs.numAttrs(); i++) {
+ attrs[outDex++] = mBagAttrs.getAttr(i);
+ }
+ if(mFriendlyName.Data) {
+ CSSM_DATA berName = {0, NULL};
+ if(mCoder.encodeItem(&mFriendlyName, kSecAsn1BMPStringTemplate,
+ berName)) {
+ p12ErrorLog("***Error encoding FriendlyName string\n");
+ P12_THROW_ENCODE;
+ }
+ attrs[outDex++] = makeAttr(CSSMOID_PKCS9_FriendlyName,
+ berName);
+ }
+ if(mLocalKeyId.Data) {
+ CSSM_DATA berName = {0, NULL};
+ if(mCoder.encodeItem(&mLocalKeyId, kSecAsn1OctetStringTemplate,
+ berName)) {
+ p12ErrorLog("***Error encoding LocalKeyId string\n");
+ P12_THROW_ENCODE;
+ }
+ attrs[outDex++] = makeAttr(CSSMOID_PKCS9_LocalKeyId,
+ berName);
+ }
+ assert(outDex == numAttrs);
+ return attrs;
+}
+
+/*
+ * Create an NSS_Attribute * for friendlyName or keyId
+ */
+NSS_Attribute *P12SafeBag::makeAttr(
+ const CSSM_OID &attrId,
+ const CSSM_DATA &attrValue)
+{
+ NSS_Attribute *attr = mCoder.mallocn<NSS_Attribute>();
+ mCoder.allocCopyItem(attrId, attr->attrType);
+ attr->attrValue = mCoder.mallocn<CSSM_DATA *>(2);
+ attr->attrValue[0] = mCoder.mallocn<CSSM_DATA>();
+ attr->attrValue[1] = NULL;
+ mCoder.allocCopyItem(attrValue, *attr->attrValue[0]);
+ return attr;
+}
+
+/*
+ * Individual bag types
+ */
+
+/* decode */
+P12CertBag::P12CertBag(
+ NSS_P12_CertBagType certType, // CT_X509, CT_SDSI
+ CSSM_DATA &certData,
+ NSS_Attribute **attrs, // optional
+ SecNssCoder &coder)
+ : P12SafeBag(attrs, coder),
+ mCertType(certType),
+ mCertRef(NULL)
+{
+ coder.allocCopyItem(certData, mCertData);
+}
+
+/* encode */
+P12CertBag::P12CertBag(
+ NSS_P12_CertBagType certType, // CT_X509, CT_SDSI
+ CSSM_DATA &certData,
+ CFStringRef fname,
+ CFDataRef keyId,
+ P12BagAttrs *otherAttrs,
+ SecNssCoder &coder)
+ : P12SafeBag(fname, keyId, otherAttrs, coder),
+ mCertType(certType),
+ mCertRef(NULL)
+{
+ coder.allocCopyItem(certData, mCertData);
+}
+
+P12CertBag::~P12CertBag()
+{
+ if(mCertRef) {
+ CFRelease(mCertRef);
+ }
+ /* everything else we allocd is via mCoder */
+}
+
+/* convert to P12CertBag to SecCertificateRef */
+SecCertificateRef P12CertBag::getSecCert()
+{
+ if(mCertRef) {
+ CFRetain(mCertRef); /* a ref count for the caller */
+ return mCertRef;
+ }
+
+ /* lazy creation... */
+ CSSM_CERT_TYPE certType;
+ CSSM_CERT_ENCODING certEncoding;
+ switch(mCertType) {
+ case CT_X509:
+ certType = CSSM_CERT_X_509v3;
+ certEncoding = CSSM_CERT_ENCODING_DER;
+ break;
+ case CT_SDSI:
+ certType = CSSM_CERT_SDSIv1;
+ /* it's base64 encoded - no value for that in this enum */
+ certEncoding = CSSM_CERT_ENCODING_UNKNOWN;
+ break;
+ default:
+ /* shouldn't currently happen, but... */
+ certType = CSSM_CERT_UNKNOWN;
+ certEncoding = CSSM_CERT_ENCODING_UNKNOWN;
+ break;
+ }
+ OSStatus ortn = SecCertificateCreateFromData(
+ &mCertData,
+ certType,
+ certEncoding,
+ &mCertRef);
+ if(ortn) {
+ MacOSError::throwMe(ortn);
+ }
+
+ /* One ref count for us, one for the caller */
+ CFRetain(mCertRef);
+ return mCertRef;
+}
+
+P12CrlBag::P12CrlBag(
+ NSS_P12_CrlBagType crlType, // CRT_X509, only for now
+ CSSM_DATA &crlData,
+ NSS_Attribute **attrs, // optional
+ SecNssCoder &coder)
+ : P12SafeBag(attrs, coder),
+ mCrlType(crlType)
+{
+ coder.allocCopyItem(crlData, mCrlData);
+}
+
+P12CrlBag::P12CrlBag(
+ NSS_P12_CrlBagType crlType, // CRT_X509, only for now
+ CFDataRef crlData,
+ CFStringRef fname,
+ CFDataRef keyId,
+ P12BagAttrs *otherAttrs,
+ SecNssCoder &coder)
+ : P12SafeBag(fname, keyId, otherAttrs, coder),
+ mCrlType(crlType)
+{
+ coder.allocCopyItem(CFDataGetBytePtr(crlData),
+ CFDataGetLength(crlData), mCrlData);
+}
+
+P12CrlBag::~P12CrlBag()
+{
+ /* nothing if everything we allocd is via mCoder */
+}
+
+/*
+ * For decode - both shrouded and plain.
+ * On decode, we own the key and will do the CSSM_FreeKey in
+ * our destructor. Caller owns the actual CSSM_KEY memory.
+ */
+P12KeyBag::P12KeyBag(
+ CSSM_KEY_PTR key,
+ CSSM_CSP_HANDLE cspHand,
+ NSS_Attribute **attrs, // optional
+ CSSM_DATA &labelData,
+ SecNssCoder &coder)
+ : P12SafeBag(attrs, coder),
+ mKey(key),
+ mCspHand(cspHand),
+ mKeyRef(NULL),
+ mWeOwnKey(true),
+ mPrivKeyCreds(NULL),
+ mDupKey(false)
+{
+ setLabel(labelData);
+}
+
+/* for encode - app owns CSSM_KEY */
+P12KeyBag::P12KeyBag(
+ const CSSM_KEY *key,
+ CSSM_CSP_HANDLE cspHand,
+ CFStringRef fname,
+ CFDataRef keyId,
+ P12BagAttrs *otherAttrs,
+ SecNssCoder &coder,
+ SecKeyRef keyRef /* = NULL */)
+
+ : P12SafeBag(fname, keyId, otherAttrs, coder),
+ mKey((CSSM_KEY_PTR)key),
+ mCspHand(cspHand),
+ mKeyRef(keyRef),
+ mWeOwnKey(false), // app giveth, app taketh away
+ mPrivKeyCreds(NULL),
+ mDupKey(false)
+{
+ if(mKeyRef) {
+ CFRetain(mKeyRef);
+ /*
+ * Get creds associated with this key
+ */
+ OSStatus ortn = SecKeyGetCredentials(mKeyRef,
+ CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED,
+ kSecCredentialTypeDefault,
+ &mPrivKeyCreds);
+ if(ortn) {
+ p12LogCssmError("SecKeyGetCredentials", ortn);
+ MacOSError::throwMe(ortn);
+ }
+ }
+ mLabel.Data = NULL;
+ mLabel.Length = 0;
+}
+
+
+P12KeyBag::~P12KeyBag()
+{
+ freeKey();
+}
+
+void P12KeyBag::setLabel(
+ const CSSM_DATA &newLabel)
+{
+ mCoder.allocCopyItem(newLabel, mLabel);
+}
+
+/* reusable key setter */
+void P12KeyBag::setKey(
+ CSSM_KEY_PTR cssmKey)
+{
+ freeKey();
+ mKey = cssmKey;
+}
+
+void P12KeyBag::freeKey()
+{
+ if(mWeOwnKey) {
+ assert(mKey != NULL);
+ assert(mCspHand != 0);
+ CSSM_FreeKey(mCspHand, NULL, mKey, CSSM_FALSE);
+ }
+ mKey = NULL;
+ if(mKeyRef) {
+ CFRelease(mKeyRef);
+ mKeyRef = NULL;
+ }
+}
+
+/*
+ * Others we don't implement
+ */
+P12OpaqueBag::P12OpaqueBag(
+ const CSSM_OID &oid,
+ const CSSM_DATA &blob,
+ NSS_Attribute **attrs, // optional
+ SecNssCoder &coder)
+ : P12SafeBag(attrs, coder)
+{
+ coder.allocCopyItem(oid, mOid);
+ coder.allocCopyItem(blob, mBlob);
+}
+
+P12OpaqueBag::P12OpaqueBag(
+ CFDataRef oid,
+ CFDataRef blob,
+ CFStringRef fname,
+ CFDataRef keyId,
+ P12BagAttrs *otherAttrs,
+ SecNssCoder &coder)
+ : P12SafeBag(fname, keyId, otherAttrs, coder)
+{
+ coder.allocCopyItem(CFDataGetBytePtr(oid),
+ CFDataGetLength(oid), mOid);
+ coder.allocCopyItem(CFDataGetBytePtr(blob),
+ CFDataGetLength(blob), mBlob);
+}
+
+P12OpaqueBag::~P12OpaqueBag()
+{
+ /* nothing if everything we allocd is via mCoder */
+}
+