NostrGit / NostrGit

A truly censorship-resistant alternative to GitHub that has a chance of working
https://nostrgit.com
GNU Affero General Public License v3.0
269 stars 24 forks source link

Considering storing Cryptographic Keys in Persistent Browser Storage #91

Closed nflatrea closed 1 year ago

nflatrea commented 1 year ago

Storing cryprographic keys are a pa**n .. but It is necessary for message signing and proofs..

I can't build anything right now, I'm overwhelmed with work but here's is some information that could be useful for you. I'll do a quick PoC asap

Multiple choices over the years

Five Web technologies have appeared over the last few years that enable new solutions for storing cryptographic keys in persistent browser storage. These technologies are Web APIs that browsers make available to JavaScript code embedded in Web pages. They include, in chronological order, the Web Storage API, the IndexedDB API, the Web Cryptography API, the Service Worker API and the Web Authentication API.

The Web Authentication API

The Web Authentication API is based on the FIDO U2F specification described above, whose adoption by the W3C may eliminate the need for browser plugins. It is scheduled to become available without plugins on Chrome, Firefox and Edge later this year.

The Web Authentication API provides a new solution to the problem faced by web applications of how to store client-side cryptographic credentials. The authentication key pair is stored in the authenticator, which is accessible to the browser. Since the authenticator should be a separate hardware device, this does not seem very different from storing credentials in a smart card. But the same authenticator may be shared by any number of web applications, and provisioning is automated. On the other hand the authenticator can only store uncertified key pairs, and the Web Authentication API can only be used to authenticate repeat visits of a user who has previously registered with the application. It cannot be used for identity proofing or for claiming attributes certified by an authoritative third party. Therefore it does not provide a general purpose key-storage solution.

The other four web technologies are not specifically intended for the storage of cryptographic keys, but they can be combined in several ways to obtain general purpose key-storage solutions.

The Web Storage API

The Web Storage API is available in all browsers (including Opera but not Opera Mini, which does not support any of the APIs that we are concerned with). It was introduced as part of the WHATWG HTML5 specification, and has been available in most browsers since 2009, before HTML5 was officially adopted by the W3C in 2014. (Detailed availability history for all the Web technologies discussed here can be found at caniuse.com.) It is very simple. It allows JavaScript code to store strings as properties of a localStorage object or a sessionStorage object, with the former providing persistent storage. Stored data is protected by the same origin policy implemented by all browsers. In first approximation, this means that if a string is stored by JavaScript code embedded in a web page downloaded from a particular DNS domain, it can only be retrieved by JavaScript code that is also embedded in a web page downloaded from the same DNS domain.

The IndexedDB API

The IndexedDB API also provides persistent storage. It is available in all browsers today and has been available in most browsers since 2012. By contrast with the Web Storage API, it has a complex asynchronous interface, which has earned it this warning in the IndexedDB API page of the Mozilla Developer Network: “IndexedDB API is powerful, but may seem too complicated for simple cases”. On the other hand it can store arbitrary JavaScript objects rather than simple JavaScript strings. Not having to convert an object to a string is only a matter of convenience in the case of an ordinary object, but makes a big difference when the object to be stored is a cryptographic key, as explained below. Data stored through the Indexed DB API is protected by the same origin policy of the browser, just like data stored through the Web Storage API.

The Web Cryptography API

The Web Cryptography API has been supported by most browsers since 2014. It is available in all browsers today, but Internet Explorer only supports an old version of the specification, and Safari requires API references to be prefixed with webkit. It provides a random bit generator, and a number of cryptographic primitives invoked via a complex asynchronous interface. The cryptographic primitives include two RSA signature schemes, RSASSA-PSS and RSASSA-PKCS1-v1_5 (described in RFC 3447), and ECDSA with NIST curves P-256, P-384 and P-512 (described, e.g., in the ECDSA Certicom white paper). Other primitives include ECDH, AES (including AES-GCM), HMAC, SHA (including SHA-1, SHA-256, SHA-384 and SHA-512), HKDF and PBKDF2. Surprisingly, DSA is not included.

In the Web Cryptography API, generation of an RSA or ECDSA key pair produces two CryptoKey objects, one containing the private key, the other containing the public key. When the key pair is generated, the private key can be made non-extractable from its CryptoKey object. This means that it cannot later be extracted from the object by JavaScript code embedded in a Web page, even if that Web page has the same origin as the Web page containing the JavaScript code that invoked the key generation procedure. A CryptoKey object is not persistent by itself, and it is not an ordinary JavaScript object that could be encoded as a string for storage in localStorage, but it can be stored in a database accessed through the IndexedDB API.

cypherhoodlum commented 1 year ago

This is a great starting point. Thanks!

nflatrea commented 1 year ago

Cryptographic operations tend to be computationally intensive tho... Since Javascript is single-threaded, doing these operations on the main UI thread will cause the browser to freeze for a few seconds.

In order to keep the application performant, we can use a Web Worker to perform cryptographic computations on a separate browser thread. We could use JSEncrypt, a reputable Javascript RSA implementation originating from Stanford. The using JSEncrypt, we can create a few helper functions for encryption, decryption, and key pair generation.

Here are some sources that can be worth looking through

https://www.cryptomathic.com/news-events/blog/cryptographic-key-management-the-risks-and-mitigations https://blog.patricktriest.com/building-an-encrypted-messenger-with-javascript/ https://github.com/47ng/session-keystore