import { Buffer } from 'buffer'
import crypto from 'isomorphic-webcrypto'
/**
* Algorithm parameters
*/
const params = {
A128GCM: { keylen: 16 },
A256GCM: { keylen: 32 },
}
/**
* AES_GCM module. Sign and encrypt data using AES-GCM symetric encryption.
*/
const AES_GCM = {
/**
* Decrypts the cyphertext with the key using the specified algorithm.
*
* Accepted options:
*
* * `aad` - Ephemeral public key
* * `iv` - Agreement PartyUInfo
* * `tag` - Agreement PartyVInfo
*
* @param {String} alg
* @param {Buffer} encrypted
* @param {Key} key
* @param {Object} opts
* @returns {Buffer}
*/
async decrypt(alg, encrypted, key, { aad, iv, tag }) {
assertKey(key, alg)
encrypted = Buffer.concat([
Buffer.from(encrypted),
Buffer.from(tag)
])
const additionalData = Buffer.from(aad ? aad : '')
const eKey = await crypto.subtle.importKey('raw', key.params.k, 'AES-GCM', false, ['decrypt'])
const result = await crypto.subtle.decrypt({ name: 'AES-GCM', iv, additionalData }, eKey, encrypted)
return Buffer.from(result)
},
/**
* Encrypts the message with the key using the specified algorithm. Returns
* an object containing the encrypted cyphertext and any headers to add to
* the Recipient.
*
* Accepted options:
*
* * `aad` - Ephemeral public key
* * `iv` - Agreement PartyUInfo
*
* @param {String} alg
* @param {Buffer|String} msg
* @param {Key} key
* @param {Object} opts
* @returns {Object}
*/
async encrypt(alg, msg, key, opts = {}) {
assertKey(key, alg)
const iv = opts.iv ? opts.iv : crypto.getRandomValues(new Uint8Array(12))
const additionalData = Buffer.from(opts.aad ? opts.aad : '')
const eKey = await crypto.subtle.importKey('raw', key.params.k, 'AES-GCM', false, ['encrypt'])
const encrypted = await crypto.subtle.encrypt({ name: 'AES-GCM', iv, additionalData }, eKey, Buffer.from(msg))
return {
encrypted: Buffer.from(encrypted).slice(0, -16),
iv: Buffer.from(iv),
tag: Buffer.from(encrypted).slice(-16)
}
}
}
/**
* Asserts the key is valid.
*
* @param {Key} key
* @param {String} alg
*/
function assertKey(key, alg) {
const { keylen } = params[alg]
if (key.type !== 'oct' || key.params.k.length !== keylen) {
throw `Invalid key for ${alg} algorithm`
}
}
export default AES_GCM