# Secure Your Data with Web Crypto API: Browser Encryption Guide


Encryption is an essential process in cryptography where readable information, known as **plaintext**, is converted into an unreadable format known as **ciphertext**. This ensures that only authorised parties who have access to the decryption key can read the original data. To encrypt data in the browser, we utilise the **Web Crypto API.**  
**Web Crypto API** is a browser interface that allows web applications to perform cryptographic operations using various algorithms. The best algorithm to be used must meet the basic cryptographic security requirements

* **Confidentiality**: Unauthorised users cannot read the data
* **Integrity:** The data has not been tampered with during transmission or storage
* **Authenticity:** The identity of the sender is verified

The encryption algorithm that satisfies the conditions above is **Advanced Encryption Method with Galois/Counter Mode** (**AES-GCM).**  To access the web crypto API, we will use the crypto.subtle interface,  this gives us access to cryptographic functions like encrypt(), decrypt(), etc. This allows us to perform cryptographic operations directly within a web application.

## Random Values
`crypto.getRandomValues()` generates cryptographically secure random numbers; this is more secure than math.random , the random numbers generated would be used as the initialization vector.
## Initialization Vector (IV)
Initialization vector (**IV**) is an important, non-secret key required by **AES-GCM** and other encryption algorithms. Its primary purpose is to make sure that the encryption produces different ciphered text each time the same plain text is encrypted. The standard method for generating a cryptographically secure IV in the browser is using the crypto.getRandomValues method, for **AES-GCM** the recommended IV size is 12 bits

```javascript
 iv = window.crypto.getRandomValues(new Uint8Array(12));
```
## Crypto Key
The crypto key is an essential part of the Web Crypto API; it is the secret material used for encryption, decryption, and other cryptographic operations. To generate a crypto key `crypto.subtle.generateKey()` is used. The `generateKey()` accepts four arguments: type, algorithm, extractable and usages.

```javascript
const key = await window.crypto.subtle
    .generateKey(
      {
        name: "AES-GCM",
        length: 256,
      },
      true,
      ["encrypt", "decrypt"]
    )
```

The type argument specifies the category of the key, which can be either “public” or “secret”. The algorithm object indicates the type of algorithm used, along with its key and other properties, such as length and hash function. Extractable tells if the key can be exported or not. Usages is an array of strings defining the allowed crypto operations for the key.
## Encrypt
`encrypt()` This is the method of the CryptoSubtle interface that encrypts the data. It accepts three arguments: the algorithm and its parameters (mostly the initialization vector), the crypto key, and the data to be encrypted.

```javascript
const encode = (text) => {
  return new TextEncoder().encode(text);
};

const encrypt = async (text, cryptoKey) => {
    const iv = crypto.getRandomValues(new Uint8Array(12));
    const encryptedBuffer = await window.crypto.subtle.encrypt(
        {
            name: CRYPTO_ALGO_NAME,
            iv,
        },
        cryptoKey,
        encode(text)
    );
}
```
## Decrypt
`decrypt().` This method of the CryptoSubtle interface turns the unreadable encrypted data into readable plaintext. It accepts as arguments the algorithm used to encrypt the data, the crypto key and the encrypted data.

```javascript
await window.crypto.subtle.decrypt(
    { name: "AES-GCM", iv: initializationVector },
    cryptoKey,
    cipheredText
  );
```

## Import and Export Key
In some cases, the crypto key needs to be stored or transmitted; in this case, the `exportKey()` method is used to extract a cryptographic key from the crypto key, which can now be stored or transmitted. The `exportKey()` accepts two arguments: the export format and the crypto key. The export format is the format in which the crypto key can be exported. There are four export formats: **raw**, **JSON Web Key (JWK)**, **Standard public key info (SPKI), and Public Key Cryptographic Standard 8 (PKCS8).**

```javascript
const exportedkey = await window.crypto.subtle.exportKey("jwk", cryptoKey);
```

To use the exported key, the `importKey()` function is used to convert the exported key back to a crypto key that can be used for decryption. The `importKey()` accepts five arguments: format, keyData, algorithm, extractable, and keyUsages.

```javascript
await window.crypto.subtle.importKey(
    "jwk",
    {
      alg: "A128GCM",
      ext: true,
      k: encryptionKey,
      key_ops: ["encrypt", "decrypt"],
      kty: "oct",
    },
    {
      name: "AES-GCM",
      length: 128,
    },
    false,
    ["decrypt"]
  );
```

## Example

For this example, we are going to build a simple page with inputs to enter text that we want to encrypt, then copy the encryption details and paste them inside the decryption inputs to decrypt. The main focus of this is on the JavaScript side of things.

This is the HTML markup

```html
 <main>
        <div>
            <div class="encrypt">
                <h2>Web Data Encryption</h2>
                <p>Enter Text that you want to encrypt</p>
                <textarea placeholder="Enter text"></textarea>
                <button id="encrypt">Encrypt</button>
            </div>
            <div class="encryption-details">
                <h3>Encryption Details</h3>
                <span>
                    <h3>Ciphered Text</h3>
                    <p class="ciphered-text" id="ciphered-text"></p>
                    <button id="copy">Copy</button>
                </span>
                <span>
                    <h3>Initialization Vector (IV)</h3>
                    <p class="iv" id="iv"></p>
                    <button id="copyIV">Copy</button>
                </span>

                <span>
                    <h3>Crypto Key</h3>
                    <p class="ciphered-text" id="cryptoKey"></p>
                    <button id="copyKey">Copy</button>
                </span>
            </div>
        </div>
        <div>
            <div class="encrypt">
                <h2>Web Data Encryption</h2>
                <p>Enter Ciphered Text to decrypt</p>
                <textarea placeholder="Enter ciphered text" id="decrypted-text-input"></textarea>
                <input type="text" id="ivInput" class="ivInput" placeholder="Enter Initialization Vector (IV)">
                <input type="text" id="cryptoKeyInput" class="ivInput" placeholder="Enter cryptoKey">
                <p class="error" id="error">Could not decrypt text</p>
                <button id="decrypt">Decrypt</button>
            </div>
            <div class="encryption-details">
                <!-- <h3>Decryption Details</h3>xx -->
                <span>
                    <h3>Decrypted Text</h3>
                    <p class="ciphered-text" id="decrypted-text"></p>
                </span>
            </div>
        </div>
    </main>
```

Then the styling

```css
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

main {
  padding: 16px;
  margin: auto;
  display: flex;
  gap: 20px;
}

main div {
  width: 100%;
  max-width: 100%;
}

textarea {
  /* margin-top: 10px; */
  padding: 16px;
}

.error {
  color: red;
  display: none;
}

.encrypt {
  /* width: 50%; */
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.encrypt button {
  border: none;
  background-color: cornflowerblue;
  color: white;
  padding: 10px 0;
  cursor: pointer;
}

.encryption-details {
  margin-top: 20px;
}

.encryption-details span p {
  word-break: break-all;
  word-wrap: break-word;
}

.encryption-details span button {
  border: none;
  padding: 10px 20px;
  margin-top: 10px;
  background-color: cornflowerblue;
  color: white;
  cursor: pointer;
  margin: 10px;
}

.ivInput {
  padding: 10px;
}

```

Then, to the JavaScript where we do the encryption and decryption  
First, we bring in all the DOM elements that we need.

```javascript
const ivOutput = document.getElementById("iv");
const cipheredTextOutput = document.getElementById("ciphered-text");
const decryptedTextTextOutput = document.getElementById("decrypted-text");
const textArea = document.querySelector("textarea");
const cipheredtextArea = document.querySelector("#decrypted-text-input");
const encrypt = document.getElementById("encrypt");
const decrypt = document.getElementById("decrypt");
const copy = document.querySelector("#copy");
const copyIV = document.querySelector("#copyIV");
const copyKey = document.querySelector("#copyKey");
const error = document.getElementById("error");
const ivInput = document.getElementById("ivInput");
const cryptoKeyOutput = document.getElementById("cryptoKey");
const cryptoKeyInput = document.getElementById("cryptoKeyInput");
```

Then we initialise variables for the cryptoKey and initialization vector (IV), and also declare the `encodeText()` and `decodeText()` functions.

```javascript
let iv;
let key;
function encodeText(text) {
  let enc = new TextEncoder();
  return enc.encode(text);
}

function decodeText(text) {
  let dec = new TextDecoder();
  return dec.decode(text);
}
```

The `encodeText()` function takes a string and returns a `Uint8Array` containing the string encoded. The `decodeText()` takes an encoded byte and returns a plain JavaScript string.

The next step is to generate the IV and the crypto key

```javascript
function generateIV() {
  iv = window.crypto.getRandomValues(new Uint8Array(12));
}

async function generateKey() {
  const key = await window.crypto.subtle.generateKey(
          {
            name: "AES-GCM",
            length: 128,
          },
          true,
          ["encrypt", "decrypt"]
  );
  return key;
}
```

Two helper functions are needed: a function to capture any text enclosed inside the square brackets, which we would call `sanitizeInput` and another function to convert data to `Uint8Array,` which we would call `dataToBytes`.

```javascript
const REGEX_FOR_EXTRACTING_TEXT_FROM_INPUT = /\[(.*?)\]/;

const sanitizeInput = (input) => {
  const val = input.match(REGEX_FOR_EXTRACTING_TEXT_FROM_INPUT)?.[1].trim();
  return val.split(",");
};

const dataToBytes = (data) => {
  const arr = Array.from(data);
  const bytes = new Uint8Array(arr);

  return bytes;
};
```

The next step is to set up the import and export key functions. The `exportCryptoKey()` function takes the crypto key as an argument and exports it in JSON Web Key (JWK) format using the `exportKey` method of `crypto.subtle,` returning the `k` value. The `importCryptoKey` function takes the exported crypto key as an argument, which is named `encryptionKey`. It turns the `encryptionKey` back into a usable crypto key using the `importKey` method of `crypto.subtle`.

```javascript
async function exportCryptoKey(cryptoKey) {
  const exportedkey = await window.crypto.subtle.exportKey("jwk", cryptoKey);
  return exportedkey.k;
}

async function importCryptoKey(encryptionKey) {
  const cryptoKey = await window.crypto.subtle.importKey(
    "jwk",
    {
      alg: "A128GCM",
      ext: true,
      k: encryptionKey,
      key_ops: ["encrypt", "decrypt"],
      kty: "oct",
    },
    {
      name: "AES-GCM",
      length: 128,
    },
    false,
    ["decrypt"]
  );
  return cryptoKey;
}
```

To encrypt and decrypt the text input, we declare the functions that would do the work: the `encryptText` and `decryptText` functions.

```javascript
async function encryptText(encodedText) {
  key = await generateKey();
  const exportedkey = await exportCryptoKey(key);
  cryptoKeyOutput.textContent = exportedkey;
  let encrypt = await window.crypto.subtle.encrypt(
    { name: "AES-GCM", iv },
    key,
    encodedText
  );
  let buffer = new Uint8Array(encrypt);
  return buffer;
}

async function decryptText(cipheredText, initializationVector) {
  const someKey = await importCryptoKey(cryptoKeyOutput.textContent);
  let decryptedText = await window.crypto.subtle.decrypt(
    { name: "AES-GCM", iv: initializationVector },
    someKey,
    cipheredText
  );
  return decryptedText;
}
```

The last step is to add event listeners to the encrypt and decrypt buttons. The ecrypt button event listener encodes the text input using the `encodeText` function that we declared earlier, generates the IV, encrypts the message and displays the encrypted message and IV to the user. The decrypt event listener cleans up the IV and ciphered text and converts them into `Uint8Array`, the `decryptText` function decrypts the message and returns bytes which is then turned into a plain text using the `decodeText` function and then displayed to the user.

```javascript
encrypt.addEventListener("click", async () => {
  const message = encodeText(textArea.value);
  generateIV();
  const encryptedText = await encryptText(message);
  cipheredTextOutput.textContent = `[${encryptedText}]`;
  ivOutput.textContent = `[${iv}]`;
});

decrypt.addEventListener("click", async () => {
  try {
    const ivInputValue = dataToBytes(sanitizeInput(ivInput.value));
    const message = cipheredtextArea.value;
    const san = dataToBytes(sanitizeInput(message));
    const decryptedMessage = await decryptText(san, ivInputValue);
    const decodedText = decodeText(decryptedMessage);
    decryptedTextTextOutput.textContent = decodedText;

    error.style.display = "none";
  } catch (e) {
    console.error(e);
    error.style.display = "block";
  }
});
```

Just to improve the user experience, we would add copy to clipboard functionality for each of the data that we need to copy; The cipheredText, initialization vector (IV), and crypto key.

```javascript
copy.addEventListener("click", async () => {
  await navigator.clipboard.writeText(cipheredTextOutput.textContent);
});

copyKey.addEventListener("click", async () => {
  await navigator.clipboard.writeText(cryptoKeyOutput.textContent);
});

copyIV.addEventListener("click", async () => {
  await navigator.clipboard.writeText(ivOutput.textContent);
});
```

