Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for passthrough SSSS secrets #1128

Merged
merged 5 commits into from
Dec 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion src/crypto/SecretStorage.js
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,27 @@ export default class SecretStorage extends EventEmitter {
await this._baseApis.setAccountData(name, {encrypted});
}

/**
* Store a secret defined to be the same as the given key.
* No secret information will be stored, instead the secret will
* be stored with a marker to say that the contents of the secret is
* the value of the given key.
* This is useful for migration from systems that predate SSSS such as
* key backup.
*
* @param {string} name The name of the secret
* @param {string} keyId The ID of the key whose value will be the
* value of the secret
* @returns {Promise} resolved when account data is saved
*/
storePassthrough(name, keyId) {
return this._baseApis.setAccountData(name, {
[keyId]: {
passthrough: true,
},
});
}

/**
* Get a secret from storage.
*
Expand Down Expand Up @@ -276,8 +297,13 @@ export default class SecretStorage extends EventEmitter {
// fetch private key from app
[keyId, decryption] = await this._getSecretStorageKey(keys);

// decrypt secret
const encInfo = secretContent.encrypted[keyId];

// We don't actually need the decryption object if it's a passthrough
// since we just want to return the key itself.
if (encInfo.passthrough) return decryption.get_private_key();

// decrypt secret
switch (keys[keyId].algorithm) {
case SECRET_STORAGE_ALGORITHM_V1:
return decryption.decrypt(
Expand Down
46 changes: 40 additions & 6 deletions src/crypto/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -336,13 +336,16 @@ Crypto.prototype.createRecoveryKeyFromPassphrase = async function(password) {
* auth data as an object.
* @param {function} [opts.createSecretStorageKey] Optional. Function
* called to await a secret storage key creation flow.
* @param {object} [opts.keyBackupInfo] The current key backup object. If passed,
* the passphrase and recovery key from this backup will be used.
* Returns:
* {Promise} A promise which resolves to key creation data for
* SecretStorage#addKey: an object with `passphrase` and/or `pubkey` fields.
*/
Crypto.prototype.bootstrapSecretStorage = async function({
authUploadDeviceSigningKeys,
createSecretStorageKey = async () => { },
keyBackupInfo,
} = {}) {
logger.log("Bootstrapping Secure Secret Storage");

Expand Down Expand Up @@ -383,18 +386,49 @@ Crypto.prototype.bootstrapSecretStorage = async function({
{ authUploadDeviceSigningKeys },
);
}
} else {
logger.log("Cross signing keys are present in secret storage");
}

// Check if Secure Secret Storage has a default key. If we don't have one, create
// the default key (which will also be signed by the cross-signing master key).
if (!this.hasSecretStorageKey()) {
logger.log("Secret storage default key not found, creating new key");
const keyOptions = await createSecretStorageKey();
const newKeyId = await this.addSecretStorageKey(
SECRET_STORAGE_ALGORITHM_V1,
keyOptions,
);
let newKeyId;
if (keyBackupInfo) {
logger.log("Secret storage default key not found, using key backup key");
const opts = {
pubkey: keyBackupInfo.auth_data.public_key,
};

if (
keyBackupInfo.auth_data.private_key_salt &&
keyBackupInfo.auth_data.private_key_iterations
) {
opts.passphrase = {
algorithm: "m.pbkdf2",
iterations: keyBackupInfo.auth_data.private_key_iterations,
salt: keyBackupInfo.auth_data.private_key_salt,
};
}

newKeyId = await this.addSecretStorageKey(
SECRET_STORAGE_ALGORITHM_V1, opts,
);

// Add an entry for the backup key in SSSS as a 'passthrough' key
// (ie. the secret is the key itself).
this._secretStorage.storePassthrough('m.megolm_backup.v1', newKeyId);
} else {
logger.log("Secret storage default key not found, creating new key");
const keyOptions = await createSecretStorageKey();
newKeyId = await this.addSecretStorageKey(
SECRET_STORAGE_ALGORITHM_V1,
keyOptions,
);
}
await this.setDefaultSecretStorageKeyId(newKeyId);
} else {
logger.log("Have secret storage key");
}

// If cross-signing keys were reset, store them in Secure Secret Storage.
Expand Down