Use non-subtle crypto in libsignal-protocol
This commit is contained in:
parent
9e9d1c8e84
commit
919259c960
6 changed files with 120 additions and 13 deletions
|
@ -23897,24 +23897,19 @@ var Internal = Internal || {};
|
||||||
crypto.getRandomValues(array);
|
crypto.getRandomValues(array);
|
||||||
return array.buffer;
|
return array.buffer;
|
||||||
},
|
},
|
||||||
|
|
||||||
encrypt: function(key, data, iv) {
|
encrypt: function(key, data, iv) {
|
||||||
return crypto.subtle.importKey('raw', key, {name: 'AES-CBC'}, false, ['encrypt']).then(function(key) {
|
return Promise.resolve(window.synchronousCrypto.encrypt(key, data, iv));
|
||||||
return crypto.subtle.encrypt({name: 'AES-CBC', iv: new Uint8Array(iv)}, key, data);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
decrypt: function(key, data, iv) {
|
decrypt: function(key, data, iv) {
|
||||||
return crypto.subtle.importKey('raw', key, {name: 'AES-CBC'}, false, ['decrypt']).then(function(key) {
|
return Promise.resolve(window.synchronousCrypto.decrypt(key, data, iv));
|
||||||
return crypto.subtle.decrypt({name: 'AES-CBC', iv: new Uint8Array(iv)}, key, data);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
sign: function(key, data) {
|
sign: function(key, data) {
|
||||||
return crypto.subtle.importKey('raw', key, {name: 'HMAC', hash: {name: 'SHA-256'}}, false, ['sign']).then(function(key) {
|
return Promise.resolve(window.synchronousCrypto.sign(key, data));
|
||||||
return crypto.subtle.sign( {name: 'HMAC', hash: 'SHA-256'}, key, data);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
hash: function(data) {
|
hash: function(data) {
|
||||||
return crypto.subtle.digest({name: 'SHA-512'}, data);
|
return Promise.resolve(window.synchronousCrypto.hash(data));
|
||||||
},
|
},
|
||||||
|
|
||||||
HKDF: function(input, salt, info) {
|
HKDF: function(input, salt, info) {
|
||||||
|
|
|
@ -411,6 +411,7 @@ try {
|
||||||
window.nodeSetImmediate = setImmediate;
|
window.nodeSetImmediate = setImmediate;
|
||||||
|
|
||||||
window.textsecure = require('./ts/textsecure').default;
|
window.textsecure = require('./ts/textsecure').default;
|
||||||
|
window.synchronousCrypto = require('./ts/util/synchronousCrypto');
|
||||||
|
|
||||||
window.WebAPI = window.textsecure.WebAPI.initialize({
|
window.WebAPI = window.textsecure.WebAPI.initialize({
|
||||||
url: config.serverUrl,
|
url: config.serverUrl,
|
||||||
|
|
10
ts/Crypto.ts
10
ts/Crypto.ts
|
@ -5,9 +5,13 @@ import pProps from 'p-props';
|
||||||
import { chunk } from 'lodash';
|
import { chunk } from 'lodash';
|
||||||
|
|
||||||
export function typedArrayToArrayBuffer(typedArray: Uint8Array): ArrayBuffer {
|
export function typedArrayToArrayBuffer(typedArray: Uint8Array): ArrayBuffer {
|
||||||
const { buffer, byteOffset, byteLength } = typedArray;
|
const ab = new ArrayBuffer(typedArray.length);
|
||||||
|
// Create a new Uint8Array backed by the ArrayBuffer and copy all values from
|
||||||
return buffer.slice(byteOffset, byteLength + byteOffset) as typeof typedArray;
|
// the `typedArray` into it by calling `.set()` method. Note that raw
|
||||||
|
// ArrayBuffer doesn't offer this API, because it is supposed to be used with
|
||||||
|
// concrete data view (i.e. Uint8Array, Float64Array, and so on.)
|
||||||
|
new Uint8Array(ab).set(typedArray, 0);
|
||||||
|
return ab;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function arrayBufferToBase64(arrayBuffer: ArrayBuffer): string {
|
export function arrayBufferToBase64(arrayBuffer: ArrayBuffer): string {
|
||||||
|
|
48
ts/test-both/util/synchronousCrypto_test.ts
Normal file
48
ts/test-both/util/synchronousCrypto_test.ts
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { assert } from 'chai';
|
||||||
|
import crypto from 'crypto';
|
||||||
|
|
||||||
|
import { typedArrayToArrayBuffer as toArrayBuffer } from '../../Crypto';
|
||||||
|
import { hash, sign, encrypt, decrypt } from '../../util/synchronousCrypto';
|
||||||
|
|
||||||
|
describe('synchronousCrypto', () => {
|
||||||
|
describe('hash', () => {
|
||||||
|
it('returns SHA512 hash of the input', () => {
|
||||||
|
const result = hash(toArrayBuffer(Buffer.from('signal')));
|
||||||
|
assert.strictEqual(
|
||||||
|
Buffer.from(result).toString('base64'),
|
||||||
|
'WxneQjrfSlY95Bi+SAzDAr2cf3mxUXePeNYn6DILN4a8NFr9VelTbP5tGHdthi+' +
|
||||||
|
'mrJLqMZd1I6w8CxCnmJ/OFw=='
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('sign', () => {
|
||||||
|
it('returns hmac SHA256 hash of the input', () => {
|
||||||
|
const result = sign(
|
||||||
|
toArrayBuffer(Buffer.from('secret')),
|
||||||
|
toArrayBuffer(Buffer.from('signal'))
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.strictEqual(
|
||||||
|
Buffer.from(result).toString('base64'),
|
||||||
|
'5ewbITW27c1F7dluF9KwGcVQSxmZp6mpVhPj3ww1Sh8='
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('encrypt+decrypt', () => {
|
||||||
|
it('returns original input', () => {
|
||||||
|
const iv = crypto.randomBytes(16);
|
||||||
|
const key = crypto.randomBytes(32);
|
||||||
|
const input = Buffer.from('plaintext');
|
||||||
|
|
||||||
|
const ciphertext = encrypt(key, input, iv);
|
||||||
|
const plaintext = decrypt(key, ciphertext, iv);
|
||||||
|
|
||||||
|
assert.strictEqual(Buffer.from(plaintext).toString(), 'plaintext');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
57
ts/util/synchronousCrypto.ts
Normal file
57
ts/util/synchronousCrypto.ts
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import crypto from 'crypto';
|
||||||
|
|
||||||
|
import { typedArrayToArrayBuffer as toArrayBuffer } from '../Crypto';
|
||||||
|
|
||||||
|
export function sign(key: ArrayBuffer, data: ArrayBuffer): ArrayBuffer {
|
||||||
|
return toArrayBuffer(
|
||||||
|
crypto
|
||||||
|
.createHmac('sha256', Buffer.from(key))
|
||||||
|
.update(Buffer.from(data))
|
||||||
|
.digest()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hash(data: ArrayBuffer): ArrayBuffer {
|
||||||
|
return toArrayBuffer(
|
||||||
|
crypto.createHash('sha512').update(Buffer.from(data)).digest()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function encrypt(
|
||||||
|
key: ArrayBuffer,
|
||||||
|
data: ArrayBuffer,
|
||||||
|
iv: ArrayBuffer
|
||||||
|
): ArrayBuffer {
|
||||||
|
const cipher = crypto.createCipheriv(
|
||||||
|
'aes-256-cbc',
|
||||||
|
Buffer.from(key),
|
||||||
|
Buffer.from(iv)
|
||||||
|
);
|
||||||
|
const encrypted = Buffer.concat([
|
||||||
|
cipher.update(Buffer.from(data)),
|
||||||
|
cipher.final(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return toArrayBuffer(encrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decrypt(
|
||||||
|
key: ArrayBuffer,
|
||||||
|
data: ArrayBuffer,
|
||||||
|
iv: ArrayBuffer
|
||||||
|
): ArrayBuffer {
|
||||||
|
const cipher = crypto.createDecipheriv(
|
||||||
|
'aes-256-cbc',
|
||||||
|
Buffer.from(key),
|
||||||
|
Buffer.from(iv)
|
||||||
|
);
|
||||||
|
const decrypted = Buffer.concat([
|
||||||
|
cipher.update(Buffer.from(data)),
|
||||||
|
cipher.final(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return toArrayBuffer(decrypted);
|
||||||
|
}
|
2
ts/window.d.ts
vendored
2
ts/window.d.ts
vendored
|
@ -95,6 +95,7 @@ import { MIMEType } from './types/MIME';
|
||||||
import { ElectronLocaleType } from './util/mapToSupportLocale';
|
import { ElectronLocaleType } from './util/mapToSupportLocale';
|
||||||
import { SignalProtocolStore } from './LibSignalStore';
|
import { SignalProtocolStore } from './LibSignalStore';
|
||||||
import { StartupQueue } from './util/StartupQueue';
|
import { StartupQueue } from './util/StartupQueue';
|
||||||
|
import * as synchronousCrypto from './util/synchronousCrypto';
|
||||||
|
|
||||||
export { Long } from 'long';
|
export { Long } from 'long';
|
||||||
|
|
||||||
|
@ -247,6 +248,7 @@ declare global {
|
||||||
};
|
};
|
||||||
systemTheme: WhatIsThis;
|
systemTheme: WhatIsThis;
|
||||||
textsecure: TextSecureType;
|
textsecure: TextSecureType;
|
||||||
|
synchronousCrypto: typeof synchronousCrypto;
|
||||||
titleBarDoubleClick: () => void;
|
titleBarDoubleClick: () => void;
|
||||||
unregisterForActive: (handler: () => void) => void;
|
unregisterForActive: (handler: () => void) => void;
|
||||||
updateTrayIcon: (count: number) => void;
|
updateTrayIcon: (count: number) => void;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue