Blame SOURCES/nss-fix-public-key-from-priv.patch

e4d72e
diff -up ./nss/gtests/pk11_gtest/pk11_import_unittest.cc.pub-priv-mechs ./nss/gtests/pk11_gtest/pk11_import_unittest.cc
e4d72e
--- ./nss/gtests/pk11_gtest/pk11_import_unittest.cc.pub-priv-mechs	2019-05-10 14:14:18.000000000 -0700
e4d72e
+++ ./nss/gtests/pk11_gtest/pk11_import_unittest.cc	2019-06-05 12:01:13.728544204 -0700
e4d72e
@@ -78,17 +78,40 @@ class Pk11KeyImportTestBase : public ::t
e4d72e
   CK_MECHANISM_TYPE mech_;
e4d72e
 
e4d72e
  private:
e4d72e
+  SECItem GetPublicComponent(ScopedSECKEYPublicKey& pub_key) {
e4d72e
+    SECItem null = { siBuffer, NULL, 0};
e4d72e
+    switch(SECKEY_GetPublicKeyType(pub_key.get())) {
e4d72e
+    case rsaKey:
e4d72e
+    case rsaPssKey:
e4d72e
+    case rsaOaepKey:
e4d72e
+       return pub_key->u.rsa.modulus;
e4d72e
+    case keaKey:
e4d72e
+       return pub_key->u.kea.publicValue;
e4d72e
+    case dsaKey:
e4d72e
+       return pub_key->u.dsa.publicValue;
e4d72e
+    case dhKey:
e4d72e
+       return pub_key->u.dh.publicValue;
e4d72e
+    case ecKey:
e4d72e
+       return pub_key->u.ec.publicValue;
e4d72e
+    case fortezzaKey: /* depricated */
e4d72e
+    case nullKey:
e4d72e
+    /* didn't use default here so we can catch new key types at compile time */
e4d72e
+       break;
e4d72e
+    }
e4d72e
+    return null;
e4d72e
+  }
e4d72e
   void CheckForPublicKey(const ScopedSECKEYPrivateKey& priv_key,
e4d72e
                          const SECItem* expected_public) {
e4d72e
     // Verify the public key exists.
e4d72e
     StackSECItem priv_id;
e4d72e
+    KeyType type = SECKEY_GetPrivateKeyType(priv_key.get());
e4d72e
     SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, priv_key.get(),
e4d72e
                                          CKA_ID, &priv_id);
e4d72e
     ASSERT_EQ(SECSuccess, rv) << "Couldn't read CKA_ID from private key: "
e4d72e
                               << PORT_ErrorToName(PORT_GetError());
e4d72e
 
e4d72e
     CK_ATTRIBUTE_TYPE value_type = CKA_VALUE;
