Encrypt device name on account create, on first launch if needed
This commit is contained in:
parent
775e31c854
commit
47f834cf5c
10 changed files with 282 additions and 95 deletions
|
@ -681,6 +681,7 @@
|
||||||
textsecure.storage.user.getDeviceId() != '1'
|
textsecure.storage.user.getDeviceId() != '1'
|
||||||
) {
|
) {
|
||||||
window.getSyncRequest();
|
window.getSyncRequest();
|
||||||
|
window.getAccountManager().maybeUpdateDeviceName();
|
||||||
}
|
}
|
||||||
|
|
||||||
const udSupportKey = 'hasRegisterSupportForUnauthenticatedDelivery';
|
const udSupportKey = 'hasRegisterSupportForUnauthenticatedDelivery';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* eslint-env browser */
|
/* eslint-env browser */
|
||||||
/* global dcodeIO */
|
/* global dcodeIO, libsignal */
|
||||||
|
|
||||||
/* eslint-disable camelcase, no-bitwise */
|
/* eslint-disable camelcase, no-bitwise */
|
||||||
|
|
||||||
|
@ -10,9 +10,11 @@ module.exports = {
|
||||||
concatenateBytes,
|
concatenateBytes,
|
||||||
constantTimeEqual,
|
constantTimeEqual,
|
||||||
decryptAesCtr,
|
decryptAesCtr,
|
||||||
|
decryptDeviceName,
|
||||||
decryptSymmetric,
|
decryptSymmetric,
|
||||||
deriveAccessKey,
|
deriveAccessKey,
|
||||||
encryptAesCtr,
|
encryptAesCtr,
|
||||||
|
encryptDeviceName,
|
||||||
encryptSymmetric,
|
encryptSymmetric,
|
||||||
fromEncodedBinaryToArrayBuffer,
|
fromEncodedBinaryToArrayBuffer,
|
||||||
getAccessKeyVerifier,
|
getAccessKeyVerifier,
|
||||||
|
@ -30,6 +32,55 @@ module.exports = {
|
||||||
|
|
||||||
// High-level Operations
|
// High-level Operations
|
||||||
|
|
||||||
|
async function encryptDeviceName(deviceName, identityPublic) {
|
||||||
|
const plaintext = bytesFromString(deviceName);
|
||||||
|
const ephemeralKeyPair = await libsignal.KeyHelper.generateIdentityKeyPair();
|
||||||
|
const masterSecret = await libsignal.Curve.async.calculateAgreement(
|
||||||
|
identityPublic,
|
||||||
|
ephemeralKeyPair.privKey
|
||||||
|
);
|
||||||
|
|
||||||
|
const key1 = await hmacSha256(masterSecret, bytesFromString('auth'));
|
||||||
|
const syntheticIv = _getFirstBytes(await hmacSha256(key1, plaintext), 16);
|
||||||
|
|
||||||
|
const key2 = await hmacSha256(masterSecret, bytesFromString('cipher'));
|
||||||
|
const cipherKey = await hmacSha256(key2, syntheticIv);
|
||||||
|
|
||||||
|
const counter = getZeroes(16);
|
||||||
|
const ciphertext = await encryptAesCtr(cipherKey, plaintext, counter);
|
||||||
|
|
||||||
|
return {
|
||||||
|
ephemeralPublic: ephemeralKeyPair.pubKey,
|
||||||
|
syntheticIv,
|
||||||
|
ciphertext,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function decryptDeviceName(
|
||||||
|
{ ephemeralPublic, syntheticIv, ciphertext } = {},
|
||||||
|
identityPrivate
|
||||||
|
) {
|
||||||
|
const masterSecret = await libsignal.Curve.async.calculateAgreement(
|
||||||
|
ephemeralPublic,
|
||||||
|
identityPrivate
|
||||||
|
);
|
||||||
|
|
||||||
|
const key2 = await hmacSha256(masterSecret, bytesFromString('cipher'));
|
||||||
|
const cipherKey = await hmacSha256(key2, syntheticIv);
|
||||||
|
|
||||||
|
const counter = getZeroes(16);
|
||||||
|
const plaintext = await decryptAesCtr(cipherKey, ciphertext, counter);
|
||||||
|
|
||||||
|
const key1 = await hmacSha256(masterSecret, bytesFromString('auth'));
|
||||||
|
const ourSyntheticIv = _getFirstBytes(await hmacSha256(key1, plaintext), 16);
|
||||||
|
|
||||||
|
if (!constantTimeEqual(ourSyntheticIv, syntheticIv)) {
|
||||||
|
throw new Error('decryptDeviceName: synthetic IV did not match');
|
||||||
|
}
|
||||||
|
|
||||||
|
return stringFromBytes(plaintext);
|
||||||
|
}
|
||||||
|
|
||||||
async function deriveAccessKey(profileKey) {
|
async function deriveAccessKey(profileKey) {
|
||||||
const iv = getZeroes(12);
|
const iv = getZeroes(12);
|
||||||
const plaintext = getZeroes(16);
|
const plaintext = getZeroes(16);
|
||||||
|
|
|
@ -325,6 +325,7 @@ function HTTPError(message, providedCode, response, stack) {
|
||||||
|
|
||||||
const URL_CALLS = {
|
const URL_CALLS = {
|
||||||
accounts: 'v1/accounts',
|
accounts: 'v1/accounts',
|
||||||
|
updateDeviceName: 'v1/accounts/name',
|
||||||
attachment: 'v1/attachments',
|
attachment: 'v1/attachments',
|
||||||
deliveryCert: 'v1/certificate/delivery',
|
deliveryCert: 'v1/certificate/delivery',
|
||||||
supportUnauthenticatedDelivery: 'v1/devices/unauthenticated_delivery',
|
supportUnauthenticatedDelivery: 'v1/devices/unauthenticated_delivery',
|
||||||
|
@ -386,6 +387,7 @@ function initialize({ url, cdnUrl, certificateAuthority, proxyUrl }) {
|
||||||
sendMessages,
|
sendMessages,
|
||||||
sendMessagesUnauth,
|
sendMessagesUnauth,
|
||||||
setSignedPreKey,
|
setSignedPreKey,
|
||||||
|
updateDeviceName,
|
||||||
};
|
};
|
||||||
|
|
||||||
function _ajax(param) {
|
function _ajax(param) {
|
||||||
|
@ -568,6 +570,16 @@ function initialize({ url, cdnUrl, certificateAuthority, proxyUrl }) {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateDeviceName(deviceName) {
|
||||||
|
return _ajax({
|
||||||
|
call: 'updateDeviceName',
|
||||||
|
httpType: 'PUT',
|
||||||
|
jsonData: {
|
||||||
|
deviceName,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function getDevices() {
|
function getDevices() {
|
||||||
return _ajax({
|
return _ajax({
|
||||||
call: 'devices',
|
call: 'devices',
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
libsignal,
|
libsignal,
|
||||||
WebSocketResource,
|
WebSocketResource,
|
||||||
btoa,
|
btoa,
|
||||||
|
Signal,
|
||||||
getString,
|
getString,
|
||||||
libphonenumber,
|
libphonenumber,
|
||||||
Event,
|
Event,
|
||||||
|
@ -45,6 +46,59 @@
|
||||||
requestSMSVerification(number) {
|
requestSMSVerification(number) {
|
||||||
return this.server.requestVerificationSMS(number);
|
return this.server.requestVerificationSMS(number);
|
||||||
},
|
},
|
||||||
|
async encryptDeviceName(name, providedIdentityKey) {
|
||||||
|
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() {
|
||||||
|
const isNameEncrypted = textsecure.storage.user.getDeviceNameEncrypted();
|
||||||
|
if (isNameEncrypted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const deviceName = await textsecure.storage.user.getDeviceName();
|
||||||
|
const base64 = await this.encryptDeviceName(deviceName);
|
||||||
|
|
||||||
|
await this.server.updateDeviceName(base64);
|
||||||
|
},
|
||||||
|
async deviceNameIsEncrypted() {
|
||||||
|
await textsecure.storage.user.setDeviceNameEncrypted();
|
||||||
|
},
|
||||||
registerSingleDevice(number, verificationCode) {
|
registerSingleDevice(number, verificationCode) {
|
||||||
const registerKeys = this.server.registerKeys.bind(this.server);
|
const registerKeys = this.server.registerKeys.bind(this.server);
|
||||||
const createAccount = this.createAccount.bind(this);
|
const createAccount = this.createAccount.bind(this);
|
||||||
|
@ -335,7 +389,7 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
createAccount(
|
async createAccount(
|
||||||
number,
|
number,
|
||||||
verificationCode,
|
verificationCode,
|
||||||
identityKeyPair,
|
identityKeyPair,
|
||||||
|
@ -353,110 +407,106 @@
|
||||||
|
|
||||||
const previousNumber = getNumber(textsecure.storage.get('number_id'));
|
const previousNumber = getNumber(textsecure.storage.get('number_id'));
|
||||||
|
|
||||||
return this.server
|
const encryptedDeviceName = await this.encryptDeviceName(
|
||||||
.confirmCode(
|
deviceName,
|
||||||
number,
|
identityKeyPair
|
||||||
verificationCode,
|
);
|
||||||
password,
|
await this.deviceNameIsEncrypted();
|
||||||
signalingKey,
|
|
||||||
registrationId,
|
|
||||||
deviceName,
|
|
||||||
{ accessKey }
|
|
||||||
)
|
|
||||||
.then(response => {
|
|
||||||
if (previousNumber && previousNumber !== number) {
|
|
||||||
window.log.warn(
|
|
||||||
'New number is different from old number; deleting all previous data'
|
|
||||||
);
|
|
||||||
|
|
||||||
return textsecure.storage.protocol.removeAllData().then(
|
const response = await this.server.confirmCode(
|
||||||
() => {
|
number,
|
||||||
window.log.info('Successfully deleted previous data');
|
verificationCode,
|
||||||
return response;
|
password,
|
||||||
},
|
signalingKey,
|
||||||
error => {
|
registrationId,
|
||||||
window.log.error(
|
encryptedDeviceName,
|
||||||
'Something went wrong deleting data from previous number',
|
{ accessKey }
|
||||||
error && error.stack ? error.stack : error
|
);
|
||||||
);
|
|
||||||
|
|
||||||
return response;
|
if (previousNumber && previousNumber !== number) {
|
||||||
}
|
window.log.warn(
|
||||||
);
|
'New number is different from old number; deleting all previous data'
|
||||||
}
|
);
|
||||||
|
|
||||||
return response;
|
try {
|
||||||
})
|
await textsecure.storage.protocol.removeAllData();
|
||||||
.then(async response => {
|
window.log.info('Successfully deleted previous data');
|
||||||
await Promise.all([
|
} catch (error) {
|
||||||
textsecure.storage.remove('identityKey'),
|
window.log.error(
|
||||||
textsecure.storage.remove('signaling_key'),
|
'Something went wrong deleting data from previous number',
|
||||||
textsecure.storage.remove('password'),
|
error && error.stack ? error.stack : error
|
||||||
textsecure.storage.remove('registrationId'),
|
|
||||||
textsecure.storage.remove('number_id'),
|
|
||||||
textsecure.storage.remove('device_name'),
|
|
||||||
textsecure.storage.remove('regionCode'),
|
|
||||||
textsecure.storage.remove('userAgent'),
|
|
||||||
textsecure.storage.remove('profileKey'),
|
|
||||||
textsecure.storage.remove('read-receipts-setting'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// update our own identity key, which may have changed
|
|
||||||
// if we're relinking after a reinstall on the master device
|
|
||||||
await textsecure.storage.protocol.saveIdentityWithAttributes(number, {
|
|
||||||
id: number,
|
|
||||||
publicKey: identityKeyPair.pubKey,
|
|
||||||
firstUse: true,
|
|
||||||
timestamp: Date.now(),
|
|
||||||
verified: textsecure.storage.protocol.VerifiedStatus.VERIFIED,
|
|
||||||
nonblockingApproval: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
await textsecure.storage.put('identityKey', identityKeyPair);
|
|
||||||
await textsecure.storage.put('signaling_key', signalingKey);
|
|
||||||
await textsecure.storage.put('password', password);
|
|
||||||
await textsecure.storage.put('registrationId', registrationId);
|
|
||||||
if (profileKey) {
|
|
||||||
await textsecure.storage.put('profileKey', profileKey);
|
|
||||||
}
|
|
||||||
if (userAgent) {
|
|
||||||
await textsecure.storage.put('userAgent', userAgent);
|
|
||||||
}
|
|
||||||
|
|
||||||
await textsecure.storage.put(
|
|
||||||
'read-receipt-setting',
|
|
||||||
Boolean(readReceipts)
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await textsecure.storage.user.setNumberAndDeviceId(
|
await Promise.all([
|
||||||
number,
|
textsecure.storage.remove('identityKey'),
|
||||||
response.deviceId || 1,
|
textsecure.storage.remove('signaling_key'),
|
||||||
deviceName
|
textsecure.storage.remove('password'),
|
||||||
);
|
textsecure.storage.remove('registrationId'),
|
||||||
await textsecure.storage.put(
|
textsecure.storage.remove('number_id'),
|
||||||
'regionCode',
|
textsecure.storage.remove('device_name'),
|
||||||
libphonenumber.util.getRegionCodeForNumber(number)
|
textsecure.storage.remove('regionCode'),
|
||||||
);
|
textsecure.storage.remove('userAgent'),
|
||||||
});
|
textsecure.storage.remove('profileKey'),
|
||||||
|
textsecure.storage.remove('read-receipts-setting'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// update our own identity key, which may have changed
|
||||||
|
// if we're relinking after a reinstall on the master device
|
||||||
|
await textsecure.storage.protocol.saveIdentityWithAttributes(number, {
|
||||||
|
id: number,
|
||||||
|
publicKey: identityKeyPair.pubKey,
|
||||||
|
firstUse: true,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
verified: textsecure.storage.protocol.VerifiedStatus.VERIFIED,
|
||||||
|
nonblockingApproval: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
await textsecure.storage.put('identityKey', identityKeyPair);
|
||||||
|
await textsecure.storage.put('signaling_key', signalingKey);
|
||||||
|
await textsecure.storage.put('password', password);
|
||||||
|
await textsecure.storage.put('registrationId', registrationId);
|
||||||
|
if (profileKey) {
|
||||||
|
await textsecure.storage.put('profileKey', profileKey);
|
||||||
|
}
|
||||||
|
if (userAgent) {
|
||||||
|
await textsecure.storage.put('userAgent', userAgent);
|
||||||
|
}
|
||||||
|
|
||||||
|
await textsecure.storage.put(
|
||||||
|
'read-receipt-setting',
|
||||||
|
Boolean(readReceipts)
|
||||||
|
);
|
||||||
|
|
||||||
|
await textsecure.storage.user.setNumberAndDeviceId(
|
||||||
|
number,
|
||||||
|
response.deviceId || 1,
|
||||||
|
deviceName
|
||||||
|
);
|
||||||
|
await textsecure.storage.put(
|
||||||
|
'regionCode',
|
||||||
|
libphonenumber.util.getRegionCodeForNumber(number)
|
||||||
|
);
|
||||||
},
|
},
|
||||||
clearSessionsAndPreKeys() {
|
async clearSessionsAndPreKeys() {
|
||||||
const store = textsecure.storage.protocol;
|
const store = textsecure.storage.protocol;
|
||||||
|
|
||||||
window.log.info('clearing all sessions, prekeys, and signed prekeys');
|
window.log.info('clearing all sessions, prekeys, and signed prekeys');
|
||||||
return Promise.all([
|
await Promise.all([
|
||||||
store.clearPreKeyStore(),
|
store.clearPreKeyStore(),
|
||||||
store.clearSignedPreKeysStore(),
|
store.clearSignedPreKeysStore(),
|
||||||
store.clearSessionStore(),
|
store.clearSessionStore(),
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
// Takes the same object returned by generateKeys
|
// Takes the same object returned by generateKeys
|
||||||
confirmKeys(keys) {
|
async confirmKeys(keys) {
|
||||||
const store = textsecure.storage.protocol;
|
const store = textsecure.storage.protocol;
|
||||||
const key = keys.signedPreKey;
|
const key = keys.signedPreKey;
|
||||||
const confirmed = true;
|
const confirmed = true;
|
||||||
|
|
||||||
window.log.info('confirmKeys: confirming key', key.keyId);
|
window.log.info('confirmKeys: confirming key', key.keyId);
|
||||||
return store.storeSignedPreKey(key.keyId, key.keyPair, confirmed);
|
await store.storeSignedPreKey(key.keyId, key.keyPair, confirmed);
|
||||||
},
|
},
|
||||||
generateKeys(count, providedProgressCallback) {
|
generateKeys(count, providedProgressCallback) {
|
||||||
const progressCallback =
|
const progressCallback =
|
||||||
|
|
|
@ -36,6 +36,9 @@
|
||||||
loadProtoBufs('SubProtocol.proto');
|
loadProtoBufs('SubProtocol.proto');
|
||||||
loadProtoBufs('DeviceMessages.proto');
|
loadProtoBufs('DeviceMessages.proto');
|
||||||
|
|
||||||
|
// Just for encrypting device names
|
||||||
|
loadProtoBufs('DeviceName.proto');
|
||||||
|
|
||||||
// Metadata-specific protos
|
// Metadata-specific protos
|
||||||
loadProtoBufs('UnidentifiedDelivery.proto');
|
loadProtoBufs('UnidentifiedDelivery.proto');
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -31,5 +31,13 @@
|
||||||
getDeviceName() {
|
getDeviceName() {
|
||||||
return textsecure.storage.get('device_name');
|
return textsecure.storage.get('device_name');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setDeviceNameEncrypted() {
|
||||||
|
return textsecure.storage.put('deviceNameEncrypted', true);
|
||||||
|
},
|
||||||
|
|
||||||
|
getDeviceNameEncrypted() {
|
||||||
|
return textsecure.storage.get('deviceNameEncrypted');
|
||||||
|
},
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
/* global libsignal */
|
||||||
|
|
||||||
describe('AccountManager', () => {
|
describe('AccountManager', () => {
|
||||||
let accountManager;
|
let accountManager;
|
||||||
|
|
||||||
|
@ -10,9 +12,14 @@ describe('AccountManager', () => {
|
||||||
let signedPreKeys;
|
let signedPreKeys;
|
||||||
const DAY = 1000 * 60 * 60 * 24;
|
const DAY = 1000 * 60 * 60 * 24;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(async () => {
|
||||||
|
const identityKey = await libsignal.KeyHelper.generateIdentityKeyPair();
|
||||||
|
|
||||||
originalProtocolStorage = window.textsecure.storage.protocol;
|
originalProtocolStorage = window.textsecure.storage.protocol;
|
||||||
window.textsecure.storage.protocol = {
|
window.textsecure.storage.protocol = {
|
||||||
|
getIdentityKeyPair() {
|
||||||
|
return identityKey;
|
||||||
|
},
|
||||||
loadSignedPreKeys() {
|
loadSignedPreKeys() {
|
||||||
return Promise.resolve(signedPreKeys);
|
return Promise.resolve(signedPreKeys);
|
||||||
},
|
},
|
||||||
|
@ -22,6 +29,17 @@ 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('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 = [
|
||||||
|
|
7
protos/DeviceName.proto
Normal file
7
protos/DeviceName.proto
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package signalservice;
|
||||||
|
|
||||||
|
message DeviceName {
|
||||||
|
optional bytes ephemeralPublic = 1;
|
||||||
|
optional bytes syntheticIv = 2;
|
||||||
|
optional bytes ciphertext = 3;
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
/* global Signal, textsecure */
|
/* global Signal, textsecure, libsignal */
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
@ -109,4 +109,41 @@ describe('Crypto', () => {
|
||||||
throw new Error('Expected error to be thrown');
|
throw new Error('Expected error to be thrown');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -244,7 +244,7 @@
|
||||||
"rule": "jQuery-wrap(",
|
"rule": "jQuery-wrap(",
|
||||||
"path": "js/background.js",
|
"path": "js/background.js",
|
||||||
"line": " wrap(",
|
"line": " wrap(",
|
||||||
"lineNumber": 727,
|
"lineNumber": 728,
|
||||||
"reasonCategory": "falseMatch",
|
"reasonCategory": "falseMatch",
|
||||||
"updated": "2018-10-18T22:23:00.485Z"
|
"updated": "2018-10-18T22:23:00.485Z"
|
||||||
},
|
},
|
||||||
|
@ -252,7 +252,7 @@
|
||||||
"rule": "jQuery-wrap(",
|
"rule": "jQuery-wrap(",
|
||||||
"path": "js/background.js",
|
"path": "js/background.js",
|
||||||
"line": " await wrap(",
|
"line": " await wrap(",
|
||||||
"lineNumber": 1257,
|
"lineNumber": 1258,
|
||||||
"reasonCategory": "falseMatch",
|
"reasonCategory": "falseMatch",
|
||||||
"updated": "2018-10-26T22:43:23.229Z"
|
"updated": "2018-10-26T22:43:23.229Z"
|
||||||
},
|
},
|
||||||
|
@ -319,7 +319,7 @@
|
||||||
"rule": "jQuery-wrap(",
|
"rule": "jQuery-wrap(",
|
||||||
"path": "js/modules/crypto.js",
|
"path": "js/modules/crypto.js",
|
||||||
"line": " return dcodeIO.ByteBuffer.wrap(arrayBuffer).toString('base64');",
|
"line": " return dcodeIO.ByteBuffer.wrap(arrayBuffer).toString('base64');",
|
||||||
"lineNumber": 271,
|
"lineNumber": 322,
|
||||||
"reasonCategory": "falseMatch",
|
"reasonCategory": "falseMatch",
|
||||||
"updated": "2018-10-05T23:12:28.961Z"
|
"updated": "2018-10-05T23:12:28.961Z"
|
||||||
},
|
},
|
||||||
|
@ -327,7 +327,7 @@
|
||||||
"rule": "jQuery-wrap(",
|
"rule": "jQuery-wrap(",
|
||||||
"path": "js/modules/crypto.js",
|
"path": "js/modules/crypto.js",
|
||||||
"line": " return dcodeIO.ByteBuffer.wrap(base64string, 'base64').toArrayBuffer();",
|
"line": " return dcodeIO.ByteBuffer.wrap(base64string, 'base64').toArrayBuffer();",
|
||||||
"lineNumber": 274,
|
"lineNumber": 325,
|
||||||
"reasonCategory": "falseMatch",
|
"reasonCategory": "falseMatch",
|
||||||
"updated": "2018-10-05T23:12:28.961Z"
|
"updated": "2018-10-05T23:12:28.961Z"
|
||||||
},
|
},
|
||||||
|
@ -335,7 +335,7 @@
|
||||||
"rule": "jQuery-wrap(",
|
"rule": "jQuery-wrap(",
|
||||||
"path": "js/modules/crypto.js",
|
"path": "js/modules/crypto.js",
|
||||||
"line": " return dcodeIO.ByteBuffer.wrap(key, 'binary').toArrayBuffer();",
|
"line": " return dcodeIO.ByteBuffer.wrap(key, 'binary').toArrayBuffer();",
|
||||||
"lineNumber": 278,
|
"lineNumber": 329,
|
||||||
"reasonCategory": "falseMatch",
|
"reasonCategory": "falseMatch",
|
||||||
"updated": "2018-10-05T23:12:28.961Z"
|
"updated": "2018-10-05T23:12:28.961Z"
|
||||||
},
|
},
|
||||||
|
@ -343,7 +343,7 @@
|
||||||
"rule": "jQuery-wrap(",
|
"rule": "jQuery-wrap(",
|
||||||
"path": "js/modules/crypto.js",
|
"path": "js/modules/crypto.js",
|
||||||
"line": " return dcodeIO.ByteBuffer.wrap(string, 'utf8').toArrayBuffer();",
|
"line": " return dcodeIO.ByteBuffer.wrap(string, 'utf8').toArrayBuffer();",
|
||||||
"lineNumber": 282,
|
"lineNumber": 333,
|
||||||
"reasonCategory": "falseMatch",
|
"reasonCategory": "falseMatch",
|
||||||
"updated": "2018-10-05T23:12:28.961Z"
|
"updated": "2018-10-05T23:12:28.961Z"
|
||||||
},
|
},
|
||||||
|
@ -351,7 +351,7 @@
|
||||||
"rule": "jQuery-wrap(",
|
"rule": "jQuery-wrap(",
|
||||||
"path": "js/modules/crypto.js",
|
"path": "js/modules/crypto.js",
|
||||||
"line": " return dcodeIO.ByteBuffer.wrap(buffer).toString('utf8');",
|
"line": " return dcodeIO.ByteBuffer.wrap(buffer).toString('utf8');",
|
||||||
"lineNumber": 285,
|
"lineNumber": 336,
|
||||||
"reasonCategory": "falseMatch",
|
"reasonCategory": "falseMatch",
|
||||||
"updated": "2018-10-05T23:12:28.961Z"
|
"updated": "2018-10-05T23:12:28.961Z"
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue