Nitrokey is an open source hardware USB key for data encryption and two-factor authentication with FIDO. While FIDO is supported by web browsers, using Nitrokey as a secure key store for email and (arbitrary) data encryption requires a native software. Therefore email encryption in webmail isn’t possible with Nitrokey. At the same time strong end-to-end encryption in web applications all share the same challenge: To store users’ private keys securely and conveniently. Therefore secure end-to-end encryption usually requires native software too (e.g. instant messenger app) or – less secure – store the user keys password-encrypted on servers. Nitrokey aims to solve these issues by developing a way to use Nitrokey with web applications. To avoid the necessity of device driver, browser add-on or separate software this project is going to utilize the FIDO (CTAP) protocol. As a result the solution will work with any modern browser (which all support WebAuthn), on any operating system even on Android. This will give any web application the option to store users’ private keys on user’s own Nitrokey devices.
- This solution is inspired by OnlyKey’s WebCrypt proof-of-concept.
- CTAP2 (FIDO2) is used for future-proofness and avoiding incompatibilities in the long run. CTAP1 (FIDO U2F) may be added for backward-compatibility.
- To reduce complexity and increase usability, we focus on ECC only. RSA may be added later and be used only for importing existing keys.
- Hence, integration with GNUK is not important. Given conflicting licenses, we aim to implement an own OpenPGP Card interface optionally.
- Solution should work via USB and NFC and ordinary web browser.
This solution will support multiple keys. Keys can be derived on the fly from a master key or imported and stored in the device (resident keys). All key operations (sign, decrypt) require the public key as a parameter. These operations follow this scheme:
- Check if public key matches any stored (residential) key and origin. If not, continue with step 2, otherwise step 3.
- Derive key: key = KDF(master key, public_key, origin) and verify it's validity against HMAC.
- Compute key operation with payload.
- Return result.
- Keys can be configurable to be used per-origin only which avoids a privacy risk. Use cases where keys are used among different origins (e.g. email encryption) can disable this option for their keys.
- Keys' attributes contain usage flag: encryption/decryption, signing, and both.
- Requirement on client software: Client software needs to know the public key related to payload. Public keys or derived keys can't be read.
The PIN is required to authenticate the user during authorizing key operations. I suggest to make it configurable if a) the PIN is required for every operation or b) PIN is required only once per device session. In any case also a touch button press is required to authorize every sign and decrypt operation.
Backup and Seed
The seed is setup during initializing the device and used to derive a master key which is used to derive keys (and as a source to encrypt residential keys). All device-generated keys can be re-generated by providing the seed, which acts as a backup. A seed allows to recover the entire device on its own. The only exception are keys which are imported by the user. Those are not restored by the seed but it's assumed that a key backup exists already. Here is a POC for generating ECC keys from passphrase/seed, and storing them in an OpenPGP format.
- Initialize or restore from seed
- Generate non-resident key
- Write resident key - for externally existing keys only
- Read public key of residential keys
- Sign(to_be_signed_data_hash, public_key, HMAC, origin)
- Decrypt(to_be_decrypted_data, public_key, HMAC, origin)
- Status (Unlocked, version, available resident key slots)
- Configure (see above)
- Unlock - For U2F compatibility.
- Reset - For U2F compatibility.
Questions & Answers
Reasoning for Derived Keys
Using derived keys (as opposed to residential keys) by default provides the following benefits:
- For encryption use cases such as we want to enable with WebCrypt, a backup mechanism is a fundamental prerequisite. At the same time the entire solution should be as easy to use as possible. Therefore we aim to provide an easy to use backup mechanism. A seed phrase or backup phrase is the most easy mechanism we could think of. For technical reasons a backup seed demands derived keys in ECC format (not RSA). As opposed to a backup seed, a classical file backup would have these disadvantages:
- A backup file needs to be stored separately and (usually) protected with a passphrase.
- A new backup file needs to be created and stored again and again after generating a new key.
- Practice proofs that backups are often not executed properly. This might result in user frustration when they can't access their encrypted data anymore.
- Having a single or few residential keys might enable malicious websites to track users' devices which could violate their privacy. Therefore it's benefitial to assume derived keys as the default.
- With resident keys, the amount of keys (key storage) would be limited. As opposed to this an unlimited amount of derived keys could be used.
- A firmware containig the WebCrypt feature.
- Optional: Patch to openpgp.js adding our WebCrypt library and make use of the device key store.
- OpenPGP Card interface
- RSA support
- OpenPGP.js integration
I. Establishing PoC
- Communication layer over FIDO2 - OnlyKey offers U2F communication. We have no ready FIDO2 transport layer yet (only U2F). Estimation assumes no code reuse.
- Initial design and structure for commands - Setting up code structure and design for commands and implementations
- Initialize and restore from seed - Master seed handling. Additional time for security analysis. Basic tests included.
- Generate non-resident key - Key generation. Additional time for security analysis. Basic tests included.
- Sign(to_be_signed_data_hash, public_key, HMAC, origin) - Expecting simple implementation. Basic tests included.
- Decrypt(to_be_decrypted_data, public_key, HMAC, origin) - Expecting simple implementation. Basic tests included.
- Status (Unlocked, version, available resident key slots) - Expecting simple implementation. Basic tests included.
- Additional firmware tests - Tests for everyday usage, edge cases, invalid use cases.
II. Encrypted storage
- Encrypted resident keys and master key (for derived keys) - for all user data entities
III. Resident keys feature
- Write resident key - for externally existing keys only - Expecting simple implementation. Basic tests included.
- Read public key of residential keys - Expecting simple implementation. Basic tests included.
IV. FIDO U2F support
- CTAP1 transport layer (for backward compatibility)
- Unlock - For U2F compatibility - Expecting simple implementation. Basic tests included.
- Reset - For U2F compatibility - Expecting simple implementation. Basic tests included.
- Configure - U2F and other options - Expecting simple implementation. Basic tests included.
- PIN's use - each action, or once per session
V. RSA support
- RSA support - firmware side - tests included. For resident keys only.
- RSA support - Nitrokey JS library - Add support to JS library
- RSA support - OpenPGP.js - Add support to 3rd party JS library
VI. NFC support
- NFC tests and bug fixes - Tests for NFC interactions (PC/Mobile)
- API design - API design for the JS Nitrokey WebCrypt library
- API implementation - Library implementation
- JS Demo Application - Demo application, similar to the OnlyKey’s demo + additional features if time permits
- Firmware: Commands and Features - Examples of use (developers-centric, to pick-up framework)
D. OpenPGP Card Interface
- Basic integration - CCID (GnuPG + etc.) and our custom access, assuming Solo’s OpenPGP card integration.
- ECDSA support - Signing function already provided, but the format parsing is to be implemented. Usable for ECC encryption.
- ECC decryption
- Feature completion and improvements of given implementation
- Security review and improvements
E. OpenPGP.js patch
Patch for OpenPGP.js to use Nitrokey WebCrypt extension (to use our device as key storage, instead of host storage)
- Key handling:
- Reserved time for additional corrections, developers input
- Documentation of OpenPGP patch changes - Documentation for OpenPGP.js developers/users, to be included by them