1

I need to store an ECDH P-384 keypair in an IndexedDB database. This is the code that generates the keypair:

async function generateKeys() {
    // generate client's ECDH asymmetric keypair (privateKey, publicKey)
    return await window.crypto.subtle.generateKey(
        {
            name: "ECDH",
            namedCurve: "P-384",
        },
        true,
        ["deriveKey", "deriveBits"]
    );
}

This is the code that opens the database, creates the 'asymmetric' object store if it doesn't exist and then generates and stores the keypair:


var request = window.indexedDB.open("main", 3);
request.onerror = function (event) {
    console.error("Database error: " + event.target.errorCode);
};
request.onupgradeneeded = function (event) {
    db = event.target.result;
    if (!db.objectStoreNames.contains('asymmetric')) {
        console.log('creating OS');
        var objectStore = db.createObjectStore("asymmetric", { keyPath: "owner" });
        objectStore.transaction.oncomplete = function (event) {
            intialKeySetup = true;
        };
    }

};
request.onsuccess = function (event) {
    db = event.target.result;
    if (intialKeySetup) { // first time running
        setupNewKeys();
    } else {
        console.log('loading keys...');
        loadKeys();
    }
};

function setupNewKeys() {
    // Generates ECDH keypair, then saves them to the 'asymmetric' object store with key '#this#'
    return generateKeys().then((kp) => {
        clientKeys = kp;
        var asymOS = db.transaction("asymmetric", "readwrite").objectStore("asymmetric");

        // Error occurs here:
        asymOS.add({ owner: '#this#', keys: kp }); // kp = { publicKey: CryptoKey object, privateKey: CryptoKey object }
        console.log('Done. Wrote keys to DB: ');
        console.log(kp);
        startComm();
    });
}


function loadKeys() {
    // Loads the client's keypair into memory from the 'asymmetric' object store at key '#this#'
    console.log('Attempting to access client keys...');
    var transaction = db.transaction(["asymmetric"]);
    var objectStore = transaction.objectStore("asymmetric");
    var request = objectStore.get("#this#");
    request.onerror = function (event) {
        console.error(event.target.errorCode);
    };
    request.onsuccess = function (event) {
        if (!request.result) {
            setupNewKeys();
        } else {
            console.log(request.result);
            clientKeys = request.result.keys;
            console.log('Retrieved Keys');
            console.log('Starting Comm...');
            startComm(); // connect to WebSocket server
        }
    };
}

This code works perfectly fine on Chrome, Chromium Edge, macOS Safari, iOS Safari and Chrome on Android, however it does not work on Firefox. It doesn't let it store the two CryptoKey objects in the object store, and generates the error:
Uncaught (in promise) DOMException: The object could not be cloned.
When I instead try to store a string instead of the CryptoKey objects, the error disappears, and I can see the string stored in the "Storage" tab of the developer tools. Why is it not letting me store the CryptoKey in the object store in Firefox? And are there any workarounds other than exporting the key?

65536
  • 106
  • 7
  • Objects being inserted into indexedDb undergo a kind of copy operation. Simplified, think of it like `var myCopy = JSON.parse(JSON.stringify(myObject));`. Some objects have circular references and other things that cause this copying, aka cloning, to fail. Take your key object and convert it to a plain object or string before storing it. Probably something in this key object that does not play nice with the cloning op. – Josh Dec 08 '20 at 15:52
  • Thanks for the suggestion. After finding multiple bug reports from quite a few years ago such as: [link](https://bugzilla.mozilla.org/show_bug.cgi?id=1379493), It looks like if I want to support Firefox I will have to export the key binary data and store that in the database instead, which is kind of annoying. – 65536 Dec 08 '20 at 16:53

0 Answers0