Last active
April 11, 2019 17:26
-
-
Save geekman/f9735602f744ebe5fa812f8ba17518c4 to your computer and use it in GitHub Desktop.
Web Crypto using HKDF
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
<html> | |
<head> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
</head> | |
<body> | |
<style> | |
* { font-family: sans-serif; font-size: 16px; } | |
input { font-family: monospace; } | |
input[readonly] { border: 1px solid #eee; } | |
div.row { padding: .8em 0; } | |
input[type=radio] { | |
opacity: 0; | |
position: absolute; | |
} | |
input[type=radio] + label { | |
border: 1px solid #ccc; | |
padding: .3em 1em; | |
} | |
input[type=radio]:checked + label { | |
background: #ddd; | |
} | |
input[type=radio]:focus + label { | |
outline: rgba(77, 97, 171, 0.5) auto 3px; | |
} | |
</style> | |
<script> | |
function mkurlsafe(b64) { | |
return b64 | |
.replace(/\//g, '_') | |
.replace(/\+/g, '-') | |
.replace(/=*$/, ''); | |
} | |
function urlsafe2arr(b64) { | |
b64 = b64 | |
.replace(/_/g, '/') | |
.replace(/-/g, '+'); | |
b64 += '==='.slice((b64.length + 3) % 4); | |
var b = atob(b64) | |
.split('') | |
.map(s => s.charCodeAt(0)); | |
return new Uint8Array(b); | |
} | |
function arr2b64(arr) { return btoa(String.fromCharCode.apply(null, new Uint8Array(arr))) } | |
function arr2hex(arr) { return [].map.call(new Uint8Array(arr), n => ('0' + n.toString(16)).slice(-2) ).join('') } | |
function txt(str) { return new TextEncoder().encode(str); } | |
var crypto = window.crypto; | |
function randB64(len) { | |
var buf = new Uint8Array(len); | |
crypto.getRandomValues(buf); | |
return mkurlsafe(arr2b64(buf)); | |
} | |
function importKeyB64(b64) { | |
return crypto.subtle.importKey('raw', urlsafe2arr(b64), 'HKDF', false, ['deriveKey']) | |
} | |
function makeEncKey(key, info) { | |
return crypto.subtle.deriveKey( | |
{name: 'HKDF', salt: new Uint8Array(), info: txt(info), hash: 'SHA-256'}, | |
key, | |
{name: 'AES-GCM', length: 128}, | |
true, | |
['encrypt', 'decrypt']); | |
} | |
function makeSignKey(key, info) { | |
return crypto.subtle.deriveKey( | |
{name: 'HKDF', salt: new Uint8Array(), info: txt(info), hash: 'SHA-256'}, | |
key, | |
{name: 'HMAC', hash: 'SHA-256'}, | |
true, | |
['sign']); | |
} | |
async function showKeyValue(elem, key) { | |
var e = document.getElementById(elem); | |
var keyBytes = await crypto.subtle.exportKey('raw', key); | |
e.dataset.hex_value = arr2hex(keyBytes); | |
e.dataset.b64_value = arr2b64(keyBytes); | |
} | |
function refreshKeyValues() { | |
document.querySelector('input[name=display_format]:checked').onchange(); | |
} | |
async function generateSubkeys(k) { | |
const masterKey = await importKeyB64(k); | |
const encKey = await makeEncKey(masterKey, 'encryption'); | |
const authKey = await makeSignKey(masterKey, 'authentication'); | |
await showKeyValue('enckey', encKey); | |
await showKeyValue('authkey', authKey); | |
refreshKeyValues(); | |
} | |
function randomKey(elem) { | |
var e = document.getElementById(elem); | |
e.value = randB64(16); | |
e.onchange(); | |
} | |
function changeDisplayFormat(e) { | |
var dispType = e.value; | |
var l = document.querySelectorAll('input[id$=key]'); | |
for (var i = 0; i < l.length; i++) { | |
var e = l[i]; | |
var v = e.dataset[dispType + '_value']; | |
if (v) e.value = v; | |
} | |
} | |
</script> | |
<div class="row"> | |
<label for="masterkey">Master key:</label> | |
<input type="text" id="masterkey" size=25 onchange="generateSubkeys(this.value)"> | |
<button id="generate" onclick="randomKey('masterkey')">Generate</button> | |
</div> | |
<div class="row"> | |
Display format: | |
<input id="show_hex" type="radio" name="display_format" value="hex" onchange="changeDisplayFormat(this);" checked> | |
<label for="show_hex">Hex</label> | |
<input id="show_b64" type="radio" name="display_format" value="b64" onchange="changeDisplayFormat(this);"> | |
<label for="show_b64">Base64</label> | |
</div> | |
<div class="row"> | |
<label for="enckey">Encryption key:</label> | |
<input type="text" id="enckey" size=40 readonly> | |
</div> | |
<div class="row"> | |
<label for="authkey">Auth key:</label> | |
<input type="text" id="authkey" size=40 readonly> | |
</div> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment