UNPKG

@digital-blueprint/greenlight-app

Version:

[GitHub Repository](https://github.com/digital-blueprint/greenlight-app) | [npmjs package](https://www.npmjs.com/package/@digital-blueprint/greenlight-app) | [Unpkg CDN](https://unpkg.com/browse/@digital-blueprint/greenlight-app/) | [Greenlight Bundle](ht

135 lines (116 loc) 3.61 kB
import {CompactEncrypt, importJWK, base64url} from 'jose'; /** * @param {string} passphraseKey * @param {ArrayBuffer } [salt] * @returns {Array} */ export async function generateKey(passphraseKey, salt) { if (salt === undefined) { salt = window.crypto.getRandomValues(new Uint8Array(24)); } let enc = new TextEncoder(); let privateKey = await window.crypto.subtle.importKey( 'raw', enc.encode(passphraseKey), 'PBKDF2', false, ['deriveBits', 'deriveKey'] ); let key = await window.crypto.subtle.deriveKey( { name: 'PBKDF2', salt: salt, iterations: 100000, hash: 'SHA-256', }, privateKey, {name: 'AES-GCM', length: 256}, true, ['encrypt', 'decrypt'] ); let binary = ''; for (let i = 0; i < salt.byteLength; i++) { binary += String.fromCharCode(salt[i]); } let saltBase64 = window.btoa(binary); return [key, saltBase64]; } /** * @param {CryptoKey} key * @param {string} plaintext * @returns {Array} */ export async function encrypt(key, plaintext) { let enc = new TextEncoder(); let iv = window.crypto.getRandomValues(new Uint8Array(24)); let cipher = await window.crypto.subtle.encrypt( { name: 'AES-GCM', iv: iv, }, key, enc.encode(plaintext) ); let binary = ''; let bytes = new Uint8Array(cipher); for (let i = 0; i < bytes.byteLength; i++) { binary += String.fromCharCode(bytes[i]); } let cipherBase64 = window.btoa(binary); binary = ''; for (let i = 0; i < iv.byteLength; i++) { binary += String.fromCharCode(iv[i]); } let ivBase64 = window.btoa(binary); return [cipherBase64, ivBase64]; } /** * @param {string} ciphertext * @param {CryptoKey} key * @param {ArrayBuffer} iv * @returns {string} */ export async function decrypt(ciphertext, key, iv) { let binary_string = window.atob(ciphertext); let bytes = new Uint8Array(binary_string.length); for (let i = 0; i < binary_string.length; i++) { bytes[i] = binary_string.charCodeAt(i); } let cipherArrayBuffer = bytes.buffer; let dec = new TextDecoder('utf-8'); let plaintext = await window.crypto.subtle.decrypt( { name: 'AES-GCM', iv: iv, }, key, cipherArrayBuffer ); return dec.decode(plaintext); } /** * This "encrypts" the additional information string using the current oauth2 * token, using A256GCM and PBES2-HS256+A128KW. * * Since we can't do any server side validation the user needs to confirm in the * UI that he/she won't abuse the system. * * By using the token we make replaying an older requests harder and by using * JOSE which needs crypto APIs, abusing the system can't reasonably be done by * accident but only deliberately. * * This doesn't make things more secure, it just makes the intent of the user * more clear in case the API isn't used through our UI flow. * * @param {string} token * @param {string} additionalInformation * @returns {string} */ export async function encodeAdditionalInformation(token, additionalInformation) { const encoder = new TextEncoder(); const key = await importJWK({kty: 'oct', k: base64url.encode(token)}, 'PBES2-HS256+A128KW'); const jwe = await new CompactEncrypt(encoder.encode(additionalInformation)) .setProtectedHeader({alg: 'PBES2-HS256+A128KW', enc: 'A256GCM'}) .encrypt(key); return jwe; }