#include "SnaccUtils.h"
#include "cldebugging.h"
#include <Security/pkcs1oids.h>
#include <Security/cdsaUtils.h>
#include <Security/cssmapple.h>
#include <Security/appleoids.h>
#include <Security/globalizer.h>
#define DEBUG_DECODE 0
#if DEBUG_DECODE
#define ddprintf(x) printf x
#else
#define ddprintf(x)
#endif
class AlgOidCache
{
public:
AlgOidCache() :
mRsaEncryption(rsaEncryption_arc),
mMd2WithRSAEncryption(md2WithRSAEncryption_arc),
mMd5WithRSAEncryption(md5WithRSAEncryption_arc),
mSha1withRSAEncryption(sha1withRSAEncryption_arc),
mId_dsa(id_dsa_arc),
mId_dsa_with_sha1(id_dsa_with_sha1_arc),
mAppleFee(appleFee_arc),
mAppleAsc(appleAsc_arc),
mAppleFeeMD5(appleFeeMD5_arc),
mAppleFeeSHA1(appleFeeSHA1_arc),
mAppleFeed(appleFeed_arc),
mAppleFeedExp(appleFeedExp_arc),
mAppleECDSA(appleECDSA_arc)
{ }
AsnOid mRsaEncryption;
AsnOid mMd2WithRSAEncryption;
AsnOid mMd5WithRSAEncryption;
AsnOid mSha1withRSAEncryption;
AsnOid mId_dsa;
AsnOid mId_dsa_with_sha1;
AsnOid mAppleFee;
AsnOid mAppleAsc;
AsnOid mAppleFeeMD5;
AsnOid mAppleFeeSHA1;
AsnOid mAppleFeed;
AsnOid mAppleFeedExp;
AsnOid mAppleECDSA;
};
static ModuleNexus<AlgOidCache> algOidCache;
void
CL_certDecodeComponents(
const CssmData &signedCert, CssmOwnedData &TBSCert, CssmOwnedData &algId, CssmOwnedData &rawSig) {
CssmAutoData encodedSig(rawSig.allocator);
AsnBuf buf;
buf.InstallData(reinterpret_cast<char *>(signedCert.data()), signedCert.length());
AsnTag tag;
AsnLen bytesDecoded = 0;
AsnLen decLen; AsnLen totalLen; char *elemStart;
int rtn;
ENV_TYPE env;
if ((rtn = setjmp (env)) == 0) {
tag = BDecTag (buf, bytesDecoded, env);
if (tag != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE)) {
errorLog1("CL_CertDecodeComponents: bad first-level tag (0x%x)\n", tag);
CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
}
decLen = BDecLen (buf, bytesDecoded, env);
elemStart = buf.DataPtr() + bytesDecoded;
tag = BDecTag (buf, bytesDecoded, env);
if(tag != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE)) {
errorLog1("CL_CertDecodeComponents: bad TBSCert tag (0x%x)\n", tag);
CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
}
decLen = BDecLen (buf, bytesDecoded, env);
totalLen = decLen + (bytesDecoded - (elemStart - buf.DataPtr()));
buf.Skip(decLen);
bytesDecoded += decLen;
TBSCert.copy(elemStart, totalLen);
ddprintf(("CL_certDecodeComponents: TBS len %d; data %02x %02x %02x %02x...\n",
totalLen, ((uint8 *)elemStart)[0], ((uint8 *)elemStart)[1],
((uint8 *)elemStart)[2], ((uint8 *)elemStart)[3]));
elemStart = buf.DataPtr() + bytesDecoded;
tag = BDecTag (buf, bytesDecoded, env);
if(tag != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE)) {
errorLog1("CL_CertDecodeComponents: bad AlgId tag (0x%x)\n", tag);
CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
}
decLen = BDecLen (buf, bytesDecoded, env);
totalLen = decLen + (bytesDecoded - (elemStart - buf.DataPtr()));
buf.Skip(decLen);
bytesDecoded += decLen;
algId.copy(elemStart, totalLen);
ddprintf(("CL_certDecodeComponents: algId len %d; data %02x %02x %02x...\n",
totalLen, ((uint8 *)elemStart)[0], ((uint8 *)elemStart)[1],
((uint8 *)elemStart)[2]));
elemStart = buf.DataPtr() + bytesDecoded;
tag = BDecTag (buf, bytesDecoded, env);
if((tag != MAKE_TAG_ID (UNIV, CONS, BITSTRING_TAG_CODE)) &&
(tag != MAKE_TAG_ID (UNIV, PRIM, BITSTRING_TAG_CODE))) {
errorLog1("CL_CertDecodeComponents: bad sig tag 0x%x\n", tag);
CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
}
decLen = BDecLen (buf, bytesDecoded, env);
totalLen = decLen + (bytesDecoded - (elemStart - buf.DataPtr()));
encodedSig.copy(elemStart, totalLen);
ddprintf(("CL_certDecodeComponents: encodedSig len %d; data %02x %02x "
"%02x %02x...\n",
totalLen, ((uint8 *)elemStart)[0], ((uint8 *)elemStart)[1],
((uint8 *)elemStart)[2], ((uint8 *)elemStart)[3]));
SC_decodeAsnBitsToCssmData(encodedSig.get(), rawSig);
ddprintf(("CL_certDecodeComponents: rawSig len %d\n", rawSig.length()));
}
else {
errorLog0("CL_CertDecodeComponents: longjmp during decode\n");
TBSCert.reset();
algId.reset();
rawSig.reset();
CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
}
}
void
CL_certEncodeComponents(
const CssmData &TBSCert, const CssmData &algId, const CssmData &rawSig, CssmOwnedData &signedCert) {
AsnBits snaccSig(reinterpret_cast<char *>(rawSig.data()),
rawSig.length() * 8);
CssmAutoData encodedSig(signedCert.allocator);
SC_encodeAsnObj(snaccSig, encodedSig, rawSig.length() + 10);
size_t contentLen = TBSCert.length() + algId.length() + encodedSig.length();
size_t lenLen = SC_lengthOfLength(contentLen);
size_t totalLen = 1 + lenLen + contentLen;
signedCert.malloc(totalLen);
char *cp = (char *)signedCert.data();
*cp++ = UNIV | CONS | SEQ_TAG_CODE;
SC_encodeLength(contentLen, cp, lenLen);
cp += lenLen;
memcpy(cp, TBSCert.data(), TBSCert.length());
cp += TBSCert.length();
memcpy(cp, algId.data(), algId.length());
cp += algId.length();
memcpy(cp, encodedSig.data(), encodedSig.length());
CASSERT((cp + encodedSig.length()) ==
((char *)signedCert.data() + signedCert.length()));
}
void CL_snaccOidToCssm(
const AsnOid &inOid,
CssmOid &outOid,
CssmAllocator &alloc)
{
outOid.Data = (uint8 *)alloc.malloc(inOid.Len());
outOid.Length = inOid.Len();
const char *cp = inOid;
memcpy(outOid.Data, cp, outOid.Length);
}
void CL_cssmAlgIdToSnacc (
const CSSM_X509_ALGORITHM_IDENTIFIER &cssmAlgId,
AlgorithmIdentifier &snaccAlgId)
{
snaccAlgId.algorithm.Set(reinterpret_cast<char *>(
cssmAlgId.algorithm.Data), cssmAlgId.algorithm.Length);
if(cssmAlgId.parameters.Data != NULL) {
snaccAlgId.parameters = new AsnAny;
CSM_Buffer *cbuf = new CSM_Buffer(
reinterpret_cast<char *>(cssmAlgId.parameters.Data),
cssmAlgId.parameters.Length);
snaccAlgId.parameters->value = cbuf;
}
else {
CL_nullAlgParams(snaccAlgId);
}
}
void CL_snaccAlgIdToCssm (
const AlgorithmIdentifier &snaccAlgId,
CSSM_X509_ALGORITHM_IDENTIFIER &cssmAlgId,
CssmAllocator &alloc)
{
memset(&cssmAlgId, 0, sizeof(CSSM_X509_ALGORITHM_IDENTIFIER));
CssmOid &outOid = CssmOid::overlay(cssmAlgId.algorithm);
CL_snaccOidToCssm(snaccAlgId.algorithm, outOid, alloc);
if(snaccAlgId.parameters != NULL) {
CSM_Buffer *cbuf = snaccAlgId.parameters->value;
cssmAlgId.parameters.Data = (uint8 *)alloc.malloc(cbuf->Length());
cssmAlgId.parameters.Length = cbuf->Length();
memmove(cssmAlgId.parameters.Data, cbuf->Access(),
cssmAlgId.parameters.Length);
}
}
CSSM_ALGORITHMS CL_snaccOidToCssmAlg(
const AsnOid &oid)
{
AlgOidCache &oc = algOidCache();
CSSM_ALGORITHMS cssmAlg = 0;
if(oid == oc.mRsaEncryption) {
cssmAlg = CSSM_ALGID_RSA;
}
else if(oid == oc.mMd2WithRSAEncryption) {
cssmAlg = CSSM_ALGID_MD2WithRSA;
}
else if(oid == oc.mMd5WithRSAEncryption) {
cssmAlg = CSSM_ALGID_MD5WithRSA;
}
else if(oid == oc.mSha1withRSAEncryption) {
cssmAlg = CSSM_ALGID_SHA1WithRSA;
}
else if(oid == oc.mId_dsa) {
cssmAlg = CSSM_ALGID_DSA;
}
else if(oid == oc.mId_dsa_with_sha1) {
cssmAlg = CSSM_ALGID_SHA1WithDSA;
}
else if(oid == oc.mAppleFee) {
cssmAlg = CSSM_ALGID_FEE;
}
else if(oid == oc.mAppleAsc) {
cssmAlg = CSSM_ALGID_ASC;
}
else if(oid == oc.mAppleFeeMD5) {
cssmAlg = CSSM_ALGID_FEE_MD5;
}
else if(oid == oc.mAppleFeeSHA1) {
cssmAlg = CSSM_ALGID_FEE_SHA1;
}
else if(oid == oc.mAppleFeed) {
cssmAlg = CSSM_ALGID_FEED;
}
else if(oid == oc.mAppleFeedExp) {
cssmAlg = CSSM_ALGID_FEEDEXP;
}
else if(oid == oc.mAppleECDSA) {
cssmAlg = CSSM_ALGID_SHA1WithECDSA;
}
else {
errorLog0("snaccOidToCssmAlg: unknown alg\n");
#ifndef NDEBUG
printf("Bogus OID: "); oid.Print(cout);
printf("\n");
#endif
CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
}
return cssmAlg;
}
void CL_cssmAlgToSnaccOid(
CSSM_ALGORITHMS cssmAlg,
AsnOid &oid)
{
switch(cssmAlg) {
case CSSM_ALGID_RSA:
oid.ReSet(rsaEncryption_arc);
break;
case CSSM_ALGID_MD2WithRSA:
oid.ReSet(md2WithRSAEncryption_arc);
break;
case CSSM_ALGID_MD5WithRSA:
oid.ReSet(md2WithRSAEncryption_arc);
break;
case CSSM_ALGID_SHA1WithRSA:
oid.ReSet(sha1withRSAEncryption_arc);
break;
case CSSM_ALGID_DSA:
oid.ReSet(id_dsa_arc);
break;
case CSSM_ALGID_SHA1WithDSA:
oid.ReSet(id_dsa_with_sha1_arc);
break;
case CSSM_ALGID_FEE:
oid.ReSet(appleFee_arc);
break;
case CSSM_ALGID_ASC:
oid.ReSet(appleAsc_arc);
break;
case CSSM_ALGID_FEE_MD5:
oid.ReSet(appleFeeMD5_arc);
break;
case CSSM_ALGID_FEE_SHA1:
oid.ReSet(appleFeeSHA1_arc);
break;
case CSSM_ALGID_FEED:
oid.ReSet(appleFeed_arc);
break;
case CSSM_ALGID_FEEDEXP:
oid.ReSet(appleFeedExp_arc);
break;
case CSSM_ALGID_SHA1WithECDSA:
oid.ReSet(appleECDSA_arc);
break;
default:
errorLog1("cssmAlgToSnaccOid: unknown alg (%d)\n", (int)cssmAlg);
CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
}
}
void CL_nullAlgParams(
AlgorithmIdentifier &snaccAlgId)
{
snaccAlgId.parameters = new AsnAny;
char encodedNull[2] = {NULLTYPE_TAG_CODE, 0};
CSM_Buffer *cbuf = new CSM_Buffer(encodedNull, 2);
snaccAlgId.parameters->value = cbuf;
}
void CL_AsnOctsToCssmData(
const AsnOcts &octs,
CSSM_DATA &cdata,
CssmAllocator &alloc)
{
const char *cp = octs;
CssmAutoData aData(alloc, (uint8 *)cp, octs.Len());
cdata = aData.release();
}
#define MAX_NAME_SIZE (4 * 1024)
void CL_snaccGeneralNamesToCdsa(
GeneralNames &snaccObj,
CE_GeneralNames &cdsaObj,
CssmAllocator &alloc)
{
cdsaObj.numNames = snaccObj.Count();
if(cdsaObj.numNames == 0) {
cdsaObj.generalName = NULL;
return;
}
cdsaObj.generalName = (CE_GeneralName *)alloc.malloc(
cdsaObj.numNames * sizeof(CE_GeneralName));
snaccObj.SetCurrToFirst();
CssmAutoData aData(alloc);
for(unsigned i=0; i<cdsaObj.numNames; i++) {
CE_GeneralName *currCdsaName = &cdsaObj.generalName[i];
GeneralName *currSnaccName = snaccObj.Curr();
char *src = NULL;
unsigned len = 0;
AsnType *toBeEncoded = NULL;
switch(currSnaccName->choiceId) {
case GeneralName::otherNameCid:
currCdsaName->nameType = GNT_OtherName;
src = *currSnaccName->otherName;
len = currSnaccName->otherName->Len();
break;
case GeneralName::rfc822NameCid:
currCdsaName->nameType = GNT_RFC822Name;
src = *currSnaccName->rfc822Name;
len = currSnaccName->rfc822Name->Len();
break;
case GeneralName::dNSNameCid:
currCdsaName->nameType = GNT_DNSName;
src = *currSnaccName->dNSName;
len = currSnaccName->dNSName->Len();
break;
case GeneralName::x400AddressCid:
currCdsaName->nameType = GNT_X400Address;
toBeEncoded = currSnaccName->x400Address;
break;
case GeneralName::directoryNameCid:
currCdsaName->nameType = GNT_DirectoryName;
toBeEncoded = currSnaccName->directoryName;
break;
case GeneralName::ediPartyNameCid:
currCdsaName->nameType = GNT_EdiPartyName;
toBeEncoded = currSnaccName->ediPartyName;
break;
case GeneralName::uniformResourceIdentifierCid:
currCdsaName->nameType = GNT_URI;
src = *currSnaccName->uniformResourceIdentifier;
len = currSnaccName->uniformResourceIdentifier->Len();
break;
case GeneralName::iPAddressCid:
currCdsaName->nameType = GNT_IPAddress;
src = *currSnaccName->iPAddress;
len = currSnaccName->iPAddress->Len();
break;
case GeneralName::registeredIDCid:
currCdsaName->nameType = GNT_RegisteredID;
src = *currSnaccName->registeredID;
len = currSnaccName->registeredID->Len();
break;
}
if(src == NULL) {
CASSERT(toBeEncoded != NULL);
SC_encodeAsnObj(*toBeEncoded, aData, MAX_NAME_SIZE);
src = aData;
len = aData.length();
aData.release();
currCdsaName->berEncoded = CSSM_TRUE;
}
else {
CASSERT(toBeEncoded == NULL);
currCdsaName->berEncoded = CSSM_FALSE;
}
currCdsaName->name.Data = (uint8 *)alloc.malloc(len);
currCdsaName->name.Length = len;
memmove(currCdsaName->name.Data, src, len);
snaccObj.GoNext();
}
}
GeneralNames *CL_cdsaGeneralNamesToSnacc(
CE_GeneralNames &cdsaObj)
{
GeneralNames *snaccObj = new GeneralNames;
bool abortFlag = false; CssmAllocator &alloc = CssmAllocator::standard();
for(unsigned i=0; i<cdsaObj.numNames; i++) {
CE_GeneralName *currCdsaName = &cdsaObj.generalName[i];
char *rawData = reinterpret_cast<char *>(currCdsaName->name.Data);
unsigned rawDataLen = currCdsaName->name.Length;
GeneralName *currSnaccName = snaccObj->Append();
CssmData &berCdata = CssmData::overlay(currCdsaName->name);
CssmRemoteData berData(alloc, berCdata);
switch(currCdsaName->nameType) {
case GNT_OtherName:
if(currCdsaName->berEncoded) {
abortFlag = true;
break;
}
currSnaccName->choiceId = GeneralName::otherNameCid;
currSnaccName->otherName = new AsnOid(rawData, rawDataLen);
break;
case GNT_RFC822Name:
if(currCdsaName->berEncoded) {
abortFlag = true;
break;
}
currSnaccName->choiceId = GeneralName::rfc822NameCid;
currSnaccName->rfc822Name = new IA5String(rawData, rawDataLen);
break;
case GNT_DNSName:
if(currCdsaName->berEncoded) {
abortFlag = true;
break;
}
currSnaccName->choiceId = GeneralName::dNSNameCid;
currSnaccName->rfc822Name = new IA5String(rawData, rawDataLen);
break;
case GNT_X400Address:
if(!currCdsaName->berEncoded) {
abortFlag = true;
break;
}
currSnaccName->choiceId = GeneralName::x400AddressCid;
currSnaccName->x400Address = new ORAddress;
try {
SC_decodeAsnObj(berData, *currSnaccName->x400Address);
}
catch(...) {
abortFlag = true;
}
break;
case GNT_DirectoryName:
if(!currCdsaName->berEncoded) {
abortFlag = true;
break;
}
currSnaccName->choiceId = GeneralName::directoryNameCid;
currSnaccName->directoryName = new Name;
try {
SC_decodeAsnObj(berData, *currSnaccName->directoryName);
}
catch(...) {
abortFlag = true;
}
break;
case GNT_EdiPartyName:
if(!currCdsaName->berEncoded) {
abortFlag = true;
break;
}
currSnaccName->choiceId = GeneralName::ediPartyNameCid;
currSnaccName->ediPartyName = new EDIPartyName;
try {
SC_decodeAsnObj(berData, *currSnaccName->ediPartyName);
}
catch(...) {
abortFlag = true;
}
break;
case GNT_URI:
if(currCdsaName->berEncoded) {
abortFlag = true;
break;
}
currSnaccName->choiceId = GeneralName::uniformResourceIdentifierCid;
currSnaccName->uniformResourceIdentifier =
new IA5String(rawData, rawDataLen);
break;
case GNT_IPAddress:
if(currCdsaName->berEncoded) {
abortFlag = true;
break;
}
currSnaccName->choiceId = GeneralName::iPAddressCid;
currSnaccName->iPAddress = new AsnOcts(rawData, rawDataLen);
break;
case GNT_RegisteredID:
if(currCdsaName->berEncoded) {
abortFlag = true;
break;
}
currSnaccName->choiceId = GeneralName::registeredIDCid;
currSnaccName->registeredID = new AsnOid(rawData, rawDataLen);
break;
}
berData.release();
if(abortFlag) {
break;
}
}
if(abortFlag) {
delete snaccObj;
CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER);
}
return snaccObj;
}
void CL_normalizeString(
char *strPtr,
int &strLen)
{
char *pCh = strPtr; char *pD = pCh; char *pEos = pCh + strLen - 1;
if(strLen == 0) {
return;
}
while(*pEos == 0) {
pEos--;
}
while(isspace(*pEos)) {
pEos--;
}
pEos++;
while(pCh < pEos) {
*pCh++ = toupper(*pCh);
}
pCh = pD;
while(isspace(*pCh) && (pCh < pEos)) {
pCh++;
}
char ch;
while(pCh < pEos) {
ch = *pCh++;
*pD++ = ch; if( isspace(ch) ){
while(isspace(*pCh) && (pCh < pEos)) {
pCh++;
}
}
};
strLen = pD - strPtr;
}
void CL_normalizeX509Name(
Name &name,
CssmAllocator &alloc)
{
RDNSequence *rdns = name.rDNSequence;
int numRdns = rdns->Count();
if((rdns == NULL) || (numRdns == 0)) {
return;
}
rdns->SetCurrElmt(0);
for(int rdnDex=0; rdnDex<numRdns; rdnDex++) {
RelativeDistinguishedName *rdn = rdns->Curr();
if(rdn == NULL) {
dprintf1("clNormalizeX509Name: NULL rdn at index %d\n", rdnDex);
rdns->GoNext();
continue;
}
int numAttrs = rdn->Count();
if(numAttrs == 0) {
dprintf1("clNormalizeX509Name: zero numAttrs at index %d\n", rdnDex);
rdns->GoNext();
continue;
}
rdn->SetCurrElmt(0);
for(int attrDex=0; attrDex<numAttrs; attrDex++) {
AttributeTypeAndDistinguishedValue *att = rdn->Curr();
if(att == NULL) {
dprintf1("clNormalizeX509Name: NULL att at index %d\n", attrDex);
rdn->GoNext();
continue;
}
CSM_Buffer *cbuf = att->value.value;
DirectoryString dirStr;
char *cbufData = const_cast<char *>(cbuf->Access());
CssmData encodedStr(cbufData, cbuf->Length());
char tagByte = cbufData[0];
if((tagByte == (UNIV | PRIM | IA5STRING_TAG_CODE)) ||
(tagByte == (UNIV | CONS | IA5STRING_TAG_CODE))) {
return;
}
try {
SC_decodeAsnObj(encodedStr, dirStr);
}
catch (...) {
errorLog0("clNormalizeX509Name: malformed DirectoryString (1)\n");
return;
}
char *strPtr = *dirStr.teletexString;
int newLen = dirStr.teletexString->Len();
CL_normalizeString(strPtr, newLen);
dirStr.teletexString->ReSet(strPtr, newLen);
CssmAutoData normEncoded(alloc);
SC_encodeAsnObj(dirStr, normEncoded, newLen + 8);
cbuf->Set((char *)normEncoded.data(), normEncoded.length());
rdn->GoNext();
}
rdns->GoNext();
}
}