Source code

Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "lowkeyi.h"
#include "secoid.h"
#include "secitem.h"
#include "secder.h"
#include "base64.h"
#include "secasn1.h"
#include "secerr.h"
#include "softoken.h"
#include "ec.h"
SEC_ASN1_MKSUB(SEC_AnyTemplate)
SEC_ASN1_MKSUB(SEC_BitStringTemplate)
SEC_ASN1_MKSUB(SEC_ObjectIDTemplate)
SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
const SEC_ASN1Template nsslowkey_AttributeTemplate[] = {
{ SEC_ASN1_SEQUENCE,
0, NULL, sizeof(NSSLOWKEYAttribute) },
{ SEC_ASN1_OBJECT_ID, offsetof(NSSLOWKEYAttribute, attrType) },
{ SEC_ASN1_SET_OF | SEC_ASN1_XTRN,
offsetof(NSSLOWKEYAttribute, attrValue),
SEC_ASN1_SUB(SEC_AnyTemplate) },
{ 0 }
};
const SEC_ASN1Template nsslowkey_SetOfAttributeTemplate[] = {
{ SEC_ASN1_SET_OF, 0, nsslowkey_AttributeTemplate },
};
/* ASN1 Templates for new decoder/encoder */
const SEC_ASN1Template nsslowkey_PrivateKeyInfoTemplate[] = {
{ SEC_ASN1_SEQUENCE,
0, NULL, sizeof(NSSLOWKEYPrivateKeyInfo) },
{ SEC_ASN1_INTEGER,
offsetof(NSSLOWKEYPrivateKeyInfo, version) },
{ SEC_ASN1_INLINE | SEC_ASN1_XTRN,
offsetof(NSSLOWKEYPrivateKeyInfo, algorithm),
SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
{ SEC_ASN1_OCTET_STRING,
offsetof(NSSLOWKEYPrivateKeyInfo, privateKey) },
{ SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
offsetof(NSSLOWKEYPrivateKeyInfo, attributes),
nsslowkey_SetOfAttributeTemplate },
{ 0 }
};
const SEC_ASN1Template nsslowkey_SubjectPublicKeyInfoTemplate[] = {
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYSubjectPublicKeyInfo) },
{ SEC_ASN1_INLINE | SEC_ASN1_XTRN,
offsetof(NSSLOWKEYSubjectPublicKeyInfo, algorithm),
SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
{ SEC_ASN1_BIT_STRING,
offsetof(NSSLOWKEYSubjectPublicKeyInfo, subjectPublicKey) },
{ 0 }
};
const SEC_ASN1Template nsslowkey_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 }
};
const SEC_ASN1Template nsslowkey_PQGParamsTemplate[] = {
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(PQGParams) },
{ SEC_ASN1_INTEGER, offsetof(PQGParams, prime) },
{ SEC_ASN1_INTEGER, offsetof(PQGParams, subPrime) },
{ SEC_ASN1_INTEGER, offsetof(PQGParams, base) },
{ 0 }
};
const SEC_ASN1Template nsslowkey_RSAPrivateKeyTemplate[] = {
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYPrivateKey) },
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey, u.rsa.version) },
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey, u.rsa.modulus) },
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey, u.rsa.publicExponent) },
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey, u.rsa.privateExponent) },
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey, u.rsa.prime1) },
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey, u.rsa.prime2) },
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey, u.rsa.exponent1) },
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey, u.rsa.exponent2) },
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey, u.rsa.coefficient) },
{ 0 }
};
const SEC_ASN1Template nsslowkey_DSAPrivateKeyTemplate[] = {
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYPrivateKey) },
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey, u.dsa.publicValue) },
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey, u.dsa.privateValue) },
{ 0 }
};
const SEC_ASN1Template nsslowkey_DSAPrivateKeyExportTemplate[] = {
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey, u.dsa.privateValue) },
};
const SEC_ASN1Template nsslowkey_DHPrivateKeyTemplate[] = {
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYPrivateKey) },
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey, u.dh.publicValue) },
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey, u.dh.privateValue) },
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey, u.dh.base) },
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey, u.dh.prime) },
{ 0 }
};
/* NOTE: The SECG specification allows the private key structure
* to contain curve parameters but recommends that they be stored
* in the PrivateKeyAlgorithmIdentifier field of the PrivateKeyInfo
* instead.
*/
const SEC_ASN1Template nsslowkey_ECPrivateKeyTemplate[] = {
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYPrivateKey) },
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey, u.ec.version) },
{ SEC_ASN1_OCTET_STRING,
offsetof(NSSLOWKEYPrivateKey, u.ec.privateValue) },
/* We only support named curves for which the parameters are
* encoded as an object ID.
*/
{ SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC |
SEC_ASN1_XTRN | 0,
offsetof(NSSLOWKEYPrivateKey, u.ec.ecParams.curveOID),
SEC_ASN1_SUB(SEC_ObjectIDTemplate) },
{ SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC |
SEC_ASN1_XTRN | 1,
offsetof(NSSLOWKEYPrivateKey, u.ec.publicValue),
SEC_ASN1_SUB(SEC_BitStringTemplate) },
{ 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.
*/
void
prepare_low_rsa_priv_key_for_asn1(NSSLOWKEYPrivateKey *key)
{
key->u.rsa.modulus.type = siUnsignedInteger;
key->u.rsa.publicExponent.type = siUnsignedInteger;
key->u.rsa.privateExponent.type = siUnsignedInteger;
key->u.rsa.prime1.type = siUnsignedInteger;
key->u.rsa.prime2.type = siUnsignedInteger;
key->u.rsa.exponent1.type = siUnsignedInteger;
key->u.rsa.exponent2.type = siUnsignedInteger;
key->u.rsa.coefficient.type = siUnsignedInteger;
}
void
prepare_low_rsa_pub_key_for_asn1(NSSLOWKEYPublicKey *key)
{
key->u.rsa.modulus.type = siUnsignedInteger;
key->u.rsa.publicExponent.type = siUnsignedInteger;
}
void
prepare_low_pqg_params_for_asn1(PQGParams *params)
{
params->prime.type = siUnsignedInteger;
params->subPrime.type = siUnsignedInteger;
params->base.type = siUnsignedInteger;
}
void
prepare_low_dsa_priv_key_for_asn1(NSSLOWKEYPrivateKey *key)
{
key->u.dsa.publicValue.type = siUnsignedInteger;
key->u.dsa.privateValue.type = siUnsignedInteger;
key->u.dsa.params.prime.type = siUnsignedInteger;
key->u.dsa.params.subPrime.type = siUnsignedInteger;
key->u.dsa.params.base.type = siUnsignedInteger;
}
void
prepare_low_dsa_priv_key_export_for_asn1(NSSLOWKEYPrivateKey *key)
{
key->u.dsa.privateValue.type = siUnsignedInteger;
}
void
prepare_low_dh_priv_key_for_asn1(NSSLOWKEYPrivateKey *key)
{
key->u.dh.prime.type = siUnsignedInteger;
key->u.dh.base.type = siUnsignedInteger;
key->u.dh.publicValue.type = siUnsignedInteger;
key->u.dh.privateValue.type = siUnsignedInteger;
}
void
prepare_low_ecparams_for_asn1(ECParams *params)
{
params->DEREncoding.type = siUnsignedInteger;
params->curveOID.type = siUnsignedInteger;
}
void
prepare_low_ec_priv_key_for_asn1(NSSLOWKEYPrivateKey *key)
{
key->u.ec.version.type = siUnsignedInteger;
key->u.ec.ecParams.DEREncoding.type = siUnsignedInteger;
key->u.ec.ecParams.curveOID.type = siUnsignedInteger;
key->u.ec.privateValue.type = siUnsignedInteger;
key->u.ec.publicValue.type = siUnsignedInteger;
}
void
nsslowkey_DestroyPrivateKey(NSSLOWKEYPrivateKey *privk)
{
if (privk && privk->arena) {
PORT_FreeArena(privk->arena, PR_TRUE);
}
}
void
nsslowkey_DestroyPublicKey(NSSLOWKEYPublicKey *pubk)
{
if (pubk && pubk->arena) {
PORT_FreeArena(pubk->arena, PR_TRUE);
}
}
unsigned
nsslowkey_PublicModulusLen(NSSLOWKEYPublicKey *pubk)
{
/* interpret modulus length as key strength... in
* fortezza that's the public key length */
switch (pubk->keyType) {
case NSSLOWKEYRSAKey:
if (pubk->u.rsa.modulus.len == 0) {
return 0;
}
if (pubk->u.rsa.modulus.data[0] == 0) {
return pubk->u.rsa.modulus.len - 1;
}
return pubk->u.rsa.modulus.len;
default:
break;
}
return 0;
}
unsigned
nsslowkey_PrivateModulusLen(NSSLOWKEYPrivateKey *privk)
{
switch (privk->keyType) {
case NSSLOWKEYRSAKey:
if (privk->u.rsa.modulus.len == 0) {
return 0;
}
if (privk->u.rsa.modulus.data[0] == 0) {
return privk->u.rsa.modulus.len - 1;
}
return privk->u.rsa.modulus.len;
default:
break;
}
return 0;
}
NSSLOWKEYPublicKey *
nsslowkey_ConvertToPublicKey(NSSLOWKEYPrivateKey *privk)
{
NSSLOWKEYPublicKey *pubk;
SECItem publicValue;
PLArenaPool *arena;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (arena == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
return NULL;
}
switch (privk->keyType) {
case NSSLOWKEYRSAKey:
case NSSLOWKEYNullKey:
pubk = (NSSLOWKEYPublicKey *)PORT_ArenaZAlloc(arena,
sizeof(NSSLOWKEYPublicKey));
if (pubk != NULL) {
SECStatus rv;
pubk->arena = arena;
pubk->keyType = privk->keyType;
if (privk->keyType == NSSLOWKEYNullKey)
return pubk;
rv = SECITEM_CopyItem(arena, &pubk->u.rsa.modulus,
&privk->u.rsa.modulus);
if (rv == SECSuccess) {
rv = SECITEM_CopyItem(arena, &pubk->u.rsa.publicExponent,
&privk->u.rsa.publicExponent);
if (rv == SECSuccess)
return pubk;
}
} else {
PORT_SetError(SEC_ERROR_NO_MEMORY);
}
break;
case NSSLOWKEYDSAKey:
pubk = (NSSLOWKEYPublicKey *)PORT_ArenaZAlloc(arena,
sizeof(NSSLOWKEYPublicKey));
if (pubk != NULL) {
SECStatus rv;
pubk->arena = arena;
pubk->keyType = privk->keyType;
/* if the public key value doesn't exist, calculate it */
if (privk->u.dsa.publicValue.len == 0) {
rv = DH_Derive(&privk->u.dsa.params.base, &privk->u.dsa.params.prime,
&privk->u.dsa.privateValue, &publicValue, 0);
if (rv != SECSuccess) {
break;
}
rv = SECITEM_CopyItem(privk->arena, &privk->u.dsa.publicValue, &publicValue);
SECITEM_ZfreeItem(&publicValue, PR_FALSE);
if (rv != SECSuccess) {
break;
}
}
rv = SECITEM_CopyItem(arena, &pubk->u.dsa.publicValue,
&privk->u.dsa.publicValue);
if (rv != SECSuccess)
break;
rv = SECITEM_CopyItem(arena, &pubk->u.dsa.params.prime,
&privk->u.dsa.params.prime);
if (rv != SECSuccess)
break;
rv = SECITEM_CopyItem(arena, &pubk->u.dsa.params.subPrime,
&privk->u.dsa.params.subPrime);
if (rv != SECSuccess)
break;
rv = SECITEM_CopyItem(arena, &pubk->u.dsa.params.base,
&privk->u.dsa.params.base);
if (rv == SECSuccess)
return pubk;
}
break;
case NSSLOWKEYDHKey:
pubk = (NSSLOWKEYPublicKey *)PORT_ArenaZAlloc(arena,
sizeof(NSSLOWKEYPublicKey));
if (pubk != NULL) {
SECStatus rv;
pubk->arena = arena;
pubk->keyType = privk->keyType;
/* if the public key value doesn't exist, calculate it */
if (privk->u.dh.publicValue.len == 0) {
rv = DH_Derive(&privk->u.dh.base, &privk->u.dh.prime,
&privk->u.dh.privateValue, &publicValue, 0);
if (rv != SECSuccess) {
break;
}
rv = SECITEM_CopyItem(privk->arena, &privk->u.dh.publicValue, &publicValue);
SECITEM_ZfreeItem(&publicValue, PR_FALSE);
if (rv != SECSuccess) {
break;
}
}
rv = SECITEM_CopyItem(arena, &pubk->u.dh.publicValue,
&privk->u.dh.publicValue);
if (rv != SECSuccess)
break;
rv = SECITEM_CopyItem(arena, &pubk->u.dh.prime,
&privk->u.dh.prime);
if (rv != SECSuccess)
break;
rv = SECITEM_CopyItem(arena, &pubk->u.dh.base,
&privk->u.dh.base);
if (rv == SECSuccess)
return pubk;
}
break;
case NSSLOWKEYECKey:
pubk = (NSSLOWKEYPublicKey *)PORT_ArenaZAlloc(arena,
sizeof(NSSLOWKEYPublicKey));
if (pubk != NULL) {
SECStatus rv;
pubk->arena = arena;
pubk->keyType = privk->keyType;
/* if the public key value doesn't exist, calculate it */
if (privk->u.ec.publicValue.len == 0) {
/* Checking if it's an ed25519 or x25519 key.
If it's the case, we derive the public key using the private key. */
SECOidTag privKeyOIDTag = SECOID_FindOIDTag(&privk->u.ec.ecParams.curveOID);
if (privKeyOIDTag == SEC_OID_ED25519_PUBLIC_KEY) {
PORT_Memset(&privk->u.ec.publicValue, 0, sizeof(privk->u.ec.publicValue));
if (SECITEM_AllocItem(privk->arena, &privk->u.ec.publicValue, Ed25519_PUBLIC_KEYLEN) == NULL) {
break;
}
rv = ED_DerivePublicKey(&privk->u.ec.privateValue, &privk->u.ec.publicValue);
if (rv != CKR_OK) {
break;
}
} else if (privKeyOIDTag == SEC_OID_X25519) {
PORT_Memset(&privk->u.ec.publicValue, 0, sizeof(privk->u.ec.publicValue));
if (SECITEM_AllocItem(privk->arena, &privk->u.ec.publicValue, X25519_PUBLIC_KEYLEN) == NULL) {
break;
}
rv = X25519_DerivePublicKey(&privk->u.ec.privateValue, &privk->u.ec.publicValue);
if (rv != CKR_OK) {
break;
}
}
}
rv = SECITEM_CopyItem(arena, &pubk->u.ec.publicValue,
&privk->u.ec.publicValue);
if (rv != SECSuccess)
break;
pubk->u.ec.ecParams.arena = arena;
/* Copy the rest of the params */
rv = EC_CopyParams(arena, &(pubk->u.ec.ecParams),
&(privk->u.ec.ecParams));
if (rv == SECSuccess)
return pubk;
}
break;
/* No Fortezza in Low Key implementations (Fortezza keys aren't
* stored in our data base */
default:
break;
}
PORT_FreeArena(arena, PR_TRUE);
return NULL;
}
NSSLOWKEYPrivateKey *
nsslowkey_CopyPrivateKey(NSSLOWKEYPrivateKey *privKey)
{
NSSLOWKEYPrivateKey *returnKey = NULL;
SECStatus rv = SECFailure;
PLArenaPool *poolp;
if (!privKey) {
return NULL;
}
poolp = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (!poolp) {
return NULL;
}
returnKey = (NSSLOWKEYPrivateKey *)PORT_ArenaZAlloc(poolp, sizeof(NSSLOWKEYPrivateKey));
if (!returnKey) {
rv = SECFailure;
goto loser;
}
returnKey->keyType = privKey->keyType;
returnKey->arena = poolp;
switch (privKey->keyType) {
case NSSLOWKEYRSAKey:
rv = SECITEM_CopyItem(poolp, &(returnKey->u.rsa.modulus),
&(privKey->u.rsa.modulus));
if (rv != SECSuccess)
break;
rv = SECITEM_CopyItem(poolp, &(returnKey->u.rsa.version),
&(privKey->u.rsa.version));
if (rv != SECSuccess)
break;
rv = SECITEM_CopyItem(poolp, &(returnKey->u.rsa.publicExponent),
&(privKey->u.rsa.publicExponent));
if (rv != SECSuccess)
break;
rv = SECITEM_CopyItem(poolp, &(returnKey->u.rsa.privateExponent),
&(privKey->u.rsa.privateExponent));
if (rv != SECSuccess)
break;
rv = SECITEM_CopyItem(poolp, &(returnKey->u.rsa.prime1),
&(privKey->u.rsa.prime1));
if (rv != SECSuccess)
break;
rv = SECITEM_CopyItem(poolp, &(returnKey->u.rsa.prime2),
&(privKey->u.rsa.prime2));
if (rv != SECSuccess)
break;
rv = SECITEM_CopyItem(poolp, &(returnKey->u.rsa.exponent1),
&(privKey->u.rsa.exponent1));
if (rv != SECSuccess)
break;
rv = SECITEM_CopyItem(poolp, &(returnKey->u.rsa.exponent2),
&(privKey->u.rsa.exponent2));
if (rv != SECSuccess)
break;
rv = SECITEM_CopyItem(poolp, &(returnKey->u.rsa.coefficient),
&(privKey->u.rsa.coefficient));
if (rv != SECSuccess)
break;
break;
case NSSLOWKEYDSAKey:
rv = SECITEM_CopyItem(poolp, &(returnKey->u.dsa.publicValue),
&(privKey->u.dsa.publicValue));
if (rv != SECSuccess)
break;
rv = SECITEM_CopyItem(poolp, &(returnKey->u.dsa.privateValue),
&(privKey->u.dsa.privateValue));
if (rv != SECSuccess)
break;
returnKey->u.dsa.params.arena = poolp;
rv = SECITEM_CopyItem(poolp, &(returnKey->u.dsa.params.prime),
&(privKey->u.dsa.params.prime));
if (rv != SECSuccess)
break;
rv = SECITEM_CopyItem(poolp, &(returnKey->u.dsa.params.subPrime),
&(privKey->u.dsa.params.subPrime));
if (rv != SECSuccess)
break;
rv = SECITEM_CopyItem(poolp, &(returnKey->u.dsa.params.base),
&(privKey->u.dsa.params.base));
if (rv != SECSuccess)
break;
break;
case NSSLOWKEYDHKey:
rv = SECITEM_CopyItem(poolp, &(returnKey->u.dh.publicValue),
&(privKey->u.dh.publicValue));
if (rv != SECSuccess)
break;
rv = SECITEM_CopyItem(poolp, &(returnKey->u.dh.privateValue),
&(privKey->u.dh.privateValue));
if (rv != SECSuccess)
break;
returnKey->u.dsa.params.arena = poolp;
rv = SECITEM_CopyItem(poolp, &(returnKey->u.dh.prime),
&(privKey->u.dh.prime));
if (rv != SECSuccess)
break;
rv = SECITEM_CopyItem(poolp, &(returnKey->u.dh.base),
&(privKey->u.dh.base));
if (rv != SECSuccess)
break;
break;
case NSSLOWKEYECKey:
rv = SECITEM_CopyItem(poolp, &(returnKey->u.ec.version),
&(privKey->u.ec.version));
if (rv != SECSuccess)
break;
rv = SECITEM_CopyItem(poolp, &(returnKey->u.ec.publicValue),
&(privKey->u.ec.publicValue));
if (rv != SECSuccess)
break;
rv = SECITEM_CopyItem(poolp, &(returnKey->u.ec.privateValue),
&(privKey->u.ec.privateValue));
if (rv != SECSuccess)
break;
returnKey->u.ec.ecParams.arena = poolp;
/* Copy the rest of the params */
rv = EC_CopyParams(poolp, &(returnKey->u.ec.ecParams),
&(privKey->u.ec.ecParams));
if (rv != SECSuccess)
break;
break;
default:
rv = SECFailure;
}
loser:
if (rv != SECSuccess) {
PORT_FreeArena(poolp, PR_TRUE);
returnKey = NULL;
}
return returnKey;
}