blob: 5a349f0aad2ec51682ca498d912b41e740364b75 [file] [log] [blame]
/* 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,
&notBefore->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,
&notAfter->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, &notBeforeA, &notAfterA);
if (rv != SECSuccess) {
return (PR_FALSE);
}
rv = nsslowcert_GetCertTimes(certb, &notBeforeB, &notAfterB);
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 (name == 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:
case SEC_OID_PKCS1_RSA_PSS_SIGNATURE:
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;
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;
default:
rv = SECFailure;
break;
}
lg_nsslowkey_DestroyPublicKey(pubk);
return NULL;
}