diff --git a/cmd/manifest.mn b/cmd/manifest.mn index be53d3c4eb..695177c9dc 100644 --- a/cmd/manifest.mn +++ b/cmd/manifest.mn @@ -56,6 +56,7 @@ NSS_SRCDIRS = \ p7sign \ p7verify \ pk12util \ + pk11importtest \ pk11ectest \ pk11gcmtest \ pk11mode \ diff --git a/cmd/pk11importtest/Makefile b/cmd/pk11importtest/Makefile new file mode 100644 index 0000000000..fc8358d5ad --- /dev/null +++ b/cmd/pk11importtest/Makefile @@ -0,0 +1,43 @@ +#! gmake +# +# 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/. + +####################################################################### +# (1) Include initial platform-independent assignments (MANDATORY). # +####################################################################### + +include manifest.mn + +####################################################################### +# (2) Include "global" configuration information. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/config.mk + +####################################################################### +# (3) Include "component" configuration information. (OPTIONAL) # +####################################################################### + +####################################################################### +# (4) Include "local" platform-dependent assignments (OPTIONAL). # +####################################################################### + +include ../platlibs.mk + +####################################################################### +# (5) Execute "global" rules. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/rules.mk + +####################################################################### +# (6) Execute "component" rules. (OPTIONAL) # +####################################################################### + +####################################################################### +# (7) Execute "local" rules. (OPTIONAL). # +####################################################################### + +include ../platrules.mk diff --git a/cmd/pk11importtest/manifest.mn b/cmd/pk11importtest/manifest.mn new file mode 100644 index 0000000000..5c40a8108a --- /dev/null +++ b/cmd/pk11importtest/manifest.mn @@ -0,0 +1,15 @@ +# 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/. + +CORE_DEPTH = ../.. + +MODULE = nss + +CSRCS = pk11importtest.c \ + $(NULL) + +REQUIRES = seccmd + +PROGRAM = pk11importtest + diff --git a/cmd/pk11importtest/pk11importtest.c b/cmd/pk11importtest/pk11importtest.c new file mode 100644 index 0000000000..b2f4890609 --- /dev/null +++ b/cmd/pk11importtest/pk11importtest.c @@ -0,0 +1,406 @@ +/* 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 "secutil.h" +#include "secmod.h" +#include "cert.h" +#include "secoid.h" +#include "nss.h" +#include "pk11pub.h" +#include "pk11pqg.h" + +/* NSPR 2.0 header files */ +#include "prinit.h" +#include "prprf.h" +#include "prsystem.h" +#include "prmem.h" +/* Portable layer header files */ +#include "plstr.h" + +SECOidData * +getCurveFromString(char *curve_name) +{ + SECOidTag tag = SEC_OID_SECG_EC_SECP256R1; + + if (PORT_Strcasecmp(curve_name, "NISTP256") == 0) { + } else if (PORT_Strcasecmp(curve_name, "NISTP384") == 0) { + tag = SEC_OID_SECG_EC_SECP384R1; + } else if (PORT_Strcasecmp(curve_name, "NISTP521") == 0) { + tag = SEC_OID_SECG_EC_SECP521R1; + } else if (PORT_Strcasecmp(curve_name, "Curve25519") == 0) { + tag = SEC_OID_CURVE25519; + } + return SECOID_FindOIDByTag(tag); +} + +void +dumpItem(const char *label, const SECItem *item) +{ + int i; + printf("%s = [%d bytes] {", label, item->len); + for (i = 0; i < item->len; i++) { + if ((i & 0xf) == 0) + printf("\n "); + else + printf(", "); + printf("%02x", item->data[i]); + } + printf("};\n"); +} + +SECStatus +handleEncryptedPrivateImportTest(char *progName, PK11SlotInfo *slot, + char *testname, CK_MECHANISM_TYPE genMech, void *params, void *pwArgs) +{ + SECStatus rv = SECSuccess; + SECItem privID = { 0 }; + SECItem pubID = { 0 }; + SECItem pubValue = { 0 }; + SECItem pbePwItem = { 0 }; + SECItem nickname = { 0 }; + SECItem token = { 0 }; + SECKEYPublicKey *pubKey = NULL; + SECKEYPrivateKey *privKey = NULL; + PK11GenericObject *objs = NULL; + PK11GenericObject *obj = NULL; + SECKEYEncryptedPrivateKeyInfo *epki = NULL; + PRBool keyFound = 0; + KeyType keyType; + + fprintf(stderr, "Testing %s PrivateKeyImport ***********************\n", + testname); + + /* generate a temp key */ + privKey = PK11_GenerateKeyPair(slot, genMech, params, &pubKey, + PR_FALSE, PR_TRUE, pwArgs); + if (privKey == NULL) { + SECU_PrintError(progName, "PK11_GenerateKeyPair Failed"); + goto cleanup; + } + + /* wrap the temp key */ + pbePwItem.data = (unsigned char *)"pw"; + pbePwItem.len = 2; + epki = PK11_ExportEncryptedPrivKeyInfo(slot, SEC_OID_AES_256_CBC, + &pbePwItem, privKey, 1, NULL); + if (epki == NULL) { + SECU_PrintError(progName, "PK11_ExportEncryptedPrivKeyInfo Failed"); + goto cleanup; + } + + /* Save the public value, which we will need on import */ + keyType = pubKey->keyType; + switch (keyType) { + case rsaKey: + SECITEM_CopyItem(NULL, &pubValue, &pubKey->u.rsa.modulus); + break; + case dhKey: + SECITEM_CopyItem(NULL, &pubValue, &pubKey->u.dh.publicValue); + break; + case dsaKey: + SECITEM_CopyItem(NULL, &pubValue, &pubKey->u.dsa.publicValue); + break; + case ecKey: + SECITEM_CopyItem(NULL, &pubValue, &pubKey->u.ec.publicValue); + break; + default: + fprintf(stderr, "Unknown keytype = %d\n", keyType); + goto cleanup; + } + if (pubValue.data == NULL) { + SECU_PrintError(progName, "Unable to allocate memory"); + goto cleanup; + } + dumpItem("pubValue", &pubValue); + + /* when Asymetric keys represent session keys, those session keys are + * destroyed when we destroy the Asymetric key representations */ + SECKEY_DestroyPublicKey(pubKey); + pubKey = NULL; + SECKEY_DestroyPrivateKey(privKey); + privKey = NULL; + + /* unwrap the temp key as a perm */ + nickname.data = (unsigned char *)"testKey"; + nickname.len = sizeof("testKey"); + rv = PK11_ImportEncryptedPrivateKeyInfoAndReturnKey( + slot, epki, &pbePwItem, &nickname, &pubValue, + PR_TRUE, PR_TRUE, keyType, 0, &privKey, NULL); + if (rv != SECSuccess) { + SECU_PrintError(progName, "PK11_ImportEncryptedPrivateKeyInfo Failed"); + goto cleanup; + } + + /* verify the public key exists */ + rv = PK11_ReadRawAttribute(PK11_TypePrivKey, privKey, CKA_ID, &privID); + if (rv != SECSuccess) { + SECU_PrintError(progName, + "Couldn't read CKA_ID from pub key, checking next key"); + goto cleanup; + } + dumpItem("privKey CKA_ID", &privID); + objs = PK11_FindGenericObjects(slot, CKO_PUBLIC_KEY); + for (obj = objs; obj; obj = PK11_GetNextGenericObject(obj)) { + rv = PK11_ReadRawAttribute(PK11_TypeGeneric, obj, CKA_ID, &pubID); + if (rv != SECSuccess) { + SECU_PrintError(progName, + "Couldn't read CKA_ID from object, checking next key"); + continue; + } + dumpItem("pubKey CKA_ID", &pubID); + if (!SECITEM_ItemsAreEqual(&privID, &pubID)) { + fprintf(stderr, + "CKA_ID does not match priv key, checking next key\n"); + SECITEM_FreeItem(&pubID, PR_FALSE); + continue; + } + SECITEM_FreeItem(&pubID, PR_FALSE); + rv = PK11_ReadRawAttribute(PK11_TypeGeneric, obj, CKA_TOKEN, &token); + if (rv == SECSuccess) { + if (token.len == 1) { + keyFound = token.data[0]; + } + SECITEM_FreeItem(&token, PR_FALSE); + } + if (keyFound) { + printf("matching public key found\n"); + break; + } + printf("Matching key was not a token key, checking next key\n"); + } + +cleanup: + if (objs) { + PK11_DestroyGenericObjects(objs); + } + SECITEM_FreeItem(&pubValue, PR_FALSE); + SECITEM_FreeItem(&privID, PR_FALSE); + PORT_FreeArena(epki->arena, PR_TRUE); + SECKEY_DestroyPublicKey(pubKey); + SECKEY_DestroyPrivateKey(privKey); + fprintf(stderr, "%s PrivateKeyImport %s ***********************\n", + testname, keyFound ? "PASSED" : "FAILED"); + return keyFound ? SECSuccess : SECFailure; +} + +static const char *const usageInfo[] = { + "pk11import - test PK11_PrivateKeyImport()" + "Options:", + " -d certdir directory containing cert database", + " -k keysize size of the rsa, dh, and dsa key to test (default 1024)", + " -C ecc_curve ecc curve (default )", + " -f pwFile file to fetch the password from", + " -p pwString password", + " -r skip rsa test", + " -D skip dsa test", + " -h skip dh test", + " -e skip ec test", +}; +static int nUsageInfo = sizeof(usageInfo) / sizeof(char *); + +static void +Usage(char *progName, FILE *outFile) +{ + int i; + fprintf(outFile, "Usage: %s [ commands ] options\n", progName); + for (i = 0; i < nUsageInfo; i++) + fprintf(outFile, "%s\n", usageInfo[i]); + exit(-1); +} + +enum { + opt_CertDir, + opt_KeySize, + opt_ECCurve, + opt_PWFile, + opt_PWString, + opt_NoRSA, + opt_NoDSA, + opt_NoEC, + opt_NoDH +}; + +static secuCommandFlag options[] = + { + { /* opt_CertDir */ 'd', PR_TRUE, 0, PR_FALSE }, + { /* opt_KeySize */ 'k', PR_TRUE, 0, PR_FALSE }, + { /* opt_ECCurve */ 'C', PR_TRUE, 0, PR_FALSE }, + { /* opt_PWFile */ 'f', PR_TRUE, 0, PR_FALSE }, + { /* opt_PWString */ 'p', PR_TRUE, 0, PR_FALSE }, + { /* opt_NORSA */ 'r', PR_TRUE, 0, PR_FALSE }, + { /* opt_NoDSA */ 'D', PR_TRUE, 0, PR_FALSE }, + { /* opt_NoDH */ 'h', PR_TRUE, 0, PR_FALSE }, + { /* opt_NoEC */ 'e', PR_TRUE, 0, PR_FALSE }, + }; + +int +main(int argc, char **argv) +{ + char *progName; + SECStatus rv; + secuCommand args; + PK11SlotInfo *slot = NULL; + PRBool failed = PR_FALSE; + secuPWData pwArgs = { PW_NONE, 0 }; + PRBool doRSA = PR_TRUE; + PRBool doDSA = PR_TRUE; + PRBool doDH = PR_FALSE; /* NSS currently can't export wrapped DH keys */ + PRBool doEC = PR_TRUE; + PQGParams *pqgParams = NULL; + int keySize; + + args.numCommands = 0; + args.numOptions = sizeof(options) / sizeof(secuCommandFlag); + args.commands = NULL; + args.options = options; + +#ifdef XP_PC + progName = strrchr(argv[0], '\\'); +#else + progName = strrchr(argv[0], '/'); +#endif + progName = progName ? progName + 1 : argv[0]; + + rv = SECU_ParseCommandLine(argc, argv, progName, &args); + if (SECSuccess != rv) { + Usage(progName, stderr); + } + + /* Set the certdb directory (default is ~/.netscape) */ + rv = NSS_InitReadWrite(SECU_ConfigDirectory(args.options[opt_CertDir].arg)); + if (rv != SECSuccess) { + SECU_PrintPRandOSError(progName); + return 255; + } + PK11_SetPasswordFunc(SECU_GetModulePassword); + + /* below here, goto cleanup */ + SECU_RegisterDynamicOids(); + + /* handle the arguments */ + if (args.options[opt_PWFile].arg) { + pwArgs.source = PW_FROMFILE; + pwArgs.data = args.options[opt_PWFile].arg; + } + if (args.options[opt_PWString].arg) { + pwArgs.source = PW_PLAINTEXT; + pwArgs.data = args.options[opt_PWString].arg; + } + if (args.options[opt_NoRSA].activated) { + doRSA = PR_FALSE; + } + if (args.options[opt_NoDSA].activated) { + doDSA = PR_FALSE; + } + if (args.options[opt_NoDH].activated) { + doDH = PR_FALSE; + } + if (args.options[opt_NoEC].activated) { + doEC = PR_FALSE; + } + + slot = PK11_GetInternalKeySlot(); + if (slot == NULL) { + SECU_PrintError(progName, "Couldn't find the internal key slot\n"); + return 255; + } + rv = PK11_Authenticate(slot, PR_TRUE, &pwArgs); + if (rv != SECSuccess) { + SECU_PrintError(progName, "Failed to log into slot"); + PK11_FreeSlot(slot); + return 255; + } + + keySize = 1024; + if (args.options[opt_KeySize].activated && + args.options[opt_KeySize].arg) { + keySize = atoi(args.options[opt_KeySize].arg); + } + + if (doDSA || doDH) { + PQGVerify *pqgVfy; + rv = PK11_PQG_ParamGenV2(keySize, 0, keySize / 16, &pqgParams, &pqgVfy); + if (rv == SECSuccess) { + PK11_PQG_DestroyVerify(pqgVfy); + } else { + SECU_PrintError(progName, + "PK11_PQG_ParamGenV2 failed, can't test DH or DSA"); + doDSA = doDH = PR_FALSE; + failed = PR_TRUE; + } + } + + if (doRSA) { + PK11RSAGenParams rsaParams; + rsaParams.keySizeInBits = keySize; + rsaParams.pe = 0x010001; + rv = handleEncryptedPrivateImportTest(progName, slot, "RSA", + CKM_RSA_PKCS_KEY_PAIR_GEN, &rsaParams, &pwArgs); + if (rv != SECSuccess) { + fprintf(stderr, "RSA Import Failed!\n"); + failed = PR_TRUE; + } + } + if (doDSA) { + rv = handleEncryptedPrivateImportTest(progName, slot, "DSA", + CKM_DSA_KEY_PAIR_GEN, pqgParams, &pwArgs); + if (rv != SECSuccess) { + fprintf(stderr, "DSA Import Failed!\n"); + failed = PR_TRUE; + } + } + if (doDH) { + SECKEYDHParams dhParams; + dhParams.prime = pqgParams->prime; + dhParams.base = pqgParams->base; + rv = handleEncryptedPrivateImportTest(progName, slot, "DH", + CKM_DH_PKCS_KEY_PAIR_GEN, &dhParams, &pwArgs); + if (rv != SECSuccess) { + fprintf(stderr, "DH Import Failed!\n"); + failed = PR_TRUE; + } + } + if (doEC) { + SECKEYECParams ecParams; + SECOidData *curve = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1); + if (args.options[opt_ECCurve].activated && + args.options[opt_ECCurve].arg) { + curve = getCurveFromString(args.options[opt_ECCurve].arg); + } + ecParams.data = PORT_Alloc(curve->oid.len + 2); + if (ecParams.data == NULL) { + rv = SECFailure; + goto ec_failed; + } + ecParams.data[0] = SEC_ASN1_OBJECT_ID; + ecParams.data[1] = (unsigned char)curve->oid.len; + PORT_Memcpy(&ecParams.data[2], curve->oid.data, curve->oid.len); + ecParams.len = curve->oid.len + 2; + rv = handleEncryptedPrivateImportTest(progName, slot, "ECC", + CKM_EC_KEY_PAIR_GEN, &ecParams, &pwArgs); + PORT_Free(ecParams.data); + ec_failed: + if (rv != SECSuccess) { + fprintf(stderr, "ECC Import Failed!\n"); + failed = PR_TRUE; + } + } + + if (pqgParams) { + PK11_PQG_DestroyParams(pqgParams); + } + + if (slot) { + PK11_FreeSlot(slot); + } + + rv = NSS_Shutdown(); + if (rv != SECSuccess) { + fprintf(stderr, "Shutdown failed\n"); + SECU_PrintPRandOSError(progName); + return 255; + } + + return failed ? 1 : 0; +} diff --git a/cmd/pk11importtest/pk11importtest.gyp b/cmd/pk11importtest/pk11importtest.gyp new file mode 100644 index 0000000000..4fce059621 --- /dev/null +++ b/cmd/pk11importtest/pk11importtest.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': 'pk11importtest', + 'type': 'executable', + 'sources': [ + 'pk11importtest.c' + ], + 'dependencies': [ + '<(DEPTH)/exports.gyp:dbm_exports', + '<(DEPTH)/exports.gyp:nss_exports' + ] + } + ], + 'variables': { + 'module': 'nss' + } +} diff --git a/cpputil/nss_scoped_ptrs.h b/cpputil/nss_scoped_ptrs.h index 03979f2c58..450e787af5 100644 --- a/cpputil/nss_scoped_ptrs.h +++ b/cpputil/nss_scoped_ptrs.h @@ -11,6 +11,7 @@ #include "cert.h" #include "keyhi.h" #include "p12.h" +#include "pk11pqg.h" #include "pk11pub.h" #include "pkcs11uri.h" @@ -41,6 +42,7 @@ struct ScopedDelete { void operator()(PLArenaPool* arena) { PORT_FreeArena(arena, PR_FALSE); } void operator()(PK11Context* context) { PK11_DestroyContext(context, true); } void operator()(PK11GenericObject* obj) { PK11_DestroyGenericObject(obj); } + void operator()(PQGParams* pqg) { PK11_PQG_DestroyParams(pqg); } void operator()(SEC_PKCS12DecoderContext* dcx) { SEC_PKCS12DecoderFinish(dcx); } @@ -66,6 +68,7 @@ SCOPED(CERTName); SCOPED(CERTSubjectPublicKeyInfo); SCOPED(PK11SlotInfo); SCOPED(PK11SymKey); +SCOPED(PQGParams); SCOPED(PRFileDesc); SCOPED(SECAlgorithmID); SCOPED(SECKEYEncryptedPrivateKeyInfo); @@ -82,4 +85,9 @@ SCOPED(CERTDistNames); #undef SCOPED +struct StackSECItem : public SECItem { + StackSECItem() : SECItem({siBuffer, nullptr, 0}) {} + ~StackSECItem() { SECITEM_FreeItem(this, PR_FALSE); } +}; + #endif // nss_scoped_ptrs_h__ diff --git a/cpputil/scoped_ptrs_util.h b/cpputil/scoped_ptrs_util.h index 2dbf34e1d3..dc6e1d7525 100644 --- a/cpputil/scoped_ptrs_util.h +++ b/cpputil/scoped_ptrs_util.h @@ -33,6 +33,7 @@ struct ScopedMaybeDelete { SCOPED(SECAlgorithmID); SCOPED(SECItem); SCOPED(PK11URI); +SCOPED(PLArenaPool); #undef SCOPED diff --git a/gtests/common/gtests.cc b/gtests/common/gtests.cc index bd5a97a8ed..7e585791b8 100644 --- a/gtests/common/gtests.cc +++ b/gtests/common/gtests.cc @@ -10,7 +10,23 @@ int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); - if (NSS_NoDB_Init(nullptr) != SECSuccess) { + const char *workdir = ""; + uint32_t flags = NSS_INIT_READONLY; + + for (int i = 0; i < argc; i++) { + if (!strcmp(argv[i], "-d")) { + if (i + 1 >= argc) { + PR_fprintf(PR_STDERR, "Usage: %s [-d [-w]]\n", argv[0]); + exit(2); + } + workdir = argv[i + 1]; + i++; + } else if (!strcmp(argv[i], "-w")) { + flags &= ~NSS_INIT_READONLY; + } + } + + if (NSS_Initialize(workdir, "", "", SECMOD_DB, flags) != SECSuccess) { return 1; } if (NSS_SetDomesticPolicy() != SECSuccess) { diff --git a/gtests/pk11_gtest/manifest.mn b/gtests/pk11_gtest/manifest.mn index ea7b43a2b0..75ddb71c31 100644 --- a/gtests/pk11_gtest/manifest.mn +++ b/gtests/pk11_gtest/manifest.mn @@ -13,6 +13,7 @@ CPPSRCS = \ pk11_ecdsa_unittest.cc \ pk11_encrypt_derive_unittest.cc \ pk11_export_unittest.cc \ + pk11_import_unittest.cc \ pk11_pbkdf2_unittest.cc \ pk11_prf_unittest.cc \ pk11_prng_unittest.cc \ @@ -33,4 +34,3 @@ EXTRA_LIBS = $(DIST)/lib/$(LIB_PREFIX)gtest.$(LIB_SUFFIX) \ $(DIST)/lib/$(LIB_PREFIX)cpputil.$(LIB_SUFFIX) \ $(DIST)/lib/$(LIB_PREFIX)gtestutil.$(LIB_SUFFIX) \ $(NULL) - diff --git a/gtests/pk11_gtest/pk11_gtest.gyp b/gtests/pk11_gtest/pk11_gtest.gyp index c73139b05a..c8d04f017a 100644 --- a/gtests/pk11_gtest/pk11_gtest.gyp +++ b/gtests/pk11_gtest/pk11_gtest.gyp @@ -18,6 +18,7 @@ 'pk11_curve25519_unittest.cc', 'pk11_ecdsa_unittest.cc', 'pk11_encrypt_derive_unittest.cc', + 'pk11_import_unittest.cc', 'pk11_pbkdf2_unittest.cc', 'pk11_prf_unittest.cc', 'pk11_prng_unittest.cc', diff --git a/lib/pk11wrap/pk11akey.c b/lib/pk11wrap/pk11akey.c index c6070e264d..97adabb504 100644 --- a/lib/pk11wrap/pk11akey.c +++ b/lib/pk11wrap/pk11akey.c @@ -1677,6 +1677,92 @@ PK11_MakeKEAPubKey(unsigned char *keyData, int length) return pubk; } +SECStatus +SECKEY_SetPublicValue(SECKEYPrivateKey *privKey, SECItem *publicValue) +{ + SECStatus rv; + SECKEYPublicKey pubKey; + PLArenaPool *arena; + PK11SlotInfo *slot = privKey->pkcs11Slot; + CK_OBJECT_HANDLE privKeyID = privKey->pkcs11ID; + + if (privKey == NULL || publicValue == NULL || + publicValue->data == NULL || publicValue->len == 0) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + pubKey.arena = NULL; + pubKey.keyType = privKey->keyType; + pubKey.pkcs11Slot = NULL; + pubKey.pkcs11ID = CK_INVALID_HANDLE; + /* can't use PORT_InitCheapArena here becase SECKEY_DestroyPublic is used + * to free it, and it uses PORT_FreeArena which not only frees the + * underlying arena, it also frees the allocated arena struct. */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + pubKey.arena = arena; + if (arena == NULL) { + return SECFailure; + } + rv = SECFailure; + switch (privKey->keyType) { + default: + /* error code already set to SECFailure */ + break; + case rsaKey: + pubKey.u.rsa.modulus = *publicValue; + rv = PK11_ReadAttribute(slot, privKeyID, CKA_PUBLIC_EXPONENT, + arena, &pubKey.u.rsa.publicExponent); + break; + case dsaKey: + pubKey.u.dsa.publicValue = *publicValue; + rv = PK11_ReadAttribute(slot, privKeyID, CKA_PRIME, + arena, &pubKey.u.dsa.params.prime); + if (rv != SECSuccess) { + break; + } + rv = PK11_ReadAttribute(slot, privKeyID, CKA_SUBPRIME, + arena, &pubKey.u.dsa.params.subPrime); + if (rv != SECSuccess) { + break; + } + rv = PK11_ReadAttribute(slot, privKeyID, CKA_BASE, + arena, &pubKey.u.dsa.params.base); + break; + case dhKey: + pubKey.u.dh.publicValue = *publicValue; + rv = PK11_ReadAttribute(slot, privKeyID, CKA_PRIME, + arena, &pubKey.u.dh.prime); + if (rv != SECSuccess) { + break; + } + rv = PK11_ReadAttribute(slot, privKeyID, CKA_BASE, + arena, &pubKey.u.dh.base); + break; + case ecKey: + pubKey.u.ec.publicValue = *publicValue; + pubKey.u.ec.encoding = ECPoint_Undefined; + pubKey.u.ec.size = 0; + rv = PK11_ReadAttribute(slot, privKeyID, CKA_EC_PARAMS, + arena, &pubKey.u.ec.DEREncodedParams); + break; + } + if (rv == SECSuccess) { + rv = PK11_ImportPublicKey(slot, &pubKey, PR_TRUE); + } + /* Even though pubKey is stored on the stack, we've allocated + * some of it's data from the arena. SECKEY_DestroyPublicKey + * destroys keys by freeing the arena, so this will clean up all + * the data we allocated specifically for the key above. It will + * also free any slot references which we may have picked up in + * PK11_ImportPublicKey. It won't delete the underlying key if + * its a Token/Permanent key (which it will be if + * PK11_ImportPublicKey succeeds). */ + SECKEY_DestroyPublicKey(&pubKey); + + return rv; +} + /* * NOTE: This function doesn't return a SECKEYPrivateKey struct to represent * the new private key object. If it were to create a session object that @@ -1802,12 +1888,6 @@ PK11_ImportEncryptedPrivateKeyInfoAndReturnKey(PK11SlotInfo *slot, nickname, publicValue, isPerm, isPrivate, key_type, usage, usageCount, wincx); if (privKey) { - if (privk) { - *privk = privKey; - } else { - SECKEY_DestroyPrivateKey(privKey); - } - privKey = NULL; rv = SECSuccess; goto done; } @@ -1837,6 +1917,25 @@ PK11_ImportEncryptedPrivateKeyInfoAndReturnKey(PK11SlotInfo *slot, rv = SECFailure; done: + if ((rv == SECSuccess) && isPerm) { + /* If we are importing a token object, + * create the corresponding public key. + * If this fails, just continue as the target + * token simply might not support persistant + * public keys. Such tokens are usable, but + * need to be authenticated before searching + * for user certs. */ + (void)SECKEY_SetPublicValue(privKey, publicValue); + } + + if (privKey) { + if (privk) { + *privk = privKey; + } else { + SECKEY_DestroyPrivateKey(privKey); + } + privKey = NULL; + } if (crypto_param != NULL) { SECITEM_ZfreeItem(crypto_param, PR_TRUE); } diff --git a/lib/softoken/pkcs11.c b/lib/softoken/pkcs11.c index 34f25a9d06..bb86baa69a 100644 --- a/lib/softoken/pkcs11.c +++ b/lib/softoken/pkcs11.c @@ -1815,8 +1815,6 @@ sftk_GetPubKey(SFTKObject *object, CK_KEY_TYPE key_type, break; /* key was not DER encoded, no need to unwrap */ } - PORT_Assert(pubKey->u.ec.ecParams.name != ECCurve25519); - /* handle the encoded case */ if ((pubKey->u.ec.publicValue.data[0] == SEC_ASN1_OCTET_STRING) && pubKey->u.ec.publicValue.len > keyLen) { @@ -1827,7 +1825,13 @@ sftk_GetPubKey(SFTKObject *object, CK_KEY_TYPE key_type, SEC_ASN1_GET(SEC_OctetStringTemplate), &pubKey->u.ec.publicValue); /* nope, didn't decode correctly */ - if ((rv != SECSuccess) || (publicValue.data[0] != EC_POINT_FORM_UNCOMPRESSED) || (publicValue.len != keyLen)) { + if ((rv != SECSuccess) || (publicValue.len != keyLen)) { + crv = CKR_ATTRIBUTE_VALUE_INVALID; + break; + } + /* we don't handle compressed points except in the case of ECCurve25519 */ + if ((pubKey->u.ec.ecParams.fieldID.type != ec_field_plain) && + (publicValue.data[0] != EC_POINT_FORM_UNCOMPRESSED)) { crv = CKR_ATTRIBUTE_VALUE_INVALID; break; } diff --git a/nss.gyp b/nss.gyp index 3cc3d7b6d7..c408058b50 100644 --- a/nss.gyp +++ b/nss.gyp @@ -168,6 +168,7 @@ 'cmd/pk11ectest/pk11ectest.gyp:pk11ectest', 'cmd/pk11gcmtest/pk11gcmtest.gyp:pk11gcmtest', 'cmd/pk11mode/pk11mode.gyp:pk11mode', + 'cmd/pk11importtest/pk11importtest.gyp:pk11importtest', 'cmd/pk1sign/pk1sign.gyp:pk1sign', 'cmd/pp/pp.gyp:pp', 'cmd/rsaperf/rsaperf.gyp:rsaperf', diff --git a/tests/dbtests/dbtests.sh b/tests/dbtests/dbtests.sh index 7b1ee351fd..9dcf738c4c 100755 --- a/tests/dbtests/dbtests.sh +++ b/tests/dbtests/dbtests.sh @@ -252,7 +252,15 @@ dbtest_main() else html_passed "Nicknane conflict test-setting nickname conflict was correctly rejected" fi - + # import a token private key and make sure the corresponding public key is + # created + ${BINDIR}/pk11importtest -d ${CONFLICT_DIR} -f ${R_PWFILE} + ret=$? + if [ $ret -ne 0 ]; then + html_failed "Importing Token Private Key does not create the corrresponding Public Key" + else + html_passed "Importing Token Private Key correctly creates the corrresponding Public Key" + fi } ################## main ################################################# diff --git a/tests/gtests/gtests.sh b/tests/gtests/gtests.sh index 583ecec62c..1731de0eb4 100755 --- a/tests/gtests/gtests.sh +++ b/tests/gtests/gtests.sh @@ -23,6 +23,7 @@ gtest_init() { cd "$(dirname "$1")" + SOURCE_DIR="$PWD"/../.. if [ -z "${INIT_SOURCED}" -o "${INIT_SOURCED}" != "TRUE" ]; then cd ../common . ./init.sh @@ -33,6 +34,7 @@ gtest_init() if [ -z "${CLEANUP}" ] ; then # if nobody else is responsible for CLEANUP="${SCRIPTNAME}" # cleaning this script will do it fi + } ########################## gtest_start ############################# @@ -42,7 +44,7 @@ gtest_start() { echo "gtests: ${GTESTS}" for i in ${GTESTS}; do - if [ ! -f ${BINDIR}/$i ]; then + if [ ! -f "${BINDIR}/$i" ]; then html_unknown "Skipping $i (not built)" continue fi @@ -50,20 +52,22 @@ gtest_start() html_head "$i" if [ ! -d "$GTESTDIR" ]; then mkdir -p "$GTESTDIR" + echo "${BINDIR}/certutil" -N -d "$GTESTDIR" --empty-password 2>&1 + "${BINDIR}/certutil" -N -d "$GTESTDIR" --empty-password 2>&1 fi cd "$GTESTDIR" GTESTREPORT="$GTESTDIR/report.xml" PARSED_REPORT="$GTESTDIR/report.parsed" echo "executing $i" - ${BINDIR}/$i "${SOURCE_DIR}/gtests/freebl_gtest/kat/Hash_DRBG.rsp" \ - -d "$GTESTDIR" --gtest_output=xml:"${GTESTREPORT}" \ - --gtest_filter="${GTESTFILTER-*}" + "${BINDIR}/$i" "${SOURCE_DIR}/gtests/freebl_gtest/kat/Hash_DRBG.rsp" \ + -d "$GTESTDIR" -w --gtest_output=xml:"${GTESTREPORT}" \ + --gtest_filter="${GTESTFILTER:-*}" html_msg $? 0 "$i run successfully" echo "test output dir: ${GTESTREPORT}" echo "executing sed to parse the xml report" - sed -f ${COMMON}/parsegtestreport.sed "${GTESTREPORT}" > "${PARSED_REPORT}" + sed -f "${COMMON}/parsegtestreport.sed" "$GTESTREPORT" > "$PARSED_REPORT" echo "processing the parsed report" - cat "${PARSED_REPORT}" | while read result name; do + cat "$PARSED_REPORT" | while read result name; do if [ "$result" = "notrun" ]; then echo "$name" SKIPPED elif [ "$result" = "run" ]; then @@ -78,13 +82,12 @@ gtest_start() gtest_cleanup() { html "
" - cd ${QADIR} + cd "${QADIR}" . common/cleanup.sh } ################## main ################################################# -GTESTS="prng_gtest certhigh_gtest certdb_gtest der_gtest pk11_gtest util_gtest freebl_gtest softoken_gtest sysinit_gtest blake2b_gtest" -SOURCE_DIR="$PWD"/../.. -gtest_init $0 +GTESTS="${GTESTS:-prng_gtest certhigh_gtest certdb_gtest der_gtest pk11_gtest util_gtest freebl_gtest softoken_gtest sysinit_gtest blake2b_gtest}" +gtest_init "$0" gtest_start gtest_cleanup