Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
converse.js
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
converse.js
Commits
fa0e7aef
Commit
fa0e7aef
authored
Aug 22, 2018
by
JC Brand
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Updated so that own device is properly created and published
Based on live testing. Updated tests accordingly.
parent
a422f078
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
417 additions
and
325 deletions
+417
-325
dist/converse.js
dist/converse.js
+173
-175
spec/chatbox.js
spec/chatbox.js
+3
-3
spec/omemo.js
spec/omemo.js
+147
-41
spec/presence.js
spec/presence.js
+5
-5
src/converse-omemo.js
src/converse-omemo.js
+88
-100
tests/utils.js
tests/utils.js
+1
-1
No files found.
dist/converse.js
View file @
fa0e7aef
...
...
@@ -63067,9 +63067,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
* that contains the message stanza, if it was
* contained, otherwise it's the message stanza itself.
*/
const _converse = this.__super__._converse,
__ = _converse.__,
archive = sizzle(`result[xmlns="${Strophe.NS.MAM}"]`, original_stanza).pop(),
const archive = sizzle(`result[xmlns="${Strophe.NS.MAM}"]`, original_stanza).pop(),
spoiler = sizzle(`spoiler[xmlns="${Strophe.NS.SPOILER}"]`, original_stanza).pop(),
delay = sizzle(`delay[xmlns="${Strophe.NS.DELAY}"]`, original_stanza).pop(),
chat_state = stanza.getElementsByTagName(_converse.COMPOSING).length && _converse.COMPOSING || stanza.getElementsByTagName(_converse.PAUSED).length && _converse.PAUSED || stanza.getElementsByTagName(_converse.INACTIVE).length && _converse.INACTIVE || stanza.getElementsByTagName(_converse.ACTIVE).length && _converse.ACTIVE || stanza.getElementsByTagName(_converse.GONE).length && _converse.GONE;
...
...
@@ -63775,10 +63773,12 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
emojione.ascii = true;
function onWindowStateChanged(data) {
_converse.chatboxviews.each(function (chatboxview) {
if (_converse.chatboxviews) {
_converse.chatboxviews.each(chatboxview => {
chatboxview.onWindowStateChanged(data.state);
});
}
}
_converse.api.listen.on('windowStateChanged', onWindowStateChanged);
...
...
@@ -66011,11 +66011,11 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
_converse.connection.reset();
_converse.off();
_converse.stopListening();
_converse.tearDown();
_converse.off();
}
if ('onpagehide' in window) {
...
...
@@ -69986,6 +69986,10 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
});
_converse.api.listen.on('afterTearDown', () => {
if (!_converse.chatboxviews) {
return;
}
const container = _converse.chatboxviews.el.querySelector("#converse-modals");
if (container) {
...
...
@@ -74075,6 +74079,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
this.devicelist = _converse.devicelists.get(_converse.bare_jid);
this.devicelist.devices.on('change:bundle', this.debouncedRender, this);
this.devicelist.devices.on('reset', this.debouncedRender, this);
this.devicelist.devices.on('remove', this.debouncedRender, this);
return this.__super__.initialize.apply(this, arguments);
},
...
...
@@ -74131,6 +74136,8 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
});
this.devicelist.devices.on('change:bundle', this.render, this);
this.devicelist.devices.on('change:trusted', this.render, this);
this.devicelist.devices.on('remove', this.render, this);
this.devicelist.devices.on('reset', this.render, this);
return this.__super__.initialize.apply(this, arguments);
},
...
...
@@ -74162,9 +74169,11 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
address = new libsignal.SignalProtocolAddress(device.get('jid'), device.get('id')),
sessionBuilder = new libsignal.SessionBuilder(_converse.omemo_store, address),
prekey = device.getRandomPreKey();
try {
return sessionBuilder.processPreKey({
'registrationId': _converse.omemo_store.get('registration_id'
),
'identityKey': _converse.omemo_store.get('identity_keypair
'),
'registrationId': parseInt(_converse.omemo_store.get('device_id'), 10
),
'identityKey': _converse.omemo_store.get('identity_key
'),
'signedPreKey': {
'keyId': bundle.signed_prekey.id,
// <Number>
...
...
@@ -74177,6 +74186,13 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
'publicKey': u.base64ToArrayBuffer(prekey.key)
}
});
} catch (e) {
_converse.log(`Error: could not build session for device ${device.get('id')}`, Strophe.LogLevel.ERROR);
_converse.log(e.message, Strophe.LogLevel.ERROR);
return Promise.resolve();
}
},
getKeyAndTag(string) {
...
...
@@ -74447,21 +74463,14 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
_converse.NUM_PREKEYS = 100; // Set here so that tests can override
function generateFingerprint(device) {
return new Promise((resolve, reject) => {
device.getBundle().then(bundle => {
if (_.isNil(bundle)) {
resolve();
} // TODO: only generate fingerprints when necessary
crypto.subtle.digest('SHA-1', u.base64ToArrayBuffer(bundle['identity_key'])).then(fp => {
let bundle;
return device.getBundle().then(b => {
bundle = b;
return crypto.subtle.digest('SHA-1', u.base64ToArrayBuffer(bundle['identity_key']));
}).then(fp => {
bundle['fingerprint'] = u.arrayBufferToHex(fp);
device.save('bundle', bundle);
device.trigger('change:bundle'); // Doesn't get triggered automatically due to pass-by-reference
resolve();
}).catch(reject);
});
});
}
...
...
@@ -74470,19 +74479,13 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
};
_converse.getDevicesForContact = function (jid) {
return new Promise((resolve, reject) => {
_converse.api.waitUntil('OMEMOInitialized').then(() => {
let devicelist = _converse.devicelists.get(jid);
if (_.isNil(devicelist)) {
devicelist = _converse.devicelists.create({
let devicelist;
return _converse.api.waitUntil('OMEMOInitialized').then(() => {
devicelist = _converse.devicelists.get(jid) || _converse.devicelists.create({
'jid': jid
});
}
devicelist.fetchDevices().then(() => resolve(devicelist.devices));
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR));
});
return devicelist.fetchDevices();
}).then(() => devicelist.devices);
};
_converse.contactHasOMEMOSupport = function (jid) {
...
...
@@ -74508,35 +74511,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
}
}
return device_id;
}
function generateBundle() {
/* The first thing that needs to happen if a client wants to
* start using OMEMO is they need to generate an IdentityKey
* and a Device ID. The IdentityKey is a Curve25519 [6]
* public/private Key pair. The Device ID is a randomly
* generated integer between 1 and 2^31 - 1.
*/
return new Promise((resolve, reject) => {
libsignal.KeyHelper.generateIdentityKeyPair().then(identity_keypair => {
const data = {
'device_id': generateDeviceID(),
'identity_keypair': identity_keypair,
'prekeys': {}
};
libsignal.KeyHelper.generateSignedPreKey(identity_keypair, 0).then(signed_prekey => {
data['signed_prekey'] = signed_prekey;
const key_promises = _.map(_.range(0, _converse.NUM_PREKEYS), id => libsignal.KeyHelper.generatePreKey(id));
Promise.all(key_promises).then(keys => {
data['prekeys'] = keys;
resolve(data);
});
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR));
});
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR));
return device_id.toString();
}
_converse.OMEMOStore = Backbone.Model.extend({
...
...
@@ -74666,11 +74641,39 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
return Promise.resolve();
},
createNewDeviceBundle() {
return generateBundle().then(data => {
// TODO: should storeSession be used here?
_converse.omemo_store.save(data);
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR));
generateBundle() {
/* The first thing that needs to happen if a client wants to
* start using OMEMO is they need to generate an IdentityKey
* and a Device ID. The IdentityKey is a Curve25519 [6]
* public/private Key pair. The Device ID is a randomly
* generated integer between 1 and 2^31 - 1.
*/
const data = {
'device_id': generateDeviceID()
};
return libsignal.KeyHelper.generateIdentityKeyPair().then(identity_keypair => {
data['identity_keypair'] = identity_keypair;
data['identity_key'] = identity_keypair.pubKey;
return libsignal.KeyHelper.generateSignedPreKey(identity_keypair, 1);
}).then(signed_prekey => {
_converse.omemo_store.storeSignedPreKey(signed_prekey.keyId, signed_prekey.keyPair);
data['signed_prekey'] = signed_prekey;
return Promise.all(_.map(_.range(0, _converse.NUM_PREKEYS), id => libsignal.KeyHelper.generatePreKey(id)));
}).then(keys => {
_.forEach(keys, k => _converse.omemo_store.storePreKey(k.keyId, k.keyPair));
data['prekeys'] = keys;
this.save(data); // Save the bundle to the device
const devicelist = _converse.devicelists.get(_converse.bare_jid),
device = devicelist.devices.create({
'id': data.device_id,
'jid': _converse.bare_jid
});
device.save('bundle', data);
});
},
fetchSession() {
...
...
@@ -74679,13 +74682,13 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
this.fetch({
'success': () => {
if (!_converse.omemo_store.get('device_id')) {
this.
createNewDevic
eBundle().then(resolve).catch(resolve);
this.
generat
eBundle().then(resolve).catch(resolve);
} else {
resolve();
}
},
'error': () => {
this.
createNewDevic
eBundle().then(resolve).catch(resolve);
this.
generat
eBundle().then(resolve).catch(resolve);
}
});
});
...
...
@@ -74760,13 +74763,13 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
this.devices.fetch({
'success': collection => {
if (collection.length === 0) {
this.fetchDevicesFromServer().then(
resolve).catch(reject
);
this.fetchDevicesFromServer().then(
ids => this.publishCurrentDevice(ids)).then(resolve).catch(resolve
);
} else {
resolve();
}
},
'error': () => {
this.fetchDevicesFromServer().then(
resolve).catch(reject
);
this.fetchDevicesFromServer().then(
ids => this.publishCurrentDevice(ids)).then(resolve).catch(resolve
);
}
});
});
...
...
@@ -74775,8 +74778,33 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
return this._devices_promise;
},
publishCurrentDevice(device_ids) {
if (this.get('jid') !== _converse.bare_jid) {
// We only publish for ourselves.
return Promise.resolve();
}
return restoreOMEMOSession().then(() => {
const device_id = _converse.omemo_store.get('device_id');
if (!_.includes(device_ids, device_id)) {
return this.publishDevices();
} else {
const own_device = this.devices.findWhere({
'id': device_id
});
if (!own_device.get('active')) {
own_device.set('active', true, {
'silent': true
});
return this.publishDevices();
}
}
});
},
fetchDevicesFromServer() {
return new Promise((resolve, reject) => {
const stanza = $iq({
'type': 'get',
'from': _converse.bare_jid,
...
...
@@ -74786,15 +74814,15 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
}).c('items', {
'node': Strophe.NS.OMEMO_DEVICELIST
});
return _converse.api.sendIQ(stanza).then(iq => {
const device_ids = _.map(sizzle(`list[xmlns="${Strophe.NS.OMEMO}"] device`, iq), dev => dev.getAttribute('id'));
_converse.connection.sendIQ(stanza, iq => {
_.forEach(sizzle(`list[xmlns="${Strophe.NS.OMEMO}"] device`, iq), dev => this.devices.create({
'id': dev.getAttribute('id'),
_.forEach(device_ids, id => this.devices.create({
'id': id,
'jid': this.get('jid')
}));
resolve();
}, reject, _converse.IQ_TIMEOUT);
return device_ids;
});
},
...
...
@@ -74821,22 +74849,6 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
return _converse.api.sendIQ(stanza);
},
addOwnDevice(device_id) {
/* Add this device to our list of devices stored on the
* server.
* https://xmpp.org/extensions/xep-0384.html#usecases-announcing
*/
if (this.get('jid') !== _converse.bare_jid) {
throw new Error("Cannot add device to someone else's device list");
}
this.devices.create({
'id': device_id.toString(),
'jid': this.get('jid')
});
return this.publishDevices();
},
removeOwnDevices(device_ids) {
if (this.get('jid') !== _converse.bare_jid) {
throw new Error("Cannot remove devices from someone else's device list");
...
...
@@ -74854,7 +74866,6 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
publishBundle() {
const store = _converse.omemo_store,
signed_prekey = store.get('signed_prekey');
return new Promise((resolve, reject) => {
const stanza = $iq({
'from': _converse.bare_jid,
'type': 'set'
...
...
@@ -74874,8 +74885,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
}).t(u.arrayBufferToBase64(prekey.keyPair.pubKey)).up();
});
_converse.connection.sendIQ(stanza, resolve, reject, _converse.IQ_TIMEOUT);
});
return _converse.api.sendIQ(stanza);
}
};
...
...
@@ -74901,28 +74911,6 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
});
}
function updateOwnDeviceList() {
/* If our own device is not on the list, add it.
* Also, deduplicate devices if necessary.
*/
const devicelist = _converse.devicelists.get(_converse.bare_jid),
device_id = _converse.omemo_store.get('device_id').toString(),
own_device = devicelist.devices.findWhere({
'id': device_id
});
if (!own_device) {
return devicelist.addOwnDevice(device_id);
} else if (!own_device.get('active')) {
own_device.set('active', true, {
'silent': true
});
return devicelist.addOwnDevice(device_id);
} else {
return Promise.resolve();
}
}
function updateBundleFromStanza(stanza) {
const items_el = sizzle(`items`, stanza).pop();
...
...
@@ -74977,20 +74965,26 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
'jid': jid
});
}
}); // Make sure our own device is on the list (i.e. if it was
// removed, add it again.
});
updateOwnDeviceList();
if (Strophe.getBareJidFromJid(jid) === _converse.bare_jid) {
// Make sure our own device is on the list (i.e. if it was
// removed, add it again.
_converse.devicelists.get(_converse.bare_jid).publishCurrentDevice(device_ids);
}
}
function registerPEPPushHandler() {
// Add a handler for devices pushed from other connected clients
_converse.connection.addHandler(message => {
if (message.querySelector('event[xmlns="' + Strophe.NS.PUBSUB + '#event"]')) {
try {
if (sizzle(`event[xmlns="${Strophe.NS.PUBSUB}#event"]`, message).length) {
updateDevicesFromStanza(message);
updateBundleFromStanza(message);
}
} catch (e) {
_converse.log(e.message, Strophe.LogLevel.ERROR);
}
return true;
}, null, 'message', 'headline');
...
...
@@ -75012,10 +75006,14 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
const id = `converse.devicelists-${_converse.bare_jid}`;
_converse.devicelists.id = id;
_converse.devicelists.browserStorage = new Backbone.BrowserStorage[_converse.storage](id);
fetchOwnDevices().then(() => restoreOMEMOSession()).then(() =>
updateOwnDeviceList()).then(() =>
_converse.omemo.publishBundle()).then(() => _converse.emit('OMEMOInitialized')).catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR));
fetchOwnDevices().then(() => restoreOMEMOSession()).then(() => _converse.omemo.publishBundle()).then(() => _converse.emit('OMEMOInitialized')).catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR));
}
_converse.api.listen.on('afterTearDown', () => _converse.devicelists.reset());
_converse.api.listen.on('afterTearDown', () => {
_converse.devicelists.reset();
delete _converse.omemo_store;
});
_converse.api.listen.on('connected', registerPEPPushHandler);
...
...
@@ -75023,7 +75021,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
_converse.api.listen.on('statusInitialized', initOMEMO);
_converse.api.listen.on('addClientFeatures', () => _converse.api.disco.own.features.add(
Strophe.NS.OMEMO_DEVICELIST + "notify"
));
_converse.api.listen.on('addClientFeatures', () => _converse.api.disco.own.features.add(
`${Strophe.NS.OMEMO_DEVICELIST}+notify`
));
_converse.api.listen.on('userDetailsModalInitialized', contact => {
const jid = contact.get('jid');
spec/chatbox.js
View file @
fa0e7aef
...
...
@@ -621,10 +621,10 @@
expect
(
view
.
model
.
get
(
'
chat_state
'
)).
toBe
(
'
inactive
'
);
spyOn
(
_converse
.
connection
,
'
send
'
);
view
.
model
.
maximize
();
return
test_utils
.
waitUntil
(()
=>
view
.
model
.
get
(
'
chat_state
'
)
===
'
active
'
,
7
00
);
return
test_utils
.
waitUntil
(()
=>
view
.
model
.
get
(
'
chat_state
'
)
===
'
active
'
,
10
00
);
}).
then
(()
=>
{
expect
(
_converse
.
connection
.
send
).
toHaveBeenCalled
();
var
calls
=
_
.
filter
(
_converse
.
connection
.
send
.
calls
.
all
(),
function
(
call
)
{
const
calls
=
_
.
filter
(
_converse
.
connection
.
send
.
calls
.
all
(),
function
(
call
)
{
return
call
.
args
[
0
]
instanceof
Strophe
.
Builder
;
});
expect
(
calls
.
length
).
toBe
(
1
);
...
...
@@ -635,7 +635,7 @@
expect
(
$stanza
.
children
().
get
(
1
).
tagName
).
toBe
(
'
no-store
'
);
expect
(
$stanza
.
children
().
get
(
2
).
tagName
).
toBe
(
'
no-permanent-store
'
);
done
();
})
;
})
.
catch
(
_
.
partial
(
_converse
.
log
,
_
,
Strophe
.
LogLevel
.
FATAL
))
}));
});
...
...
spec/omemo.js
View file @
fa0e7aef
...
...
@@ -65,10 +65,41 @@
.
c
(
'
device
'
,
{
'
id
'
:
'
482886413b977930064a5888b92134fe
'
});
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
_converse
.
emit
(
'
OMEMOInitialized
'
);
// Check that device list for contact is fetched when chat is opened.
return
test_utils
.
openChatBoxFor
(
_converse
,
contact_jid
);
// Check that own device was published
return
test_utils
.
waitUntil
(()
=>
{
return
_
.
filter
(
_converse
.
connection
.
IQ_stanzas
,
(
iq
)
=>
{
const
node
=
iq
.
nodeTree
.
querySelector
(
'
iq[from="
'
+
_converse
.
bare_jid
+
'
"] publish[node="eu.siacs.conversations.axolotl.devicelist"]
'
);
if
(
node
)
{
iq_stanza
=
iq
.
nodeTree
;}
return
node
;
}).
length
;
});
}).
then
(()
=>
{
const
stanza
=
$iq
({
'
from
'
:
_converse
.
bare_jid
,
'
id
'
:
iq_stanza
.
getAttribute
(
'
id
'
),
'
to
'
:
_converse
.
bare_jid
,
'
type
'
:
'
result
'
});
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
return
test_utils
.
waitUntil
(()
=>
{
return
_
.
filter
(
_converse
.
connection
.
IQ_stanzas
,
function
(
iq
)
{
const
node
=
iq
.
nodeTree
.
querySelector
(
'
publish[node="eu.siacs.conversations.axolotl.bundles:123456789"]
'
);
if
(
node
)
{
iq_stanza
=
iq
.
nodeTree
;
}
return
node
;
}).
length
;
});
}).
then
(()
=>
{
const
stanza
=
$iq
({
'
from
'
:
_converse
.
bare_jid
,
'
id
'
:
iq_stanza
.
getAttribute
(
'
id
'
),
'
to
'
:
_converse
.
bare_jid
,
'
type
'
:
'
result
'
});
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
return
_converse
.
api
.
waitUntil
(
'
OMEMOInitialized
'
);
}).
then
(()
=>
test_utils
.
openChatBoxFor
(
_converse
,
contact_jid
))
.
then
(()
=>
{
return
test_utils
.
waitUntil
(()
=>
{
return
_
.
filter
(
_converse
.
connection
.
IQ_stanzas
,
...
...
@@ -90,7 +121,8 @@
.
c
(
'
list
'
,
{
'
xmlns
'
:
"
eu.siacs.conversations.axolotl
"
})
.
c
(
'
device
'
,
{
'
id
'
:
'
555
'
});
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
return
test_utils
.
waitUntil
(()
=>
_converse
.
omemo_store
);
}).
then
(()
=>
{
const
devicelist
=
_converse
.
devicelists
.
get
({
'
jid
'
:
contact_jid
});
expect
(
devicelist
.
devices
.
length
).
toBe
(
1
);
...
...
@@ -233,7 +265,8 @@
test_utils
.
createContacts
(
_converse
,
'
current
'
,
1
);
const
contact_jid
=
mock
.
cur_names
[
0
].
replace
(
/ /g
,
'
.
'
).
toLowerCase
()
+
'
@localhost
'
;
test_utils
.
waitUntil
(
function
()
{
// Wait until own devices are fetched
test_utils
.
waitUntil
(()
=>
{
return
_
.
filter
(
_converse
.
connection
.
IQ_stanzas
,
(
iq
)
=>
{
...
...
@@ -241,7 +274,7 @@
if
(
node
)
{
iq_stanza
=
iq
.
nodeTree
;}
return
node
;
}).
length
;
}).
then
(
function
()
{
}).
then
(
()
=>
{
expect
(
iq_stanza
.
outerHTML
).
toBe
(
'
<iq type="get" from="dummy@localhost" to="dummy@localhost" xmlns="jabber:client" id="
'
+
iq_stanza
.
getAttribute
(
"
id
"
)
+
'
">
'
+
'
<pubsub xmlns="http://jabber.org/protocol/pubsub">
'
+
...
...
@@ -259,16 +292,48 @@
.
c
(
'
list
'
,
{
'
xmlns
'
:
"
eu.siacs.conversations.axolotl
"
})
.
c
(
'
device
'
,
{
'
id
'
:
'
555
'
});
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
return
test_utils
.
waitUntil
(()
=>
_converse
.
omemo_store
);
}).
then
(()
=>
{
expect
(
_converse
.
devicelists
.
length
).
toBe
(
1
);
const
devicelist
=
_converse
.
devicelists
.
get
(
_converse
.
bare_jid
);
expect
(
devicelist
.
devices
.
length
).
toBe
(
1
);
expect
(
devicelist
.
devices
.
length
).
toBe
(
2
);
expect
(
devicelist
.
devices
.
at
(
0
).
get
(
'
id
'
)).
toBe
(
'
555
'
);
return
test_utils
.
waitUntil
(()
=>
_converse
.
devicelists
);
}).
then
(
function
()
{
// We simply emit, to avoid doing all the setup work
_converse
.
emit
(
'
OMEMOInitialized
'
);
expect
(
devicelist
.
devices
.
at
(
1
).
get
(
'
id
'
)).
toBe
(
'
123456789
'
);
// Check that own device was published
return
test_utils
.
waitUntil
(()
=>
{
return
_
.
filter
(
_converse
.
connection
.
IQ_stanzas
,
(
iq
)
=>
{
const
node
=
iq
.
nodeTree
.
querySelector
(
'
iq[from="
'
+
_converse
.
bare_jid
+
'
"] publish[node="eu.siacs.conversations.axolotl.devicelist"]
'
);
if
(
node
)
{
iq_stanza
=
iq
.
nodeTree
;}
return
node
;
}).
length
;
});
}).
then
(()
=>
{
const
stanza
=
$iq
({
'
from
'
:
_converse
.
bare_jid
,
'
id
'
:
iq_stanza
.
getAttribute
(
'
id
'
),
'
to
'
:
_converse
.
bare_jid
,
'
type
'
:
'
result
'
});
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
return
test_utils
.
waitUntil
(()
=>
{
return
_
.
filter
(
_converse
.
connection
.
IQ_stanzas
,
function
(
iq
)
{
const
node
=
iq
.
nodeTree
.
querySelector
(
'
publish[node="eu.siacs.conversations.axolotl.bundles:123456789"]
'
);
if
(
node
)
{
iq_stanza
=
iq
.
nodeTree
;
}
return
node
;
}).
length
;
});
}).
then
(()
=>
{
const
stanza
=
$iq
({
'
from
'
:
_converse
.
bare_jid
,
'
id
'
:
iq_stanza
.
getAttribute
(
'
id
'
),
'
to
'
:
_converse
.
bare_jid
,
'
type
'
:
'
result
'
});
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
return
_converse
.
api
.
waitUntil
(
'
OMEMOInitialized
'
);
}).
then
(()
=>
{
let
stanza
=
$msg
({
'
from
'
:
contact_jid
,
'
to
'
:
_converse
.
bare_jid
,
...
...
@@ -319,6 +384,7 @@
.
c
(
'
items
'
,
{
'
node
'
:
'
eu.siacs.conversations.axolotl.devicelist
'
})
.
c
(
'
item
'
)
.
c
(
'
list
'
,
{
'
xmlns
'
:
'
eu.siacs.conversations.axolotl
'
})
.
c
(
'
device
'
,
{
'
id
'
:
'
123456789
'
})
.
c
(
'
device
'
,
{
'
id
'
:
'
555
'
})
.
c
(
'
device
'
,
{
'
id
'
:
'
777
'
})
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
...
...
@@ -346,7 +412,8 @@
.
c
(
'
device
'
,
{
'
id
'
:
'
444
'
})
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
return
test_utils
.
waitUntil
(
function
()
{
// Check that own device was published
return
test_utils
.
waitUntil
(()
=>
{
return
_
.
filter
(
_converse
.
connection
.
IQ_stanzas
,
(
iq
)
=>
{
...
...
@@ -355,7 +422,7 @@
return
node
;
}).
length
;
});
}).
then
(
function
()
{
}).
then
(
()
=>
{
// Check that our own device is added again, but that removed
// devices are not added.
expect
(
iq_stanza
.
outerHTML
).
toBe
(
...
...
@@ -394,7 +461,7 @@
test_utils
.
createContacts
(
_converse
,
'
current
'
);
const
contact_jid
=
mock
.
cur_names
[
3
].
replace
(
/ /g
,
'
.
'
).
toLowerCase
()
+
'
@localhost
'
;
test_utils
.
waitUntil
(
function
()
{
test_utils
.
waitUntil
(
()
=>
{
return
_
.
filter
(
_converse
.
connection
.
IQ_stanzas
,
(
iq
)
=>
{
...
...
@@ -402,7 +469,7 @@
if
(
node
)
{
iq_stanza
=
iq
.
nodeTree
;}
return
node
;
}).
length
;
}).
then
(
function
()
{
}).
then
(
()
=>
{
expect
(
iq_stanza
.
outerHTML
).
toBe
(
'
<iq type="get" from="dummy@localhost" to="dummy@localhost" xmlns="jabber:client" id="
'
+
iq_stanza
.
getAttribute
(
"
id
"
)
+
'
">
'
+
'
<pubsub xmlns="http://jabber.org/protocol/pubsub">
'
+
...
...
@@ -421,18 +488,49 @@
.
c
(
'
list
'
,
{
'
xmlns
'
:
"
eu.siacs.conversations.axolotl
"
})
.
c
(
'
device
'
,
{
'
id
'
:
'
555
'
});
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
expect
(
_converse
.
devicelists
.
length
).
toBe
(
1
);
return
test_utils
.
waitUntil
(()
=>
_converse
.
devicelists
);
return
test_utils
.
waitUntil
(()
=>
_converse
.
omemo_store
);
}).
then
(
function
()
{
// We simply emit, to avoid doing all the setup work
expect
(
_converse
.
devicelists
.
length
).
toBe
(
1
);
le
t
devicelist
=
_converse
.
devicelists
.
get
(
_converse
.
bare_jid
);
cons
t
devicelist
=
_converse
.
devicelists
.
get
(
_converse
.
bare_jid
);
expect
(
devicelist
.
devices
.
length
).
toBe
(
2
);
expect
(
devicelist
.
devices
.
at
(
0
).
get
(
'
id
'
)).
toBe
(
'
555
'
);
expect
(
devicelist
.
devices
.
at
(
1
).
get
(
'
id
'
)).
toBe
(
'
123456789
'
);
_converse
.
emit
(
'
OMEMOInitialized
'
);
// Check that own device was published
return
test_utils
.
waitUntil
(()
=>
{
return
_
.
filter
(
_converse
.
connection
.
IQ_stanzas
,
(
iq
)
=>
{
const
node
=
iq
.
nodeTree
.
querySelector
(
'
iq[from="
'
+
_converse
.
bare_jid
+
'
"] publish[node="eu.siacs.conversations.axolotl.devicelist"]
'
);
if
(
node
)
{
iq_stanza
=
iq
.
nodeTree
;}
return
node
;
}).
length
;
});
}).
then
(()
=>
{
const
stanza
=
$iq
({
'
from
'
:
_converse
.
bare_jid
,
'
id
'
:
iq_stanza
.
getAttribute
(
'
id
'
),
'
to
'
:
_converse
.
bare_jid
,
'
type
'
:
'
result
'
});
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
// Check that own bundle gets published
return
test_utils
.
waitUntil
(()
=>
{
return
_
.
filter
(
_converse
.
connection
.
IQ_stanzas
,
(
iq
)
=>
{
const
node
=
iq
.
nodeTree
.
querySelector
(
'
publish[node="eu.siacs.conversations.axolotl.bundles:123456789"]
'
);
if
(
node
)
{
iq_stanza
=
iq
.
nodeTree
;
}
return
node
;
}).
length
;
});
}).
then
(()
=>
{
const
stanza
=
$iq
({
'
from
'
:
_converse
.
bare_jid
,
'
id
'
:
iq_stanza
.
getAttribute
(
'
id
'
),
'
to
'
:
_converse
.
bare_jid
,
'
type
'
:
'
result
'
});
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
return
_converse
.
api
.
waitUntil
(
'
OMEMOInitialized
'
);
}).
then
(()
=>
{
let
stanza
=
$msg
({
'
from
'
:
contact_jid
,
'
to
'
:
_converse
.
bare_jid
,
...
...
@@ -452,7 +550,7 @@
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
expect
(
_converse
.
devicelists
.
length
).
toBe
(
2
);
devicelist
=
_converse
.
devicelists
.
get
(
contact_jid
);
let
devicelist
=
_converse
.
devicelists
.
get
(
contact_jid
);
expect
(
devicelist
.
devices
.
length
).
toBe
(
1
);
let
device
=
devicelist
.
devices
.
at
(
0
);
expect
(
device
.
get
(
'
bundle
'
).
identity_key
).
toBe
(
'
3333
'
);
...
...
@@ -543,7 +641,7 @@
_converse
.
emit
(
'
rosterContactsFetched
'
);
const
contact_jid
=
mock
.
cur_names
[
0
].
replace
(
/ /g
,
'
.
'
).
toLowerCase
()
+
'
@localhost
'
;
test_utils
.
waitUntil
(
function
()
{
test_utils
.
waitUntil
(
()
=>
{
return
_
.
filter
(
_converse
.
connection
.
IQ_stanzas
,
(
iq
)
=>
{
...
...
@@ -551,7 +649,7 @@
if
(
node
)
{
iq_stanza
=
iq
.
nodeTree
;}
return
node
;
}).
length
;
}).
then
(
function
()
{
}).
then
(
()
=>
{
const
stanza
=
$iq
({
'
from
'
:
contact_jid
,
'
id
'
:
iq_stanza
.
getAttribute
(
'
id
'
),
...
...
@@ -589,14 +687,14 @@
return
node
;
}).
length
;
});
}).
then
(
function
()
{
}).
then
(
()
=>
{
expect
(
iq_stanza
.
outerHTML
).
toBe
(
`<iq from="dummy@localhost" type="set" xmlns="jabber:client" id="
${
iq_stanza
.
getAttribute
(
'
id
'
)}
">`
+
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`
+
`<publish node="eu.siacs.conversations.axolotl.bundles:123456789">`
+
`<item>`
+
`<bundle xmlns="eu.siacs.conversations.axolotl">`
+
`<signedPreKeyPublic signedPreKeyId="
0
">
${
btoa
(
'
1234
'
)}
</signedPreKeyPublic>`
+
`<signedPreKeyPublic signedPreKeyId="
1
">
${
btoa
(
'
1234
'
)}
</signedPreKeyPublic>`
+
`<signedPreKeySignature>
${
btoa
(
'
11112222333344445555
'
)}
</signedPreKeySignature>`
+
`<identityKey>
${
btoa
(
'
1234
'
)}
</identityKey>`
+
`<prekeys>`
+
...
...
@@ -646,7 +744,7 @@
'
</iq>
'
);
const
stanza
=
$iq
({
'
from
'
:
contact
_jid
,
'
from
'
:
_converse
.
bare
_jid
,
'
id
'
:
iq_stanza
.
getAttribute
(
'
id
'
),
'
to
'
:
_converse
.
bare_jid
,
'
type
'
:
'
result
'
,
...
...
@@ -656,18 +754,20 @@
.
c
(
'
list
'
,
{
'
xmlns
'
:
"
eu.siacs.conversations.axolotl
"
})
.
c
(
'
device
'
,
{
'
id
'
:
'
482886413b977930064a5888b92134fe
'
});
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
return
test_utils
.
waitUntil
(()
=>
_converse
.
omemo_store
);
}).
then
(()
=>
{
expect
(
_converse
.
devicelists
.
length
).
toBe
(
1
);
const
devicelist
=
_converse
.
devicelists
.
get
(
_converse
.
bare_jid
);
expect
(
devicelist
.
devices
.
length
).
toBe
(
1
);
expect
(
devicelist
.
devices
.
length
).
toBe
(
2
);
expect
(
devicelist
.
devices
.
at
(
0
).
get
(
'
id
'
)).
toBe
(
'
482886413b977930064a5888b92134fe
'
);
return
test_utils
.
openChatBoxFor
(
_converse
,
contact_jid
);
}).
then
(()
=>
{
expect
(
devicelist
.
devices
.
at
(
1
).
get
(
'
id
'
)).
toBe
(
'
123456789
'
);
// Check that own device was published
return
test_utils
.
waitUntil
(()
=>
{
return
_
.
filter
(
_converse
.
connection
.
IQ_stanzas
,
function
(
iq
)
{
const
node
=
iq
.
nodeTree
.
querySelector
(
'
publish[node="eu.siacs.conversations.axolotl.devicelist"]
'
);
if
(
node
)
{
iq_stanza
=
iq
.
nodeTree
;
}
return
_
.
filter
(
_converse
.
connection
.
IQ_stanzas
,
(
iq
)
=>
{
const
node
=
iq
.
nodeTree
.
querySelector
(
'
iq[from="
'
+
_converse
.
bare_jid
+
'
"] publish[node="eu.siacs.conversations.axolotl.devicelist"]
'
);
if
(
node
)
{
iq_stanza
=
iq
.
nodeTree
;}
return
node
;
}).
length
;
});
...
...
@@ -693,8 +793,9 @@
'
type
'
:
'
result
'
});
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
// Check that own bundle gets published
return
test_utils
.
waitUntil
(()
=>
{
return
_
.
filter
(
_converse
.
connection
.
IQ_stanzas
,
function
(
iq
)
{
return
_
.
filter
(
_converse
.
connection
.
IQ_stanzas
,
(
iq
)
=>
{
const
node
=
iq
.
nodeTree
.
querySelector
(
'
publish[node="eu.siacs.conversations.axolotl.bundles:123456789"]
'
);
if
(
node
)
{
iq_stanza
=
iq
.
nodeTree
;
}
return
node
;
...
...
@@ -707,7 +808,7 @@
const
signed_prekeys
=
iq_stanza
.
querySelectorAll
(
'
signedPreKeyPublic
'
);
expect
(
signed_prekeys
.
length
).
toBe
(
1
);
const
signed_prekey
=
signed_prekeys
[
0
];
expect
(
signed_prekey
.
getAttribute
(
'
signedPreKeyId
'
)).
toBe
(
'
0
'
)
expect
(
signed_prekey
.
getAttribute
(
'
signedPreKeyId
'
)).
toBe
(
'
1
'
)
expect
(
iq_stanza
.
querySelectorAll
(
'
signedPreKeySignature
'
).
length
).
toBe
(
1
);
expect
(
iq_stanza
.
querySelectorAll
(
'
identityKey
'
).
length
).
toBe
(
1
);
...
...
@@ -717,7 +818,10 @@
'
to
'
:
_converse
.
bare_jid
,
'
type
'
:
'
result
'
});
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
return
_converse
.
api
.
waitUntil
(
'
OMEMOInitialized
'
,
1000
);
}).
then
(()
=>
{
return
test_utils
.
openChatBoxFor
(
_converse
,
contact_jid
);
}).
then
(()
=>
{
return
test_utils
.
waitUntil
(()
=>
{
return
_
.
filter
(
_converse
.
connection
.
IQ_stanzas
,
...
...
@@ -748,7 +852,9 @@
.
c
(
'
device
'
,
{
'
id
'
:
'
4e30f35051b7b8b42abe083742187228
'
}).
up
()
.
c
(
'
device
'
,
{
'
id
'
:
'
ae890ac52d0df67ed7cfdf51b644e901
'
});
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
const
devicelist
=
_converse
.
devicelists
.
get
(
contact_jid
);
return
test_utils
.
waitUntil
(()
=>
devicelist
.
devices
.
length
);
}).
then
(()
=>
{
expect
(
_converse
.
devicelists
.
length
).
toBe
(
2
);
const
devicelist
=
_converse
.
devicelists
.
get
(
contact_jid
);
expect
(
devicelist
.
devices
.
length
).
toBe
(
4
);
...
...
spec/presence.js
View file @
fa0e7aef
...
...
@@ -47,7 +47,7 @@
"
<presence xmlns='jabber:client'>
"
+
"
<status>Hello world</status>
"
+
"
<priority>0</priority>
"
+
"
<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='
nE765l4CRVrSUEIPAdtgCw4+5cc
='/>
"
+
"
<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='
ggltNSI5YG/7dFKB57Bk2dRYRU0
='/>
"
+
"
</presence>
"
);
_converse
.
priority
=
2
;
...
...
@@ -57,7 +57,7 @@
"
<show>away</show>
"
+
"
<status>Going jogging</status>
"
+
"
<priority>2</priority>
"
+
"
<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='
nE765l4CRVrSUEIPAdtgCw4+5cc
='/>
"
+
"
<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='
ggltNSI5YG/7dFKB57Bk2dRYRU0
='/>
"
+
"
</presence>
"
);
...
...
@@ -68,7 +68,7 @@
"
<show>dnd</show>
"
+
"
<status>Doing taxes</status>
"
+
"
<priority>0</priority>
"
+
"
<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='
nE765l4CRVrSUEIPAdtgCw4+5cc
='/>
"
+
"
<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='
ggltNSI5YG/7dFKB57Bk2dRYRU0
='/>
"
+
"
</presence>
"
);
}));
...
...
@@ -97,7 +97,7 @@
.
toBe
(
"
<presence xmlns='jabber:client'>
"
+
"
<status>My custom status</status>
"
+
"
<priority>0</priority>
"
+
"
<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='
nE765l4CRVrSUEIPAdtgCw4+5cc
='/>
"
+
"
<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='
ggltNSI5YG/7dFKB57Bk2dRYRU0
='/>
"
+
"
</presence>
"
)
return
test_utils
.
waitUntil
(
function
()
{
...
...
@@ -113,7 +113,7 @@
modal
.
el
.
querySelector
(
'
[type="submit"]
'
).
click
();
expect
(
_converse
.
connection
.
send
.
calls
.
mostRecent
().
args
[
0
].
toLocaleString
())
.
toBe
(
"
<presence xmlns='jabber:client'><show>dnd</show><status>My custom status</status><priority>0</priority>
"
+
"
<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='
nE765l4CRVrSUEIPAdtgCw4+5cc
='/>
"
+
"
<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='
ggltNSI5YG/7dFKB57Bk2dRYRU0
='/>
"
+
"
</presence>
"
)
done
();
});
...
...
src/converse-omemo.js
View file @
fa0e7aef
...
...
@@ -177,7 +177,7 @@
try
{
return
sessionBuilder
.
processPreKey
({
'
registrationId
'
:
parseInt
(
_converse
.
omemo_store
.
get
(
'
device_id
'
),
10
),
'
identityKey
'
:
_converse
.
omemo_store
.
get
(
'
identity_key
pair
'
).
pubKey
,
'
identityKey
'
:
_converse
.
omemo_store
.
get
(
'
identity_key
'
)
,
'
signedPreKey
'
:
{
'
keyId
'
:
bundle
.
signed_prekey
.
id
,
// <Number>
'
publicKey
'
:
u
.
base64ToArrayBuffer
(
bundle
.
signed_prekey
.
public_key
),
...
...
@@ -478,16 +478,12 @@
}
_converse
.
getDevicesForContact
=
function
(
jid
)
{
return
new
Promise
((
resolve
,
reject
)
=>
{
_converse
.
api
.
waitUntil
(
'
OMEMOInitialized
'
).
then
(()
=>
{
let
devicelist
=
_converse
.
devicelists
.
get
(
jid
);
if
(
_
.
isNil
(
devicelist
))
{
devicelist
=
_converse
.
devicelists
.
create
({
'
jid
'
:
jid
});
}
devicelist
.
fetchDevices
().
then
(()
=>
resolve
(
devicelist
.
devices
));
}).
catch
(
_
.
partial
(
_converse
.
log
,
_
,
Strophe
.
LogLevel
.
ERROR
));
});
let
devicelist
;
return
_converse
.
api
.
waitUntil
(
'
OMEMOInitialized
'
)
.
then
(()
=>
{
devicelist
=
_converse
.
devicelists
.
get
(
jid
)
||
_converse
.
devicelists
.
create
({
'
jid
'
:
jid
});
return
devicelist
.
fetchDevices
();
}).
then
(()
=>
devicelist
.
devices
);
}
_converse
.
contactHasOMEMOSupport
=
function
(
jid
)
{
...
...
@@ -516,36 +512,6 @@
}
function
generateBundle
()
{
/* The first thing that needs to happen if a client wants to
* start using OMEMO is they need to generate an IdentityKey
* and a Device ID. The IdentityKey is a Curve25519 [6]
* public/private Key pair. The Device ID is a randomly
* generated integer between 1 and 2^31 - 1.
*/
return
new
Promise
((
resolve
,
reject
)
=>
{
libsignal
.
KeyHelper
.
generateIdentityKeyPair
().
then
((
identity_keypair
)
=>
{
const
data
=
{
'
device_id
'
:
generateDeviceID
(),
'
identity_keypair
'
:
identity_keypair
,
'
prekeys
'
:
{}
};
libsignal
.
KeyHelper
.
generateSignedPreKey
(
identity_keypair
,
1
)
.
then
((
signed_prekey
)
=>
{
_converse
.
omemo_store
.
storeSignedPreKey
(
signed_prekey
.
keyId
,
signed_prekey
.
keyPair
);
data
[
'
signed_prekey
'
]
=
signed_prekey
;
const
key_promises
=
_
.
map
(
_
.
range
(
0
,
_converse
.
NUM_PREKEYS
),
id
=>
libsignal
.
KeyHelper
.
generatePreKey
(
id
));
Promise
.
all
(
key_promises
).
then
(
keys
=>
{
_
.
forEach
(
keys
,
k
=>
_converse
.
omemo_store
.
storePreKey
(
k
.
keyId
,
k
.
keyPair
));
data
[
'
prekeys
'
]
=
keys
;
resolve
(
data
)
});
}).
catch
(
_
.
partial
(
_converse
.
log
,
_
,
Strophe
.
LogLevel
.
ERROR
));
});
}).
catch
(
_
.
partial
(
_converse
.
log
,
_
,
Strophe
.
LogLevel
.
ERROR
));
}
_converse
.
OMEMOStore
=
Backbone
.
Model
.
extend
({
Direction
:
{
...
...
@@ -652,19 +618,49 @@
return
Promise
.
resolve
();
},
generateBundle
()
{
/* The first thing that needs to happen if a client wants to
* start using OMEMO is they need to generate an IdentityKey
* and a Device ID. The IdentityKey is a Curve25519 [6]
* public/private Key pair. The Device ID is a randomly
* generated integer between 1 and 2^31 - 1.
*/
const
data
=
{
'
device_id
'
:
generateDeviceID
()
};
return
libsignal
.
KeyHelper
.
generateIdentityKeyPair
()
.
then
(
identity_keypair
=>
{
data
[
'
identity_keypair
'
]
=
identity_keypair
;
data
[
'
identity_key
'
]
=
identity_keypair
.
pubKey
;
return
libsignal
.
KeyHelper
.
generateSignedPreKey
(
identity_keypair
,
1
);
}).
then
(
signed_prekey
=>
{
_converse
.
omemo_store
.
storeSignedPreKey
(
signed_prekey
.
keyId
,
signed_prekey
.
keyPair
);
data
[
'
signed_prekey
'
]
=
signed_prekey
;
return
Promise
.
all
(
_
.
map
(
_
.
range
(
0
,
_converse
.
NUM_PREKEYS
),
id
=>
libsignal
.
KeyHelper
.
generatePreKey
(
id
)));
}).
then
(
keys
=>
{
_
.
forEach
(
keys
,
k
=>
_converse
.
omemo_store
.
storePreKey
(
k
.
keyId
,
k
.
keyPair
));
data
[
'
prekeys
'
]
=
keys
;
this
.
save
(
data
)
// Save the bundle to the device
const
devicelist
=
_converse
.
devicelists
.
get
(
_converse
.
bare_jid
),
device
=
devicelist
.
devices
.
create
({
'
id
'
:
data
.
device_id
,
'
jid
'
:
_converse
.
bare_jid
});
device
.
save
(
'
bundle
'
,
data
);
});
},
fetchSession
()
{
if
(
_
.
isUndefined
(
this
.
_setup_promise
))
{
this
.
_setup_promise
=
new
Promise
((
resolve
,
reject
)
=>
{
this
.
fetch
({
'
success
'
:
()
=>
{
if
(
!
_converse
.
omemo_store
.
get
(
'
device_id
'
))
{
generateBundle
().
then
(
data
=>
resolve
(
this
.
save
(
data
))
).
catch
(
resolve
);
this
.
generateBundle
().
then
(
resolve
).
catch
(
resolve
);
}
else
{
resolve
();
}
},
'
error
'
:
()
=>
{
generateBundle
().
then
(
data
=>
resolve
(
this
.
save
(
data
))
).
catch
(
resolve
);
this
.
generateBundle
().
then
(
resolve
).
catch
(
resolve
);
}
});
});
...
...
@@ -738,13 +734,19 @@
this
.
devices
.
fetch
({
'
success
'
:
(
collection
)
=>
{
if
(
collection
.
length
===
0
)
{
this
.
fetchDevicesFromServer
().
then
(
resolve
).
catch
(
reject
);
this
.
fetchDevicesFromServer
()
.
then
(
ids
=>
this
.
publishCurrentDevice
(
ids
))
.
then
(
resolve
)
.
catch
(
resolve
);
}
else
{
resolve
();
}
},
'
error
'
:
()
=>
{
this
.
fetchDevicesFromServer
().
then
(
resolve
).
catch
(
reject
);
this
.
fetchDevicesFromServer
()
.
then
(
ids
=>
this
.
publishCurrentDevice
(
ids
))
.
then
(
resolve
)
.
catch
(
resolve
)
}
});
});
...
...
@@ -752,25 +754,38 @@
return
this
.
_devices_promise
;
},
publishCurrentDevice
(
device_ids
)
{
if
(
this
.
get
(
'
jid
'
)
!==
_converse
.
bare_jid
)
{
// We only publish for ourselves.
return
Promise
.
resolve
();
}
return
restoreOMEMOSession
()
.
then
(()
=>
{
const
device_id
=
_converse
.
omemo_store
.
get
(
'
device_id
'
);
if
(
!
_
.
includes
(
device_ids
,
device_id
))
{
return
this
.
publishDevices
();
}
else
{
const
own_device
=
this
.
devices
.
findWhere
({
'
id
'
:
device_id
})
if
(
!
own_device
.
get
(
'
active
'
))
{
own_device
.
set
(
'
active
'
,
true
,
{
'
silent
'
:
true
});
return
this
.
publishDevices
();
}
}
});
},
fetchDevicesFromServer
()
{
return
new
Promise
((
resolve
,
reject
)
=>
{
const
stanza
=
$iq
({
'
type
'
:
'
get
'
,
'
from
'
:
_converse
.
bare_jid
,
'
to
'
:
this
.
get
(
'
jid
'
)
}).
c
(
'
pubsub
'
,
{
'
xmlns
'
:
Strophe
.
NS
.
PUBSUB
})
.
c
(
'
items
'
,
{
'
node
'
:
Strophe
.
NS
.
OMEMO_DEVICELIST
});
_converse
.
connection
.
sendIQ
(
stanza
,
(
iq
)
=>
{
_
.
forEach
(
sizzle
(
`list[xmlns="
${
Strophe
.
NS
.
OMEMO
}
"] device`
,
iq
),
(
dev
)
=>
this
.
devices
.
create
({
'
id
'
:
dev
.
getAttribute
(
'
id
'
),
'
jid
'
:
this
.
get
(
'
jid
'
)})
);
resolve
();
},
reject
,
_converse
.
IQ_TIMEOUT
);
return
_converse
.
api
.
sendIQ
(
stanza
)
.
then
(
iq
=>
{
const
device_ids
=
_
.
map
(
sizzle
(
`list[xmlns="
${
Strophe
.
NS
.
OMEMO
}
"] device`
,
iq
),
dev
=>
dev
.
getAttribute
(
'
id
'
));
_
.
forEach
(
device_ids
,
id
=>
this
.
devices
.
create
({
'
id
'
:
id
,
'
jid
'
:
this
.
get
(
'
jid
'
)}));
return
device_ids
;
});
},
...
...
@@ -788,18 +803,6 @@
return
_converse
.
api
.
sendIQ
(
stanza
);
},
addOwnDevice
(
device_id
)
{
/* Add this device to our list of devices stored on the
* server.
* https://xmpp.org/extensions/xep-0384.html#usecases-announcing
*/
if
(
this
.
get
(
'
jid
'
)
!==
_converse
.
bare_jid
)
{
throw
new
Error
(
"
Cannot add device to someone else's device list
"
);
}
this
.
devices
.
create
({
'
id
'
:
device_id
.
toString
(),
'
jid
'
:
this
.
get
(
'
jid
'
)});
return
this
.
publishDevices
();
},
removeOwnDevices
(
device_ids
)
{
if
(
this
.
get
(
'
jid
'
)
!==
_converse
.
bare_jid
)
{
throw
new
Error
(
"
Cannot remove devices from someone else's device list
"
);
...
...
@@ -861,25 +864,6 @@
});
}
function
updateOwnDeviceList
()
{
/* If our own device is not on the list, add it.
* Also, deduplicate devices if necessary.
*/
const
devicelist
=
_converse
.
devicelists
.
get
(
_converse
.
bare_jid
),
device_id
=
_converse
.
omemo_store
.
get
(
'
device_id
'
).
toString
(),
own_device
=
devicelist
.
devices
.
findWhere
({
'
id
'
:
device_id
});
if
(
!
own_device
)
{
return
devicelist
.
addOwnDevice
(
device_id
);
}
else
if
(
!
own_device
.
get
(
'
active
'
))
{
own_device
.
set
(
'
active
'
,
true
,
{
'
silent
'
:
true
});
return
devicelist
.
addOwnDevice
(
device_id
);
}
else
{
return
Promise
.
resolve
();
}
}
function
updateBundleFromStanza
(
stanza
)
{
const
items_el
=
sizzle
(
`items`
,
stanza
).
pop
();
if
(
!
items_el
||
!
items_el
.
getAttribute
(
'
node
'
).
startsWith
(
Strophe
.
NS
.
OMEMO_BUNDLES
))
{
...
...
@@ -916,9 +900,11 @@
devices
.
create
({
'
id
'
:
device_id
,
'
jid
'
:
jid
})
}
});
if
(
Strophe
.
getBareJidFromJid
(
jid
)
===
_converse
.
bare_jid
)
{
// Make sure our own device is on the list (i.e. if it was
// removed, add it again.
updateOwnDeviceList
();
_converse
.
devicelists
.
get
(
_converse
.
bare_jid
).
publishCurrentDevice
(
device_ids
);
}
}
function
registerPEPPushHandler
()
{
...
...
@@ -954,13 +940,15 @@
fetchOwnDevices
()
.
then
(()
=>
restoreOMEMOSession
())
.
then
(()
=>
updateOwnDeviceList
())
.
then
(()
=>
_converse
.
omemo
.
publishBundle
())
.
then
(()
=>
_converse
.
emit
(
'
OMEMOInitialized
'
))
.
catch
(
_
.
partial
(
_converse
.
log
,
_
,
Strophe
.
LogLevel
.
ERROR
));
}
_converse
.
api
.
listen
.
on
(
'
afterTearDown
'
,
()
=>
_converse
.
devicelists
.
reset
());
_converse
.
api
.
listen
.
on
(
'
afterTearDown
'
,
()
=>
{
_converse
.
devicelists
.
reset
();
delete
_converse
.
omemo_store
;
});
_converse
.
api
.
listen
.
on
(
'
connected
'
,
registerPEPPushHandler
);
_converse
.
api
.
listen
.
on
(
'
renderToolbar
'
,
(
view
)
=>
view
.
renderOMEMOToolbarButton
());
_converse
.
api
.
listen
.
on
(
'
statusInitialized
'
,
initOMEMO
);
...
...
tests/utils.js
View file @
fa0e7aef
...
...
@@ -100,7 +100,7 @@
utils
.
openChatBoxFor
=
function
(
_converse
,
jid
)
{
_converse
.
roster
.
get
(
jid
).
trigger
(
"
open
"
);
return
utils
.
waitUntil
(()
=>
_converse
.
chatboxviews
.
get
(
jid
));
return
utils
.
waitUntil
(()
=>
_converse
.
chatboxviews
.
get
(
jid
)
,
1000
);
};
utils
.
openChatRoomViaModal
=
function
(
_converse
,
jid
,
nick
=
''
)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment