diff options
-rw-r--r-- | cmd/manifest.mn | 1 | ||||
-rw-r--r-- | cmd/rsapoptst/rsapoptst.c | 183 | ||||
-rw-r--r-- | cmd/rsapoptst/rsapoptst.gyp | 25 | ||||
-rw-r--r-- | lib/nss/nss.def | 1 | ||||
-rw-r--r-- | lib/pk11wrap/pk11merge.c | 1 | ||||
-rw-r--r-- | lib/pk11wrap/pk11obj.c | 38 | ||||
-rw-r--r-- | lib/pk11wrap/pk11pub.h | 4 | ||||
-rw-r--r-- | lib/pk11wrap/secmodti.h | 1 | ||||
-rw-r--r-- | nss.gyp | 1 | ||||
-rwxr-xr-x | tests/cipher/cipher.sh | 16 |
10 files changed, 230 insertions, 41 deletions
diff --git a/cmd/manifest.mn b/cmd/manifest.mn index 153384ce1..f5e6bc236 100644 --- a/cmd/manifest.mn +++ b/cmd/manifest.mn @@ -63,6 +63,7 @@ NSS_SRCDIRS = \ pp \ pwdecrypt \ rsaperf \ + rsapoptst \ sdrtest \ selfserv \ signtool \ diff --git a/cmd/rsapoptst/rsapoptst.c b/cmd/rsapoptst/rsapoptst.c index 81ddcd6c4..800c75473 100644 --- a/cmd/rsapoptst/rsapoptst.c +++ b/cmd/rsapoptst/rsapoptst.c @@ -23,7 +23,7 @@ static const struct test_args test_array[] = { { "d_n_q", 0x02, "private exponent, modulus, prime2" }, { "d_p_q", 0x04, "private exponent, prime1, prime2" }, { "e_d_q", 0x08, "public exponent, private exponent, prime2" }, - { "e_d_n", 0x10, "public exponent, private exponent, moduls" } + { "e_d_n", 0x10, "public exponent, private exponent, modulus" } }; static const int test_array_size = (sizeof(test_array) / sizeof(struct test_args)); @@ -58,6 +58,7 @@ const static CK_ATTRIBUTE rsaTemplate[] = { { CKA_TOKEN, NULL, 0 }, { CKA_SENSITIVE, NULL, 0 }, { CKA_PRIVATE, NULL, 0 }, + { CKA_ID, NULL, 0 }, { CKA_MODULUS, NULL, 0 }, { CKA_PUBLIC_EXPONENT, NULL, 0 }, { CKA_PRIVATE_EXPONENT, NULL, 0 }, @@ -123,46 +124,77 @@ fail: #define ATTR_STRING(x) getNameFromAttribute(x) +static void +dumphex(FILE *file, const unsigned char *cpval, int start, int end) +{ + int i; + for (i = start; i < end; i++) { + if ((i % 16) == 0) + fprintf(file, "\n "); + fprintf(file, " %02x", cpval[i]); + } + return; +} + void -dumpTemplate(CK_ATTRIBUTE *template, int start, int end) +dumpTemplate(FILE *file, const CK_ATTRIBUTE *template, int start, int end) { - int i, j; - for (i = 0; i < end; i++) { + int i; + for (i = start; i < end; i++) { unsigned char cval; CK_ULONG ulval; - unsigned char *cpval; + const unsigned char *cpval; - fprintf(stderr, "%s:", ATTR_STRING(template[i].type)); + fprintf(file, "%s:", ATTR_STRING(template[i].type)); switch (template[i].ulValueLen) { case 1: cval = *(unsigned char *)template[i].pValue; switch (cval) { case 0: - fprintf(stderr, " false"); + fprintf(file, " false"); break; case 1: - fprintf(stderr, " true"); + fprintf(file, " true"); break; default: - fprintf(stderr, " %d (=0x%02x,'%c')", cval, cval, cval); + fprintf(file, " %d (=0x%02x,'%c')", cval, cval, cval); break; } break; case sizeof(CK_ULONG): ulval = *(CK_ULONG *)template[i].pValue; - fprintf(stderr, " %ld (=0x%04lx)", ulval, ulval); + fprintf(file, " %ld (=0x%04lx)", ulval, ulval); break; default: - cpval = (unsigned char *)template[i].pValue; - for (j = 0; j < template[i].ulValueLen; j++) { - if ((j % 16) == 0) - fprintf(stderr, "\n "); - fprintf(stderr, " %02x", cpval[j]); - } + cpval = (const unsigned char *)template[i].pValue; + dumphex(file, cpval, 0, template[i].ulValueLen); break; } - fprintf(stderr, "\n"); + fprintf(file, "\n"); + } +} + +void +dumpItem(FILE *file, const SECItem *item) +{ + const unsigned char *cpval; + + if (item == NULL) { + fprintf(file, " pNULL "); + return; + } + if (item->data == NULL) { + fprintf(file, " NULL "); + return; } + if (item->len == 0) { + fprintf(file, " Empty "); + return; + } + cpval = item->data; + dumphex(file, cpval, 0, item->len); + fprintf(file, " "); + return; } PRBool @@ -191,6 +223,9 @@ rsaKeysAreEqual(PK11ObjectType srcType, void *src, } for (i = 0; i < RSA_ATTRIBUTES; i++) { + if (srcTemplate[i].type == CKA_ID) { + continue; /* we purposefully make the CKA_ID different */ + } if (srcTemplate[i].ulValueLen != destTemplate[i].ulValueLen) { printf("key->%s not equal src_len = %ld, dest_len=%ld\n", ATTR_STRING(srcTemplate[i].type), @@ -204,18 +239,22 @@ rsaKeysAreEqual(PK11ObjectType srcType, void *src, } if (!areEqual) { fprintf(stderr, "original key:\n"); - dumpTemplate(srcTemplate, 0, RSA_ATTRIBUTES); + dumpTemplate(stderr, srcTemplate, 0, RSA_ATTRIBUTES); fprintf(stderr, "created key:\n"); - dumpTemplate(destTemplate, 0, RSA_ATTRIBUTES); + dumpTemplate(stderr, destTemplate, 0, RSA_ATTRIBUTES); } + resetTemplate(srcTemplate, 0, RSA_ATTRIBUTES); + resetTemplate(destTemplate, 0, RSA_ATTRIBUTES); return areEqual; } static int exp_exp_prime_fail_count = 0; +#define LEAK_ID 0xf + static int doRSAPopulateTest(unsigned int keySize, unsigned long exponent, - int mask, void *pwarg) + int mask, int round, void *pwarg) { SECKEYPrivateKey *rsaPrivKey; SECKEYPublicKey *rsaPubKey; @@ -227,7 +266,10 @@ doRSAPopulateTest(unsigned int keySize, unsigned long exponent, CK_OBJECT_CLASS obj_class = CKO_PRIVATE_KEY; CK_KEY_TYPE key_type = CKK_RSA; CK_BBOOL ck_false = CK_FALSE; + CK_BYTE cka_id[2] = { 0, 0 }; int failed = 0; + int leak_found; /* did we find the expected leak */ + int expect_leak = 0; /* are we expecting a leak? */ rsaParams.pe = exponent; rsaParams.keySizeInBits = keySize; @@ -259,11 +301,15 @@ doRSAPopulateTest(unsigned int keySize, unsigned long exponent, tstTemplate[3].ulValueLen = sizeof(ck_false); tstTemplate[4].pValue = &ck_false; tstTemplate[4].ulValueLen = sizeof(ck_false); - tstHeaderCount = 5; + tstTemplate[5].pValue = &cka_id[0]; + tstTemplate[5].ulValueLen = sizeof(cka_id); + tstHeaderCount = 6; + cka_id[0] = round; if (mask & 1) { printf("%s\n", test_array[1].description); resetTemplate(tstTemplate, tstHeaderCount, RSA_ATTRIBUTES); + cka_id[1] = 0; copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, tstHeaderCount, CKA_PUBLIC_EXPONENT); copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, @@ -271,10 +317,10 @@ doRSAPopulateTest(unsigned int keySize, unsigned long exponent, copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, tstHeaderCount + 2, CKA_PRIME_1); - tstPrivKey = PK11_CreateGenericObject(slot, tstTemplate, - tstHeaderCount + - 3, - PR_FALSE); + tstPrivKey = PK11_CreateManagedGenericObject(slot, tstTemplate, + tstHeaderCount + + 3, + PR_FALSE); if (tstPrivKey == NULL) { fprintf(stderr, "RSA Populate failed: pubExp mod p\n"); failed = 1; @@ -290,6 +336,7 @@ doRSAPopulateTest(unsigned int keySize, unsigned long exponent, printf("%s\n", test_array[2].description); /* test the basic2 case, public exponent, modulus, prime2 */ resetTemplate(tstTemplate, tstHeaderCount, RSA_ATTRIBUTES); + cka_id[1] = 1; copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, tstHeaderCount, CKA_PUBLIC_EXPONENT); copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, @@ -299,10 +346,10 @@ doRSAPopulateTest(unsigned int keySize, unsigned long exponent, /* test with q in the prime1 position */ tstTemplate[tstHeaderCount + 2].type = CKA_PRIME_1; - tstPrivKey = PK11_CreateGenericObject(slot, tstTemplate, - tstHeaderCount + - 3, - PR_FALSE); + tstPrivKey = PK11_CreateManagedGenericObject(slot, tstTemplate, + tstHeaderCount + + 3, + PR_FALSE); if (tstPrivKey == NULL) { fprintf(stderr, "RSA Populate failed: pubExp mod q\n"); failed = 1; @@ -318,6 +365,7 @@ doRSAPopulateTest(unsigned int keySize, unsigned long exponent, printf("%s\n", test_array[3].description); /* test the medium case, private exponent, prime1, prime2 */ resetTemplate(tstTemplate, tstHeaderCount, RSA_ATTRIBUTES); + cka_id[1] = 2; copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, tstHeaderCount, CKA_PRIVATE_EXPONENT); @@ -329,10 +377,10 @@ doRSAPopulateTest(unsigned int keySize, unsigned long exponent, tstTemplate[tstHeaderCount + 2].type = CKA_PRIME_1; tstTemplate[tstHeaderCount + 1].type = CKA_PRIME_2; - tstPrivKey = PK11_CreateGenericObject(slot, tstTemplate, - tstHeaderCount + - 3, - PR_FALSE); + tstPrivKey = PK11_CreateManagedGenericObject(slot, tstTemplate, + tstHeaderCount + + 3, + PR_FALSE); if (tstPrivKey == NULL) { fprintf(stderr, "RSA Populate failed: privExp p q\n"); failed = 1; @@ -348,6 +396,7 @@ doRSAPopulateTest(unsigned int keySize, unsigned long exponent, printf("%s\n", test_array[4].description); /* test the advanced case, public exponent, private exponent, prime2 */ resetTemplate(tstTemplate, tstHeaderCount, RSA_ATTRIBUTES); + cka_id[1] = 3; copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, tstHeaderCount, CKA_PRIVATE_EXPONENT); copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, @@ -355,10 +404,10 @@ doRSAPopulateTest(unsigned int keySize, unsigned long exponent, copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, tstHeaderCount + 2, CKA_PRIME_2); - tstPrivKey = PK11_CreateGenericObject(slot, tstTemplate, - tstHeaderCount + - 3, - PR_FALSE); + tstPrivKey = PK11_CreateManagedGenericObject(slot, tstTemplate, + tstHeaderCount + + 3, + PR_FALSE); if (tstPrivKey == NULL) { fprintf(stderr, "RSA Populate failed: pubExp privExp q\n"); fprintf(stderr, " this is expected periodically. It means we\n"); @@ -373,11 +422,12 @@ doRSAPopulateTest(unsigned int keySize, unsigned long exponent, if (tstPrivKey) PK11_DestroyGenericObject(tstPrivKey); } - if (mask & 16) { + if (mask & 0x10) { printf("%s\n", test_array[5].description); /* test the advanced case2, public exponent, private exponent, modulus */ resetTemplate(tstTemplate, tstHeaderCount, RSA_ATTRIBUTES); + cka_id[1] = LEAK_ID; copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, tstHeaderCount, CKA_PRIVATE_EXPONENT); @@ -386,6 +436,7 @@ doRSAPopulateTest(unsigned int keySize, unsigned long exponent, copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, tstHeaderCount + 2, CKA_MODULUS); + /* purposefully use the old version. This will create a leak */ tstPrivKey = PK11_CreateGenericObject(slot, tstTemplate, tstHeaderCount + 3, @@ -398,9 +449,59 @@ doRSAPopulateTest(unsigned int keySize, unsigned long exponent, fprintf(stderr, "RSA Populate key mismatch: pubExp privExp mod\n"); failed = 1; } + expect_leak = 1; if (tstPrivKey) PK11_DestroyGenericObject(tstPrivKey); } + resetTemplate(tstTemplate, tstHeaderCount, RSA_ATTRIBUTES); + SECKEY_DestroyPrivateKey(rsaPrivKey); + SECKEY_DestroyPublicKey(rsaPubKey); + + /* make sure we didn't leak */ + leak_found = 0; + tstPrivKey = PK11_FindGenericObjects(slot, CKO_PRIVATE_KEY); + if (tstPrivKey) { + SECStatus rv; + PK11GenericObject *thisKey; + int i; + + fprintf(stderr, "Leaking keys...\n"); + for (i = 0, thisKey = tstPrivKey; thisKey; i++, + thisKey = PK11_GetNextGenericObject(thisKey)) { + SECItem id = { 0, NULL, 0 }; + + rv = PK11_ReadRawAttribute(PK11_TypeGeneric, thisKey, + CKA_ID, &id); + if (rv != SECSuccess) { + fprintf(stderr, "Key %d: couldn't read CKA_ID: %s\n", + i, PORT_ErrorToString(PORT_GetError())); + continue; + } + fprintf(stderr, "id = { "); + dumpItem(stderr, &id); + fprintf(stderr, "};"); + if (id.data[1] == LEAK_ID) { + fprintf(stderr, " ---> leak expected\n"); + if (id.data[0] == round) + leak_found = 1; + } else { + if (id.len != sizeof(cka_id)) { + fprintf(stderr, + " ---> ERROR unexpected leak in generated key\n"); + } else { + fprintf(stderr, + " ---> ERROR unexpected leak in constructed key\n"); + } + failed = 1; + } + SECITEM_FreeItem(&id, PR_FALSE); + } + PK11_DestroyGenericObjects(tstPrivKey); + } + if (expect_leak && !leak_found) { + fprintf(stderr, "ERROR expected leak not found\n"); + failed = 1; + } PK11_FreeSlot(slot); return failed ? -1 : 0; @@ -517,7 +618,7 @@ main(int argc, char **argv) exp_exp_prime_fail_count = 0; for (i = 0; i < repeat; i++) { printf("Running RSA Populate test run %d\n", i); - ret = doRSAPopulateTest(keySize, exponent, mask, NULL); + ret = doRSAPopulateTest(keySize, exponent, mask, i, NULL); if (ret != 0) { i++; break; @@ -531,5 +632,9 @@ main(int argc, char **argv) exp_exp_prime_fail_count, i, (((double)exp_exp_prime_fail_count) * 100.0) / (double)i); } + if (NSS_Shutdown() != SECSuccess) { + fprintf(stderr, "Shutdown failed\n"); + ret = -1; + } return ret; } diff --git a/cmd/rsapoptst/rsapoptst.gyp b/cmd/rsapoptst/rsapoptst.gyp new file mode 100644 index 000000000..325a10909 --- /dev/null +++ b/cmd/rsapoptst/rsapoptst.gyp @@ -0,0 +1,25 @@ +# 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/. +{ + 'includes': [ + '../../coreconf/config.gypi', + '../../cmd/platlibs.gypi' + ], + 'targets': [ + { + 'target_name': 'rsapoptst', + 'type': 'executable', + 'sources': [ + 'rsapoptst.c' + ], + 'dependencies': [ + '<(DEPTH)/exports.gyp:dbm_exports', + '<(DEPTH)/exports.gyp:nss_exports', + ] + } + ], + 'variables': { + 'module': 'nss', + } +} diff --git a/lib/nss/nss.def b/lib/nss/nss.def index c1d2b3104..4f0ade4d0 100644 --- a/lib/nss/nss.def +++ b/lib/nss/nss.def @@ -1125,6 +1125,7 @@ CERT_FindCertByNicknameOrEmailAddrForUsageCX; ;+}; ;+NSS_3.34 { # NSS 3.34 release ;+ global: +PK11_CreateManagedGenericObject; SGN_NewContextWithAlgorithmID; SEC_SignDataWithAlgorithmID; SEC_DerSignDataWithAlgorithmID; diff --git a/lib/pk11wrap/pk11merge.c b/lib/pk11wrap/pk11merge.c index 8c4c5129a..d14f44c78 100644 --- a/lib/pk11wrap/pk11merge.c +++ b/lib/pk11wrap/pk11merge.c @@ -1258,6 +1258,7 @@ pk11_newMergeLogNode(PLArenaPool *arena, /* initialize it */ obj->slot = slot; obj->objectID = id; + obj->owner = PR_FALSE; newLog->object = obj; newLog->error = error; diff --git a/lib/pk11wrap/pk11obj.c b/lib/pk11wrap/pk11obj.c index 9e9b611e5..b97caddd4 100644 --- a/lib/pk11wrap/pk11obj.c +++ b/lib/pk11wrap/pk11obj.c @@ -1505,6 +1505,7 @@ PK11_FindGenericObjects(PK11SlotInfo *slot, CK_OBJECT_CLASS objClass) /* initialize it */ obj->slot = PK11_ReferenceSlot(slot); obj->objectID = objectIDs[i]; + obj->owner = PR_FALSE; obj->next = NULL; obj->prev = NULL; @@ -1585,6 +1586,9 @@ PK11_DestroyGenericObject(PK11GenericObject *object) PK11_UnlinkGenericObject(object); if (object->slot) { + if (object->owner) { + PK11_DestroyObject(object->slot, object->objectID); + } PK11_FreeSlot(object->slot); } PORT_Free(object); @@ -1626,8 +1630,9 @@ PK11_DestroyGenericObjects(PK11GenericObject *objects) * Hand Create a new object and return the Generic object for our new object. */ PK11GenericObject * -PK11_CreateGenericObject(PK11SlotInfo *slot, const CK_ATTRIBUTE *pTemplate, - int count, PRBool token) +pk11_CreateGenericObjectHelper(PK11SlotInfo *slot, + const CK_ATTRIBUTE *pTemplate, + int count, PRBool token, PRBool owner) { CK_OBJECT_HANDLE objectID; PK11GenericObject *obj; @@ -1651,11 +1656,40 @@ PK11_CreateGenericObject(PK11SlotInfo *slot, const CK_ATTRIBUTE *pTemplate, /* initialize it */ obj->slot = PK11_ReferenceSlot(slot); obj->objectID = objectID; + obj->owner = owner; obj->next = NULL; obj->prev = NULL; return obj; } +/* This is the classic interface. Applications would call this function to + * create new object that would not be destroyed later. This lead to resource + * leaks (and thus memory leaks in the PKCS #11 module). To solve this we have + * a new interface that automatically marks objects created on the fly to be + * destroyed later. + * The old interface is preserved because applications like Mozilla purposefully + * leak the reference to be found later with PK11_FindGenericObjects. New + * applications should use the new interface PK11_CreateManagedGenericObject */ +PK11GenericObject * +PK11_CreateGenericObject(PK11SlotInfo *slot, const CK_ATTRIBUTE *pTemplate, + int count, PRBool token) +{ + return pk11_CreateGenericObjectHelper(slot, pTemplate, count, token, + PR_FALSE); +} + +/* Use this interface. It will automatically destroy any temporary objects + * (token = PR_FALSE) when the PK11GenericObject is freed. Permanent objects still + * need to be destroyed by hand with PK11_DestroyTokenObject. + */ +PK11GenericObject * +PK11_CreateManagedGenericObject(PK11SlotInfo *slot, + const CK_ATTRIBUTE *pTemplate, int count, PRBool token) +{ + return pk11_CreateGenericObjectHelper(slot, pTemplate, count, token, + !token); +} + /* * Change an attribute on a raw object */ diff --git a/lib/pk11wrap/pk11pub.h b/lib/pk11wrap/pk11pub.h index edfe82f5a..dbd8da092 100644 --- a/lib/pk11wrap/pk11pub.h +++ b/lib/pk11wrap/pk11pub.h @@ -831,6 +831,10 @@ SECStatus PK11_LinkGenericObject(PK11GenericObject *list, PK11GenericObject *object); SECStatus PK11_DestroyGenericObjects(PK11GenericObject *object); SECStatus PK11_DestroyGenericObject(PK11GenericObject *object); +PK11GenericObject *PK11_CreateManagedGenericObject(PK11SlotInfo *slot, + const CK_ATTRIBUTE *pTemplate, + int count, PRBool token); +/* deprecated */ PK11GenericObject *PK11_CreateGenericObject(PK11SlotInfo *slot, const CK_ATTRIBUTE *pTemplate, int count, PRBool token); diff --git a/lib/pk11wrap/secmodti.h b/lib/pk11wrap/secmodti.h index 63c207929..260e6387d 100644 --- a/lib/pk11wrap/secmodti.h +++ b/lib/pk11wrap/secmodti.h @@ -175,6 +175,7 @@ struct PK11GenericObjectStr { PK11GenericObject *next; PK11SlotInfo *slot; CK_OBJECT_HANDLE objectID; + PRBool owner; }; #define MAX_TEMPL_ATTRS 16 /* maximum attributes in template */ @@ -148,6 +148,7 @@ 'cmd/pk1sign/pk1sign.gyp:pk1sign', 'cmd/pp/pp.gyp:pp', 'cmd/rsaperf/rsaperf.gyp:rsaperf', + 'cmd/rsapoptst/rsapoptst.gyp:rsapoptst', 'cmd/sdrtest/sdrtest.gyp:sdrtest', 'cmd/selfserv/selfserv.gyp:selfserv', 'cmd/shlibsign/mangle/mangle.gyp:mangle', diff --git a/tests/cipher/cipher.sh b/tests/cipher/cipher.sh index 1d2561d9c..11a621815 100755 --- a/tests/cipher/cipher.sh +++ b/tests/cipher/cipher.sh @@ -107,6 +107,21 @@ cipher_gcm() done < ${GCM_TXT} } +###################### cipher_rsa_populate ############################ +# Test the ability to reconstruct rsa private key reconstruction +# also test the PK11GenericObject interface +################################################################### +cipher_rsa_populate() +{ + TESTNAME="RSA Reconstruct Private Keys Test" + echo "$SCRIPTNAME: $TESTNAME --------------------------------" + echo "rsapoptst -t all -r 10" +# skip e_d_q. It isn't reliable, and can return incorrect data. e_d_q should +# be turned off. + ${PROFTOOL} ${BINDIR}/rsapoptst -t e_n_p,d_n_q,d_p_q,e_d_n -r 10 + html_msg $? 0 "$TESTNAME" +} + ############################## cipher_cleanup ############################ # local shell function to finish this script (no exit since it might be # sourced) @@ -136,5 +151,6 @@ fi # Skip cipher_gcm if this is a softoken only build. if [ "${NSS_BUILD_SOFTOKEN_ONLY}" != "1" ]; then cipher_gcm + cipher_rsa_populate fi cipher_cleanup |