e4d72e
-    switch (SECKEY_GetPrivateKeyType(priv_key.get())) {
e4d72e
+    switch (type) {
e4d72e
       case rsaKey:
e4d72e
         value_type = CKA_MODULUS;
e4d72e
         break;
e4d72e
@@ -106,6 +129,8 @@ class Pk11KeyImportTestBase : public ::t
e4d72e
         FAIL() << "unknown key type";
e4d72e
     }
e4d72e
 
e4d72e
+    // Scan public key objects until we find one with the same CKA_ID as
e4d72e
+    // priv_key
e4d72e
     std::unique_ptr<PK11GenericObject, PK11GenericObjectsDeleter> objs(
e4d72e
         PK11_FindGenericObjects(slot_.get(), CKO_PUBLIC_KEY));
e4d72e
     ASSERT_NE(nullptr, objs);
e4d72e
@@ -128,20 +153,46 @@ class Pk11KeyImportTestBase : public ::t
e4d72e
       ASSERT_EQ(1U, token.len);
e4d72e
       ASSERT_NE(0, token.data[0]);
e4d72e
 
e4d72e
-      StackSECItem value;
e4d72e
-      rv = PK11_ReadRawAttribute(PK11_TypeGeneric, obj, value_type, &value);
e4d72e
+      StackSECItem raw_value;
e4d72e
+      SECItem decoded_value;
e4d72e
+      rv = PK11_ReadRawAttribute(PK11_TypeGeneric, obj, value_type, &raw_value);
e4d72e
       ASSERT_EQ(SECSuccess, rv);
e4d72e
+      SECItem value = raw_value;
e4d72e
 
e4d72e
+      // Decode the EC_POINT and check the output against expected.
e4d72e
       // CKA_EC_POINT isn't stable, see Bug 1520649.
e4d72e
+      ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
e4d72e
+      ASSERT_TRUE(arena);
e4d72e
       if (value_type == CKA_EC_POINT) {
e4d72e
-        continue;
e4d72e
-      }
e4d72e
 
e4d72e
+        // If this fails due to the noted inconsistency, we may need to
e4d72e
+        // check the whole raw_value, or remove a leading UNCOMPRESSED_POINT tag
e4d72e
+        rv = SEC_QuickDERDecodeItem(arena.get(), &decoded_value,
e4d72e
+                                    SEC_ASN1_GET(SEC_OctetStringTemplate),
e4d72e
+                                    &raw_value);
e4d72e
+        ASSERT_EQ(SECSuccess, rv);
e4d72e
+        value = decoded_value;
e4d72e
+      }
e4d72e
       ASSERT_TRUE(SECITEM_ItemsAreEqual(expected_public, &value))
e4d72e
           << "expected: "
e4d72e
           << DataBuffer(expected_public->data, expected_public->len)
e4d72e
           << std::endl
e4d72e
           << "actual: " << DataBuffer(value.data, value.len) << std::endl;
e4d72e
+
e4d72e
+      // Finally, convert the private to public and ensure it matches.
e4d72e
+      ScopedSECKEYPublicKey pub_key(
e4d72e
+            SECKEY_ConvertToPublicKey(priv_key.get()));
e4d72e
+      ASSERT_TRUE(pub_key);
e4d72e
+      SECItem converted_public = GetPublicComponent(pub_key);
e4d72e
+      ASSERT_TRUE(converted_public.len != 0);
e4d72e
+
e4d72e
+      ASSERT_TRUE(SECITEM_ItemsAreEqual(expected_public, &converted_public))
e4d72e
+            << "expected: "
e4d72e
+            << DataBuffer(expected_public->data, expected_public->len)
e4d72e
+            << std::endl
e4d72e
+            << "actual: "
e4d72e
+            << DataBuffer(converted_public.data, converted_public.len)
e4d72e
+            << std::endl;
e4d72e
     }
e4d72e
   }
e4d72e
 
e4d72e
diff -up ./nss/lib/cryptohi/seckey.c.pub-priv-mechs ./nss/lib/cryptohi/seckey.c
e4d72e
--- ./nss/lib/cryptohi/seckey.c.pub-priv-mechs	2019-05-10 14:14:18.000000000 -0700
e4d72e
+++ ./nss/lib/cryptohi/seckey.c	2019-06-05 12:01:13.729544204 -0700
e4d72e
@@ -1206,6 +1206,37 @@ SECKEY_CopyPublicKey(const SECKEYPublicK
e4d72e
     return NULL;
e4d72e
 }
e4d72e
 
