diff --git a/src/services/webCryptoFunction.service.spec.ts b/src/services/webCryptoFunction.service.spec.ts index 498aa17b0f..c5c9f70e91 100644 --- a/src/services/webCryptoFunction.service.spec.ts +++ b/src/services/webCryptoFunction.service.spec.ts @@ -65,6 +65,54 @@ describe('WebCrypto Function Service', () => { testHmac(true, 'sha256', sha256Mac); testHmac(true, 'sha512', sha512Mac); }); + + describe('aesEncrypt', () => { + it('should successfully aes 256 encrypt data', async () => { + const webCryptoFunctionService = getWebCryptoFunctionService(); + const iv = makeStaticByteArray(16); + const key = makeStaticByteArray(32); + const data = UtilsService.fromUtf8ToArray('EncryptMe!'); + const encValue = await webCryptoFunctionService.aesEncrypt(data.buffer, iv.buffer, key.buffer); + expect(UtilsService.fromBufferToB64(encValue)).toBe('ByUF8vhyX4ddU9gcooznwA=='); + }); + }); + + describe('aesDecryptSmall', () => { + it('should successfully aes 256 decrypt data', async () => { + const webCryptoFunctionService = getWebCryptoFunctionService(); + const iv = makeStaticByteArray(16); + const key = makeStaticByteArray(32); + const data = UtilsService.fromB64ToArray('ByUF8vhyX4ddU9gcooznwA=='); + const decValue = await webCryptoFunctionService.aesDecryptSmall(data.buffer, iv.buffer, key.buffer); + expect(UtilsService.fromBufferToUtf8(decValue)).toBe('EncryptMe!'); + }); + }); + + describe('aesDecryptLarge', () => { + it('should successfully aes 256 decrypt data', async () => { + const webCryptoFunctionService = getWebCryptoFunctionService(); + const iv = makeStaticByteArray(16); + const key = makeStaticByteArray(32); + const data = UtilsService.fromB64ToArray('ByUF8vhyX4ddU9gcooznwA=='); + const decValue = await webCryptoFunctionService.aesDecryptLarge(data.buffer, iv.buffer, key.buffer); + expect(UtilsService.fromBufferToUtf8(decValue)).toBe('EncryptMe!'); + }); + }); + + describe('randomBytes', () => { + it('should make a value of the correct length', async () => { + const webCryptoFunctionService = getWebCryptoFunctionService(); + const randomData = await webCryptoFunctionService.randomBytes(16); + expect(randomData.byteLength).toBe(16); + }); + + it('should not make the same value twice', async () => { + const webCryptoFunctionService = getWebCryptoFunctionService(); + const randomData = await webCryptoFunctionService.randomBytes(16); + const randomData2 = await webCryptoFunctionService.randomBytes(16); + expect(randomData.byteLength === randomData2.byteLength && randomData !== randomData2).toBeTruthy(); + }); + }); }); function testPbkdf2(edge: boolean, algorithm: 'sha256' | 'sha512', regularKey: string, @@ -149,6 +197,14 @@ function getWebCryptoFunctionService(edge = false) { return new WebCryptoFunctionService(window, platformUtilsService); } +function makeStaticByteArray(length: number) { + const arr = new Uint8Array(length); + for (let i = 0; i < length; i++) { + arr[i] = i; + } + return arr; +} + class BrowserPlatformUtilsService implements PlatformUtilsService { identityClientId: string; getDevice: () => DeviceType; diff --git a/src/services/webCryptoFunction.service.ts b/src/services/webCryptoFunction.service.ts index 4278e0647a..dbba69ad1f 100644 --- a/src/services/webCryptoFunction.service.ts +++ b/src/services/webCryptoFunction.service.ts @@ -80,6 +80,34 @@ export class WebCryptoFunctionService implements CryptoFunctionService { return await this.subtle.sign(signingAlgorithm, impKey, value); } + async aesEncrypt(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise { + const impKey = await this.subtle.importKey('raw', key, { name: 'AES-CBC' }, false, ['encrypt']); + return await this.subtle.encrypt({ name: 'AES-CBC', iv: iv }, impKey, data); + } + + async aesDecryptSmall(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise { + const dataBytes = this.toByteString(data); + const ivBytes = this.toByteString(iv); + const keyBytes = this.toByteString(key); + const dataBuffer = (forge as any).util.createBuffer(dataBytes); + const decipher = (forge as any).cipher.createDecipher('AES-CBC', keyBytes); + decipher.start({ iv: ivBytes }); + decipher.update(dataBuffer); + decipher.finish(); + return this.fromByteStringToBuf(decipher.output.getBytes()); + } + + async aesDecryptLarge(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise { + const impKey = await this.subtle.importKey('raw', key, { name: 'AES-CBC' }, false, ['decrypt']); + return await this.subtle.decrypt({ name: 'AES-CBC', iv: iv }, impKey, data); + } + + randomBytes(length: number): ArrayBuffer { + const arr = new Uint8Array(length); + this.crypto.getRandomValues(arr); + return arr.buffer; + } + private toBuf(value: string | ArrayBuffer): ArrayBuffer { let buf: ArrayBuffer; if (typeof (value) === 'string') {