Last active
January 3, 2020 00:12
-
-
Save rafaelsq/5af573af7e2d763869e2f4cce0a8357a to your computer and use it in GitHub Desktop.
WebCrypto - PBKDF2-HMAC-256 + encode AES-CBC/AES-GCM
This file contains 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
// PBKDF2 HMAC-256 | |
const pbkdf2 = (password, salt, iterations, hash, mode) => | |
crypto.subtle.importKey("raw", password, {name: "PBKDF2"}, false, ["deriveKey"]) | |
.then(baseKey => crypto.subtle.deriveKey({name: "PBKDF2", salt, iterations, hash}, baseKey, {"name": mode, "length": 256}, true, ["encrypt", "decrypt"])) | |
.then(key => crypto.subtle.exportKey("raw", key)) | |
let password = strToBuf("password"), | |
salt = strToBuf("salt"), | |
iterations = 1000, | |
hash = "SHA-256", | |
mode = "AES-GCM" | |
pbkdf2(password, salt, iterations, hash, mode) | |
.then(keyBytes => console.log("hexKey", bufToHex(keyBytes))) |
This file contains 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
// param mode: string; "AES-GCM" | "AES-CBC" | |
const encrypt = (data, key, iv, mode) => | |
crypto.subtle.importKey("raw", key, {name: mode}, true, ["encrypt", "decrypt"]) | |
.then(bufKey => crypto.subtle.encrypt({name: mode, iv}, bufKey, data)) | |
//.then(buf => new Uint8Array(buf)) | |
const decrypt = (data, key, iv, mode) => | |
crypto.subtle.importKey("raw", key, {name: mode}, true, ["encrypt", "decrypt"]) | |
.then(bufKey => crypto.subtle.decrypt({name: mode, iv}, bufKey, data)) | |
//.then(buf => new Uint8Array(buf)) | |
let data = strToBuf("á ✓ ᕦ(ò_óˇ)ᕤ ♥ à"), | |
key = hexToBuf("0CD1D07EB67E19EF56EA0F3A9A8F8A7C957A2CB208327E0E536608FF83256C96"), | |
iv = window.crypto.getRandomValues(new Uint8Array(16)) | |
encrypt(data, key, iv, "AES-GCM") | |
.then(buf => decrypt(buf, key, iv, "AES-GCM")) | |
.then(buf => console.log("data:", bufToStr(buf))) |
This file contains 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
export const EncodeGCM = (pass, salt, iv, iterations, data) => | |
new window.Promise((ok, fail) => { | |
pbkdf2(strToBuf(pass), salt, iterations, 'SHA-256', 'AES-GCM') | |
.then(key => { | |
const chunkSize = 2 << 10 | |
let len = data.byteLength / chunkSize + (data.byteLength % chunkSize ? 1 : 0) | 0 | |
let out = new window.Uint8Array([...salt, ...iv]) | |
for (let i = 0; i < len; i++) { | |
encrypt(data.slice(i * chunkSize, i * chunkSize + chunkSize), key, iv, 'AES-GCM') | |
.then(buf => { | |
out = new window.Uint8Array([...out, ...new window.Uint8Array(buf)]) | |
if (i == len - 1) { | |
ok(out) | |
} | |
}) | |
.catch(e => { | |
throw Error(e) | |
}) | |
} | |
}) | |
.catch(fail) | |
}) | |
export const DecodeGCM = (pass, iterations, rawBuf) => | |
new window.Promise((ok, fail) => { | |
const salt = rawBuf.slice(0, 8) | |
const iv = rawBuf.slice(8, 20) | |
const buffer = rawBuf.slice(20) | |
pbkdf2(strToBuf(pass), salt, iterations, 'SHA-256', 'AES-GCM') | |
.then(key => { | |
const chunkSize = 2 << 10 | |
let len = buffer.byteLength / (chunkSize + 16) + (buffer.byteLength % (chunkSize + 16) ? 1 : 0) | 0 | |
let out = new window.Uint8Array(len * chunkSize) | |
for ( | |
let i = 0, s = 0, e = chunkSize + 16; | |
i < len; | |
i++, s = i * (chunkSize + 16), e = s + chunkSize + 16 | |
) { | |
if (e > buffer.byteLength) e = buffer.byteLength | |
decrypt(buffer.slice(s, e), key, iv, 'AES-GCM') | |
.then(buf => { | |
out.set(new window.Uint8Array(buf), i * chunkSize) | |
if (i == len - 1) { | |
ok(out.slice(0, i * chunkSize + buf.byteLength)) | |
} | |
}) | |
.catch(e => { | |
throw Error(e) | |
}) | |
} | |
}) | |
.catch(fail) | |
}) | |
// ex | |
EncodeGCM("pass", salt, iv, iterations, strToBuf("content")) | |
.then(buf => { | |
console.log("out", bufToHex(buf)) | |
}) | |
.catch(err => { | |
console.error(err) | |
}) | |
DecodeGCM("pass", iterations, hexToBuf(state.enc)) | |
.then(buf => { | |
console.log("out", bufToStr(buf)) | |
}) | |
.catch(err => { | |
console.error(err) | |
}) |
This file contains 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
const hexToBuf = hex => { | |
for (var bytes = [], c = 0; c < hex.length; c += 2) | |
bytes.push(parseInt(hex.substr(c, 2), 16)); | |
return new Uint8Array(bytes); | |
}; | |
const bufToHex = buf => { | |
var byteArray = new Uint8Array(buf); | |
var hexString = ""; | |
var nextHexByte; | |
for (var i=0; i<byteArray.byteLength; i++) { | |
nextHexByte = byteArray[i].toString(16); | |
if (nextHexByte.length < 2) { | |
nextHexByte = "0" + nextHexByte; | |
} | |
hexString += nextHexByte; | |
} | |
return hexString; | |
}; | |
const strToBuf = str => (new TextEncoder().encode(str)); | |
const bufToStr = str => (new TextDecoder().decode(str)); | |
const strToB64 = str => btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (match, p1) => String.fromCharCode('0x' + p1))); | |
const b64ToStr = str => decodeURIComponent(Array.prototype.map.call(atob(str), c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)).join('')); | |
// const bufToB64 = buf => btoa(Array.prototype.map.call(buf, ch => String.fromCharCode(ch)).join('')); | |
const bufToB64 = buf => btoa(String.fromCharCode(...new Uint8Array(buf))) | |
const b64ToBuf = b64 => { | |
const binstr = atob(b64), | |
buf = new Uint8Array(binstr.length); | |
Array.prototype.forEach.call(binstr, (ch, i) => { | |
buf[i] = ch.charCodeAt(0); | |
}); | |
return buf; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://github.com/diafygi/webcrypto-examples
https://jsfiddle.net/dnqr4qcs/
In action:
https://jsfiddle.net/tue5L8n8/
https://jsfiddle.net/z2rgocjv/ encStr/decStr