Browse Source

Handle SignedPrekeyId properly

Fix Session building when sending the first message
Various other fixes and cleanup
pull/983/head
Timothée Jaussoin 4 years ago
parent
commit
5a9f202d2b
  1. 11
      app/Bundle.php
  2. 2
      app/MessageOmemoHeader.php
  3. 16
      app/widgets/Chat/chat.js
  4. 5
      app/widgets/ChatOmemo/ChatOmemo.php
  5. 32
      app/widgets/ChatOmemo/chatomemo.js
  6. 24
      app/widgets/ChatOmemo/chatomemo_storage.js
  7. 2
      app/widgets/ContactActions/_contactactions_drawer.tpl
  8. 6
      database/migrations/20210526164828_create_bundles_table.php

11
app/Bundle.php

@ -20,8 +20,10 @@ class Bundle extends Model
$this->jid = $jid;
$this->bundle_id = $bundleId;
$this->prekeypublic = (string)$bundle->signedPreKeyPublic;
$this->prekeysignature = (string)$bundle->signedPreKeySignature;
$this->signedprekeypublic = (string)$bundle->signedPreKeyPublic;
$this->signedprekeyid = (int)$bundle->signedPreKeyPublic->attributes()->signedPreKeyId;
$this->signedprekeysignature = (string)$bundle->signedPreKeySignature;
$this->identitykey = (string)$bundle->identityKey;
$prekeys = [];
@ -38,8 +40,9 @@ class Bundle extends Model
return (
isset($this->attributes['prekeys'])
&& $this->attributes['prekeys'] == $bundle->attributes['prekeys']
&& $this->attributes['prekeypublic'] == $bundle->attributes['prekeypublic']
&& $this->attributes['prekeysignature'] == $bundle->attributes['prekeysignature']
&& $this->attributes['signedprekeypublic'] == $bundle->attributes['signedprekeypublic']
&& $this->attributes['signedprekeyid'] == $bundle->attributes['signedprekeyid']
&& $this->attributes['signedprekeysignature'] == $bundle->attributes['signedprekeysignature']
&& $this->attributes['identitykey'] == $bundle->attributes['identitykey']
);
}

2
app/MessageOmemoHeader.php

@ -19,7 +19,7 @@ class MessageOmemoHeader
public function set($stanza)
{
$this->sid = (string)$stanza->encrypted->header->attributes()->sid;
$this->sid = (int)$stanza->encrypted->header->attributes()->sid;
$this->iv = (string)$stanza->encrypted->header->iv;
$this->payload = (string)$stanza->encrypted->payload;

16
app/widgets/Chat/chat.js

@ -136,8 +136,8 @@ var Chat = {
sendMessage: function()
{
var textarea = Chat.getTextarea();
var text = textarea.value;
var muc = Boolean(textarea.dataset.muc);
var mucReceipts = false;
var jid = textarea.dataset.jid;
@ -233,8 +233,8 @@ var Chat = {
sessions = Object.values(sessions);
sessions = sessions.map(devices => devices.includes(String(localDeviceId)));
ChatOmemo.getContactState(jid).then(enabled => {
let state = 'no';
ChatOmemo.getContactState(jid).then(enabled => {
let state = 'no';
if (enabled) {
/**
@ -243,18 +243,16 @@ var Chat = {
* 2 some sessions need to be built, build then encrypt
*/
if (sessions.length > 0) {
if (sessions.every(good => good)) {
state = 'yes';
} else {
state = 'build';
}
state = (sessions.every(good => good))
? 'yes'
: 'build';
}
} else {
state = 'disabled';
}
Chat.setOmemoState(state);
});
});
});
},
setOmemoState: function(state)

5
app/widgets/ChatOmemo/ChatOmemo.php

@ -133,8 +133,9 @@ class ChatOmemo extends \Movim\Widget\Base
$pickedKey = array_rand($bundle->prekeys);
return [
'identitykey' => $bundle->identitykey,
'prekeypublic' => $bundle->prekeypublic,
'prekeysignature' => $bundle->prekeysignature,
'signedprekeypublic' => $bundle->signedprekeypublic,
'signedprekeyid' => $bundle->signedprekeyid,
'signedprekeysignature' => $bundle->signedprekeysignature,
'prekey' => ['id' => $pickedKey, 'value' => $bundle->prekeys[$pickedKey]]
];
}

