Merge pull request #1390 from Bilb/master

pull/1546/head v1.4.1
Audric Ackermann 5 years ago committed by GitHub
commit 0d28f070c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -221,10 +221,6 @@
// start a background worker for ecc // start a background worker for ecc
textsecure.startWorker('js/libsignal-protocol-worker.js'); textsecure.startWorker('js/libsignal-protocol-worker.js');
Whisper.KeyChangeListener.init(textsecure.storage.protocol); Whisper.KeyChangeListener.init(textsecure.storage.protocol);
textsecure.storage.protocol.on('removePreKey', () => {
getAccountManager().refreshPreKeys();
});
let messageReceiver; let messageReceiver;
window.getSocketStatus = () => { window.getSocketStatus = () => {
if (messageReceiver) { if (messageReceiver) {

@ -7,7 +7,6 @@
lokiFileServerAPI, lokiFileServerAPI,
mnemonic, mnemonic,
btoa, btoa,
Signal,
getString, getString,
Event, Event,
dcodeIO, dcodeIO,
@ -48,60 +47,6 @@
AccountManager.prototype = new textsecure.EventTarget(); AccountManager.prototype = new textsecure.EventTarget();
AccountManager.prototype.extend({ AccountManager.prototype.extend({
constructor: AccountManager, constructor: AccountManager,
requestVoiceVerification(number) {},
requestSMSVerification(number) {},
async encryptDeviceName(name, providedIdentityKey) {
if (!name) {
return null;
}
const identityKey =
providedIdentityKey ||
(await textsecure.storage.protocol.getIdentityKeyPair());
if (!identityKey) {
throw new Error(
'Identity key was not provided and is not in database!'
);
}
const encrypted = await Signal.Crypto.encryptDeviceName(
name,
identityKey.pubKey
);
const proto = new textsecure.protobuf.DeviceName();
proto.ephemeralPublic = encrypted.ephemeralPublic;
proto.syntheticIv = encrypted.syntheticIv;
proto.ciphertext = encrypted.ciphertext;
const arrayBuffer = proto.encode().toArrayBuffer();
return Signal.Crypto.arrayBufferToBase64(arrayBuffer);
},
async decryptDeviceName(base64) {
const identityKey = await textsecure.storage.protocol.getIdentityKeyPair();
const arrayBuffer = Signal.Crypto.base64ToArrayBuffer(base64);
const proto = textsecure.protobuf.DeviceName.decode(arrayBuffer);
const encrypted = {
ephemeralPublic: proto.ephemeralPublic.toArrayBuffer(),
syntheticIv: proto.syntheticIv.toArrayBuffer(),
ciphertext: proto.ciphertext.toArrayBuffer(),
};
const name = await Signal.Crypto.decryptDeviceName(
encrypted,
identityKey.privKey
);
return name;
},
async maybeUpdateDeviceName() {
throw new Error('Signal method called: maybeUpdateDeviceName');
},
async deviceNameIsEncrypted() {
await textsecure.storage.user.setDeviceNameEncrypted();
},
async maybeDeleteSignalingKey() {
throw new Error('Signal method called: maybeDeleteSignalingKey');
},
registerSingleDevice(mnemonic, mnemonicLanguage, profileName) { registerSingleDevice(mnemonic, mnemonicLanguage, profileName) {
const createAccount = this.createAccount.bind(this); const createAccount = this.createAccount.bind(this);
const clearSessionsAndPreKeys = this.clearSessionsAndPreKeys.bind(this); const clearSessionsAndPreKeys = this.clearSessionsAndPreKeys.bind(this);
@ -115,11 +60,11 @@
// handle shorter than 32 bytes seeds // handle shorter than 32 bytes seeds
const privKeyHexLength = 32 * 2; const privKeyHexLength = 32 * 2;
if (seedHex.length !== privKeyHexLength) { if (seedHex.length !== privKeyHexLength) {
seedHex = seedHex.concat(seedHex); seedHex = seedHex.concat('0'.repeat(32));
seedHex = seedHex.substring(0, privKeyHexLength); seedHex = seedHex.substring(0, privKeyHexLength);
} }
const seed = dcodeIO.ByteBuffer.wrap(seedHex, 'hex').toArrayBuffer(); const seed = dcodeIO.ByteBuffer.wrap(seedHex, 'hex').toArrayBuffer();
return libsignal.Curve.async.createKeyPair(seed); return window.sessionGenerateKeyPair(seed);
}; };
} else { } else {
generateKeypair = libsignal.KeyHelper.generateIdentityKeyPair; generateKeypair = libsignal.KeyHelper.generateIdentityKeyPair;
@ -140,65 +85,6 @@
) )
); );
}, },
async addMockContact(doSave) {
if (doSave === undefined) {
// eslint-disable-next-line no-param-reassign
doSave = true;
}
const keyPair = await libsignal.KeyHelper.generateIdentityKeyPair();
const pubKey = StringView.arrayBufferToHex(keyPair.pubKey);
const privKey = StringView.arrayBufferToHex(keyPair.privKey);
log.info(`contact pubkey ${pubKey}`);
log.info(`contact privkey ${privKey}`);
const signedKeyId = Math.floor(Math.random() * 1000 + 1);
const signedPreKey = await libsignal.KeyHelper.generateSignedPreKey(
keyPair,
signedKeyId
);
const contactSignedPreKey = {
publicKey: signedPreKey.keyPair.pubKey,
signature: signedPreKey.signature,
keyId: signedPreKey.keyId,
};
if (doSave) {
await textsecure.storage.protocol.storeContactSignedPreKey(
pubKey,
contactSignedPreKey
);
} else {
log.info(
`signed prekey:
${StringView.arrayBufferToHex(contactSignedPreKey.publicKey)}`
);
log.info(
`signature:
${StringView.arrayBufferToHex(contactSignedPreKey.signature)}`
);
}
for (let keyId = 0; keyId < 10; keyId += 1) {
const preKey = await libsignal.KeyHelper.generatePreKey(keyId);
if (doSave) {
await textsecure.storage.protocol.storeContactPreKey(pubKey, {
publicKey: preKey.keyPair.pubKey,
keyId,
});
} else {
log.info(
`signed prekey:
${StringView.arrayBufferToHex(preKey.keyPair.pubKey)}`
);
}
}
log.info('Added mock contact');
},
registerSecondDevice(setProvisioningUrl, confirmNumber, progressCallback) {
throw new Error(
'account_manager: registerSecondDevice has not been implemented!'
);
},
refreshPreKeys() {},
rotateSignedPreKey() { rotateSignedPreKey() {
return this.queueTask(() => { return this.queueTask(() => {
const signedKeyId = textsecure.storage.get('signedKeyId', 1); const signedKeyId = textsecure.storage.get('signedKeyId', 1);

@ -29,22 +29,6 @@ describe('AccountManager', () => {
window.textsecure.storage.protocol = originalProtocolStorage; window.textsecure.storage.protocol = originalProtocolStorage;
}); });
describe('encrypted device name', () => {
it('roundtrips', async () => {
const deviceName = 'v2.5.0 on Ubunto 20.04';
const encrypted = await accountManager.encryptDeviceName(deviceName);
assert.strictEqual(typeof encrypted, 'string');
const decrypted = await accountManager.decryptDeviceName(encrypted);
assert.strictEqual(decrypted, deviceName);
});
it('handles null deviceName', async () => {
const encrypted = await accountManager.encryptDeviceName(null);
assert.strictEqual(encrypted, null);
});
});
it('keeps three confirmed keys even if over a week old', () => { it('keeps three confirmed keys even if over a week old', () => {
const now = Date.now(); const now = Date.now();
signedPreKeys = [ signedPreKeys = [

@ -1178,6 +1178,43 @@ ipc.on('decrypt-lns-entry', (event, lnsName, ciphertext) => {
decryptLns(event, lnsName, ciphertext); decryptLns(event, lnsName, ciphertext);
}); });
async function sessionGenerateKeyPair(event, seed) {
const sodium = await getSodium();
try {
const ed25519KeyPair = sodium.crypto_sign_seed_keypair(
new Uint8Array(seed)
);
const x25519PublicKey = sodium.crypto_sign_ed25519_pk_to_curve25519(
ed25519KeyPair.publicKey
);
// prepend version byte (coming from `processKeys(raw_keys)`)
const origPub = new Uint8Array(x25519PublicKey);
const prependedX25519PublicKey = new Uint8Array(33);
prependedX25519PublicKey.set(origPub, 1);
prependedX25519PublicKey[0] = 5;
const x25519SecretKey = sodium.crypto_sign_ed25519_sk_to_curve25519(
ed25519KeyPair.privateKey
);
// prepend with 05 the public key
const x25519KeyPair = {
pubKey: prependedX25519PublicKey.buffer,
privKey: x25519SecretKey.buffer,
ed25519KeyPair,
};
// null as first parameter to indivate no error
event.reply('generate-keypair-seed-response', null, x25519KeyPair);
} catch (err) {
event.reply('generate-keypair-seed-response', err);
}
}
ipc.on('generate-keypair-seed', (event, seed) => {
sessionGenerateKeyPair(event, seed);
});
ipc.on('set-auto-update-setting', (event, enabled) => { ipc.on('set-auto-update-setting', (event, enabled) => {
userConfig.set('autoUpdate', !!enabled); userConfig.set('autoUpdate', !!enabled);

@ -2,7 +2,7 @@
"name": "session-messenger-desktop", "name": "session-messenger-desktop",
"productName": "Session", "productName": "Session",
"description": "Private messaging from your desktop", "description": "Private messaging from your desktop",
"version": "1.4.0", "version": "1.4.1",
"license": "GPL-3.0", "license": "GPL-3.0",
"author": { "author": {
"name": "Loki Project", "name": "Loki Project",

@ -118,7 +118,7 @@ const localeMessages = ipc.sendSync('locale-data');
window.blake2b = input => window.blake2b = input =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
ipc.once('blake2b-digest-response', (event, error, res) => { ipc.once('blake2b-digest-response', (_, error, res) => {
// eslint-disable-next-line no-unused-expressions // eslint-disable-next-line no-unused-expressions
error ? reject(error) : resolve(res); error ? reject(error) : resolve(res);
}); });
@ -128,7 +128,7 @@ window.blake2b = input =>
window.decryptLnsEntry = (key, value) => window.decryptLnsEntry = (key, value) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
ipc.once('decrypt-lns-response', (event, error, res) => { ipc.once('decrypt-lns-response', (_, error, res) => {
// eslint-disable-next-line no-unused-expressions // eslint-disable-next-line no-unused-expressions
error ? reject(error) : resolve(res); error ? reject(error) : resolve(res);
}); });
@ -136,6 +136,16 @@ window.decryptLnsEntry = (key, value) =>
ipc.send('decrypt-lns-entry', key, value); ipc.send('decrypt-lns-entry', key, value);
}); });
window.sessionGenerateKeyPair = seed =>
new Promise((resolve, reject) => {
ipc.once('generate-keypair-seed-response', (_, error, res) => {
// eslint-disable-next-line no-unused-expressions
error ? reject(error) : resolve(res);
});
ipc.send('generate-keypair-seed', seed);
});
window.updateZoomFactor = () => { window.updateZoomFactor = () => {
const zoomFactor = window.getSettingValue('zoom-factor-setting') || 100; const zoomFactor = window.getSettingValue('zoom-factor-setting') || 100;
window.setZoomFactor(zoomFactor / 100); window.setZoomFactor(zoomFactor / 100);

@ -110,43 +110,6 @@ describe('Crypto', () => {
}); });
}); });
describe('encrypted device name', () => {
it('roundtrips', async () => {
const deviceName = 'v1.19.0 on Windows 10';
const identityKey = await libsignal.KeyHelper.generateIdentityKeyPair();
const encrypted = await Signal.Crypto.encryptDeviceName(
deviceName,
identityKey.pubKey
);
const decrypted = await Signal.Crypto.decryptDeviceName(
encrypted,
identityKey.privKey
);
assert.strictEqual(decrypted, deviceName);
});
it('fails if iv is changed', async () => {
const deviceName = 'v1.19.0 on Windows 10';
const identityKey = await libsignal.KeyHelper.generateIdentityKeyPair();
const encrypted = await Signal.Crypto.encryptDeviceName(
deviceName,
identityKey.pubKey
);
encrypted.syntheticIv = Signal.Crypto.getRandomBytes(16);
try {
await Signal.Crypto.decryptDeviceName(encrypted, identityKey.privKey);
} catch (error) {
assert.strictEqual(
error.message,
'decryptDeviceName: synthetic IV did not match'
);
}
});
});
describe('attachment encryption', () => { describe('attachment encryption', () => {
it('roundtrips', async () => { it('roundtrips', async () => {
const staticKeyPair = await libsignal.KeyHelper.generateIdentityKeyPair(); const staticKeyPair = await libsignal.KeyHelper.generateIdentityKeyPair();

@ -174,14 +174,14 @@ export class RegistrationTabs extends React.Component<{}, State> {
// handle shorter than 32 bytes seeds // handle shorter than 32 bytes seeds
const privKeyHexLength = 32 * 2; const privKeyHexLength = 32 * 2;
if (seedHex.length !== privKeyHexLength) { if (seedHex.length !== privKeyHexLength) {
seedHex = seedHex.concat(seedHex); seedHex = seedHex.concat('0'.repeat(32));
seedHex = seedHex.substring(0, privKeyHexLength); seedHex = seedHex.substring(0, privKeyHexLength);
} }
const seed = window.dcodeIO.ByteBuffer.wrap( const seed = window.dcodeIO.ByteBuffer.wrap(
seedHex, seedHex,
'hex' 'hex'
).toArrayBuffer(); ).toArrayBuffer();
const keyPair = await window.libsignal.Curve.async.createKeyPair(seed); const keyPair = await window.sessionGenerateKeyPair(seed);
const hexGeneratedPubKey = StringUtils.decode(keyPair.pubKey, 'hex'); const hexGeneratedPubKey = StringUtils.decode(keyPair.pubKey, 'hex');
this.setState({ this.setState({
@ -297,9 +297,7 @@ export class RegistrationTabs extends React.Component<{}, State> {
{this.renderRegistrationContent()} {this.renderRegistrationContent()}
<SessionButton <SessionButton
onClick={() => { onClick={this.onCompleteSignUpClick}
this.onCompleteSignUpClick();
}}
buttonType={SessionButtonType.Brand} buttonType={SessionButtonType.Brand}
buttonColor={SessionButtonColor.Green} buttonColor={SessionButtonColor.Green}
text={window.i18n('getStarted')} text={window.i18n('getStarted')}

3
ts/window.d.ts vendored

@ -99,5 +99,8 @@ declare global {
GroupBuffer: any; GroupBuffer: any;
SwarmPolling: SwarmPolling; SwarmPolling: SwarmPolling;
owsDesktopApp: any; owsDesktopApp: any;
sessionGenerateKeyPair: (
seed: ArrayBuffer
) => Promise<{ pubKey: ArrayBufferLike; privKey: ArrayBufferLike }>;
} }
} }

Loading…
Cancel
Save