Skip to content

Commit

Permalink
update encrypt/decrypt methods to use web crypto api
Browse files Browse the repository at this point in the history
  • Loading branch information
tmountjr committed Apr 22, 2024
1 parent 33b907d commit de3461c
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 44 deletions.
79 changes: 47 additions & 32 deletions ECToken.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ const crypto = require('crypto')
const base64url = require('base64url')

const iv_size_bytes = 12
const aes_gcm_tag_size_bytes = 16

/**
* An EdgeCast Token
Expand Down Expand Up @@ -71,29 +70,40 @@ class ECToken {
* @param {String} key The secret key to encode the token.
* @param {ECToken} token The token to encrypt.
* @param {Boolean} verbose Whether to print verbose output.
* @returns {String} The encrypted token.
* @returns {Promise<String>} The encrypted token.
*/
function encrypt(key, token, verbose = false) {
const token_str = token.serialize().toString('utf-8')
const key_encoded = key.toString('utf-8')
async function encrypt(key, token, verbose = false) {
const token_str = new TextEncoder().encode(token.serialize())
const key_encoded = new TextEncoder().encode(key)

const key_digest = crypto.createHash('sha256').update(key_encoded).digest()
const key_digest = await crypto.subtle.digest('SHA-256', key_encoded)

const iv = crypto.randomBytes(iv_size_bytes)
const iv = crypto.getRandomValues(new Uint8Array(iv_size_bytes))

const encryptor = crypto.createCipheriv('aes-256-gcm', key_digest, iv).setAutoPadding(false)
const ciphertext = encryptor.update(token_str)
encryptor.final('utf-8')
const tag = encryptor.getAuthTag()
const iv_ciphertext = Buffer.concat([iv, ciphertext, tag])
const cryptoKey = await crypto.subtle.importKey(
'raw',
key_digest,
{ name: 'AES-GCM', length: 256 },
false,
['encrypt']
)

const ciphertext = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
cryptoKey,
token_str
)

const iv_ciphertext = new Uint8Array(iv.byteLength + ciphertext.byteLength)
iv_ciphertext.set(new Uint8Array(iv), 0)
iv_ciphertext.set(new Uint8Array(ciphertext), iv.byteLength)

if (verbose) {
console.log('+---------------------------------------------------------------------------------------------------')
console.log('| iv:', iv.toString('hex'))
console.log('| ciphertext:', ciphertext.toString('hex'))
console.log('| tag:', tag.toString('hex'))
console.log('| iv:', Buffer.from(iv).toString('hex'))
console.log('| ciphertext:', Buffer.from(ciphertext).toString('hex'))
console.log('+---------------------------------------------------------------------------------------------------')
console.log('| encoded_token:', iv_ciphertext.toString('hex'))
console.log('| encoded_token:', Buffer.from(iv_ciphertext).toString('hex'))
console.log('+---------------------------------------------------------------------------------------------------')
}

Expand All @@ -105,44 +115,49 @@ function encrypt(key, token, verbose = false) {
* @param {String} key The secret to decode the token.
* @param {String} token The token to decrypt.
* @param {Boolean} verbose Whether to print verbose output.
* @returns {String} The decrypted token.
* @returns {Promise<String>} The decrypted token.
*/
function decrypt(key, token, verbose = false) {
const key_digest = crypto.createHash('sha256').update(key).digest()
async function decrypt(key, token, verbose = false) {
const key_encoded = new TextEncoder().encode(key)
const key_digest = await crypto.subtle.digest('SHA-256', key_encoded)

const decoded_token = base64url.toBuffer(token)

// First n bytes (iv_size_bytes) is the iv.
const iv = decoded_token.subarray(0, iv_size_bytes)

// Last n bytes (aes_gcm_tag_size_bytes) is the tag.
const tag = decoded_token.subarray(-aes_gcm_tag_size_bytes)

// Middle bit is the ciphertext.
const ciphertext = decoded_token.subarray(iv_size_bytes, -aes_gcm_tag_size_bytes)
// last bit is the ciphertext.
const ciphertext = decoded_token.subarray(iv_size_bytes)

if (verbose) {
console.log('+---------------------------------------------------------------------------------------------------')
console.log('| decoded_token:', decoded_token.toString('hex'))
console.log('+---------------------------------------------------------------------------------------------------')
console.log('| iv:', iv.toString('hex'))
console.log('| ciphertext:', ciphertext.toString('hex'))
console.log('| tag:', tag.toString('hex'))
console.log('+---------------------------------------------------------------------------------------------------')
}

const decipher = crypto.createDecipheriv('aes-256-gcm', key_digest, iv).setAutoPadding(false)
decipher.setAuthTag(tag)
const decrypted_buffer = decipher.update(ciphertext)
decipher.final('utf8')
const decrypted_str = decrypted_buffer.toString('utf-8')
const cryptoKey = await crypto.subtle.importKey(
'raw',
key_digest,
{ name: 'AES-GCM', length: 256 },
false,
['decrypt']
)

const decrypted_str = await crypto.subtle.decrypt(
{ name: 'AES-GCM', iv },
cryptoKey,
ciphertext
)

if (verbose) {
console.log('| decrypted_str:', decrypted_str)
console.log('| decrypted_str:', new TextDecoder().decode(decrypted_str))
console.log('+---------------------------------------------------------------------------------------------------')
}

return decrypted_str
return new TextDecoder().decode(decrypted_str)
}

module.exports = {
Expand Down
30 changes: 18 additions & 12 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
const { ECToken, encrypt, decrypt } = require('./ECToken.js')
const { ECToken, encrypt, decrypt } = require('./ECToken.js');

// Create the token.
const ec_token = new ECToken()
ec_token.addValue('ec_country_allow', 'US')
ec_token.addValue('ec_country_allow', 'CA')
ec_token.addValue('ec_expire', (Date.now() / 1000) + (60 * 60 * 24))
(async () => {
// Create the token.
const ec_token = new ECToken()
ec_token.addValue('ec_country_allow', 'US')
ec_token.addValue('ec_country_allow', 'CA')
ec_token.addValue('ec_expire', (Date.now() / 1000) + (60 * 60 * 24))

// Encrypt and encode it.
const ec_token_str = await encrypt('my-secret-key', ec_token)
console.log(`Encoded: ${ec_token_str}`)

// Encrypt and encode it.
const ec_token_str = encrypt('my-secret-key', ec_token)
console.log(`Encoded: ${ec_token_str}`)
console.log(' ')

// Now decrypt it back to plaintext.
const plaintext = await decrypt('my-secret-key', ec_token_str)
console.log(`Plaintext: ${plaintext}`)

// Now decrypt it back to plaintext.
const plaintext = decrypt('my-secret-key', ec_token_str)
console.log(`Plaintext: ${plaintext}`)
process.exit()
})()

0 comments on commit de3461c

Please sign in to comment.