Created
April 17, 2020 11:00
-
-
Save zillwc/187834a5f830a5948c19fe564728e1ac to your computer and use it in GitHub Desktop.
Testing webcrypto functionality: encrypt, decrypt, wrapKey, unwrapKey, importKey, generateKey, and deriveKey
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
const KEY_OP_ALGO = 'AES-GCM'; | |
const KEY_WRAPPING_ALGO = 'AES-KW'; | |
const KEY_DERIVATION_ALGO = 'PBKDF2'; | |
const encode = (t) => (new TextEncoder()).encode(t); | |
const decode = (t) => (new TextDecoder()).decode(t); | |
const bytesToArrayBuffer = (b) => new Uint8Array(b); | |
const areBuffersEqual = (buf1, buf2) => { | |
const a1 = bytesToArrayBuffer(buf1); | |
const a2 = bytesToArrayBuffer(buf2); | |
return !(a1.some((elem, idx) => elem !== a2[idx])); | |
}; | |
function generateNewKey() { | |
const algorithm = { name: KEY_OP_ALGO, length: 256 }; | |
const extractable = true; | |
const keyUsages = [ 'encrypt', 'decrypt' ]; | |
return crypto.subtle.generateKey(algorithm, extractable, keyUsages); | |
} | |
async function encrypt(algorithm, key, plaintextSecret) { | |
const plaintext = encode(plaintextSecret); | |
return window.crypto.subtle.encrypt(algorithm, key, plaintext); | |
} | |
async function decrypt(algorithm, key, cipher) { | |
const decrypted = await window.crypto.subtle.decrypt(algorithm, key, cipher); | |
return decode(decrypted); | |
} | |
function getWrappingKey(salt, keyMaterial) { | |
const algorithm = { name: KEY_DERIVATION_ALGO, salt, iterations: 100000, hash: 'SHA-256' }; | |
const derivedKeyAlgorithm = { name: KEY_WRAPPING_ALGO, length: 256 }; | |
const extractable = true; | |
const keyUsages = [ 'wrapKey', 'unwrapKey' ]; | |
return window.crypto.subtle.deriveKey( | |
algorithm, keyMaterial, derivedKeyAlgorithm, extractable, keyUsages | |
); | |
} | |
function getKeyMaterial(plaintextSecret) { | |
const keyData = encode(plaintextSecret); | |
const format = 'raw'; | |
const algorithm = { name: KEY_DERIVATION_ALGO }; | |
const extractable = false; | |
const keyUsages = [ 'deriveBits', 'deriveKey' ]; | |
return window.crypto.subtle.importKey(format, keyData, algorithm, extractable, keyUsages); | |
} | |
async function wrapKey(wrappingKeySecret, keyToWrap) { | |
const keyMaterial = await getKeyMaterial(wrappingKeySecret); | |
const salt = window.crypto.getRandomValues(new Uint8Array(16)); | |
const wrappingKey = await getWrappingKey(salt, keyMaterial); | |
const format = 'raw'; | |
const algorithm = KEY_WRAPPING_ALGO; | |
const wrappedKey = await window.crypto.subtle.wrapKey( | |
format, keyToWrap, wrappingKey, algorithm | |
); | |
return { wrappedKeySalt: salt, wrappedKey }; | |
} | |
async function unwrapKey(wrappedKeySalt, wrappedKey, wrappingKeySecret) { | |
const keyMaterial = await getKeyMaterial(wrappingKeySecret); | |
const unwrappingKey = await getWrappingKey(wrappedKeySalt, keyMaterial); | |
const wrappedKeyBuffer = bytesToArrayBuffer(wrappedKey); | |
const format = 'raw'; | |
const extractable = true; | |
const keyUsages = [ 'encrypt', 'decrypt' ]; | |
return window.crypto.subtle.unwrapKey( | |
format, wrappedKeyBuffer, unwrappingKey, KEY_WRAPPING_ALGO, KEY_OP_ALGO, extractable, keyUsages | |
); | |
} | |
async function testEncryptionDecryption() { | |
const plaintext = "s3cret"; | |
const masterKey = await generateNewKey(); | |
const iv = window.crypto.getRandomValues(new Uint8Array(12)); | |
const algorithm = { name: KEY_OP_ALGO, iv }; | |
const cipher = await encrypt(algorithm, masterKey, plaintext); | |
const decrypted = await decrypt(algorithm, masterKey, cipher); | |
const wasSuccessful = decrypted === plaintext; | |
console.assert(wasSuccessful, 'ENCRYPT/DECRYPT FAILED'); | |
} | |
async function testKeyWrapUnwrap() { | |
const plaintext = "s3cret"; | |
const masterKey = await generateNewKey(); | |
const wrappingKeySecret = 'wr4pM3'; | |
const { wrappedKeySalt, wrappedKey } = await wrapKey(wrappingKeySecret, masterKey); | |
const unwrappedMasterKey = await unwrapKey(wrappedKeySalt, wrappedKey, wrappingKeySecret); | |
const iv = window.crypto.getRandomValues(new Uint8Array(12)); | |
const algorithm = { name: KEY_OP_ALGO, iv }; | |
const encrypted = await encrypt(algorithm, masterKey, plaintext); | |
const reencrypted = await encrypt(algorithm, unwrappedMasterKey, plaintext); | |
const wasSuccessful = areBuffersEqual(encrypted, reencrypted); | |
console.assert(wasSuccessful, 'KEY WRAP/UNWRAP FAILED'); | |
} | |
async function test() { | |
await testEncryptionDecryption(); | |
await testKeyWrapUnwrap(); | |
} | |
test(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment