2

I want to encrypt/decrypt a string with a human readable password. The encrypted data should be decryptable knowing the password only. What is the recommended tool in 2020?

  1. symmetric encryption in the built-in SubtleCrypto interface require generated key (based on the examples I found), and they also need additional data for decryption (a counter, or iv value).
  2. related SO question is 6 years old

    const data = "Hello World";
    const key = "SuperSecret";
    
    const encrypted = encrypt(data, key);
    console.log(encrypted);  // gibberish
    
    const decrypted = decrypt(encrypted, key);
    console.log(decrypted);  // Hello World
    
nagy.zsolt.hun
  • 6,292
  • 12
  • 56
  • 95
  • there is an aes-cbc example on the linked page – Lawrence Cherone May 30 '20 at 12:00
  • 1
    Probably the best mature and carefully analyzed algorithm is the argon2 family. Unfortunately it's not widely implemented yet. The most widely available algorithm that is pretty good is [pbkdf2](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveKey) – President James K. Polk May 30 '20 at 12:01
  • The question you already found might be old but has some recent answers, and there's not been much innovation since. Please explain why you think these answers don't solve your problem. Btw, asking for recommendations is off-topic. – Bergi May 30 '20 at 12:03

1 Answers1

1

Here is an example, to achieve simple encrypt/decrypt

// derive string key
async function deriveKey(password) {
  const algo = {
    name: 'PBKDF2',
    hash: 'SHA-256',
    salt: new TextEncoder().encode('a-unique-salt'),
    iterations: 1000
  }
  return crypto.subtle.deriveKey(
    algo,
    await crypto.subtle.importKey(
      'raw',
      new TextEncoder().encode(password),
      {
        name: algo.name
      },
      false,
      ['deriveKey']
    ),
    {
      name: 'AES-GCM',
      length: 256
    },
    false,
    ['encrypt', 'decrypt']
  )
}

// Encrypt function
async function encrypt(text, password) {
  const algo = {
    name: 'AES-GCM',
    length: 256,
    iv: crypto.getRandomValues(new Uint8Array(12))
  }
  return {
    cipherText: await crypto.subtle.encrypt(
      algo,
      await deriveKey(password),
      new TextEncoder().encode(text)
    ),
    iv: algo.iv
  }
}

// Decrypt function
async function decrypt(encrypted, password) {
  const algo = {
    name: 'AES-GCM',
    length: 256,
    iv: encrypted.iv
  }
  return new TextDecoder().decode(
    await crypto.subtle.decrypt(
      algo,
      await deriveKey(password),
      encrypted.cipherText
    )
  )
}

// example
;(async () => {
  // encrypt
  const encrypted = await encrypt('Secret text', 'password')

  // the cipher text
  console.log(
    String.fromCharCode.apply(null, new Uint8Array(encrypted.cipherText))
  )

  // decrypt it
  const decrypted = await decrypt(encrypted, 'password')
  console.log(decrypted) // Secret text
})()
nagy.zsolt.hun
  • 6,292
  • 12
  • 56
  • 95
Lawrence Cherone
  • 46,049
  • 7
  • 62
  • 106