mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
15f8bb0d21
r=wtc
832 lines
23 KiB
C
832 lines
23 KiB
C
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is the Netscape security libraries.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 1994-2000
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
/*
|
|
* Certificate handling code
|
|
*
|
|
* $Id: lowcert.c,v 1.5 2009/04/12 01:31:45 nelson%bolyard.com Exp $
|
|
*/
|
|
|
|
#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;
|
|
|
|
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;
|
|
|
|
*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);
|
|
}
|
|
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);
|
|
break;
|
|
}
|
|
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(PRArenaPool *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);
|
|
|
|
/* 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(PRArenaPool *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;
|
|
PRArenaPool *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;
|
|
#ifdef NSS_ENABLE_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_ENABLE_ECC */
|
|
default:
|
|
rv = SECFailure;
|
|
break;
|
|
}
|
|
|
|
nsslowkey_DestroyPublicKey (pubk);
|
|
return NULL;
|
|
}
|
|
|