Blob Blame History Raw
# HG changeset patch
# User Robert Relyea <rrelyea@redhat.com>
# Date 1544226862 28800
#      Fri Dec 07 15:54:22 2018 -0800
# Node ID 521a5b2f10cc197b9349df033f9d3cca0b5226c5
# Parent  5ac4d4904afae59149bb1fab49c3b21244a51a22
try: -b do -u all - p all -t all

diff --git a/cmd/manifest.mn b/cmd/manifest.mn
--- a/cmd/manifest.mn
+++ b/cmd/manifest.mn
@@ -51,16 +51,17 @@ NSS_SRCDIRS = \
  ocspclnt  \
  ocspresp \
  oidcalc  \
  p7content  \
  p7env  \
  p7sign  \
  p7verify  \
  pk12util \
+ pk11import \
  pk11ectest \
  pk11gcmtest \
  pk11mode \
  pk1sign  \
  pp  \
  pwdecrypt \
  rsaperf \
  rsapoptst \
diff --git a/cmd/pk11import/Makefile b/cmd/pk11import/Makefile
new file mode 100644
--- /dev/null
+++ b/cmd/pk11import/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/pk11import/manifest.mn b/cmd/pk11import/manifest.mn
new file mode 100644
--- /dev/null
+++ b/cmd/pk11import/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 = pk11import.c	\
+	$(NULL)
+
+REQUIRES = seccmd
+
+PROGRAM = pk11import
+
diff --git a/cmd/pk11import/pk11import.c b/cmd/pk11import/pk11import.c
new file mode 100644
--- /dev/null
+++ b/cmd/pk11import/pk11import.c
@@ -0,0 +1,410 @@
+/* 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;
+    }
+    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 pub key, 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);
+    }
+    if (pubValue.data) {
+        SECITEM_FreeItem(&pubValue, PR_FALSE);
+    }
+    if (privID.data) {
+        SECITEM_FreeItem(&privID, PR_FALSE);
+    }
+    if (epki) {
+        PORT_FreeArena(epki->arena, PR_TRUE);
+    }
+    if (pubKey) {
+        SECKEY_DestroyPublicKey(pubKey);
+    }
+    if (privKey) {
+        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_NoDH,
+    opt_NoEC
+};
+
+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_FALSE, 0, PR_FALSE },
+      { /* opt_NoDSA            */ 'D', PR_FALSE, 0, PR_FALSE },
+      { /* opt_NoDH             */ 'h', PR_FALSE, 0, PR_FALSE },
+      { /* opt_NoEC             */ 'e', PR_FALSE, 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/pk11import/pk11import.gyp b/cmd/pk11import/pk11import.gyp
new file mode 100644
--- /dev/null
+++ b/cmd/pk11import/pk11import.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': 'pk11import',
+      'type': 'executable',
+      'sources': [
+        'pk11import.c'
+      ],
+      'dependencies': [
+        '<(DEPTH)/exports.gyp:dbm_exports',
+        '<(DEPTH)/exports.gyp:nss_exports'
+      ]
+    }
+  ],
+  'variables': {
+    'module': 'nss'
+  }
+}
diff --git a/lib/pk11wrap/pk11akey.c b/lib/pk11wrap/pk11akey.c
--- a/lib/pk11wrap/pk11akey.c
+++ b/lib/pk11wrap/pk11akey.c
@@ -1672,16 +1672,96 @@ PK11_MakeKEAPubKey(unsigned char *keyDat
     rv = SECITEM_CopyItem(arena, &pubk->u.fortezza.KEAKey, &pkData);
     if (rv != SECSuccess) {
         PORT_FreeArena(arena, PR_FALSE);
         return NULL;
     }
     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;
+
+    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
  * could later be looked up by its nickname, it would leak a SECKEYPrivateKey.
  * So isPerm must be true.
  */
 SECStatus
 PK11_ImportEncryptedPrivateKeyInfo(PK11SlotInfo *slot,
@@ -1797,22 +1877,16 @@ try_faulty_3des:
 
     PORT_Assert(usage != NULL);
     PORT_Assert(usageCount != 0);
     privKey = PK11_UnwrapPrivKey(slot, key, cryptoMechType,
                                  crypto_param, &epki->encryptedData,
                                  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;
     }
 
     /* if we are unable to import the key and the pbeMechType is
      * CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC, then it is possible that
      * the encrypted blob was created with a buggy key generation method
      * which is described in the PKCS 12 implementation notes.  So we
@@ -1832,16 +1906,35 @@ try_faulty_3des:
         faulty3DES = PR_TRUE;
         goto try_faulty_3des;
     }
 
     /* key import really did fail */
     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);
     }
 
     if (key != NULL) {
         PK11_FreeSymKey(key);
     }
 
diff --git a/lib/softoken/pkcs11.c b/lib/softoken/pkcs11.c
--- a/lib/softoken/pkcs11.c
+++ b/lib/softoken/pkcs11.c
@@ -1810,29 +1810,36 @@ sftk_GetPubKey(SFTKObject *object, CK_KE
                  * Some curves are always pressumed to be non-DER.
                  */
                 if (pubKey->u.ec.publicValue.len == keyLen &&
                     (pubKey->u.ec.ecParams.fieldID.type == ec_field_plain ||
                      pubKey->u.ec.publicValue.data[0] == EC_POINT_FORM_UNCOMPRESSED)) {
                     break; /* key was not DER encoded, no need to unwrap */
                 }
 
-                PORT_Assert(pubKey->u.ec.ecParams.name != ECCurve25519);
+                /* The PKCS #11 spec says that the Params should be DER encoded. Even though the params from the
+                 * Certificate aren't according the the ECCurve 25519 spec. We should accept this encoding.
+                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) {
                     SECItem publicValue;
                     SECStatus rv;
 
                     rv = SEC_QuickDERDecodeItem(arena, &publicValue,
                                                 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;
                     }
                     /* replace our previous with the decoded key */
                     pubKey->u.ec.publicValue = publicValue;
                     break;
                 }
                 crv = CKR_ATTRIBUTE_VALUE_INVALID;
diff --git a/nss.gyp b/nss.gyp
--- a/nss.gyp
+++ b/nss.gyp
@@ -159,16 +159,17 @@
             'cmd/oidcalc/oidcalc.gyp:oidcalc',
             'cmd/p7content/p7content.gyp:p7content',
             'cmd/p7env/p7env.gyp:p7env',
             'cmd/p7sign/p7sign.gyp:p7sign',
             'cmd/p7verify/p7verify.gyp:p7verify',
             'cmd/pk11ectest/pk11ectest.gyp:pk11ectest',
             'cmd/pk11gcmtest/pk11gcmtest.gyp:pk11gcmtest',
             'cmd/pk11mode/pk11mode.gyp:pk11mode',
+            'cmd/pk11import/pk11import.gyp:pk11import',
             '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',
             'cmd/strsclnt/strsclnt.gyp:strsclnt',
diff --git a/tests/dbtests/dbtests.sh b/tests/dbtests/dbtests.sh
--- a/tests/dbtests/dbtests.sh
+++ b/tests/dbtests/dbtests.sh
@@ -247,16 +247,35 @@ dbtest_main()
     # the old one should still be there...
     ${BINDIR}/certutil -L -n bob -d ${CONFLICT_DIR}
     ret=$?
     if [ $ret -ne 0 ]; then
       html_failed "Nicknane conflict test-setting nickname conflict incorrectly worked"
     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}/pk11import -d ${CONFLICT_DIR} -f ${R_PWFILE}
+    echo ${BINDIR}/pk11import -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
+    # import a token private key and make sure the corresponding public key is
+    # created
+    ${BINDIR}/pk11import -r -D -h -C Curve25519 -d ${CONFLICT_DIR} -f ${R_PWFILE}
+    echo ${BINDIR}/pk11import -r -D -h -C Curve25519 -d ${CONFLICT_DIR} -f ${R_PWFILE}
+    ret=$?
+    if [ $ret -ne 0 ]; then
+      html_failed "Importing ECC Curve 25519 Token Private Key does not create the corrresponding Public Key"
+    else
+      html_passed "Importing ECC Curve 25519 Token Private Key correctly creates the corrresponding Public Key"
+    fi
 }
 
 ################## main #################################################
 
 dbtest_init 
 dbtest_main 2>&1
 dbtest_cleanup