summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrrelyea%redhat.com <devnull@localhost>2009-03-31 21:05:32 +0000
committerrrelyea%redhat.com <devnull@localhost>2009-03-31 21:05:32 +0000
commitf31ac0d86fd0d7227e7217a7bdbb0de8478beb4a (patch)
treeee47318920de63aebed28924a7e533cba6851628
parent4ffe6848c044bcb4f2ab712129444aa6b4d61b4f (diff)
downloadnss-hg-f31ac0d86fd0d7227e7217a7bdbb0de8478beb4a.tar.gz
Bug 480280 - The CKA_EC_POINT PKCS#11 attribute is encoded in the wrong way: missing encapsulating octet string
Add code to 1) accept either type of encoding on input (both NSS and softoken). 2) output the correct encoding unless the environment variable NSS_USE_DECODED_CKA_EC_POINT is set. r= nelson
-rw-r--r--security/nss/lib/pk11wrap/pk11akey.c332
-rw-r--r--security/nss/lib/pk11wrap/pk11skey.c41
-rw-r--r--security/nss/lib/softoken/legacydb/lgattr.c17
-rw-r--r--security/nss/lib/softoken/legacydb/lgcreate.c83
-rw-r--r--security/nss/lib/softoken/pkcs11.c45
-rw-r--r--security/nss/lib/softoken/pkcs11c.c68
6 files changed, 549 insertions, 37 deletions
diff --git a/security/nss/lib/pk11wrap/pk11akey.c b/security/nss/lib/pk11wrap/pk11akey.c
index 5eec5130e..5095b2ae5 100644
--- a/security/nss/lib/pk11wrap/pk11akey.c
+++ b/security/nss/lib/pk11wrap/pk11akey.c
@@ -107,6 +107,7 @@ PK11_ImportPublicKey(PK11SlotInfo *slot, SECKEYPublicKey *pubKey,
CK_ATTRIBUTE *signedattr = NULL;
CK_ATTRIBUTE *attrs = theTemplate;
SECItem *ckaId = NULL;
+ SECItem *pubValue = NULL;
int signedcount = 0;
int templateCount = 0;
SECStatus rv;
@@ -203,8 +204,22 @@ PK11_ImportPublicKey(PK11SlotInfo *slot, SECKEYPublicKey *pubKey,
PK11_SETATTRS(attrs, CKA_EC_PARAMS,
pubKey->u.ec.DEREncodedParams.data,
pubKey->u.ec.DEREncodedParams.len); attrs++;
- PK11_SETATTRS(attrs, CKA_EC_POINT, pubKey->u.ec.publicValue.data,
+ if (PR_GetEnv("NSS_USE_DECODED_CKA_EC_POINT")) {
+ PK11_SETATTRS(attrs, CKA_EC_POINT,
+ pubKey->u.ec.publicValue.data,
pubKey->u.ec.publicValue.len); attrs++;
+ } else {
+ pubValue = SEC_ASN1EncodeItem(NULL, NULL,
+ &pubKey->u.ec.publicValue, SEC_OctetStringTemplate);
+ if (pubValue == NULL) {
+ if (ckaId) {
+ SECITEM_FreeItem(ckaId,PR_TRUE);
+ }
+ return CK_INVALID_HANDLE;
+ }
+ PK11_SETATTRS(attrs, CKA_EC_POINT,
+ pubValue->data, pubValue->len); attrs++;
+ }
break;
default:
PORT_SetError( SEC_ERROR_BAD_KEY );
@@ -222,6 +237,9 @@ PK11_ImportPublicKey(PK11SlotInfo *slot, SECKEYPublicKey *pubKey,
if (ckaId) {
SECITEM_FreeItem(ckaId,PR_TRUE);
}
+ if (pubValue) {
+ SECITEM_FreeItem(pubValue,PR_TRUE);
+ }
if ( rv != SECSuccess) {
return CK_INVALID_HANDLE;
}
@@ -237,7 +255,7 @@ PK11_ImportPublicKey(PK11SlotInfo *slot, SECKEYPublicKey *pubKey,
* take an attribute and copy it into a secitem
*/
static CK_RV
-pk11_Attr2SecItem(PRArenaPool *arena, CK_ATTRIBUTE *attr, SECItem *item)
+pk11_Attr2SecItem(PRArenaPool *arena, const CK_ATTRIBUTE *attr, SECItem *item)
{
item->data = NULL;
@@ -249,6 +267,309 @@ pk11_Attr2SecItem(PRArenaPool *arena, CK_ATTRIBUTE *attr, SECItem *item)
return CKR_OK;
}
+
+/*
+ * get a curve length from a set of ecParams.
+ *
+ * We need this so we can reliably determine if a the ecPoint passed to us
+ * was encoded or not. With out this, for many curves, we would incorrectly
+ * identify an unencoded curve as an encoded curve 1 in 65536 times, and for
+ * a few we would make that same mistake 1 in 32768 times. These are bad
+ * numbers since they are rare enough to pass tests, but common enough to
+ * be tripped over in the field.
+ *
+ * This function will only work for curves we recognized as of March 2009.
+ * The assumption is curves in use after March of 2009 would be supplied by
+ * PKCS #11 modules that already pass the correct encoding to us.
+ *
+ * Point length = (Roundup(curveLenInBits/8)*2+1)
+ */
+static int
+pk11_get_EC_PointLenInBytes(PRArenaPool *arena, const SECItem *ecParams)
+{
+ SECItem oid;
+ SECOidTag tag;
+ SECStatus rv;
+
+ /* decode the OID tag */
+ rv = SEC_QuickDERDecodeItem(arena, &oid,
+ SEC_ObjectIDTemplate, ecParams);
+ if (rv != SECSuccess) {
+ /* could be explict curves, allow them to work if the
+ * PKCS #11 module support them. If we try to parse the
+ * explicit curve value in the future, we may return -1 here
+ * to indicate an invalid parameter if the explicit curve
+ * decode fails. */
+ return 0;
+ }
+
+ tag = SECOID_FindOIDTag(&oid);
+ switch (tag) {
+ case SEC_OID_SECG_EC_SECP112R1:
+ case SEC_OID_SECG_EC_SECP112R2:
+ return 29; /* curve len in bytes = 14 bytes */
+ case SEC_OID_SECG_EC_SECT113R1:
+ case SEC_OID_SECG_EC_SECT113R2:
+ return 31; /* curve len in bytes = 15 bytes */
+ case SEC_OID_SECG_EC_SECP128R1:
+ case SEC_OID_SECG_EC_SECP128R2:
+ return 33; /* curve len in bytes = 16 bytes */
+ case SEC_OID_SECG_EC_SECT131R1:
+ case SEC_OID_SECG_EC_SECT131R2:
+ return 35; /* curve len in bytes = 17 bytes */
+ case SEC_OID_SECG_EC_SECP160K1:
+ case SEC_OID_SECG_EC_SECP160R1:
+ case SEC_OID_SECG_EC_SECP160R2:
+ return 41; /* curve len in bytes = 20 bytes */
+ case SEC_OID_SECG_EC_SECT163K1:
+ case SEC_OID_SECG_EC_SECT163R1:
+ case SEC_OID_SECG_EC_SECT163R2:
+ case SEC_OID_ANSIX962_EC_C2PNB163V1:
+ case SEC_OID_ANSIX962_EC_C2PNB163V2:
+ case SEC_OID_ANSIX962_EC_C2PNB163V3:
+ return 43; /* curve len in bytes = 21 bytes */
+ case SEC_OID_ANSIX962_EC_C2PNB176V1:
+ return 45; /* curve len in bytes = 22 bytes */
+ case SEC_OID_ANSIX962_EC_C2TNB191V1:
+ case SEC_OID_ANSIX962_EC_C2TNB191V2:
+ case SEC_OID_ANSIX962_EC_C2TNB191V3:
+ case SEC_OID_SECG_EC_SECP192K1:
+ case SEC_OID_ANSIX962_EC_PRIME192V1:
+ case SEC_OID_ANSIX962_EC_PRIME192V2:
+ case SEC_OID_ANSIX962_EC_PRIME192V3:
+ return 49; /*curve len in bytes = 24 bytes */
+ case SEC_OID_SECG_EC_SECT193R1:
+ case SEC_OID_SECG_EC_SECT193R2:
+ return 51; /*curve len in bytes = 25 bytes */
+ case SEC_OID_ANSIX962_EC_C2PNB208W1:
+ return 53; /*curve len in bytes = 26 bytes */
+ case SEC_OID_SECG_EC_SECP224K1:
+ case SEC_OID_SECG_EC_SECP224R1:
+ return 57; /*curve len in bytes = 28 bytes */
+ case SEC_OID_SECG_EC_SECT233K1:
+ case SEC_OID_SECG_EC_SECT233R1:
+ case SEC_OID_SECG_EC_SECT239K1:
+ case SEC_OID_ANSIX962_EC_PRIME239V1:
+ case SEC_OID_ANSIX962_EC_PRIME239V2:
+ case SEC_OID_ANSIX962_EC_PRIME239V3:
+ case SEC_OID_ANSIX962_EC_C2TNB239V1:
+ case SEC_OID_ANSIX962_EC_C2TNB239V2:
+ case SEC_OID_ANSIX962_EC_C2TNB239V3:
+ return 61; /*curve len in bytes = 30 bytes */
+ case SEC_OID_ANSIX962_EC_PRIME256V1:
+ case SEC_OID_SECG_EC_SECP256K1:
+ return 65; /*curve len in bytes = 32 bytes */
+ case SEC_OID_ANSIX962_EC_C2PNB272W1:
+ return 69; /*curve len in bytes = 34 bytes */
+ case SEC_OID_SECG_EC_SECT283K1:
+ case SEC_OID_SECG_EC_SECT283R1:
+ return 73; /*curve len in bytes = 36 bytes */
+ case SEC_OID_ANSIX962_EC_C2PNB304W1:
+ return 77; /*curve len in bytes = 38 bytes */
+ case SEC_OID_ANSIX962_EC_C2TNB359V1:
+ return 91; /*curve len in bytes = 45 bytes */
+ case SEC_OID_ANSIX962_EC_C2PNB368W1:
+ return 93; /*curve len in bytes = 46 bytes */
+ case SEC_OID_SECG_EC_SECP384R1:
+ return 97; /*curve len in bytes = 48 bytes */
+ case SEC_OID_SECG_EC_SECT409K1:
+ case SEC_OID_SECG_EC_SECT409R1:
+ return 105; /*curve len in bytes = 52 bytes */
+ case SEC_OID_ANSIX962_EC_C2TNB431R1:
+ return 109; /*curve len in bytes = 54 bytes */
+ case SEC_OID_SECG_EC_SECP521R1:
+ return 133; /*curve len in bytes = 66 bytes */
+ case SEC_OID_SECG_EC_SECT571K1:
+ case SEC_OID_SECG_EC_SECT571R1:
+ return 145; /*curve len in bytes = 72 bytes */
+ /* unknown or unrecognized OIDs. return unknown length */
+ default:
+ break;
+ }
+ return 0;
+}
+
+/*
+ * returns the decoded point. In some cases the point may already be decoded.
+ * this function tries to detect those cases and return the point in
+ * publicKeyValue. In other cases it's DER encoded. In those cases the point
+ * is first decoded and returned. Space for the point is allocated out of
+ * the passed in arena.
+ */
+static CK_RV
+pk11_get_Decoded_ECPoint(PRArenaPool *arena, const SECItem *ecParams,
+ const CK_ATTRIBUTE *ecPoint, SECItem *publicKeyValue)
+{
+ SECItem encodedPublicValue;
+ SECStatus rv;
+ int keyLen;
+
+ if (ecPoint->ulValueLen == 0) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ /*
+ * The PKCS #11 spec requires ecPoints to be encoded as a DER OCTET String.
+ * NSS has mistakenly passed unencoded values, and some PKCS #11 vendors
+ * followed that mistake. Now we need to detect which encoding we were
+ * passed in. The task is made more complicated by the fact the the
+ * DER encoding byte (SEC_ASN_OCTET_STRING) is the same as the
+ * EC_POINT_FORM_UNCOMPRESSED byte (0x04), so we can't use that to
+ * determine which curve we are using.
+ */
+
+ /* get the expected key length for the passed in curve.
+ * pk11_get_EC_PointLenInBytes only returns valid values for curves
+ * NSS has traditionally recognized. If the curve is not recognized,
+ * it will return '0', and we have to figure out if the key was
+ * encoded or not heuristically. If the ecParams are invalid, it
+ * will return -1 for the keyLen.
+ */
+ keyLen = pk11_get_EC_PointLenInBytes(arena, ecParams);
+ if (keyLen < 0) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+
+ /* If the point is uncompressed and the lengths match, it
+ * must be an unencoded point */
+ if ((*((char *)ecPoint->pValue) == EC_POINT_FORM_UNCOMPRESSED)
+ && (ecPoint->ulValueLen == keyLen)) {
+ return pk11_Attr2SecItem(arena, ecPoint, publicKeyValue);
+ }
+
+ /* now assume the key passed to us was encoded and decode it */
+ if (*((char *)ecPoint->pValue) == SEC_ASN1_OCTET_STRING) {
+ /* OK, now let's try to decode it and see if it's valid */
+ encodedPublicValue.data = ecPoint->pValue;
+ encodedPublicValue.len = ecPoint->ulValueLen;
+ rv = SEC_QuickDERDecodeItem(arena, publicKeyValue,
+ SEC_OctetStringTemplate, &encodedPublicValue);
+
+ /* it coded correctly & we know the key length (and they match)
+ * then we are done, return the results. */
+ if (keyLen && rv == SECSuccess && publicKeyValue->len == keyLen) {
+ return CKR_OK;
+ }
+
+ /* if we know the key length, one of the above tests should have
+ * succeded. If it doesn't the module gave us bad data */
+ if (keyLen) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+
+ /* We don't know the key length, so we don't know deterministically
+ * which encoding was used. We now will try to pick the most likely
+ * form that's correct, with a preference for the encoded form if we
+ * can't determine for sure. We do this by checking the key we got
+ * back from SEC_QuickDERDecodeItem for defects. If no defects are
+ * found, we assume the encoded paramter was was passed to us.
+ * our defect tests include:
+ * 1) it didn't decode.
+ * 2) The decode key had an invalid length (must be odd).
+ * 3) The decoded key wasn't an UNCOMPRESSED key.
+ * 4) The decoded key didn't include the entire encoded block
+ * except the DER encoding values. (fixing DER length to one
+ * particular value).
+ */
+ if ((rv != SECSuccess)
+ || ((publicKeyValue->len & 1) != 1)
+ || (publicKeyValue->data[0] != EC_POINT_FORM_UNCOMPRESSED)
+ || (PORT_Memcmp(&encodedPublicValue.data[encodedPublicValue.len -
+ publicKeyValue->len], publicKeyValue->data,
+ publicKeyValue->len) != 0)) {
+ /* The decoded public key was flawed, the original key must have
+ * already been in decoded form. Do a quick sanity check then
+ * return the original key value.
+ */
+ if ((encodedPublicValue.len & 1) == 0) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ return pk11_Attr2SecItem(arena, ecPoint, publicKeyValue);
+ }
+
+ /* as best we can figure, the passed in key was encoded, and we've
+ * now decoded it. Note: there is a chance this could be wrong if the
+ * following conditions hold:
+ * 1) The first byte or bytes of the X point looks like a valid length
+ * of precisely the right size (2*curveSize -1). this means for curves
+ * less than 512 bits (64 bytes), this will happen 1 in 256 times*.
+ * for curves between 512 and 1024, this will happen 1 in 65,536 times*
+ * for curves between 1024 and 256K this will happen 1 in 16 million*
+ * 2) The length of the 'DER length field' is odd
+ * (making both the encoded and decode
+ * values an odd length. this is true of all curves less than 512,
+ * as well as curves between 1024 and 256K).
+ * 3) The X[length of the 'DER length field'] == 0x04, 1 in 256.
+ *
+ * (* assuming all values are equally likely in the first byte,
+ * This isn't true if the curve length is not a multiple of 8. In these
+ * cases, if the DER length is possible, it's more likely,
+ * if it's not possible, then we have no false decodes).
+ *
+ * For reference here are the odds for the various curves we currently
+ * have support for (and the only curves SSL will negotiate at this
+ * time). NOTE: None of the supported curves will show up here
+ * because we return a valid length for all of these curves.
+ * The only way to get here is to have some application (not SSL)
+ * which supports some unknown curve and have some vendor supplied
+ * PKCS #11 module support that curve. NOTE: in this case, one
+ * presumes that that pkcs #11 module is likely to be using the
+ * correct encodings.
+ *
+ * Prime Curves (GFp):
+ * Bit False Odds of
+ * Size DER Len False Decode Positive
+ * 112 27 1 in 65536
+ * 128 31 1 in 65536
+ * 160 39 1 in 65536
+ * 192 47 1 in 65536
+ * 224 55 1 in 65536
+ * 239 59 1 in 32768 (top byte can only be 0-127)
+ * 256 63 1 in 65536
+ * 521 129,131 0 (decoded value would be even)
+ *
+ * Binary curves (GF2m).
+ * Bit False Odds of
+ * Size DER Len False Decode Positive
+ * 131 33 0 (top byte can only be 0-7)
+ * 163 41 0 (top byte can only be 0-7)
+ * 176 43 1 in 65536
+ * 191 47 1 in 32768 (top byte can only be 0-127)
+ * 193 49 0 (top byte can only be 0-1)
+ * 208 51 1 in 65536
+ * 233 59 0 (top byte can only be 0-1)
+ * 239 59 1 in 32768 (top byte can only be 0-127)
+ * 272 67 1 in 65536
+ * 283 71 0 (top byte can only be 0-7)
+ * 304 75 1 in 65536
+ * 359 89 1 in 32768 (top byte can only be 0-127)
+ * 368 91 1 in 65536
+ * 409 103 0 (top byte can only be 0-1)
+ * 431 107 1 in 32768 (top byte can only be 0-127)
+ * 571 129,143 0 (decoded value would be even)
+ *
+ */
+
+ return CKR_OK;
+ }
+
+ /* In theory, we should handle the case where the curve == 0 and
+ * the first byte is EC_POINT_FORM_UNCOMPRESSED, (which would be
+ * handled by doing a santity check on the key length and returning
+ * pk11_Attr2SecItem() to copy the ecPoint to the publicKeyValue).
+ *
+ * This test is unnecessary, however, due to the fact that
+ * EC_POINT_FORM_UNCOMPRESSED == SEC_ASIN1_OCTET_STRING, that case is
+ * handled in the above if. That means if we get here, the initial
+ * byte of our ecPoint value was invalid, so we can safely return.
+ * invalid attribute.
+ */
+
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+}
+
/*
* extract a public key from a slot and id
*/
@@ -399,7 +720,7 @@ PK11_ExtractPublicKey(PK11SlotInfo *slot,KeyType keyType,CK_OBJECT_HANDLE id)
PK11_SETATTRS(attrs, CKA_EC_POINT, NULL, 0); attrs++;
templateCount = attrs - template;
PR_ASSERT(templateCount <= sizeof(template)/sizeof(CK_ATTRIBUTE));
- crv = PK11_GetAttributes(tmp_arena,slot,id,template,templateCount);
+ crv = PK11_GetAttributes(arena,slot,id,template,templateCount);
if (crv != CKR_OK) break;
if ((keyClass != CKO_PUBLIC_KEY) || (pk11KeyType != CKK_EC)) {
@@ -410,8 +731,9 @@ PK11_ExtractPublicKey(PK11SlotInfo *slot,KeyType keyType,CK_OBJECT_HANDLE id)
crv = pk11_Attr2SecItem(arena,ecparams,
&pubKey->u.ec.DEREncodedParams);
if (crv != CKR_OK) break;
- crv = pk11_Attr2SecItem(arena,value,&pubKey->u.ec.publicValue);
- if (crv != CKR_OK) break;
+ crv = pk11_get_Decoded_ECPoint(arena,
+ &pubKey->u.ec.DEREncodedParams, value,
+ &pubKey->u.ec.publicValue);
break;
case fortezzaKey:
case nullKey:
diff --git a/security/nss/lib/pk11wrap/pk11skey.c b/security/nss/lib/pk11wrap/pk11skey.c
index 0527dcd0e..20e155e5b 100644
--- a/security/nss/lib/pk11wrap/pk11skey.c
+++ b/security/nss/lib/pk11wrap/pk11skey.c
@@ -1614,6 +1614,7 @@ PK11_PubDerive(SECKEYPrivateKey *privKey, SECKEYPublicKey *pubKey,
mechanism.mechanism = derive;
/* we can undefine these when we define diffie-helman keys */
+
mechanism.pParameter = pubKey->u.dh.publicValue.data;
mechanism.ulParameterLen = pubKey->u.dh.publicValue.len;
@@ -1635,6 +1636,7 @@ PK11_PubDerive(SECKEYPrivateKey *privKey, SECKEYPublicKey *pubKey,
int templateCount;
CK_ATTRIBUTE *attrs = keyTemplate;
CK_ECDH1_DERIVE_PARAMS *mechParams = NULL;
+ SECItem *pubValue = NULL;
if (pubKey->keyType != ecKey) {
PORT_SetError(SEC_ERROR_BAD_KEY);
@@ -1660,8 +1662,20 @@ PK11_PubDerive(SECKEYPrivateKey *privKey, SECKEYPublicKey *pubKey,
mechParams->kdf = CKD_SHA1_KDF;
mechParams->ulSharedDataLen = 0;
mechParams->pSharedData = NULL;
- mechParams->ulPublicDataLen = pubKey->u.ec.publicValue.len;
- mechParams->pPublicData = pubKey->u.ec.publicValue.data;
+
+ if (PR_GetEnv("NSS_USE_DECODED_CKA_EC_POINT")) {
+ mechParams->ulPublicDataLen = pubKey->u.ec.publicValue.len;
+ mechParams->pPublicData = pubKey->u.ec.publicValue.data;
+ } else {
+ pubValue = SEC_ASN1EncodeItem(NULL, NULL,
+ &pubKey->u.ec.publicValue, SEC_OctetStringTemplate);
+ if (pubValue == NULL) {
+ PORT_ZFree(mechParams, sizeof(CK_ECDH1_DERIVE_PARAMS));
+ break;
+ }
+ mechParams->ulPublicDataLen = pubValue->len;
+ mechParams->pPublicData = pubValue->data;
+ }
mechanism.mechanism = derive;
mechanism.pParameter = mechParams;
@@ -1673,6 +1687,9 @@ PK11_PubDerive(SECKEYPrivateKey *privKey, SECKEYPublicKey *pubKey,
templateCount, &symKey->objectID);
pk11_ExitKeyMonitor(symKey);
+ if (pubValue) {
+ SECITEM_FreeItem(pubValue,PR_TRUE);
+ }
PORT_ZFree(mechParams, sizeof(CK_ECDH1_DERIVE_PARAMS));
if (crv == CKR_OK) return symKey;
@@ -1704,6 +1721,7 @@ pk11_PubDeriveECKeyWithKDF(
int templateCount;
CK_ATTRIBUTE *attrs = keyTemplate;
CK_ECDH1_DERIVE_PARAMS *mechParams = NULL;
+ SECItem *pubValue = NULL;
if (pubKey->keyType != ecKey) {
PORT_SetError(SEC_ERROR_BAD_KEY);
@@ -1748,8 +1766,20 @@ pk11_PubDeriveECKeyWithKDF(
mechParams->ulSharedDataLen = sharedData->len;
mechParams->pSharedData = sharedData->data;
}
- mechParams->ulPublicDataLen = pubKey->u.ec.publicValue.len;
- mechParams->pPublicData = pubKey->u.ec.publicValue.data;
+ if (PR_GetEnv("NSS_USE_DECODED_CKA_EC_POINT")) {
+ mechParams->ulPublicDataLen = pubKey->u.ec.publicValue.len;
+ mechParams->pPublicData = pubKey->u.ec.publicValue.data;
+ } else {
+ pubValue = SEC_ASN1EncodeItem(NULL, NULL,
+ &pubKey->u.ec.publicValue, SEC_OctetStringTemplate);
+ if (pubValue == NULL) {
+ PORT_ZFree(mechParams, sizeof(CK_ECDH1_DERIVE_PARAMS));
+ PK11_FreeSymKey(symKey);
+ return NULL;
+ }
+ mechParams->ulPublicDataLen = pubValue->len;
+ mechParams->pPublicData = pubValue->data;
+ }
mechanism.mechanism = derive;
mechanism.pParameter = mechParams;
@@ -1761,6 +1791,9 @@ pk11_PubDeriveECKeyWithKDF(
pk11_ExitKeyMonitor(symKey);
PORT_ZFree(mechParams, sizeof(CK_ECDH1_DERIVE_PARAMS));
+ if (pubValue) {
+ SECITEM_FreeItem(pubValue,PR_TRUE);
+ }
if (crv != CKR_OK) {
PK11_FreeSymKey(symKey);
diff --git a/security/nss/lib/softoken/legacydb/lgattr.c b/security/nss/lib/softoken/legacydb/lgattr.c
index 15cfc63ec..8c53ed6ab 100644
--- a/security/nss/lib/softoken/legacydb/lgattr.c
+++ b/security/nss/lib/softoken/legacydb/lgattr.c
@@ -45,6 +45,7 @@
#include "pcert.h"
#include "blapi.h"
#include "secerr.h"
+#include "secasn1.h"
/*
* Cache the object we are working on during Set's and Get's
@@ -610,9 +611,23 @@ lg_FindECPublicKeyAttribute(NSSLOWKEYPublicKey *key, CK_ATTRIBUTE_TYPE type,
key->u.ec.ecParams.DEREncoding.data,
key->u.ec.ecParams.DEREncoding.len);
case CKA_EC_POINT:
- return lg_CopyAttributeSigned(attribute, type,
+ if (getenv("NSS_USE_DECODED_CKA_EC_POINT")) {
+ return lg_CopyAttributeSigned(attribute, type,
key->u.ec.publicValue.data,
key->u.ec.publicValue.len);
+ } else {
+ SECItem *pubValue = SEC_ASN1EncodeItem(NULL, NULL,
+ &(key->u.ec.publicValue), SEC_OctetStringTemplate);
+ CK_RV crv;
+ if (!pubValue) {
+ return CKR_HOST_MEMORY;
+ }
+ crv = lg_CopyAttributeSigned(attribute, type,
+ pubValue->data,
+ pubValue->len);
+ SECITEM_FreeItem(pubValue, PR_TRUE);
+ return crv;
+ }
default:
break;
}
diff --git a/security/nss/lib/softoken/legacydb/lgcreate.c b/security/nss/lib/softoken/legacydb/lgcreate.c
index 1dd1b2709..d4fcca69e 100644
--- a/security/nss/lib/softoken/legacydb/lgcreate.c
+++ b/security/nss/lib/softoken/legacydb/lgcreate.c
@@ -41,6 +41,7 @@
#include "lowkeyi.h"
#include "blapi.h"
#include "secder.h"
+#include "secasn1.h"
#include "keydbi.h"
@@ -429,10 +430,16 @@ lg_createPublicKeyObject(SDB *sdb, CK_KEY_TYPE key_type,
CK_OBJECT_HANDLE *handle, const CK_ATTRIBUTE *templ, CK_ULONG count)
{
CK_ATTRIBUTE_TYPE pubKeyAttr = CKA_VALUE;
- CK_RV crv;
+ CK_RV crv = CKR_OK;
NSSLOWKEYPrivateKey *priv;
- SECItem pubKey;
+ SECItem pubKeySpace = {siBuffer, NULL, 0};
+ SECItem *pubKey;
+#ifdef NSS_ENABLE_ECC
+ SECItem pubKey2Space = {siBuffer, NULL, 0};
+ PRArenaPool *arena = NULL;
+#endif /* NSS_ENABLE_ECC */
NSSLOWKEYDBHandle *keyHandle = NULL;
+
switch (key_type) {
case CKK_RSA:
@@ -451,34 +458,80 @@ lg_createPublicKeyObject(SDB *sdb, CK_KEY_TYPE key_type,
}
- crv = lg_Attribute2SSecItem(NULL,pubKeyAttr,templ,count,&pubKey);
+ pubKey = &pubKeySpace;
+ crv = lg_Attribute2SSecItem(NULL,pubKeyAttr,templ,count,pubKey);
if (crv != CKR_OK) return crv;
- PORT_Assert(pubKey.data);
+#ifdef NSS_ENABLE_ECC
+ if (key_type == CKK_EC) {
+ SECStatus rv;
+ /*
+ * for ECC, use the decoded key first.
+ */
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ crv = CKR_HOST_MEMORY;
+ goto done;
+ }
+ rv= SEC_QuickDERDecodeItem(arena, &pubKey2Space,
+ SEC_OctetStringTemplate, pubKey);
+ if (rv != SECSuccess) {
+ /* decode didn't work, just try the pubKey */
+ PORT_FreeArena(arena, PR_FALSE);
+ arena = NULL;
+ } else {
+ /* try the decoded pub key first */
+ pubKey = &pubKey2Space;
+ }
+ }
+#endif /* NSS_ENABLE_ECC */
+
+ PORT_Assert(pubKey->data);
+ if (pubKey->data == NULL) {
+ crv = CKR_ATTRIBUTE_VALUE_INVALID;
+ goto done;
+ }
keyHandle = lg_getKeyDB(sdb);
if (keyHandle == NULL) {
- PORT_Free(pubKey.data);
- return CKR_TOKEN_WRITE_PROTECTED;
+ crv = CKR_TOKEN_WRITE_PROTECTED;
+ goto done;
}
if (keyHandle->version != 3) {
unsigned char buf[SHA1_LENGTH];
- SHA1_HashBuf(buf,pubKey.data,pubKey.len);
- PORT_Memcpy(pubKey.data,buf,sizeof(buf));
- pubKey.len = sizeof(buf);
+ SHA1_HashBuf(buf,pubKey->data,pubKey->len);
+ PORT_Memcpy(pubKey->data,buf,sizeof(buf));
+ pubKey->len = sizeof(buf);
}
/* make sure the associated private key already exists */
/* only works if we are logged in */
- priv = nsslowkey_FindKeyByPublicKey(keyHandle, &pubKey, sdb /*password*/);
+ priv = nsslowkey_FindKeyByPublicKey(keyHandle, pubKey, sdb /*password*/);
+#ifdef NSS_ENABLE_ECC
+ if (priv == NULL && pubKey == &pubKey2Space) {
+ /* no match on the decoded key, match the original pubkey */
+ pubKey = &pubKeySpace;
+ priv = nsslowkey_FindKeyByPublicKey(keyHandle, pubKey,
+ sdb /*password*/);
+ }
+#endif
if (priv == NULL) {
- PORT_Free(pubKey.data);
- return crv;
+ /* the legacy database can only 'store' public keys which already
+ * have their corresponding private keys in the database */
+ crv = CKR_ATTRIBUTE_VALUE_INVALID;
+ goto done;
}
nsslowkey_DestroyPrivateKey(priv);
+ crv = CKR_OK;
- *handle = lg_mkHandle(sdb, &pubKey, LG_TOKEN_TYPE_PUB);
- PORT_Free(pubKey.data);
+ *handle = lg_mkHandle(sdb, pubKey, LG_TOKEN_TYPE_PUB);
- return CKR_OK;
+done:
+ PORT_Free(pubKeySpace.data);
+#ifdef NSS_ENABLE_ECC
+ if (arena)
+ PORT_FreeArena(arena, PR_FALSE);
+#endif
+
+ return crv;
}
/* make a private key from a verified object */
diff --git a/security/nss/lib/softoken/pkcs11.c b/security/nss/lib/softoken/pkcs11.c
index 19882fe89..71eaefb6e 100644
--- a/security/nss/lib/softoken/pkcs11.c
+++ b/security/nss/lib/softoken/pkcs11.c
@@ -67,6 +67,8 @@
#include "secoid.h"
#include "sftkdb.h"
#include "sftkpars.h"
+#include "ec.h"
+#include "secasn1.h"
PRBool parentForkedAfterC_Initialize;
@@ -851,7 +853,6 @@ sftk_handlePublicKeyObject(SFTKSession *session, SFTKObject *object,
CK_BBOOL wrap = CK_TRUE;
CK_BBOOL derive = CK_FALSE;
CK_BBOOL verify = CK_TRUE;
- CK_ATTRIBUTE_TYPE pubKeyAttr = CKA_VALUE;
CK_RV crv;
switch (key_type) {
@@ -865,7 +866,6 @@ sftk_handlePublicKeyObject(SFTKSession *session, SFTKObject *object,
if (crv != CKR_OK) {
return crv;
}
- pubKeyAttr = CKA_MODULUS;
break;
case CKK_DSA:
crv = sftk_ConstrainAttribute(object, CKA_SUBPRIME,
@@ -918,7 +918,6 @@ sftk_handlePublicKeyObject(SFTKSession *session, SFTKObject *object,
if ( !sftk_hasAttribute(object, CKA_EC_POINT)) {
return CKR_TEMPLATE_INCOMPLETE;
}
- pubKeyAttr = CKA_EC_POINT;
derive = CK_TRUE; /* for ECDH */
verify = CK_TRUE; /* for ECDSA */
encrypt = CK_FALSE;
@@ -1258,7 +1257,6 @@ sftk_handleKeyObject(SFTKSession *session, SFTKObject *object)
{
SFTKAttribute *attribute;
CK_KEY_TYPE key_type;
- CK_BBOOL cktrue = CK_TRUE;
CK_BBOOL ckfalse = CK_FALSE;
CK_RV crv;
@@ -1636,6 +1634,45 @@ NSSLOWKEYPublicKey *sftk_GetPubKey(SFTKObject *object,CK_KEY_TYPE key_type,
crv = sftk_Attribute2SSecItem(arena,&pubKey->u.ec.publicValue,
object,CKA_EC_POINT);
+ if (crv == CKR_OK) {
+ int keyLen,curveLen;
+
+ curveLen = (pubKey->u.ec.ecParams.fieldID.size +7)/8;
+ keyLen = (2*curveLen)+1;
+
+ /* special note: We can't just use the first byte to determine
+ * between these 2 cases because both EC_POINT_FORM_UNCOMPRESSED
+ * and SEC_ASN1_OCTET_STRING are 0x04 */
+
+ /* handle the non-DER encoded case (UNCOMPRESSED only) */
+ if (pubKey->u.ec.publicValue.data[0] == EC_POINT_FORM_UNCOMPRESSED
+ && pubKey->u.ec.publicValue.len == keyLen) {
+ break; /* key was not DER encoded, no need to unwrap */
+ }
+
+ /* if we ever support compressed, handle it here */
+
+ /* handle the encoded case */
+ if ((pubKey->u.ec.publicValue.data[0] == SEC_ASN1_OCTET_STRING)
+ && pubKey->u.ec.publicValue.len > keyLen) {
+ SECItem publicValue;
+ SECStatus rv;
+
+ rv = SEC_QuickDERDecodeItem(arena, &publicValue,
+ SEC_OctetStringTemplate, &pubKey->u.ec.publicValue);
+ /* nope, didn't decode correctly */
+ if ((rv != SECSuccess)
+ || (publicValue.data[0] != EC_POINT_FORM_UNCOMPRESSED)
+ || (publicValue.len != keyLen)) {
+ crv = CKR_ATTRIBUTE_VALUE_INVALID;
+ break;
+ }
+ /* replace our previous with the decoded key */
+ pubKey->u.ec.publicValue = publicValue;
+ break;
+ }
+ crv = CKR_ATTRIBUTE_VALUE_INVALID;
+ }
break;
#endif /* NSS_ENABLE_ECC */
default:
diff --git a/security/nss/lib/softoken/pkcs11c.c b/security/nss/lib/softoken/pkcs11c.c
index 5ebbf8608..49f00109f 100644
--- a/security/nss/lib/softoken/pkcs11c.c
+++ b/security/nss/lib/softoken/pkcs11c.c
@@ -4017,8 +4017,20 @@ dhgn_done:
break;
}
- crv = sftk_AddAttributeType(publicKey, CKA_EC_POINT,
+ if (getenv("NSS_USE_DECODED_CKA_EC_POINT")) {
+ crv = sftk_AddAttributeType(publicKey, CKA_EC_POINT,
sftk_item_expand(&ecPriv->publicValue));
+ } else {
+ SECItem *pubValue = SEC_ASN1EncodeItem(NULL, NULL,
+ &ecPriv->publicValue, SEC_OctetStringTemplate);
+ if (!pubValue) {
+ crv = CKR_ARGUMENTS_BAD;
+ goto ecgn_done;
+ }
+ crv = sftk_AddAttributeType(publicKey, CKA_EC_POINT,
+ sftk_item_expand(pubValue));
+ SECITEM_FreeItem(pubValue, PR_TRUE);
+ }
if (crv != CKR_OK) goto ecgn_done;
crv = sftk_AddAttributeType(privateKey, CKA_VALUE,
@@ -5843,9 +5855,10 @@ key_and_mac_derive_fail:
unsigned char secret_hash[20];
unsigned char *secret;
unsigned char *keyData = NULL;
- int secretlen;
+ int secretlen, curveLen, pubKeyLen;
CK_ECDH1_DERIVE_PARAMS *mechParams;
NSSLOWKEYPrivateKey *privKey;
+ PLArenaPool *arena = NULL;
/* Check mechanism parameters */
mechParams = (CK_ECDH1_DERIVE_PARAMS *) pMechanism->pParameter;
@@ -5868,6 +5881,31 @@ key_and_mac_derive_fail:
ecPoint.data = mechParams->pPublicData;
ecPoint.len = mechParams->ulPublicDataLen;
+ curveLen = (privKey->u.ec.ecParams.fieldID.size +7)/8;
+ pubKeyLen = (2*curveLen) + 1;
+
+ /* if the len is too small, can't be a valid point */
+ if (ecPoint.len < pubKeyLen) {
+ goto ec_loser;
+ }
+ /* if the len is too large, must be an encoded point (length is
+ * equal case just falls through */
+ if (ecPoint.len > pubKeyLen) {
+ SECItem newPoint;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ goto ec_loser;
+ }
+
+ rv = SEC_QuickDERDecodeItem(arena, &newPoint,
+ SEC_OctetStringTemplate, &ecPoint);
+ if (rv != SECSuccess) {
+ goto ec_loser;
+ }
+ ecPoint = newPoint;
+ }
+
if (pMechanism->mechanism == CKM_ECDH1_COFACTOR_DERIVE) {
withCofactor = PR_TRUE;
} else {
@@ -5877,19 +5915,22 @@ key_and_mac_derive_fail:
*/
if (EC_ValidatePublicKey(&privKey->u.ec.ecParams, &ecPoint)
!= SECSuccess) {
- crv = CKR_ARGUMENTS_BAD;
- PORT_Free(ecScalar.data);
- if (privKey != sourceKey->objectInfo)
- nsslowkey_DestroyPrivateKey(privKey);
- break;
+ goto ec_loser;
}
}
rv = ECDH_Derive(&ecPoint, &privKey->u.ec.ecParams, &ecScalar,
withCofactor, &tmp);
PORT_Free(ecScalar.data);
- if (privKey != sourceKey->objectInfo)
+ ecScalar.data = NULL;
+ if (privKey != sourceKey->objectInfo) {
nsslowkey_DestroyPrivateKey(privKey);
+ privKey=NULL;
+ }
+ if (arena) {
+ PORT_FreeArena(arena,PR_FALSE);
+ arena=NULL;
+ }
if (rv != SECSuccess) {
crv = sftk_MapCryptError(PORT_GetError());
@@ -5951,6 +5992,17 @@ key_and_mac_derive_fail:
PORT_Memset(secret_hash, 0, 20);
break;
+
+ec_loser:
+ crv = CKR_ARGUMENTS_BAD;
+ PORT_Free(ecScalar.data);
+ if (privKey != sourceKey->objectInfo)
+ nsslowkey_DestroyPrivateKey(privKey);
+ if (arena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ break;
+
}
#endif /* NSS_ENABLE_ECC */