32
app/widgets/ChatOmemo/chatomemo.js

@ -19,12 +19,11 @@ var ChatOmemo = {
const identityKeyPair = await KeyHelper.generateIdentityKeyPair();
const bundle = {};
const identityKey = MovimUtils.arrayBufferToBase64(identityKeyPair.pubKey);
const localDeviceId = await store.getLocalRegistrationId();
const deviceId = localDeviceId ?? KeyHelper.generateRegistrationId();
bundle['identityKey'] = identityKey;
bundle['identityKey'] = MovimUtils.arrayBufferToBase64(identityKeyPair.pubKey);
bundle['deviceId'] = deviceId;
store.setLocalRegistrationId(deviceId);
@ -88,28 +87,30 @@ var ChatOmemo = {
Promise.all(promises).then(results => {
Chat.setOmemoState('yes');
Chat.disableSending();
Chat.sendMessage();
if (Chat.getTextarea().value.length > 0) {
Chat.sendMessage();
}
});
},
handlePreKey: function (jid, deviceId, preKey) {
handlePreKey: async function (jid, deviceId, preKey) {
var store = new ChatOmemoStorage();
var address = new libsignal.SignalProtocolAddress(jid, deviceId);
var sessionBuilder = new libsignal.SessionBuilder(store, address);
var promise = sessionBuilder.processPreKey({
registrationId: 0,
registrationId: parseInt(deviceId, 10),
identityKey: MovimUtils.base64ToArrayBuffer(preKey.identitykey),
signedPreKey: {
keyId: 1,
publicKey: MovimUtils.base64ToArrayBuffer(preKey.prekeypublic),
signature: MovimUtils.base64ToArrayBuffer(preKey.prekeysignature)
keyId: parseInt(preKey.signedprekeyid, 10),
publicKey: MovimUtils.base64ToArrayBuffer(preKey.signedprekeypublic),
signature: MovimUtils.base64ToArrayBuffer(preKey.signedprekeysignature)
},
preKey: {
keyId: preKey.prekey.id,
publicKey: MovimUtils.base64ToArrayBuffer(preKey.prekey.value)
}
})
});
promise.then(function onsuccess() {
console.log('success ' + jid + ':' + deviceId);
@ -190,7 +191,7 @@ var ChatOmemo = {
let plainKey;
try {
plainKey = await this.decryptDevice(atob(key.payload), key.prekey, message.jidfrom, message.omemoheader.sid);
plainKey = await this.decryptDevice(MovimUtils.base64ToArrayBuffer(key.payload), key.prekey, message.jidfrom, message.omemoheader.sid);
} catch (err) {
console.log('Error during decryption: ' + err);
return;
@ -269,7 +270,7 @@ var ChatOmemo = {
});
},
encryptDevice: function (plaintext, jid, deviceId) {
var address = new libsignal.SignalProtocolAddress(jid, deviceId);
var address = new libsignal.SignalProtocolAddress(jid, parseInt(deviceId, 10));
var store = new ChatOmemoStorage();
var sessionCipher = new libsignal.SessionCipher(store, address);
@ -277,16 +278,15 @@ var ChatOmemo = {
.then(payload => ({ 'payload': payload, 'device': deviceId }));
},
decryptDevice: async function(ciphertext, preKey, jid, deviceId) {
var address = new libsignal.SignalProtocolAddress(jid, deviceId);
var address = new libsignal.SignalProtocolAddress(jid, parseInt(deviceId, 10));
var store = new ChatOmemoStorage();
var sessionCipher = new libsignal.SessionCipher(store, address);
let plaintextBuffer;
if (preKey) {
plaintextBuffer = await sessionCipher.decryptPreKeyWhisperMessage(ciphertext, 'binary');
plaintextBuffer = await sessionCipher.decryptPreKeyWhisperMessage(ciphertext, 'binary');
} else {
plaintextBuffer = await sessionCipher.decryptWhisperMessage(ciphertext, 'binary');
plaintextBuffer = await sessionCipher.decryptWhisperMessage(ciphertext, 'binary');
}
return plaintextBuffer;

24
app/widgets/ChatOmemo/chatomemo_storage.js

@ -12,7 +12,7 @@ ChatOmemoStorage.prototype = {
if (key === undefined || value === undefined || key === null || value === null)
throw new Error("Tried to store undefined/null");
localStorage.setObject(key, value);
return localStorage.setObject(key, value);
},
get: function (key, defaultValue) {
if (key === null || key === undefined)
@ -65,13 +65,13 @@ ChatOmemoStorage.prototype = {
if (trusted === undefined) {
return Promise.resolve(true);
}
return Promise.resolve(true);
//return Promise.resolve(libsignal.util.toString(identityKey) === libsignal.util.toString(trusted));
return Promise.resolve(libsignal.util.toString(identityKey) === libsignal.util.toString(trusted));
},
loadIdentityKey: function (identifier) {
if (identifier === null || identifier === undefined)
throw new Error("Tried to get identity key for undefined/null key");
return Promise.resolve(this.get(this.jid + '.identityKey' + identifier));
return Promise.resolve(MovimUtils.base64ToArrayBuffer(this.get(this.jid + '.identityKey' + identifier)));
},
saveIdentity: function (identifier, identityKey) {
if (identifier === null || identifier === undefined)
@ -80,7 +80,7 @@ ChatOmemoStorage.prototype = {
var address = new libsignal.SignalProtocolAddress.fromString(identifier);
var existing = this.get(this.jid + '.identityKey' + address.getName());
this.put(this.jid + '.identityKey' + address.getName(), identityKey)
this.put(this.jid + '.identityKey' + address.getName(), MovimUtils.arrayBufferToBase64(identityKey))
if (existing && toString(identityKey) !== toString(existing)) {
return Promise.resolve(true);
@ -143,17 +143,19 @@ ChatOmemoStorage.prototype = {
return Promise.resolve(this.remove(this.jid + '.session' + identifier));
},
removeAllSessions: function () {
for (key in Object.keys(localStorage)
.filter(key => key.startsWith(this.jid + '.session'))) {
this.remove(key);
let filtered = Object.keys(localStorage).filter(key => key.startsWith(this.jid + '.session'));
for (id in filtered) {
this.remove(filtered[key]);
}
return Promise.resolve();
},
removeAllSessionsOfJid: function (identifier) {
for (key in Object.keys(localStorage)
.filter(key => key.startsWith(this.jid + '.session' + identifier))) {
this.remove(key);
let filtered = Object.keys(localStorage).filter(key => key.startsWith(this.jid + '.session' + identifier));
for (id in filtered) {
this.remove(filtered[id]);
}
return Promise.resolve();

2
app/widgets/ContactActions/_contactactions_drawer.tpl

@ -103,7 +103,7 @@
</span>
<div>
<p class="normal">
<span class="fingerprint">
<span class="fingerprint" title="{$value->bundle_id}">
{$value->fingerprint}
</span>
</p>

6
database/migrations/20210426164828_create_bundles_table.php → database/migrations/20210526164828_create_bundles_table.php

@ -13,8 +13,10 @@ class CreateBundlesTable extends Migration
$table->integer('bundle_id');
$table->string('jid', 128);
$table->text('prekeypublic');
$table->text('prekeysignature');
$table->integer('signedprekeyid');
$table->text('signedprekeypublic');
$table->text('signedprekeysignature');
$table->text('identitykey');
$table->text('prekeys');
$table->timestamps();
Loading…
Cancel
Save