You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-26 17:03:12 +03:00
sign backups with master key
This commit is contained in:
194
src/client.js
194
src/client.js
@@ -1209,16 +1209,8 @@ MatrixClient.prototype.prepareKeyBackupVersion = async function(password) {
|
|||||||
throw new Error("End-to-end encryption disabled");
|
throw new Error("End-to-end encryption disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
let decryption;
|
const decryption = new global.Olm.PkDecryption();
|
||||||
let encryption;
|
|
||||||
let signing;
|
|
||||||
try {
|
try {
|
||||||
decryption = new global.Olm.PkDecryption();
|
|
||||||
encryption = new global.Olm.PkEncryption();
|
|
||||||
if (global.Olm.PkSigning) {
|
|
||||||
signing = new global.Olm.PkSigning();
|
|
||||||
}
|
|
||||||
|
|
||||||
let publicKey;
|
let publicKey;
|
||||||
const authData = {};
|
const authData = {};
|
||||||
if (password) {
|
if (password) {
|
||||||
@@ -1231,64 +1223,15 @@ MatrixClient.prototype.prepareKeyBackupVersion = async function(password) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
authData.public_key = publicKey;
|
authData.public_key = publicKey;
|
||||||
encryption.set_recipient_key(publicKey);
|
|
||||||
|
|
||||||
const returnInfo = {
|
return {
|
||||||
algorithm: olmlib.MEGOLM_BACKUP_ALGORITHM,
|
algorithm: olmlib.MEGOLM_BACKUP_ALGORITHM,
|
||||||
auth_data: authData,
|
auth_data: authData,
|
||||||
recovery_key: encodeRecoveryKey(decryption.get_private_key()),
|
recovery_key: encodeRecoveryKey(decryption.get_private_key()),
|
||||||
accountKeys: null,
|
accountKeys: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (signing) {
|
|
||||||
await this._cryptoStore.doTxn(
|
|
||||||
'readonly', [IndexedDBCryptoStore.STORE_ACCOUNT],
|
|
||||||
(txn) => {
|
|
||||||
this._cryptoStore.getAccountKeys(txn, (keys) => {
|
|
||||||
returnInfo.accountKeys = keys;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!returnInfo.accountKeys) {
|
|
||||||
const sskSeed = signing.generate_seed();
|
|
||||||
const uskSeed = signing.generate_seed();
|
|
||||||
|
|
||||||
returnInfo.accountKeys = {
|
|
||||||
self_signing_key_seed: Buffer.from(sskSeed).toString('base64'),
|
|
||||||
user_signing_key_seed: Buffer.from(uskSeed).toString('base64'),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// put the encrypted version of the seed in the auth data to upload
|
|
||||||
// XXX: our encryption really should support encrypting binary data.
|
|
||||||
authData.self_signing_key_seed = encryption.encrypt(
|
|
||||||
returnInfo.accountKeys.self_signing_key_seed,
|
|
||||||
);
|
|
||||||
// also keep the public part there
|
|
||||||
returnInfo.ssk_public = signing.init_with_seed(
|
|
||||||
Buffer.from(returnInfo.accountKeys.self_signing_key_seed, 'base64'),
|
|
||||||
);
|
|
||||||
signing.free();
|
|
||||||
|
|
||||||
// same for the USK
|
|
||||||
authData.user_signing_key_seed = encryption.encrypt(
|
|
||||||
returnInfo.accountKeys.user_signing_key_seed,
|
|
||||||
);
|
|
||||||
returnInfo.usk_public = signing.init_with_seed(
|
|
||||||
Buffer.from(returnInfo.accountKeys.user_signing_key_seed, 'base64'),
|
|
||||||
);
|
|
||||||
signing.free();
|
|
||||||
|
|
||||||
// we don't save these keys back to the store yet: we'll do that when (if) we
|
|
||||||
// actually create the backup
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnInfo;
|
|
||||||
} finally {
|
} finally {
|
||||||
if (decryption) decryption.free();
|
decryption.free();
|
||||||
if (encryption) encryption.free();
|
|
||||||
if (signing) signing.free();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1297,11 +1240,9 @@ MatrixClient.prototype.prepareKeyBackupVersion = async function(password) {
|
|||||||
* from prepareKeyBackupVersion.
|
* from prepareKeyBackupVersion.
|
||||||
*
|
*
|
||||||
* @param {object} info Info object from prepareKeyBackupVersion
|
* @param {object} info Info object from prepareKeyBackupVersion
|
||||||
* @param {object} auth Auth object for UI auth
|
|
||||||
* @param {string} replacesSsk If the SSK is being replaced, the ID of the old key
|
|
||||||
* @returns {Promise<object>} Object with 'version' param indicating the version created
|
* @returns {Promise<object>} Object with 'version' param indicating the version created
|
||||||
*/
|
*/
|
||||||
MatrixClient.prototype.createKeyBackupVersion = async function(info, auth, replacesSsk) {
|
MatrixClient.prototype.createKeyBackupVersion = async function(info) {
|
||||||
if (this._crypto === null) {
|
if (this._crypto === null) {
|
||||||
throw new Error("End-to-end encryption disabled");
|
throw new Error("End-to-end encryption disabled");
|
||||||
}
|
}
|
||||||
@@ -1311,73 +1252,25 @@ MatrixClient.prototype.createKeyBackupVersion = async function(info, auth, repla
|
|||||||
auth_data: info.auth_data,
|
auth_data: info.auth_data,
|
||||||
};
|
};
|
||||||
|
|
||||||
const uskInfo = {
|
|
||||||
user_id: this.credentials.userId,
|
|
||||||
usage: ['user_signing'],
|
|
||||||
keys: {
|
|
||||||
['ed25519:' + info.usk_public]: info.usk_public,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// sign the USK with the SSK
|
|
||||||
pkSign(
|
|
||||||
uskInfo,
|
|
||||||
Buffer.from(info.accountKeys.self_signing_key_seed, 'base64'),
|
|
||||||
this.credentials.userId,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Now sig the backup auth data. Do it as this device first because crypto._signObject
|
// Now sig the backup auth data. Do it as this device first because crypto._signObject
|
||||||
// is dumb and bluntly replaces the whole signatures block...
|
// is dumb and bluntly replaces the whole signatures block...
|
||||||
// this can probably go away very soon in favour of just signing with the SSK.
|
// this can probably go away very soon in favour of just signing with the SSK.
|
||||||
await this._crypto._signObject(data.auth_data);
|
await this._crypto._signObject(data.auth_data);
|
||||||
|
|
||||||
// now also sign the auth data with the SSK
|
if (this._crypto._crossSigningInfo.getId()) {
|
||||||
pkSign(
|
// now also sign the auth data with the SSK
|
||||||
data.auth_data,
|
await this._crypto._crossSigningInfo.signObject(data.auth_data, "master");
|
||||||
Buffer.from(info.accountKeys.self_signing_key_seed, 'base64'),
|
}
|
||||||
this.credentials.userId,
|
|
||||||
|
const res = await this._http.authedRequest(
|
||||||
|
undefined, "POST", "/room_keys/version", undefined, data,
|
||||||
);
|
);
|
||||||
|
this.enableKeyBackup({
|
||||||
const keys = {
|
algorithm: info.algorithm,
|
||||||
self_signing_key: {
|
auth_data: info.auth_data,
|
||||||
user_id: this.credentials.userId,
|
version: res.version,
|
||||||
usage: ['self_signing'],
|
|
||||||
keys: {
|
|
||||||
['ed25519:' + info.ssk_public]: info.ssk_public,
|
|
||||||
},
|
|
||||||
replaces: replacesSsk,
|
|
||||||
},
|
|
||||||
user_signing_key: uskInfo,
|
|
||||||
auth,
|
|
||||||
};
|
|
||||||
|
|
||||||
return this._cryptoStore.doTxn(
|
|
||||||
'readwrite', [IndexedDBCryptoStore.STORE_ACCOUNT],
|
|
||||||
(txn) => {
|
|
||||||
// store the newly generated account keys
|
|
||||||
this._cryptoStore.storeAccountKeys(txn, info.accountKeys);
|
|
||||||
},
|
|
||||||
).then(() => {
|
|
||||||
// re-check the SSK in the device store if necessary
|
|
||||||
return this._crypto.checkOwnSskTrust();
|
|
||||||
}).then(() => {
|
|
||||||
// upload the public part of the account keys
|
|
||||||
return this.uploadDeviceSigningKeys(keys);
|
|
||||||
}).then(() => {
|
|
||||||
return this._http.authedRequest(
|
|
||||||
undefined, "POST", "/room_keys/version", undefined, data,
|
|
||||||
);
|
|
||||||
}).then((res) => {
|
|
||||||
this.enableKeyBackup({
|
|
||||||
algorithm: info.algorithm,
|
|
||||||
auth_data: info.auth_data,
|
|
||||||
version: res.version,
|
|
||||||
});
|
|
||||||
return res;
|
|
||||||
}).then(() => {
|
|
||||||
// upload signatures between the SSK & this device
|
|
||||||
return this._crypto.uploadDeviceKeySignatures();
|
|
||||||
});
|
});
|
||||||
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
MatrixClient.prototype.deleteKeyBackupVersion = function(version) {
|
MatrixClient.prototype.deleteKeyBackupVersion = function(version) {
|
||||||
@@ -1483,7 +1376,7 @@ MatrixClient.prototype.restoreKeyBackupWithRecoveryKey = function(
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
MatrixClient.prototype._restoreKeyBackup = async function(
|
MatrixClient.prototype._restoreKeyBackup = function(
|
||||||
privKey, targetRoomId, targetSessionId, backupInfo,
|
privKey, targetRoomId, targetSessionId, backupInfo,
|
||||||
) {
|
) {
|
||||||
if (this._crypto === null) {
|
if (this._crypto === null) {
|
||||||
@@ -1492,46 +1385,14 @@ MatrixClient.prototype._restoreKeyBackup = async function(
|
|||||||
let totalKeyCount = 0;
|
let totalKeyCount = 0;
|
||||||
let keys = [];
|
let keys = [];
|
||||||
|
|
||||||
|
const path = this._makeKeyBackupPath(
|
||||||
|
targetRoomId, targetSessionId, backupInfo.version,
|
||||||
|
);
|
||||||
|
|
||||||
const decryption = new global.Olm.PkDecryption();
|
const decryption = new global.Olm.PkDecryption();
|
||||||
let backupPubKey;
|
let backupPubKey;
|
||||||
try {
|
try {
|
||||||
backupPubKey = decryption.init_with_private_key(privKey);
|
backupPubKey = decryption.init_with_private_key(privKey);
|
||||||
|
|
||||||
// decrypt the account keys from the backup info if there are any
|
|
||||||
// fetch the old ones first so we don't lose info if only one of them is in the backup
|
|
||||||
let accountKeys;
|
|
||||||
await this._cryptoStore.doTxn(
|
|
||||||
'readonly', [IndexedDBCryptoStore.STORE_ACCOUNT],
|
|
||||||
(txn) => {
|
|
||||||
this._cryptoStore.getAccountKeys(txn, (keys) => {
|
|
||||||
accountKeys = keys || {};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (backupInfo.auth_data.self_signing_key_seed) {
|
|
||||||
accountKeys.self_signing_key_seed = decryption.decrypt(
|
|
||||||
backupInfo.auth_data.self_signing_key_seed.ephemeral,
|
|
||||||
backupInfo.auth_data.self_signing_key_seed.mac,
|
|
||||||
backupInfo.auth_data.self_signing_key_seed.ciphertext,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (backupInfo.auth_data.user_signing_key_seed) {
|
|
||||||
accountKeys.user_signing_key_seed = decryption.decrypt(
|
|
||||||
backupInfo.auth_data.user_signing_key_seed.ephemeral,
|
|
||||||
backupInfo.auth_data.user_signing_key_seed.mac,
|
|
||||||
backupInfo.auth_data.user_signing_key_seed.ciphertext,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await this._cryptoStore.doTxn(
|
|
||||||
'readwrite', [IndexedDBCryptoStore.STORE_ACCOUNT],
|
|
||||||
(txn) => {
|
|
||||||
this._cryptoStore.storeAccountKeys(txn, accountKeys);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
await this._crypto.checkOwnSskTrust();
|
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
decryption.free();
|
decryption.free();
|
||||||
throw e;
|
throw e;
|
||||||
@@ -1544,16 +1405,9 @@ MatrixClient.prototype._restoreKeyBackup = async function(
|
|||||||
return Promise.reject({errcode: MatrixClient.RESTORE_BACKUP_ERROR_BAD_KEY});
|
return Promise.reject({errcode: MatrixClient.RESTORE_BACKUP_ERROR_BAD_KEY});
|
||||||
}
|
}
|
||||||
|
|
||||||
// start by signing this device from the SSK now we have it
|
return this._http.authedRequest(
|
||||||
return this._crypto.uploadDeviceKeySignatures().then(() => {
|
undefined, "GET", path.path, path.queryData,
|
||||||
// Now fetch the encrypted keys
|
).then((res) => {
|
||||||
const path = this._makeKeyBackupPath(
|
|
||||||
targetRoomId, targetSessionId, backupInfo.version,
|
|
||||||
);
|
|
||||||
return this._http.authedRequest(
|
|
||||||
undefined, "GET", path.path, path.queryData,
|
|
||||||
);
|
|
||||||
}).then((res) => {
|
|
||||||
if (res.rooms) {
|
if (res.rooms) {
|
||||||
for (const [roomId, roomData] of Object.entries(res.rooms)) {
|
for (const [roomId, roomData] of Object.entries(res.rooms)) {
|
||||||
if (!roomData.sessions) continue;
|
if (!roomData.sessions) continue;
|
||||||
|
|||||||
@@ -40,13 +40,15 @@ async function getPrivateKey(self, type, check) {
|
|||||||
// FIXME: the key needs to be interpreted?
|
// FIXME: the key needs to be interpreted?
|
||||||
const signing = new global.Olm.PkSigning();
|
const signing = new global.Olm.PkSigning();
|
||||||
const pubkey = signing.init_with_seed(key);
|
const pubkey = signing.init_with_seed(key);
|
||||||
error = check(pubkey, signing);
|
// make sure it agrees with the pubkey that we have
|
||||||
if (error) {
|
if (pubkey !== getPublicKey(self.keys[type])[1]) {
|
||||||
|
error = "Key does not match";
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
signing.free();
|
signing.free();
|
||||||
resolve([null, null]);
|
resolve([null, null]);
|
||||||
|
} else {
|
||||||
|
resolve([pubkey, signing]);
|
||||||
}
|
}
|
||||||
resolve([pubkey, signing]);
|
|
||||||
},
|
},
|
||||||
cancel: (error) => {
|
cancel: (error) => {
|
||||||
reject(error || new Error("Cancelled"));
|
reject(error || new Error("Cancelled"));
|
||||||
@@ -131,14 +133,7 @@ export class CrossSigningInfo extends EventEmitter {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
[masterPub, masterSigning] = await getPrivateKey(
|
[masterPub, masterSigning] = await getPrivateKey(this, "master");
|
||||||
this, "master", (pubkey) => {
|
|
||||||
// make sure it agrees with the pubkey that we have
|
|
||||||
if (pubkey !== getPublicKey(this.keys.master)[1]) {
|
|
||||||
return "Key does not match";
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (level & CrossSigningLevel.SELF_SIGNING) {
|
if (level & CrossSigningLevel.SELF_SIGNING) {
|
||||||
@@ -258,21 +253,21 @@ export class CrossSigningInfo extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async signObject(data, type) {
|
||||||
|
const [pubkey, signing] = await getPrivateKey(this, type);
|
||||||
|
try {
|
||||||
|
pkSign(data, signing, this.userId, pubkey);
|
||||||
|
return data;
|
||||||
|
} finally {
|
||||||
|
signing.free();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async signUser(key) {
|
async signUser(key) {
|
||||||
if (!this.keys.user_signing) {
|
if (!this.keys.user_signing) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const [pubkey, usk] = await getPrivateKey(this, "user_signing", (key) => {
|
return this.signObject(key.keys.master, "user_signing");
|
||||||
// FIXME:
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
const otherMaster = key.keys.master;
|
|
||||||
pkSign(otherMaster, usk, this.userId, pubkey);
|
|
||||||
return otherMaster;
|
|
||||||
} finally {
|
|
||||||
usk.free();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async signDevice(userId, device) {
|
async signDevice(userId, device) {
|
||||||
@@ -284,22 +279,14 @@ export class CrossSigningInfo extends EventEmitter {
|
|||||||
if (!this.keys.self_signing) {
|
if (!this.keys.self_signing) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const [pubkey, ssk] = await getPrivateKey(this, "self_signing", (key) => {
|
return this.signObject(
|
||||||
// FIXME:
|
{
|
||||||
return;
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
const keyObj = {
|
|
||||||
algorithms: device.algorithms,
|
algorithms: device.algorithms,
|
||||||
keys: device.keys,
|
keys: device.keys,
|
||||||
device_id: device.deviceId,
|
device_id: device.deviceId,
|
||||||
user_id: userId,
|
user_id: userId,
|
||||||
};
|
}, "self_signing",
|
||||||
pkSign(keyObj, ssk, this.userId, pubkey);
|
);
|
||||||
return keyObj;
|
|
||||||
} finally {
|
|
||||||
ssk.free();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
checkUserTrust(userCrossSigning) {
|
checkUserTrust(userCrossSigning) {
|
||||||
|
|||||||
@@ -513,52 +513,10 @@ Crypto.prototype.checkOwnCrossSigningTrust = async function() {
|
|||||||
this._baseApis.emit("cross-signing.keysChanged", {});
|
this._baseApis.emit("cross-signing.keysChanged", {});
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME:
|
// Now we may be able to trust our key backup
|
||||||
// Now dig out the account keys and get the pubkey of the one in there
|
await this.checkKeyBackup();
|
||||||
/*
|
// FIXME: if we previously trusted the backup, should we automatically sign
|
||||||
let accountKeys = null;
|
// the backup with the new key (if not already signed)?
|
||||||
await this._cryptoStore.doTxn(
|
|
||||||
'readonly', [IndexedDBCryptoStore.STORE_ACCOUNT],
|
|
||||||
(txn) => {
|
|
||||||
this._cryptoStore.getAccountKeys(txn, keys => {
|
|
||||||
accountKeys = keys;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (!accountKeys || !accountKeys.self_signing_key_seed) {
|
|
||||||
logger.info(
|
|
||||||
"Ignoring new self-signing key for us because we have no private part stored",
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let signing;
|
|
||||||
let localPubkey;
|
|
||||||
try {
|
|
||||||
signing = new global.Olm.PkSigning();
|
|
||||||
localPubkey = signing.init_with_seed(
|
|
||||||
Buffer.from(accountKeys.self_signing_key_seed, 'base64'),
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
if (signing) signing.free();
|
|
||||||
signing = null;
|
|
||||||
}
|
|
||||||
if (!localPubkey) {
|
|
||||||
logger.error("Unable to compute public key for stored SSK seed");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, are they the same?
|
|
||||||
if (seenPubkey === localPubkey) {
|
|
||||||
logger.info("Published self-signing key matches local copy: marking as verified");
|
|
||||||
this.setSskVerification(userId, SskInfo.SskVerification.VERIFIED);
|
|
||||||
// Now we may be able to trust our key backup
|
|
||||||
await this.checkKeyBackup();
|
|
||||||
} else {
|
|
||||||
logger.info(
|
|
||||||
"Published self-signing key DOES NOT match local copy! Local: " +
|
|
||||||
localPubkey + ", published: " + seenPubkey,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -686,21 +644,23 @@ Crypto.prototype.isKeyBackupTrusted = async function(backupInfo) {
|
|||||||
// Could be an SSK but just say this is the device ID for backwards compat
|
// Could be an SSK but just say this is the device ID for backwards compat
|
||||||
const sigInfo = { deviceId: keyIdParts[1] }; // XXX: is this how we're supposed to get the device ID?
|
const sigInfo = { deviceId: keyIdParts[1] }; // XXX: is this how we're supposed to get the device ID?
|
||||||
|
|
||||||
// first check to see if it's from our SSK
|
// first check to see if it's from our cross-signing key
|
||||||
const ssk = this._deviceList.getStoredSskForUser(this._userId);
|
const crossSigningId = this._crossSigningInfo.getId();
|
||||||
if (ssk && ssk.getKeyId() === keyId) {
|
if (crossSigningId === keyId) {
|
||||||
sigInfo.self_signing_key = ssk;
|
sigInfo.cross_signing_key = crossSigningId;
|
||||||
try {
|
try {
|
||||||
await olmlib.verifySignature(
|
await olmlib.verifySignature(
|
||||||
this._olmDevice,
|
this._olmDevice,
|
||||||
backupInfo.auth_data,
|
backupInfo.auth_data,
|
||||||
this._userId,
|
this._userId,
|
||||||
sigInfo.deviceId,
|
sigInfo.deviceId,
|
||||||
ssk.getFingerprint(),
|
crossSigningId,
|
||||||
);
|
);
|
||||||
sigInfo.valid = true;
|
sigInfo.valid = true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("Bad signature from ssk " + ssk.getKeyId(), e);
|
logger.warning(
|
||||||
|
"Bad signature from cross signing key " + crossSigningId, e,
|
||||||
|
);
|
||||||
sigInfo.valid = false;
|
sigInfo.valid = false;
|
||||||
}
|
}
|
||||||
ret.sigs.push(sigInfo);
|
ret.sigs.push(sigInfo);
|
||||||
@@ -745,7 +705,7 @@ Crypto.prototype.isKeyBackupTrusted = async function(backupInfo) {
|
|||||||
return (
|
return (
|
||||||
s.valid && (
|
s.valid && (
|
||||||
(s.device && s.device.isVerified()) ||
|
(s.device && s.device.isVerified()) ||
|
||||||
(s.self_signing_key && s.self_signing_key.isVerified())
|
(s.cross_signing_key)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -843,7 +803,6 @@ Crypto.prototype.uploadDeviceKeys = function() {
|
|||||||
user_id: userId,
|
user_id: userId,
|
||||||
};
|
};
|
||||||
|
|
||||||
let accountKeys;
|
|
||||||
return crypto._signObject(deviceKeys).then(() => {
|
return crypto._signObject(deviceKeys).then(() => {
|
||||||
return crypto._baseApis.uploadKeysRequest({
|
return crypto._baseApis.uploadKeysRequest({
|
||||||
device_keys: deviceKeys,
|
device_keys: deviceKeys,
|
||||||
@@ -855,52 +814,6 @@ Crypto.prototype.uploadDeviceKeys = function() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* If a self-signing key is available, uploads the signature of this device from
|
|
||||||
* the self-signing key
|
|
||||||
*
|
|
||||||
* @return {bool} Promise: True if signatures were uploaded or otherwise false
|
|
||||||
* (eg. if no account keys were available)
|
|
||||||
*/
|
|
||||||
Crypto.prototype.uploadDeviceKeySignatures = async function() {
|
|
||||||
const crypto = this;
|
|
||||||
const userId = crypto._userId;
|
|
||||||
const deviceId = crypto._deviceId;
|
|
||||||
|
|
||||||
const thisDeviceKey = {
|
|
||||||
algorithms: crypto._supportedAlgorithms,
|
|
||||||
device_id: deviceId,
|
|
||||||
keys: crypto._deviceKeys,
|
|
||||||
user_id: userId,
|
|
||||||
};
|
|
||||||
let accountKeys;
|
|
||||||
await this._cryptoStore.doTxn(
|
|
||||||
'readonly', [IndexedDBCryptoStore.STORE_ACCOUNT],
|
|
||||||
(txn) => {
|
|
||||||
this._cryptoStore.getAccountKeys(txn, keys => {
|
|
||||||
accountKeys = keys;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
if (!accountKeys || !accountKeys.self_signing_key_seed) return false;
|
|
||||||
|
|
||||||
// Sign this device with the SSK
|
|
||||||
pkSign(
|
|
||||||
thisDeviceKey,
|
|
||||||
Buffer.from(accountKeys.self_signing_key_seed, 'base64'),
|
|
||||||
userId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const content = {
|
|
||||||
[userId]: {
|
|
||||||
[deviceId]: thisDeviceKey,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
await crypto._baseApis.uploadKeySignatures(content);
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the current one_time_key count which will be handled later (in a call of
|
* Stores the current one_time_key count which will be handled later (in a call of
|
||||||
* onSyncCompleted). The count is e.g. coming from a /sync response.
|
* onSyncCompleted). The count is e.g. coming from a /sync response.
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2019 New Vector Ltd
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @module crypto/sskinfo
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Information about a user's self-signing key
|
|
||||||
*
|
|
||||||
* @constructor
|
|
||||||
* @alias module:crypto/sskinfo
|
|
||||||
*
|
|
||||||
* @property {Object.<string,string>} keys a map from
|
|
||||||
* <key type>:<id> -> <base64-encoded key>>
|
|
||||||
*
|
|
||||||
* @property {module:crypto/sskinfo.SskVerification} verified
|
|
||||||
* whether the device has been verified/blocked by the user
|
|
||||||
*
|
|
||||||
* @property {boolean} known
|
|
||||||
* whether the user knows of this device's existence (useful when warning
|
|
||||||
* the user that a user has added new devices)
|
|
||||||
*
|
|
||||||
* @property {Object} unsigned additional data from the homeserver
|
|
||||||
*/
|
|
||||||
export default class SskInfo {
|
|
||||||
constructor() {
|
|
||||||
this.keys = {};
|
|
||||||
this.verified = SskInfo.SskVerification.UNVERIFIED;
|
|
||||||
//this.known = false; // is this useful?
|
|
||||||
this.unsigned = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @enum
|
|
||||||
*/
|
|
||||||
static SskVerification = {
|
|
||||||
VERIFIED: 1,
|
|
||||||
UNVERIFIED: 0,
|
|
||||||
BLOCKED: -1,
|
|
||||||
};
|
|
||||||
|
|
||||||
static fromStorage(obj) {
|
|
||||||
const res = new SskInfo();
|
|
||||||
for (const [prop, val] of Object.entries(obj)) {
|
|
||||||
res[prop] = val;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
getKeyId() {
|
|
||||||
return Object.keys(this.keys)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
getFingerprint() {
|
|
||||||
return Object.values(this.keys)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
isVerified() {
|
|
||||||
return this.verified == SskInfo.SskVerification.VERIFIED;
|
|
||||||
}
|
|
||||||
|
|
||||||
isUnverified() {
|
|
||||||
return this.verified == SskInfo.SskVerification.UNVERIFIED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user