Create secret

API query: POST https://password.link/api/secrets

Required API key type: private API key

An example of encrypting a secret and sending it over to the password.link API using JavaScript. Uses jQuery, seedrandom and SJCL.

A script like this can be used on local computer or local network to conveniently create secrets on our service, however never put this to a public location without proper authentication.

<html>
  <head>
    <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
    <meta content="utf-8" http-equiv="encoding">

    <!-- Get the latest jQuery from https://jquery.com -->
    <script src="jquery-3.4.1.min.js"></script>

    <!-- Get sjcl.js from https://github.com/bitwiseshiftleft/sjcl -->
    <script src="sjcl.js"></script>

    <!-- Get seedrandom.min.js from https://github.com/davidbau/seedrandom -->
    <script src="seedrandom.min.js"></script>

    <script>
      // Set the base URL for the generated link
      var LINK_BASE_URL = "https://some.site/secret.html";

      // Set the API key here - a private API key is required
      var PRIVATE_API_KEY = "private_key_abcd...";

      // ----------------------------------- //

      // A function for sending the secret to password.link API
      function send_to_passwordlink_api(secret) {
        // Create the public password part
        var password_part_public = generate_string();
        var password_part_public_base64 = btoa(password_part_public);

        // Create the private password part
        var password_part_private = generate_string();
        var password_part_private_base64 = btoa(password_part_private);

        // Create an SJCL compatible Base64 encoded ciphertext
        var ciphertext = encrypt_secret(password_part_public, password_part_private, secret);

        // The data which will be sent over to the password.link API
        var data = {
          "ciphertext": ciphertext,
          "password_part_private": password_part_private_base64
        };

        // Send a request to the password.link API and process the result
        $.ajax({
          type: "POST",
          url: "https://password.link/api/secrets",
          dataType: "json",
          contentType: "application/json",
          headers: {
            "Authorization": "ApiKey " + PRIVATE_API_KEY
          },
          data: JSON.stringify(data),
          success: function(data) {
            var secret = data.data;
            var meta = data.metadata;
            var secret_url = LINK_BASE_URL + "?" + secret.id + "#" + password_part_public_base64;
            $("#secret").html("URL to secret: " + secret_url);
            $("#secret-meta").html("Total secrets: " + meta.secrets_total);
            $("#secret-meta").append("<br> Usage: " + meta.secrets_usage);
            $("#secret-meta").append("<br> Allowance: " + meta.secrets_allowance);
          },
          error: function(data) {
            var error = data.responseJSON.error;
            $("#secret").html("Error: " + error.message);
          }
        });
      }

      // A function for encrypting a secret, returns a Base64 encoded SJCL compatible ciphertext
      function encrypt_secret(password_part_public, password_part_private, secret) {
        try {
          var ciphertext_base64 = btoa(sjcl.encrypt(
            password_part_private + password_part_public,
            secret,
            { "mode": "gcm", "ks": 256, "iter": 10000 }
          ));

          return ciphertext_base64;
        }
        // Catch and show errors
        catch(e) {
          $("#secret").html("Error during encryption: " + e);
        }
      }

      // A function for generating a random 18 characters long string
      function generate_string() {
        var len = 18;
        var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$|/\!_+,.-?()[]{}<>&#^*=@";

        // Use seedrandom.js to create autoseeded PRNG
        Math.seedrandom();

        var str = "";
        for (var i = 0; i < len; i++) {
          str += chars.charAt(Math.floor(Math.random() * chars.length));
        }

        return str;
      }

      // Execute the send function when the button is clicked
      $(document).ready(function() {
        $("#secret-button").click(function(e) {
          e.preventDefault();
          var secret = $("#secret-input").val();
          send_to_passwordlink_api(secret);
        });
      });
    </script>
    <style>
      body {
        font: 12px Arial;
      }
      .container {
        max-width: 960px;
        margin: 0 auto;
        text-align: center;
        margin-top: 60px;
      }
      #secret {
        margin-top: 30px;
        font-size: 18px;
      }
      #secret-meta {
        margin-top: 40px;
      }
      .secret-form {
        display: flex;
        flex-flow: row wrap;
        align-items: center;
        flex-direction: vertical;
        justify-content: center;
      }
      .secret-form input {
        vertical-align: middle;
        margin: 5px 10px 5px 0;
        padding: 10px;
        border: 1px solid #ddd;
        width: 200px;
      }
      .secret-form button {
        padding: 10px 20px;
        border: 1px solid #ddd;
      }
      .secret-form button:hover {
        cursor: pointer;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <h2>Encrypt and create a secret on password.link</h2>
      <form class="secret-form">
        <input id="secret-input" type="text" name="secret">
        <button id="secret-button">Encrypt and create link</button>
      </form>

      <!-- This div will contain the link to the secret (or error) -->
      <div id="secret"></div>
      <div id="secret-meta"></div>
    </div>
  </body>
</html>

View secret

API query: GET https://password.link/api/secrets/<id>

Required API key type: public API key

An example of fetching, decrypting and displaying a secret using JavaScript. Uses jQuery and SJCL.

A script like this can be used to create a self-hosted view secret page.

<html>
  <head>
    <!-- Get the latest jQuery from https://jquery.com -->
    <script src="jquery-3.4.1.min.js"></script>

    <!-- Get sjcl.js from https://github.com/bitwiseshiftleft/sjcl -->
    <script src="sjcl.js"></script>

    <style>
      body {
        font: 12px Arial;
      }
      .container {
        max-width: 960px;
        margin: 0 auto;
        text-align: center;
        margin-top: 60px;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <h2>Here's the secret</h2>

      <!-- This div will contain the decrypted secret (or error) -->
      <div id="secret"></div>
    </div>

    <script>
      (function() {
        // Set the API key here - a public API key is required
        var PUBLIC_API_KEY = "public_key_abcd...";

        // ----------------------------------- //

        // Get the secret ID from the query string part of the URL
        // E.g. https://some.site/secret.html?secret_id
        var secret_id = location.search.substr(1);

        // Get the public password (encryption key) part from the hash part of the URL, in Base64 format
        // E.g. https://some.site/secret.html?secret_id#password_part_public
        var password_part_public = location.hash.substr(1);

        // Send a request to the password.link API and process the result
        $.ajax({
          type: "GET",
          url: "https://password.link/api/secrets/" + secret_id,
          dataType: "json",
          headers: {
            "Authorization": "ApiKey " + PUBLIC_API_KEY
          },
          success: function(data) {
            var secret = data.data;
            decrypt_secret(password_part_public, secret.password_part_private, secret.ciphertext);
          },
          error: function(data) {
            var error = data.responseJSON.error;
            $("#secret").html("Error: " + error.message);
          }
        });

        // A function for decrypting the secret received from the password.link API
        function decrypt_secret(password_part_public, password_part_private, ciphertext) {
          try {
            // Decrypt the secret using SJCL
            // All parameters are in Base64 format
            var decrypted_secret = sjcl.decrypt(atob(password_part_private) + atob(password_part_public), atob(ciphertext));

            // Set the content of the element with id "secret" to the decrypted secret
            // Use .textContent to avoid XSS
            document.getElementById("secret").textContent = decrypted_secret;
          }
          // Catch and show errors
          catch(e) {
            document.getElementById("secret").textContent = "Error during decryption: " + e;
          }
        }
      })();
    </script>
  </body>
</html>