Created
March 26, 2019 02:10
-
-
Save junderw/1d41158403978ba0363e5868d4f434d9 to your computer and use it in GitHub Desktop.
Using web crypto API for AES-GCM encryption and decryption.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
async function aesEncrypt(data, password, difficulty = 10) { | |
const hashKey = await grindKey(password, difficulty) | |
const iv = await getIv(password, data) | |
const key = await window.crypto.subtle.importKey( | |
'raw', | |
hashKey, { | |
name: 'AES-GCM', | |
}, | |
false, | |
['encrypt'] | |
) | |
const encrypted = await window.crypto.subtle.encrypt({ | |
name: 'AES-GCM', | |
iv, | |
tagLength: 128, | |
}, | |
key, | |
new TextEncoder('utf-8').encode(data) | |
) | |
const result = Array.from(iv).concat(Array.from(new Uint8Array(encrypted))) | |
return base64Encode(new Uint8Array(result)) | |
} | |
async function aesDecrypt(ciphertext, password, difficulty = 10) { | |
const ciphertextBuffer = Array.from(base64Decode(ciphertext)) | |
const hashKey = await grindKey(password, difficulty) | |
const key = await window.crypto.subtle.importKey( | |
'raw', | |
hashKey, { | |
name: 'AES-GCM', | |
}, | |
false, | |
['decrypt'] | |
) | |
const decrypted = await window.crypto.subtle.decrypt({ | |
name: 'AES-GCM', | |
iv: new Uint8Array(ciphertextBuffer.slice(0, 12)), | |
tagLength: 128, | |
}, | |
key, | |
new Uint8Array(ciphertextBuffer.slice(12)) | |
) | |
return new TextDecoder('utf-8').decode(new Uint8Array(decrypted)) | |
} | |
function base64Encode(u8) { | |
return btoa(String.fromCharCode.apply(null, u8)) | |
} | |
function base64Decode(str) { | |
return new Uint8Array(atob(str).split('').map(c => c.charCodeAt(0))) | |
} | |
function grindKey(password, difficulty) { | |
return pbkdf2(password, password + password, Math.pow(2, difficulty), 32, 'SHA-256') | |
} | |
function getIv(password, data) { | |
const randomData = base64Encode(window.crypto.getRandomValues(new Uint8Array(12))) | |
return pbkdf2(password + randomData, data + (new Date().getTime().toString()), 1, 12, 'SHA-256') | |
} | |
async function pbkdf2(message, salt, iterations, keyLen, algorithm) { | |
const msgBuffer = new TextEncoder('utf-8').encode(message) | |
const msgUint8Array = new Uint8Array(msgBuffer) | |
const saltBuffer = new TextEncoder('utf-8').encode(salt) | |
const saltUint8Array = new Uint8Array(saltBuffer) | |
const key = await crypto.subtle.importKey('raw', msgUint8Array, { | |
name: 'PBKDF2' | |
}, false, ['deriveBits']) | |
const buffer = await crypto.subtle.deriveBits({ | |
name: 'PBKDF2', | |
salt: saltUint8Array, | |
iterations: iterations, | |
hash: algorithm | |
}, key, keyLen * 8) | |
return new Uint8Array(buffer) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment