canmove, Confirmed users
640
edits
(Update.) |
|||
Line 24: | Line 24: | ||
'''tl;dr''': replace the passphrase with an AES key, which will be schlepped around using J-PAKE (so typing it is likely unnecessary). Use this key to indirectly encrypt the 'bulk' symkeys. No RSA involved. | '''tl;dr''': replace the passphrase with an AES key, which will be schlepped around using J-PAKE (so typing it is likely unnecessary). Use this key to indirectly encrypt the 'bulk' symkeys. No RSA involved. | ||
Existing passphrases will be upgraded to this scheme using PBKDF2. | |||
Line 30: | Line 32: | ||
Rather than have a user enter a passphrase (which will likely be weak), we have already transitioned to having them generate a "sync key" (which they can replace if they so choose). This is 20 alphanumeric characters. | Rather than have a user enter a passphrase (which will likely be weak), we have already transitioned to having them generate a "sync key" (which they can replace if they so choose). This is 20 alphanumeric characters. | ||
We propose to expand this to | We propose to expand this to 26 characters, enough for a base32-encoded 128-bit AES key. This avoids the use of PBKDF2 to routinely bootstrap the sync key into an AES key. Remove the ability for users to enter a key; it's always generated (giving us more confidence in the amount of entropy), and can be regenerated if desired. | ||
The length of this key is not a big issue: we intend to use J-PAKE for the (infrequent) migration of keys between devices. In any case, | The length of this key is not a big issue: we intend to use J-PAKE for the (infrequent) migration of keys between devices. In any case, 26 is not significantly worse than 20 if typing it does enter the picture, and the use of a nice base32 alphabet makes keyboard entry less error-prone. | ||
As before, the sync key is stored on the client. | As before, the sync key is stored on the client. | ||
Line 39: | Line 41: | ||
* Spot old version | * Spot old version | ||
* Get a salt ( | * Get a salt (Services.syncID from the meta/global object. The client will be bumping this…) | ||
* Apply PBKDF2 to salt and passphrase to yield our new AES key | * Apply PBKDF2 to salt and passphrase to yield our new AES key | ||
* Generate bulk keys, encrypt | * Generate bulk keys, encrypt | ||
Line 47: | Line 49: | ||
So long as the salt is available, other clients can apply PBKDF2 to their stored passphrase and the salt to yield the new key without any re-entry or J-PAKE-style key distribution. | So long as the salt is available, other clients can apply PBKDF2 to their stored passphrase and the salt to yield the new key without any re-entry or J-PAKE-style key distribution. | ||
The generated | The generated base32 alphanumeric key doesn't actually need to be decoded: it is used as input into hash operations which yield an encryption key and an HMAC key. | ||
('''Note:''' however, for NSS convenience in the future, we intend to decode it. Still pending.) | |||
A known fixed string (which includes encryption algo details) and the username are used as input to SHA256-HMAC. The hash operations are chained. | |||
let m = this.keyStr; | |||
if (m) { | |||
// Reuse the hasher. | |||
let h = Utils.makeHMACHasher(); | |||
// First key. | |||
let u = this.username; | |||
let k1 = Utils.makeHMACKey("" + HMAC_INPUT + u + "\x01"); | |||
let enc = Utils.sha256HMACBytes(m, k1, h); | |||
// Second key: depends on the output of the first run. | |||
let k2 = Utils.makeHMACKey(enc + HMAC_INPUT + u + "\x02"); | |||
let hmac = Utils.sha256HMACBytes(m, k2, h); | |||
// Save them. | |||
this._encrypt = btoa(enc); // WeaveCrypto expects it. | |||
this._hmac = hmac; | |||
} | |||
The outputs are then used during the key encryption process. | The outputs are then used during the key encryption process. | ||
Line 58: | Line 82: | ||
The server stores one or more bulk keys: one default ("keys/default"), and an optional set of keys associated with specific collections. This will allow rudimentary sharing scenarios (provide your bookmarks collection key to a web app, and your passwords remain secure). A default key is simpler than having per-engine/collection keys without an obvious need. | The server stores one or more bulk keys: one default ("keys/default"), and an optional set of keys associated with specific collections. This will allow rudimentary sharing scenarios (provide your bookmarks collection key to a web app, and your passwords remain secure). A default key is simpler than having per-engine/collection keys without an obvious need. | ||
Bulk keys are encrypted and HMACed using the sync key outputs, and cached on the client. ( | Bulk keys are encrypted and HMACed using the sync key outputs, and cached on the client. (Current caching is per-session, but they're stored as identities to make persistence easier to implement.) | ||
The timestamp on the collections record allows clients to invalidate their key cache when a new key is associated with a collection: the 'keys' collection will appear to have changed. | The timestamp on the collections record allows clients to invalidate their key cache when a new key is associated with a collection: the 'keys' collection will appear to have changed. | ||
Line 70: | Line 92: | ||
=== HMAC === | === HMAC === | ||
It's a good practice to use separate keys for HMAC and for encryption. | It's a good practice to use separate keys for HMAC and for encryption. Bulk keys are really pairs of keys, each of which is randomly generated. | ||
This approach was selected over having a single HMAC key because of the convenience for implementing some sharing-like scenarios. | |||
== Proposed flows == | == Proposed flows == | ||
Line 86: | Line 101: | ||
* Generate a 128-bit Sync key (25 characters in base36). Store it as an Identity (as we do now.) | * Generate a 128-bit Sync key (25 characters in base36). Store it as an Identity (as we do now.) | ||
* Generate a random default key. Encrypt it with the sync key, upload it to the server. Store it as an Identity. | * Generate a random default key and HMAC. Encrypt it with the sync key, upload it to the server. Store it as an Identity. | ||
* Encrypt and upload collections in the obvious way. | * Encrypt and upload collections in the obvious way. | ||
Line 98: | Line 113: | ||
* (On startup: invalidate/refresh key cache if keys collection has changed. I believe we make this fetch anyway...) | * (On startup: invalidate/refresh key cache if keys collection has changed. I believe we make this fetch anyway...) | ||
* Retrieve object from collection. | * Retrieve object from collection. | ||
* Look up key for collection name (defaulting to "keys/default"). Fetch if necessary. | * Look up key for collection name (defaulting to "keys/default"). Fetch if necessary. | ||
* Verify HMAC using appropriate per-collection or default key. On failure, check for changed keys. | |||
* Decrypt object. | * Decrypt object. | ||