1
0
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:
David Baker
2019-11-13 17:52:24 +00:00
parent 1798f3921f
commit 7218e31a9c
4 changed files with 73 additions and 17 deletions

View File

@@ -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',

View File

@@ -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;

View File

@@ -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":

View File

@@ -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);