mirror of
https://github.com/sigmasternchen/axowall
synced 2025-03-15 08:38:55 +00:00
demo & proof of concept
This commit is contained in:
commit
44f96fffe9
2 changed files with 159 additions and 0 deletions
106
index.html
Normal file
106
index.html
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="main.js"></script>
|
||||||
|
<style>
|
||||||
|
.captcha {
|
||||||
|
width: 300px;
|
||||||
|
height: 70px;
|
||||||
|
padding: 20px;
|
||||||
|
border: 1px solid grey;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 20px;
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.captcha span {
|
||||||
|
position: relative;
|
||||||
|
top: -9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.captcha .checkbox {
|
||||||
|
position: relative;
|
||||||
|
margin: 0;
|
||||||
|
margin-right: 20px;
|
||||||
|
height: 30px;
|
||||||
|
width: 30px;
|
||||||
|
display: inline-block;
|
||||||
|
background-color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid black;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.captcha .checkbox:hover {
|
||||||
|
background-color: lightgrey;
|
||||||
|
}
|
||||||
|
|
||||||
|
.captcha .checkbox.checked {
|
||||||
|
background-color: #2196F3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.captcha .checkbox.checked::before {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
content: "";
|
||||||
|
width: 7px;
|
||||||
|
height: 15px;
|
||||||
|
top: 2px;
|
||||||
|
left: 9px;
|
||||||
|
border: white solid;
|
||||||
|
border-width: 0 5px 5px 0;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.captcha .checkbox.loading {
|
||||||
|
}
|
||||||
|
|
||||||
|
.captcha .checkbox.loading::before {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 2.5px;
|
||||||
|
left: 2.5px;
|
||||||
|
width: 17px;
|
||||||
|
height: 17px;
|
||||||
|
border: 4px solid;
|
||||||
|
border-radius: 50%;
|
||||||
|
border-color: black transparent black transparent;
|
||||||
|
animation: captchaLoading infinite 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes captchaLoading {
|
||||||
|
to{
|
||||||
|
transform: rotate(.5turn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
window.addEventListener("load", () => {
|
||||||
|
[...document.querySelectorAll(".captcha .checkbox")].forEach(c => c.addEventListener("click", async function() {
|
||||||
|
const result = document.getElementById("result");
|
||||||
|
result.innerText = "Calculating...";
|
||||||
|
|
||||||
|
this.classList.add("loading");
|
||||||
|
|
||||||
|
const challenge = makeSuffix(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)) + ":" + makeSuffix(new Date().valueOf()) + ":";
|
||||||
|
|
||||||
|
const response = await findHashWithPrefix(15, challenge);
|
||||||
|
result.innerText = "Challenge Response: " + response;
|
||||||
|
|
||||||
|
this.classList.remove("loading");
|
||||||
|
this.classList.add("checked");
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="captcha">
|
||||||
|
<div class="checkbox"></div>
|
||||||
|
<span>I am not a robot.</span>
|
||||||
|
</div>
|
||||||
|
<div id="result"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
53
main.js
Normal file
53
main.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
const hexCharset = "0123456789abcdef".split("");
|
||||||
|
const suffixCharset = [
|
||||||
|
"0123456789",
|
||||||
|
"abcdefghijklmnopqrstuvwxyz",
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
||||||
|
"+-",
|
||||||
|
].flatMap(set => set.split(""));
|
||||||
|
const suffixCharsetBits = 6;
|
||||||
|
|
||||||
|
const sha256 = "SHA-256";
|
||||||
|
|
||||||
|
Uint8Array.prototype.toHex = function() {
|
||||||
|
return [...this].map(c => hexCharset[c >> 4] + hexCharset[c & 15]).join("");
|
||||||
|
};
|
||||||
|
|
||||||
|
Uint8Array.prototype.hasPrefix = function(array, bits) {
|
||||||
|
for(let i = 0; i < array.length && i * 8 < bits; i++) {
|
||||||
|
const bitsForByte = Math.min(8, bits - i * 8);
|
||||||
|
const bitMask = ((1 << bitsForByte) - 1) << (8 - bitsForByte);
|
||||||
|
|
||||||
|
if ((this[i] & bitMask) != (array[i] & bitMask)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function digest(algo, input) {
|
||||||
|
return new Uint8Array(await crypto.subtle.digest(algo, new TextEncoder().encode(input)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeSuffix(index) {
|
||||||
|
let result = "";
|
||||||
|
|
||||||
|
while (index > 0) {
|
||||||
|
result += suffixCharset[index & ((1 << suffixCharsetBits) - 1)];
|
||||||
|
index >>= suffixCharsetBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function findHashWithPrefix(hashPrefixBits, inputPrefix) {
|
||||||
|
const hashPrefix = new Uint8Array(Array(Math.ceil(hashPrefixBits / 8)).map(c => 0));
|
||||||
|
let iteration = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
var message = inputPrefix + makeSuffix(iteration++)
|
||||||
|
var hash = await digest(sha256, message);
|
||||||
|
} while (!hash.hasPrefix(hashPrefix, hashPrefixBits));
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
Loading…
Reference in a new issue