Skip to content

Instantly share code, notes, and snippets.

@bryanjhv
Created January 18, 2024 21:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bryanjhv/910e7189ebe0d068224ac5a892d1751f to your computer and use it in GitHub Desktop.
Save bryanjhv/910e7189ebe0d068224ac5a892d1751f to your computer and use it in GitHub Desktop.
Laravel Encryption Crypt WebCrypto implementation
function s2b(data: string) {
return Uint8Array.from(data, c => c.charCodeAt(0)).buffer
}
function b2s(data: ArrayBufferLike) {
return String.fromCharCode(...new Uint8Array(data))
}
function hex(data: ArrayBufferLike) {
return [...new Uint8Array(data)].map(x => x.toString(16).padStart(2, '0')).join('')
}
let keyData: ArrayBuffer
interface Payload {
iv: string
value: string
mac: string
}
function setKey(data: string) {
keyData = s2b(atob(data.slice(7)))
}
async function subtle(usage: 'encrypt' | 'decrypt' | 'sign', data: string, iv: string) {
const algorithm = !usage.includes('crypt')
? [{ name: 'HMAC', hash: 'SHA-256' }, 'HMAC']
: ['AES-CBC', { name: 'AES-CBC', iv: s2b(atob(iv)) }]
const key = await crypto.subtle.importKey('raw', keyData, algorithm[0]!, true, [usage])
return await crypto.subtle[usage](algorithm[1]!, key, s2b(data))
}
async function decryptString(data: string) {
const payload = JSON.parse(atob(data)) as Payload
const decrypted = await subtle('decrypt', atob(payload.value), payload.iv)
// TODO: validate HMAC
return b2s(decrypted)
}
async function encryptString(data: string) {
const tmp = new Uint8Array(16)
crypto.getRandomValues(tmp)
const iv = btoa(b2s(tmp.buffer))
const value = btoa(b2s(await subtle('encrypt', data, iv)))
const mac = hex(await subtle('sign', iv + value, ''))
return btoa(JSON.stringify({ iv, value, mac } as Payload))
}
export { setKey, decryptString, encryptString }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment