1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-29 06:33:40 +00:00

Apply Prettier (#581)

This commit is contained in:
Oscar Hinton
2021-12-16 13:36:21 +01:00
committed by GitHub
parent 8b2dfc6cdc
commit 193434461d
589 changed files with 46650 additions and 41924 deletions

View File

@@ -1,119 +1,130 @@
import * as fs from 'fs';
import * as lowdb from 'lowdb';
import * as FileSync from 'lowdb/adapters/FileSync';
import * as path from 'path';
import * as fs from "fs";
import * as lowdb from "lowdb";
import * as FileSync from "lowdb/adapters/FileSync";
import * as path from "path";
import { LogService } from 'jslib-common/abstractions/log.service';
import { StorageService } from 'jslib-common/abstractions/storage.service';
import { LogService } from "jslib-common/abstractions/log.service";
import { StorageService } from "jslib-common/abstractions/storage.service";
import { NodeUtils } from 'jslib-common/misc/nodeUtils';
import { Utils } from 'jslib-common/misc/utils';
import { NodeUtils } from "jslib-common/misc/nodeUtils";
import { Utils } from "jslib-common/misc/utils";
export class LowdbStorageService implements StorageService {
protected dataFilePath: string;
private db: lowdb.LowdbSync<any>;
private defaults: any;
protected dataFilePath: string;
private db: lowdb.LowdbSync<any>;
private defaults: any;
constructor(protected logService: LogService, defaults?: any, private dir?: string, private allowCache = false) {
this.defaults = defaults;
constructor(
protected logService: LogService,
defaults?: any,
private dir?: string,
private allowCache = false
) {
this.defaults = defaults;
}
async init() {
this.logService.info("Initializing lowdb storage service.");
let adapter: lowdb.AdapterSync<any>;
if (Utils.isNode && this.dir != null) {
if (!fs.existsSync(this.dir)) {
this.logService.warning(`Could not find dir, "${this.dir}"; creating it instead.`);
NodeUtils.mkdirpSync(this.dir, "700");
this.logService.info(`Created dir "${this.dir}".`);
}
this.dataFilePath = path.join(this.dir, "data.json");
if (!fs.existsSync(this.dataFilePath)) {
this.logService.warning(
`Could not find data file, "${this.dataFilePath}"; creating it instead.`
);
fs.writeFileSync(this.dataFilePath, "", { mode: 0o600 });
fs.chmodSync(this.dataFilePath, 0o600);
this.logService.info(`Created data file "${this.dataFilePath}" with chmod 600.`);
} else {
this.logService.info(`db file "${this.dataFilePath} already exists"; using existing db`);
}
await this.lockDbFile(() => {
adapter = new FileSync(this.dataFilePath);
});
}
async init() {
this.logService.info('Initializing lowdb storage service.');
let adapter: lowdb.AdapterSync<any>;
if (Utils.isNode && this.dir != null) {
if (!fs.existsSync(this.dir)) {
this.logService.warning(`Could not find dir, "${this.dir}"; creating it instead.`);
NodeUtils.mkdirpSync(this.dir, '700');
this.logService.info(`Created dir "${this.dir}".`);
}
this.dataFilePath = path.join(this.dir, 'data.json');
if (!fs.existsSync(this.dataFilePath)) {
this.logService.warning(`Could not find data file, "${this.dataFilePath}"; creating it instead.`);
fs.writeFileSync(this.dataFilePath, '', { mode: 0o600 });
fs.chmodSync(this.dataFilePath, 0o600);
this.logService.info(`Created data file "${this.dataFilePath}" with chmod 600.`);
} else {
this.logService.info(`db file "${this.dataFilePath} already exists"; using existing db`);
}
await this.lockDbFile(() => {
adapter = new FileSync(this.dataFilePath);
});
}
try {
this.logService.info('Attempting to create lowdb storage adapter.');
this.db = lowdb(adapter);
this.logService.info('Successfully created lowdb storage adapter.');
} catch (e) {
if (e instanceof SyntaxError) {
this.logService.warning(`Error creating lowdb storage adapter, "${e.message}"; emptying data file.`);
if (fs.existsSync(this.dataFilePath)) {
const backupPath = this.dataFilePath + '.bak';
this.logService.warning(`Writing backup of data file to ${backupPath}`);
await fs.copyFile(this.dataFilePath, backupPath, err => {
this.logService.warning(`Error while creating data file backup, "${e.message}". No backup may have been created.`);
});
}
adapter.write({});
this.db = lowdb(adapter);
} else {
this.logService.error(`Error creating lowdb storage adapter, "${e.message}".`);
throw e;
}
}
if (this.defaults != null) {
this.lockDbFile(() => {
this.logService.info('Writing defaults.');
this.readForNoCache();
this.db.defaults(this.defaults).write();
this.logService.info('Successfully wrote defaults to db.');
});
try {
this.logService.info("Attempting to create lowdb storage adapter.");
this.db = lowdb(adapter);
this.logService.info("Successfully created lowdb storage adapter.");
} catch (e) {
if (e instanceof SyntaxError) {
this.logService.warning(
`Error creating lowdb storage adapter, "${e.message}"; emptying data file.`
);
if (fs.existsSync(this.dataFilePath)) {
const backupPath = this.dataFilePath + ".bak";
this.logService.warning(`Writing backup of data file to ${backupPath}`);
await fs.copyFile(this.dataFilePath, backupPath, (err) => {
this.logService.warning(
`Error while creating data file backup, "${e.message}". No backup may have been created.`
);
});
}
adapter.write({});
this.db = lowdb(adapter);
} else {
this.logService.error(`Error creating lowdb storage adapter, "${e.message}".`);
throw e;
}
}
get<T>(key: string): Promise<T> {
return this.lockDbFile(() => {
this.readForNoCache();
const val = this.db.get(key).value();
this.logService.debug(`Successfully read ${key} from db`);
if (val == null) {
return null;
}
return val as T;
});
if (this.defaults != null) {
this.lockDbFile(() => {
this.logService.info("Writing defaults.");
this.readForNoCache();
this.db.defaults(this.defaults).write();
this.logService.info("Successfully wrote defaults to db.");
});
}
}
has(key: string): Promise<boolean> {
return this.get(key).then(v => v != null);
}
get<T>(key: string): Promise<T> {
return this.lockDbFile(() => {
this.readForNoCache();
const val = this.db.get(key).value();
this.logService.debug(`Successfully read ${key} from db`);
if (val == null) {
return null;
}
return val as T;
});
}
save(key: string, obj: any): Promise<any> {
return this.lockDbFile(() => {
this.readForNoCache();
this.db.set(key, obj).write();
this.logService.debug(`Successfully wrote ${key} to db`);
return;
});
}
has(key: string): Promise<boolean> {
return this.get(key).then((v) => v != null);
}
remove(key: string): Promise<any> {
return this.lockDbFile(() => {
this.readForNoCache();
this.db.unset(key).write();
this.logService.debug(`Successfully removed ${key} from db`);
return;
});
}
save(key: string, obj: any): Promise<any> {
return this.lockDbFile(() => {
this.readForNoCache();
this.db.set(key, obj).write();
this.logService.debug(`Successfully wrote ${key} to db`);
return;
});
}
protected async lockDbFile<T>(action: () => T): Promise<T> {
// Lock methods implemented in clients
return Promise.resolve(action());
}
remove(key: string): Promise<any> {
return this.lockDbFile(() => {
this.readForNoCache();
this.db.unset(key).write();
this.logService.debug(`Successfully removed ${key} from db`);
return;
});
}
private readForNoCache() {
if (!this.allowCache) {
this.db.read();
}
protected async lockDbFile<T>(action: () => T): Promise<T> {
// Lock methods implemented in clients
return Promise.resolve(action());
}
private readForNoCache() {
if (!this.allowCache) {
this.db.read();
}
}
}

View File

@@ -1,12 +1,12 @@
import * as FormData from 'form-data';
import { HttpsProxyAgent } from 'https-proxy-agent';
import * as fe from 'node-fetch';
import * as FormData from "form-data";
import { HttpsProxyAgent } from "https-proxy-agent";
import * as fe from "node-fetch";
import { ApiService } from 'jslib-common/services/api.service';
import { ApiService } from "jslib-common/services/api.service";
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { TokenService } from 'jslib-common/abstractions/token.service';
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { TokenService } from "jslib-common/abstractions/token.service";
(global as any).fetch = fe.default;
(global as any).Request = fe.Request;
@@ -15,18 +15,23 @@ import { TokenService } from 'jslib-common/abstractions/token.service';
(global as any).FormData = FormData;
export class NodeApiService extends ApiService {
constructor(tokenService: TokenService, platformUtilsService: PlatformUtilsService,
environmentService: EnvironmentService, logoutCallback: (expired: boolean) => Promise<void>,
customUserAgent: string = null, apiKeyRefresh: (clientId: string, clientSecret: string) => Promise<any>) {
super(tokenService, platformUtilsService, environmentService, logoutCallback, customUserAgent);
this.apiKeyRefresh = apiKeyRefresh;
}
constructor(
tokenService: TokenService,
platformUtilsService: PlatformUtilsService,
environmentService: EnvironmentService,
logoutCallback: (expired: boolean) => Promise<void>,
customUserAgent: string = null,
apiKeyRefresh: (clientId: string, clientSecret: string) => Promise<any>
) {
super(tokenService, platformUtilsService, environmentService, logoutCallback, customUserAgent);
this.apiKeyRefresh = apiKeyRefresh;
}
nativeFetch(request: Request): Promise<Response> {
const proxy = process.env.http_proxy || process.env.https_proxy;
if (proxy) {
(request as any).agent = new HttpsProxyAgent(proxy);
}
return fetch(request);
nativeFetch(request: Request): Promise<Response> {
const proxy = process.env.http_proxy || process.env.https_proxy;
if (proxy) {
(request as any).agent = new HttpsProxyAgent(proxy);
}
return fetch(request);
}
}

View File

@@ -1,263 +1,302 @@
import * as crypto from 'crypto';
import * as forge from 'node-forge';
import * as crypto from "crypto";
import * as forge from "node-forge";
import { CryptoFunctionService } from 'jslib-common/abstractions/cryptoFunction.service';
import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service";
import { DecryptParameters } from 'jslib-common/models/domain/decryptParameters';
import { SymmetricCryptoKey } from 'jslib-common/models/domain/symmetricCryptoKey';
import { DecryptParameters } from "jslib-common/models/domain/decryptParameters";
import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey";
import { Utils } from 'jslib-common/misc/utils';
import { Utils } from "jslib-common/misc/utils";
export class NodeCryptoFunctionService implements CryptoFunctionService {
pbkdf2(password: string | ArrayBuffer, salt: string | ArrayBuffer, algorithm: 'sha256' | 'sha512',
iterations: number): Promise<ArrayBuffer> {
const len = algorithm === 'sha256' ? 32 : 64;
const nodePassword = this.toNodeValue(password);
const nodeSalt = this.toNodeValue(salt);
return new Promise<ArrayBuffer>((resolve, reject) => {
crypto.pbkdf2(nodePassword, nodeSalt, iterations, len, algorithm, (error, key) => {
if (error != null) {
reject(error);
} else {
resolve(this.toArrayBuffer(key));
}
});
});
}
// ref: https://tools.ietf.org/html/rfc5869
async hkdf(ikm: ArrayBuffer, salt: string | ArrayBuffer, info: string | ArrayBuffer,
outputByteSize: number, algorithm: 'sha256' | 'sha512'): Promise<ArrayBuffer> {
const saltBuf = this.toArrayBuffer(salt);
const prk = await this.hmac(ikm, saltBuf, algorithm);
return this.hkdfExpand(prk, info, outputByteSize, algorithm);
}
// ref: https://tools.ietf.org/html/rfc5869
async hkdfExpand(prk: ArrayBuffer, info: string | ArrayBuffer, outputByteSize: number,
algorithm: 'sha256' | 'sha512'): Promise<ArrayBuffer> {
const hashLen = algorithm === 'sha256' ? 32 : 64;
if (outputByteSize > 255 * hashLen) {
throw new Error('outputByteSize is too large.');
}
const prkArr = new Uint8Array(prk);
if (prkArr.length < hashLen) {
throw new Error('prk is too small.');
}
const infoBuf = this.toArrayBuffer(info);
const infoArr = new Uint8Array(infoBuf);
let runningOkmLength = 0;
let previousT = new Uint8Array(0);
const n = Math.ceil(outputByteSize / hashLen);
const okm = new Uint8Array(n * hashLen);
for (let i = 0; i < n; i++) {
const t = new Uint8Array(previousT.length + infoArr.length + 1);
t.set(previousT);
t.set(infoArr, previousT.length);
t.set([i + 1], t.length - 1);
previousT = new Uint8Array(await this.hmac(t.buffer, prk, algorithm));
okm.set(previousT, runningOkmLength);
runningOkmLength += previousT.length;
if (runningOkmLength >= outputByteSize) {
break;
}
}
return okm.slice(0, outputByteSize).buffer;
}
hash(value: string | ArrayBuffer, algorithm: 'sha1' | 'sha256' | 'sha512' | 'md5'): Promise<ArrayBuffer> {
const nodeValue = this.toNodeValue(value);
const hash = crypto.createHash(algorithm);
hash.update(nodeValue);
return Promise.resolve(this.toArrayBuffer(hash.digest()));
}
hmac(value: ArrayBuffer, key: ArrayBuffer, algorithm: 'sha1' | 'sha256' | 'sha512'): Promise<ArrayBuffer> {
const nodeValue = this.toNodeBuffer(value);
const nodeKey = this.toNodeBuffer(key);
const hmac = crypto.createHmac(algorithm, nodeKey);
hmac.update(nodeValue);
return Promise.resolve(this.toArrayBuffer(hmac.digest()));
}
async compare(a: ArrayBuffer, b: ArrayBuffer): Promise<boolean> {
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);
}
compareFast(a: ArrayBuffer, b: ArrayBuffer): Promise<boolean> {
return this.compare(a, b);
}
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()]);
return Promise.resolve(this.toArrayBuffer(encBuf));
}
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> {
const decBuf = await this.aesDecrypt(parameters.data, parameters.iv, parameters.encKey);
return Utils.fromBufferToUtf8(decBuf);
}
aesDecrypt(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise<ArrayBuffer> {
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()]);
return Promise.resolve(this.toArrayBuffer(decBuf));
}
rsaEncrypt(data: ArrayBuffer, publicKey: ArrayBuffer, algorithm: 'sha1' | 'sha256'): Promise<ArrayBuffer> {
if (algorithm === 'sha256') {
throw new Error('Node crypto does not support RSA-OAEP SHA-256');
}
const pem = this.toPemPublicKey(publicKey);
const decipher = crypto.publicEncrypt(pem, this.toNodeBuffer(data));
return Promise.resolve(this.toArrayBuffer(decipher));
}
rsaDecrypt(data: ArrayBuffer, privateKey: ArrayBuffer, algorithm: 'sha1' | 'sha256'): Promise<ArrayBuffer> {
if (algorithm === 'sha256') {
throw new Error('Node crypto does not support RSA-OAEP SHA-256');
}
const pem = this.toPemPrivateKey(privateKey);
const decipher = crypto.privateDecrypt(pem, this.toNodeBuffer(data));
return Promise.resolve(this.toArrayBuffer(decipher));
}
rsaExtractPublicKey(privateKey: ArrayBuffer): Promise<ArrayBuffer> {
const privateKeyByteString = Utils.fromBufferToByteString(privateKey);
const privateKeyAsn1 = forge.asn1.fromDer(privateKeyByteString);
const forgePrivateKey = (forge as any).pki.privateKeyFromAsn1(privateKeyAsn1);
const forgePublicKey = (forge.pki as any).setRsaPublicKey(forgePrivateKey.n, forgePrivateKey.e);
const publicKeyAsn1 = (forge.pki as any).publicKeyToAsn1(forgePublicKey);
const publicKeyByteString = forge.asn1.toDer(publicKeyAsn1).data;
const publicKeyArray = Utils.fromByteStringToArray(publicKeyByteString);
return Promise.resolve(publicKeyArray.buffer);
}
async rsaGenerateKeyPair(length: 1024 | 2048 | 4096): Promise<[ArrayBuffer, ArrayBuffer]> {
return new Promise<[ArrayBuffer, ArrayBuffer]>((resolve, reject) => {
forge.pki.rsa.generateKeyPair({
bits: length,
workers: -1,
e: 0x10001, // 65537
}, (error, keyPair) => {
if (error != null) {
reject(error);
return;
}
const publicKeyAsn1 = (forge.pki as any).publicKeyToAsn1(keyPair.publicKey);
const publicKeyByteString = forge.asn1.toDer(publicKeyAsn1).getBytes();
const publicKey = Utils.fromByteStringToArray(publicKeyByteString);
const privateKeyAsn1 = (forge.pki as any).privateKeyToAsn1(keyPair.privateKey);
const privateKeyPkcs8 = (forge.pki as any).wrapRsaPrivateKey(privateKeyAsn1);
const privateKeyByteString = forge.asn1.toDer(privateKeyPkcs8).getBytes();
const privateKey = Utils.fromByteStringToArray(privateKeyByteString);
resolve([publicKey.buffer, privateKey.buffer]);
});
});
}
randomBytes(length: number): Promise<ArrayBuffer> {
return new Promise<ArrayBuffer>((resolve, reject) => {
crypto.randomBytes(length, (error, bytes) => {
if (error != null) {
reject(error);
} else {
resolve(this.toArrayBuffer(bytes));
}
});
});
}
private toNodeValue(value: string | ArrayBuffer): string | Buffer {
let nodeValue: string | Buffer;
if (typeof (value) === 'string') {
nodeValue = value;
pbkdf2(
password: string | ArrayBuffer,
salt: string | ArrayBuffer,
algorithm: "sha256" | "sha512",
iterations: number
): Promise<ArrayBuffer> {
const len = algorithm === "sha256" ? 32 : 64;
const nodePassword = this.toNodeValue(password);
const nodeSalt = this.toNodeValue(salt);
return new Promise<ArrayBuffer>((resolve, reject) => {
crypto.pbkdf2(nodePassword, nodeSalt, iterations, len, algorithm, (error, key) => {
if (error != null) {
reject(error);
} else {
nodeValue = this.toNodeBuffer(value);
resolve(this.toArrayBuffer(key));
}
return nodeValue;
});
});
}
// ref: https://tools.ietf.org/html/rfc5869
async hkdf(
ikm: ArrayBuffer,
salt: string | ArrayBuffer,
info: string | ArrayBuffer,
outputByteSize: number,
algorithm: "sha256" | "sha512"
): Promise<ArrayBuffer> {
const saltBuf = this.toArrayBuffer(salt);
const prk = await this.hmac(ikm, saltBuf, algorithm);
return this.hkdfExpand(prk, info, outputByteSize, algorithm);
}
// ref: https://tools.ietf.org/html/rfc5869
async hkdfExpand(
prk: ArrayBuffer,
info: string | ArrayBuffer,
outputByteSize: number,
algorithm: "sha256" | "sha512"
): Promise<ArrayBuffer> {
const hashLen = algorithm === "sha256" ? 32 : 64;
if (outputByteSize > 255 * hashLen) {
throw new Error("outputByteSize is too large.");
}
const prkArr = new Uint8Array(prk);
if (prkArr.length < hashLen) {
throw new Error("prk is too small.");
}
const infoBuf = this.toArrayBuffer(info);
const infoArr = new Uint8Array(infoBuf);
let runningOkmLength = 0;
let previousT = new Uint8Array(0);
const n = Math.ceil(outputByteSize / hashLen);
const okm = new Uint8Array(n * hashLen);
for (let i = 0; i < n; i++) {
const t = new Uint8Array(previousT.length + infoArr.length + 1);
t.set(previousT);
t.set(infoArr, previousT.length);
t.set([i + 1], t.length - 1);
previousT = new Uint8Array(await this.hmac(t.buffer, prk, algorithm));
okm.set(previousT, runningOkmLength);
runningOkmLength += previousT.length;
if (runningOkmLength >= outputByteSize) {
break;
}
}
return okm.slice(0, outputByteSize).buffer;
}
hash(
value: string | ArrayBuffer,
algorithm: "sha1" | "sha256" | "sha512" | "md5"
): Promise<ArrayBuffer> {
const nodeValue = this.toNodeValue(value);
const hash = crypto.createHash(algorithm);
hash.update(nodeValue);
return Promise.resolve(this.toArrayBuffer(hash.digest()));
}
hmac(
value: ArrayBuffer,
key: ArrayBuffer,
algorithm: "sha1" | "sha256" | "sha512"
): Promise<ArrayBuffer> {
const nodeValue = this.toNodeBuffer(value);
const nodeKey = this.toNodeBuffer(key);
const hmac = crypto.createHmac(algorithm, nodeKey);
hmac.update(nodeValue);
return Promise.resolve(this.toArrayBuffer(hmac.digest()));
}
async compare(a: ArrayBuffer, b: ArrayBuffer): Promise<boolean> {
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;
}
private toNodeBuffer(value: ArrayBuffer): Buffer {
return Buffer.from(new Uint8Array(value) as any);
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;
}
}
private toArrayBuffer(value: Buffer | string | ArrayBuffer): ArrayBuffer {
let buf: ArrayBuffer;
if (typeof (value) === 'string') {
buf = Utils.fromUtf8ToArray(value).buffer;
return true;
}
hmacFast(
value: ArrayBuffer,
key: ArrayBuffer,
algorithm: "sha1" | "sha256" | "sha512"
): Promise<ArrayBuffer> {
return this.hmac(value, key, algorithm);
}
compareFast(a: ArrayBuffer, b: ArrayBuffer): Promise<boolean> {
return this.compare(a, b);
}
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()]);
return Promise.resolve(this.toArrayBuffer(encBuf));
}
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> {
const decBuf = await this.aesDecrypt(parameters.data, parameters.iv, parameters.encKey);
return Utils.fromBufferToUtf8(decBuf);
}
aesDecrypt(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise<ArrayBuffer> {
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()]);
return Promise.resolve(this.toArrayBuffer(decBuf));
}
rsaEncrypt(
data: ArrayBuffer,
publicKey: ArrayBuffer,
algorithm: "sha1" | "sha256"
): Promise<ArrayBuffer> {
if (algorithm === "sha256") {
throw new Error("Node crypto does not support RSA-OAEP SHA-256");
}
const pem = this.toPemPublicKey(publicKey);
const decipher = crypto.publicEncrypt(pem, this.toNodeBuffer(data));
return Promise.resolve(this.toArrayBuffer(decipher));
}
rsaDecrypt(
data: ArrayBuffer,
privateKey: ArrayBuffer,
algorithm: "sha1" | "sha256"
): Promise<ArrayBuffer> {
if (algorithm === "sha256") {
throw new Error("Node crypto does not support RSA-OAEP SHA-256");
}
const pem = this.toPemPrivateKey(privateKey);
const decipher = crypto.privateDecrypt(pem, this.toNodeBuffer(data));
return Promise.resolve(this.toArrayBuffer(decipher));
}
rsaExtractPublicKey(privateKey: ArrayBuffer): Promise<ArrayBuffer> {
const privateKeyByteString = Utils.fromBufferToByteString(privateKey);
const privateKeyAsn1 = forge.asn1.fromDer(privateKeyByteString);
const forgePrivateKey = (forge as any).pki.privateKeyFromAsn1(privateKeyAsn1);
const forgePublicKey = (forge.pki as any).setRsaPublicKey(forgePrivateKey.n, forgePrivateKey.e);
const publicKeyAsn1 = (forge.pki as any).publicKeyToAsn1(forgePublicKey);
const publicKeyByteString = forge.asn1.toDer(publicKeyAsn1).data;
const publicKeyArray = Utils.fromByteStringToArray(publicKeyByteString);
return Promise.resolve(publicKeyArray.buffer);
}
async rsaGenerateKeyPair(length: 1024 | 2048 | 4096): Promise<[ArrayBuffer, ArrayBuffer]> {
return new Promise<[ArrayBuffer, ArrayBuffer]>((resolve, reject) => {
forge.pki.rsa.generateKeyPair(
{
bits: length,
workers: -1,
e: 0x10001, // 65537
},
(error, keyPair) => {
if (error != null) {
reject(error);
return;
}
const publicKeyAsn1 = (forge.pki as any).publicKeyToAsn1(keyPair.publicKey);
const publicKeyByteString = forge.asn1.toDer(publicKeyAsn1).getBytes();
const publicKey = Utils.fromByteStringToArray(publicKeyByteString);
const privateKeyAsn1 = (forge.pki as any).privateKeyToAsn1(keyPair.privateKey);
const privateKeyPkcs8 = (forge.pki as any).wrapRsaPrivateKey(privateKeyAsn1);
const privateKeyByteString = forge.asn1.toDer(privateKeyPkcs8).getBytes();
const privateKey = Utils.fromByteStringToArray(privateKeyByteString);
resolve([publicKey.buffer, privateKey.buffer]);
}
);
});
}
randomBytes(length: number): Promise<ArrayBuffer> {
return new Promise<ArrayBuffer>((resolve, reject) => {
crypto.randomBytes(length, (error, bytes) => {
if (error != null) {
reject(error);
} else {
buf = new Uint8Array(value).buffer;
resolve(this.toArrayBuffer(bytes));
}
return buf;
}
});
});
}
private toPemPrivateKey(key: ArrayBuffer): string {
const byteString = Utils.fromBufferToByteString(key);
const asn1 = forge.asn1.fromDer(byteString);
const privateKey = (forge as any).pki.privateKeyFromAsn1(asn1);
const rsaPrivateKey = (forge.pki as any).privateKeyToAsn1(privateKey);
const privateKeyInfo = (forge.pki as any).wrapRsaPrivateKey(rsaPrivateKey);
return (forge.pki as any).privateKeyInfoToPem(privateKeyInfo);
private toNodeValue(value: string | ArrayBuffer): string | Buffer {
let nodeValue: string | Buffer;
if (typeof value === "string") {
nodeValue = value;
} else {
nodeValue = this.toNodeBuffer(value);
}
return nodeValue;
}
private toPemPublicKey(key: ArrayBuffer): string {
const byteString = Utils.fromBufferToByteString(key);
const asn1 = forge.asn1.fromDer(byteString);
const publicKey = (forge as any).pki.publicKeyFromAsn1(asn1);
return (forge.pki as any).publicKeyToPem(publicKey);
private toNodeBuffer(value: ArrayBuffer): Buffer {
return Buffer.from(new Uint8Array(value) as any);
}
private toArrayBuffer(value: Buffer | string | ArrayBuffer): ArrayBuffer {
let buf: ArrayBuffer;
if (typeof value === "string") {
buf = Utils.fromUtf8ToArray(value).buffer;
} else {
buf = new Uint8Array(value).buffer;
}
return buf;
}
private toPemPrivateKey(key: ArrayBuffer): string {
const byteString = Utils.fromBufferToByteString(key);
const asn1 = forge.asn1.fromDer(byteString);
const privateKey = (forge as any).pki.privateKeyFromAsn1(asn1);
const rsaPrivateKey = (forge.pki as any).privateKeyToAsn1(privateKey);
const privateKeyInfo = (forge.pki as any).wrapRsaPrivateKey(rsaPrivateKey);
return (forge.pki as any).privateKeyInfoToPem(privateKeyInfo);
}
private toPemPublicKey(key: ArrayBuffer): string {
const byteString = Utils.fromBufferToByteString(key);
const asn1 = forge.asn1.fromDer(byteString);
const publicKey = (forge as any).pki.publicKeyFromAsn1(asn1);
return (forge.pki as any).publicKeyToPem(publicKey);
}
}