Skip to main content

Encryption Algorithm

Enterprise Kitchen uses the AES-256 encryption algorithm along with the CTR Cipher Mode. 

To encrypt data with this algorithm:

  • Data—waiting for encryption

  • Password—32 bytes

  • Initialization Vector—16 bytes

Password

While a password format is not yet established, the overall vision is that the Secret Key value is set and sourced from Portal API.

Because raw passwords typically do not meet the length and entropy requirements for cryptographic keys, it is required to derive a key from the Secret Key before using a password for AES-256 encryption. Use the PBKDF2 algorithm to derive a Secret Key. 

Benefits of Secret Key Derivation:

  • Length: AES-256 requires a 256-bit (32-byted) key, but secret key can be shorter

  • Security: PBKDF2 increases the security by incorporating a salt and multiple iterations to thwart brute force attacks

Initialization Vector

An Initialization Vector (IV) is a fixed-size input to a cryptographic algorithm that typically has a random or pseudorandom requirement. Since sharing the IV is an essential part of the encryption and decryption process and because is not secret, the IV has the following requirements:

  • Unique and unpredictable for each encryption operation

  • Only transmitted openily along  with the ciphertext or HTTP request meta information

A key point of an IV is its fixed-size. This is determined by the block size of the encryption algorithm—for AES, the block size is 128 bits (16 bytes)

Data for Encryption

The raw payload that a POS intends to send to Enterprise Kitchen.

Encryption Example

A Node.js example, showing a possible encryption method:

const crypto = require("crypto");

async function generateDerivedKey(password, saltbase) {
  const textEncoder = new TextEncoder();
  const encodedPassword = textEncoder.encode(password);
  // Derive a key from the password using PBKDF2
  const passwordKey = await crypto.subtle.importKey(
    "raw",
    encodedPassword,
    { name: "PBKDF2" },
    false,
    ["deriveKey"]
  );
  const salt = textEncoder.encode(saltbase);
  const derivedKey = await crypto.subtle.deriveKey(
    {
      name: "PBKDF2",
      salt: salt,
      iterations: 100000,
      hash: "SHA-256",
    },
    passwordKey,
    { name: "AES-CTR", length: 256 },
    true,
    ["encrypt", "decrypt"]
  );
  return derivedKey;
}

async function encryptText(plainText, password, siteId) {
  // Convert the plain text and password to Uint8Array
  const textEncoder = new TextEncoder();
  const encodedText = textEncoder.encode(plainText);
  // generate derived key
  const derivedKey = await generateDerivedKey(password, siteId);
  // Generate a random IV
  const iv = crypto.getRandomValues(new Uint8Array(16));
  // Encrypt the text using AES-CTR
  const encryptedContent = await crypto.subtle.encrypt(
    {
      name: "AES-CTR",
      counter: iv,
      length: 64,
    },
    derivedKey,
    encodedText
  );

  // Convert the encrypted content and IV to hex strings
  const encryptedArray = new Uint8Array(encryptedContent);
  const encryptedHex = Array.from(encryptedArray)
    .map((b) => b.toString(16).padStart(2, "0"))
    .join("");
  const ivHex = Array.from(iv)
    .map((b) => b.toString(16).padStart(2, "0"))
    .join("");
  return { encryptedHex, ivHex };
}

async function decryptText(encryptedHex, ivHex, password, siteId) {
  // Convert the encrypted text, IV and password to Uint8Array
  const textEncoder = new TextEncoder();
  const encryptedArray = new Uint8Array(
    encryptedHex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
  );
  const iv = new Uint8Array(
    ivHex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
  );
  const derivedKey = await generateDerivedKey(password, siteId);
  // Decrypt the text using AES-CTR
  const decryptedContent = await crypto.subtle.decrypt(
    {
      name: "AES-CTR",
      counter: iv,
      length: 64,
    },
    derivedKey,
    encryptedArray
  );
  // Convert the decrypted content to a string
  const textDecoder = new TextDecoder();
  const decryptedText = textDecoder.decode(decryptedContent);
  return decryptedText;
}
(async function () {
  const secret_key = "secret"; //this value is a secret phrase from the Portal
  const siteId = "6621401d146e98f840719201"; //site id value should be used as a salt for derived key
  const { encryptedHex, ivHex } = await encryptText(
    "text to be encrypted",
    secret_key,
    siteId
  );
  const result = await decryptText(encryptedHex, ivHex, secret_key, siteId);
  console.log(result);
})();