initial commit
This commit is contained in:
15
sspoc/static/index.html
Normal file
15
sspoc/static/index.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<script src="/js/sspoc.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<form id="login-form">
|
||||
<label for="username">Username</label>
|
||||
<input id="username" type="text">
|
||||
<label for="password">Password</label>
|
||||
<input id="password" type="password">
|
||||
<button id="login-button" type="button">Login</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
109
sspoc/static/js/sspoc.js
Normal file
109
sspoc/static/js/sspoc.js
Normal file
@@ -0,0 +1,109 @@
|
||||
function integerToBytes(integer, numBytes) {
|
||||
const bytesArray = new Array(numBytes).fill(0);
|
||||
for (let i = 0; i < numBytes; i++) {
|
||||
bytesArray[numBytes - i - 1] = integer & 0xff;
|
||||
integer >>= 8;
|
||||
}
|
||||
return Uint8Array.from(bytesArray);
|
||||
}
|
||||
|
||||
function concatenateUInt8Arrays(array1, array2) {
|
||||
const newArray = new Uint8Array(array1.length + array2.length);
|
||||
newArray.set(array1);
|
||||
newArray.set(array2, array1.length);
|
||||
return newArray;
|
||||
}
|
||||
|
||||
/*
|
||||
Convert an ArrayBuffer into a string
|
||||
from https://developer.chrome.com/blog/how-to-convert-arraybuffer-to-and-from-string/
|
||||
*/
|
||||
function ab2str(buf) {
|
||||
return String.fromCharCode.apply(null, new Uint8Array(buf));
|
||||
}
|
||||
|
||||
/*
|
||||
Export the given key and write it into the "exported-key" space.
|
||||
*/
|
||||
async function exportCryptoKeyPem(key) {
|
||||
const exported = await window.crypto.subtle.exportKey("spki", key);
|
||||
const exportedAsString = ab2str(exported);
|
||||
const exportedAsBase64 = btoa(exportedAsString);
|
||||
return exportedAsBase64;
|
||||
}
|
||||
|
||||
async function exportCryptoKey(key) {
|
||||
const exported = await window.crypto.subtle.exportKey("jwk", key);
|
||||
return JSON.stringify(exported, null, " ");
|
||||
}
|
||||
|
||||
async function createKeyPair() {
|
||||
const crypto = window.crypto.subtle
|
||||
const keyPair = await window.crypto.subtle.generateKey(
|
||||
{
|
||||
name: "RSA-OAEP",
|
||||
modulusLength: 2048,
|
||||
publicExponent: new Uint8Array([4, 0, 1]),
|
||||
hash: "SHA-256",
|
||||
},
|
||||
true,
|
||||
["encrypt", "decrypt"]
|
||||
);
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
let keyPair = createKeyPair();
|
||||
let loginForm = document.getElementById('login-form');
|
||||
let loginButton = document.getElementById('login-button');
|
||||
let nonce = null
|
||||
loginButton.addEventListener('click', async evt => {
|
||||
const publicKey = (await keyPair).publicKey;
|
||||
const publicKeyPem = await exportCryptoKeyPem(publicKey);
|
||||
fetch('api/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'public-key' : publicKeyPem,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ username: loginForm.username.value, password: loginForm.password.value})
|
||||
}).then(async response => {
|
||||
const nonceHeader = response.headers.get('nonce');
|
||||
const encryptedNonce = atob(nonceHeader);
|
||||
const privateKey = (await keyPair).privateKey;
|
||||
const crypto = window.crypto.subtle;
|
||||
const encryptedBuffer = Uint8Array.from(atob(nonceHeader), c => c.charCodeAt(0));
|
||||
nonce = await crypto.decrypt({ name: "RSA-OAEP" }, privateKey, encryptedBuffer)
|
||||
.then(it => new Uint8Array(it));
|
||||
return response.text();
|
||||
}).then(text => {
|
||||
let paragraph = document.createElement('p');
|
||||
paragraph.textContent = text;
|
||||
document.body.appendChild(paragraph);
|
||||
});
|
||||
});
|
||||
|
||||
let button = document.createElement('button');
|
||||
button.textContent = 'Press me'
|
||||
document.body.appendChild(button);
|
||||
|
||||
button.addEventListener('click', async evt => {
|
||||
let header = {};
|
||||
if(nonce != null) {
|
||||
const crypto = window.crypto.subtle;
|
||||
const epochTick = Math.floor(new Date().getTime() / 10000)
|
||||
const data = concatenateUInt8Arrays(nonce, integerToBytes(epochTick, 8))
|
||||
const hash = new Uint8Array(await crypto.digest("SHA-256", data));
|
||||
const token = btoa(Array.from(hash, byte => String.fromCharCode(byte)).join(''));
|
||||
headers = {
|
||||
'x-token': token
|
||||
};
|
||||
}
|
||||
fetch('api/hello', {
|
||||
method: 'GET',
|
||||
headers
|
||||
}).then(response => response.text()).then(text => {
|
||||
let paragraph = document.createElement('p');
|
||||
paragraph.textContent = text;
|
||||
document.body.appendChild(paragraph);
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user