mirror of https://github.com/waku-org/js-waku.git
Implement and test symmetric encryption in the browser
This commit is contained in:
parent
8c66022a18
commit
57f65267c2
|
@ -18,11 +18,8 @@ if (subtle === undefined) {
|
||||||
throw new Error('Failed to load Subtle CryptoAPI');
|
throw new Error('Failed to load Subtle CryptoAPI');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export async function encrypt(
|
||||||
* Proceed with symmetric encryption of `clearText` value.
|
iv: Buffer | Uint8Array,
|
||||||
*/
|
|
||||||
async function encrypt(
|
|
||||||
iv: Buffer,
|
|
||||||
key: Buffer,
|
key: Buffer,
|
||||||
clearText: Buffer
|
clearText: Buffer
|
||||||
): Promise<Buffer> {
|
): Promise<Buffer> {
|
||||||
|
@ -34,10 +31,7 @@ async function encrypt(
|
||||||
.then(Buffer.from);
|
.then(Buffer.from);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export async function decrypt(
|
||||||
* Proceed with symmetric decryption of `cipherText` value.
|
|
||||||
*/
|
|
||||||
async function decrypt(
|
|
||||||
iv: Buffer,
|
iv: Buffer,
|
||||||
key: Buffer,
|
key: Buffer,
|
||||||
cipherText: Buffer
|
cipherText: Buffer
|
||||||
|
@ -45,21 +39,17 @@ async function decrypt(
|
||||||
return subtle
|
return subtle
|
||||||
.importKey('raw', key, Algorithm, false, ['decrypt'])
|
.importKey('raw', key, Algorithm, false, ['decrypt'])
|
||||||
.then((cryptoKey) =>
|
.then((cryptoKey) =>
|
||||||
subtle.encrypt({ iv, ...Algorithm }, cryptoKey, cipherText)
|
subtle.decrypt({ iv, ...Algorithm }, cryptoKey, cipherText)
|
||||||
)
|
)
|
||||||
.then(Buffer.from);
|
.then(Buffer.from);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export function generateKeyForSymmetricEnc(): Buffer {
|
||||||
* Generate a new private key for Symmetric encryption purposes.
|
|
||||||
*/
|
|
||||||
function generateKeyForSymmetricEnc(): Buffer {
|
|
||||||
return crypto.getRandomValues(Buffer.alloc(SymmetricKeySize));
|
return crypto.getRandomValues(Buffer.alloc(SymmetricKeySize));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export function generateIv(): Uint8Array {
|
||||||
* Generate an Initialisation Vector (iv) for for Symmetric encryption purposes.
|
const iv = new Uint8Array(IvSize);
|
||||||
*/
|
crypto.getRandomValues(iv);
|
||||||
function generateIv(): Buffer {
|
return iv;
|
||||||
return crypto.getRandomValues(Buffer.alloc(IvSize));
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,17 +3,33 @@ export const IvSize = 12;
|
||||||
export const TagSize = 16;
|
export const TagSize = 16;
|
||||||
|
|
||||||
export interface Symmetric {
|
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<Buffer>;
|
||||||
|
/**
|
||||||
|
* Proceed with symmetric decryption of `cipherText` value.
|
||||||
|
*/
|
||||||
|
decrypt: (iv: Buffer, key: Buffer, cipherText: Buffer) => Promise<Buffer>;
|
||||||
|
/**
|
||||||
|
* Generate a new private key for Symmetric encryption purposes.
|
||||||
|
*/
|
||||||
generateKeyForSymmetricEnc: () => Buffer;
|
generateKeyForSymmetricEnc: () => Buffer;
|
||||||
generateIv: () => Buffer;
|
/**
|
||||||
|
* Generate an Initialization Vector (iv) for for Symmetric encryption purposes.
|
||||||
|
*/
|
||||||
|
generateIv: () => Uint8Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
export let symmetric: Symmetric = {} as unknown as Symmetric;
|
export let symmetric: Symmetric = {} as unknown as Symmetric;
|
||||||
|
|
||||||
import('./browser')
|
import('./browser')
|
||||||
.then((mod) => {
|
.then((mod) => {
|
||||||
symmetric = mod as unknown as Symmetric;
|
symmetric = mod;
|
||||||
})
|
})
|
||||||
.catch((eBrowser) => {
|
.catch((eBrowser) => {
|
||||||
import('./node')
|
import('./node')
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import { createCipheriv, createDecipheriv, randomBytes } from 'crypto';
|
import { createCipheriv, createDecipheriv, randomBytes } from 'crypto';
|
||||||
|
|
||||||
import { IvSize, SymmetricKeySize } from './index';
|
import { IvSize, SymmetricKeySize, TagSize } from './index';
|
||||||
|
|
||||||
const Algorithm = 'aes-256-gcm';
|
const Algorithm = 'aes-256-gcm';
|
||||||
|
|
||||||
/**
|
export async function encrypt(
|
||||||
* Proceed with symmetric encryption of `clearText` value.
|
iv: Buffer | Uint8Array,
|
||||||
*/
|
key: Buffer,
|
||||||
export function encrypt(iv: Buffer, key: Buffer, clearText: Buffer): Buffer {
|
clearText: Buffer
|
||||||
|
): Promise<Buffer> {
|
||||||
const cipher = createCipheriv(Algorithm, key, iv);
|
const cipher = createCipheriv(Algorithm, key, iv);
|
||||||
const a = cipher.update(clearText);
|
const a = cipher.update(clearText);
|
||||||
const b = cipher.final();
|
const b = cipher.final();
|
||||||
|
@ -15,15 +16,14 @@ export function encrypt(iv: Buffer, key: Buffer, clearText: Buffer): Buffer {
|
||||||
return Buffer.concat([a, b, tag]);
|
return Buffer.concat([a, b, tag]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export async function decrypt(
|
||||||
* Proceed with symmetric decryption of `cipherText` value.
|
|
||||||
*/
|
|
||||||
export function decrypt(
|
|
||||||
iv: Buffer,
|
iv: Buffer,
|
||||||
tag: Buffer,
|
|
||||||
key: Buffer,
|
key: Buffer,
|
||||||
cipherText: Buffer
|
data: Buffer
|
||||||
): Buffer {
|
): Promise<Buffer> {
|
||||||
|
const tagStart = data.length - TagSize;
|
||||||
|
const cipherText = data.slice(0, tagStart);
|
||||||
|
const tag = data.slice(tagStart);
|
||||||
const decipher = createDecipheriv(Algorithm, key, iv);
|
const decipher = createDecipheriv(Algorithm, key, iv);
|
||||||
decipher.setAuthTag(tag);
|
decipher.setAuthTag(tag);
|
||||||
const a = decipher.update(cipherText);
|
const a = decipher.update(cipherText);
|
||||||
|
@ -31,16 +31,10 @@ export function decrypt(
|
||||||
return Buffer.concat([a, b]);
|
return Buffer.concat([a, b]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a new private key for Symmetric encryption purposes.
|
|
||||||
*/
|
|
||||||
export function generateKeyForSymmetricEnc(): Buffer {
|
export function generateKeyForSymmetricEnc(): Buffer {
|
||||||
return randomBytes(SymmetricKeySize);
|
return randomBytes(SymmetricKeySize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate an Initialisation Vector (iv) for for Symmetric encryption purposes.
|
|
||||||
*/
|
|
||||||
export function generateIv(): Buffer {
|
export function generateIv(): Buffer {
|
||||||
return randomBytes(IvSize);
|
return randomBytes(IvSize);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import * as secp256k1 from 'secp256k1';
|
||||||
|
|
||||||
import { hexToBuf } from '../utils';
|
import { hexToBuf } from '../utils';
|
||||||
|
|
||||||
import { IvSize, symmetric, TagSize } from './symmetric';
|
import { IvSize, symmetric } from './symmetric';
|
||||||
|
|
||||||
const FlagsLength = 1;
|
const FlagsLength = 1;
|
||||||
const FlagMask = 3; // 0011
|
const FlagMask = 3; // 0011
|
||||||
|
@ -146,7 +146,7 @@ export async function encryptSymmetric(
|
||||||
const iv = symmetric.generateIv();
|
const iv = symmetric.generateIv();
|
||||||
|
|
||||||
// Returns `cipher | tag`
|
// 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]);
|
return Buffer.concat([cipher, iv]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,13 +164,11 @@ export async function decryptSymmetric(
|
||||||
key: Uint8Array | Buffer | string
|
key: Uint8Array | Buffer | string
|
||||||
): Promise<Uint8Array> {
|
): Promise<Uint8Array> {
|
||||||
const data = Buffer.from(payload);
|
const data = Buffer.from(payload);
|
||||||
const ivStart = payload.length - IvSize;
|
const ivStart = data.length - IvSize;
|
||||||
const tagStart = ivStart - TagSize;
|
const cipher = data.slice(0, ivStart);
|
||||||
const cipher = data.slice(0, tagStart);
|
|
||||||
const tag = data.slice(tagStart, ivStart);
|
|
||||||
const iv = data.slice(ivStart);
|
const iv = data.slice(ivStart);
|
||||||
|
|
||||||
return symmetric.decrypt(iv, tag, hexToBuf(key), cipher);
|
return symmetric.decrypt(iv, hexToBuf(key), cipher);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -5,7 +5,9 @@ import {
|
||||||
clearDecode,
|
clearDecode,
|
||||||
clearEncode,
|
clearEncode,
|
||||||
decryptAsymmetric,
|
decryptAsymmetric,
|
||||||
|
decryptSymmetric,
|
||||||
encryptAsymmetric,
|
encryptAsymmetric,
|
||||||
|
encryptSymmetric,
|
||||||
getPublicKey,
|
getPublicKey,
|
||||||
} from '../../lib/waku_message/version_1';
|
} 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);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue