Unable to store CryptoKey in IndexedDB: DataCloneError

Issue #12782255 • Assigned to Shawn P.

Details

Author
Michal L.
Created
Jul 15, 2017
Privacy
This issue is public.
Found in
  • Microsoft Edge
Standard affected
Web Cryptography API

Found in build #
40.15063
Reports
Reported by 3 people

Sign in to watch or report this issue.

Steps to reproduce

Run the following script in the console or create a HTML page with the following script:

window.crypto.subtle.generateKey({
        name: "RSASSA-PKCS1-v1_5",
        modulusLength: 2048,
        publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
        hash: { name: "SHA-256" }
    }, false, // exportable
    ["sign", "verify"])
.then(function (keys) {
    var indexedDB = window.indexedDB;

    var db_open = indexedDB.open("MyDatabase", 1);

    db_open.onupgradeneeded = function () {
        var db = db_open.result;
        var store = db.createObjectStore("MyObjectStore", { keyPath: "id" });
    };

    db_open.onsuccess = function () {
        // Start a new transaction
        var db = db_open.result;
        var tx = db.transaction("MyObjectStore", "readwrite");
        var store = tx.objectStore("MyObjectStore");

        store.put({ id: 1, keys: keys });
        
        tx.oncomplete = function () {
            db.close();
            
            console.log('Everything works properly.');
        };
    };
});

A DataCloneError will appear at: store.put({ id: 1, keys: keys });.

There is nothing wrong with storing CryptoKey objects inside IndexedDB. This feature is quite crucial for some use cases and it’s described in the W3C standard on Web Crypto API. An important remark is that IndexedDB should be also able to store objects with CryptoKey.exportable property set to false (right now it doesn’t work in any case).

The above PoC works well in latest Chrome and Firefox, as they conform with this part of standard.

Attachments

0 attachments

    Comments and activity

    • Yeah, I really need to serialize crypto keys for critical project for my enterprise customer. Now the deadlines are closing in, and I don’t know what to do - I’ll probably have to use some INSECURE workaround (like making key exportable and serializing it by hand), but security was one of the more important goals of this project. I really don’t have time, and this code works on every other browser. Help!

    • In case anybody wonder, on Safari (10.1) it works by replacing the first line

      window.crypto.subtle.generateKey({
      

      with

      window.crypto.webkitSubtle.generateKey({
      

      Also failed making Edge (build 40) work

    • Here is a W3C recommendation about storing CryptoKeys in IndexedDB:

      https://www.w3.org/TR/2017/REC-WebCryptoAPI-20170126/#concepts-key-storage

      another important section is linked in “Standard affected”

    • Microsoft Edge Team

      Changed Assigned To to “Steven K.”

    • Hi,

      I am looking at this now.  While you are waiting for my response, I thought I would check to see if you have the link to the Microsoft Edge IndexedDB documentation?

      https://docs.microsoft.com/en-us/microsoft-edge/dev-guide/storage/indexeddb

      I will let you know as soon as I have reviewed your submission,

      Steve

    • Hi Michal,

      I believe what is required here is to use either the wrapKey() and unwrapKey() methods or the exportKey() and importKey() methods and then passing the returned variable into

      objectStore.put(id: x, key: exportedKey)

      Here are references for these operations in Edge:

      https://docs.microsoft.com/en-us/microsoft-edge/dev-guide/security/web-cryptography-api

      https://msdn.microsoft.com/en-us/library/dn904640(v=vs.85).aspx

      https://www.w3.org/2012/webcrypto/wiki/KeyWrap_Proposal#AES_Key_Wrap

      Additional information on the topic:

      The standard does not allow a complete serialization of the entire CyptoKey objects directly (i.e., without using wrapKey() or exportKey()) as the CryptoKey object includes the “set of internal slots that store information about the key” that is not meant to be exposed.

      http://www.w3.org/TR/WebCryptoAPI/#cryptokey-interface-internal-slots

      "The [[handle]] slot is an opaque type that contains whatever data the underlying cryptographic implementation uses to represent a logical key. Different cryptographic implementations may use different types, ranging from opaque identifiers represented as integers, pointer types, or structures that provide identifying information. These handles are never exposed to applications. "

      From the 4th paragraph in "Section 6.2. Security considerations for authors",

      https://www.w3.org/TR/WebCryptoAPI/#security-developers

      “Authors should be aware that this specification places no normative requirements on implementations as to how the underlying cryptographic key material is stored. The only requirement is that key material is not exposed to script, except through the use of the exportKey and wrapKey operations.”

      Hope this helps,

      Steve

    • Microsoft Edge Team

      Changed Assigned To to “Venkat K.”

    • Hello,

      I’m afraid you misunderstood part of the standard.

      https://www.w3.org/TR/WebCryptoAPI/#concepts-key-storage


      "5.2. Key Storage

      This specification does not explicitly provide any new storage mechanisms for CryptoKey objects. Instead, by allowing the CryptoKey to be used with the structured clone algorithm, any existing or future web storage mechanisms that support storing structured clonable objects can be used to store CryptoKey objects.

      In practice, it is expected that most authors will make use of the Indexed Database API, which allows associative storage of key/value pairs, where the key is some string identifier meaningful to the application, and the value is a CryptoKey object. This allows the storage and retrieval of key material, without ever exposing that key material to the application or the JavaScript environment. Additionally, this allows authors the full flexibility to store any additional metadata with the CryptoKey itself."


      It was stated that in general it’s possible for IndexedDB to store whole CryptoKey objects thanks to the internal structured clone algorithm. Moreover, please check out this statement carefully:

      This allows the storage and retrieval of key material, without ever exposing that key material to the application or the JavaScript environment.

      Using wrapKey() or exportKey() is a slightly another approach in which it’s required to expose key material to the JavaScript environment. The thing about internal slots is only about that they are not directly exposed to the scripts, it’s still not an obstacle to allow storing them in the IndexedDB.

      However, I need to restate the bug, because it turned out to be more narrow than I thought. Actually, in my above example with generateKey() you receive CryptoKeyPair object afterwards, which contains two fields: publicKey and privateKey, both of type CryptoKey. And if you would try to do this:

      store.put({ id: 1, keys: keys }); // keys - a CryptoKeyPair object
      

      Edge will raise DataCloneError. But, which is very important, recently I discovered that if you would do:

      store.put({ id: 1, publicKey: keys.publicKey, privateKey: keys.privateKey });
      

      It works perfectly fine on Edge, however it’s still confusing.

      So my restatement of the bug report is: Right now it is possible to store CryptoKey objects in IndexedDB but it’s not possible to store them if they are wrapped in CryptoKeyPair object, which is very misleading. On the other browsers (tested on Chrome, Firefox, Opera, Safari) there is no problem with storing CryptoKeyPair directly.

      So could you please investigate that CryptoKey objects could be stored properly, but an attempt to store CryptoKeyPair is raising DataCloneError? If you would fix it, then the implementation will be compatible with other popular browsers.

    • Microsoft Edge Team

      Changed Assigned To from “Venkat K.” to “Shawn P.”

    • Thank you for the clarification on the standard interpretation, and for the updated specifics related to this bug.

      It appears that the standard does allow storing the keys and specifically warns users that it is their responsibility to mange the security of the stored content.  This is taken in the same paragraph mentioned about, however, its the second half of the paragraph that I did not quote previously.  I believe that is why using the wrap and unwrap method is recommended if the storage medium is unknown or untrusted and was the source of my confusion, I.e. made assumption that user would want the key securely stored.  The standard apparently allows the developer the flexibility of providing that guarantee another way.

      http://www.w3.org/TR/WebCryptoAPI/#security-developers

      … "In particular, it does not guarantee that the underlying cryptographic key material will not be persisted to disk, possibly unencrypted, nor that it will be inaccessible to users or other applications running with the same privileges as the User Agent. Any application or user that has access to the device storage may be able to recover the key material, even through scripts may be prohibited. "

      Example, on stackoverlfow:

      https://stackoverflow.com/questions/38413391/generate-rsa-key-pair-using-webcrypto-api-and-protect-it-with-passphrase

      Thanks again for the bug report and the help with this,

      Steve

    • I can add that I’m struggling with a similar problem in Internet Explorer 11: When using the approach suggested by Michal above (only storing the individual CryptoKey’s, not CryptoKeyPair), IE11 stores the composite object in IndexedDB without complaint, but when I retrieve the entry later on, the type information seems to be gone entirely. DevTools lists the properties as having type object, not CryptoKey.
      I have no problems with this in Edge or Chrome.

      The IE11 docs for this (https://msdn.microsoft.com/en-us/library/dn302338(v=vs.85).aspx) seems to state that it should be possible to store CryptoKey’s this way, but contains no guidelines on how to actually implement this.

      Any pointers on how to get this working would be greatly appreciated ?

    • @Mikkel,

      Storing both keys at the same time is also not working in IE?  You see the same DataCloneError?  

      // Edge will raise DataCloneError.

      store
      .put({ id
      : 1
      , keys
      : keys }); // keys - a CryptoKeyPair object

    You need to sign in to your Microsoft account to add a comment.

    Sign in