--- /dev/null
+/*
+ * Copyright (c) 2003,2011-2012,2014 Apple Inc. All Rights Reserved.
+ *
+ * The contents of this file constitute Original Code as defined in and are
+ * subject to the Apple Public Source License Version 1.2 (the 'License').
+ * You may not use this file except in compliance with the License. Please obtain
+ * a copy of the License at http://www.apple.com/publicsource and read it before
+ * using this file.
+ *
+ * This 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.
+ */
+
+
+/*
+ * opensslAsn1.h - ANS1 encode/decode of openssl object, libssnasn1 version
+ */
+#include "opensslAsn1.h"
+#include "BinaryKey.h"
+#include "AppleCSPUtils.h"
+#include "opensshCoding.h"
+#include <Security/osKeyTemplates.h>
+#include <openssl/err.h>
+#include <openssl/bn.h>
+#include <openssl/crypto.h>
+
+#include <security_asn1/SecNssCoder.h>
+#include <security_asn1/secerr.h>
+#include <Security/keyTemplates.h>
+#include <security_utilities/debugging.h>
+#include <Security/oidsalg.h>
+#include <Security/SecAsn1Templates.h>
+
+#include <assert.h>
+
+#define sslAsn1Debug(args...) secdebug("sslAsn1", ##args)
+
+#ifndef NDEBUG
+/* set to 1 to see all ASN related errors */
+#define LOG_ASN_ERRORS 0
+#else
+#define LOG_ASN_ERRORS 0
+#endif
+
+#if LOG_ASN_ERRORS
+#include <stdio.h>
+#include <security_asn1/secerr.h>
+
+static void logAsnErr(
+ const char *op,
+ PRErrorCode perr)
+{
+ printf("Error on %s: %s\n", op, SECErrorString(perr));
+}
+#else
+#define logAsnErr(op, perr)
+#endif /* LOG_ASN_ERRORS */
+
+/* CSSM_DATA --> BIGNUM */
+BIGNUM *cssmDataToBn(
+ const CSSM_DATA &cdata)
+{
+ BIGNUM *bn = BN_new();
+ BIGNUM *rtn;
+
+ rtn = BN_bin2bn(cdata.Data, (int)cdata.Length, bn);
+ if(rtn == NULL) {
+ BN_free(bn);
+ CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
+ }
+ return bn;
+}
+
+/* BIGNUM --> CSSM_DATA, mallocing from a SecNssCoder's PL_ArenaPool */
+void bnToCssmData(
+ const BIGNUM *bn,
+ CSSM_DATA &cdata,
+ SecNssCoder &coder)
+{
+ assert(bn != NULL);
+ unsigned numBytes = BN_num_bytes(bn);
+ cdata.Data = (uint8 *)coder.malloc(numBytes);
+ if(cdata.Data == NULL) {
+ CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
+ }
+ cdata.Length = numBytes;
+ BN_bn2bin(bn, cdata.Data);
+}
+
+/*
+ * CSSM_DATA --> unsigned int
+ */
+unsigned cssmDataToInt(
+ const CSSM_DATA &cdata)
+{
+ if((cdata.Length == 0) || (cdata.Data == NULL)) {
+ return 0;
+ }
+ unsigned len = (unsigned)cdata.Length;
+ if(len > sizeof(int)) {
+ logAsnErr("cssmDataToInt: Length error (%u)", len);
+ CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_ALG_PARAMS);
+ }
+
+ unsigned rtn = 0;
+ uint8 *cp = cdata.Data;
+ for(unsigned i=0; i<len; i++) {
+ rtn = (rtn << 8) | *cp++;
+ }
+ return rtn;
+}
+
+/*
+ * unsigned int --> CSSM_DATA, mallocing from an SecNssCoder
+ */
+void intToCssmData(
+ unsigned num,
+ CSSM_DATA &cdata,
+ SecNssCoder &coder)
+{
+ unsigned len = 0;
+
+ if(num < 0x100) {
+ len = 1;
+ }
+ else if(num < 0x10000) {
+ len = 2;
+ }
+ else if(num < 0x1000000) {
+ len = 3;
+ }
+ else {
+ len = 4;
+ }
+ cdata.Data = (uint8 *)coder.malloc(len);
+ cdata.Length = len;
+ uint8 *cp = &cdata.Data[len - 1];
+ for(unsigned i=0; i<len; i++) {
+ *cp-- = num & 0xff;
+ num >>= 8;
+ }
+}
+
+/*
+ * Set up a encoded NULL for AlgorithmIdentifier.parameters,
+ * required for RSA
+ */
+static void nullAlgParams(
+ CSSM_X509_ALGORITHM_IDENTIFIER &algId)
+{
+ static const uint8 encNull[2] = { SEC_ASN1_NULL, 0 };
+ CSSM_DATA encNullData;
+ encNullData.Data = (uint8 *)encNull;
+ encNullData.Length = 2;
+
+ algId.parameters = encNullData;
+}
+
+#pragma mark -
+#pragma mark *** RSA key encode/decode ***
+
+/*
+ * DER encode/decode RSA keys in various formats.
+ *
+ * Public key, CSSM_KEYBLOB_RAW_FORMAT_PKCS1
+ * -- compatible with BSAFE
+ * -- used for CSSM_KEYBLOB_RAW_FORMAT_DIGEST on both keys
+ */
+static CSSM_RETURN RSAPublicKeyDecodePKCS1(
+ SecNssCoder &coder,
+ RSA *openKey,
+ void *p,
+ size_t length)
+{
+ NSS_RSAPublicKeyPKCS1 nssPubKey;
+
+ memset(&nssPubKey, 0, sizeof(nssPubKey));
+ PRErrorCode perr = coder.decode(p, length,
+ kSecAsn1RSAPublicKeyPKCS1Template, &nssPubKey);
+ if(perr) {
+ logAsnErr("decode(RSAPublicKeyPKCS1)", perr);
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+
+ try {
+ openKey->n = cssmDataToBn(nssPubKey.modulus);
+ openKey->e = cssmDataToBn(nssPubKey.publicExponent);
+ }
+ catch(...) {
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+ return 0;
+}
+
+static CSSM_RETURN RSAPublicKeyEncodePKCS1(
+ SecNssCoder &coder,
+ RSA *openKey,
+ CssmOwnedData &encodedKey)
+{
+ /* convert to NSS_RSAPublicKeyPKCS1 */
+ NSS_RSAPublicKeyPKCS1 nssPubKey;
+
+ try {
+ bnToCssmData(openKey->n, nssPubKey.modulus, coder);
+ bnToCssmData(openKey->e, nssPubKey.publicExponent, coder);
+ }
+ catch(...) {
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+
+ PRErrorCode prtn;
+ prtn = SecNssEncodeItemOdata(&nssPubKey,
+ kSecAsn1RSAPublicKeyPKCS1Template, encodedKey);
+ if(prtn) {
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+ return CSSM_OK;
+}
+
+/*
+ * SubjectPublicKeyInfo, as used by openssl.
+ * The subjectPublicKey component is a PKCS1-style RSAPublicKey.
+ */
+static CSSM_RETURN RSAPublicKeyDecodeX509(
+ SecNssCoder &coder,
+ RSA *openKey,
+ void *p,
+ CSSM_SIZE length,
+ /* mallocd/returned encoded alg params for OAEP key */
+ uint8 **algParams,
+ CSSM_SIZE *algParamLen)
+{
+ CSSM_X509_SUBJECT_PUBLIC_KEY_INFO nssPubKeyInfo;
+ PRErrorCode perr;
+
+ memset(&nssPubKeyInfo, 0, sizeof(nssPubKeyInfo));
+ perr = coder.decode(p, length, kSecAsn1SubjectPublicKeyInfoTemplate,
+ &nssPubKeyInfo);
+ if(perr) {
+ logAsnErr("decode(RSA SubjectPublicKeyInfo)", perr);
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+
+ /* verify alg identifier */
+ const CSSM_OID *oid = &nssPubKeyInfo.algorithm.algorithm;
+ if(!cspCompareCssmData(oid, &CSSMOID_RSA)) {
+ if(!cspCompareCssmData(oid, &CSSMOID_RSAWithOAEP)) {
+ sslAsn1Debug("RSAPublicKeyDecodeX509: bad OID");
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+ if(nssPubKeyInfo.algorithm.parameters.Data != NULL) {
+ CSSM_SIZE len = nssPubKeyInfo.algorithm.parameters.Length;
+ *algParams = (uint8 *)malloc(len);
+ memmove(*algParams, nssPubKeyInfo.algorithm.parameters.Data, len);
+ *algParamLen = len;
+ }
+ }
+
+ /* decode the raw bits */
+ CSSM_DATA *pubKey = &nssPubKeyInfo.subjectPublicKey;
+ /* decoded length was in bits */
+ pubKey->Length = (pubKey->Length + 7) / 8;
+ return RSAPublicKeyDecodePKCS1(coder, openKey, pubKey->Data,
+ pubKey->Length);
+}
+
+static CSSM_RETURN RSAPublicKeyEncodeX509(
+ SecNssCoder &coder,
+ RSA *openKey,
+ CssmOwnedData &encodedKey,
+ /* encoded alg params for OAEP key */
+ uint8 *algParams,
+ uint32 algParamsLen)
+{
+ CssmAutoData aData(Allocator::standard());
+ CSSM_RETURN crtn;
+
+ /* First get an encoded PKCS1-style RSAPublicKey */
+ crtn = RSAPublicKeyEncodePKCS1(coder, openKey, aData);
+ if(crtn) {
+ return crtn;
+ }
+
+ /*
+ * That's the AsnBits subjectPublicKey component of a
+ * SubjectPublicKeyInfo
+ */
+ CSSM_X509_SUBJECT_PUBLIC_KEY_INFO nssPubKeyInfo;
+ memset(&nssPubKeyInfo, 0, sizeof(nssPubKeyInfo));
+ nssPubKeyInfo.subjectPublicKey.Data = (uint8 *)aData.data();
+ nssPubKeyInfo.subjectPublicKey.Length = aData.length() * 8;
+
+ CSSM_X509_ALGORITHM_IDENTIFIER &algId = nssPubKeyInfo.algorithm;
+ algId.algorithm = CSSMOID_RSA;
+
+ if(algParams) {
+ algId.parameters.Data = (uint8 *)algParams;
+ algId.parameters.Length = algParamsLen;
+ }
+ else {
+ /* NULL algorithm parameters */
+ nullAlgParams(algId);
+ }
+
+ /* DER encode */
+ PRErrorCode perr;
+ perr = SecNssEncodeItemOdata(&nssPubKeyInfo,
+ kSecAsn1SubjectPublicKeyInfoTemplate, encodedKey);
+
+ if(perr) {
+ logAsnErr("encode(RSA SubjectPublicKeyInfo)", perr);
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+ return CSSM_OK;
+}
+
+/*
+ * RSA private key, PKCS1 format, used by openssl.
+ */
+static CSSM_RETURN RSAPrivateKeyDecodePKCS1(
+ SecNssCoder &coder,
+ RSA *openKey,
+ void *p,
+ size_t length)
+{
+ NSS_RSAPrivateKeyPKCS1 nssPrivKey;
+ PRErrorCode perr;
+
+ memset(&nssPrivKey, 0, sizeof(nssPrivKey));
+ perr = coder.decode(p, length, kSecAsn1RSAPrivateKeyPKCS1Template, &nssPrivKey);
+ if(perr) {
+ logAsnErr("decode(RSAPrivateKeyPKCS)", perr);
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+
+ /* convert nssPrivKey fields to RSA key fields */
+ try {
+ openKey->version = cssmDataToInt(nssPrivKey.version);
+ openKey->n = cssmDataToBn(nssPrivKey.modulus);
+ openKey->e = cssmDataToBn(nssPrivKey.publicExponent);
+ openKey->d = cssmDataToBn(nssPrivKey.privateExponent);
+ openKey->p = cssmDataToBn(nssPrivKey.prime1);
+ openKey->q = cssmDataToBn(nssPrivKey.prime2);
+ openKey->dmp1 = cssmDataToBn(nssPrivKey.exponent1);
+ openKey->dmq1 = cssmDataToBn(nssPrivKey.exponent2);
+ openKey->iqmp = cssmDataToBn(nssPrivKey.coefficient);
+ }
+ catch(...) {
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+ return 0;
+}
+
+static CSSM_RETURN RSAPrivateKeyEncodePKCS1(
+ SecNssCoder &coder,
+ RSA *openKey,
+ CssmOwnedData &encodedKey)
+{
+ NSS_RSAPrivateKeyPKCS1 nssPrivKey;
+ PRErrorCode perr;
+
+ /* convert to NSS_RSAPrivateKeyPKCS1 */
+ try {
+ intToCssmData(openKey->version, nssPrivKey.version, coder);
+ bnToCssmData(openKey->n, nssPrivKey.modulus, coder);
+ bnToCssmData(openKey->e, nssPrivKey.publicExponent, coder);
+ bnToCssmData(openKey->d, nssPrivKey.privateExponent, coder);
+ bnToCssmData(openKey->p, nssPrivKey.prime1, coder);
+ bnToCssmData(openKey->q, nssPrivKey.prime2, coder);
+ bnToCssmData(openKey->dmp1, nssPrivKey.exponent1, coder);
+ bnToCssmData(openKey->dmq1, nssPrivKey.exponent2, coder);
+ bnToCssmData(openKey->iqmp, nssPrivKey.coefficient, coder);
+ }
+ catch(...) {
+ /* ? */
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+
+ /* DER encode */
+ perr = SecNssEncodeItemOdata(&nssPrivKey, kSecAsn1RSAPrivateKeyPKCS1Template,
+ encodedKey);
+ if(perr) {
+ logAsnErr("encode(RSAPrivateKeyPKCS1)", perr);
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+ return CSSM_OK;
+}
+
+/*
+ * RSA private key, PKCS8, compatible with BSAFE.
+ */
+static CSSM_RETURN RSAPrivateKeyDecodePKCS8(
+ SecNssCoder &coder,
+ RSA *openKey,
+ void *p,
+ CSSM_SIZE length,
+ /* mallocd/returned encoded alg params for OAEP key */
+ uint8 **algParams,
+ CSSM_SIZE *algParamLen)
+{
+ NSS_PrivateKeyInfo nssPrivKeyInfo;
+ PRErrorCode perr;
+
+ memset(&nssPrivKeyInfo, 0, sizeof(nssPrivKeyInfo));
+ perr = coder.decode(p, length, kSecAsn1PrivateKeyInfoTemplate, &nssPrivKeyInfo);
+ if(perr) {
+ logAsnErr("decode(PrivateKeyInfo)", perr);
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+
+ /* verify alg identifier */
+ const CSSM_OID *oid = &nssPrivKeyInfo.algorithm.algorithm;
+ if(!cspCompareCssmData(oid, &CSSMOID_RSA)) {
+ if(!cspCompareCssmData(oid, &CSSMOID_RSAWithOAEP)) {
+ sslAsn1Debug("RSAPrivateKeyDecodePKCS8: bad OID");
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+ if(nssPrivKeyInfo.algorithm.parameters.Data != NULL) {
+ CSSM_SIZE len = nssPrivKeyInfo.algorithm.parameters.Length;
+ *algParams = (uint8 *)malloc(len);
+ memmove(*algParams, nssPrivKeyInfo.algorithm.parameters.Data, len);
+ *algParamLen = len;
+ }
+ }
+
+ /*
+ * nssPrivKeyInfo.privateKey is an octet string which needs
+ * subsequent decoding
+ */
+ CSSM_DATA *privKey = &nssPrivKeyInfo.privateKey;
+ return RSAPrivateKeyDecodePKCS1(coder, openKey,
+ privKey->Data, privKey->Length);
+}
+
+static CSSM_RETURN RSAPrivateKeyEncodePKCS8(
+ SecNssCoder &coder,
+ RSA *openKey,
+ CssmOwnedData &encodedKey,
+ /* encoded alg params for OAEP key */
+ uint8 *algParams,
+ uint32 algParamsLen)
+{
+
+ /* First get PKCS1-style encoding */
+ CssmAutoData aData(Allocator::standard());
+ CSSM_RETURN crtn = RSAPrivateKeyEncodePKCS1(coder, openKey, aData);
+ if(crtn) {
+ return crtn;
+ }
+
+ /* that encoding is the privateKey field of a NSS_PrivateKeyInfo */
+ NSS_PrivateKeyInfo nssPrivKeyInfo;
+ memset(&nssPrivKeyInfo, 0, sizeof(nssPrivKeyInfo));
+ nssPrivKeyInfo.privateKey.Data = (uint8 *)aData.data();
+ nssPrivKeyInfo.privateKey.Length = aData.length();
+
+ CSSM_X509_ALGORITHM_IDENTIFIER &algId = nssPrivKeyInfo.algorithm;
+ algId.algorithm = CSSMOID_RSA;
+
+ if(algParams) {
+ algId.parameters.Data = (uint8 *)algParams;
+ algId.parameters.Length = algParamsLen;
+ }
+ else {
+ /* NULL algorithm parameters */
+ nullAlgParams(algId);
+ }
+
+ /* FIXME : attributes? */
+
+ uint8 vers = 0;
+ nssPrivKeyInfo.version.Data = &vers;
+ nssPrivKeyInfo.version.Length = 1;
+
+ /* DER encode */
+ PRErrorCode perr;
+ perr = SecNssEncodeItemOdata(&nssPrivKeyInfo,
+ kSecAsn1PrivateKeyInfoTemplate, encodedKey);
+
+ if(perr) {
+ logAsnErr("encode(RSA PrivateKeyInfo)", perr);
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+ return CSSM_OK;
+}
+
+CSSM_RETURN RSAPublicKeyDecode(
+ RSA *openKey,
+ CSSM_KEYBLOB_FORMAT format,
+ void *p,
+ size_t length)
+{
+ SecNssCoder coder;
+
+ switch(format) {
+ case CSSM_KEYBLOB_RAW_FORMAT_PKCS1:
+ return RSAPublicKeyDecodePKCS1(coder, openKey, p, length);
+ case CSSM_KEYBLOB_RAW_FORMAT_X509:
+ return RSAPublicKeyDecodeX509(coder, openKey, p, length, NULL, NULL);
+ case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH:
+ return RSAPublicKeyDecodeOpenSSH1(openKey, p, length);
+ case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2:
+ return RSAPublicKeyDecodeOpenSSH2(openKey, p, length);
+ default:
+ assert(0);
+ return CSSMERR_CSP_INTERNAL_ERROR;
+ }
+}
+
+CSSM_RETURN RSAPublicKeyEncode(
+ RSA *openKey,
+ CSSM_KEYBLOB_FORMAT format,
+ const CssmData &descData,
+ CssmOwnedData &encodedKey)
+{
+ SecNssCoder coder;
+
+ switch(format) {
+ case CSSM_KEYBLOB_RAW_FORMAT_PKCS1:
+ return RSAPublicKeyEncodePKCS1(coder, openKey, encodedKey);
+ case CSSM_KEYBLOB_RAW_FORMAT_X509:
+ return RSAPublicKeyEncodeX509(coder, openKey, encodedKey, NULL, 0);
+ case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH:
+ return RSAPublicKeyEncodeOpenSSH1(openKey, descData, encodedKey);
+ case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2:
+ return RSAPublicKeyEncodeOpenSSH2(openKey, descData, encodedKey);
+ default:
+ assert(0);
+ return CSSMERR_CSP_INTERNAL_ERROR;
+ }
+}
+
+CSSM_RETURN RSAPrivateKeyDecode(
+ RSA *openKey,
+ CSSM_KEYBLOB_FORMAT format,
+ void *p,
+ size_t length)
+{
+ SecNssCoder coder;
+
+ switch(format) {
+ case CSSM_KEYBLOB_RAW_FORMAT_PKCS1:
+ return RSAPrivateKeyDecodePKCS1(coder, openKey, p, length);
+ case CSSM_KEYBLOB_RAW_FORMAT_PKCS8:
+ return RSAPrivateKeyDecodePKCS8(coder, openKey, p, length, NULL, NULL);
+ case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH:
+ return RSAPrivateKeyDecodeOpenSSH1(openKey, p, length);
+ default:
+ assert(0);
+ return CSSMERR_CSP_INTERNAL_ERROR;
+ }
+}
+
+CSSM_RETURN RSAPrivateKeyEncode(
+ RSA *openKey,
+ CSSM_KEYBLOB_FORMAT format,
+ const CssmData &descData,
+ CssmOwnedData &encodedKey)
+{
+ SecNssCoder coder;
+
+ switch(format) {
+ case CSSM_KEYBLOB_RAW_FORMAT_PKCS1:
+ return RSAPrivateKeyEncodePKCS1(coder, openKey, encodedKey);
+ case CSSM_KEYBLOB_RAW_FORMAT_PKCS8:
+ return RSAPrivateKeyEncodePKCS8(coder, openKey, encodedKey, NULL, 0);
+ case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH:
+ return RSAPrivateKeyEncodeOpenSSH1(openKey, descData, encodedKey);
+ default:
+ assert(0);
+ return CSSMERR_CSP_INTERNAL_ERROR;
+ }
+}
+
+CSSM_RETURN RSAOAEPPrivateKeyEncode(
+ RSA *openKey,
+ const CSSM_DATA *label,
+ CssmOwnedData &encodedKey)
+{
+ SecNssCoder coder;
+ CSSM_DATA encodedParams = {0, NULL};
+ /* TBD encode the label into a RSAES-OAEP-params */
+
+ return RSAPrivateKeyEncodePKCS8(coder, openKey, encodedKey, encodedParams.Data, (unsigned int)encodedParams.Length);
+}
+
+CSSM_RETURN RSAOAEPPublicKeyEncode(
+ RSA *openKey,
+ const CSSM_DATA *label,
+ CssmOwnedData &encodedKey)
+{
+ SecNssCoder coder;
+ CSSM_DATA encodedParams = {0, NULL};
+ /* TBD encode the label into a RSAES-OAEP-params */
+
+ return RSAPublicKeyEncodeX509(coder, openKey, encodedKey, encodedParams.Data, (unsigned int)encodedParams.Length);
+}
+
+CSSM_RETURN RSAOAEPPublicKeyDecode(
+ RSA *openKey,
+ void *p,
+ size_t length,
+ /* mallocd and returned label */
+ CSSM_DATA *label)
+{
+ SecNssCoder coder;
+ CSSM_RETURN crtn;
+ CSSM_DATA encodedParams = {0, NULL};
+
+ crtn = RSAPublicKeyDecodeX509(coder, openKey, p, length, &encodedParams.Data,
+ &encodedParams.Length);
+ if(crtn) {
+ return crtn;
+ }
+
+ /* TBD - decode label from encoded alg params */
+ label->Data = NULL;
+ label->Length = 0;
+ return CSSM_OK;
+}
+
+CSSM_RETURN RSAOAEPPrivateKeyDecode(
+ RSA *openKey,
+ void *p,
+ size_t length,
+ /* mallocd and returned label */
+ CSSM_DATA *label)
+{
+ SecNssCoder coder;
+ CSSM_RETURN crtn;
+ CSSM_DATA encodedParams = {0, NULL};
+
+ crtn = RSAPrivateKeyDecodePKCS8(coder, openKey, p, length, &encodedParams.Data,
+ &encodedParams.Length);
+ if(crtn) {
+ return crtn;
+ }
+
+ /* TBD - decode label from encoded alg params */
+ label->Data = NULL;
+ label->Length = 0;
+ return CSSM_OK;
+}
+
+#pragma mark -
+#pragma mark *** DSA key encode/decode ***
+
+/***
+ *** DSA
+ ***/
+
+/* NSS_DSAAlgorithmIdBSAFE <--> DSA->{p,g,q} */
+static void dsaToNssAlgIdBSAFE(
+ const DSA *openKey,
+ NSS_DSAAlgorithmIdBSAFE &algId,
+ SecNssCoder &coder)
+{
+ /* non-standard, BSAFE-specific OID */
+ algId.algorithm = CSSMOID_DSA; // not mallocd
+ unsigned numBits = BN_num_bits(openKey->p);
+ intToCssmData(numBits, algId.params.keySizeInBits, coder);
+ bnToCssmData(openKey->p, algId.params.p, coder);
+ bnToCssmData(openKey->q, algId.params.q, coder);
+ bnToCssmData(openKey->g, algId.params.g, coder);
+}
+
+static CSSM_RETURN nssAlgIdToDsaBSAFE(
+ NSS_DSAAlgorithmIdBSAFE &algId,
+ DSA *openKey)
+{
+ /* non-standard, BSAFE-specific OID */
+ if(!cspCompareCssmData(&algId.algorithm, &CSSMOID_DSA)) {
+ sslAsn1Debug("nssAlgIdToDsaBSAFE: bad OID");
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+ openKey->p = cssmDataToBn(algId.params.p);
+ openKey->q = cssmDataToBn(algId.params.q);
+ openKey->g = cssmDataToBn(algId.params.g);
+ return CSSM_OK;
+}
+
+/* NSS_DSAAlgorithmIdX509 <--> DSA->{p,g,q} */
+static void dsaToNssAlgIdX509(
+ const DSA *openKey,
+ NSS_DSAAlgorithmIdX509 &algId,
+ SecNssCoder &coder)
+{
+ algId.algorithm = CSSMOID_DSA_CMS; // not mallocd
+ bnToCssmData(openKey->p, algId.params->p, coder);
+ bnToCssmData(openKey->q, algId.params->q, coder);
+ bnToCssmData(openKey->g, algId.params->g, coder);
+}
+
+static CSSM_RETURN nssAlgIdToDsaX509(
+ NSS_DSAAlgorithmIdX509 &algId,
+ DSA *openKey)
+{
+ if(!cspCompareCssmData(&algId.algorithm, &CSSMOID_DSA_CMS) &&
+ !cspCompareCssmData(&algId.algorithm, &CSSMOID_DSA_JDK)) {
+ sslAsn1Debug("nssAlgIdToDsaX509: bad OID");
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+ /* these might be absent per CMS */
+ if(algId.params == NULL) {
+ return CSSM_OK;
+ }
+ openKey->p = cssmDataToBn(algId.params->p);
+ openKey->q = cssmDataToBn(algId.params->q);
+ openKey->g = cssmDataToBn(algId.params->g);
+ return CSSM_OK;
+}
+
+/*
+ * DSA public keys, FIPS186 format.
+ * Compatible with BSAFE.
+ */
+static
+CSSM_RETURN DSAPublicKeyDecodeFIPS186(
+ SecNssCoder &coder,
+ DSA *openKey,
+ void *p,
+ size_t length)
+{
+ NSS_DSAPublicKeyBSAFE nssPubKey;
+ PRErrorCode perr;
+ CSSM_RETURN crtn;
+
+ memset(&nssPubKey, 0, sizeof(nssPubKey));
+ perr = coder.decode(p, length, kSecAsn1DSAPublicKeyBSAFETemplate,
+ &nssPubKey);
+ if(perr) {
+ logAsnErr("decode(DSAPublicKeyBSAFE)", perr);
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+
+ /* BSAFE style DSA-specific alg params */
+ NSS_DSAAlgorithmIdBSAFE &algId = nssPubKey.dsaAlg;
+ crtn = nssAlgIdToDsaBSAFE(algId, openKey);
+ if(crtn) {
+ return crtn;
+ }
+
+ /* inside of nssPubKey.publicKey is the DER-encoding of a
+ * ASN Integer; decoded length was in bits */
+ nssPubKey.publicKey.Length = (nssPubKey.publicKey.Length + 7) / 8;
+ CSSM_DATA pubKeyBytes;
+ perr = coder.decodeItem(nssPubKey.publicKey,
+ kSecAsn1UnsignedIntegerTemplate,
+ &pubKeyBytes);
+ if(perr) {
+ logAsnErr("decode(NSS_DSAPublicKeyBSAFE.publicKey)", perr);
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+ openKey->pub_key = cssmDataToBn(pubKeyBytes);
+
+ if(openKey->pub_key == NULL) {
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+ return 0;
+}
+
+static
+CSSM_RETURN DSAPublicKeyEncodeFIPS186(
+ SecNssCoder &coder,
+ DSA *openKey,
+ CssmOwnedData &encodedKey)
+{
+ try {
+ /* convert to NSS_DSAPublicKeyBSAFE */
+ NSS_DSAPublicKeyBSAFE nssPubKey;
+ memset(&nssPubKey, 0, sizeof(nssPubKey));
+ dsaToNssAlgIdBSAFE(openKey, nssPubKey.dsaAlg, coder);
+
+ /*
+ * publicKey is the DER-encoding of a ASN INTEGER wrapped in
+ * an AsnBits
+ */
+ CSSM_DATA pubKeyRaw;
+ PRErrorCode perr;
+ bnToCssmData(openKey->pub_key, pubKeyRaw, coder);
+ perr = coder.encodeItem(&pubKeyRaw, kSecAsn1UnsignedIntegerTemplate,
+ nssPubKey.publicKey);
+ if(perr) {
+ logAsnErr("encodeItem(DSAPublicKeyBSAFE.publicKey)", perr);
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+ nssPubKey.publicKey.Length *= 8;
+
+ /* DER encode */
+ SecNssEncodeItemOdata(&nssPubKey, kSecAsn1DSAPublicKeyBSAFETemplate,
+ encodedKey);
+ return CSSM_OK;
+ }
+ catch(...) {
+ /* ? */
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+}
+
+/*
+ * DSA private keys, FIPS186 format.
+ * Compatible with BSAFE.
+ */
+static
+CSSM_RETURN DSAPrivateKeyDecodeFIPS186(
+ SecNssCoder &coder,
+ DSA *openKey,
+ void *p,
+ unsigned length)
+{
+ NSS_DSAPrivateKeyBSAFE nssPrivKeyInfo;
+ PRErrorCode perr;
+
+ memset(&nssPrivKeyInfo, 0, sizeof(nssPrivKeyInfo));
+ perr = coder.decode(p, length, kSecAsn1DSAPrivateKeyBSAFETemplate,
+ &nssPrivKeyInfo);
+ if(perr) {
+ logAsnErr("decode(DSA PrivateKeyInfo)", perr);
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+
+ CSSM_RETURN crtn = nssAlgIdToDsaBSAFE(nssPrivKeyInfo.dsaAlg, openKey);
+ if(crtn) {
+ return crtn;
+ }
+
+ /* nssPrivKeyInfo.privateKey is the DER-encoding of a
+ * DSAPrivateKeyOcts... */
+ try {
+ PRErrorCode perr;
+ NSS_DSAPrivateKeyOcts keyOcts;
+
+ perr = coder.decodeItem(nssPrivKeyInfo.privateKey,
+ kSecAsn1DSAPrivateKeyOctsTemplate, &keyOcts);
+ if(perr) {
+ logAsnErr("decode(DSA PrivateKeyInfoOcts)", perr);
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+
+ openKey->priv_key = cssmDataToBn(keyOcts.privateKey);
+ if(openKey->priv_key == NULL) {
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+ return 0;
+ }
+ catch(...) {
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+}
+
+static
+CSSM_RETURN DSAPrivateKeyEncodeFIPS186(
+ SecNssCoder &coder,
+ DSA *openKey,
+ CssmOwnedData &encodedKey)
+{
+ try {
+ /* First convert into a NSS_DSAPrivateKeyBSAFE */
+ NSS_DSAPrivateKeyBSAFE nssPrivKey;
+ intToCssmData(openKey->version, nssPrivKey.version, coder);
+ dsaToNssAlgIdBSAFE(openKey, nssPrivKey.dsaAlg, coder);
+
+ /* nssPrivKey.privateKey is the DER-encoding of one of these... */
+ NSS_DSAPrivateKeyOcts privKeyOcts;
+ bnToCssmData(openKey->priv_key, privKeyOcts.privateKey, coder);
+
+ /* DER encode the privateKey portion into arena pool memory
+ * into NSS_DSAPrivateKeyPKCS8.privateKey */
+ coder.encodeItem(&privKeyOcts, kSecAsn1DSAPrivateKeyOctsTemplate,
+ nssPrivKey.privateKey);
+
+ /* DER encode the whole thing */
+ PRErrorCode perr;
+ perr = SecNssEncodeItemOdata(&nssPrivKey,
+ kSecAsn1DSAPrivateKeyBSAFETemplate, encodedKey);
+ return 0;
+ }
+ catch(...) {
+ /* ? */
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+}
+
+/*
+ * DSA private keys, PKCS8/SMIME format.
+ */
+static
+CSSM_RETURN DSAPrivateKeyDecodePKCS8(
+ SecNssCoder &coder,
+ DSA *openKey,
+ void *p,
+ unsigned length)
+{
+ NSS_DSAPrivateKeyPKCS8 nssPrivKeyInfo;
+ PRErrorCode perr;
+
+ memset(&nssPrivKeyInfo, 0, sizeof(nssPrivKeyInfo));
+ perr = coder.decode(p, length, kSecAsn1DSAPrivateKeyPKCS8Template,
+ &nssPrivKeyInfo);
+ if(perr) {
+ logAsnErr("decode(DSA NSS_DSAPrivateKeyPKCS8)", perr);
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+
+ CSSM_RETURN crtn = nssAlgIdToDsaX509(nssPrivKeyInfo.dsaAlg, openKey);
+ if(crtn) {
+ return crtn;
+ }
+
+ /*
+ * Post-decode, nssPrivKeyInfo.privateKey is the DER-encoding of a
+ * an ASN integer.
+ */
+ try {
+ PRErrorCode perr;
+ CSSM_DATA privKeyInt = {0, NULL};
+
+ perr = coder.decodeItem(nssPrivKeyInfo.privateKey,
+ kSecAsn1UnsignedIntegerTemplate, &privKeyInt);
+ if(perr) {
+ logAsnErr("decode(DSA nssPrivKeyInfo.privateKey)", perr);
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+
+ openKey->priv_key = cssmDataToBn(privKeyInt);
+ if(openKey->priv_key == NULL) {
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+ return 0;
+ }
+ catch(...) {
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+}
+
+static
+CSSM_RETURN DSAPrivateKeyEncodePKCS8(
+ SecNssCoder &coder,
+ DSA *openKey,
+ CssmOwnedData &encodedKey)
+{
+ try {
+ /* First convert into a NSS_DSAPrivateKeyPKCS8 */
+ NSS_DSAPrivateKeyPKCS8 nssPrivKey;
+ NSS_DSAAlgParams algParams;
+ memset(&nssPrivKey, 0, sizeof(nssPrivKey));
+ memset(&algParams, 0, sizeof(algParams));
+ nssPrivKey.dsaAlg.params = &algParams;
+ intToCssmData(openKey->version, nssPrivKey.version, coder);
+ dsaToNssAlgIdX509(openKey, nssPrivKey.dsaAlg, coder);
+
+ /* pre-encode, nssPrivKey.privateKey is the DER-encoding of
+ * an ASN integer... */
+ CSSM_DATA privKeyInt;
+ bnToCssmData(openKey->priv_key, privKeyInt, coder);
+
+ /* DER encode the privateKey portion into arena pool memory
+ * into NSS_DSAPrivateKeyPKCS8.privateKey */
+ coder.encodeItem(&privKeyInt, kSecAsn1UnsignedIntegerTemplate,
+ nssPrivKey.privateKey);
+
+ /* DER encode the whole thing */
+ PRErrorCode perr;
+ perr = SecNssEncodeItemOdata(&nssPrivKey,
+ kSecAsn1DSAPrivateKeyPKCS8Template, encodedKey);
+ return 0;
+ }
+ catch(...) {
+ /* ? */
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+}
+
+/*
+ * DSA public key, X509/openssl format.
+ */
+static CSSM_RETURN DSAPublicKeyDecodeX509(
+ SecNssCoder &coder,
+ DSA *openKey,
+ void *p,
+ size_t length)
+{
+ NSS_DSAPublicKeyX509 nssPubKey;
+ PRErrorCode perr;
+ CSSM_RETURN crtn;
+
+ memset(&nssPubKey, 0, sizeof(nssPubKey));
+ perr = coder.decode(p, length, kSecAsn1DSAPublicKeyX509Template,
+ &nssPubKey);
+ if(perr) {
+ logAsnErr("decode(DSAPublicKeyX509)", perr);
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+
+ /* X509 style DSA-specific alg params */
+ NSS_DSAAlgorithmIdX509 &algId = nssPubKey.dsaAlg;
+ crtn = nssAlgIdToDsaX509(algId, openKey);
+ if(crtn) {
+ return crtn;
+ }
+
+ /* inside of nssPubKey.publicKey is the DER-encoding of a
+ * ASN Integer; decoded length was in bits */
+ nssPubKey.publicKey.Length = (nssPubKey.publicKey.Length + 7) / 8;
+ CSSM_DATA pubKeyBytes = {0, NULL};
+ perr = coder.decodeItem(nssPubKey.publicKey,
+ kSecAsn1UnsignedIntegerTemplate,
+ &pubKeyBytes);
+ if(perr) {
+ logAsnErr("decode(NSS_DSAPublicKeyX509.publicKey)", perr);
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+ openKey->pub_key = cssmDataToBn(pubKeyBytes);
+
+ if(openKey->pub_key == NULL) {
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+ return 0;
+}
+
+static CSSM_RETURN DSAPublicKeyEncodeX509(
+ SecNssCoder &coder,
+ DSA *openKey,
+ CssmOwnedData &encodedKey)
+{
+ try {
+ /* convert to NSS_DSAPublicKeyX509 */
+ NSS_DSAPublicKeyX509 nssPubKey;
+ NSS_DSAAlgParams algParams;
+ memset(&nssPubKey, 0, sizeof(nssPubKey));
+ memset(&algParams, 0, sizeof(algParams));
+ nssPubKey.dsaAlg.params = &algParams;
+ dsaToNssAlgIdX509(openKey, nssPubKey.dsaAlg, coder);
+
+ /*
+ * publicKey is the DER-encoding of a ASN INTEGER wrapped in
+ * an AsnBits
+ */
+ CSSM_DATA pubKeyRaw;
+ PRErrorCode perr;
+ bnToCssmData(openKey->pub_key, pubKeyRaw, coder);
+ perr = coder.encodeItem(&pubKeyRaw, kSecAsn1UnsignedIntegerTemplate,
+ nssPubKey.publicKey);
+ if(perr) {
+ logAsnErr("encodeItem(DSAPublicKeyX509.publicKey)", perr);
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+ nssPubKey.publicKey.Length *= 8;
+
+ /* DER encode */
+ SecNssEncodeItemOdata(&nssPubKey, kSecAsn1DSAPublicKeyX509Template,
+ encodedKey);
+ return CSSM_OK;
+ }
+ catch(...) {
+ /* ? */
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+}
+
+/*
+ * Encode public key portion only for calculating key digest.
+ * Note this works just fine on a partial DSA public key, i.e.,
+ * A DSA public key's digest-capable blob is the same whether or
+ * not the DSA key has its DSA parameters p, q, and g.
+ */
+static CSSM_RETURN DSAPublicKeyEncodeHashable(
+ SecNssCoder &coder,
+ DSA *openKey,
+ CssmOwnedData &encodedKey)
+{
+ try {
+ /*
+ * publicKey is the DER-encoding of an ASN integer
+ */
+ CSSM_DATA pubKey;
+ bnToCssmData(openKey->pub_key, pubKey, coder);
+ PRErrorCode perr;
+
+ perr = SecNssEncodeItemOdata(&pubKey, kSecAsn1UnsignedIntegerTemplate,
+ encodedKey);
+ if(perr) {
+ logAsnErr("encode(DSAPubHashable)", perr);
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+ return CSSM_OK;
+ }
+ catch(...) {
+ /* ? */
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+}
+
+/*
+ * DSA private key, custom openssl format.
+ */
+static CSSM_RETURN DSAPrivateKeyDecodeOpenssl(
+ SecNssCoder &coder,
+ DSA *openKey,
+ void *p,
+ size_t length)
+{
+ NSS_DSAPrivateKeyOpenssl nssPrivKey;
+ PRErrorCode perr;
+
+ memset(&nssPrivKey, 0, sizeof(nssPrivKey));
+ perr = coder.decode(p, length, kSecAsn1DSAPrivateKeyOpensslTemplate,
+ &nssPrivKey);
+ if(perr) {
+ logAsnErr("decode(DSAPrivateKeyOpenssl)", perr);
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+
+ /* convert nssPrivKey fields to RSA key fields */
+ try {
+ openKey->version = cssmDataToInt(nssPrivKey.version);
+ openKey->p = cssmDataToBn(nssPrivKey.p);
+ openKey->q = cssmDataToBn(nssPrivKey.q);
+ openKey->g = cssmDataToBn(nssPrivKey.g);
+ openKey->pub_key = cssmDataToBn(nssPrivKey.pub);
+ openKey->priv_key = cssmDataToBn(nssPrivKey.priv);
+ }
+ catch(...) {
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+ return 0;
+}
+
+static CSSM_RETURN DSAPrivateKeyEncodeOpenssl(
+ SecNssCoder &coder,
+ DSA *openKey,
+ CssmOwnedData &encodedKey)
+{
+ NSS_DSAPrivateKeyOpenssl nssPrivKey;
+ PRErrorCode perr;
+
+ /* convert to NSS_DSAPrivateKeyOpenssl */
+ try {
+ intToCssmData(openKey->version, nssPrivKey.version, coder);
+ bnToCssmData(openKey->p, nssPrivKey.p, coder);
+ bnToCssmData(openKey->q, nssPrivKey.q, coder);
+ bnToCssmData(openKey->g, nssPrivKey.g, coder);
+ bnToCssmData(openKey->pub_key, nssPrivKey.pub, coder);
+ bnToCssmData(openKey->priv_key, nssPrivKey.priv, coder);
+ }
+ catch(...) {
+ /* ? */
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+
+ /* DER encode */
+ perr = SecNssEncodeItemOdata(&nssPrivKey, kSecAsn1DSAPrivateKeyOpensslTemplate,
+ encodedKey);
+ if(perr) {
+ logAsnErr("encode(DSAPrivateKeyOpenssl)", perr);
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+ return CSSM_OK;
+}
+
+CSSM_RETURN DSAPublicKeyDecode(
+ DSA *openKey,
+ CSSM_KEYBLOB_FORMAT format,
+ void *p,
+ size_t length)
+{
+ SecNssCoder coder;
+
+ switch(format) {
+ case CSSM_KEYBLOB_RAW_FORMAT_FIPS186:
+ return DSAPublicKeyDecodeFIPS186(coder, openKey, p, length);
+ case CSSM_KEYBLOB_RAW_FORMAT_X509:
+ return DSAPublicKeyDecodeX509(coder, openKey, p, length);
+ case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2:
+ return DSAPublicKeyDecodeOpenSSH2(openKey, p, length);
+ default:
+ assert(0);
+ return CSSMERR_CSP_INTERNAL_ERROR;
+ }
+}
+
+CSSM_RETURN DSAPublicKeyEncode(
+ DSA *openKey,
+ CSSM_KEYBLOB_FORMAT format,
+ const CssmData &descData,
+ CssmOwnedData &encodedKey)
+{
+ SecNssCoder coder;
+
+ switch(format) {
+ case CSSM_KEYBLOB_RAW_FORMAT_FIPS186:
+ return DSAPublicKeyEncodeFIPS186(coder, openKey, encodedKey);
+ case CSSM_KEYBLOB_RAW_FORMAT_X509:
+ return DSAPublicKeyEncodeX509(coder, openKey, encodedKey);
+ case CSSM_KEYBLOB_RAW_FORMAT_DIGEST:
+ return DSAPublicKeyEncodeHashable(coder, openKey, encodedKey);
+ case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2:
+ return DSAPublicKeyEncodeOpenSSH2(openKey, descData, encodedKey);
+ default:
+ assert(0);
+ return CSSMERR_CSP_INTERNAL_ERROR;
+ }
+}
+
+CSSM_RETURN DSAPrivateKeyDecode(
+ DSA *openKey,
+ CSSM_KEYBLOB_FORMAT format,
+ void *p,
+ size_t length)
+{
+ SecNssCoder coder;
+
+ switch(format) {
+ case CSSM_KEYBLOB_RAW_FORMAT_FIPS186:
+ return DSAPrivateKeyDecodeFIPS186(coder, openKey, p, (unsigned)length);
+ case CSSM_KEYBLOB_RAW_FORMAT_OPENSSL:
+ return DSAPrivateKeyDecodeOpenssl(coder, openKey, p, length);
+ case CSSM_KEYBLOB_RAW_FORMAT_PKCS8:
+ return DSAPrivateKeyDecodePKCS8(coder, openKey, p, (unsigned)length);
+ default:
+ assert(0);
+ return CSSMERR_CSP_INTERNAL_ERROR;
+ }
+}
+
+CSSM_RETURN DSAPrivateKeyEncode(
+ DSA *openKey,
+ CSSM_KEYBLOB_FORMAT format,
+ const CssmData &descData,
+ CssmOwnedData &encodedKey)
+{
+ SecNssCoder coder;
+
+ switch(format) {
+ case CSSM_KEYBLOB_RAW_FORMAT_FIPS186:
+ return DSAPrivateKeyEncodeFIPS186(coder, openKey, encodedKey);
+ case CSSM_KEYBLOB_RAW_FORMAT_OPENSSL:
+ return DSAPrivateKeyEncodeOpenssl(coder, openKey, encodedKey);
+ case CSSM_KEYBLOB_RAW_FORMAT_PKCS8:
+ return DSAPrivateKeyEncodePKCS8(coder, openKey, encodedKey);
+ default:
+ assert(0);
+ return CSSMERR_CSP_INTERNAL_ERROR;
+ }
+}
+
+#pragma mark -
+#pragma mark *** DSA Signature encode/decode ***
+
+CSSM_RETURN DSASigEncode(
+ DSA_SIG *openSig,
+ CssmOwnedData &encodedSig)
+{
+ /* temp allocs from this pool */
+ SecNssCoder coder;
+ /* convert to NSS_DSASignature */
+ NSS_DSASignature nssSig;
+
+ try {
+ bnToCssmData(openSig->r, nssSig.r, coder);
+ bnToCssmData(openSig->s, nssSig.s, coder);
+ }
+ catch(...) {
+ /* ? */
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+
+ PRErrorCode prtn = SecNssEncodeItemOdata(&nssSig,
+ kSecAsn1DSASignatureTemplate, encodedSig);
+ if(prtn) {
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+ return CSSM_OK;
+}
+
+CSSM_RETURN DSASigDecode(
+ DSA_SIG *openSig,
+ const void *p,
+ unsigned length)
+{
+ NSS_DSASignature nssSig;
+ SecNssCoder coder;
+
+ memset(&nssSig, 0, sizeof(nssSig));
+ PRErrorCode perr = coder.decode(p, length,
+ kSecAsn1DSASignatureTemplate, &nssSig);
+ if(perr) {
+ logAsnErr("decode(DSASigDecode)", perr);
+ return CSSMERR_CSP_INVALID_SIGNATURE;
+ }
+
+ try {
+ openSig->r = cssmDataToBn(nssSig.r);
+ openSig->s = cssmDataToBn(nssSig.s);
+ }
+ catch(...) {
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+ return 0;
+}
+
+#pragma mark -
+#pragma mark *** DSA Algorithm Parameters encode/decode ***
+
+CSSM_RETURN DSAEncodeAlgParams(
+ NSS_DSAAlgParams &algParams,
+ CssmOwnedData &encodedParams)
+{
+ PRErrorCode prtn = SecNssEncodeItemOdata(&algParams,
+ kSecAsn1DSAAlgParamsTemplate, encodedParams);
+ if(prtn) {
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+ return CSSM_OK;
+}
+
+CSSM_RETURN DSADecodeAlgParams(
+ NSS_DSAAlgParams &algParams,
+ const void *p,
+ unsigned len,
+ SecNssCoder &coder)
+{
+
+ memset(&algParams, 0, sizeof(algParams));
+ PRErrorCode perr = coder.decode(p, len,
+ kSecAsn1DSAAlgParamsTemplate, &algParams);
+ if(perr) {
+ logAsnErr("decode(DSAAlgParams)", perr);
+ return CSSMERR_CSP_INVALID_ATTR_ALG_PARAMS;
+ }
+ return CSSM_OK;
+}
+
+#pragma mark -
+#pragma mark *** Diffie-Hellman key encode/decode ***
+static
+CSSM_RETURN DHPrivateKeyDecodePKCS3(
+ SecNssCoder &coder,
+ DH *openKey,
+ unsigned char *p,
+ unsigned length)
+{
+ NSS_DHPrivateKey nssPrivKey;
+ PRErrorCode perr;
+
+ memset(&nssPrivKey, 0, sizeof(nssPrivKey));
+ perr = coder.decode(p, length, kSecAsn1DHPrivateKeyTemplate, &nssPrivKey);
+ if(perr) {
+ logAsnErr("decode(DHPrivateKey)", perr);
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+
+ /* verify alg identifier */
+ const CSSM_OID *oid = &nssPrivKey.dhOid;
+ if(!cspCompareCssmData(oid, &CSSMOID_DH)) {
+ sslAsn1Debug("DHPrivateKeyDecode: bad OID");
+ return CSSMERR_CSP_ALGID_MISMATCH;
+ }
+
+ NSS_DHParameter ¶ms = nssPrivKey.params;
+
+ try {
+ openKey->priv_key = cssmDataToBn(nssPrivKey.secretPart);
+ openKey->p = cssmDataToBn(params.prime);
+ openKey->g = cssmDataToBn(params.base);
+ /* TBD - ignore privateValueLength for now */
+ }
+ catch(...) {
+ /* FIXME - bad sig? memory? */
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+ return 0;
+}
+
+static
+CSSM_RETURN DHPrivateKeyEncodePKCS3(
+ SecNssCoder &coder,
+ DH *openKey,
+ CssmOwnedData &encodedKey)
+{
+ /* convert into a NSS_DHPrivateKey */
+ NSS_DHPrivateKey nssPrivKey;
+ NSS_DHParameter ¶ms = nssPrivKey.params;
+ memset(&nssPrivKey, 0, sizeof(nssPrivKey));
+ nssPrivKey.dhOid = CSSMOID_DH;
+
+
+ try {
+ bnToCssmData(openKey->priv_key, nssPrivKey.secretPart, coder);
+ bnToCssmData(openKey->p, params.prime, coder);
+ bnToCssmData(openKey->g, params.base, coder);
+ if(openKey->length) {
+ /* actually currently not supported in openssl... */
+ intToCssmData(openKey->length, params.privateValueLength, coder);
+ }
+ }
+ catch(...) {
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+
+ /* DER encode */
+ PRErrorCode perr;
+ perr = SecNssEncodeItemOdata(&nssPrivKey, kSecAsn1DHPrivateKeyTemplate,
+ encodedKey);
+ if(perr) {
+ logAsnErr("encode(DHPrivateKey)", perr);
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+ return CSSM_OK;
+}
+
+/*
+ * NSS_DHAlgorithmIdentifierX942 <--> DH
+ * NOTE this is incomplete. It's functional on decode, but we throw
+ * away everything except p and g. On encode, we put zeroes in
+ * all the fields we don't deal with. Thus the encode side will NOT be
+ * interoperable with other implementations.
+ */
+static void dhToNssAlgIdX942(
+ const DH *openKey,
+ NSS_DHAlgorithmIdentifierX942 &algId,
+ SecNssCoder &coder)
+{
+ /*
+ * When trying to encode a public key in X509 form, we may in
+ * fact have nothing here - public keys created and exported in
+ * PKCS3 have the pub_key value, and that's it.
+ */
+
+ memset(&algId, 0, sizeof(algId));
+ algId.oid = CSSMOID_ANSI_DH_PUB_NUMBER; // not mallocd
+ NSS_DHDomainParamsX942 ¶ms = algId.params;
+ uint8 zero = 0;
+ CSSM_DATA czero = {1, &zero};
+ if(openKey->p != NULL) {
+ bnToCssmData(openKey->p, params.p, coder);
+ }
+ else {
+ coder.allocCopyItem(czero, params.p);
+ }
+ if(openKey->g != NULL) {
+ bnToCssmData(openKey->g, params.g, coder);
+ }
+ else {
+ coder.allocCopyItem(czero, params.g);
+ }
+ /* and we never have a vali0d q */
+ coder.allocCopyItem(czero, params.q);
+
+}
+
+static CSSM_RETURN nssAlgIdToDhX942(
+ NSS_DHAlgorithmIdentifierX942 &algId,
+ DH *openKey)
+{
+ if(!cspCompareCssmData(&algId.oid, &CSSMOID_ANSI_DH_PUB_NUMBER)) {
+ sslAsn1Debug("nssAlgIdToDhX942: bad OID");
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+ openKey->p = cssmDataToBn(algId.params.p);
+ openKey->g = cssmDataToBn(algId.params.g);
+ return CSSM_OK;
+}
+
+static
+CSSM_RETURN DHPrivateKeyDecodePKCS8(
+ SecNssCoder &coder,
+ DH *openKey,
+ unsigned char *p,
+ unsigned length)
+{
+ NSS_DHPrivateKeyPKCS8 nssPrivKey;
+ PRErrorCode perr;
+
+ memset(&nssPrivKey, 0, sizeof(nssPrivKey));
+ perr = coder.decode(p, length, kSecAsn1DHPrivateKeyPKCS8Template,
+ &nssPrivKey);
+ if(perr) {
+ logAsnErr("decode(DHPrivateKeyPKCS8)", perr);
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+
+ try {
+ CSSM_RETURN crtn = nssAlgIdToDhX942(nssPrivKey.algorithm, openKey);
+ if(crtn) {
+ return crtn;
+ }
+
+ /* post-decode private key is a DER encoded integer */
+ CSSM_DATA privKeyInt = {0, NULL};
+ if(coder.decodeItem(nssPrivKey.privateKey,
+ kSecAsn1UnsignedIntegerTemplate,
+ &privKeyInt)) {
+ logAsnErr("decode(DHPrivateKeyPKCS8 privKey int)", perr);
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+
+ openKey->priv_key = cssmDataToBn(privKeyInt);
+ }
+ catch(...) {
+ /* FIXME - bad sig? memory? */
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+ return 0;
+}
+
+static
+CSSM_RETURN DHPrivateKeyEncodePKCS8(
+ SecNssCoder &coder,
+ DH *openKey,
+ CssmOwnedData &encodedKey)
+{
+ /* convert into a NSS_DHPrivateKeyPKCS8 */
+ NSS_DHPrivateKeyPKCS8 nssPrivKey;
+ memset(&nssPrivKey, 0, sizeof(nssPrivKey));
+ uint8 vers = 0;
+ nssPrivKey.version.Length = 1;
+ nssPrivKey.version.Data = &vers;
+ NSS_DHAlgorithmIdentifierX942 &alg = nssPrivKey.algorithm;
+
+ try {
+
+ dhToNssAlgIdX942(openKey, alg, coder);
+ /* pre-encode, nssPrivKey.privateKey is the DER-encoding of
+ * an ASN integer... */
+ CSSM_DATA privKeyInt;
+ bnToCssmData(openKey->priv_key, privKeyInt, coder);
+
+ /* DER encode the privateKey portion into arena pool memory
+ * into nssPrivKey.privateKey */
+ coder.encodeItem(&privKeyInt, kSecAsn1UnsignedIntegerTemplate,
+ nssPrivKey.privateKey);
+ }
+ catch(...) {
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+
+ /* DER encode */
+ PRErrorCode perr;
+ perr = SecNssEncodeItemOdata(&nssPrivKey, kSecAsn1DHPrivateKeyPKCS8Template,
+ encodedKey);
+ if(perr) {
+ logAsnErr("encode(DHPrivateKey)", perr);
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+ return CSSM_OK;
+}
+
+/*
+ * In the PKCS3 form, the public blob is simply the literal
+ * public key value, not DER encoded.
+ */
+static CSSM_RETURN DHPublicKeyDecodePKCS3(
+ DH *openKey,
+ SecNssCoder &coder,
+ unsigned char *p,
+ unsigned length)
+{
+ try {
+ CSSM_DATA pubKey = {(uint32)length, (uint8 *)p};
+ openKey->pub_key = cssmDataToBn(pubKey);
+ return CSSM_OK;
+ }
+ catch(...) {
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+}
+
+static CSSM_RETURN DHPublicKeyEncodePKCS3(
+ DH *openKey,
+ SecNssCoder &coder,
+ CssmOwnedData &encodedKey)
+{
+ try {
+ CSSM_DATA pubKey;
+ bnToCssmData(openKey->pub_key, pubKey, coder);
+ encodedKey.copy(CssmData::overlay(pubKey));
+ return CSSM_OK;
+ }
+ catch(...) {
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+}
+
+static CSSM_RETURN DHPublicKeyDecodeX509(
+ DH *openKey,
+ SecNssCoder &coder,
+ unsigned char *p,
+ unsigned length)
+{
+ NSS_DHPublicKeyX509 nssPubKey;
+ PRErrorCode perr;
+
+ memset(&nssPubKey, 0, sizeof(nssPubKey));
+ perr = coder.decode(p, length, kSecAsn1DHPublicKeyX509Template,
+ &nssPubKey);
+ if(perr) {
+ logAsnErr("decode(DHPublicKeyX509)", perr);
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+
+ try {
+ CSSM_RETURN crtn = nssAlgIdToDhX942(nssPubKey.algorithm, openKey);
+ if(crtn) {
+ return crtn;
+ }
+
+ /*
+ * Post-decode public key length in bits
+ * Contents are pub_key as DER-encoded INTEGER
+ */
+ CSSM_DATA &pubKey = nssPubKey.publicKey;
+ pubKey.Length = (pubKey.Length + 7) / 8;
+ CSSM_DATA pubKeyInt = {0, NULL};
+ if(coder.decodeItem(pubKey,
+ kSecAsn1UnsignedIntegerTemplate, &pubKeyInt)) {
+ logAsnErr("decode(DHPublicKeyX509 pub key int)", perr);
+ return CSSMERR_CSP_INVALID_KEY;
+ }
+ openKey->pub_key = cssmDataToBn(pubKeyInt);
+ }
+ catch(...) {
+ /* FIXME - bad sig? memory? */
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+ return 0;
+}
+
+static CSSM_RETURN DHPublicKeyEncodeX509(
+ DH *openKey,
+ SecNssCoder &coder,
+ CssmOwnedData &encodedKey)
+{
+ /* convert into a NSS_DHPublicKeyX509 */
+ NSS_DHPublicKeyX509 nssPubKey;
+ memset(&nssPubKey, 0, sizeof(nssPubKey));
+ NSS_DHAlgorithmIdentifierX942 &alg = nssPubKey.algorithm;
+
+ try {
+ dhToNssAlgIdX942(openKey, alg, coder);
+
+ /* encode pub_key as integer */
+ CSSM_DATA pubKeyInt = {0, NULL};
+ bnToCssmData(openKey->pub_key, pubKeyInt, coder);
+ coder.encodeItem(&pubKeyInt, kSecAsn1UnsignedIntegerTemplate,
+ nssPubKey.publicKey);
+ /* specify length in bits */
+ nssPubKey.publicKey.Length *= 8;
+ }
+ catch(...) {
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+
+ /* DER encode */
+ PRErrorCode perr;
+ perr = SecNssEncodeItemOdata(&nssPubKey, kSecAsn1DHPublicKeyX509Template,
+ encodedKey);
+ if(perr) {
+ logAsnErr("encode(DHPublicKeyX509)", perr);
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+ return CSSM_OK;
+}
+
+CSSM_RETURN DHPrivateKeyDecode(
+ DH *openKey,
+ CSSM_KEYBLOB_FORMAT format,
+ unsigned char *p,
+ unsigned length)
+{
+ SecNssCoder coder;
+
+ switch(format) {
+ case CSSM_KEYBLOB_RAW_FORMAT_PKCS3:
+ return DHPrivateKeyDecodePKCS3(coder, openKey, p, length);
+ case CSSM_KEYBLOB_RAW_FORMAT_PKCS8:
+ return DHPrivateKeyDecodePKCS8(coder, openKey, p, length);
+ default:
+ assert(0);
+ return CSSMERR_CSP_INTERNAL_ERROR;
+ }
+}
+
+CSSM_RETURN DHPrivateKeyEncode(
+ DH *openKey,
+ CSSM_KEYBLOB_FORMAT format,
+ CssmOwnedData &encodedKey)
+{
+ SecNssCoder coder;
+
+ switch(format) {
+ case CSSM_KEYBLOB_RAW_FORMAT_PKCS3:
+ return DHPrivateKeyEncodePKCS3(coder, openKey, encodedKey);
+ case CSSM_KEYBLOB_RAW_FORMAT_PKCS8:
+ return DHPrivateKeyEncodePKCS8(coder, openKey, encodedKey);
+ default:
+ assert(0);
+ return CSSMERR_CSP_INTERNAL_ERROR;
+ }
+}
+
+CSSM_RETURN DHPublicKeyDecode(
+ DH *openKey,
+ CSSM_KEYBLOB_FORMAT format,
+ unsigned char *p,
+ unsigned length)
+{
+ SecNssCoder coder;
+
+ switch(format) {
+ case CSSM_KEYBLOB_RAW_FORMAT_PKCS3:
+ return DHPublicKeyDecodePKCS3(openKey, coder, p, length);
+ case CSSM_KEYBLOB_RAW_FORMAT_X509:
+ return DHPublicKeyDecodeX509(openKey, coder, p, length);
+ default:
+ assert(0);
+ return CSSMERR_CSP_INTERNAL_ERROR;
+ }
+}
+
+CSSM_RETURN DHPublicKeyEncode(
+ DH *openKey,
+ CSSM_KEYBLOB_FORMAT format,
+ CssmOwnedData &encodedKey)
+{
+ SecNssCoder coder;
+
+ switch(format) {
+ case CSSM_KEYBLOB_RAW_FORMAT_PKCS3:
+ return DHPublicKeyEncodePKCS3(openKey, coder, encodedKey);
+ case CSSM_KEYBLOB_RAW_FORMAT_X509:
+ return DHPublicKeyEncodeX509(openKey, coder, encodedKey);
+ default:
+ assert(0);
+ return CSSMERR_CSP_INTERNAL_ERROR;
+ }
+}
+
+/*
+ * Encode/decode a NSS_DHParameterBlock.
+ */
+CSSM_RETURN DHParamBlockDecode(
+ const CSSM_DATA &encParam,
+ NSS_DHParameterBlock ¶mBlock,
+ SecNssCoder &coder)
+{
+ PRErrorCode perr;
+
+ memset(¶mBlock, 0, sizeof(paramBlock));
+ perr = coder.decodeItem(encParam, kSecAsn1DHParameterBlockTemplate,
+ ¶mBlock);
+ if(perr == 0) {
+ return CSSM_OK;
+ }
+
+ /*
+ * CDSA Extension: the CDSA Algorithm Guide says that the D-H
+ * parameter block is supposed to be wrapped with its accompanying
+ * OID. However Openssl does not do this; it just exports
+ * an encoded DHParameter rather than a DHParameterBlock.
+ * For compatibility we'll try decoding the parameters as one
+ * of these.
+ */
+ memset(¶mBlock, 0, sizeof(paramBlock));
+ perr = coder.decodeItem(encParam, kSecAsn1DHParameterTemplate,
+ ¶mBlock.params);
+ if(perr == 0) {
+ return CSSM_OK;
+ }
+ return CSSMERR_CSP_INVALID_ATTR_ALG_PARAMS;
+}
+
+#pragma mark -
+#pragma mark *** Message Digest ***
+
+/*
+ * Given a message digest and associated algorithm, cook up a PKCS1-style
+ * DigestInfo and return its DER encoding. This is a necessary step for
+ * RSA signature (both generating and verifying) - the output of this
+ * routine is what gets encrypted during signing, and what is expected when
+ * verifying (i.e., decrypting the signature).
+ *
+ * A good guess for the length of the output digestInfo is the size of the
+ * key being used to sign/verify. The digest can never be larger than that.
+ */
+CSSM_RETURN generateDigestInfo(
+ const void *msgDigest,
+ size_t digestLen,
+ CSSM_ALGORITHMS digestAlg, // CSSM_ALGID_SHA1, etc.
+ CssmOwnedData &encodedInfo,
+ size_t maxEncodedSize)
+{
+ if(digestAlg == CSSM_ALGID_NONE) {
+ /* special case, no encode, just copy */
+ encodedInfo.copy(msgDigest, digestLen);
+ return 0;
+ }
+
+ NSS_DigestInfo digestInfo;
+ CSSM_X509_ALGORITHM_IDENTIFIER &algId = digestInfo.digestAlgorithm;
+
+ memset(&digestInfo, 0, sizeof(digestInfo));
+ switch(digestAlg) {
+ case CSSM_ALGID_MD5:
+ algId.algorithm = CSSMOID_MD5;
+ break;
+ case CSSM_ALGID_MD2:
+ algId.algorithm = CSSMOID_MD2;
+ break;
+ case CSSM_ALGID_SHA1:
+ algId.algorithm = CSSMOID_SHA1;
+ break;
+ case CSSM_ALGID_SHA224:
+ algId.algorithm = CSSMOID_SHA224;
+ break;
+ case CSSM_ALGID_SHA256:
+ algId.algorithm = CSSMOID_SHA256;
+ break;
+ case CSSM_ALGID_SHA384:
+ algId.algorithm = CSSMOID_SHA384;
+ break;
+ case CSSM_ALGID_SHA512:
+ algId.algorithm = CSSMOID_SHA512;
+ break;
+ default:
+ return CSSMERR_CSP_INVALID_ALGORITHM;
+ }
+ nullAlgParams(algId);
+ digestInfo.digest.Data = (uint8 *)msgDigest;
+ digestInfo.digest.Length = digestLen;
+
+ /* DER encode */
+ PRErrorCode perr;
+ perr = SecNssEncodeItemOdata(&digestInfo, kSecAsn1DigestInfoTemplate,
+ encodedInfo);
+ if(perr) {
+ logAsnErr("encode(digestInfo)", perr);
+ return CSSMERR_CSP_MEMORY_ERROR;
+ }
+ return CSSM_OK;
+}
+