E2E Encrypted

Generate a Doppler Share link by sending an encrypted secret. The receive flow the user goes through will be end-to-end encrypted where the encrypted secret will be decrypted on the browser.

Encryption Examples

// Imports
const crypto = require("crypto");
const pbkdf2Async = util.promisify(crypto.pbkdf2);

// Constants
const algorithm = "aes-256-gcm";
const keyLength = 256 / 8;
const ivLength = 12;
const saltLength = 16;
const saltRounds = 100000;
const authTagLength = 16;

// Encryption
async function encrypt(plainText) {
  // Generate Password
  const password = crypto.randomBytes(32).toString("hex");
  const hashedPassword = crypto.createHash("sha256").update(password).digest("hex");

  // Derive key with PBKD2
  const salt = crypto.randomBytes(saltLength);
  const iv = crypto.randomBytes(ivLength);
  const key = await pbkdf2Async(password, salt, saltRounds, keyLength, "sha256");
  
  // Encrypt plain text
  const cipher = crypto.createCipheriv(algorithm, key, iv);
  const encryptedData = Buffer.concat([cipher.update(plainText, "utf8"), cipher.final()]);
  const authTag = cipher.getAuthTag();

  return {
    encrypted_secret: Buffer.concat([salt, iv, authTag, encryptedData]).toString("base64"),
    password: password,
    hashedPassword: hashedPassword
  };
}

// Testing
encrypt("SECRET TO ENCRYPT").then(payload => {
  console.log(payload);
})
class CryptoLib {
  constructor() {
    this._encoder = new TextEncoder();
    this._decoder = new TextDecoder();
    this._saltRounds = 100000;
    this._keyLength = 256;
    this._ivLength = 12;
    this._saltLength = 16;
    this._authTagLength = 16;
  }

  randomString(length) {
    const validChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    let array = this.randomArray(length);
    array = array.map((x) => validChars.charCodeAt(x % validChars.length));
    return String.fromCharCode(...array);
  }

  randomArray(length) {
    return window.crypto.getRandomValues(new Uint8Array(length));
  }

  // Source: https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest#converting_a_digest_to_a_hex_string
  async sha256(text) {
    const msgUint8 = this._encoder.encode(text); // encode as (utf-8) Uint8Array
    const hashBuffer = await crypto.subtle.digest("SHA-256", msgUint8); // hash the message
    const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array
    const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join(""); // convert bytes to hex string
    return hashHex;
  }

  async generatePassword() {
    const password = this.randomString(64);

    return {
      password: password,
      hashedPassword: await this.sha256(password),
    };
  }

  async encrypt(secret) {
    const { password, hashedPassword } = await this.generatePassword();
    const salt = this.randomArray(this._saltLength);
    const iv = this.randomArray(this._ivLength);
    const aesKey = await this._deriveKey(password, salt, this._saltRounds, ["encrypt"]);
    const encryptedContent = await window.crypto.subtle.encrypt(
      {
        name: "AES-GCM",
        iv: iv,
        tagLength: this._authTagLength * 8,
      },
      aesKey,
      this._encoder.encode(secret)
    );
    const authTag = new Uint8Array(encryptedContent.slice(encryptedContent.byteLength - this._authTagLength));
    const encryptedContentArray = new Uint8Array(encryptedContent.slice(0, encryptedContent.byteLength - this._authTagLength));
    const buffer = new Uint8Array(salt.byteLength + iv.byteLength + authTag.byteLength + encryptedContentArray.byteLength);
    buffer.set(salt, 0);
    buffer.set(iv, salt.byteLength);
    buffer.set(authTag, salt.byteLength + iv.byteLength);
    buffer.set(encryptedContentArray, salt.byteLength + iv.byteLength + authTag.byteLength);

    return {
      encryptedSecret: this._bufferToBase64(buffer),
      password: password,
      hashedPassword: hashedPassword,
      kdf: "pbkdf2",
      saltRounds: this._saltRounds,
    };
  }

  async _deriveKey(password, salt, saltRounds, keyUsage) {
    const passwordKey = await window.crypto.subtle.importKey("raw", this._encoder.encode(password), "PBKDF2", false, [
      "deriveKey",
    ]);

    return window.crypto.subtle.deriveKey(
      {
        name: "PBKDF2",
        salt: salt,
        iterations: saltRounds,
        hash: "SHA-256",
      },
      passwordKey,
      { name: "AES-GCM", length: this._keyLength },
      false,
      keyUsage
    );
  }

  _bufferToBase64(buffer) {
    return btoa(String.fromCharCode.apply(null, buffer));
  }
}

// Testing
const cryptoLib = new CryptoLib();
cryptoLib.encrypt("SECRET TO ENCRYPT").then(payload => {
  console.log(payload);
})
Language
Credentials
Basic
base64
:
Click Try It! to start a request and see the response here!