Mercurial > trustbridge > nss-cmake-static
diff nss/lib/softoken/legacydb/lowcert.c @ 3:150b72113545
Add DBM and legacydb support
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Tue, 05 Aug 2014 18:32:02 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nss/lib/softoken/legacydb/lowcert.c Tue Aug 05 18:32:02 2014 +0200 @@ -0,0 +1,826 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Certificate handling code + */ + +#include "seccomon.h" +#include "secder.h" +#include "nssilock.h" +#include "lowkeyi.h" +#include "secasn1.h" +#include "secoid.h" +#include "secerr.h" +#include "pcert.h" + +SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate) + +static const SEC_ASN1Template nsslowcert_SubjectPublicKeyInfoTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWCERTSubjectPublicKeyInfo) }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, + offsetof(NSSLOWCERTSubjectPublicKeyInfo,algorithm), + SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, + { SEC_ASN1_BIT_STRING, + offsetof(NSSLOWCERTSubjectPublicKeyInfo,subjectPublicKey), }, + { 0, } +}; + +static const SEC_ASN1Template nsslowcert_RSAPublicKeyTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYPublicKey) }, + { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPublicKey,u.rsa.modulus), }, + { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPublicKey,u.rsa.publicExponent), }, + { 0, } +}; +static const SEC_ASN1Template nsslowcert_DSAPublicKeyTemplate[] = { + { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPublicKey,u.dsa.publicValue), }, + { 0, } +}; +static const SEC_ASN1Template nsslowcert_DHPublicKeyTemplate[] = { + { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPublicKey,u.dh.publicValue), }, + { 0, } +}; + +/* + * See bugzilla bug 125359 + * Since NSS (via PKCS#11) wants to handle big integers as unsigned ints, + * all of the templates above that en/decode into integers must be converted + * from ASN.1's signed integer type. This is done by marking either the + * source or destination (encoding or decoding, respectively) type as + * siUnsignedInteger. + */ + +static void +prepare_low_rsa_pub_key_for_asn1(NSSLOWKEYPublicKey *pubk) +{ + pubk->u.rsa.modulus.type = siUnsignedInteger; + pubk->u.rsa.publicExponent.type = siUnsignedInteger; +} + +static void +prepare_low_dsa_pub_key_for_asn1(NSSLOWKEYPublicKey *pubk) +{ + pubk->u.dsa.publicValue.type = siUnsignedInteger; + pubk->u.dsa.params.prime.type = siUnsignedInteger; + pubk->u.dsa.params.subPrime.type = siUnsignedInteger; + pubk->u.dsa.params.base.type = siUnsignedInteger; +} + +static void +prepare_low_dh_pub_key_for_asn1(NSSLOWKEYPublicKey *pubk) +{ + pubk->u.dh.prime.type = siUnsignedInteger; + pubk->u.dh.base.type = siUnsignedInteger; + pubk->u.dh.publicValue.type = siUnsignedInteger; +} + +/* + * simple cert decoder to avoid the cost of asn1 engine + */ +static unsigned char * +nsslowcert_dataStart(unsigned char *buf, unsigned int length, + unsigned int *data_length, PRBool includeTag, + unsigned char* rettag) { + unsigned char tag; + unsigned int used_length= 0; + + /* need at least a tag and a 1 byte length */ + if (length < 2) { + return NULL; + } + + tag = buf[used_length++]; + + if (rettag) { + *rettag = tag; + } + + /* blow out when we come to the end */ + if (tag == 0) { + return NULL; + } + + *data_length = buf[used_length++]; + + if (*data_length&0x80) { + int len_count = *data_length & 0x7f; + + if (len_count+used_length > length) { + return NULL; + } + + *data_length = 0; + + while (len_count-- > 0) { + *data_length = (*data_length << 8) | buf[used_length++]; + } + } + + if (*data_length > (length-used_length) ) { + *data_length = length-used_length; + return NULL; + } + if (includeTag) *data_length += used_length; + + return (buf + (includeTag ? 0 : used_length)); +} + +static void SetTimeType(SECItem* item, unsigned char tagtype) +{ + switch (tagtype) { + case SEC_ASN1_UTC_TIME: + item->type = siUTCTime; + break; + + case SEC_ASN1_GENERALIZED_TIME: + item->type = siGeneralizedTime; + break; + + default: + PORT_Assert(0); + break; + } +} + +static int +nsslowcert_GetValidityFields(unsigned char *buf,int buf_length, + SECItem *notBefore, SECItem *notAfter) +{ + unsigned char tagtype; + notBefore->data = nsslowcert_dataStart(buf,buf_length, + ¬Before->len,PR_FALSE, &tagtype); + if (notBefore->data == NULL) return SECFailure; + SetTimeType(notBefore, tagtype); + buf_length -= (notBefore->data-buf) + notBefore->len; + buf = notBefore->data + notBefore->len; + notAfter->data = nsslowcert_dataStart(buf,buf_length, + ¬After->len,PR_FALSE, &tagtype); + if (notAfter->data == NULL) return SECFailure; + SetTimeType(notAfter, tagtype); + return SECSuccess; +} + +static int +nsslowcert_GetCertFields(unsigned char *cert,int cert_length, + SECItem *issuer, SECItem *serial, SECItem *derSN, SECItem *subject, + SECItem *valid, SECItem *subjkey, SECItem *extensions) +{ + unsigned char *buf; + unsigned int buf_length; + unsigned char *dummy; + unsigned int dummylen; + + /* get past the signature wrap */ + buf = nsslowcert_dataStart(cert,cert_length,&buf_length,PR_FALSE, NULL); + if (buf == NULL) return SECFailure; + /* get into the raw cert data */ + buf = nsslowcert_dataStart(buf,buf_length,&buf_length,PR_FALSE, NULL); + if (buf == NULL) return SECFailure; + /* skip past any optional version number */ + if ((buf[0] & 0xa0) == 0xa0) { + dummy = nsslowcert_dataStart(buf,buf_length,&dummylen,PR_FALSE, NULL); + if (dummy == NULL) return SECFailure; + buf_length -= (dummy-buf) + dummylen; + buf = dummy + dummylen; + } + /* serial number */ + if (derSN) { + derSN->data=nsslowcert_dataStart(buf,buf_length,&derSN->len,PR_TRUE, NULL); + /* derSN->data doesn't need to be checked because if it fails so will + * serial->data below. The only difference between the two calls is + * whether or not the tags are included in the returned buffer */ + } + serial->data = nsslowcert_dataStart(buf,buf_length,&serial->len,PR_FALSE, NULL); + if (serial->data == NULL) return SECFailure; + buf_length -= (serial->data-buf) + serial->len; + buf = serial->data + serial->len; + /* skip the OID */ + dummy = nsslowcert_dataStart(buf,buf_length,&dummylen,PR_FALSE, NULL); + if (dummy == NULL) return SECFailure; + buf_length -= (dummy-buf) + dummylen; + buf = dummy + dummylen; + /* issuer */ + issuer->data = nsslowcert_dataStart(buf,buf_length,&issuer->len,PR_TRUE, NULL); + if (issuer->data == NULL) return SECFailure; + buf_length -= (issuer->data-buf) + issuer->len; + buf = issuer->data + issuer->len; + + /* only wanted issuer/SN */ + if (valid == NULL) { + return SECSuccess; + } + /* validity */ + valid->data = nsslowcert_dataStart(buf,buf_length,&valid->len,PR_FALSE, NULL); + if (valid->data == NULL) return SECFailure; + buf_length -= (valid->data-buf) + valid->len; + buf = valid->data + valid->len; + /*subject */ + subject->data=nsslowcert_dataStart(buf,buf_length,&subject->len,PR_TRUE, NULL); + if (subject->data == NULL) return SECFailure; + buf_length -= (subject->data-buf) + subject->len; + buf = subject->data + subject->len; + /* subject key info */ + subjkey->data=nsslowcert_dataStart(buf,buf_length,&subjkey->len,PR_TRUE, NULL); + if (subjkey->data == NULL) return SECFailure; + buf_length -= (subjkey->data-buf) + subjkey->len; + buf = subjkey->data + subjkey->len; + + extensions->data = NULL; + extensions->len = 0; + while (buf_length > 0) { + /* EXTENSIONS */ + if (buf[0] == 0xa3) { + extensions->data = nsslowcert_dataStart(buf,buf_length, + &extensions->len, PR_FALSE, NULL); + /* if the DER is bad, we should fail. Previously we accepted + * bad DER here and treated the extension as missin */ + if (extensions->data == NULL || + (extensions->data - buf) + extensions->len != buf_length) + return SECFailure; + buf = extensions->data; + buf_length = extensions->len; + /* now parse the SEQUENCE holding the extensions. */ + dummy = nsslowcert_dataStart(buf,buf_length,&dummylen,PR_FALSE,NULL); + if (dummy == NULL || + (dummy - buf) + dummylen != buf_length) + return SECFailure; + buf_length -= (dummy - buf); + buf = dummy; + /* Now parse the extensions inside this sequence */ + } + dummy = nsslowcert_dataStart(buf,buf_length,&dummylen,PR_FALSE,NULL); + if (dummy == NULL) return SECFailure; + buf_length -= (dummy - buf) + dummylen; + buf = dummy + dummylen; + } + return SECSuccess; +} + +static SECStatus +nsslowcert_GetCertTimes(NSSLOWCERTCertificate *c, PRTime *notBefore, PRTime *notAfter) +{ + int rv; + NSSLOWCERTValidity validity; + + rv = nsslowcert_GetValidityFields(c->validity.data,c->validity.len, + &validity.notBefore,&validity.notAfter); + if (rv != SECSuccess) { + return rv; + } + + /* convert DER not-before time */ + rv = DER_DecodeTimeChoice(notBefore, &validity.notBefore); + if (rv) { + return(SECFailure); + } + + /* convert DER not-after time */ + rv = DER_DecodeTimeChoice(notAfter, &validity.notAfter); + if (rv) { + return(SECFailure); + } + + return(SECSuccess); +} + +/* + * is certa newer than certb? If one is expired, pick the other one. + */ +PRBool +nsslowcert_IsNewer(NSSLOWCERTCertificate *certa, NSSLOWCERTCertificate *certb) +{ + PRTime notBeforeA, notAfterA, notBeforeB, notAfterB, now; + SECStatus rv; + PRBool newerbefore, newerafter; + + rv = nsslowcert_GetCertTimes(certa, ¬BeforeA, ¬AfterA); + if ( rv != SECSuccess ) { + return(PR_FALSE); + } + + rv = nsslowcert_GetCertTimes(certb, ¬BeforeB, ¬AfterB); + if ( rv != SECSuccess ) { + return(PR_TRUE); + } + + newerbefore = PR_FALSE; + if ( LL_CMP(notBeforeA, >, notBeforeB) ) { + newerbefore = PR_TRUE; + } + + newerafter = PR_FALSE; + if ( LL_CMP(notAfterA, >, notAfterB) ) { + newerafter = PR_TRUE; + } + + if ( newerbefore && newerafter ) { + return(PR_TRUE); + } + + if ( ( !newerbefore ) && ( !newerafter ) ) { + return(PR_FALSE); + } + + /* get current time */ + now = PR_Now(); + + if ( newerbefore ) { + /* cert A was issued after cert B, but expires sooner */ + /* if A is expired, then pick B */ + if ( LL_CMP(notAfterA, <, now ) ) { + return(PR_FALSE); + } + return(PR_TRUE); + } else { + /* cert B was issued after cert A, but expires sooner */ + /* if B is expired, then pick A */ + if ( LL_CMP(notAfterB, <, now ) ) { + return(PR_TRUE); + } + return(PR_FALSE); + } +} + +#define SOFT_DEFAULT_CHUNKSIZE 2048 + +static SECStatus +nsslowcert_KeyFromIssuerAndSN(PLArenaPool *arena, + SECItem *issuer, SECItem *sn, SECItem *key) +{ + unsigned int len = sn->len + issuer->len; + + if (!arena) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + goto loser; + } + if (len > NSS_MAX_LEGACY_DB_KEY_SIZE) { + PORT_SetError(SEC_ERROR_INPUT_LEN); + goto loser; + } + key->data = (unsigned char*)PORT_ArenaAlloc(arena, len); + if ( !key->data ) { + goto loser; + } + + key->len = len; + /* copy the serialNumber */ + PORT_Memcpy(key->data, sn->data, sn->len); + + /* copy the issuer */ + PORT_Memcpy(&key->data[sn->len], issuer->data, issuer->len); + + return(SECSuccess); + +loser: + return(SECFailure); +} + +static SECStatus +nsslowcert_KeyFromIssuerAndSNStatic(unsigned char *space, + int spaceLen, SECItem *issuer, SECItem *sn, SECItem *key) +{ + unsigned int len = sn->len + issuer->len; + + key->data = pkcs11_allocStaticData(len, space, spaceLen); + if ( !key->data ) { + goto loser; + } + + key->len = len; + /* copy the serialNumber */ + PORT_Memcpy(key->data, sn->data, sn->len); + + /* copy the issuer */ + PORT_Memcpy(&key->data[sn->len], issuer->data, issuer->len); + + return(SECSuccess); + +loser: + return(SECFailure); +} + + +static char * +nsslowcert_EmailName(SECItem *derDN, char *space, unsigned int len) +{ + unsigned char *buf; + unsigned int buf_length; + + /* unwrap outer sequence */ + buf=nsslowcert_dataStart(derDN->data,derDN->len,&buf_length,PR_FALSE,NULL); + if (buf == NULL) return NULL; + + /* Walk each RDN */ + while (buf_length > 0) { + unsigned char *rdn; + unsigned int rdn_length; + + /* grab next rdn */ + rdn=nsslowcert_dataStart(buf, buf_length, &rdn_length, PR_FALSE, NULL); + if (rdn == NULL) { return NULL; } + buf_length -= (rdn - buf) + rdn_length; + buf = rdn+rdn_length; + + while (rdn_length > 0) { + unsigned char *ava; + unsigned int ava_length; + unsigned char *oid; + unsigned int oid_length; + unsigned char *name; + unsigned int name_length; + SECItem oidItem; + SECOidTag type; + + /* unwrap the ava */ + ava=nsslowcert_dataStart(rdn, rdn_length, &ava_length, PR_FALSE, + NULL); + if (ava == NULL) return NULL; + rdn_length -= (ava-rdn)+ava_length; + rdn = ava + ava_length; + + oid=nsslowcert_dataStart(ava, ava_length, &oid_length, PR_FALSE, + NULL); + if (oid == NULL) { return NULL; } + ava_length -= (oid-ava)+oid_length; + ava = oid+oid_length; + + name=nsslowcert_dataStart(ava, ava_length, &name_length, PR_FALSE, + NULL); + if (oid == NULL) { return NULL; } + ava_length -= (name-ava)+name_length; + ava = name+name_length; + + oidItem.data = oid; + oidItem.len = oid_length; + type = SECOID_FindOIDTag(&oidItem); + if ((type == SEC_OID_PKCS9_EMAIL_ADDRESS) || + (type == SEC_OID_RFC1274_MAIL)) { + /* Email is supposed to be IA5String, so no + * translation necessary */ + char *emailAddr; + emailAddr = (char *)pkcs11_copyStaticData(name,name_length+1, + (unsigned char *)space,len); + if (emailAddr) { + emailAddr[name_length] = 0; + } + return emailAddr; + } + } + } + return NULL; +} + +static char * +nsslowcert_EmailAltName(NSSLOWCERTCertificate *cert, char *space, + unsigned int len) +{ + unsigned char *exts; + unsigned int exts_length; + + /* unwrap the sequence */ + exts = nsslowcert_dataStart(cert->extensions.data, cert->extensions.len, + &exts_length, PR_FALSE, NULL); + /* loop through extension */ + while (exts && exts_length > 0) { + unsigned char * ext; + unsigned int ext_length; + unsigned char *oid; + unsigned int oid_length; + unsigned char *nameList; + unsigned int nameList_length; + SECItem oidItem; + SECOidTag type; + + ext = nsslowcert_dataStart(exts, exts_length, &ext_length, + PR_FALSE, NULL); + if (ext == NULL) { break; } + exts_length -= (ext - exts) + ext_length; + exts = ext+ext_length; + + oid=nsslowcert_dataStart(ext, ext_length, &oid_length, PR_FALSE, NULL); + if (oid == NULL) { break; } + ext_length -= (oid - ext) + oid_length; + ext = oid+oid_length; + oidItem.data = oid; + oidItem.len = oid_length; + type = SECOID_FindOIDTag(&oidItem); + + /* get Alt Extension */ + if (type != SEC_OID_X509_SUBJECT_ALT_NAME) { + continue; + } + + /* skip passed the critical flag */ + if (ext[0] == 0x01) { /* BOOLEAN */ + unsigned char *dummy; + unsigned int dummy_length; + dummy = nsslowcert_dataStart(ext, ext_length, &dummy_length, + PR_FALSE, NULL); + if (dummy == NULL) { break; } + ext_length -= (dummy - ext) + dummy_length; + ext = dummy+dummy_length; + } + + + /* unwrap the name list */ + nameList = nsslowcert_dataStart(ext, ext_length, &nameList_length, + PR_FALSE, NULL); + if (nameList == NULL) { break; } + ext_length -= (nameList - ext) + nameList_length; + ext = nameList+nameList_length; + nameList = nsslowcert_dataStart(nameList, nameList_length, + &nameList_length, PR_FALSE, NULL); + /* loop through the name list */ + while (nameList && nameList_length > 0) { + unsigned char *thisName; + unsigned int thisName_length; + + thisName = nsslowcert_dataStart(nameList, nameList_length, + &thisName_length, PR_FALSE, NULL); + if (thisName == NULL) { break; } + if (nameList[0] == 0xa2) { /* DNS Name */ + SECItem dn; + char *emailAddr; + + dn.data = thisName; + dn.len = thisName_length; + emailAddr = nsslowcert_EmailName(&dn, space, len); + if (emailAddr) { + return emailAddr; + } + } + if (nameList[0] == 0x81) { /* RFC 822name */ + char *emailAddr; + emailAddr = (char *)pkcs11_copyStaticData(thisName, + thisName_length+1, (unsigned char *)space,len); + if (emailAddr) { + emailAddr[thisName_length] = 0; + } + return emailAddr; + } + nameList_length -= (thisName-nameList) + thisName_length; + nameList = thisName + thisName_length; + } + break; + } + return NULL; +} + +static char * +nsslowcert_GetCertificateEmailAddress(NSSLOWCERTCertificate *cert) +{ + char *emailAddr = NULL; + char *str; + + emailAddr = nsslowcert_EmailName(&cert->derSubject,cert->emailAddrSpace, + sizeof(cert->emailAddrSpace)); + /* couldn't find the email address in the DN, check the subject Alt name */ + if (!emailAddr && cert->extensions.data) { + emailAddr = nsslowcert_EmailAltName(cert, cert->emailAddrSpace, + sizeof(cert->emailAddrSpace)); + } + + + /* make it lower case */ + str = emailAddr; + while ( str && *str ) { + *str = tolower( *str ); + str++; + } + return emailAddr; + +} + +/* + * take a DER certificate and decode it into a certificate structure + */ +NSSLOWCERTCertificate * +nsslowcert_DecodeDERCertificate(SECItem *derSignedCert, char *nickname) +{ + NSSLOWCERTCertificate *cert; + int rv; + + /* allocate the certificate structure */ + cert = nsslowcert_CreateCert(); + + if ( !cert ) { + goto loser; + } + + /* point to passed in DER data */ + cert->derCert = *derSignedCert; + cert->nickname = NULL; + cert->certKey.data = NULL; + cert->referenceCount = 1; + + /* decode the certificate info */ + rv = nsslowcert_GetCertFields(cert->derCert.data, cert->derCert.len, + &cert->derIssuer, &cert->serialNumber, &cert->derSN, &cert->derSubject, + &cert->validity, &cert->derSubjKeyInfo, &cert->extensions); + + if (rv != SECSuccess) { + goto loser; + } + + /* cert->subjectKeyID; x509v3 subject key identifier */ + cert->subjectKeyID.data = NULL; + cert->subjectKeyID.len = 0; + cert->dbEntry = NULL; + cert ->trust = NULL; + cert ->dbhandle = NULL; + + /* generate and save the database key for the cert */ + rv = nsslowcert_KeyFromIssuerAndSNStatic(cert->certKeySpace, + sizeof(cert->certKeySpace), &cert->derIssuer, + &cert->serialNumber, &cert->certKey); + if ( rv ) { + goto loser; + } + + /* set the nickname */ + if ( nickname == NULL ) { + cert->nickname = NULL; + } else { + /* copy and install the nickname */ + cert->nickname = pkcs11_copyNickname(nickname,cert->nicknameSpace, + sizeof(cert->nicknameSpace)); + } + +#ifdef FIXME + /* initialize the subjectKeyID */ + rv = cert_GetKeyID(cert); + if ( rv != SECSuccess ) { + goto loser; + } +#endif + + /* set the email address */ + cert->emailAddr = nsslowcert_GetCertificateEmailAddress(cert); + + + cert->referenceCount = 1; + + return(cert); + +loser: + if (cert) { + nsslowcert_DestroyCertificate(cert); + } + + return(0); +} + +char * +nsslowcert_FixupEmailAddr(char *emailAddr) +{ + char *retaddr; + char *str; + + if ( emailAddr == NULL ) { + return(NULL); + } + + /* copy the string */ + str = retaddr = PORT_Strdup(emailAddr); + if ( str == NULL ) { + return(NULL); + } + + /* make it lower case */ + while ( *str ) { + *str = tolower( *str ); + str++; + } + + return(retaddr); +} + + +/* + * Generate a database key, based on serial number and issuer, from a + * DER certificate. + */ +SECStatus +nsslowcert_KeyFromDERCert(PLArenaPool *arena, SECItem *derCert, SECItem *key) +{ + int rv; + NSSLOWCERTCertKey certkey; + + PORT_Memset(&certkey, 0, sizeof(NSSLOWCERTCertKey)); + + rv = nsslowcert_GetCertFields(derCert->data, derCert->len, + &certkey.derIssuer, &certkey.serialNumber, NULL, NULL, + NULL, NULL, NULL); + + if ( rv ) { + goto loser; + } + + return(nsslowcert_KeyFromIssuerAndSN(arena, &certkey.derIssuer, + &certkey.serialNumber, key)); +loser: + return(SECFailure); +} + +NSSLOWKEYPublicKey * +nsslowcert_ExtractPublicKey(NSSLOWCERTCertificate *cert) +{ + NSSLOWCERTSubjectPublicKeyInfo spki; + NSSLOWKEYPublicKey *pubk; + SECItem os; + SECStatus rv; + PLArenaPool *arena; + SECOidTag tag; + SECItem newDerSubjKeyInfo; + + arena = PORT_NewArena (DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) + return NULL; + + pubk = (NSSLOWKEYPublicKey *) + PORT_ArenaZAlloc(arena, sizeof(NSSLOWKEYPublicKey)); + if (pubk == NULL) { + PORT_FreeArena (arena, PR_FALSE); + return NULL; + } + + pubk->arena = arena; + PORT_Memset(&spki,0,sizeof(spki)); + + /* copy the DER into the arena, since Quick DER returns data that points + into the DER input, which may get freed by the caller */ + rv = SECITEM_CopyItem(arena, &newDerSubjKeyInfo, &cert->derSubjKeyInfo); + if ( rv != SECSuccess ) { + PORT_FreeArena (arena, PR_FALSE); + return NULL; + } + + /* we haven't bothered decoding the spki struct yet, do it now */ + rv = SEC_QuickDERDecodeItem(arena, &spki, + nsslowcert_SubjectPublicKeyInfoTemplate, &newDerSubjKeyInfo); + if (rv != SECSuccess) { + PORT_FreeArena (arena, PR_FALSE); + return NULL; + } + + /* Convert bit string length from bits to bytes */ + os = spki.subjectPublicKey; + DER_ConvertBitString (&os); + + tag = SECOID_GetAlgorithmTag(&spki.algorithm); + switch ( tag ) { + case SEC_OID_X500_RSA_ENCRYPTION: + case SEC_OID_PKCS1_RSA_ENCRYPTION: + pubk->keyType = NSSLOWKEYRSAKey; + prepare_low_rsa_pub_key_for_asn1(pubk); + rv = SEC_QuickDERDecodeItem(arena, pubk, + nsslowcert_RSAPublicKeyTemplate, &os); + if (rv == SECSuccess) + return pubk; + break; + case SEC_OID_ANSIX9_DSA_SIGNATURE: + pubk->keyType = NSSLOWKEYDSAKey; + prepare_low_dsa_pub_key_for_asn1(pubk); + rv = SEC_QuickDERDecodeItem(arena, pubk, + nsslowcert_DSAPublicKeyTemplate, &os); + if (rv == SECSuccess) return pubk; + break; + case SEC_OID_X942_DIFFIE_HELMAN_KEY: + pubk->keyType = NSSLOWKEYDHKey; + prepare_low_dh_pub_key_for_asn1(pubk); + rv = SEC_QuickDERDecodeItem(arena, pubk, + nsslowcert_DHPublicKeyTemplate, &os); + if (rv == SECSuccess) return pubk; + break; +#ifndef NSS_DISABLE_ECC + case SEC_OID_ANSIX962_EC_PUBLIC_KEY: + pubk->keyType = NSSLOWKEYECKey; + /* Since PKCS#11 directly takes the DER encoding of EC params + * and public value, we don't need any decoding here. + */ + rv = SECITEM_CopyItem(arena, &pubk->u.ec.ecParams.DEREncoding, + &spki.algorithm.parameters); + if ( rv != SECSuccess ) + break; + + /* Fill out the rest of the ecParams structure + * based on the encoded params + */ + if (LGEC_FillParams(arena, &pubk->u.ec.ecParams.DEREncoding, + &pubk->u.ec.ecParams) != SECSuccess) + break; + + rv = SECITEM_CopyItem(arena, &pubk->u.ec.publicValue, &os); + if (rv == SECSuccess) return pubk; + break; +#endif /* NSS_DISABLE_ECC */ + default: + rv = SECFailure; + break; + } + + lg_nsslowkey_DestroyPublicKey (pubk); + return NULL; +} +