Bug 1106087 - Recreate newly generated ECDH private keys with an CKA_EC_POINT attribute to support JWK and PKCS8 export r=rbarnes
authorTim Taubert <ttaubert@mozilla.com>
Sat, 25 Apr 2015 14:53:43 +0200
changeset 276454 7627c939c0bde6a3ac8d51983f9a794360a1005c
parent 276453 2b1b43089518b1c90b6352f4486bcbcd88042bfc
child 276455 82f24d061990e464e02ed3977ff10da01d326c1b
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrbarnes
bugs1106087
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1106087 - Recreate newly generated ECDH private keys with an CKA_EC_POINT attribute to support JWK and PKCS8 export r=rbarnes
dom/crypto/CryptoKey.cpp
dom/crypto/CryptoKey.h
dom/crypto/WebCryptoTask.cpp
--- a/dom/crypto/CryptoKey.cpp
+++ b/dom/crypto/CryptoKey.cpp
@@ -58,16 +58,39 @@ StringToUsage(const nsString& aUsage, Cr
   } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_UNWRAPKEY)) {
     aUsageOut = CryptoKey::UNWRAPKEY;
   } else {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
   return NS_OK;
 }
 
+SECKEYPrivateKey*
+PrivateKeyFromPrivateKeyTemplate(SECItem* aObjID,
+                                 CK_ATTRIBUTE* aTemplate,
+                                 CK_ULONG aTemplateSize)
+{
+  // Create a generic object with the contents of the key
+  ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+  if (!slot) {
+    return nullptr;
+  }
+
+  ScopedPK11GenericObject obj(PK11_CreateGenericObject(slot,
+                                                       aTemplate,
+                                                       aTemplateSize,
+                                                       PR_FALSE));
+  if (!obj) {
+    return nullptr;
+  }
+
+  // Have NSS translate the object to a private key.
+  return PK11_FindKeyByKeyID(slot, aObjID, nullptr);
+}
+
 CryptoKey::CryptoKey(nsIGlobalObject* aGlobal)
   : mGlobal(aGlobal)
   , mAttributes(0)
   , mSymKey()
   , mPrivateKey(nullptr)
   , mPublicKey(nullptr)
 {
 }
@@ -218,16 +241,83 @@ void
 CryptoKey::SetExtractable(bool aExtractable)
 {
   mAttributes &= CLEAR_EXTRACTABLE;
   if (aExtractable) {
     mAttributes |= EXTRACTABLE;
   }
 }
 
+// NSS exports private EC keys without the CKA_EC_POINT attribute, i.e. the
+// public value. To properly export the private key to JWK or PKCS #8 we need
+// the public key data though and so we use this method to augment a private
+// key with data from the given public key.
+nsresult
+CryptoKey::AddPublicKeyData(SECKEYPublicKey* aPublicKey)
+{
+  // This should be a private key.
+  MOZ_ASSERT(GetKeyType() == PRIVATE);
+  // There should be a private NSS key with type 'EC'.
+  MOZ_ASSERT(mPrivateKey && mPrivateKey->keyType == ecKey);
+  // The given public key should have the same key type.
+  MOZ_ASSERT(aPublicKey->keyType == mPrivateKey->keyType);
+
+  nsNSSShutDownPreventionLock locker;
+
+  ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+  if (!slot) {
+    return NS_ERROR_DOM_OPERATION_ERR;
+  }
+
+  // Generate a random 160-bit object ID.
+  ScopedSECItem objID(::SECITEM_AllocItem(nullptr, nullptr, 20));
+  SECStatus rv = PK11_GenerateRandomOnSlot(slot, objID->data, objID->len);
+  if (rv != SECSuccess) {
+    return NS_ERROR_DOM_OPERATION_ERR;
+  }
+
+  // Read EC params.
+  ScopedSECItem params(::SECITEM_AllocItem(nullptr, nullptr, 0));
+  rv = PK11_ReadRawAttribute(PK11_TypePrivKey, mPrivateKey, CKA_EC_PARAMS,
+                             params);
+  if (rv != SECSuccess) {
+    return NS_ERROR_DOM_OPERATION_ERR;
+  }
+
+  // Read private value.
+  ScopedSECItem value(::SECITEM_AllocItem(nullptr, nullptr, 0));
+  rv = PK11_ReadRawAttribute(PK11_TypePrivKey, mPrivateKey, CKA_VALUE, value);
+  if (rv != SECSuccess) {
+    return NS_ERROR_DOM_OPERATION_ERR;
+  }
+
+  SECItem* point = &aPublicKey->u.ec.publicValue;
+  CK_OBJECT_CLASS privateKeyValue = CKO_PRIVATE_KEY;
+  CK_BBOOL falseValue = CK_FALSE;
+  CK_KEY_TYPE ecValue = CKK_EC;
+
+  CK_ATTRIBUTE keyTemplate[9] = {
+    { CKA_CLASS,            &privateKeyValue,     sizeof(privateKeyValue) },
+    { CKA_KEY_TYPE,         &ecValue,             sizeof(ecValue) },
+    { CKA_TOKEN,            &falseValue,          sizeof(falseValue) },
+    { CKA_SENSITIVE,        &falseValue,          sizeof(falseValue) },
+    { CKA_PRIVATE,          &falseValue,          sizeof(falseValue) },
+    { CKA_ID,               objID->data,          objID->len },
+    { CKA_EC_PARAMS,        params->data,         params->len },
+    { CKA_EC_POINT,         point->data,          point->len },
+    { CKA_VALUE,            value->data,          value->len },
+  };
+
+  mPrivateKey = PrivateKeyFromPrivateKeyTemplate(objID, keyTemplate,
+                                                 PR_ARRAY_SIZE(keyTemplate));
+  NS_ENSURE_TRUE(mPrivateKey, NS_ERROR_DOM_OPERATION_ERR);
+
+  return NS_OK;
+}
+
 void
 CryptoKey::ClearUsages()
 {
   mAttributes &= CLEAR_USAGES;
 }
 
 nsresult
 CryptoKey::AddUsage(const nsString& aUsage)
@@ -362,16 +452,19 @@ void CryptoKey::destructorSafeDestroyNSS
 // Serialization and deserialization convenience methods
 
 SECKEYPrivateKey*
 CryptoKey::PrivateKeyFromPkcs8(CryptoBuffer& aKeyData,
                          const nsNSSShutDownPreventionLock& /*proofOfLock*/)
 {
   SECKEYPrivateKey* privKey;
   ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+  if (!slot) {
+    return nullptr;
+  }
 
   ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
   if (!arena) {
     return nullptr;
   }
 
   SECItem pkcs8Item = { siBuffer, nullptr, 0 };
   if (!aKeyData.ToSECItem(arena, &pkcs8Item)) {
@@ -578,46 +671,16 @@ CreateECPointForCoordinates(const Crypto
   point->data[0] = EC_POINT_FORM_UNCOMPRESSED;
   memcpy(point->data + 1, aX.Elements(), aX.Length());
   memcpy(point->data + 1 + aX.Length(), aY.Elements(), aY.Length());
 
   return point;
 }
 
 SECKEYPrivateKey*
-PrivateKeyFromPrivateKeyTemplate(SECItem* aObjID,
-                                 CK_ATTRIBUTE* aTemplate,
-                                 CK_ULONG aTemplateSize)
-{
-  // Create a generic object with the contents of the key
-  ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
-  if (!slot.get()) {
-    return nullptr;
-  }
-
-  ScopedPK11GenericObject obj(PK11_CreateGenericObject(slot.get(),
-                                                       aTemplate,
-                                                       aTemplateSize,
-                                                       PR_FALSE));
-  if (!obj.get()) {
-    return nullptr;
-  }
-
-  // Have NSS translate the object to a private key by inspection
-  // and make a copy we can own
-  ScopedSECKEYPrivateKey privKey(PK11_FindKeyByKeyID(slot.get(), aObjID,
-                                                     nullptr));
-  if (!privKey.get()) {
-    return nullptr;
-  }
-
-  return SECKEY_CopyPrivateKey(privKey.get());
-}
-
-SECKEYPrivateKey*
 CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk,
                              const nsNSSShutDownPreventionLock& /*proofOfLock*/)
 {
   CK_OBJECT_CLASS privateKeyValue = CKO_PRIVATE_KEY;
   CK_BBOOL falseValue = CK_FALSE;
 
   if (aJwk.mKty.EqualsLiteral(JWK_TYPE_EC)) {
     // Verify that all of the required parameters are present
--- a/dom/crypto/CryptoKey.h
+++ b/dom/crypto/CryptoKey.h
@@ -111,16 +111,17 @@ public:
   // them to manipulate the object
 
   KeyAlgorithmProxy& Algorithm();
   const KeyAlgorithmProxy& Algorithm() const;
   KeyType GetKeyType() const;
   nsresult SetType(const nsString& aType);
   void SetType(KeyType aType);
   void SetExtractable(bool aExtractable);
+  nsresult AddPublicKeyData(SECKEYPublicKey* point);
   void ClearUsages();
   nsresult AddUsage(const nsString& aUsage);
   nsresult AddUsageIntersecting(const nsString& aUsage, uint32_t aUsageMask);
   void AddUsage(KeyUsage aUsage);
   bool HasAnyUsage();
   bool HasUsage(KeyUsage aUsage);
   bool HasUsageOtherThan(uint32_t aUsages);
   static bool IsRecognizedUsage(const nsString& aUsage);
--- a/dom/crypto/WebCryptoTask.cpp
+++ b/dom/crypto/WebCryptoTask.cpp
@@ -2326,16 +2326,24 @@ private:
                                        PR_FALSE, PR_FALSE, nullptr);
     mPublicKey = pubKey;
     if (!mPrivateKey.get() || !mPublicKey.get()) {
       return NS_ERROR_DOM_UNKNOWN_ERR;
     }
 
     mKeyPair.mPrivateKey.get()->SetPrivateKey(mPrivateKey);
     mKeyPair.mPublicKey.get()->SetPublicKey(mPublicKey);
+
+    // PK11_GenerateKeyPair() does not set a CKA_EC_POINT attribute on the
+    // private key, we need this later when exporting to PKCS8 and JWK though.
+    if (mMechanism == CKM_EC_KEY_PAIR_GEN) {
+      nsresult rv = mKeyPair.mPrivateKey->AddPublicKeyData(mPublicKey);
+      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
+    }
+
     return NS_OK;
   }
 
   virtual void Resolve() override
   {
     mResultPromise->MaybeResolve(mKeyPair);
   }
 };