Commit ea5144c7 authored by JC Brand's avatar JC Brand

Refactor the OMEMOStore to not duplicate prekeys

Before these changes, prekeys were stored in two places, one place that
converse-omemo accessed and one that libsignal accessed and when
libsignal deleted a prekey the other store wasn't updated.

Now we let the methods called by libsignal store/remove prekeys (and the
signed_prekey) in the same place as used by the code in converse-omemo.
parent 5406e973
...@@ -74300,7 +74300,10 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ ...@@ -74300,7 +74300,10 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
'tag': aes_data.tag 'tag': aes_data.tag
})); }));
}).then(plaintext => { }).then(plaintext => {
// TODO remove newly used key before republishing // TODO the prekey should now have been removed.
// Double-check that this is the case and then
// generate a new key to replace it, before
// republishing.
_converse.omemo_store.publishBundle(); _converse.omemo_store.publishBundle();
return _.extend(attrs, { return _.extend(attrs, {
...@@ -74647,54 +74650,81 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ ...@@ -74647,54 +74650,81 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
} }
}, },
loadPreKey(keyId) { getPreKeys() {
let res = this.get('25519KeypreKey' + keyId); return this.get('prekeys') || {};
},
loadPreKey(key_id) {
const res = this.getPreKeys()[key_id];
if (res) { if (res) {
res = { return Promise.resolve({
'privKey': u.base64ToArrayBuffer(res.privKey), 'privKey': u.base64ToArrayBuffer(res.privKey),
'pubKey': u.base64ToArrayBuffer(res.pubKey) 'pubKey': u.base64ToArrayBuffer(res.pubKey)
}; });
} }
return Promise.resolve(res); return Promise.resolve();
}, },
storePreKey(keyId, keyPair) { storePreKey(key_id, key_pair) {
this.save('25519KeypreKey' + keyId, { const prekey = {};
'privKey': u.arrayBufferToBase64(keyPair.privKey), prekey[key_id] = {
'pubKey': u.arrayBufferToBase64(keyPair.pubKey) 'pubKey': u.arrayBufferToBase64(key_pair.pubKey),
}); 'privKey': u.arrayBufferToBase64(key_pair.privKey)
};
this.save('prekeys', _.extend(this.getPreKeys(), prekey));
return Promise.resolve(); return Promise.resolve();
}, },
removePreKey(keyId) { removePreKey(key_id) {
return Promise.resolve(this.unset('25519KeypreKey' + keyId)); this.save('prekeys', _.omit(this.getPreKeys(), key_id));
return Promise.resolve();
}, },
loadSignedPreKey(keyId) { loadSignedPreKey(keyId) {
let res = this.get('25519KeysignedKey' + keyId); const res = this.get('signed_prekey');
if (res) { if (res) {
res = { return Promise.resolve({
'privKey': u.base64ToArrayBuffer(res.privKey), 'privKey': u.base64ToArrayBuffer(res.privKey),
'pubKey': u.base64ToArrayBuffer(res.pubKey) 'pubKey': u.base64ToArrayBuffer(res.pubKey)
}; });
} }
return Promise.resolve(res); return Promise.resolve();
}, },
storeSignedPreKey(keyId, keyPair) { storeSignedPreKey(spk) {
this.save('25519KeysignedKey' + keyId, { if (typeof spk !== "object") {
'privKey': u.arrayBufferToBase64(keyPair.privKey), // XXX: We've changed the signature of this method from the
'pubKey': u.arrayBufferToBase64(keyPair.pubKey) // example given in InMemorySignalProtocolStore.
// Should be fine because the libsignal code doesn't
// actually call this method.
throw new Error("storeSignedPreKey: expected an object");
}
this.save('signed_prekey', {
'id': spk.keyId,
'privKey': u.arrayBufferToBase64(spk.keyPair.privKey),
'pubKey': u.arrayBufferToBase64(spk.keyPair.pubKey),
// XXX: The InMemorySignalProtocolStore does not pass
// in or store the signature, but we need it when we
// publish out bundle and this method isn't called from
// within libsignal code, so we modify it to also store
// the signature.
'signature': u.arrayBufferToBase64(spk.signature)
}); });
return Promise.resolve(); return Promise.resolve();
}, },
removeSignedPreKey(keyId) { removeSignedPreKey(key_id) {
return Promise.resolve(this.unset('25519KeysignedKey' + keyId)); if (this.get('signed_prekey')['id'] === key_id) {
this.unset('signed_prekey');
this.save();
}
return Promise.resolve();
}, },
loadSession(identifier) { loadSession(identifier) {
...@@ -74738,12 +74768,12 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ ...@@ -74738,12 +74768,12 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
}).c('item').c('bundle', { }).c('item').c('bundle', {
'xmlns': Strophe.NS.OMEMO 'xmlns': Strophe.NS.OMEMO
}).c('signedPreKeyPublic', { }).c('signedPreKeyPublic', {
'signedPreKeyId': signed_prekey.keyId 'signedPreKeyId': signed_prekey.id
}).t(signed_prekey.keyPair.pubKey).up().c('signedPreKeySignature').t(signed_prekey.signature).up().c('identityKey').t(this.get('identity_keypair').pubKey).up().c('prekeys'); }).t(signed_prekey.pubKey).up().c('signedPreKeySignature').t(signed_prekey.signature).up().c('identityKey').t(this.get('identity_keypair').pubKey).up().c('prekeys');
_.forEach(this.get('prekeys').slice(0, _converse.NUM_PREKEYS), prekey => stanza.c('preKeyPublic', { _.forEach(this.get('prekeys'), (prekey, id) => stanza.c('preKeyPublic', {
'preKeyId': prekey.keyId 'preKeyId': id
}).t(prekey.keyPair.pubKey).up()); }).t(prekey.pubKey).up());
return _converse.api.sendIQ(stanza); return _converse.api.sendIQ(stanza);
}, },
...@@ -74755,52 +74785,45 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ ...@@ -74755,52 +74785,45 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
* public/private Key pair. The Device ID is a randomly * public/private Key pair. The Device ID is a randomly
* generated integer between 1 and 2^31 - 1. * generated integer between 1 and 2^31 - 1.
*/ */
const data = { const bundle = {};
'device_id': generateDeviceID()
};
return libsignal.KeyHelper.generateIdentityKeyPair().then(identity_keypair => { return libsignal.KeyHelper.generateIdentityKeyPair().then(identity_keypair => {
const identity_key = u.arrayBufferToBase64(identity_keypair.pubKey); const identity_key = u.arrayBufferToBase64(identity_keypair.pubKey),
data['identity_keypair'] = { device_id = generateDeviceID();
'privKey': u.arrayBufferToBase64(identity_keypair.privKey), bundle['identity_key'] = identity_key;
'pubKey': identity_key bundle['device_id'] = device_id;
}; this.save({
data['identity_key'] = identity_key; 'device_id': device_id,
return libsignal.KeyHelper.generateSignedPreKey(identity_keypair, 1); 'identity_keypair': {
'privKey': u.arrayBufferToBase64(identity_keypair.privKey),
'pubKey': identity_key
},
'identity_key': identity_key
});
return libsignal.KeyHelper.generateSignedPreKey(identity_keypair, 0);
}).then(signed_prekey => { }).then(signed_prekey => {
_converse.omemo_store.storeSignedPreKey(signed_prekey.keyId, signed_prekey.keyPair); _converse.omemo_store.storeSignedPreKey(signed_prekey);
data['signed_prekey'] = { bundle['signed_prekey'] = {
'keyId': signed_prekey.keyId, 'id': signed_prekey.keyId,
'keyPair': { 'public_key': u.arrayBufferToBase64(signed_prekey.keyPair.privKey),
'privKey': u.arrayBufferToBase64(signed_prekey.keyPair.privKey),
'pubKey': u.arrayBufferToBase64(signed_prekey.keyPair.pubKey)
},
'signature': u.arrayBufferToBase64(signed_prekey.signature) 'signature': u.arrayBufferToBase64(signed_prekey.signature)
}; };
return Promise.all(_.map(_.range(0, _converse.NUM_PREKEYS), id => libsignal.KeyHelper.generatePreKey(id))); return Promise.all(_.map(_.range(0, _converse.NUM_PREKEYS), id => libsignal.KeyHelper.generatePreKey(id)));
}).then(keys => { }).then(keys => {
_.forEach(keys, k => _converse.omemo_store.storePreKey(k.keyId, k.keyPair)); _.forEach(keys, k => _converse.omemo_store.storePreKey(k.keyId, k.keyPair));
const marshalled_keys = _.map(keys, k => {
return {
'keyId': k.keyId,
'keyPair': {
'pubKey': u.arrayBufferToBase64(k.keyPair.pubKey),
'privKey': u.arrayBufferToBase64(k.keyPair.privKey)
}
};
});
data['prekeys'] = marshalled_keys;
this.save(data); // Save the bundle to the device
const devicelist = _converse.devicelists.get(_converse.bare_jid), const devicelist = _converse.devicelists.get(_converse.bare_jid),
device = devicelist.devices.create({ device = devicelist.devices.create({
'id': data.device_id, 'id': bundle.device_id,
'jid': _converse.bare_jid 'jid': _converse.bare_jid
}); }),
marshalled_keys = _.map(keys, k => ({
'id': k.keyId,
'key': u.arrayBufferToBase64(k.keyPair.pubKey)
}));
device.save('bundle', data); bundle['prekeys'] = marshalled_keys;
device.save('bundle', bundle);
}); });
}, },
...@@ -682,7 +682,7 @@ ...@@ -682,7 +682,7 @@
`<publish node="eu.siacs.conversations.axolotl.bundles:123456789">`+ `<publish node="eu.siacs.conversations.axolotl.bundles:123456789">`+
`<item>`+ `<item>`+
`<bundle xmlns="eu.siacs.conversations.axolotl">`+ `<bundle xmlns="eu.siacs.conversations.axolotl">`+
`<signedPreKeyPublic signedPreKeyId="1">${btoa('1234')}</signedPreKeyPublic>`+ `<signedPreKeyPublic signedPreKeyId="0">${btoa('1234')}</signedPreKeyPublic>`+
`<signedPreKeySignature>${btoa('11112222333344445555')}</signedPreKeySignature>`+ `<signedPreKeySignature>${btoa('11112222333344445555')}</signedPreKeySignature>`+
`<identityKey>${btoa('1234')}</identityKey>`+ `<identityKey>${btoa('1234')}</identityKey>`+
`<prekeys>`+ `<prekeys>`+
...@@ -796,7 +796,7 @@ ...@@ -796,7 +796,7 @@
const signed_prekeys = iq_stanza.querySelectorAll('signedPreKeyPublic'); const signed_prekeys = iq_stanza.querySelectorAll('signedPreKeyPublic');
expect(signed_prekeys.length).toBe(1); expect(signed_prekeys.length).toBe(1);
const signed_prekey = signed_prekeys[0]; const signed_prekey = signed_prekeys[0];
expect(signed_prekey.getAttribute('signedPreKeyId')).toBe('1') expect(signed_prekey.getAttribute('signedPreKeyId')).toBe('0')
expect(iq_stanza.querySelectorAll('signedPreKeySignature').length).toBe(1); expect(iq_stanza.querySelectorAll('signedPreKeySignature').length).toBe(1);
expect(iq_stanza.querySelectorAll('identityKey').length).toBe(1); expect(iq_stanza.querySelectorAll('identityKey').length).toBe(1);
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment