From 571c5a329b88c680de02dc882bd48fa2741ca634 Mon Sep 17 00:00:00 2001 From: Hinton Date: Fri, 29 Apr 2022 14:20:07 +0200 Subject: [PATCH] Add encrypt and decrypt example of EncObject --- common/spec/services/crypto.service.spec.ts | 79 +++++++++++++++++++++ common/src/abstractions/crypto.service.ts | 4 ++ common/src/models/domain/encObject.ts | 8 +-- common/src/services/crypto.service.ts | 35 +++++++++ 4 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 common/spec/services/crypto.service.spec.ts diff --git a/common/spec/services/crypto.service.spec.ts b/common/spec/services/crypto.service.spec.ts new file mode 100644 index 00000000..4d75a575 --- /dev/null +++ b/common/spec/services/crypto.service.spec.ts @@ -0,0 +1,79 @@ +import { Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; + +import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service"; +import { LogService } from "jslib-common/abstractions/log.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { StateService } from "jslib-common/abstractions/state.service"; +import { Utils } from "jslib-common/misc/utils"; +import { EncArrayBuffer } from "jslib-common/models/domain/encArrayBuffer"; +import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey"; +import { CryptoService } from "jslib-common/services/crypto.service"; +import { WebCryptoFunctionService } from "jslib-common/services/webCryptoFunction.service"; + +import { makeStaticByteArray } from "../utils"; + +describe("Crypto Service", () => { + let cryptoFunctionService: CryptoFunctionService; + let platformUtilsService: SubstituteOf; + let logService: SubstituteOf; + let stateService: SubstituteOf; + + let cryptoService: CryptoService; + + beforeEach(() => { + cryptoFunctionService = new WebCryptoFunctionService(window); + platformUtilsService = Substitute.for(); + logService = Substitute.for(); + stateService = Substitute.for(); + + cryptoService = new CryptoService( + cryptoFunctionService, + platformUtilsService, + logService, + stateService + ); + }); + + it("encrypt EncObject", async () => { + const data = { + name: "Random", + }; + + const spy = jest + .spyOn(cryptoFunctionService, "randomBytes") + .mockImplementation(() => Promise.resolve(makeStaticByteArray(16))); + + const key = makeStaticByteArray(32); + const symKey = new SymmetricCryptoKey(key.buffer); + + const encrypted = await cryptoService.encryptObject(data, symKey); + + expect(encrypted).toEqual({ + encryptionType: 0, + data: "HQjAyiEJss8N3xwNqJen8R4aE/XToFeV7LIBI7SYkTc=", + iv: "AAECAwQFBgcICQoLDA0ODw==", + mac: null, + }); + + spy.mockRestore(); + }); + + it("decrypt EncObject", async () => { + const key = makeStaticByteArray(32); + const symKey = new SymmetricCryptoKey(key.buffer); + + const decrypted = await cryptoService.decryptObject( + { + encryptionType: 0, + data: "HQjAyiEJss8N3xwNqJen8R4aE/XToFeV7LIBI7SYkTc=", + iv: "AAECAwQFBgcICQoLDA0ODw==", + mac: null, + }, + symKey + ); + + expect(decrypted).toEqual({ + name: "Random", + }); + }); +}); diff --git a/common/src/abstractions/crypto.service.ts b/common/src/abstractions/crypto.service.ts index bc61ba7e..01be9c29 100644 --- a/common/src/abstractions/crypto.service.ts +++ b/common/src/abstractions/crypto.service.ts @@ -1,3 +1,5 @@ +import { EncObject } from "jslib-common/models/domain/encObject"; + import { HashPurpose } from "../enums/hashPurpose"; import { KdfType } from "../enums/kdfType"; import { KeySuffixOptions } from "../enums/keySuffixOptions"; @@ -75,12 +77,14 @@ export abstract class CryptoService { encKey?: SymmetricCryptoKey ) => Promise<[SymmetricCryptoKey, EncString]>; encrypt: (plainValue: string | ArrayBuffer, key?: SymmetricCryptoKey) => Promise; + encryptObject: (plainValue: T, key?: SymmetricCryptoKey) => Promise>; encryptToBytes: (plainValue: ArrayBuffer, key?: SymmetricCryptoKey) => Promise; rsaEncrypt: (data: ArrayBuffer, publicKey?: ArrayBuffer) => Promise; rsaDecrypt: (encValue: string, privateKeyValue?: ArrayBuffer) => Promise; decryptToBytes: (encString: EncString, key?: SymmetricCryptoKey) => Promise; decryptToUtf8: (encString: EncString, key?: SymmetricCryptoKey) => Promise; decryptFromBytes: (encBuf: ArrayBuffer, key: SymmetricCryptoKey) => Promise; + decryptObject: (encrypted: EncObject, key?: SymmetricCryptoKey) => Promise; randomNumber: (min: number, max: number) => Promise; validateKey: (key: SymmetricCryptoKey) => Promise; } diff --git a/common/src/models/domain/encObject.ts b/common/src/models/domain/encObject.ts index a3feaaff..5d259ef0 100644 --- a/common/src/models/domain/encObject.ts +++ b/common/src/models/domain/encObject.ts @@ -12,9 +12,9 @@ export class EncObject { * @param mac */ constructor( - private encryptionType: EncryptionType, - private data: string, - private iv: string, - private mac: string + public encryptionType: EncryptionType, + public data: string, + public iv: string, + public mac: string ) {} } diff --git a/common/src/services/crypto.service.ts b/common/src/services/crypto.service.ts index c49d7763..eb70ccd2 100644 --- a/common/src/services/crypto.service.ts +++ b/common/src/services/crypto.service.ts @@ -1,5 +1,7 @@ import * as bigInt from "big-integer"; +import { EncObject } from "jslib-common/models/domain/encObject"; + import { CryptoService as CryptoServiceAbstraction } from "../abstractions/crypto.service"; import { CryptoFunctionService } from "../abstractions/cryptoFunction.service"; import { LogService } from "../abstractions/log.service"; @@ -522,6 +524,39 @@ export class CryptoService implements CryptoServiceAbstraction { return new EncString(encObj.key.encType, data, iv, mac); } + async encryptObject(obj: T, key?: SymmetricCryptoKey): Promise> { + if (obj == null) { + return null; + } + + const json = JSON.stringify(obj); + const plainBuf = Utils.fromUtf8ToArray(json).buffer; + + const encObj = await this.aesEncrypt(plainBuf, key); + const iv = Utils.fromBufferToB64(encObj.iv); + const data = Utils.fromBufferToB64(encObj.data); + const mac = encObj.mac != null ? Utils.fromBufferToB64(encObj.mac) : null; + return new EncObject(encObj.key.encType, data, iv, mac); + } + + async decryptObject(obj: EncObject, key?: SymmetricCryptoKey): Promise { + if (obj == null) { + return null; + } + + const decryptedJson = await this.aesDecryptToUtf8( + obj.encryptionType, + obj.data, + obj.iv, + obj.mac, + key + ); + + const decrypted = JSON.parse(decryptedJson); + + return decrypted; + } + async encryptToBytes(plainValue: ArrayBuffer, key?: SymmetricCryptoKey): Promise { const encValue = await this.aesEncrypt(plainValue, key); let macLen = 0;