1 parent
4f019a97c0
commit
7e1a99a691
6 files changed
+29
-13
No files matched your search
+3
-1
@@ -236,6 +236,7 @@
|
||||
var it = 0;
|
||||
var start = Date.now();
|
||||
const challengeNonce = "{{ tl:text challengeNonce }}";
|
||||
const challengeTimestamp = "{{ tl:text challengeTimestamp }}";
|
||||
const difficulty = parseInt("{{ tl:text challengeDifficulty }}");
|
||||
const challengeSig = "{{ tl:text challengeSignature }}";
|
||||
const challengeFingerprint = "{{ tl:text challengeFingerprint }}";
|
||||
@@ -267,7 +268,8 @@
|
||||
solution: it,
|
||||
difficulty: difficulty,
|
||||
sig: challengeSig,
|
||||
fingerprint: challengeFingerprint
|
||||
fingerprint: challengeFingerprint,
|
||||
timestamp: challengeTimestamp
|
||||
});
|
||||
|
||||
fetch("/checkpoint/challenge",
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
<!DOCTYPE html><head><title>STOP! - Checkpoint</title></head><body style="background-color:#0e0e0e;overflow:hidden"><style>@font-face{font-family:"Noto Sans";src:url(/checkpoint/NotoSans.woff)}*{font-family:"Noto Sans"}.middle-box{display:block;position:absolute;width:30rem;height:calc(100% - 2rem);left:calc(50% - 15rem);top:1rem;background-color:#111;border-radius:.5rem;border:1px solid #280d0e}.big-icon{margin:0;padding:0;font-size:8rem;color:#fff;width:100%;text-align:center}.subtext{margin:0;padding:0;font-size:3rem;color:#d9d9d9;width:100%;text-align:center}.text-hr{margin:auto;margin-top:1rem;margin-bottom:1rem;padding:0;background-color:#444;width:50%;box-shadow:none;text-align:center;height:1px;border:none}.text-hr-small{margin:auto;margin-top:1rem;margin-bottom:1rem;padding:0;background-color:#444;width:25%;box-shadow:none;text-align:center;height:1px;border:none}.text-description{margin:0;padding:0;font-size:1rem;color:#d9d9d9;width:85%;margin-left:7.5%;text-align:center}.text-question{margin:0;margin-top:4rem;padding:0;font-size:1.4rem;color:#d9d9d9;width:85%;margin-left:7.5%;text-align:center}.bottom-bar{position:absolute;width:100%;height:auto;bottom:.4rem;left:0}.bottom-hash-text{margin:0;padding:0;font-size:1rem;color:#d9d9d9;width:85%;margin-left:7.5%;text-align:center}.bottom-credit-text{display:block;margin:0;padding:0;font-size:.5rem;color:#4b4b4b;width:98%;text-align:right;transition:ease-in-out .1s;margin-top:.2rem}.bottom-credit-text:hover,.bottom-credit-text:link:hover,.bottom-credit-text:visited:hover{color:#764343;cursor:pointer}.bottom-credit-text:link,.bottom-credit-text:visited{color:#4b4b4b}.bottom-progress{position:relative;height:3px;background-color:#222;width:80%;margin-left:10%;margin-bottom:.8rem}.bottom-progress-light{position:absolute;left:0;top:0;width:0%;height:3px;background-color:#692225;transition:ease-in-out .1s}@media (pointer:none),(pointer:coarse){.big-icon{margin-top:10rem}.middle-box{width:90%;left:5%}.subtext{font-size:6rem}.text-description{font-size:3rem}.text-hr{height:3px}.bottom-hash-text{font-size:2.5rem}.bottom-credit-text{font-size:1rem}}</style><div class="middle-box"><p class="big-icon">🛑</p><p class="subtext" id="subtext">STOP!</p><hr class="text-hr"><p class="text-description">Verifying that you are not a bot. This might take a short moment.<br><br>You do not need to do anything.</p><p class="text-question">Why am I seeing this?</p><hr class="text-hr-small"><p class="text-description">This website protects itself from AI bots and scrapers by asking you to complete a cryptographic challenge before allowing you entry.</p><div class="bottom-bar"><div class="bottom-progress"><div class="bottom-progress-light" name="progress-light"></div></div><p class="bottom-hash-text" id="results">Difficulty {{ tl:text challengeDifficulty }}, elapsed 0ms, 0h, 0h/s</p><a class="bottom-credit-text" href="https://github.com/vaxerski/checkpoint"><i>Powered by checkpoint v{{ tl:text checkpointVersion }}</i></a></div></div>
|
||||
<script type="text/javascript">setTimeout(async function(){var e=0,t=Date.now();let l="{{ tl:text challengeNonce }}",n=parseInt("{{ tl:text challengeDifficulty }}");function $(e){return e>1e6?parseInt(e/1e5)/10+"M":parseInt(e/100)/10+"k"}function r(e){return e>1e3?parseInt(e/100)/10+"s":e+"ms"}function o(e){return Math.floor((1-1/(Math.pow(e/Math.pow(2,4*n)*3,2)+1))*100)}function i(e){document.getElementById("results").innerHTML="Success! Completed challenge after "+$(e)+" iterations, in "+r(Math.floor(Date.now()-t))+".",document.getElementsByName("progress-light")[0].style.width="100%";let o=JSON.stringify({challenge:l,solution:e,difficulty:n,sig:"{{ tl:text challengeSignature }}",fingerprint:"{{ tl:text challengeFingerprint }}"});fetch("/checkpoint/challenge",{headers:{"Content-Type":"application/json"},method:"POST",body:o}).then(e=>{200==e.status?(console.log("Got token."),window.location.reload()):console.log("Server error")})}let s=new Uint8Array(l.length+10);for(let _=0;_<l.length;_++)s[_]=l.charCodeAt(_);for(;;){let c=0===e?1:~~Math.floor(Math.log10(e))+1;for(let g=c-1;g>=0;g--)s[l.length+(c-1-g)]=48+~~Math.floor(e/Math.pow(10,g)%10);let a=new Uint8Array(await window.crypto.subtle.digest("sha-256",s.slice(0,l.length+c))),f=0;for(;f<n&&(f%2==0?a[~~(f/2)]>>4:15&a[~~(f/2)])==0;f++);if(f>=n){i(e),console.log("Success: it "+e+": "+Array.from(a).map(e=>e.toString(16).padStart(2,"0")).join(""));break}if(++e%11377==0){let h=Math.floor(Date.now()-t),u=e/(h/1e3);document.getElementById("results").innerHTML="Difficulty: "+n+", elapsed "+r(h)+", "+$(e)+"h, "+$(u)+"h/s",document.getElementsByName("progress-light")[0].style.width=o(e)+"%"}}},100);var currentTitle=1;setInterval(()=>{let e=["STOP","HALT","STÓJ","ARRÊT","СТІЙ"];document.getElementById("subtext").innerHTML=e[currentTitle]+"!",++currentTitle>=e.length&&(currentTitle=0)},2e3);</script></body>
|
||||
<script type="text/javascript">setTimeout(async function(){var e=0,t=Date.now();let l="{{ tl:text challengeNonce }}",n=parseInt("{{ tl:text challengeDifficulty }}");function $(e){return e>1e6?parseInt(e/1e5)/10+"M":parseInt(e/100)/10+"k"}function r(e){return e>1e3?parseInt(e/100)/10+"s":e+"ms"}function o(e){return Math.floor((1-1/(Math.pow(e/Math.pow(2,4*n)*3,2)+1))*100)}function i(e){document.getElementById("results").innerHTML="Success! Completed challenge after "+$(e)+" iterations, in "+r(Math.floor(Date.now()-t))+".",document.getElementsByName("progress-light")[0].style.width="100%";let o=JSON.stringify({challenge:l,solution:e,difficulty:n,sig:"{{ tl:text challengeSignature }}",fingerprint:"{{ tl:text challengeFingerprint }}",timestamp:"{{ tl:text challengeTimestamp }}"});fetch("/checkpoint/challenge",{headers:{"Content-Type":"application/json"},method:"POST",body:o}).then(e=>{200==e.status?(console.log("Got token."),window.location.reload()):console.log("Server error")})}let s=new Uint8Array(l.length+10);for(let a=0;a<l.length;a++)s[a]=l.charCodeAt(a);for(;;){let c=0===e?1:~~Math.floor(Math.log10(e))+1;for(let g=c-1;g>=0;g--)s[l.length+(c-1-g)]=48+~~Math.floor(e/Math.pow(10,g)%10);let _=new Uint8Array(await window.crypto.subtle.digest("sha-256",s.slice(0,l.length+c))),f=0;for(;f<n&&(f%2==0?_[~~(f/2)]>>4:15&_[~~(f/2)])==0;f++);if(f>=n){i(e),console.log("Success: it "+e+": "+Array.from(_).map(e=>e.toString(16).padStart(2,"0")).join(""));break}if(++e%11377==0){let h=Math.floor(Date.now()-t),u=e/(h/1e3);document.getElementById("results").innerHTML="Difficulty: "+n+", elapsed "+r(h)+", "+$(e)+"h, "+$(u)+"h/s",document.getElementsByName("progress-light")[0].style.width=o(e)+"%"}}},100);var currentTitle=1;setInterval(()=>{let e=["STOP","HALT","STÓJ","ARRÊT","СТІЙ"];document.getElementById("subtext").innerHTML=e[currentTitle]+"!",++currentTitle>=e.length&&(currentTitle=0)},2e3);</script></body>
|
||||
+13
-4
@@ -5,10 +5,11 @@
|
||||
#include <fmt/format.h>
|
||||
#include <glaze/glaze.hpp>
|
||||
|
||||
constexpr const uint64_t CHALLENGE_VERSION = 1;
|
||||
constexpr const uint64_t CHALLENGE_VERSION = 2;
|
||||
constexpr const uint64_t CHALLENGE_EXPIRE_TIME_S = 600; // 10 minutes
|
||||
|
||||
CChallenge::CChallenge(const std::string& fingerprint, const std::string& challenge, int difficulty) :
|
||||
m_fingerprint(fingerprint), m_challenge(challenge), m_difficulty(difficulty) {
|
||||
m_fingerprint(fingerprint), m_challenge(challenge), m_difficulty(difficulty), m_issued(std::chrono::system_clock::now()) {
|
||||
std::string toSign = getSigString();
|
||||
|
||||
m_sig = g_pCrypto->sign(toSign);
|
||||
@@ -28,6 +29,10 @@ CChallenge::CChallenge(const std::string& jsonResponse) {
|
||||
m_fingerprint = s.fingerprint;
|
||||
m_sig = s.sig;
|
||||
|
||||
try {
|
||||
m_issued = std::chrono::system_clock::time_point(std::chrono::seconds(std::stoull(s.timestamp)));
|
||||
} catch (std::exception& e) { return; }
|
||||
|
||||
if (!g_pCrypto->verifySignature(getSigString(), m_sig))
|
||||
return;
|
||||
|
||||
@@ -54,9 +59,13 @@ std::string CChallenge::signature() const {
|
||||
}
|
||||
|
||||
bool CChallenge::valid() const {
|
||||
return m_valid;
|
||||
return m_valid && std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - m_issued).count() < CHALLENGE_EXPIRE_TIME_S;
|
||||
}
|
||||
|
||||
std::string CChallenge::getSigString() {
|
||||
return fmt::format("{}-{},{}", CHALLENGE_VERSION, m_fingerprint, m_challenge);
|
||||
return fmt::format("{}-{},{},{}", CHALLENGE_VERSION, m_fingerprint, m_challenge, std::chrono::duration_cast<std::chrono::seconds>(m_issued.time_since_epoch()).count());
|
||||
}
|
||||
|
||||
std::string CChallenge::timestampAsString() const {
|
||||
return std::to_string(std::chrono::duration_cast<std::chrono::seconds>(m_issued.time_since_epoch()).count());
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
class CChallenge {
|
||||
public:
|
||||
@@ -10,17 +11,20 @@ class CChallenge {
|
||||
std::string fingerprint() const;
|
||||
std::string challenge() const;
|
||||
std::string signature() const;
|
||||
std::string timestampAsString() const;
|
||||
bool valid() const;
|
||||
|
||||
private:
|
||||
std::string getSigString();
|
||||
std::string getSigString();
|
||||
|
||||
std::string m_sig, m_fingerprint, m_challenge;
|
||||
bool m_valid = false;
|
||||
int m_difficulty = 4;
|
||||
std::string m_sig, m_fingerprint, m_challenge;
|
||||
bool m_valid = false;
|
||||
int m_difficulty = 4;
|
||||
|
||||
std::chrono::system_clock::time_point m_issued;
|
||||
|
||||
struct SChallengeJSON {
|
||||
std::string fingerprint, challenge, sig;
|
||||
std::string fingerprint, challenge, sig, timestamp;
|
||||
int difficulty = 4, solution = 0;
|
||||
};
|
||||
};
|
||||
+2
-2
@@ -98,7 +98,7 @@ std::string CCrypto::sha256(const std::string& in) {
|
||||
}
|
||||
|
||||
bool CCrypto::genKey() {
|
||||
EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, nullptr);
|
||||
EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, nullptr);
|
||||
|
||||
if (!ctx)
|
||||
return false;
|
||||
@@ -178,7 +178,7 @@ bool CCrypto::verifySignature(const std::string& in, const std::string& sig) {
|
||||
|
||||
auto sigAsArr = toByteArr(sig);
|
||||
|
||||
int ret = EVP_DigestVerify(ctx, sigAsArr.data(), sigAsArr.size(), (const unsigned char*)in.c_str(), in.size());
|
||||
int ret = EVP_DigestVerify(ctx, sigAsArr.data(), sigAsArr.size(), (const unsigned char*)in.c_str(), in.size());
|
||||
|
||||
if (ret == 1) {
|
||||
// match
|
||||
|
||||
@@ -296,6 +296,7 @@ void CServerHandler::serveStop(const Pistache::Http::Request& req, Pistache::Htt
|
||||
page.add("challengeNonce", CTinylatesProp(NONCE));
|
||||
page.add("challengeSignature", CTinylatesProp(CHALLENGE.signature()));
|
||||
page.add("challengeFingerprint", CTinylatesProp(CHALLENGE.fingerprint()));
|
||||
page.add("challengeTimestamp", CTinylatesProp(CHALLENGE.timestampAsString()));
|
||||
page.add("checkpointVersion", CTinylatesProp(CHECKPOINT_VERSION));
|
||||
response.send(Pistache::Http::Code::Ok, page.render().value_or("error"));
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user