Use non-subtle crypto in libsignal-protocol

This commit is contained in:
Fedor Indutny 2021-03-23 17:50:02 -07:00 committed by GitHub
parent 9e9d1c8e84
commit 919259c960
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 120 additions and 13 deletions

View File

@ -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) {

View File

@ -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,

View File

@ -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 {

View 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');
});
});
});

View 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
View File

@ -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;