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