diff options
Diffstat (limited to 'security/nss/lib/cryptohi/seckey.c')
-rw-r--r-- | security/nss/lib/cryptohi/seckey.c | 1965 |
1 files changed, 1965 insertions, 0 deletions
diff --git a/security/nss/lib/cryptohi/seckey.c b/security/nss/lib/cryptohi/seckey.c new file mode 100644 index 000000000..1c1bd24af --- /dev/null +++ b/security/nss/lib/cryptohi/seckey.c @@ -0,0 +1,1965 @@ +/* + * 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 Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * Dr Stephen Henson <stephen.henson@gemplus.com> + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ +#include "cryptohi.h" +#include "keyhi.h" +#include "secrng.h" +#include "secoid.h" +#include "secitem.h" +#include "secder.h" +#include "base64.h" +#include "secasn1.h" +#include "cert.h" +#include "pk11func.h" +#include "secerr.h" +#include "secdig.h" +#include "prtime.h" + +const SEC_ASN1Template CERT_SubjectPublicKeyInfoTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTSubjectPublicKeyInfo) }, + { SEC_ASN1_INLINE, + offsetof(CERTSubjectPublicKeyInfo,algorithm), + SECOID_AlgorithmIDTemplate }, + { SEC_ASN1_BIT_STRING, + offsetof(CERTSubjectPublicKeyInfo,subjectPublicKey), }, + { 0, } +}; + +const SEC_ASN1Template CERT_PublicKeyAndChallengeTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTPublicKeyAndChallenge) }, + { SEC_ASN1_ANY, offsetof(CERTPublicKeyAndChallenge,spki) }, + { SEC_ASN1_IA5_STRING, offsetof(CERTPublicKeyAndChallenge,challenge) }, + { 0 } +}; + +const SEC_ASN1Template SECKEY_RSAPublicKeyTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECKEYPublicKey) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYPublicKey,u.rsa.modulus), }, + { SEC_ASN1_INTEGER, offsetof(SECKEYPublicKey,u.rsa.publicExponent), }, + { 0, } +}; + +const SEC_ASN1Template SECKEY_DSAPublicKeyTemplate[] = { + { SEC_ASN1_INTEGER, offsetof(SECKEYPublicKey,u.dsa.publicValue), }, + { 0, } +}; + +const SEC_ASN1Template SECKEY_PQGParamsTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECKEYPQGParams) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYPQGParams,prime) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYPQGParams,subPrime) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYPQGParams,base) }, + { 0, } +}; + +const SEC_ASN1Template SECKEY_DHPublicKeyTemplate[] = { + { SEC_ASN1_INTEGER, offsetof(SECKEYPublicKey,u.dh.publicValue), }, + { 0, } +}; + +const SEC_ASN1Template SECKEY_DHParamKeyTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECKEYPublicKey) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYPublicKey,u.dh.prime), }, + { SEC_ASN1_INTEGER, offsetof(SECKEYPublicKey,u.dh.base), }, + /* XXX chrisk: this needs to be expanded for decoding of j and validationParms (RFC2459 7.3.2) */ + { SEC_ASN1_SKIP_REST }, + { 0, } +}; + +const SEC_ASN1Template SECKEY_FortezzaParameterTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECKEYPQGParams) }, + { SEC_ASN1_OCTET_STRING, offsetof(SECKEYPQGParams,prime), }, + { SEC_ASN1_OCTET_STRING, offsetof(SECKEYPQGParams,subPrime), }, + { SEC_ASN1_OCTET_STRING, offsetof(SECKEYPQGParams,base), }, + { 0 }, +}; + +const SEC_ASN1Template SECKEY_FortezzaDiffParameterTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECKEYDiffPQGParams) }, + { SEC_ASN1_INLINE, offsetof(SECKEYDiffPQGParams,DiffKEAParams), + SECKEY_FortezzaParameterTemplate}, + { SEC_ASN1_INLINE, offsetof(SECKEYDiffPQGParams,DiffDSAParams), + SECKEY_FortezzaParameterTemplate}, + { 0 }, +}; + +const SEC_ASN1Template SECKEY_FortezzaPreParamTemplate[] = { + { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | + SEC_ASN1_CONTEXT_SPECIFIC | 1, offsetof(SECKEYPQGDualParams,CommParams), + SECKEY_FortezzaParameterTemplate}, + { 0, } +}; + +const SEC_ASN1Template SECKEY_FortezzaAltPreParamTemplate[] = { + { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | + SEC_ASN1_CONTEXT_SPECIFIC | 0, offsetof(SECKEYPQGDualParams,DiffParams), + SECKEY_FortezzaDiffParameterTemplate}, + { 0, } +}; + +const SEC_ASN1Template SECKEY_KEAPublicKeyTemplate[] = { + { SEC_ASN1_INTEGER, offsetof(SECKEYPublicKey,u.kea.publicValue), }, + { 0, } +}; + +const SEC_ASN1Template SECKEY_KEAParamsTemplate[] = { + { SEC_ASN1_OCTET_STRING, offsetof(SECKEYPublicKey,u.kea.params.hash), }, + { 0, } +}; + +SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_DSAPublicKeyTemplate) +SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_RSAPublicKeyTemplate) +SEC_ASN1_CHOOSER_IMPLEMENT(CERT_SubjectPublicKeyInfoTemplate) + +/* + * 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_rsa_pub_key_for_asn1(SECKEYPublicKey *pubk) +{ + pubk->u.rsa.modulus.type = siUnsignedInteger; + pubk->u.rsa.publicExponent.type = siUnsignedInteger; +} + +static void +prepare_dsa_pub_key_for_asn1(SECKEYPublicKey *pubk) +{ + pubk->u.dsa.publicValue.type = siUnsignedInteger; +} + +static void +prepare_pqg_params_for_asn1(SECKEYPQGParams *params) +{ + params->prime.type = siUnsignedInteger; + params->subPrime.type = siUnsignedInteger; + params->base.type = siUnsignedInteger; +} + +static void +prepare_dh_pub_key_for_asn1(SECKEYPublicKey *pubk) +{ + pubk->u.dh.prime.type = siUnsignedInteger; + pubk->u.dh.base.type = siUnsignedInteger; + pubk->u.dh.publicValue.type = siUnsignedInteger; +} + +static void +prepare_kea_pub_key_for_asn1(SECKEYPublicKey *pubk) +{ + pubk->u.kea.publicValue.type = siUnsignedInteger; +} + +/* Create an RSA key pair is any slot able to do so. +** The created keys are "session" (temporary), not "token" (permanent), +** and they are "sensitive", which makes them costly to move to another token. +*/ +SECKEYPrivateKey * +SECKEY_CreateRSAPrivateKey(int keySizeInBits,SECKEYPublicKey **pubk, void *cx) +{ + SECKEYPrivateKey *privk; + PK11SlotInfo *slot = PK11_GetBestSlot(CKM_RSA_PKCS_KEY_PAIR_GEN,cx); + PK11RSAGenParams param; + + param.keySizeInBits = keySizeInBits; + param.pe = 65537L; + + privk = PK11_GenerateKeyPair(slot,CKM_RSA_PKCS_KEY_PAIR_GEN,¶m,pubk, + PR_FALSE, PR_TRUE, cx); + PK11_FreeSlot(slot); + return(privk); +} + +/* Create a DH key pair in any slot able to do so, +** This is a "session" (temporary), not "token" (permanent) key. +** Because of the high probability that this key will need to be moved to +** another token, and the high cost of moving "sensitive" keys, we attempt +** to create this key pair without the "sensitive" attribute, but revert to +** creating a "sensitive" key if necessary. +*/ +SECKEYPrivateKey * +SECKEY_CreateDHPrivateKey(SECKEYDHParams *param, SECKEYPublicKey **pubk, void *cx) +{ + SECKEYPrivateKey *privk; + PK11SlotInfo *slot = PK11_GetBestSlot(CKM_DH_PKCS_KEY_PAIR_GEN,cx); + + privk = PK11_GenerateKeyPair(slot, CKM_DH_PKCS_KEY_PAIR_GEN, param, + pubk, PR_FALSE, PR_FALSE, cx); + if (!privk) + privk = PK11_GenerateKeyPair(slot, CKM_DH_PKCS_KEY_PAIR_GEN, param, + pubk, PR_FALSE, PR_TRUE, cx); + + PK11_FreeSlot(slot); + return(privk); +} + +void +SECKEY_DestroyPrivateKey(SECKEYPrivateKey *privk) +{ + if (privk) { + if (privk->pkcs11Slot) { + if (privk->pkcs11IsTemp) { + PK11_DestroyObject(privk->pkcs11Slot,privk->pkcs11ID); + } + PK11_FreeSlot(privk->pkcs11Slot); + + } + if (privk->arena) { + PORT_FreeArena(privk->arena, PR_TRUE); + } + } +} + +void +SECKEY_DestroyPublicKey(SECKEYPublicKey *pubk) +{ + if (pubk) { + if (pubk->pkcs11Slot) { + if (!PK11_IsPermObject(pubk->pkcs11Slot,pubk->pkcs11ID)) { + PK11_DestroyObject(pubk->pkcs11Slot,pubk->pkcs11ID); + } + PK11_FreeSlot(pubk->pkcs11Slot); + } + if (pubk->arena) { + PORT_FreeArena(pubk->arena, PR_FALSE); + } + } +} + +SECStatus +SECKEY_CopySubjectPublicKeyInfo(PRArenaPool *arena, + CERTSubjectPublicKeyInfo *to, + CERTSubjectPublicKeyInfo *from) +{ + SECStatus rv; + + rv = SECOID_CopyAlgorithmID(arena, &to->algorithm, &from->algorithm); + if (rv == SECSuccess) + rv = SECITEM_CopyItem(arena, &to->subjectPublicKey, &from->subjectPublicKey); + + return rv; +} + +SECStatus +SECKEY_KEASetParams(SECKEYKEAParams * params, SECKEYPublicKey * pubKey) { + + if (pubKey->keyType == fortezzaKey) { + /* the key is a fortezza V1 public key */ + + /* obtain hash of pubkey->u.fortezza.params.prime.data + + pubkey->u.fortezza.params.subPrime.data + + pubkey->u.fortezza.params.base.data */ + + /* store hash in params->hash */ + + } else if (pubKey->keyType == keaKey) { + + /* the key is a new fortezza KEA public key. */ + SECITEM_CopyItem(pubKey->arena, ¶ms->hash, + &pubKey->u.kea.params.hash ); + + } else { + + /* the key has no KEA parameters */ + return SECFailure; + } + return SECSuccess; +} + + +SECStatus +SECKEY_KEAParamCompare(CERTCertificate *cert1,CERTCertificate *cert2) +{ + + SECStatus rv; + + SECKEYPublicKey *pubKey1 = 0; + SECKEYPublicKey *pubKey2 = 0; + + SECKEYKEAParams params1; + SECKEYKEAParams params2; + + + rv = SECFailure; + + /* get cert1's public key */ + pubKey1 = CERT_ExtractPublicKey(cert1); + if ( !pubKey1 ) { + return(SECFailure); + } + + + /* get cert2's public key */ + pubKey2 = CERT_ExtractPublicKey(cert2); + if ( !pubKey2 ) { + return(SECFailure); + } + + /* handle the case when both public keys are new + * fortezza KEA public keys. */ + + if ((pubKey1->keyType == keaKey) && + (pubKey2->keyType == keaKey) ) { + + rv = (SECStatus)SECITEM_CompareItem(&pubKey1->u.kea.params.hash, + &pubKey2->u.kea.params.hash); + goto done; + } + + /* handle the case when both public keys are old fortezza + * public keys. */ + + if ((pubKey1->keyType == fortezzaKey) && + (pubKey2->keyType == fortezzaKey) ) { + + rv = (SECStatus)SECITEM_CompareItem(&pubKey1->u.fortezza.keaParams.prime, + &pubKey2->u.fortezza.keaParams.prime); + + if (rv == SECEqual) { + rv = (SECStatus)SECITEM_CompareItem(&pubKey1->u.fortezza.keaParams.subPrime, + &pubKey2->u.fortezza.keaParams.subPrime); + } + + if (rv == SECEqual) { + rv = (SECStatus)SECITEM_CompareItem(&pubKey1->u.fortezza.keaParams.base, + &pubKey2->u.fortezza.keaParams.base); + } + + goto done; + } + + + /* handle the case when the public keys are a mixture of + * old and new. */ + + rv = SECKEY_KEASetParams(¶ms1, pubKey1); + if (rv != SECSuccess) return rv; + + rv = SECKEY_KEASetParams(¶ms2, pubKey2); + if (rv != SECSuccess) return rv; + + rv = (SECStatus)SECITEM_CompareItem(¶ms1.hash, ¶ms2.hash); + +done: + SECKEY_DestroyPublicKey(pubKey1); + SECKEY_DestroyPublicKey(pubKey2); + + return rv; /* returns SECEqual if parameters are equal */ + +} + + +/* Procedure to update the pqg parameters for a cert's public key. + * pqg parameters only need to be updated for DSA and fortezza certificates. + * The procedure uses calls to itself recursively to update a certificate + * issuer's pqg parameters. Some important rules are: + * - Do nothing if the cert already has PQG parameters. + * - If the cert does not have PQG parameters, obtain them from the issuer. + * - A valid cert chain cannot have a DSA or Fortezza cert without + * pqg parameters that has a parent that is not a DSA or Fortezza cert. + * - pqg paramters are stored in two different formats: the standard + * DER encoded format and the fortezza-only wrapped format. The params + * should be copied from issuer to subject cert without modifying the + * formats. The public key extraction code will deal with the different + * formats at the time of extraction. */ + +SECStatus +seckey_UpdateCertPQGChain(CERTCertificate * subjectCert, int count) +{ + SECStatus rv, rvCompare; + SECOidData *oid=NULL; + int tag; + CERTSubjectPublicKeyInfo * subjectSpki=NULL; + CERTSubjectPublicKeyInfo * issuerSpki=NULL; + CERTCertificate *issuerCert = NULL; + + rv = SECSuccess; + + /* increment cert chain length counter*/ + count++; + + /* check if cert chain length exceeds the maximum length*/ + if (count > CERT_MAX_CERT_CHAIN) { + return SECFailure; + } + + oid = SECOID_FindOID(&subjectCert->subjectPublicKeyInfo.algorithm.algorithm); + if (oid != NULL) { + tag = oid->offset; + + /* Check if cert has a DSA or Fortezza public key. If not, return + * success since no PQG params need to be updated. */ + + if ( (tag != SEC_OID_MISSI_KEA_DSS_OLD) && + (tag != SEC_OID_MISSI_DSS_OLD) && + (tag != SEC_OID_MISSI_KEA_DSS) && + (tag != SEC_OID_MISSI_DSS) && + (tag != SEC_OID_ANSIX9_DSA_SIGNATURE) && + (tag != SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST) && + (tag != SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST) && + (tag != SEC_OID_SDN702_DSA_SIGNATURE) ) { + + return SECSuccess; + } + } else { + return SECFailure; /* return failure if oid is NULL */ + } + + /* if cert has PQG parameters, return success */ + + subjectSpki=&subjectCert->subjectPublicKeyInfo; + + if (subjectSpki->algorithm.parameters.len != 0) { + return SECSuccess; + } + + /* check if the cert is self-signed */ + rvCompare = (SECStatus)SECITEM_CompareItem(&subjectCert->derSubject, + &subjectCert->derIssuer); + if (rvCompare == SECEqual) { + /* fail since cert is self-signed and has no pqg params. */ + return SECFailure; + } + + /* get issuer cert */ + issuerCert = CERT_FindCertIssuer(subjectCert, PR_Now(), certUsageAnyCA); + if ( ! issuerCert ) { + return SECFailure; + } + + /* if parent is not DSA or fortezza, return failure since + we don't allow this case. */ + + oid = SECOID_FindOID(&issuerCert->subjectPublicKeyInfo.algorithm.algorithm); + if (oid != NULL) { + tag = oid->offset; + + /* Check if issuer cert has a DSA or Fortezza public key. If not, + * return failure. */ + + if ( (tag != SEC_OID_MISSI_KEA_DSS_OLD) && + (tag != SEC_OID_MISSI_DSS_OLD) && + (tag != SEC_OID_MISSI_KEA_DSS) && + (tag != SEC_OID_MISSI_DSS) && + (tag != SEC_OID_ANSIX9_DSA_SIGNATURE) && + (tag != SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST) && + (tag != SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST) && + (tag != SEC_OID_SDN702_DSA_SIGNATURE) ) { + + return SECFailure; + } + } else { + return SECFailure; /* return failure if oid is NULL */ + } + + + /* at this point the subject cert has no pqg parameters and the + * issuer cert has a DSA or fortezza public key. Update the issuer's + * pqg parameters with a recursive call to this same function. */ + + rv = seckey_UpdateCertPQGChain(issuerCert, count); + if (rv != SECSuccess) return rv; + + /* ensure issuer has pqg parameters */ + + issuerSpki=&issuerCert->subjectPublicKeyInfo; + if (issuerSpki->algorithm.parameters.len == 0) { + rv = SECFailure; + } + + /* if update was successful and pqg params present, then copy the + * parameters to the subject cert's key. */ + + if (rv == SECSuccess) { + rv = SECITEM_CopyItem(subjectCert->arena, + &subjectSpki->algorithm.parameters, + &issuerSpki->algorithm.parameters); + } + + return rv; + +} + + +SECStatus +SECKEY_UpdateCertPQG(CERTCertificate * subjectCert) +{ + return(seckey_UpdateCertPQGChain(subjectCert,0)); +} + + +/* Decode the PQG parameters. The params could be stored in two + * possible formats, the old fortezza-only wrapped format or + * the standard DER encoded format. Store the decoded parameters in an + * old fortezza cert data structure */ + +SECStatus +SECKEY_FortezzaDecodePQGtoOld(PRArenaPool *arena, SECKEYPublicKey *pubk, + SECItem *params) { + SECStatus rv; + SECKEYPQGDualParams dual_params; + + if (params == NULL) return SECFailure; + + if (params->data == NULL) return SECFailure; + + /* Check if params use the standard format. + * The value 0xa1 will appear in the first byte of the parameter data + * if the PQG parameters are not using the standard format. This + * code should be changed to use a better method to detect non-standard + * parameters. */ + + if ((params->data[0] != 0xa1) && + (params->data[0] != 0xa0)) { + + /* PQG params are in the standard format */ + + /* Store DSA PQG parameters */ + prepare_pqg_params_for_asn1(&pubk->u.fortezza.params); + rv = SEC_ASN1DecodeItem(arena, &pubk->u.fortezza.params, + SECKEY_PQGParamsTemplate, + params); + + if (rv == SECSuccess) { + + /* Copy the DSA PQG parameters to the KEA PQG parameters. */ + rv = SECITEM_CopyItem(arena, &pubk->u.fortezza.keaParams.prime, + &pubk->u.fortezza.params.prime); + if (rv != SECSuccess) return rv; + rv = SECITEM_CopyItem(arena, &pubk->u.fortezza.keaParams.subPrime, + &pubk->u.fortezza.params.subPrime); + if (rv != SECSuccess) return rv; + rv = SECITEM_CopyItem(arena, &pubk->u.fortezza.keaParams.base, + &pubk->u.fortezza.params.base); + if (rv != SECSuccess) return rv; + } + + } else { + + dual_params.CommParams.prime.len = 0; + dual_params.CommParams.subPrime.len = 0; + dual_params.CommParams.base.len = 0; + dual_params.DiffParams.DiffDSAParams.prime.len = 0; + dual_params.DiffParams.DiffDSAParams.subPrime.len = 0; + dual_params.DiffParams.DiffDSAParams.base.len = 0; + + /* else the old fortezza-only wrapped format is used. */ + + if (params->data[0] == 0xa1) { + rv = SEC_ASN1DecodeItem(arena, &dual_params, + SECKEY_FortezzaPreParamTemplate, params); + } else { + rv = SEC_ASN1DecodeItem(arena, &dual_params, + SECKEY_FortezzaAltPreParamTemplate, params); + } + + if (rv < 0) return rv; + + if ( (dual_params.CommParams.prime.len > 0) && + (dual_params.CommParams.subPrime.len > 0) && + (dual_params.CommParams.base.len > 0) ) { + /* copy in common params */ + + rv = SECITEM_CopyItem(arena, &pubk->u.fortezza.params.prime, + &dual_params.CommParams.prime); + if (rv != SECSuccess) return rv; + rv = SECITEM_CopyItem(arena, &pubk->u.fortezza.params.subPrime, + &dual_params.CommParams.subPrime); + if (rv != SECSuccess) return rv; + rv = SECITEM_CopyItem(arena, &pubk->u.fortezza.params.base, + &dual_params.CommParams.base); + + /* Copy the DSA PQG parameters to the KEA PQG parameters. */ + rv = SECITEM_CopyItem(arena, &pubk->u.fortezza.keaParams.prime, + &pubk->u.fortezza.params.prime); + if (rv != SECSuccess) return rv; + rv = SECITEM_CopyItem(arena, &pubk->u.fortezza.keaParams.subPrime, + &pubk->u.fortezza.params.subPrime); + if (rv != SECSuccess) return rv; + rv = SECITEM_CopyItem(arena, &pubk->u.fortezza.keaParams.base, + &pubk->u.fortezza.params.base); + if (rv != SECSuccess) return rv; + + } else { + + /* else copy in different params */ + + /* copy DSA PQG parameters */ + rv = SECITEM_CopyItem(arena, &pubk->u.fortezza.params.prime, + &dual_params.DiffParams.DiffDSAParams.prime); + if (rv != SECSuccess) return rv; + rv = SECITEM_CopyItem(arena, &pubk->u.fortezza.params.subPrime, + &dual_params.DiffParams.DiffDSAParams.subPrime); + if (rv != SECSuccess) return rv; + rv = SECITEM_CopyItem(arena, &pubk->u.fortezza.params.base, + &dual_params.DiffParams.DiffDSAParams.base); + + /* copy KEA PQG parameters */ + + rv = SECITEM_CopyItem(arena, &pubk->u.fortezza.keaParams.prime, + &dual_params.DiffParams.DiffKEAParams.prime); + if (rv != SECSuccess) return rv; + rv = SECITEM_CopyItem(arena, &pubk->u.fortezza.keaParams.subPrime, + &dual_params.DiffParams.DiffKEAParams.subPrime); + if (rv != SECSuccess) return rv; + rv = SECITEM_CopyItem(arena, &pubk->u.fortezza.keaParams.base, + &dual_params.DiffParams.DiffKEAParams.base); + } + + } + return rv; +} + + +/* Decode the DSA PQG parameters. The params could be stored in two + * possible formats, the old fortezza-only wrapped format or + * the normal standard format. Store the decoded parameters in + * a V3 certificate data structure. */ + +SECStatus +SECKEY_DSADecodePQG(PRArenaPool *arena, SECKEYPublicKey *pubk, SECItem *params) { + SECStatus rv; + SECKEYPQGDualParams dual_params; + + if (params == NULL) return SECFailure; + + if (params->data == NULL) return SECFailure; + + /* Check if params use the standard format. + * The value 0xa1 will appear in the first byte of the parameter data + * if the PQG parameters are not using the standard format. This + * code should be changed to use a better method to detect non-standard + * parameters. */ + + if ((params->data[0] != 0xa1) && + (params->data[0] != 0xa0)) { + + /* PQG params are in the standard format */ + prepare_pqg_params_for_asn1(&pubk->u.dsa.params); + rv = SEC_ASN1DecodeItem(arena, &pubk->u.dsa.params, + SECKEY_PQGParamsTemplate, + params); + } else { + + dual_params.CommParams.prime.len = 0; + dual_params.CommParams.subPrime.len = 0; + dual_params.CommParams.base.len = 0; + dual_params.DiffParams.DiffDSAParams.prime.len = 0; + dual_params.DiffParams.DiffDSAParams.subPrime.len = 0; + dual_params.DiffParams.DiffDSAParams.base.len = 0; + + /* else the old fortezza-only wrapped format is used. */ + if (params->data[0] == 0xa1) { + rv = SEC_ASN1DecodeItem(arena, &dual_params, + SECKEY_FortezzaPreParamTemplate, params); + } else { + rv = SEC_ASN1DecodeItem(arena, &dual_params, + SECKEY_FortezzaAltPreParamTemplate, params); + } + + if (rv < 0) return rv; + + if ( (dual_params.CommParams.prime.len > 0) && + (dual_params.CommParams.subPrime.len > 0) && + (dual_params.CommParams.base.len > 0) ) { + /* copy in common params */ + + rv = SECITEM_CopyItem(arena, &pubk->u.dsa.params.prime, + &dual_params.CommParams.prime); + if (rv != SECSuccess) return rv; + rv = SECITEM_CopyItem(arena, &pubk->u.dsa.params.subPrime, + &dual_params.CommParams.subPrime); + if (rv != SECSuccess) return rv; + rv = SECITEM_CopyItem(arena, &pubk->u.dsa.params.base, + &dual_params.CommParams.base); + + } else { + + /* else copy in different params */ + + /* copy DSA PQG parameters */ + rv = SECITEM_CopyItem(arena, &pubk->u.dsa.params.prime, + &dual_params.DiffParams.DiffDSAParams.prime); + if (rv != SECSuccess) return rv; + rv = SECITEM_CopyItem(arena, &pubk->u.dsa.params.subPrime, + &dual_params.DiffParams.DiffDSAParams.subPrime); + if (rv != SECSuccess) return rv; + rv = SECITEM_CopyItem(arena, &pubk->u.dsa.params.base, + &dual_params.DiffParams.DiffDSAParams.base); + + } + } + return rv; +} + + + +/* Decodes the DER encoded fortezza public key and stores the results in a + * structure of type SECKEYPublicKey. */ + +SECStatus +SECKEY_FortezzaDecodeCertKey(PRArenaPool *arena, SECKEYPublicKey *pubk, + SECItem *rawkey, SECItem *params) { + + unsigned char *rawptr = rawkey->data; + unsigned char *end = rawkey->data + rawkey->len; + unsigned char *clearptr; + + /* first march down and decode the raw key data */ + + /* version */ + pubk->u.fortezza.KEAversion = *rawptr++; + if (*rawptr++ != 0x01) { + return SECFailure; + } + + /* KMID */ + PORT_Memcpy(pubk->u.fortezza.KMID,rawptr, + sizeof(pubk->u.fortezza.KMID)); + rawptr += sizeof(pubk->u.fortezza.KMID); + + /* clearance (the string up to the first byte with the hi-bit on */ + clearptr = rawptr; + while ((rawptr < end) && (*rawptr++ & 0x80)); + + if (rawptr >= end) { return SECFailure; } + pubk->u.fortezza.clearance.len = rawptr - clearptr; + pubk->u.fortezza.clearance.data = + (unsigned char*)PORT_ArenaZAlloc(arena,pubk->u.fortezza.clearance.len); + if (pubk->u.fortezza.clearance.data == NULL) { + return SECFailure; + } + PORT_Memcpy(pubk->u.fortezza.clearance.data,clearptr, + pubk->u.fortezza.clearance.len); + + /* KEAPrivilege (the string up to the first byte with the hi-bit on */ + clearptr = rawptr; + while ((rawptr < end) && (*rawptr++ & 0x80)); + if (rawptr >= end) { return SECFailure; } + pubk->u.fortezza.KEApriviledge.len = rawptr - clearptr; + pubk->u.fortezza.KEApriviledge.data = + (unsigned char*)PORT_ArenaZAlloc(arena,pubk->u.fortezza.KEApriviledge.len); + if (pubk->u.fortezza.KEApriviledge.data == NULL) { + return SECFailure; + } + PORT_Memcpy(pubk->u.fortezza.KEApriviledge.data,clearptr, + pubk->u.fortezza.KEApriviledge.len); + + + /* now copy the key. The next to bytes are the key length, and the + * key follows */ + pubk->u.fortezza.KEAKey.len = (*rawptr << 8) | rawptr[1]; + + rawptr += 2; + if (rawptr+pubk->u.fortezza.KEAKey.len > end) { return SECFailure; } + pubk->u.fortezza.KEAKey.data = + (unsigned char*)PORT_ArenaZAlloc(arena,pubk->u.fortezza.KEAKey.len); + if (pubk->u.fortezza.KEAKey.data == NULL) { + return SECFailure; + } + PORT_Memcpy(pubk->u.fortezza.KEAKey.data,rawptr, + pubk->u.fortezza.KEAKey.len); + rawptr += pubk->u.fortezza.KEAKey.len; + + /* shared key */ + if (rawptr >= end) { + pubk->u.fortezza.DSSKey.len = pubk->u.fortezza.KEAKey.len; + /* this depends on the fact that we are going to get freed with an + * ArenaFree call. We cannot free DSSKey and KEAKey separately */ + pubk->u.fortezza.DSSKey.data= + pubk->u.fortezza.KEAKey.data; + pubk->u.fortezza.DSSpriviledge.len = + pubk->u.fortezza.KEApriviledge.len; + pubk->u.fortezza.DSSpriviledge.data = + pubk->u.fortezza.DSSpriviledge.data; + goto done; + } + + + /* DSS Version is next */ + pubk->u.fortezza.DSSversion = *rawptr++; + + if (*rawptr++ != 2) { + return SECFailure; + } + + /* DSSPrivilege (the string up to the first byte with the hi-bit on */ + clearptr = rawptr; + while ((rawptr < end) && (*rawptr++ & 0x80)); + if (rawptr >= end) { return SECFailure; } + pubk->u.fortezza.DSSpriviledge.len = rawptr - clearptr; + pubk->u.fortezza.DSSpriviledge.data = + (unsigned char*)PORT_ArenaZAlloc(arena,pubk->u.fortezza.DSSpriviledge.len); + if (pubk->u.fortezza.DSSpriviledge.data == NULL) { + return SECFailure; + } + PORT_Memcpy(pubk->u.fortezza.DSSpriviledge.data,clearptr, + pubk->u.fortezza.DSSpriviledge.len); + + /* finally copy the DSS key. The next to bytes are the key length, + * and the key follows */ + pubk->u.fortezza.DSSKey.len = (*rawptr << 8) | rawptr[1]; + + rawptr += 2; + if (rawptr+pubk->u.fortezza.DSSKey.len > end){ return SECFailure; } + pubk->u.fortezza.DSSKey.data = + (unsigned char*)PORT_ArenaZAlloc(arena,pubk->u.fortezza.DSSKey.len); + if (pubk->u.fortezza.DSSKey.data == NULL) { + return SECFailure; + } + PORT_Memcpy(pubk->u.fortezza.DSSKey.data,rawptr, + pubk->u.fortezza.DSSKey.len); + + /* ok, now we decode the parameters */ +done: + + return SECKEY_FortezzaDecodePQGtoOld(arena, pubk, params); +} + + +/* Function used to determine what kind of cert we are dealing with. */ +KeyType +CERT_GetCertKeyType (CERTSubjectPublicKeyInfo *spki) { + int tag; + KeyType keyType; + + tag = SECOID_GetAlgorithmTag(&spki->algorithm); + switch (tag) { + case SEC_OID_X500_RSA_ENCRYPTION: + case SEC_OID_PKCS1_RSA_ENCRYPTION: + keyType = rsaKey; + break; + case SEC_OID_ANSIX9_DSA_SIGNATURE: + keyType = dsaKey; + break; + case SEC_OID_MISSI_KEA_DSS_OLD: + case SEC_OID_MISSI_KEA_DSS: + case SEC_OID_MISSI_DSS_OLD: + case SEC_OID_MISSI_DSS: + keyType = fortezzaKey; + break; + case SEC_OID_MISSI_KEA: + case SEC_OID_MISSI_ALT_KEA: + keyType = keaKey; + break; + case SEC_OID_X942_DIFFIE_HELMAN_KEY: + keyType = dhKey; + default: + keyType = nullKey; + } + return keyType; +} + +static SECKEYPublicKey * +seckey_ExtractPublicKey(CERTSubjectPublicKeyInfo *spki) +{ + SECKEYPublicKey *pubk; + SECItem os; + SECStatus rv; + PRArenaPool *arena; + SECOidTag tag; + + arena = PORT_NewArena (DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) + return NULL; + + pubk = (SECKEYPublicKey *) PORT_ArenaZAlloc(arena, sizeof(SECKEYPublicKey)); + if (pubk == NULL) { + PORT_FreeArena (arena, PR_FALSE); + return NULL; + } + + pubk->arena = arena; + pubk->pkcs11Slot = 0; + pubk->pkcs11ID = CK_INVALID_HANDLE; + + + /* 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 = rsaKey; + prepare_rsa_pub_key_for_asn1(pubk); + rv = SEC_ASN1DecodeItem(arena, pubk, SECKEY_RSAPublicKeyTemplate, &os); + if (rv == SECSuccess) + return pubk; + break; + case SEC_OID_ANSIX9_DSA_SIGNATURE: + case SEC_OID_SDN702_DSA_SIGNATURE: + pubk->keyType = dsaKey; + prepare_dsa_pub_key_for_asn1(pubk); + rv = SEC_ASN1DecodeItem(arena, pubk, SECKEY_DSAPublicKeyTemplate, &os); + if (rv != SECSuccess) break; + + rv = SECKEY_DSADecodePQG(arena, pubk, + &spki->algorithm.parameters); + + if (rv == SECSuccess) return pubk; + break; + case SEC_OID_X942_DIFFIE_HELMAN_KEY: + pubk->keyType = dhKey; + prepare_dh_pub_key_for_asn1(pubk); + rv = SEC_ASN1DecodeItem(arena, pubk, SECKEY_DHPublicKeyTemplate, &os); + if (rv != SECSuccess) break; + + rv = SEC_ASN1DecodeItem(arena, pubk, SECKEY_DHParamKeyTemplate, + &spki->algorithm.parameters); + + if (rv == SECSuccess) return pubk; + break; + case SEC_OID_MISSI_KEA_DSS_OLD: + case SEC_OID_MISSI_KEA_DSS: + case SEC_OID_MISSI_DSS_OLD: + case SEC_OID_MISSI_DSS: + pubk->keyType = fortezzaKey; + rv = SECKEY_FortezzaDecodeCertKey(arena, pubk, &os, + &spki->algorithm.parameters); + if (rv == SECSuccess) + return pubk; + break; + + case SEC_OID_MISSI_KEA: + pubk->keyType = keaKey; + + prepare_kea_pub_key_for_asn1(pubk); + rv = SEC_ASN1DecodeItem(arena, pubk, + SECKEY_KEAPublicKeyTemplate, &os); + if (rv != SECSuccess) break; + + + rv = SEC_ASN1DecodeItem(arena, pubk, SECKEY_KEAParamsTemplate, + &spki->algorithm.parameters); + + if (rv == SECSuccess) + return pubk; + + break; + + case SEC_OID_MISSI_ALT_KEA: + pubk->keyType = keaKey; + + rv = SECITEM_CopyItem(arena,&pubk->u.kea.publicValue,&os); + if (rv != SECSuccess) break; + + rv = SEC_ASN1DecodeItem(arena, pubk, SECKEY_KEAParamsTemplate, + &spki->algorithm.parameters); + + if (rv == SECSuccess) + return pubk; + + break; + + + default: + rv = SECFailure; + break; + } + + SECKEY_DestroyPublicKey (pubk); + return NULL; +} + + +/* required for JSS */ +SECKEYPublicKey * +SECKEY_ExtractPublicKey(CERTSubjectPublicKeyInfo *spki) +{ + return seckey_ExtractPublicKey(spki); +} + +SECKEYPublicKey * +CERT_ExtractPublicKey(CERTCertificate *cert) +{ + SECStatus rv; + + rv = SECKEY_UpdateCertPQG(cert); + if (rv != SECSuccess) return NULL; + + return seckey_ExtractPublicKey(&cert->subjectPublicKeyInfo); +} + +/* + * Get the public key for the fortezza KMID. NOTE this requires the + * PQG paramters to be set. We probably should have a fortezza call that + * just extracts the kmid for us directly so this function can work + * without having the whole cert chain + */ +SECKEYPublicKey * +CERT_KMIDPublicKey(CERTCertificate *cert) +{ + return seckey_ExtractPublicKey(&cert->subjectPublicKeyInfo); +} + +/* returns key strength in bytes (not bits) */ +unsigned +SECKEY_PublicKeyStrength(SECKEYPublicKey *pubk) +{ + unsigned char b0; + + /* interpret modulus length as key strength... in + * fortezza that's the public key length */ + + switch (pubk->keyType) { + case rsaKey: + b0 = pubk->u.rsa.modulus.data[0]; + return b0 ? pubk->u.rsa.modulus.len : pubk->u.rsa.modulus.len - 1; + case dsaKey: + b0 = pubk->u.dsa.publicValue.data[0]; + return b0 ? pubk->u.dsa.publicValue.len : + pubk->u.dsa.publicValue.len - 1; + case dhKey: + b0 = pubk->u.dh.publicValue.data[0]; + return b0 ? pubk->u.dh.publicValue.len : + pubk->u.dh.publicValue.len - 1; + case fortezzaKey: + return PR_MAX(pubk->u.fortezza.KEAKey.len, pubk->u.fortezza.DSSKey.len); + default: + break; + } + return 0; +} + +SECKEYPrivateKey * +SECKEY_CopyPrivateKey(SECKEYPrivateKey *privk) +{ + SECKEYPrivateKey *copyk; + PRArenaPool *arena; + + if (privk == NULL) { + return NULL; + } + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + PORT_SetError (SEC_ERROR_NO_MEMORY); + return NULL; + } + + copyk = (SECKEYPrivateKey *) PORT_ArenaZAlloc (arena, sizeof (SECKEYPrivateKey)); + if (copyk) { + copyk->arena = arena; + copyk->keyType = privk->keyType; + + /* copy the PKCS #11 parameters */ + copyk->pkcs11Slot = PK11_ReferenceSlot(privk->pkcs11Slot); + /* if the key we're referencing was a temparary key we have just + * created, that we want to go away when we're through, we need + * to make a copy of it */ + if (privk->pkcs11IsTemp) { + copyk->pkcs11ID = + PK11_CopyKey(privk->pkcs11Slot,privk->pkcs11ID); + if (copyk->pkcs11ID == CK_INVALID_HANDLE) goto fail; + } else { + copyk->pkcs11ID = privk->pkcs11ID; + } + copyk->pkcs11IsTemp = privk->pkcs11IsTemp; + copyk->wincx = privk->wincx; + return copyk; + } else { + PORT_SetError (SEC_ERROR_NO_MEMORY); + } + +fail: + PORT_FreeArena (arena, PR_FALSE); + return NULL; +} + +SECKEYPublicKey * +SECKEY_CopyPublicKey(SECKEYPublicKey *pubk) +{ + SECKEYPublicKey *copyk; + PRArenaPool *arena; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + PORT_SetError (SEC_ERROR_NO_MEMORY); + return NULL; + } + + copyk = (SECKEYPublicKey *) PORT_ArenaZAlloc (arena, sizeof (SECKEYPublicKey)); + if (copyk != NULL) { + SECStatus rv = SECSuccess; + + copyk->arena = arena; + copyk->keyType = pubk->keyType; + if (pubk->pkcs11Slot && + PK11_IsPermObject(pubk->pkcs11Slot,pubk->pkcs11ID)) { + copyk->pkcs11Slot = PK11_ReferenceSlot(pubk->pkcs11Slot); + copyk->pkcs11ID = pubk->pkcs11ID; + } else { + copyk->pkcs11Slot = NULL; /* go get own reference */ + copyk->pkcs11ID = CK_INVALID_HANDLE; + } + switch (pubk->keyType) { + case rsaKey: + rv = SECITEM_CopyItem(arena, ©k->u.rsa.modulus, + &pubk->u.rsa.modulus); + if (rv == SECSuccess) { + rv = SECITEM_CopyItem (arena, ©k->u.rsa.publicExponent, + &pubk->u.rsa.publicExponent); + if (rv == SECSuccess) + return copyk; + } + break; + case dsaKey: + rv = SECITEM_CopyItem(arena, ©k->u.dsa.publicValue, + &pubk->u.dsa.publicValue); + if (rv != SECSuccess) break; + rv = SECITEM_CopyItem(arena, ©k->u.dsa.params.prime, + &pubk->u.dsa.params.prime); + if (rv != SECSuccess) break; + rv = SECITEM_CopyItem(arena, ©k->u.dsa.params.subPrime, + &pubk->u.dsa.params.subPrime); + if (rv != SECSuccess) break; + rv = SECITEM_CopyItem(arena, ©k->u.dsa.params.base, + &pubk->u.dsa.params.base); + break; + case keaKey: + rv = SECITEM_CopyItem(arena, ©k->u.kea.publicValue, + &pubk->u.kea.publicValue); + if (rv != SECSuccess) break; + rv = SECITEM_CopyItem(arena, ©k->u.kea.params.hash, + &pubk->u.kea.params.hash); + break; + case fortezzaKey: + copyk->u.fortezza.KEAversion = pubk->u.fortezza.KEAversion; + copyk->u.fortezza.DSSversion = pubk->u.fortezza.DSSversion; + PORT_Memcpy(copyk->u.fortezza.KMID, pubk->u.fortezza.KMID, + sizeof(pubk->u.fortezza.KMID)); + rv = SECITEM_CopyItem(arena, ©k->u.fortezza.clearance, + &pubk->u.fortezza.clearance); + if (rv != SECSuccess) break; + rv = SECITEM_CopyItem(arena, ©k->u.fortezza.KEApriviledge, + &pubk->u.fortezza.KEApriviledge); + if (rv != SECSuccess) break; + rv = SECITEM_CopyItem(arena, ©k->u.fortezza.DSSpriviledge, + &pubk->u.fortezza.DSSpriviledge); + if (rv != SECSuccess) break; + rv = SECITEM_CopyItem(arena, ©k->u.fortezza.KEAKey, + &pubk->u.fortezza.KEAKey); + if (rv != SECSuccess) break; + rv = SECITEM_CopyItem(arena, ©k->u.fortezza.DSSKey, + &pubk->u.fortezza.DSSKey); + if (rv != SECSuccess) break; + rv = SECITEM_CopyItem(arena, ©k->u.fortezza.params.prime, + &pubk->u.fortezza.params.prime); + if (rv != SECSuccess) break; + rv = SECITEM_CopyItem(arena, ©k->u.fortezza.params.subPrime, + &pubk->u.fortezza.params.subPrime); + if (rv != SECSuccess) break; + rv = SECITEM_CopyItem(arena, ©k->u.fortezza.params.base, + &pubk->u.fortezza.params.base); + if (rv != SECSuccess) break; + rv = SECITEM_CopyItem(arena, ©k->u.fortezza.keaParams.prime, + &pubk->u.fortezza.keaParams.prime); + if (rv != SECSuccess) break; + rv = SECITEM_CopyItem(arena, ©k->u.fortezza.keaParams.subPrime, + &pubk->u.fortezza.keaParams.subPrime); + if (rv != SECSuccess) break; + rv = SECITEM_CopyItem(arena, ©k->u.fortezza.keaParams.base, + &pubk->u.fortezza.keaParams.base); + break; + case dhKey: + rv = SECITEM_CopyItem(arena,©k->u.dh.prime,&pubk->u.dh.prime); + if (rv != SECSuccess) break; + rv = SECITEM_CopyItem(arena,©k->u.dh.base,&pubk->u.dh.base); + if (rv != SECSuccess) break; + rv = SECITEM_CopyItem(arena, ©k->u.dh.publicValue, + &pubk->u.dh.publicValue); + break; + case nullKey: + return copyk; + default: + rv = SECFailure; + break; + } + if (rv == SECSuccess) + return copyk; + + SECKEY_DestroyPublicKey (copyk); + } else { + PORT_SetError (SEC_ERROR_NO_MEMORY); + } + + PORT_FreeArena (arena, PR_FALSE); + return NULL; +} + + +SECKEYPublicKey * +SECKEY_ConvertToPublicKey(SECKEYPrivateKey *privk) +{ + SECKEYPublicKey *pubk; + PRArenaPool *arena; + CERTCertificate *cert; + SECStatus rv; + + /* + * First try to look up the cert. + */ + cert = PK11_GetCertFromPrivateKey(privk); + if (cert) { + pubk = CERT_ExtractPublicKey(cert); + CERT_DestroyCertificate(cert); + return pubk; + } + + /* couldn't find the cert, build pub key by hand */ + arena = PORT_NewArena (DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + PORT_SetError (SEC_ERROR_NO_MEMORY); + return NULL; + } + pubk = (SECKEYPublicKey *)PORT_ArenaZAlloc(arena, + sizeof (SECKEYPublicKey)); + if (pubk == NULL) { + PORT_FreeArena(arena,PR_FALSE); + return NULL; + } + pubk->keyType = privk->keyType; + pubk->pkcs11Slot = NULL; + pubk->pkcs11ID = CK_INVALID_HANDLE; + pubk->arena = arena; + + /* + * fortezza is at the head of this switch, since we don't want to + * allocate an arena... CERT_ExtractPublicKey will to that for us. + */ + switch(privk->keyType) { + case fortezzaKey: + case nullKey: + case dhKey: + case dsaKey: + /* Nothing to query, if the cert isn't there, we're done -- no way + * to get the public key */ + break; + case rsaKey: + rv = PK11_ReadAttribute(privk->pkcs11Slot,privk->pkcs11ID, + CKA_MODULUS,arena,&pubk->u.rsa.modulus); + if (rv != SECSuccess) break; + rv = PK11_ReadAttribute(privk->pkcs11Slot,privk->pkcs11ID, + CKA_PUBLIC_EXPONENT,arena,&pubk->u.rsa.publicExponent); + if (rv != SECSuccess) break; + return pubk; + break; + default: + break; + } + + PORT_FreeArena (arena, PR_FALSE); + return NULL; +} + +CERTSubjectPublicKeyInfo * +SECKEY_CreateSubjectPublicKeyInfo(SECKEYPublicKey *pubk) +{ + CERTSubjectPublicKeyInfo *spki; + PRArenaPool *arena; + SECItem params = { siBuffer, NULL, 0 }; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + spki = (CERTSubjectPublicKeyInfo *) PORT_ArenaZAlloc(arena, sizeof (*spki)); + if (spki != NULL) { + SECStatus rv; + SECItem *rv_item; + + spki->arena = arena; + switch(pubk->keyType) { + case rsaKey: + rv = SECOID_SetAlgorithmID(arena, &spki->algorithm, + SEC_OID_PKCS1_RSA_ENCRYPTION, 0); + if (rv == SECSuccess) { + /* + * DER encode the public key into the subjectPublicKeyInfo. + */ + prepare_rsa_pub_key_for_asn1(pubk); + rv_item = SEC_ASN1EncodeItem(arena, &spki->subjectPublicKey, + pubk, SECKEY_RSAPublicKeyTemplate); + if (rv_item != NULL) { + /* + * The stored value is supposed to be a BIT_STRING, + * so convert the length. + */ + spki->subjectPublicKey.len <<= 3; + /* + * We got a good one; return it. + */ + return spki; + } + } + break; + case dsaKey: + /* DER encode the params. */ + prepare_pqg_params_for_asn1(&pubk->u.dsa.params); + rv_item = SEC_ASN1EncodeItem(arena, ¶ms, &pubk->u.dsa.params, + SECKEY_PQGParamsTemplate); + if (rv_item != NULL) { + rv = SECOID_SetAlgorithmID(arena, &spki->algorithm, + SEC_OID_ANSIX9_DSA_SIGNATURE, + ¶ms); + if (rv == SECSuccess) { + /* + * DER encode the public key into the subjectPublicKeyInfo. + */ + prepare_dsa_pub_key_for_asn1(pubk); + rv_item = SEC_ASN1EncodeItem(arena, &spki->subjectPublicKey, + pubk, + SECKEY_DSAPublicKeyTemplate); + if (rv_item != NULL) { + /* + * The stored value is supposed to be a BIT_STRING, + * so convert the length. + */ + spki->subjectPublicKey.len <<= 3; + /* + * We got a good one; return it. + */ + return spki; + } + } + } + SECITEM_FreeItem(¶ms, PR_FALSE); + break; + case keaKey: + case dhKey: /* later... */ + + break; + case fortezzaKey: +#ifdef notdef + /* encode the DSS parameters (PQG) */ + rv = FortezzaBuildParams(¶ms,pubk); + if (rv != SECSuccess) break; + + /* set the algorithm */ + rv = SECOID_SetAlgorithmID(arena, &spki->algorithm, + SEC_OID_MISSI_KEA_DSS, ¶ms); + PORT_Free(params.data); + if (rv == SECSuccess) { + /* + * Encode the public key into the subjectPublicKeyInfo. + * Fortezza key material is not standard DER + */ + rv = FortezzaEncodeCertKey(arena,&spki->subjectPublicKey,pubk); + if (rv == SECSuccess) { + /* + * The stored value is supposed to be a BIT_STRING, + * so convert the length. + */ + spki->subjectPublicKey.len <<= 3; + + /* + * We got a good one; return it. + */ + return spki; + } + } +#endif + break; + default: + break; + } + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + + PORT_FreeArena(arena, PR_FALSE); + return NULL; +} + +void +SECKEY_DestroySubjectPublicKeyInfo(CERTSubjectPublicKeyInfo *spki) +{ + if (spki && spki->arena) { + PORT_FreeArena(spki->arena, PR_FALSE); + } +} + +/* + * this only works for RSA keys... need to do something + * similiar to CERT_ExtractPublicKey for other key times. + */ +SECKEYPublicKey * +SECKEY_DecodeDERPublicKey(SECItem *pubkder) +{ + PRArenaPool *arena; + SECKEYPublicKey *pubk; + SECStatus rv; + + arena = PORT_NewArena (DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + PORT_SetError (SEC_ERROR_NO_MEMORY); + return NULL; + } + + pubk = (SECKEYPublicKey *) PORT_ArenaZAlloc (arena, sizeof (SECKEYPublicKey)); + if (pubk != NULL) { + pubk->arena = arena; + pubk->pkcs11Slot = NULL; + pubk->pkcs11ID = 0; + prepare_rsa_pub_key_for_asn1(pubk); + rv = SEC_ASN1DecodeItem(arena, pubk, SECKEY_RSAPublicKeyTemplate, + pubkder); + if (rv == SECSuccess) + return pubk; + SECKEY_DestroyPublicKey (pubk); + } else { + PORT_SetError (SEC_ERROR_NO_MEMORY); + } + + PORT_FreeArena (arena, PR_FALSE); + return NULL; +} + +/* + * Decode a base64 ascii encoded DER encoded public key. + */ +SECKEYPublicKey * +SECKEY_ConvertAndDecodePublicKey(char *pubkstr) +{ + SECKEYPublicKey *pubk; + SECStatus rv; + SECItem der; + + rv = ATOB_ConvertAsciiToItem (&der, pubkstr); + if (rv != SECSuccess) + return NULL; + + pubk = SECKEY_DecodeDERPublicKey (&der); + + PORT_Free (der.data); + return pubk; +} + +SECItem * +SECKEY_EncodeDERSubjectPublicKeyInfo(SECKEYPublicKey *pubk) +{ + CERTSubjectPublicKeyInfo *spki=NULL; + SECItem *spkiDER=NULL; + + /* get the subjectpublickeyinfo */ + spki = SECKEY_CreateSubjectPublicKeyInfo(pubk); + if( spki == NULL ) { + goto finish; + } + + /* DER-encode the subjectpublickeyinfo */ + spkiDER = SEC_ASN1EncodeItem(NULL /*arena*/, NULL/*dest*/, spki, + CERT_SubjectPublicKeyInfoTemplate); +finish: + if (spki!=NULL) { + SECKEY_DestroySubjectPublicKeyInfo(spki); + } + return spkiDER; +} + + +CERTSubjectPublicKeyInfo * +SECKEY_DecodeDERSubjectPublicKeyInfo(SECItem *spkider) +{ + PRArenaPool *arena; + CERTSubjectPublicKeyInfo *spki; + SECStatus rv; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + spki = (CERTSubjectPublicKeyInfo *) + PORT_ArenaZAlloc(arena, sizeof (CERTSubjectPublicKeyInfo)); + if (spki != NULL) { + spki->arena = arena; + rv = SEC_ASN1DecodeItem(arena,spki, + CERT_SubjectPublicKeyInfoTemplate,spkider); + if (rv == SECSuccess) + return spki; + SECKEY_DestroySubjectPublicKeyInfo(spki); + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + + PORT_FreeArena(arena, PR_FALSE); + return NULL; +} + +/* + * Decode a base64 ascii encoded DER encoded subject public key info. + */ +CERTSubjectPublicKeyInfo * +SECKEY_ConvertAndDecodeSubjectPublicKeyInfo(char *spkistr) +{ + CERTSubjectPublicKeyInfo *spki; + SECStatus rv; + SECItem der; + + rv = ATOB_ConvertAsciiToItem(&der, spkistr); + if (rv != SECSuccess) + return NULL; + + spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&der); + + PORT_Free(der.data); + return spki; +} + +/* + * Decode a base64 ascii encoded DER encoded public key and challenge + * Verify digital signature and make sure challenge matches + */ +CERTSubjectPublicKeyInfo * +SECKEY_ConvertAndDecodePublicKeyAndChallenge(char *pkacstr, char *challenge, + void *wincx) +{ + CERTSubjectPublicKeyInfo *spki = NULL; + CERTPublicKeyAndChallenge pkac; + SECStatus rv; + SECItem signedItem; + PRArenaPool *arena = NULL; + CERTSignedData sd; + SECItem sig; + SECKEYPublicKey *pubKey = NULL; + unsigned int len; + + signedItem.data = NULL; + + /* convert the base64 encoded data to binary */ + rv = ATOB_ConvertAsciiToItem(&signedItem, pkacstr); + if (rv != SECSuccess) { + goto loser; + } + + /* create an arena */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + goto loser; + } + + /* decode the outer wrapping of signed data */ + PORT_Memset(&sd, 0, sizeof(CERTSignedData)); + rv = SEC_ASN1DecodeItem(arena, &sd, CERT_SignedDataTemplate, &signedItem ); + if ( rv ) { + goto loser; + } + + /* decode the public key and challenge wrapper */ + PORT_Memset(&pkac, 0, sizeof(CERTPublicKeyAndChallenge)); + rv = SEC_ASN1DecodeItem(arena, &pkac, CERT_PublicKeyAndChallengeTemplate, + &sd.data); + if ( rv ) { + goto loser; + } + + /* decode the subject public key info */ + spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&pkac.spki); + if ( spki == NULL ) { + goto loser; + } + + /* get the public key */ + pubKey = seckey_ExtractPublicKey(spki); + if ( pubKey == NULL ) { + goto loser; + } + + /* check the signature */ + sig = sd.signature; + DER_ConvertBitString(&sig); + rv = VFY_VerifyData(sd.data.data, sd.data.len, pubKey, &sig, + SECOID_GetAlgorithmTag(&(sd.signatureAlgorithm)), wincx); + if ( rv != SECSuccess ) { + goto loser; + } + + /* check the challenge */ + if ( challenge ) { + len = PORT_Strlen(challenge); + /* length is right */ + if ( len != pkac.challenge.len ) { + goto loser; + } + /* actual data is right */ + if ( PORT_Memcmp(challenge, pkac.challenge.data, len) != 0 ) { + goto loser; + } + } + goto done; + +loser: + /* make sure that we return null if we got an error */ + if ( spki ) { + SECKEY_DestroySubjectPublicKeyInfo(spki); + } + spki = NULL; + +done: + if ( signedItem.data ) { + PORT_Free(signedItem.data); + } + if ( arena ) { + PORT_FreeArena(arena, PR_FALSE); + } + if ( pubKey ) { + SECKEY_DestroyPublicKey(pubKey); + } + + return spki; +} + +void +SECKEY_DestroyPrivateKeyInfo(SECKEYPrivateKeyInfo *pvk, + PRBool freeit) +{ + PRArenaPool *poolp; + + if(pvk != NULL) { + if(pvk->arena) { + poolp = pvk->arena; + /* zero structure since PORT_FreeArena does not support + * this yet. + */ + PORT_Memset(pvk->privateKey.data, 0, pvk->privateKey.len); + PORT_Memset((char *)pvk, 0, sizeof(*pvk)); + if(freeit == PR_TRUE) { + PORT_FreeArena(poolp, PR_TRUE); + } else { + pvk->arena = poolp; + } + } else { + SECITEM_ZfreeItem(&pvk->version, PR_FALSE); + SECITEM_ZfreeItem(&pvk->privateKey, PR_FALSE); + SECOID_DestroyAlgorithmID(&pvk->algorithm, PR_FALSE); + PORT_Memset((char *)pvk, 0, sizeof(pvk)); + if(freeit == PR_TRUE) { + PORT_Free(pvk); + } + } + } +} + +void +SECKEY_DestroyEncryptedPrivateKeyInfo(SECKEYEncryptedPrivateKeyInfo *epki, + PRBool freeit) +{ + PRArenaPool *poolp; + + if(epki != NULL) { + if(epki->arena) { + poolp = epki->arena; + /* zero structure since PORT_FreeArena does not support + * this yet. + */ + PORT_Memset(epki->encryptedData.data, 0, epki->encryptedData.len); + PORT_Memset((char *)epki, 0, sizeof(*epki)); + if(freeit == PR_TRUE) { + PORT_FreeArena(poolp, PR_TRUE); + } else { + epki->arena = poolp; + } + } else { + SECITEM_ZfreeItem(&epki->encryptedData, PR_FALSE); + SECOID_DestroyAlgorithmID(&epki->algorithm, PR_FALSE); + PORT_Memset((char *)epki, 0, sizeof(epki)); + if(freeit == PR_TRUE) { + PORT_Free(epki); + } + } + } +} + +SECStatus +SECKEY_CopyPrivateKeyInfo(PRArenaPool *poolp, + SECKEYPrivateKeyInfo *to, + SECKEYPrivateKeyInfo *from) +{ + SECStatus rv = SECFailure; + + if((to == NULL) || (from == NULL)) { + return SECFailure; + } + + rv = SECOID_CopyAlgorithmID(poolp, &to->algorithm, &from->algorithm); + if(rv != SECSuccess) { + return SECFailure; + } + rv = SECITEM_CopyItem(poolp, &to->privateKey, &from->privateKey); + if(rv != SECSuccess) { + return SECFailure; + } + rv = SECITEM_CopyItem(poolp, &to->version, &from->version); + + return rv; +} + +SECStatus +SECKEY_CopyEncryptedPrivateKeyInfo(PRArenaPool *poolp, + SECKEYEncryptedPrivateKeyInfo *to, + SECKEYEncryptedPrivateKeyInfo *from) +{ + SECStatus rv = SECFailure; + + if((to == NULL) || (from == NULL)) { + return SECFailure; + } + + rv = SECOID_CopyAlgorithmID(poolp, &to->algorithm, &from->algorithm); + if(rv != SECSuccess) { + return SECFailure; + } + rv = SECITEM_CopyItem(poolp, &to->encryptedData, &from->encryptedData); + + return rv; +} + +KeyType +SECKEY_GetPrivateKeyType(SECKEYPrivateKey *privKey) +{ + return privKey->keyType; +} + +KeyType +SECKEY_GetPublicKeyType(SECKEYPublicKey *pubKey) +{ + return pubKey->keyType; +} + +SECKEYPublicKey* +SECKEY_ImportDERPublicKey(SECItem *derKey, CK_KEY_TYPE type) +{ + SECKEYPublicKey *pubk = NULL; + SECStatus rv = SECFailure; + + pubk = PORT_ZNew(SECKEYPublicKey); + if(pubk == NULL) { + goto finish; + } + pubk->arena = NULL; + pubk->pkcs11Slot = NULL; + pubk->pkcs11ID = CK_INVALID_HANDLE; + + switch( type ) { + case CKK_RSA: + prepare_rsa_pub_key_for_asn1(pubk); + rv = SEC_ASN1DecodeItem(NULL, pubk, SECKEY_RSAPublicKeyTemplate,derKey); + pubk->keyType = rsaKey; + break; + case CKK_DSA: + prepare_dsa_pub_key_for_asn1(pubk); + rv = SEC_ASN1DecodeItem(NULL, pubk, SECKEY_DSAPublicKeyTemplate,derKey); + pubk->keyType = dsaKey; + break; + case CKK_DH: + prepare_dh_pub_key_for_asn1(pubk); + rv = SEC_ASN1DecodeItem(NULL, pubk, SECKEY_DHPublicKeyTemplate, derKey); + pubk->keyType = dhKey; + break; + default: + rv = SECFailure; + break; + } + +finish: + if( rv != SECSuccess && pubk != NULL) { + PORT_Free(pubk); + pubk = NULL; + } + return pubk; +} + +SECKEYPrivateKeyList* +SECKEY_NewPrivateKeyList(void) +{ + PRArenaPool *arena = NULL; + SECKEYPrivateKeyList *ret = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if ( arena == NULL ) { + goto loser; + } + + ret = (SECKEYPrivateKeyList *)PORT_ArenaZAlloc(arena, + sizeof(SECKEYPrivateKeyList)); + if ( ret == NULL ) { + goto loser; + } + + ret->arena = arena; + + PR_INIT_CLIST(&ret->list); + + return(ret); + +loser: + if ( arena != NULL ) { + PORT_FreeArena(arena, PR_FALSE); + } + + return(NULL); +} + +void +SECKEY_DestroyPrivateKeyList(SECKEYPrivateKeyList *keys) +{ + while( !PR_CLIST_IS_EMPTY(&keys->list) ) { + SECKEY_RemovePrivateKeyListNode( + (SECKEYPrivateKeyListNode*)(PR_LIST_HEAD(&keys->list)) ); + } + + PORT_FreeArena(keys->arena, PR_FALSE); + + return; +} + + +void +SECKEY_RemovePrivateKeyListNode(SECKEYPrivateKeyListNode *node) +{ + PR_ASSERT(node->key); + SECKEY_DestroyPrivateKey(node->key); + node->key = NULL; + PR_REMOVE_LINK(&node->links); + return; + +} + +SECStatus +SECKEY_AddPrivateKeyToListTail( SECKEYPrivateKeyList *list, + SECKEYPrivateKey *key) +{ + SECKEYPrivateKeyListNode *node; + + node = (SECKEYPrivateKeyListNode *)PORT_ArenaZAlloc(list->arena, + sizeof(SECKEYPrivateKeyListNode)); + if ( node == NULL ) { + goto loser; + } + + PR_INSERT_BEFORE(&node->links, &list->list); + node->key = key; + return(SECSuccess); + +loser: + return(SECFailure); +} + + +SECKEYPublicKeyList* +SECKEY_NewPublicKeyList(void) +{ + PRArenaPool *arena = NULL; + SECKEYPublicKeyList *ret = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if ( arena == NULL ) { + goto loser; + } + + ret = (SECKEYPublicKeyList *)PORT_ArenaZAlloc(arena, + sizeof(SECKEYPublicKeyList)); + if ( ret == NULL ) { + goto loser; + } + + ret->arena = arena; + + PR_INIT_CLIST(&ret->list); + + return(ret); + +loser: + if ( arena != NULL ) { + PORT_FreeArena(arena, PR_FALSE); + } + + return(NULL); +} + +void +SECKEY_DestroyPublicKeyList(SECKEYPublicKeyList *keys) +{ + while( !PR_CLIST_IS_EMPTY(&keys->list) ) { + SECKEY_RemovePublicKeyListNode( + (SECKEYPublicKeyListNode*)(PR_LIST_HEAD(&keys->list)) ); + } + + PORT_FreeArena(keys->arena, PR_FALSE); + + return; +} + + +void +SECKEY_RemovePublicKeyListNode(SECKEYPublicKeyListNode *node) +{ + PR_ASSERT(node->key); + SECKEY_DestroyPublicKey(node->key); + node->key = NULL; + PR_REMOVE_LINK(&node->links); + return; + +} + +SECStatus +SECKEY_AddPublicKeyToListTail( SECKEYPublicKeyList *list, + SECKEYPublicKey *key) +{ + SECKEYPublicKeyListNode *node; + + node = (SECKEYPublicKeyListNode *)PORT_ArenaZAlloc(list->arena, + sizeof(SECKEYPublicKeyListNode)); + if ( node == NULL ) { + goto loser; + } + + PR_INSERT_BEFORE(&node->links, &list->list); + node->key = key; + return(SECSuccess); + +loser: + return(SECFailure); +} |