e4d72e
+/*
e4d72e
+ * Use the private key to find a public key handle. The handle will be on
e4d72e
+ * the same slot as the private key.
e4d72e
+ */
e4d72e
+static CK_OBJECT_HANDLE
e4d72e
+seckey_FindPublicKeyHandle(SECKEYPrivateKey *privk, SECKEYPublicKey *pubk)
e4d72e
+{
e4d72e
+    CK_OBJECT_HANDLE keyID;
e4d72e
+
e4d72e
+    /* this helper function is only used below. If we want to make this more
e4d72e
+     * general, we would need to free up any already cached handles if the
e4d72e
+     * slot doesn't match up with the private key slot */
e4d72e
+    PORT_Assert(pubk->pkcs11ID == CK_INVALID_HANDLE);
e4d72e
+
e4d72e
+    /* first look for a matching public key */
e4d72e
+    keyID = PK11_MatchItem(privk->pkcs11Slot, privk->pkcs11ID, CKO_PUBLIC_KEY);
e4d72e
+    if (keyID != CK_INVALID_HANDLE) {
e4d72e
+        return keyID;
e4d72e
+    }
e4d72e
+
e4d72e
+    /* none found, create a temp one, make the pubk the owner */
e4d72e
+    pubk->pkcs11ID = PK11_DerivePubKeyFromPrivKey(privk);
e4d72e
+    if (pubk->pkcs11ID == CK_INVALID_HANDLE) {
e4d72e
+        /* end of the road. Token doesn't have matching public key, nor can
e4d72e
+          * token regenerate a new public key from and existing private key. */
e4d72e
+        return CK_INVALID_HANDLE;
e4d72e
+    }
e4d72e
+    pubk->pkcs11Slot = PK11_ReferenceSlot(privk->pkcs11Slot);
e4d72e
+    return pubk->pkcs11ID;
e4d72e
+}
e4d72e
+
e4d72e
 SECKEYPublicKey *
e4d72e
 SECKEY_ConvertToPublicKey(SECKEYPrivateKey *privk)
