#include "ckconfig.h"
#if CRYPTKIT_DER_ENABLE
#include <security_cryptkit/CryptKitDER.h>
#include <security_cryptkit/falloc.h>
#include <security_cryptkit/feeDebug.h>
#include <security_cryptkit/feeFunctions.h>
#include <security_cryptkit/ckutilities.h>
#include "CryptKitAsn1.h"
#include <security_asn1/SecNssCoder.h>
#include <security_asn1/nssUtils.h>
#include <Security/keyTemplates.h>
#include <Security/oidsalg.h>
#include <Security/oidsattr.h>
#define PRINT_SIG_GIANTS 0
#define PRINT_CURVE_PARAMS 0
#define PRINT_SIZES 0
#if PRINT_SIZES
#define szprint(s) printf s
#else
#define szprint(s)
#endif
class feeException
{
protected:
feeException(feeReturn frtn, const char *op);
public:
~feeException() throw() {}
feeReturn frtn() const throw() { return mFrtn; }
static void throwMe(feeReturn frtn, const char *op = NULL) __attribute__((noreturn));
private:
feeReturn mFrtn;
};
feeException::feeException(
feeReturn frtn,
const char *op)
: mFrtn(frtn)
{
if(op) {
dbgLog(("%s: %s\n", op, feeReturnString(frtn)));
}
}
void feeException::throwMe(feeReturn frtn, const char *op ) { throw feeException(frtn, op); }
static unsigned feeSizeOfSnaccGiant(
giant g)
{
unsigned rtn = abs(g->sign) * GIANT_BYTES_PER_DIGIT;
szprint(("feeSizeOfSnaccGiant: sign %d size %d\n", g->sign, rtn + 4));
return rtn + 4;
}
unsigned feeSizeOfDERSig(
giant g1,
giant g2)
{
unsigned rtn = feeSizeOfSnaccGiant(g1);
rtn += feeSizeOfSnaccGiant(g2);
szprint(("feeSizeOfDERSig: size %d\n", rtn + 4));
return rtn + 4;
}
static void twosComplement(
unsigned char *bytePtr, unsigned numBytes)
{
unsigned char *outp = bytePtr + numBytes - 1;
unsigned char carry = 1; for(unsigned byteDex=0; byteDex<numBytes; byteDex++) {
*outp = ~*outp + carry;
if(carry && (*outp == 0)) {
carry = 1;
}
else {
carry = 0;
}
outp--;
}
}
static unsigned cssmDataToInt(
const CSSM_DATA &cdata)
{
if((cdata.Length == 0) || (cdata.Data == NULL)) {
return 0;
}
unsigned len = (unsigned)cdata.Length;
if(len > sizeof(int)) {
feeException::throwMe(FR_BadKeyBlob, "cssmDataToInt");
}
unsigned rtn = 0;
uint8 *cp = cdata.Data;
for(unsigned i=0; i<len; i++) {
rtn = (rtn << 8) | *cp++;
}
return rtn;
}
static 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;
}
}
static giant cssmDataToGiant(
const CSSM_DATA &cdata)
{
char *rawOcts = (char *)cdata.Data;
unsigned numBytes = (unsigned)cdata.Length;
unsigned numGiantDigits;
int sign = 1;
giant grtn;
feeReturn frtn = FR_Success;
unsigned char *inp = NULL;
unsigned digitDex;
if((numBytes == 0) || ((numBytes == 1) && rawOcts[0] == 0)) {
grtn = newGiant(1);
if(grtn == NULL) {
feeException::throwMe(FR_Memory, "newGiant(1)");
}
int_to_giant(0, grtn);
return grtn;
}
unsigned char *byteArray = NULL;
bool didMalloc = false;
if(rawOcts[0] & 0x80) {
sign = -1;
numBytes++;
byteArray = (unsigned char *)fmalloc(numBytes);
didMalloc = true;
byteArray[0] = 0xff;
memmove(byteArray + 1, rawOcts, numBytes-1);
twosComplement(byteArray, numBytes);
}
else {
char *foo = rawOcts;
byteArray = (unsigned char *)foo;
}
numGiantDigits = (numBytes + GIANT_BYTES_PER_DIGIT - 1) /
GIANT_BYTES_PER_DIGIT;
grtn = newGiant(numGiantDigits);
if(grtn == NULL) {
frtn = FR_Memory;
goto abort;
}
digitDex = 0; giantDigit thisDigit;
inp = byteArray + numBytes - 1;
unsigned dex; unsigned byteDex; unsigned shiftCount;
for(dex=0; dex<numBytes; ) { thisDigit = 0;
shiftCount = 0;
for(byteDex=0; byteDex<GIANT_BYTES_PER_DIGIT; byteDex++) {
thisDigit |= ((giantDigit)(*inp--) << shiftCount);
shiftCount += 8;
if(++dex == numBytes) {
break;
}
}
CKASSERT(digitDex < numGiantDigits);
grtn->n[digitDex++] = thisDigit;
}
grtn->sign = (int)numGiantDigits * sign;
gtrimSign(grtn);
abort:
if(didMalloc) {
ffree(byteArray);
}
if(frtn) {
feeException::throwMe(frtn, "bigIntStrToGiant");
}
return grtn;
}
static void giantToCssmData(
giant g,
CSSM_DATA &cdata,
SecNssCoder &coder)
{
unsigned char doPrepend = 0;
unsigned numGiantDigits = abs(g->sign);
unsigned numBytes = numGiantDigits * GIANT_BYTES_PER_DIGIT;
giantDigit msGiantBit = 0;
if(isZero(g)) {
intToCssmData(0, cdata, coder);
return;
}
else {
msGiantBit = g->n[numGiantDigits - 1] >> (GIANT_BITS_PER_DIGIT - 1);
}
if((g->sign < 0) || ((g->sign > 0) && msGiantBit)) { doPrepend = 1;
numBytes++;
}
unsigned char *rawBytes = (unsigned char *)fmalloc(numBytes);
if(rawBytes == NULL) {
feeException::throwMe(FR_Memory, "giantToCssmData fmalloc(rawBytes)");
}
unsigned char *outp = rawBytes;
if(doPrepend) {
*outp++ = 0;
}
int digitDex; unsigned byteDex; for(digitDex=numGiantDigits-1; digitDex>=0; digitDex--) {
giantDigit thisDigit = g->n[digitDex];
unsigned char *bp = outp + GIANT_BYTES_PER_DIGIT - 1;
for(byteDex=0; byteDex<GIANT_BYTES_PER_DIGIT; byteDex++) {
*bp-- = (unsigned char)(thisDigit) & 0xff;
thisDigit >>= 8;
}
outp += GIANT_BYTES_PER_DIGIT;
}
if(g->sign < 0) {
twosComplement(rawBytes, numBytes);
}
outp = rawBytes;
unsigned char *endp = outp + numBytes - 1;
while((*outp == 0) && (outp < endp) && (!(outp[1] & 0x80))) { outp++;
numBytes--;
}
while((*outp == 0xff) && (outp < endp) && (outp[1] & 0x80)) { outp++;
numBytes--;
}
cdata.Data = (uint8 *)coder.malloc(numBytes);
memmove(cdata.Data, outp, numBytes);
cdata.Length = numBytes;
ffree(rawBytes);
return;
}
static void feeCurveParamsToASN1(
const curveParams *cp,
FEECurveParametersASN1 &asnCp,
SecNssCoder &coder)
{
#if PRINT_CURVE_PARAMS
printf("===encoding curveParams; cp:\n"); printCurveParams(cp);
#endif
memset(&asnCp, 0, sizeof(asnCp));
try {
intToCssmData(cp->primeType, asnCp.primeType, coder);
intToCssmData(cp->curveType, asnCp.curveType, coder);
intToCssmData(cp->q, asnCp.q, coder);
intToCssmData(cp->k, asnCp.k, coder);
intToCssmData(cp->m, asnCp.m, coder);
giantToCssmData(cp->a, asnCp.a, coder);
giantToCssmData(cp->b, asnCp.b_, coder);
giantToCssmData(cp->c, asnCp.c, coder);
giantToCssmData(cp->x1Plus, asnCp.x1Plus, coder);
giantToCssmData(cp->x1Minus, asnCp.x1Minus, coder);
giantToCssmData(cp->cOrderPlus, asnCp.cOrderPlus, coder);
giantToCssmData(cp->cOrderMinus, asnCp.cOrderMinus, coder);
giantToCssmData(cp->x1OrderPlus, asnCp.x1OrderPlus, coder);
giantToCssmData(cp->x1OrderMinus, asnCp.x1OrderMinus, coder);
if(cp->primeType == FPT_General) {
giantToCssmData(cp->basePrime, asnCp.basePrime, coder);
}
}
catch(const feeException &ferr) {
throw;
}
catch(...) {
feeException::throwMe(FR_Memory, "feeCurveParamsToSnacc catchall"); }
}
static curveParams *feeCurveParamsFromAsn1(
const FEECurveParametersASN1 &asnCp)
{
curveParams *cp = newCurveParams();
if(cp == NULL) {
feeException::throwMe(FR_Memory, "feeCurveParamsFromSnacc alloc cp");
}
cp->primeType = (feePrimeType)cssmDataToInt(asnCp.primeType);
cp->curveType = (feeCurveType)cssmDataToInt(asnCp.curveType);
cp->q = cssmDataToInt(asnCp.q);
cp->k = cssmDataToInt(asnCp.k);
cp->m = cssmDataToInt(asnCp.m);
cp->a = cssmDataToGiant(asnCp.a);
cp->b = cssmDataToGiant(asnCp.b_);
cp->c = cssmDataToGiant(asnCp.c);
cp->x1Plus = cssmDataToGiant(asnCp.x1Plus);
cp->x1Minus = cssmDataToGiant(asnCp.x1Minus);
cp->cOrderPlus = cssmDataToGiant(asnCp.cOrderPlus);
cp->cOrderMinus = cssmDataToGiant(asnCp.cOrderMinus);
cp->x1OrderPlus = cssmDataToGiant(asnCp.x1OrderPlus);
cp->x1OrderMinus = cssmDataToGiant(asnCp.x1OrderMinus);
if(asnCp.basePrime.Data != NULL) {
cp->basePrime = cssmDataToGiant(asnCp.basePrime);
}
curveParamsInferFields(cp);
allocRecipGiants(cp);
#if PRINT_CURVE_PARAMS
printf("===decoding curveParams; cp:\n"); printCurveParams(cp);
#endif
return cp;
}
feeReturn feeDEREncodeElGamalSignature(
giant u,
giant PmX,
unsigned char **encodedSig, unsigned *encodedSigLen) {
FEEElGamalSignatureASN1 asnSig;
SecNssCoder coder;
try {
giantToCssmData(u, asnSig.u, coder);
giantToCssmData(PmX, asnSig.pmX, coder);
}
catch(const feeException &ferr) {
return ferr.frtn();
}
PRErrorCode perr;
CSSM_DATA encBlob; perr = coder.encodeItem(&asnSig, FEEElGamalSignatureASN1Template, encBlob);
if(perr) {
return FR_Memory;
}
*encodedSig = (unsigned char *)fmalloc((unsigned)encBlob.Length);
*encodedSigLen = (unsigned)encBlob.Length;
memmove(*encodedSig, encBlob.Data, encBlob.Length);
#if PRINT_SIG_GIANTS
printf("feeEncodeElGamalSignature:\n");
printf(" u : "); printGiantHex(u);
printf(" PmX : "); printGiantHex(PmX);
#endif
return FR_Success;
}
feeReturn feeDEREncodeECDSASignature(
giant c,
giant d,
unsigned char **encodedSig, unsigned *encodedSigLen) {
FEEECDSASignatureASN1 asnSig;
SecNssCoder coder;
try {
giantToCssmData(c, asnSig.c, coder);
giantToCssmData(d, asnSig.d, coder);
}
catch(const feeException &ferr) {
return ferr.frtn();
}
PRErrorCode perr;
CSSM_DATA encBlob; perr = coder.encodeItem(&asnSig, FEEECDSASignatureASN1Template, encBlob);
if(perr) {
return FR_Memory;
}
*encodedSig = (unsigned char *)fmalloc((unsigned)encBlob.Length);
*encodedSigLen = (unsigned)encBlob.Length;
memmove(*encodedSig, encBlob.Data, encBlob.Length);
#if PRINT_SIG_GIANTS
printf("feeDEREncodeECDSASignature:\n");
printf(" c : "); printGiantHex(c);
printf(" d : "); printGiantHex(d);
#endif
return FR_Success;
}
#if PRINT_SIG_GIANTS
static void printHex(
const unsigned char *buf,
unsigned len,
unsigned maxLen)
{
bool doEllipsis = false;
unsigned dex;
if(len > maxLen) {
len = maxLen;
doEllipsis = true;
}
for(dex=0; dex<len; dex++) {
printf("%02X ", *buf++);
}
if(doEllipsis) {
printf("...etc.");
}
}
#endif
feeReturn feeRAWEncodeECDSASignature(unsigned groupBytesLen,
giant c,
giant d,
unsigned char **encodedSig, unsigned *encodedSigLen) {
*encodedSig = (unsigned char *)fmalloc(2*groupBytesLen);
*encodedSigLen = (unsigned)2*groupBytesLen;
try {
serializeGiant(c, *encodedSig, groupBytesLen);
serializeGiant(d, *encodedSig+groupBytesLen, groupBytesLen);
}
catch(const feeException &ferr) {
return ferr.frtn();
}
#if PRINT_SIG_GIANTS
printf("feeRAWEncodeECDSASignature:\n");
printf(" c : "); printGiantHex(c);
printf(" d : "); printGiantHex(d);
printf(" sig : "); printHex(*encodedSig,*encodedSigLen,512);
#endif
return FR_Success;
}
feeReturn feeDERDecodeElGamalSignature(
const unsigned char *encodedSig,
size_t encodedSigLen,
giant *u, giant *PmX) {
FEEElGamalSignatureASN1 asnSig;
SecNssCoder coder;
memset(&asnSig, 0, sizeof(asnSig));
PRErrorCode perr = coder.decode(encodedSig, encodedSigLen,
FEEElGamalSignatureASN1Template, &asnSig);
if(perr) {
return FR_BadSignatureFormat;
}
try {
*u = cssmDataToGiant(asnSig.u);
*PmX = cssmDataToGiant(asnSig.pmX);
}
catch(const feeException &ferr) {
return ferr.frtn();
}
catch(...) {
return FR_Memory;
}
#if PRINT_SIG_GIANTS
printf("feeDecodeElGamalSignature:\n");
printf(" u : "); printGiantHex(*u);
printf(" PmX : "); printGiantHex(*PmX);
#endif
return FR_Success;
}
feeReturn feeDERDecodeECDSASignature(
const unsigned char *encodedSig,
size_t encodedSigLen,
giant *c, giant *d) {
FEEECDSASignatureASN1 asnSig;
SecNssCoder coder;
memset(&asnSig, 0, sizeof(asnSig));
PRErrorCode perr = coder.decode(encodedSig, encodedSigLen,
FEEECDSASignatureASN1Template, &asnSig);
if(perr) {
return FR_BadSignatureFormat;
}
try {
*c = cssmDataToGiant(asnSig.c);
*d = cssmDataToGiant(asnSig.d);
}
catch(const feeException &ferr) {
return ferr.frtn();
}
catch(...) {
return FR_Memory;
}
#if PRINT_SIG_GIANTS
printf("feeDERDecodeECDSASignature:\n");
printf(" c : "); printGiantHex(*c);
printf(" d : "); printGiantHex(*d);
#endif
return FR_Success;
}
feeReturn feeRAWDecodeECDSASignature(unsigned groupBytesLen,
const unsigned char *encodedSig,
size_t encodedSigLen,
giant *c, giant *d) {
if (((encodedSigLen & 1) == 1) || (groupBytesLen != (encodedSigLen>>1))) {
return FR_BadSignatureFormat;
}
try {
*c = giant_with_data((uint8_t*)encodedSig,(int)groupBytesLen);
*d = giant_with_data((uint8_t*)encodedSig+groupBytesLen, (int)groupBytesLen);
}
catch(const feeException &ferr) {
return ferr.frtn();
}
catch(...) {
return FR_Memory;
}
#if PRINT_SIG_GIANTS
printf("feeRAWDecodeECDSASignature:\n");
printf(" c : "); printGiantHex(*c);
printf(" d : "); printGiantHex(*d);
#endif
return FR_Success;
}
feeReturn feeDEREncodePublicKey(
int version,
const curveParams *cp,
giant plusX,
giant minusX,
giant plusY, unsigned char **keyBlob, unsigned *keyBlobLen) {
FEEPublicKeyASN1 asnKey;
SecNssCoder coder;
memset(&asnKey, 0, sizeof(asnKey));
intToCssmData(version, asnKey.version, coder);
try {
feeCurveParamsToASN1(cp, asnKey.curveParams, coder);
giantToCssmData(plusX, asnKey.plusX, coder);
giantToCssmData(minusX, asnKey.minusX, coder);
if(plusY != NULL) {
giantToCssmData(plusY, asnKey.plusY, coder);
}
}
catch(const feeException &ferr) {
return ferr.frtn();
}
PRErrorCode perr;
CSSM_DATA encBlob; perr = coder.encodeItem(&asnKey, FEEPublicKeyASN1Template, encBlob);
if(perr) {
return FR_Memory;
}
*keyBlob = (unsigned char *)fmalloc((unsigned)encBlob.Length);
*keyBlobLen = (unsigned)encBlob.Length;
memmove(*keyBlob, encBlob.Data, encBlob.Length);
return FR_Success;
}
feeReturn feeDEREncodePrivateKey(
int version,
const curveParams *cp,
const giant privData,
unsigned char **keyBlob, unsigned *keyBlobLen) {
FEEPrivateKeyASN1 asnKey;
SecNssCoder coder;
memset(&asnKey, 0, sizeof(asnKey));
intToCssmData(version, asnKey.version, coder);
try {
feeCurveParamsToASN1(cp, asnKey.curveParams, coder);
giantToCssmData(privData, asnKey.privData, coder);
}
catch(const feeException &ferr) {
return ferr.frtn();
}
PRErrorCode perr;
CSSM_DATA encBlob; perr = coder.encodeItem(&asnKey, FEEPrivateKeyASN1Template, encBlob);
if(perr) {
return FR_Memory;
}
*keyBlob = (unsigned char *)fmalloc((unsigned)encBlob.Length);
*keyBlobLen = (unsigned)encBlob.Length;
memmove(*keyBlob, encBlob.Data, encBlob.Length);
return FR_Success;
}
feeReturn feeDERDecodePublicKey(
const unsigned char *keyBlob,
unsigned keyBlobLen,
int *version, curveParams **cp,
giant *plusX,
giant *minusX,
giant *plusY) {
FEEPublicKeyASN1 asnKey;
SecNssCoder coder;
memset(&asnKey, 0, sizeof(asnKey));
PRErrorCode perr = coder.decode(keyBlob, keyBlobLen,
FEEPublicKeyASN1Template, &asnKey);
if(perr) {
return FR_BadKeyBlob;
}
try {
*version = cssmDataToInt(asnKey.version);
*cp = feeCurveParamsFromAsn1(asnKey.curveParams);
*plusX = cssmDataToGiant(asnKey.plusX);
*minusX = cssmDataToGiant(asnKey.minusX);
if(asnKey.plusY.Data != NULL) {
*plusY = cssmDataToGiant(asnKey.plusY);
}
else {
*plusY = newGiant(1);
int_to_giant(0, *plusY);
}
}
catch(const feeException &ferr) {
return ferr.frtn();
}
catch(...) {
return FR_Memory;
}
return FR_Success;
}
feeReturn feeDERDecodePrivateKey(
const unsigned char *keyBlob,
unsigned keyBlobLen,
int *version, curveParams **cp,
giant *privData) {
FEEPrivateKeyASN1 asnKey;
SecNssCoder coder;
memset(&asnKey, 0, sizeof(asnKey));
PRErrorCode perr = coder.decode(keyBlob, keyBlobLen,
FEEPrivateKeyASN1Template, &asnKey);
if(perr) {
return FR_BadKeyBlob;
}
try {
*version = cssmDataToInt(asnKey.version);
*cp = feeCurveParamsFromAsn1(asnKey.curveParams);
*privData = cssmDataToGiant(asnKey.privData);
}
catch(const feeException &ferr) {
return ferr.frtn();
}
catch(...) {
return FR_Memory;
}
return FR_Success;
}
#pragma mark --- ECDSA support ---
static const CSSM_OID *depthToOid(
feeDepth depth)
{
switch(depth) {
case FEE_DEPTH_secp192r1:
return &CSSMOID_secp192r1;
case FEE_DEPTH_secp256r1:
return &CSSMOID_secp256r1;
case FEE_DEPTH_secp384r1:
return &CSSMOID_secp384r1;
case FEE_DEPTH_secp521r1:
return &CSSMOID_secp521r1;
default:
dbgLog(("depthToOid needs work\n"));
return NULL;
}
}
static feeReturn curveOidToFeeDepth(
const CSSM_OID *curveOid,
feeDepth *depth)
{
if(nssCompareCssmData(curveOid, &CSSMOID_secp192r1)) {
*depth = FEE_DEPTH_secp192r1;
}
else if(nssCompareCssmData(curveOid, &CSSMOID_secp256r1)) {
*depth = FEE_DEPTH_secp256r1;
}
else if(nssCompareCssmData(curveOid, &CSSMOID_secp384r1)) {
*depth = FEE_DEPTH_secp384r1;
}
else if(nssCompareCssmData(curveOid, &CSSMOID_secp521r1)) {
*depth = FEE_DEPTH_secp521r1;
}
else {
dbgLog(("curveOidToFeeDepth: unknown curve OID\n"));
return FR_BadKeyBlob;
}
return FR_Success;
}
static feeReturn feeAlgIdToDepth(
const CSSM_X509_ALGORITHM_IDENTIFIER *algId,
feeDepth *depth)
{
const CSSM_OID *oid = &algId->algorithm;
if(!nssCompareCssmData(oid, &CSSMOID_ecPublicKey)) {
dbgLog(("feeAlgIdToDepth: bad OID"));
return FR_BadKeyBlob;
}
const CSSM_DATA *param = &algId->parameters;
if((param->Length <= 2) || (param->Data[0] != BER_TAG_OID)) {
dbgLog(("feeAlgIdToDepth: no curve params\n"));
return FR_BadKeyBlob;
}
CSSM_OID decOid = {param->Length-2, algId->parameters.Data+2};
return curveOidToFeeDepth(&decOid, depth);
}
static feeReturn feeSetupAlgId(
feeDepth depth,
SecNssCoder &coder,
CSSM_X509_ALGORITHM_IDENTIFIER &algId)
{
algId.algorithm = CSSMOID_ecPublicKey;
const CSSM_OID *curveOid = depthToOid(depth);
if(curveOid == NULL) {
return FR_IllegalDepth;
}
coder.allocItem(algId.parameters, curveOid->Length + 2);
algId.parameters.Data[0] = BER_TAG_OID;
algId.parameters.Data[1] = curveOid->Length;
memmove(algId.parameters.Data+2, curveOid->Data, curveOid->Length);
return FR_Success;
}
#pragma mark --- ECDSA public key, X.509 format ---
feeReturn feeDEREncodeX509PublicKey(
const unsigned char *pubBlob,
unsigned pubBlobLen,
curveParams *cp,
unsigned char **x509Blob,
unsigned *x509BlobLen)
{
SecNssCoder coder;
CSSM_X509_SUBJECT_PUBLIC_KEY_INFO nssPubKeyInfo;
memset(&nssPubKeyInfo, 0, sizeof(nssPubKeyInfo));
nssPubKeyInfo.subjectPublicKey.Data = (uint8 *)pubBlob;
nssPubKeyInfo.subjectPublicKey.Length = pubBlobLen * 8;
feeDepth depth;
feeReturn frtn = curveParamsDepth(cp, &depth);
if(frtn) {
dbgLog(("feeDEREncodePKCS8PrivateKey: curveParamsDepth error\n"));
return frtn;
}
CSSM_X509_ALGORITHM_IDENTIFIER &algId = nssPubKeyInfo.algorithm;
frtn = feeSetupAlgId(depth, coder, algId);
if(frtn) {
return frtn;
}
CSSM_DATA encBlob; PRErrorCode perr = coder.encodeItem(&nssPubKeyInfo, kSecAsn1SubjectPublicKeyInfoTemplate, encBlob);
if(perr) {
return FR_Memory;
}
*x509Blob = (unsigned char *)fmalloc((unsigned)encBlob.Length);
*x509BlobLen = (unsigned)encBlob.Length;
memmove(*x509Blob, encBlob.Data, encBlob.Length);
return FR_Success;
}
feeReturn feeDERDecodeX509PublicKey(
const unsigned char *x509Blob,
unsigned x509BlobLen,
feeDepth *depth,
unsigned char **pubBlob,
unsigned *pubBlobLen)
{
SecNssCoder coder;
CSSM_X509_SUBJECT_PUBLIC_KEY_INFO nssPubKeyInfo;
PRErrorCode perr;
memset(&nssPubKeyInfo, 0, sizeof(nssPubKeyInfo));
perr = coder.decode(x509Blob, x509BlobLen, kSecAsn1SubjectPublicKeyInfoTemplate,
&nssPubKeyInfo);
if(perr) {
dbgLog(("decode(SubjectPublicKeyInfo) error"));
return FR_BadKeyBlob;
}
feeReturn frtn = feeAlgIdToDepth(&nssPubKeyInfo.algorithm, depth);
if(frtn) {
return frtn;
}
CSSM_DATA *pubKey = &nssPubKeyInfo.subjectPublicKey;
unsigned keyLen =(unsigned) (pubKey->Length + 7) / 8;
*pubBlob = (unsigned char *)fmalloc(keyLen);
if(*pubBlob == NULL) {
return FR_Memory;
}
memmove(*pubBlob, pubKey->Data, keyLen);
*pubBlobLen = keyLen;
return FR_Success;
}
#pragma mark --- ECDSA keys, OpenSSL format ---
feeReturn feeDEREncodeOpenSSLPrivateKey(
const unsigned char *privBlob,
unsigned privBlobLen,
const unsigned char *pubBlob,
unsigned pubBlobLen,
curveParams *cp,
unsigned char **openBlob,
unsigned *openBlobLen)
{
feeDepth depth;
const CSSM_OID *curveOid;
SecNssCoder coder;
NSS_ECDSA_PrivateKey ecdsaPrivKey;
memset(&ecdsaPrivKey, 0, sizeof(ecdsaPrivKey));
uint8 vers = 1;
ecdsaPrivKey.version.Data = &vers;
ecdsaPrivKey.version.Length = 1;
ecdsaPrivKey.privateKey.Data = (uint8 *)privBlob;
ecdsaPrivKey.privateKey.Length = privBlobLen;
if(curveParamsDepth(cp, &depth)) {
dbgLog(("feeDEREncodeOpenSSLPrivateKey: bad depth"));
return FR_BadKeyBlob;
}
curveOid = depthToOid(depth);
if(curveOid == NULL) {
return FR_BadKeyBlob;
}
try {
coder.allocItem(ecdsaPrivKey.params, curveOid->Length + 2);
}
catch(...) {
return FR_Memory;
}
ecdsaPrivKey.params.Data[0] = BER_TAG_OID;
ecdsaPrivKey.params.Data[1] = curveOid->Length;
memmove(ecdsaPrivKey.params.Data+2, curveOid->Data, curveOid->Length);
if(pubBlob) {
ecdsaPrivKey.pubKey.Data = (uint8 *)pubBlob;
ecdsaPrivKey.pubKey.Length = pubBlobLen * 8;
}
CSSM_DATA encPriv = {0, NULL};
PRErrorCode perr = coder.encodeItem(&ecdsaPrivKey, kSecAsn1ECDSAPrivateKeyInfoTemplate, encPriv);
if(perr) {
return FR_Memory;
}
*openBlob = (unsigned char *)fmalloc((unsigned)encPriv.Length);
*openBlobLen = (unsigned)encPriv.Length;
memmove(*openBlob, encPriv.Data, encPriv.Length);
return FR_Success;
}
feeReturn feeDERDecodeOpenSSLKey(
const unsigned char *osBlob,
unsigned osBlobLen,
feeDepth *depth,
unsigned char **privBlob,
unsigned *privBlobLen,
unsigned char **pubBlob,
unsigned *pubBlobLen)
{
SecNssCoder coder;
NSS_ECDSA_PrivateKey ecdsaPrivKey;
memset(&ecdsaPrivKey, 0, sizeof(ecdsaPrivKey));
if(coder.decode(osBlob, osBlobLen,
kSecAsn1ECDSAPrivateKeyInfoTemplate, &ecdsaPrivKey)) {
dbgLog(("Error decoding openssl priv key\n"));
return FR_BadKeyBlob;
}
unsigned keyLen = (unsigned)ecdsaPrivKey.privateKey.Length;
if(keyLen == 0) {
dbgLog(("NULL priv key data in PKCS8\n"));
}
*privBlob = (unsigned char *)fmalloc(keyLen);
if(*privBlob == NULL) {
return FR_Memory;
}
*privBlobLen = keyLen;
memmove(*privBlob, ecdsaPrivKey.privateKey.Data, keyLen);
if(ecdsaPrivKey.params.Data != NULL) {
const CSSM_DATA *param = &ecdsaPrivKey.params;
if((param->Data[0] != BER_TAG_OID) || (param->Length <= 2)) {
dbgLog(("feeDERDecodeOpenSSLKey: bad curve params\n"));
return FR_BadKeyBlob;
}
CSSM_OID decOid = {param->Length-2, param->Data+2};
if(curveOidToFeeDepth(&decOid, depth)) {
return FR_BadKeyBlob;
}
}
if((ecdsaPrivKey.pubKey.Length != 0) && (pubBlob != NULL)) {
*pubBlobLen = (unsigned)(ecdsaPrivKey.pubKey.Length + 7) / 8;
*pubBlob = (unsigned char *)fmalloc(*pubBlobLen);
memmove(*pubBlob, ecdsaPrivKey.pubKey.Data, *pubBlobLen);
}
return FR_Success;
}
#pragma mark --- ECDSA public key, PKCS8 format ---
feeReturn feeDEREncodePKCS8PrivateKey(
const unsigned char *privBlob,
unsigned privBlobLen,
const unsigned char *pubBlob,
unsigned pubBlobLen,
curveParams *cp,
unsigned char **pkcs8Blob,
unsigned *pkcs8BlobLen)
{
unsigned char *encPriv = NULL;
unsigned encPrivLen = 0;
feeReturn frtn = feeDEREncodeOpenSSLPrivateKey(privBlob, privBlobLen,
pubBlob, pubBlobLen, cp, &encPriv, &encPrivLen);
if(frtn) {
return frtn;
}
SecNssCoder coder;
NSS_PrivateKeyInfo nssPrivKeyInfo;
CSSM_X509_ALGORITHM_IDENTIFIER &algId = nssPrivKeyInfo.algorithm;
memset(&nssPrivKeyInfo, 0, sizeof(nssPrivKeyInfo));
nssPrivKeyInfo.privateKey.Data = (uint8 *)encPriv;
nssPrivKeyInfo.privateKey.Length = encPrivLen;
uint8 vers = 0;
feeDepth depth;
frtn = curveParamsDepth(cp, &depth);
if(frtn) {
dbgLog(("feeDEREncodePKCS8PrivateKey: curveParamsDepth error\n"));
goto errOut;
}
frtn = feeSetupAlgId(depth, coder, algId);
if(frtn) {
goto errOut;
}
nssPrivKeyInfo.version.Data = &vers;
nssPrivKeyInfo.version.Length = 1;
CSSM_DATA encPrivInfo; if(coder.encodeItem(&nssPrivKeyInfo, kSecAsn1PrivateKeyInfoTemplate, encPrivInfo)) {
frtn = FR_Memory;
goto errOut;
}
*pkcs8Blob = (unsigned char *)fmalloc((unsigned)encPrivInfo.Length);
*pkcs8BlobLen = (unsigned)encPrivInfo.Length;
memmove(*pkcs8Blob, encPrivInfo.Data, encPrivInfo.Length);
errOut:
if(encPriv) {
ffree(encPriv);
}
return frtn;
}
feeReturn feeDERDecodePKCS8PrivateKey(
const unsigned char *pkcs8Blob,
unsigned pkcs8BlobLen,
feeDepth *depth,
unsigned char **privBlob,
unsigned *privBlobLen,
unsigned char **pubBlob,
unsigned *pubBlobLen)
{
NSS_PrivateKeyInfo nssPrivKeyInfo;
PRErrorCode perr;
SecNssCoder coder;
memset(&nssPrivKeyInfo, 0, sizeof(nssPrivKeyInfo));
perr = coder.decode(pkcs8Blob, pkcs8BlobLen, kSecAsn1PrivateKeyInfoTemplate, &nssPrivKeyInfo);
if(perr) {
dbgLog(("Error decoding top level PKCS8\n"));
return FR_BadKeyBlob;
}
feeReturn frtn = feeAlgIdToDepth(&nssPrivKeyInfo.algorithm, depth);
if(frtn) {
return frtn;
}
frtn = feeDERDecodeOpenSSLKey((const unsigned char *)nssPrivKeyInfo.privateKey.Data,
(unsigned)nssPrivKeyInfo.privateKey.Length, depth,
privBlob, privBlobLen,
pubBlob, pubBlobLen);
return frtn;
}
#endif