Projects STRLCPY link-lock Commits 650bb1e0
🤬
  • ■ ■ ■ ■ ■
    README.md
    skipped 80 lines
    81 81   
    82 82  ## Usage
    83 83   
    84  -- Create a locked link [here](https://jstrieb.github.io/link-lock).
     84 +- Create a locked link here: [https://jstrieb.github.io/link-lock](https://jstrieb.github.io/link-lock).
    85 85  - Use the advanced options when creating a link to make the encryption more
    86 86   secure (at the cost of a longer link).
    87 87   - By default, the initialization vector is randomized for security, but
    skipped 3 lines
    91 91  - To bookmark a locked link, drag it from the output box to the bookmarks bar.
    92 92   Alternatively, visit the locked link and bookmark it before entering the
    93 93   password.
     94 +- If you lose the password, it is almost impossible to recover the original
     95 + link. The strong security guaranteed by encryption can be a blessing or a
     96 + curse if you are not careful!
     97 +- Currently, the only way to recover a lost password is by trying all possible
     98 + options (very slowly) by brute force. An example application to brute force
     99 + Link Lock URLs can be found here:
     100 + [https://jstrieb.github.com/link-lock/bruteforce](https://jstrieb.github.com/link-lock/bruteforce/).
    94 101   
    95 102   
    96 103   
    skipped 8 lines
  • ■ ■ ■ ■ ■ ■
    bruteforce/bruteforce.js
     1 +function error(text) {
     2 + const alert = document.querySelector(".alert");
     3 + alert.innerText = text;
     4 + alert.style.opacity = 1;
     5 +}
     6 + 
     7 +function onBruteForce() {
     8 + if (!("importKey" in window.crypto.subtle)) {
     9 + error("window.crypto not loaded. Please reload over https");
     10 + return;
     11 + }
     12 + if (!("b64" in window && "apiVersions" in window)) {
     13 + error("Important libraries not loaded!");
     14 + return;
     15 + }
     16 + 
     17 + const urlText = document.querySelector("#encrypted-url").value;
     18 + let url;
     19 + try {
     20 + url = new URL(urlText);
     21 + } catch {
     22 + error("Entered text is not a valid URL. Make sure it includes \"https://\" too!");
     23 + return;
     24 + }
     25 + 
     26 + let params;
     27 + try {
     28 + params = JSON.parse(b64.decode(url.hash.slice(1)));
     29 + } catch {
     30 + error("The link appears corrupted.");
     31 + return;
     32 + }
     33 + 
     34 + if (!("v" in params && "e" in params)) {
     35 + error("The link appears corrupted. The encoded URL is missing necessary parameters.");
     36 + return;
     37 + }
     38 + 
     39 + if (!(params["v"] in apiVersions)) {
     40 + error("Unsupported API version. The link may be corrupted.");
     41 + return;
     42 + }
     43 + 
     44 + const api = apiVersions[params["v"]];
     45 + 
     46 + const encrypted = b64.base64ToBinary(params["e"]);
     47 + const salt = "s" in params ? b64.base64ToBinary(params["s"]) : null;
     48 + const iv = "i" in params ? b64.base64ToBinary(params["i"]) : null;
     49 + 
     50 + const cset = document.querySelector("#charset").value.split("");
     51 + if (charset == "") {
     52 + error("Charset cannot be empty.");
     53 + return;
     54 + }
     55 + 
     56 + var progress = {
     57 + tried: 0,
     58 + total: 0,
     59 + len: 0,
     60 + overallTotal: 0,
     61 + done: false,
     62 + startTime: performance.now()
     63 + };
     64 + 
     65 + async function tryAllLen(prefix, len, curLen) {
     66 + if (progress.done) return;
     67 + if (len == curLen) {
     68 + progress.tried++;
     69 + try {
     70 + await api.decrypt(encrypted, prefix, salt, iv);
     71 + document.querySelector("#output").value = prefix;
     72 + progress.done = true;
     73 + error("Completed!");
     74 + } catch {}
     75 + return;
     76 + }
     77 + for (let i=0; i < cset.length; i++) {
     78 + let c = cset[i];
     79 + await tryAllLen(prefix + c, len, curLen + 1);
     80 + }
     81 + }
     82 + 
     83 + function progressUpdate() {
     84 + if (progress.done) {
     85 + clearInterval();
     86 + return;
     87 + }
     88 + let delta = performance.now() - progress.startTime;
     89 + error(`Trying ${progress.total} passwords of length ${progress.len} – ${Math.round(100000 * progress.tried / progress.total)/1000}% complete. Testing ${Math.round(1000000 * (progress.overallTotal + progress.tried) / delta)/1000} passwords per second.`);
     90 + }
     91 + 
     92 + (async () => {
     93 + for (let len=0; !progress.done; len++) {
     94 + progress.overallTotal += progress.tried;
     95 + progress.tried = 0;
     96 + progress.total = Math.pow(cset.length, len);
     97 + progress.len = len;
     98 + progressUpdate();
     99 + await tryAllLen("", len, 0);
     100 + }
     101 + })();
     102 + 
     103 + setInterval(progressUpdate, 4000);
     104 +}
     105 + 
  • ■ ■ ■ ■ ■ ■
    bruteforce/index.html
     1 +<!DOCTYPE html>
     2 +<head>
     3 + <!-- Metadata -->
     4 + <meta charset="utf-8" />
     5 + <meta name="author" content="Jacob Strieb" />
     6 + <meta name="description" content="Brute force password-protected links created with Link Lock from within the browser." />
     7 + <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
     8 + 
     9 + <link rel="shortcut icon" type="image/x-icon" href="../favicon.ico">
     10 + 
     11 + <title>Brute Force Link Lock</title>
     12 + 
     13 + <!-- Styles -->
     14 + <link rel="stylesheet" href="../style.css" type="text/css" />
     15 + 
     16 + <!-- Scripts -->
     17 + <script type="text/javascript" src="../b64.js"></script>
     18 + <script type="text/javascript" src="../api.js"></script>
     19 + <script type="text/javascript" src="bruteforce.js"></script>
     20 +</head>
     21 + 
     22 +<body>
     23 + <!-- View on GitHub ribbon -->
     24 + <a href="https://github.com/jstrieb/link-lock" target="_blank">
     25 + <img class="ribbon" src="../corner-ribbon-minified.svg" alt="View on GitHub" />
     26 + </a>
     27 + 
     28 + <!-- Explanation for those who do not have JavaScript enabled -->
     29 + <noscript>
     30 + <div style="border: 3px solid red; padding: 2em;">
     31 + <p>If you are seeing this, it means that you have JavaScript disabled. As a result, the application will not work properly for you. For example, none of the buttons will work.</p>
     32 + 
     33 + <p>This application is entirely programmed in JavaScript. This was done intentionally, so that all encryption and decryption happens client-side. This means the code runs as a distributed application, relying only on GitHub Pages for infrastructure. It also means that no data about locked links is ever stored on a server. The code is designed to be auditable so users can investigate what is happening behind the scenes.</p>
     34 + 
     35 + <p>If you still want to run the application, I encourage you to clone the <a href="https://github.com/jstrieb/link-lock">source code on GitHub</a>. That way you can disable JavaScript only for trusted files on your local machine.</p>
     36 + </div>
     37 + </noscript>
     38 + 
     39 + <!-- Introduction -->
     40 + <h1>Brute Force Link Lock URLs</h1>
     41 + <p>"Brute forcing" a password is the act of guessing it by brute force – literally trying all passwords until one works. This page is a simple, proof-of-concept brute force application. It is designed to decrypt <a href="https://github.com/jstrieb/link-lock" target="_blank">Link Lock</a> URLs by trying every single possible password. It is not optimized in any way, and does minimal error-checking.</p>
     42 + <p>For more information about brute forcing Link Lock URLs, read the open <a href="https://github.com/jstrieb/link-lock/issues/1" target="_blank">GitHub issue</a> about it.</p>
     43 + 
     44 + <hr />
     45 + 
     46 + <!-- Main form -->
     47 + <div class="form">
     48 + <label for="charset">charset</label>
     49 + <input type="text" id="charset" value="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" />
     50 + <label for="encrypted-url">encrypted url</label>
     51 + <input type="url" id="encrypted-url" oninput="document.querySelector('.alert').style.opacity = 0" />
     52 + <button onclick="onBruteForce()">Brute Force</button>
     53 + <p class="alert">INVISIBLE</p>
     54 + </div>
     55 + 
     56 + <hr />
     57 + 
     58 + <!-- Output area -->
     59 + <div class="output">
     60 + <label for="output">brute-forced password</label>
     61 + <input type="text" id="output" />
     62 + </div>
     63 + 
     64 + <!-- Page footer -->
     65 + <footer>
     66 + <hr />
     67 + <p class="copyright">Created by <a href="https://jstrieb.github.io">Jacob Strieb</a>.</p>
     68 + </footer>
     69 +</body>
     70 + 
     71 +</html>
     72 + 
  • ■ ■ ■ ■ ■ ■
    create/index.html
    skipped 78 lines
    79 79   </ul>
    80 80   </div>
    81 81   
     82 + <hr />
     83 + 
    82 84   <!-- Main form -->
    83 85   <div class="form">
    84 86   <div class="labeled-input">
    skipped 53 lines
Please wait...
Page is in error, reload to recover