From 57f65267c2aadd5dc49293b715a6e54952c14966 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Wed, 14 Jul 2021 16:31:11 +1000 Subject: [PATCH] Implement and test symmetric encryption in the browser --- src/lib/waku_message/symmetric/browser.ts | 28 +++++++-------------- src/lib/waku_message/symmetric/index.ts | 24 +++++++++++++++--- src/lib/waku_message/symmetric/node.ts | 30 +++++++++-------------- src/lib/waku_message/version_1.ts | 12 ++++----- src/tests/browser/version_1.spec.ts | 17 +++++++++++++ 5 files changed, 63 insertions(+), 48 deletions(-) diff --git a/src/lib/waku_message/symmetric/browser.ts b/src/lib/waku_message/symmetric/browser.ts index bda8d7eda5..4f092f74da 100644 --- a/src/lib/waku_message/symmetric/browser.ts +++ b/src/lib/waku_message/symmetric/browser.ts @@ -18,11 +18,8 @@ if (subtle === undefined) { throw new Error('Failed to load Subtle CryptoAPI'); } -/** - * Proceed with symmetric encryption of `clearText` value. - */ -async function encrypt( - iv: Buffer, +export async function encrypt( + iv: Buffer | Uint8Array, key: Buffer, clearText: Buffer ): Promise { @@ -34,10 +31,7 @@ async function encrypt( .then(Buffer.from); } -/** - * Proceed with symmetric decryption of `cipherText` value. - */ -async function decrypt( +export async function decrypt( iv: Buffer, key: Buffer, cipherText: Buffer @@ -45,21 +39,17 @@ async function decrypt( return subtle .importKey('raw', key, Algorithm, false, ['decrypt']) .then((cryptoKey) => - subtle.encrypt({ iv, ...Algorithm }, cryptoKey, cipherText) + subtle.decrypt({ iv, ...Algorithm }, cryptoKey, cipherText) ) .then(Buffer.from); } -/** - * Generate a new private key for Symmetric encryption purposes. - */ -function generateKeyForSymmetricEnc(): Buffer { +export function generateKeyForSymmetricEnc(): Buffer { return crypto.getRandomValues(Buffer.alloc(SymmetricKeySize)); } -/** - * Generate an Initialisation Vector (iv) for for Symmetric encryption purposes. - */ -function generateIv(): Buffer { - return crypto.getRandomValues(Buffer.alloc(IvSize)); +export function generateIv(): Uint8Array { + const iv = new Uint8Array(IvSize); + crypto.getRandomValues(iv); + return iv; } diff --git a/src/lib/waku_message/symmetric/index.ts b/src/lib/waku_message/symmetric/index.ts index 06be09d234..76a0b2faa1 100644 --- a/src/lib/waku_message/symmetric/index.ts +++ b/src/lib/waku_message/symmetric/index.ts @@ -3,17 +3,33 @@ export const IvSize = 12; export const TagSize = 16; export interface Symmetric { - encrypt: (iv: Buffer, key: Buffer, clearText: Buffer) => Buffer; - decrypt: (iv: Buffer, tag: Buffer, key: Buffer, cipherText: Buffer) => Buffer; + /** + * Proceed with symmetric encryption of `clearText` value. + */ + encrypt: ( + iv: Buffer | Uint8Array, + key: Buffer, + clearText: Buffer + ) => Promise; + /** + * Proceed with symmetric decryption of `cipherText` value. + */ + decrypt: (iv: Buffer, key: Buffer, cipherText: Buffer) => Promise; + /** + * Generate a new private key for Symmetric encryption purposes. + */ generateKeyForSymmetricEnc: () => Buffer; - generateIv: () => Buffer; + /** + * Generate an Initialization Vector (iv) for for Symmetric encryption purposes. + */ + generateIv: () => Uint8Array; } export let symmetric: Symmetric = {} as unknown as Symmetric; import('./browser') .then((mod) => { - symmetric = mod as unknown as Symmetric; + symmetric = mod; }) .catch((eBrowser) => { import('./node') diff --git a/src/lib/waku_message/symmetric/node.ts b/src/lib/waku_message/symmetric/node.ts index 53c021a584..2ea07f4b5b 100644 --- a/src/lib/waku_message/symmetric/node.ts +++ b/src/lib/waku_message/symmetric/node.ts @@ -1,13 +1,14 @@ import { createCipheriv, createDecipheriv, randomBytes } from 'crypto'; -import { IvSize, SymmetricKeySize } from './index'; +import { IvSize, SymmetricKeySize, TagSize } from './index'; const Algorithm = 'aes-256-gcm'; -/** - * Proceed with symmetric encryption of `clearText` value. - */ -export function encrypt(iv: Buffer, key: Buffer, clearText: Buffer): Buffer { +export async function encrypt( + iv: Buffer | Uint8Array, + key: Buffer, + clearText: Buffer +): Promise { const cipher = createCipheriv(Algorithm, key, iv); const a = cipher.update(clearText); const b = cipher.final(); @@ -15,15 +16,14 @@ export function encrypt(iv: Buffer, key: Buffer, clearText: Buffer): Buffer { return Buffer.concat([a, b, tag]); } -/** - * Proceed with symmetric decryption of `cipherText` value. - */ -export function decrypt( +export async function decrypt( iv: Buffer, - tag: Buffer, key: Buffer, - cipherText: Buffer -): Buffer { + data: Buffer +): Promise { + const tagStart = data.length - TagSize; + const cipherText = data.slice(0, tagStart); + const tag = data.slice(tagStart); const decipher = createDecipheriv(Algorithm, key, iv); decipher.setAuthTag(tag); const a = decipher.update(cipherText); @@ -31,16 +31,10 @@ export function decrypt( return Buffer.concat([a, b]); } -/** - * Generate a new private key for Symmetric encryption purposes. - */ export function generateKeyForSymmetricEnc(): Buffer { return randomBytes(SymmetricKeySize); } -/** - * Generate an Initialisation Vector (iv) for for Symmetric encryption purposes. - */ export function generateIv(): Buffer { return randomBytes(IvSize); } diff --git a/src/lib/waku_message/version_1.ts b/src/lib/waku_message/version_1.ts index 30abafec40..7cdbdf68e8 100644 --- a/src/lib/waku_message/version_1.ts +++ b/src/lib/waku_message/version_1.ts @@ -7,7 +7,7 @@ import * as secp256k1 from 'secp256k1'; import { hexToBuf } from '../utils'; -import { IvSize, symmetric, TagSize } from './symmetric'; +import { IvSize, symmetric } from './symmetric'; const FlagsLength = 1; const FlagMask = 3; // 0011 @@ -146,7 +146,7 @@ export async function encryptSymmetric( const iv = symmetric.generateIv(); // Returns `cipher | tag` - const cipher = symmetric.encrypt(iv, hexToBuf(key), Buffer.from(data)); + const cipher = await symmetric.encrypt(iv, hexToBuf(key), Buffer.from(data)); return Buffer.concat([cipher, iv]); } @@ -164,13 +164,11 @@ export async function decryptSymmetric( key: Uint8Array | Buffer | string ): Promise { const data = Buffer.from(payload); - const ivStart = payload.length - IvSize; - const tagStart = ivStart - TagSize; - const cipher = data.slice(0, tagStart); - const tag = data.slice(tagStart, ivStart); + const ivStart = data.length - IvSize; + const cipher = data.slice(0, ivStart); const iv = data.slice(ivStart); - return symmetric.decrypt(iv, tag, hexToBuf(key), cipher); + return symmetric.decrypt(iv, hexToBuf(key), cipher); } /** diff --git a/src/tests/browser/version_1.spec.ts b/src/tests/browser/version_1.spec.ts index fd5348e87e..8721e40d74 100644 --- a/src/tests/browser/version_1.spec.ts +++ b/src/tests/browser/version_1.spec.ts @@ -5,7 +5,9 @@ import { clearDecode, clearEncode, decryptAsymmetric, + decryptSymmetric, encryptAsymmetric, + encryptSymmetric, getPublicKey, } from '../../lib/waku_message/version_1'; @@ -54,4 +56,19 @@ describe('Waku Message Version 1', function () { ) ); }); + + it('Symmetric encrypt & Decrypt', async function () { + await fc.assert( + fc.asyncProperty( + fc.uint8Array(), + fc.uint8Array({ minLength: 32, maxLength: 32 }), + async (message, key) => { + const enc = await encryptSymmetric(message, key); + const res = await decryptSymmetric(enc, key); + + expect(res).deep.equal(message); + } + ) + ); + }); });