You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-28 05:03:59 +03:00
Sign & verify SSSS keys
This commit is contained in:
@@ -49,6 +49,18 @@ describe("Secrets", function() {
|
||||
const pubkey = decryption.generate_key();
|
||||
const privkey = decryption.get_private_key();
|
||||
|
||||
const signing = new global.Olm.PkSigning();
|
||||
const signingKey = signing.generate_seed();
|
||||
const signingPubKey = signing.init_with_seed(signingKey);
|
||||
|
||||
const signingkeyInfo = {
|
||||
user_id: "@alice:example.com",
|
||||
usage: ['master'],
|
||||
keys: {
|
||||
['ed25519:' + signingPubKey]: signingPubKey,
|
||||
},
|
||||
};
|
||||
|
||||
const getKey = expect.createSpy().andCall(e => {
|
||||
expect(Object.keys(e.keys)).toEqual(["abc"]);
|
||||
return ['abc', privkey];
|
||||
@@ -58,10 +70,15 @@ describe("Secrets", function() {
|
||||
{userId: "@alice:example.com", deviceId: "Osborne2"},
|
||||
{
|
||||
cryptoCallbacks: {
|
||||
getCrossSigningKey: t => signingKey,
|
||||
getSecretStorageKey: getKey,
|
||||
},
|
||||
},
|
||||
);
|
||||
alice._crypto._crossSigningInfo.setKeys({
|
||||
master: signingkeyInfo,
|
||||
});
|
||||
|
||||
const secretStorage = alice._crypto._secretStorage;
|
||||
|
||||
alice.setAccountData = async function(eventType, contents, callback) {
|
||||
@@ -76,13 +93,16 @@ describe("Secrets", function() {
|
||||
}
|
||||
};
|
||||
|
||||
const keyAccountData = {
|
||||
algorithm: "m.secret_storage.v1.curve25519-aes-sha2",
|
||||
pubkey: pubkey,
|
||||
};
|
||||
await alice._crypto._crossSigningInfo.signObject(keyAccountData, 'master');
|
||||
|
||||
alice.store.storeAccountDataEvents([
|
||||
new MatrixEvent({
|
||||
type: "m.secret_storage.key.abc",
|
||||
content: {
|
||||
algorithm: "m.secret_storage.v1.curve25519-aes-sha2",
|
||||
pubkey: pubkey,
|
||||
},
|
||||
content: keyAccountData,
|
||||
}),
|
||||
]);
|
||||
|
||||
@@ -123,8 +143,15 @@ describe("Secrets", function() {
|
||||
});
|
||||
|
||||
it("should encrypt with default key if keys is null", async function() {
|
||||
let keys = {};
|
||||
const alice = await makeTestClient(
|
||||
{userId: "@alice:example.com", deviceId: "Osborne2"},
|
||||
{
|
||||
cryptoCallbacks: {
|
||||
getCrossSigningKey: t => keys[t],
|
||||
saveCrossSigningKeys: k => keys = k,
|
||||
},
|
||||
},
|
||||
);
|
||||
alice.setAccountData = async function(eventType, contents, callback) {
|
||||
alice.store.storeAccountDataEvents([
|
||||
@@ -134,6 +161,7 @@ describe("Secrets", function() {
|
||||
}),
|
||||
]);
|
||||
};
|
||||
alice.resetCrossSigningKeys();
|
||||
|
||||
const newKeyId = await alice.addSecretKey(
|
||||
'm.secret_storage.v1.curve25519-aes-sha2',
|
||||
|
||||
@@ -23,7 +23,7 @@ import {pkSign, pkVerify} from './olmlib';
|
||||
import {EventEmitter} from 'events';
|
||||
import logger from '../logger';
|
||||
|
||||
function getPublicKey(keyInfo) {
|
||||
function publicKeyFromKeyInfo(keyInfo) {
|
||||
return Object.entries(keyInfo.keys)[0];
|
||||
}
|
||||
|
||||
@@ -50,6 +50,14 @@ export class CrossSigningInfo extends EventEmitter {
|
||||
this.firstUse = true;
|
||||
}
|
||||
|
||||
getPublicKey(type) {
|
||||
if (!this.keys[type]) {
|
||||
throw new Error("No " + type + " key present");
|
||||
}
|
||||
const keyInfo = this.keys[type];
|
||||
return publicKeyFromKeyInfo(keyInfo)[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the app callback to ask for a private key
|
||||
* @param {string} type The key type ("master", "self_signing", or "user_signing")
|
||||
@@ -62,7 +70,7 @@ export class CrossSigningInfo extends EventEmitter {
|
||||
}
|
||||
|
||||
if (expectedPubkey === undefined) {
|
||||
expectedPubkey = getPublicKey(this.keys[type])[1];
|
||||
expectedPubkey = this.getPublicKey(type);
|
||||
}
|
||||
|
||||
const privkey = await this._callbacks.getCrossSigningKey(type, expectedPubkey);
|
||||
@@ -109,7 +117,7 @@ export class CrossSigningInfo extends EventEmitter {
|
||||
*/
|
||||
getId(type) {
|
||||
type = type || "master";
|
||||
return this.keys[type] && getPublicKey(this.keys[type])[1];
|
||||
return this.keys[type] && this.getPublicKey(type);
|
||||
}
|
||||
|
||||
async resetKeys(level) {
|
||||
@@ -201,7 +209,7 @@ export class CrossSigningInfo extends EventEmitter {
|
||||
if (!this.keys.master) {
|
||||
// this is the first key we've seen, so first-use is true
|
||||
this.firstUse = true;
|
||||
} else if (getPublicKey(keys.master)[1] !== this.getId()) {
|
||||
} else if (publicKeyFromKeyInfo(keys.master)[1] !== this.getId()) {
|
||||
// this is a different key, so first-use is false
|
||||
this.firstUse = false;
|
||||
} // otherwise, same key, so no change
|
||||
@@ -211,7 +219,7 @@ export class CrossSigningInfo extends EventEmitter {
|
||||
} else {
|
||||
throw new Error("Tried to set cross-signing keys without a master key");
|
||||
}
|
||||
const masterKey = getPublicKey(signingKeys.master)[1];
|
||||
const masterKey = publicKeyFromKeyInfo(signingKeys.master)[1];
|
||||
|
||||
// verify signatures
|
||||
if (keys.user_signing) {
|
||||
@@ -262,6 +270,11 @@ export class CrossSigningInfo extends EventEmitter {
|
||||
}
|
||||
|
||||
async signObject(data, type) {
|
||||
if (!this.keys[type]) {
|
||||
throw new Error(
|
||||
"Attempted to sign with " + type + " key but no such key present",
|
||||
);
|
||||
}
|
||||
const [pubkey, signing] = await this.getCrossSigningKey(type);
|
||||
try {
|
||||
pkSign(data, signing, this.userId, pubkey);
|
||||
@@ -316,7 +329,7 @@ export class CrossSigningInfo extends EventEmitter {
|
||||
|
||||
let userTrusted;
|
||||
const userMaster = userCrossSigning.keys.master;
|
||||
const uskId = getPublicKey(this.keys.user_signing)[1];
|
||||
const uskId = this.getPublicKey('user_signing');
|
||||
try {
|
||||
pkVerify(userMaster, uskId, this.userId);
|
||||
userTrusted = true;
|
||||
@@ -339,7 +352,7 @@ export class CrossSigningInfo extends EventEmitter {
|
||||
const deviceObj = deviceToObject(device, userCrossSigning.userId);
|
||||
try {
|
||||
pkVerify(userSSK, userCrossSigning.getId(), userCrossSigning.userId);
|
||||
pkVerify(deviceObj, getPublicKey(userSSK)[1], userCrossSigning.userId);
|
||||
pkVerify(deviceObj, publicKeyFromKeyInfo(userSSK)[1], userCrossSigning.userId);
|
||||
return userTrust;
|
||||
} catch (e) {
|
||||
return 0;
|
||||
|
||||
@@ -20,18 +20,20 @@ import olmlib from './olmlib';
|
||||
import { randomString } from '../randomstring';
|
||||
import { keyForNewBackup } from './backup_password';
|
||||
import { encodeRecoveryKey } from './recoverykey';
|
||||
import { pkVerify } from './olmlib';
|
||||
|
||||
/**
|
||||
* Implements secret storage and sharing (MSC-1946)
|
||||
* @module crypto/Secrets
|
||||
*/
|
||||
export default class SecretStorage extends EventEmitter {
|
||||
constructor(baseApis, cryptoCallbacks) {
|
||||
constructor(baseApis, cryptoCallbacks, crossSigningInfo) {
|
||||
super();
|
||||
this._baseApis = baseApis;
|
||||
this._cryptoCallbacks = cryptoCallbacks;
|
||||
this._crossSigningInfo = crossSigningInfo;
|
||||
this._requests = {};
|
||||
this._incomingRequests = {};
|
||||
this._cryptoCallbacks = cryptoCallbacks;
|
||||
}
|
||||
|
||||
getDefaultKeyId() {
|
||||
@@ -119,7 +121,7 @@ export default class SecretStorage extends EventEmitter {
|
||||
} while (this._baseApis.getAccountData(`m.secret_storage.key.${keyID}`));
|
||||
}
|
||||
|
||||
// FIXME: sign keyData?
|
||||
await this._crossSigningInfo.signObject(keyData, 'master');
|
||||
|
||||
await this._baseApis.setAccountData(
|
||||
`m.secret_storage.key.${keyID}`, keyData,
|
||||
@@ -162,7 +164,14 @@ export default class SecretStorage extends EventEmitter {
|
||||
throw new Error("Unknown key: " +keyName);
|
||||
}
|
||||
const keyInfoContent = keyInfo.getContent();
|
||||
// FIXME: check signature of key info
|
||||
|
||||
// check signature of key info
|
||||
pkVerify(
|
||||
keyInfoContent,
|
||||
this._crossSigningInfo.getPublicKey('master'),
|
||||
this._crossSigningInfo.userId,
|
||||
);
|
||||
|
||||
// encrypt secret, based on the algorithm
|
||||
switch (keyInfoContent.algorithm) {
|
||||
case "m.secret_storage.v1.curve25519-aes-sha2":
|
||||
@@ -261,6 +270,8 @@ export default class SecretStorage extends EventEmitter {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (checkKey === undefined) checkKey = true;
|
||||
|
||||
const secretContent = secretInfo.getContent();
|
||||
|
||||
if (!secretContent.encrypted) {
|
||||
@@ -276,7 +287,11 @@ export default class SecretStorage extends EventEmitter {
|
||||
).getContent();
|
||||
const encInfo = secretContent.encrypted[keyName];
|
||||
if (checkKey) {
|
||||
// FIXME: check signature on key
|
||||
pkVerify(
|
||||
keyInfo,
|
||||
this._crossSigningInfo.getPublicKey('master'),
|
||||
this._crossSigningInfo.userId,
|
||||
);
|
||||
}
|
||||
switch (keyInfo.algorithm) {
|
||||
case "m.secret_storage.v1.curve25519-aes-sha2":
|
||||
|
||||
@@ -208,7 +208,7 @@ export default function Crypto(baseApis, sessionStore, userId, deviceId,
|
||||
);
|
||||
|
||||
this._secretStorage = new SecretStorage(
|
||||
baseApis, this._baseApis._cryptoCallbacks,
|
||||
baseApis, this._baseApis._cryptoCallbacks, this._crossSigningInfo,
|
||||
);
|
||||
}
|
||||
utils.inherits(Crypto, EventEmitter);
|
||||
|
||||
Reference in New Issue
Block a user