Skip to content

Commit

Permalink
Document crypto module and add ArrayBuffer encoding
Browse files Browse the repository at this point in the history
Signed-off-by: Knut Ahlers <knut@ahlers.me>
  • Loading branch information
Luzifer committed Oct 1, 2023
1 parent a098395 commit 37b84cf
Showing 1 changed file with 59 additions and 4 deletions.
63 changes: 59 additions & 4 deletions src/crypto.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,47 @@
const opensslBanner = new Uint8Array(new TextEncoder('utf8').encode('Salted__'))
const pbkdf2Params = { hash: 'SHA-512', iterations: 300000, name: 'PBKDF2' }

/**
* @param {ArrayBuffer} data Data to encode to base64
* @returns String
*/
function abToB64(data) {
return btoa(String.fromCodePoint(...new Uint8Array(data)))
}

/**
*
* @param {String} encoded Base64 encoded data
* @returns ArrayBuffer
*/
function b64ToAb(encoded) {
const binary = atob(encoded)
return Uint8Array.from(binary, c => c.codePointAt(0)).buffer
}

/**
* @param {String} cipherText Encrypted data in base64 encoded form
* @param {String} passphrase Encryption passphrase used for key-derivation
* @returns String
*/
function dec(cipherText, passphrase) {
decrypt(passphrase, cipherText)
}
/**
*
* @param {String} plainText Data to encrypt
* @param {String} passphrase Encryption passphrase used for key-derivation
* @returns String
*/
function enc(plainText, passphrase) {
encrypt(passphrase, generateSalt(), plainText)
}

/**
* @param {String} passphrase Encryption passphrase used for key-derivation
* @param {String} encData Encrypted data in base64 encoded form
* @returns String
*/
function decrypt(passphrase, encData) {
const data = new Uint8Array(atob(encData).split('')
.map(c => c.charCodeAt(0)))
Expand All @@ -10,26 +51,40 @@ function decrypt(passphrase, encData) {
.then(data => new TextDecoder('utf8').decode(data))
}

/**
*
* @param {String} passphrase
* @param {Uint8Array} salt
* @returns Object
*/
function deriveKey(passphrase, salt) {
return window.crypto.subtle.importKey('raw', new TextEncoder('utf8').encode(passphrase), 'PBKDF2', false, ['deriveBits'])
.then(passwordKey => window.crypto.subtle.deriveBits({ ...pbkdf2Params, salt }, passwordKey, 384))
.then(key => window.crypto.subtle.importKey('raw', key.slice(0, 32), { name: 'AES-CBC' }, false, ['encrypt', 'decrypt'])
.then(aesKey => ({ iv: key.slice(32, 48), key: aesKey })))
}

/**
* @param {String} passphrase Encryption passphrase used for key-derivation
* @param {Uint8Array} salt Cryptographically random salt of 8 byte length
* @param {String} plainData Data to encrypt
* @returns String
*/
function encrypt(passphrase, salt, plainData) {
return deriveKey(passphrase, salt)
.then(({ iv, key }) => window.crypto.subtle.encrypt({ iv, name: 'AES-CBC' }, key, new TextEncoder('utf8').encode(plainData)))
.then(encData => new Uint8Array([...opensslBanner, ...salt, ...new Uint8Array(encData)]))
.then(data => btoa(String.fromCharCode.apply(null, data)))
}

/**
* Generates a cryptographically secure random salt
*
* @returns Uint8Array
*/
function generateSalt() {
const salt = new Uint8Array(8) // Salt MUST consist of 8 byte
return window.crypto.getRandomValues(salt)
}

export default {
dec: (cipherText, passphrase) => decrypt(passphrase, cipherText),
enc: (plainText, passphrase) => encrypt(passphrase, generateSalt(), plainText),
}
export default { abToB64, b64ToAb, dec, enc }

0 comments on commit 37b84cf

Please sign in to comment.