bitwarden-estensione-browser/src/services/nodeCryptoFunction.service.ts

186 lines
7.0 KiB
TypeScript
Raw Normal View History

2018-04-19 06:00:53 +02:00
import * as constants from 'constants';
2018-04-20 05:12:06 +02:00
import * as crypto from 'crypto';
2018-04-20 21:32:25 +02:00
import * as forge from 'node-forge';
import { CryptoFunctionService } from '../abstractions/cryptoFunction.service';
2018-04-20 21:32:25 +02:00
import { Utils } from '../misc/utils';
2018-05-07 15:00:49 +02:00
import { DecryptParameters } from '../models/domain/decryptParameters';
import { SymmetricCryptoKey } from '../models/domain/symmetricCryptoKey';
export class NodeCryptoFunctionService implements CryptoFunctionService {
2018-04-18 14:50:02 +02:00
pbkdf2(password: string | ArrayBuffer, salt: string | ArrayBuffer, algorithm: 'sha256' | 'sha512',
iterations: number): Promise<ArrayBuffer> {
2018-04-25 22:30:47 +02:00
const len = algorithm === 'sha256' ? 32 : 64;
const nodePassword = this.toNodeValue(password);
const nodeSalt = this.toNodeValue(salt);
return new Promise<ArrayBuffer>((resolve, reject) => {
2018-04-25 22:30:47 +02:00
crypto.pbkdf2(nodePassword, nodeSalt, iterations, len, algorithm, (error, key) => {
if (error != null) {
reject(error);
} else {
2018-04-20 16:59:55 +02:00
resolve(this.toArrayBuffer(key));
}
});
});
}
2018-04-18 14:50:02 +02:00
hash(value: string | ArrayBuffer, algorithm: 'sha1' | 'sha256' | 'sha512'): Promise<ArrayBuffer> {
const nodeValue = this.toNodeValue(value);
const hash = crypto.createHash(algorithm);
hash.update(nodeValue);
2018-04-20 16:59:55 +02:00
return Promise.resolve(this.toArrayBuffer(hash.digest()));
}
2018-04-18 14:50:02 +02:00
hmac(value: ArrayBuffer, key: ArrayBuffer, algorithm: 'sha1' | 'sha256' | 'sha512'): Promise<ArrayBuffer> {
const nodeValue = this.toNodeBuffer(value);
const nodeKey = this.toNodeBuffer(key);
2018-04-18 14:50:02 +02:00
const hmac = crypto.createHmac(algorithm, nodeKey);
hmac.update(nodeValue);
2018-04-20 16:59:55 +02:00
return Promise.resolve(this.toArrayBuffer(hmac.digest()));
2018-04-18 14:50:02 +02:00
}
2018-05-07 18:14:40 +02:00
async compare(a: ArrayBuffer, b: ArrayBuffer): Promise<boolean> {
2018-05-07 15:00:49 +02:00
const key = await this.randomBytes(32);
const mac1 = await this.hmac(a, key, 'sha256');
const mac2 = await this.hmac(b, key, 'sha256');
if (mac1.byteLength !== mac2.byteLength) {
return false;
}
const arr1 = new Uint8Array(mac1);
const arr2 = new Uint8Array(mac2);
for (let i = 0; i < arr2.length; i++) {
if (arr1[i] !== arr2[i]) {
return false;
}
}
return true;
}
hmacFast(value: ArrayBuffer, key: ArrayBuffer, algorithm: 'sha1' | 'sha256' | 'sha512'): Promise<ArrayBuffer> {
return this.hmac(value, key, algorithm);
}
2018-05-07 18:14:40 +02:00
compareFast(a: ArrayBuffer, b: ArrayBuffer): Promise<boolean> {
return this.compare(a, b);
2018-05-07 15:00:49 +02:00
}
2018-04-18 14:50:02 +02:00
aesEncrypt(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise<ArrayBuffer> {
const nodeData = this.toNodeBuffer(data);
const nodeIv = this.toNodeBuffer(iv);
const nodeKey = this.toNodeBuffer(key);
const cipher = crypto.createCipheriv('aes-256-cbc', nodeKey, nodeIv);
const encBuf = Buffer.concat([cipher.update(nodeData), cipher.final()]);
2018-04-20 16:59:55 +02:00
return Promise.resolve(this.toArrayBuffer(encBuf));
2018-04-18 14:50:02 +02:00
}
2018-05-07 15:00:49 +02:00
aesDecryptFastParameters(data: string, iv: string, mac: string, key: SymmetricCryptoKey):
DecryptParameters<ArrayBuffer> {
const p = new DecryptParameters<ArrayBuffer>();
p.encKey = key.encKey;
p.data = Utils.fromB64ToArray(data).buffer;
p.iv = Utils.fromB64ToArray(iv).buffer;
const macData = new Uint8Array(p.iv.byteLength + p.data.byteLength);
macData.set(new Uint8Array(p.iv), 0);
macData.set(new Uint8Array(p.data), p.iv.byteLength);
p.macData = macData.buffer;
if (key.macKey != null) {
p.macKey = key.macKey;
}
if (mac != null) {
p.mac = Utils.fromB64ToArray(mac).buffer;
}
return p;
}
async aesDecryptFast(parameters: DecryptParameters<ArrayBuffer>): Promise<string> {
2018-05-07 18:14:40 +02:00
const decBuf = await this.aesDecrypt(parameters.data, parameters.iv, parameters.encKey);
2018-05-07 15:00:49 +02:00
return Utils.fromBufferToUtf8(decBuf);
2018-04-18 14:50:02 +02:00
}
2018-05-07 18:14:40 +02:00
aesDecrypt(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise<ArrayBuffer> {
2018-04-18 14:50:02 +02:00
const nodeData = this.toNodeBuffer(data);
const nodeIv = this.toNodeBuffer(iv);
const nodeKey = this.toNodeBuffer(key);
const decipher = crypto.createDecipheriv('aes-256-cbc', nodeKey, nodeIv);
const decBuf = Buffer.concat([decipher.update(nodeData), decipher.final()]);
2018-04-20 16:59:55 +02:00
return Promise.resolve(this.toArrayBuffer(decBuf));
2018-04-18 14:50:02 +02:00
}
2018-04-20 21:32:25 +02:00
rsaEncrypt(data: ArrayBuffer, publicKey: ArrayBuffer, algorithm: 'sha1' | 'sha256'): Promise<ArrayBuffer> {
let md: forge.md.MessageDigest;
if (algorithm === 'sha256') {
md = forge.md.sha256.create();
} else {
md = forge.md.sha1.create();
2018-04-19 06:00:53 +02:00
}
2018-04-20 21:32:25 +02:00
const dataBytes = Utils.fromBufferToByteString(data);
const key = this.toForgePublicKey(publicKey);
const decBytes: string = key.encrypt(dataBytes, 'RSA-OAEP', { md: md });
return Promise.resolve(Utils.fromByteStringToArray(decBytes).buffer);
}
rsaDecrypt(data: ArrayBuffer, privateKey: ArrayBuffer, algorithm: 'sha1' | 'sha256'): Promise<ArrayBuffer> {
let md: forge.md.MessageDigest;
if (algorithm === 'sha256') {
md = forge.md.sha256.create();
} else {
md = forge.md.sha1.create();
}
const dataBytes = Utils.fromBufferToByteString(data);
const key = this.toForgePrivateKey(privateKey);
const decBytes: string = key.decrypt(dataBytes, 'RSA-OAEP', { md: md });
return Promise.resolve(Utils.fromByteStringToArray(decBytes).buffer);
2018-04-19 06:00:53 +02:00
}
2018-04-18 14:50:02 +02:00
randomBytes(length: number): Promise<ArrayBuffer> {
return new Promise<ArrayBuffer>((resolve, reject) => {
crypto.randomBytes(length, (error, bytes) => {
if (error != null) {
reject(error);
} else {
2018-04-20 16:59:55 +02:00
resolve(this.toArrayBuffer(bytes));
2018-04-18 14:50:02 +02:00
}
});
});
2018-04-18 01:02:58 +02:00
}
private toNodeValue(value: string | ArrayBuffer): string | Buffer {
let nodeValue: string | Buffer;
if (typeof (value) === 'string') {
nodeValue = value;
} else {
2018-04-18 14:50:02 +02:00
nodeValue = this.toNodeBuffer(value);
}
return nodeValue;
}
2018-04-18 14:50:02 +02:00
private toNodeBuffer(value: ArrayBuffer): Buffer {
2018-04-18 19:43:42 +02:00
return Buffer.from(new Uint8Array(value) as any);
2018-04-18 14:50:02 +02:00
}
2018-04-19 06:00:53 +02:00
2018-04-20 16:59:55 +02:00
private toArrayBuffer(buf: Buffer): ArrayBuffer {
return new Uint8Array(buf).buffer;
}
2018-04-20 21:32:25 +02:00
private toForgePrivateKey(key: ArrayBuffer): any {
const byteString = Utils.fromBufferToByteString(key);
const asn1 = forge.asn1.fromDer(byteString);
return (forge as any).pki.privateKeyFromAsn1(asn1);
}
private toForgePublicKey(key: ArrayBuffer): any {
const byteString = Utils.fromBufferToByteString(key);
const asn1 = forge.asn1.fromDer(byteString);
return (forge as any).pki.publicKeyFromAsn1(asn1);
2018-04-19 06:00:53 +02:00
}
}