Created
August 24, 2023 10:31
-
-
Save Mix-Liten/0ea954b3cea402ce5a4c883973b20229 to your computer and use it in GitHub Desktop.
Symmetric Encryption in Web environment
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class SymmetricEncryptor { | |
#key | |
/** | |
* Create a new SymmetricEncryptor instance. | |
* @param {string} key - The base64-encoded encryption key. | |
*/ | |
constructor(key) { | |
/** | |
* The encryption key. | |
* @type {Uint8Array} | |
* @private | |
*/ | |
this.#key = new Uint8Array( | |
atob(key) | |
.split('') | |
.map(char => char.charCodeAt(0)) | |
) | |
} | |
/** | |
* Encrypts the given plaintext using AES-GCM. | |
* @param {string} plaintext - The plaintext message to encrypt. | |
* @returns {Promise<{ciphertext: string, iv: string}>} A Promise that resolves to an object containing the ciphertext and initialization vector (IV). | |
*/ | |
async encrypt(plaintext) { | |
const iv = crypto.getRandomValues(new Uint8Array(12)) | |
const encodedPlaintext = new TextEncoder().encode(plaintext) | |
const secretKey = await crypto.subtle.importKey( | |
'raw', | |
this.#key, | |
{ | |
name: 'AES-GCM', | |
length: 256, | |
}, | |
true, | |
['encrypt', 'decrypt'] | |
) | |
const ciphertext = await crypto.subtle.encrypt( | |
{ | |
name: 'AES-GCM', | |
iv, | |
}, | |
secretKey, | |
encodedPlaintext | |
) | |
return { | |
ciphertext: btoa(String.fromCharCode(...new Uint8Array(ciphertext))), | |
iv: btoa(String.fromCharCode(...iv)), | |
} | |
} | |
/** | |
* Decrypts the given ciphertext using AES-GCM. | |
* @param {string} ciphertext - The ciphertext to decrypt. | |
* @param {string} iv - The initialization vector (IV) used during encryption. | |
* @returns {Promise<string>} A Promise that resolves to the decrypted plaintext message. | |
*/ | |
async decrypt(ciphertext, iv) { | |
const secretKey = await crypto.subtle.importKey( | |
'raw', | |
this.#key, | |
{ | |
name: 'AES-GCM', | |
length: 256, | |
}, | |
true, | |
['encrypt', 'decrypt'] | |
) | |
const cleartext = await crypto.subtle.decrypt( | |
{ | |
name: 'AES-GCM', | |
iv: new Uint8Array( | |
atob(iv) | |
.split('') | |
.map(char => char.charCodeAt(0)) | |
), | |
}, | |
secretKey, | |
new Uint8Array( | |
atob(ciphertext) | |
.split('') | |
.map(char => char.charCodeAt(0)) | |
) | |
) | |
return new TextDecoder().decode(cleartext) | |
} | |
} | |
/** | |
* Generates a random AES-GCM encryption key and returns it as a base64-encoded string. | |
* @returns {Promise<string>} A Promise that resolves to the generated base64-encoded encryption key. | |
*/ | |
async function generateKey() { | |
/** | |
* Generate a random AES-GCM key pair. | |
* @type {CryptoKeyPair} | |
*/ | |
const key = await crypto.subtle.generateKey( | |
{ | |
name: 'AES-GCM', | |
length: 256, | |
}, | |
true, | |
['encrypt', 'decrypt'] | |
) | |
const keyBuffer = await crypto.subtle.exportKey('raw', key) | |
const keyArray = new Uint8Array(keyBuffer) | |
const base64Key = btoa(String.fromCharCode(...keyArray)) | |
return base64Key | |
} | |
// Example usage | |
const plaintext = '----- Secret Data -----' | |
const key = await generateKey() | |
const symmetricEncryptor = new SymmetricEncryptor(key) | |
console.log('Init sample:', { plaintext, key }) | |
const { ciphertext, iv } = await symmetricEncryptor.encrypt(plaintext) | |
console.log('Encrypted return:', { ciphertext, iv }) | |
const decryptedPlaintext = await symmetricEncryptor.decrypt(ciphertext, iv) | |
console.log('Decrypted Plaintext:', decryptedPlaintext) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment