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 pubkey = decryption.generate_key();
const privkey = decryption.get_private_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 => { const getKey = expect.createSpy().andCall(e => {
expect(Object.keys(e.keys)).toEqual(["abc"]); expect(Object.keys(e.keys)).toEqual(["abc"]);
return ['abc', privkey]; return ['abc', privkey];
@@ -58,10 +70,15 @@ describe("Secrets", function() {
{userId: "@alice:example.com", deviceId: "Osborne2"}, {userId: "@alice:example.com", deviceId: "Osborne2"},
{ {
cryptoCallbacks: { cryptoCallbacks: {
getCrossSigningKey: t => signingKey,
getSecretStorageKey: getKey, getSecretStorageKey: getKey,
}, },
}, },
); );
alice._crypto._crossSigningInfo.setKeys({
master: signingkeyInfo,
});
const secretStorage = alice._crypto._secretStorage; const secretStorage = alice._crypto._secretStorage;
alice.setAccountData = async function(eventType, contents, callback) { 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([ alice.store.storeAccountDataEvents([
new MatrixEvent({ new MatrixEvent({
type: "m.secret_storage.key.abc", type: "m.secret_storage.key.abc",
content: { content: keyAccountData,
algorithm: "m.secret_storage.v1.curve25519-aes-sha2",
pubkey: pubkey,
},
}), }),
]); ]);
@@ -123,8 +143,15 @@ describe("Secrets", function() {
}); });
it("should encrypt with default key if keys is null", async function() { it("should encrypt with default key if keys is null", async function() {
let keys = {};
const alice = await makeTestClient( const alice = await makeTestClient(
{userId: "@alice:example.com", deviceId: "Osborne2"}, {userId: "@alice:example.com", deviceId: "Osborne2"},
{
cryptoCallbacks: {
getCrossSigningKey: t => keys[t],
saveCrossSigningKeys: k => keys = k,
},
},
); );
alice.setAccountData = async function(eventType, contents, callback) { alice.setAccountData = async function(eventType, contents, callback) {
alice.store.storeAccountDataEvents([ alice.store.storeAccountDataEvents([
@@ -134,6 +161,7 @@ describe("Secrets", function() {
}), }),
]); ]);
}; };
alice.resetCrossSigningKeys();
const newKeyId = await alice.addSecretKey( const newKeyId = await alice.addSecretKey(
'm.secret_storage.v1.curve25519-aes-sha2', 'm.secret_storage.v1.curve25519-aes-sha2',

View File

@@ -23,7 +23,7 @@ import {pkSign, pkVerify} from './olmlib';
import {EventEmitter} from 'events'; import {EventEmitter} from 'events';
import logger from '../logger'; import logger from '../logger';
function getPublicKey(keyInfo) { function publicKeyFromKeyInfo(keyInfo) {
return Object.entries(keyInfo.keys)[0]; return Object.entries(keyInfo.keys)[0];
} }
@@ -50,6 +50,14 @@ export class CrossSigningInfo extends EventEmitter {
this.firstUse = true; 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 * Calls the app callback to ask for a private key
* @param {string} type The key type ("master", "self_signing", or "user_signing") * @param {string} type The key type ("master", "self_signing", or "user_signing")
@@ -62,7 +70,7 @@ export class CrossSigningInfo extends EventEmitter {
} }
if (expectedPubkey === undefined) { if (expectedPubkey === undefined) {
expectedPubkey = getPublicKey(this.keys[type])[1]; expectedPubkey = this.getPublicKey(type);
} }
const privkey = await this._callbacks.getCrossSigningKey(type, expectedPubkey); const privkey = await this._callbacks.getCrossSigningKey(type, expectedPubkey);
@@ -109,7 +117,7 @@ export class CrossSigningInfo extends EventEmitter {
*/ */
getId(type) { getId(type) {
type = type || "master"; type = type || "master";
return this.keys[type] && getPublicKey(this.keys[type])[1]; return this.keys[type] && this.getPublicKey(type);
} }
async resetKeys(level) { async resetKeys(level) {
@@ -201,7 +209,7 @@ export class CrossSigningInfo extends EventEmitter {
if (!this.keys.master) { if (!this.keys.master) {
// this is the first key we've seen, so first-use is true // this is the first key we've seen, so first-use is true
this.firstUse = 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 is a different key, so first-use is false
this.firstUse = false; this.firstUse = false;
} // otherwise, same key, so no change } // otherwise, same key, so no change
@@ -211,7 +219,7 @@ export class CrossSigningInfo extends EventEmitter {
} else { } else {
throw new Error("Tried to set cross-signing keys without a master key"); 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 // verify signatures
if (keys.user_signing) { if (keys.user_signing) {
@@ -262,6 +270,11 @@ export class CrossSigningInfo extends EventEmitter {
} }
async signObject(data, type) { 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); const [pubkey, signing] = await this.getCrossSigningKey(type);
try { try {
pkSign(data, signing, this.userId, pubkey); pkSign(data, signing, this.userId, pubkey);
@@ -316,7 +329,7 @@ export class CrossSigningInfo extends EventEmitter {
let userTrusted; let userTrusted;
const userMaster = userCrossSigning.keys.master; const userMaster = userCrossSigning.keys.master;
const uskId = getPublicKey(this.keys.user_signing)[1]; const uskId = this.getPublicKey('user_signing');
try { try {
pkVerify(userMaster, uskId, this.userId); pkVerify(userMaster, uskId, this.userId);
userTrusted = true; userTrusted = true;
@@ -339,7 +352,7 @@ export class CrossSigningInfo extends EventEmitter {
const deviceObj = deviceToObject(device, userCrossSigning.userId); const deviceObj = deviceToObject(device, userCrossSigning.userId);
try { try {
pkVerify(userSSK, userCrossSigning.getId(), userCrossSigning.userId); pkVerify(userSSK, userCrossSigning.getId(), userCrossSigning.userId);
pkVerify(deviceObj, getPublicKey(userSSK)[1], userCrossSigning.userId); pkVerify(deviceObj, publicKeyFromKeyInfo(userSSK)[1], userCrossSigning.userId);
return userTrust; return userTrust;
} catch (e) { } catch (e) {
return 0; return 0;

View File

@@ -20,18 +20,20 @@ import olmlib from './olmlib';
import { randomString } from '../randomstring'; import { randomString } from '../randomstring';
import { keyForNewBackup } from './backup_password'; import { keyForNewBackup } from './backup_password';
import { encodeRecoveryKey } from './recoverykey'; import { encodeRecoveryKey } from './recoverykey';
import { pkVerify } from './olmlib';
/** /**
* Implements secret storage and sharing (MSC-1946) * Implements secret storage and sharing (MSC-1946)
* @module crypto/Secrets * @module crypto/Secrets
*/ */
export default class SecretStorage extends EventEmitter { export default class SecretStorage extends EventEmitter {
constructor(baseApis, cryptoCallbacks) { constructor(baseApis, cryptoCallbacks, crossSigningInfo) {
super(); super();
this._baseApis = baseApis; this._baseApis = baseApis;
this._cryptoCallbacks = cryptoCallbacks;
this._crossSigningInfo = crossSigningInfo;
this._requests = {}; this._requests = {};
this._incomingRequests = {}; this._incomingRequests = {};
this._cryptoCallbacks = cryptoCallbacks;
} }
getDefaultKeyId() { getDefaultKeyId() {
@@ -119,7 +121,7 @@ export default class SecretStorage extends EventEmitter {
} while (this._baseApis.getAccountData(`m.secret_storage.key.${keyID}`)); } while (this._baseApis.getAccountData(`m.secret_storage.key.${keyID}`));
} }
// FIXME: sign keyData? await this._crossSigningInfo.signObject(keyData, 'master');
await this._baseApis.setAccountData( await this._baseApis.setAccountData(
`m.secret_storage.key.${keyID}`, keyData, `m.secret_storage.key.${keyID}`, keyData,
@@ -162,7 +164,14 @@ export default class SecretStorage extends EventEmitter {
throw new Error("Unknown key: " +keyName); throw new Error("Unknown key: " +keyName);
} }
const keyInfoContent = keyInfo.getContent(); 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 // encrypt secret, based on the algorithm
switch (keyInfoContent.algorithm) { switch (keyInfoContent.algorithm) {
case "m.secret_storage.v1.curve25519-aes-sha2": case "m.secret_storage.v1.curve25519-aes-sha2":
@@ -261,6 +270,8 @@ export default class SecretStorage extends EventEmitter {
return false; return false;
} }
if (checkKey === undefined) checkKey = true;
const secretContent = secretInfo.getContent(); const secretContent = secretInfo.getContent();
if (!secretContent.encrypted) { if (!secretContent.encrypted) {
@@ -276,7 +287,11 @@ export default class SecretStorage extends EventEmitter {
).getContent(); ).getContent();
const encInfo = secretContent.encrypted[keyName]; const encInfo = secretContent.encrypted[keyName];
if (checkKey) { if (checkKey) {
// FIXME: check signature on key pkVerify(
keyInfo,
this._crossSigningInfo.getPublicKey('master'),
this._crossSigningInfo.userId,
);
} }
switch (keyInfo.algorithm) { switch (keyInfo.algorithm) {
case "m.secret_storage.v1.curve25519-aes-sha2": 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( this._secretStorage = new SecretStorage(
baseApis, this._baseApis._cryptoCallbacks, baseApis, this._baseApis._cryptoCallbacks, this._crossSigningInfo,
); );
} }
utils.inherits(Crypto, EventEmitter); utils.inherits(Crypto, EventEmitter);