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);
|
||||
return array.buffer;
|
||||
},
|
||||
|
||||
encrypt: function(key, data, iv) {
|
||||
return crypto.subtle.importKey('raw', key, {name: 'AES-CBC'}, false, ['encrypt']).then(function(key) {
|
||||
return crypto.subtle.encrypt({name: 'AES-CBC', iv: new Uint8Array(iv)}, key, data);
|
||||
});
|
||||
return Promise.resolve(window.synchronousCrypto.encrypt(key, data, iv));
|
||||
},
|
||||
decrypt: function(key, data, iv) {
|
||||
return crypto.subtle.importKey('raw', key, {name: 'AES-CBC'}, false, ['decrypt']).then(function(key) {
|
||||
return crypto.subtle.decrypt({name: 'AES-CBC', iv: new Uint8Array(iv)}, key, data);
|
||||
});
|
||||
return Promise.resolve(window.synchronousCrypto.decrypt(key, data, iv));
|
||||
},
|
||||
sign: function(key, data) {
|
||||
return crypto.subtle.importKey('raw', key, {name: 'HMAC', hash: {name: 'SHA-256'}}, false, ['sign']).then(function(key) {
|
||||
return crypto.subtle.sign( {name: 'HMAC', hash: 'SHA-256'}, key, data);
|
||||
});
|
||||
return Promise.resolve(window.synchronousCrypto.sign(key, data));
|
||||
},
|
||||
|
||||
hash: function(data) {
|
||||
return crypto.subtle.digest({name: 'SHA-512'}, data);
|
||||
return Promise.resolve(window.synchronousCrypto.hash(data));
|
||||
},
|
||||
|
||||
HKDF: function(input, salt, info) {
|
||||
|
|
|
@ -411,6 +411,7 @@ try {
|
|||
window.nodeSetImmediate = setImmediate;
|
||||
|
||||
window.textsecure = require('./ts/textsecure').default;
|
||||
window.synchronousCrypto = require('./ts/util/synchronousCrypto');
|
||||
|
||||
window.WebAPI = window.textsecure.WebAPI.initialize({
|
||||
url: config.serverUrl,
|
||||
|
|
10
ts/Crypto.ts
10
ts/Crypto.ts
|
@ -5,9 +5,13 @@ import pProps from 'p-props';
|
|||
import { chunk } from 'lodash';
|
||||
|
||||
export function typedArrayToArrayBuffer(typedArray: Uint8Array): ArrayBuffer {
|
||||
const { buffer, byteOffset, byteLength } = typedArray;
|
||||
|
||||
return buffer.slice(byteOffset, byteLength + byteOffset) as typeof typedArray;
|
||||
const ab = new ArrayBuffer(typedArray.length);
|
||||
// Create a new Uint8Array backed by the ArrayBuffer and copy all values from
|
||||
// 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 {
|
||||
|
|
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 { SignalProtocolStore } from './LibSignalStore';
|
||||
import { StartupQueue } from './util/StartupQueue';
|
||||
import * as synchronousCrypto from './util/synchronousCrypto';
|
||||
|
||||
export { Long } from 'long';
|
||||
|
||||
|
@ -247,6 +248,7 @@ declare global {
|
|||
};
|
||||
systemTheme: WhatIsThis;
|
||||
textsecure: TextSecureType;
|
||||
synchronousCrypto: typeof synchronousCrypto;
|
||||
titleBarDoubleClick: () => void;
|
||||
unregisterForActive: (handler: () => void) => void;
|
||||
updateTrayIcon: (count: number) => void;
|
||||
|
|
Loading…
Reference in a new issue