Feature #2297

HTML5 crypto.subtle api for packet signing, hashing, verification

Added by Ryan Bennett about 6 years ago. Updated about 5 years ago.

Start date:
Due date:
% Done:


Estimated time:


Chrome and Firefox both have enough of the crypto.subtle API in recent versions to do SHA-256 hashing, RSA signing & verification, and key generation.

I did a quick branch to check it out Performance improvements are substantial, orders of magnitude faster for signing (in chrome, slightly less in FF): (be sure to wait until you get results from one bench before starting the other)

-crypto.subtle uses an async promise-based api... This is actually a good thing, as it allows the main thread to keep going about it's business, but would require some slight changes to use of data.sign() and data.verify() (most simply using data.sign(callback) and data.verify(callback))
-crypto.subtle.importKey has no obvious way to import our hard-coded pem string... supported import formats are "raw"(?), "jwk"(JSON Web Key), "pkcs8", and "spci". As a workaround for the above benchmark, the branch simply generates a fresh key per session.

The code changes are reflected in

only hash and verify are implemented, and theres a check to gracefully degrade to JS in unsupported environments, or if no callback is provided to the sign() method call


Updated by Jeff Thompson about 6 years ago

Hi Ryan. Thanks a bunch for looking into this. It's good to see the new crypto API has come along so far.

For decoding the private key, the other NDN libraries use unencrypted PKCS8. It looks like we should adopt that here.

A question about the async interface to signing. Usually async is for tasks that have some kind of "wait" like network I/O. But JavaScript is single-threaded, so if you call sync and it starts using CPU cycles right away, doesn't it effectively block until it calls the "callback"? Would we lose anything by trying to keep the blocking API?


Updated by Ryan Bennett about 6 years ago

My understanding is that the signing is done by a separate thread in the browser (presumably running native code, hence the speed disparity), which then returns the result asynchronously... The test code I linked to does a for loop to call the .sign function, and prints the elapsed time immediately, and then once all the signatures have completed it prints the second line of results. for the sake of not defaulting to something super time consuming, I set the default to 100 in the benchmark, but you start to notice the difference if you get into order of doing thousands of signatures.

Additionally, I'm at a loss for how we could even maintain a blocking API backed by the crypto.subtle interface... any setTimeout/interval to check the result would of course cause the function to return, and adding an loop that just cycles until the promise is fulfilled seems like a dangerous approach.

I've updated my webCrypto branch to work with both async and sync calls to data.sign: if no callback is provided and/or the crypto.subtle interface is unavailable, it gracefully falls back to the existing js-crypto. (if there is a callback but crypto.subtle isn't available, the callback is simply called syncronously).

Another thing to note: crypto.subtle only works in "secure" location environments (https, chrome extensions, firefox addons...) and while testing I realized that if one is using ndn-js during an https session, you can't connect over an unsecured websocket. I don't know if NFD currently supports wss, but it was pretty trivial to set up in JS for the gremlin... might we wish to revive the old 'wsproxy' code to enable wss to ws bridging? Either way, that's probably a conversation for another issue.


Updated by Jeff Thompson about 6 years ago

  • Status changed from New to In Progress

Updated by Junxiao Shi about 6 years ago

Why don't NDN.JS also use a Promise based API? It's considered superior to a callback-based API.


Updated by Jeff Thompson about 6 years ago

We have a goal to keep a similar API across all the CCL libraries like PyNDN and NDN-CPP. The use of callbacks is fairly simple and doesn't need the sophistication of promises.

Is there any code that will be broken without promises?


Updated by Jeff Thompson about 6 years ago

Ryan's code is merged for UseSubtleCrypto(), and support in sign and verify. Had to add an onComplete callback to KeyChain.sign.

We still need to decide on how to add callbacks to all the other API methods that do crypto.


Updated by Jeff Thompson about 5 years ago

  • Status changed from In Progress to Closed

API methods that use crypto (as well as IndexedDB) now have onComplete and onError callbacks to allow async.

Also available in: Atom PDF