@jsxc/jsxc
Version:
Real-time XMPP chat application with video calls, file transfer and encrypted communication
73 lines (58 loc) • 2.19 kB
text/typescript
import ArrayBufferUtils from '../util/ArrayBuffer';
import { AES_TAG_LENGTH, AES_KEY_LENGTH, AES_EXTRACTABLE } from '../util/Const';
const ALGO_NAME = 'AES-GCM';
export async function decrypt(exportedAESKey: ArrayBuffer, iv: Uint8Array, data: ArrayBuffer): Promise<string> {
let key = await window.crypto.subtle.importKey('raw', exportedAESKey, ALGO_NAME, false, ['decrypt']);
let decryptedBuffer = await window.crypto.subtle.decrypt(
{
name: ALGO_NAME,
iv,
tagLength: AES_TAG_LENGTH,
},
key,
data
);
return ArrayBufferUtils.decode(decryptedBuffer);
}
export async function encrypt(plaintext): Promise<{ keydata: ArrayBuffer; iv: BufferSource; payload: ArrayBuffer }> {
let iv = window.crypto.getRandomValues(new Uint8Array(12));
let key = await generateAESKey();
let encrypted = await generateAESencryptedMessage(iv, key, plaintext);
let ciphertext = encrypted.ciphertext;
let authenticationTag = encrypted.authenticationTag;
let keydata = await window.crypto.subtle.exportKey('raw', <CryptoKey>key);
return {
keydata: ArrayBufferUtils.concat(keydata, <ArrayBuffer>authenticationTag),
iv,
payload: ciphertext,
};
}
async function generateAESKey(): Promise<CryptoKey | CryptoKeyPair> {
let algo = {
name: ALGO_NAME,
length: AES_KEY_LENGTH,
};
let keyUsage: KeyUsage[] = ['encrypt', 'decrypt'];
let key = await window.crypto.subtle.generateKey(algo, AES_EXTRACTABLE, keyUsage);
return key;
}
async function generateAESencryptedMessage(
iv,
key,
plaintext
): Promise<{ ciphertext: ArrayBuffer; authenticationTag: ArrayBuffer }> {
let encryptOptions = {
name: ALGO_NAME,
iv,
tagLength: AES_TAG_LENGTH,
};
let encodedPlaintext = ArrayBufferUtils.encode(plaintext);
let encrypted = await window.crypto.subtle.encrypt(encryptOptions, key, encodedPlaintext);
let ciphertextLength = encrypted.byteLength - ((128 + 7) >> 3);
let ciphertext = encrypted.slice(0, ciphertextLength);
let authenticationTag = encrypted.slice(ciphertextLength);
return {
ciphertext,
authenticationTag,
};
}