e4d72e
 {
e4d72e
@@ -1213,6 +1244,8 @@ SECKEY_ConvertToPublicKey(SECKEYPrivateK
e4d72e
     PLArenaPool *arena;
e4d72e
     CERTCertificate *cert;
e4d72e
     SECStatus rv;
e4d72e
+    CK_OBJECT_HANDLE pubKeyHandle;
e4d72e
+    SECItem decodedPoint;
e4d72e
 
e4d72e
     /*
e4d72e
      * First try to look up the cert.
e4d72e
@@ -1243,11 +1276,47 @@ SECKEY_ConvertToPublicKey(SECKEYPrivateK
e4d72e
 
e4d72e
     switch (privk->keyType) {
e4d72e
         case nullKey:
e4d72e
-        case dhKey:
e4d72e
-        case dsaKey:
e4d72e
             /* Nothing to query, if the cert isn't there, we're done -- no way
e4d72e
              * to get the public key */
e4d72e
             break;
e4d72e
+        case dsaKey:
e4d72e
+            pubKeyHandle = seckey_FindPublicKeyHandle(privk, pubk);
e4d72e
+            if (pubKeyHandle == CK_INVALID_HANDLE)
e4d72e
+                break;
e4d72e
+            rv = PK11_ReadAttribute(privk->pkcs11Slot, pubKeyHandle,
e4d72e
+                                    CKA_BASE, arena, &pubk->u.dsa.params.base);
e4d72e
+            if (rv != SECSuccess)
e4d72e
+                break;
e4d72e
+            rv = PK11_ReadAttribute(privk->pkcs11Slot, pubKeyHandle,
e4d72e
+                                    CKA_PRIME, arena, &pubk->u.dsa.params.prime);
e4d72e
+            if (rv != SECSuccess)
e4d72e
+                break;
e4d72e
+            rv = PK11_ReadAttribute(privk->pkcs11Slot, pubKeyHandle,
e4d72e
+                                    CKA_SUBPRIME, arena, &pubk->u.dsa.params.subPrime);
e4d72e
+            if (rv != SECSuccess)
e4d72e
+                break;
e4d72e
+            rv = PK11_ReadAttribute(privk->pkcs11Slot, pubKeyHandle,
e4d72e
+                                    CKA_VALUE, arena, &pubk->u.dsa.publicValue);
e4d72e
+            if (rv != SECSuccess)
e4d72e
+                break;
e4d72e
+            return pubk;
e4d72e
+        case dhKey:
e4d72e
+            pubKeyHandle = seckey_FindPublicKeyHandle(privk, pubk);
e4d72e
+            if (pubKeyHandle == CK_INVALID_HANDLE)
e4d72e
+                break;
e4d72e
+            rv = PK11_ReadAttribute(privk->pkcs11Slot, pubKeyHandle,
e4d72e
+                                    CKA_BASE, arena, &pubk->u.dh.base);
e4d72e
+            if (rv != SECSuccess)
e4d72e
+                break;
e4d72e
+            rv = PK11_ReadAttribute(privk->pkcs11Slot, pubKeyHandle,
e4d72e
+                                    CKA_PRIME, arena, &pubk->u.dh.prime);
e4d72e
+            if (rv != SECSuccess)
e4d72e
+                break;
e4d72e
+            rv = PK11_ReadAttribute(privk->pkcs11Slot, pubKeyHandle,
e4d72e
+                                    CKA_VALUE, arena, &pubk->u.dh.publicValue);
e4d72e
+            if (rv != SECSuccess)
e4d72e
+                break;
e4d72e
+            return pubk;
e4d72e
         case rsaKey:
e4d72e
             rv = PK11_ReadAttribute(privk->pkcs11Slot, privk->pkcs11ID,
e4d72e
                                     CKA_MODULUS, arena, &pubk->u.rsa.modulus);
e4d72e
@@ -1258,7 +1327,6 @@ SECKEY_ConvertToPublicKey(SECKEYPrivateK
e4d72e
             if (rv != SECSuccess)
e4d72e
                 break;
e4d72e
             return pubk;
e4d72e
-            break;
e4d72e
         case ecKey:
e4d72e
             rv = PK11_ReadAttribute(privk->pkcs11Slot, privk->pkcs11ID,
e4d72e
                                     CKA_EC_PARAMS, arena, &pubk->u.ec.DEREncodedParams);
e4d72e
@@ -1268,7 +1336,23 @@ SECKEY_ConvertToPublicKey(SECKEYPrivateK
e4d72e
             rv = PK11_ReadAttribute(privk->pkcs11Slot, privk->pkcs11ID,
e4d72e
                                     CKA_EC_POINT, arena, &pubk->u.ec.publicValue);
e4d72e
             if (rv != SECSuccess || pubk->u.ec.publicValue.len == 0) {
e4d72e
-                break;
e4d72e
+                pubKeyHandle = seckey_FindPublicKeyHandle(privk, pubk);
e4d72e
+                if (pubKeyHandle == CK_INVALID_HANDLE)
e4d72e
+                    break;
e4d72e
+                rv = PK11_ReadAttribute(privk->pkcs11Slot, pubKeyHandle,
e4d72e
+                                        CKA_EC_POINT, arena, &pubk->u.ec.publicValue);
e4d72e
+                if (rv != SECSuccess)
e4d72e
+                    break;
e4d72e
+            }
e4d72e
+            /* ec.publicValue should be decoded, PKCS #11 defines CKA_EC_POINT
e4d72e
+             * as encoded, but it's not always. try do decoded it and if it
e4d72e
+             * succeeds store the decoded value */
e4d72e
+            rv = SEC_QuickDERDecodeItem(arena, &decodedPoint,
e4d72e
+                                        SEC_ASN1_GET(SEC_OctetStringTemplate), &pubk->u.ec.publicValue);
e4d72e
+            if (rv == SECSuccess) {
e4d72e
+                /* both values are in the public key arena, so it's safe to
e4d72e
+                 * overwrite  the old value */
e4d72e
+                pubk->u.ec.publicValue = decodedPoint;
e4d72e
             }
e4d72e
             pubk->u.ec.encoding = ECPoint_Undefined;
e4d72e
             return pubk;
e4d72e
@@ -1276,7 +1360,9 @@ SECKEY_ConvertToPublicKey(SECKEYPrivateK
e4d72e
             break;
e4d72e
     }
e4d72e
 
e4d72e
-    PORT_FreeArena(arena, PR_FALSE);
e4d72e
+    /* must use Destroy public key here, because some paths create temporary
e4d72e
+     * PKCS #11 objects which need to be freed */
e4d72e
+    SECKEY_DestroyPublicKey(pubk);
e4d72e
     return NULL;
e4d72e
 }
e4d72e
 
e4d72e
diff -up ./nss/lib/pk11wrap/pk11priv.h.pub-priv-mechs ./nss/lib/pk11wrap/pk11priv.h
e4d72e
--- ./nss/lib/pk11wrap/pk11priv.h.pub-priv-mechs	2019-05-10 14:14:18.000000000 -0700
e4d72e
+++ ./nss/lib/pk11wrap/pk11priv.h	2019-06-05 12:01:13.729544204 -0700
e4d72e
@@ -111,6 +111,7 @@ CK_OBJECT_HANDLE PK11_FindObjectForCert(
e4d72e
 PK11SymKey *pk11_CopyToSlot(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
e4d72e
                             CK_ATTRIBUTE_TYPE operation, PK11SymKey *symKey);
e4d72e
 unsigned int pk11_GetPredefinedKeyLength(CK_KEY_TYPE keyType);
e4d72e
+CK_OBJECT_HANDLE PK11_DerivePubKeyFromPrivKey(SECKEYPrivateKey *privKey);
e4d72e
 
e4d72e
 /**********************************************************************
e4d72e
  *                   Certs
e4d72e
diff -up ./nss/lib/pk11wrap/pk11skey.c.pub-priv-mechs ./nss/lib/pk11wrap/pk11skey.c
e4d72e
--- ./nss/lib/pk11wrap/pk11skey.c.pub-priv-mechs	2019-05-10 14:14:18.000000000 -0700
e4d72e
+++ ./nss/lib/pk11wrap/pk11skey.c	2019-06-05 12:01:13.730544203 -0700
e4d72e
@@ -1840,6 +1840,35 @@ loser:
e4d72e
 }
e4d72e
 
e4d72e
 /*
e4d72e
+ * This regenerate a public key from a private key. This function is currently
e4d72e
+ * NSS private. If we want to make it public, we need to add and optional
e4d72e
+ * template or at least flags (a.la. PK11_DeriveWithFlags).
e4d72e
+ */
e4d72e
+CK_OBJECT_HANDLE
e4d72e
+PK11_DerivePubKeyFromPrivKey(SECKEYPrivateKey *privKey)
e4d72e
+{
e4d72e
+    PK11SlotInfo *slot = privKey->pkcs11Slot;
e4d72e
+    CK_MECHANISM mechanism;
e4d72e
+    CK_OBJECT_HANDLE objectID = CK_INVALID_HANDLE;
e4d72e
+    CK_RV crv;
e4d72e
+
e4d72e
+    mechanism.mechanism = CKM_NSS_PUB_FROM_PRIV;
e4d72e
+    mechanism.pParameter = NULL;
e4d72e
+    mechanism.ulParameterLen = 0;
e4d72e
+
e4d72e
+    PK11_EnterSlotMonitor(slot);
e4d72e
+    crv = PK11_GETTAB(slot)->C_DeriveKey(slot->session, &mechanism,
e4d72e
+                                         privKey->pkcs11ID, NULL, 0,
e4d72e
+                                         &objectID);
e4d72e
+    PK11_ExitSlotMonitor(slot);
e4d72e
+    if (crv != CKR_OK) {
e4d72e
+        PORT_SetError(PK11_MapError(crv));
e4d72e
+        return CK_INVALID_HANDLE;
e4d72e
+    }
e4d72e
+    return objectID;
e4d72e
+}
e4d72e
+
e4d72e
+/*
e4d72e
  * This Generates a wrapping key based on a privateKey, publicKey, and two
e4d72e
  * random numbers. For Mail usage RandomB should be NULL. In the Sender's
e4d72e
  * case RandomA is generate, outherwize it is passed.