0

I have some code in JavaScript running inside the browser using WebCrypto. It uses AES-GCM to encrypt some data.

I generate a key and then export it to hex, then generate an initialization vector, and then use that to encode some data which I convert to hex as well.

function generateKey() {
    window.crypto.subtle
      .generateKey(
        {
          name: "AES-GCM",
          length: 256,
        },
        true,
    ["encrypt", "decrypt"],
      )
      .then(key => {
        log('Got key back');
        setKey(key);
      })
  }

  // Export the given key and write it into the "exported-key" space.                                       
  async function exportCryptoKey(key) {
    const exported = await window.crypto.subtle.exportKey("raw", key);
    const exportedKeyBuffer = new Uint8Array(exported);
    hex = buf2hex(exportedKeyBuffer);
    log('HEX is', hex );
  }

  function buf2hex(buffer) { // buffer is an ArrayBuffer                                                    
    return [...new Uint8Array(buffer)]
      .map(x => x.toString(16).padStart(2, '0'))
      .join('');
  }
                                 
  function setKey(_key) {
    log('Key is', key);
    key = _key;
    if (key.extractable) {
      exportCryptoKey(key);
    } else {
      log('Cannot export key, sadface');
    }
  }

  async function encrypt() {
    log('Key is', key );
    const iv = window.crypto.getRandomValues(new Uint8Array(16));
    let enc = new TextEncoder();
    const data = enc.encode('Hi there');
    const result = await window.crypto.subtle.encrypt(
      {
        name: "AES-GCM",
        iv,
      },
      key,
      data
    );
    encrypted = new Uint8Array(result);
    log('Encrypted is', buf2hex(encrypted));
    log('IV is', buf2hex(iv));
  }

I want to decode it using python. I see some python examples use a nonce with some of the data plucked out. Is that the same as the initialization vector used in the JavaScript? What is wrong? When I run the python code, I get Decryption failed

# pip install pycryptodome
from Crypto.Cipher import AES

hex_key = '0db43c44650a8b71547dbc6951432f0c545509814d6bca1a10e387ad2d16bc4a'
encrypted_message = '37183efb9e4ce92265d1144110cdbe1e768a85f9630bf872'
iv = '43005ad454f6e3c6e3ead90f7137f007'

key = bytes.fromhex(hex_key)
data = bytes.fromhex(encrypted_message)
nonce = bytes.fromhex(iv)
cipher = AES.new(key, AES.MODE_GCM, nonce)
try:
    dec = cipher.decrypt_and_verify(data, nonce) # ciphertext, tag                                          
    print(dec)
except ValueError:
    print("Decryption failed")
4
  • 2
    You are using the IV/nonce as authentication tag, which is wrong. WebCrypto implicitly concatenates ciphertext and tag, while both are processed separately in PyCryptodome, so that a separation is required on the Python side: cipher.decrypt_and_verify(data[:-16], data[-16:]). Note that for GCM a 12 bytes IV/nonce is recommended. Commented May 13, 2024 at 13:15
  • I thought that the IV was public and required. I don't see how I use that in the python code. If I just use the data slice, is it provided somehow implicitly? Commented May 13, 2024 at 14:41
  • 1
    Your last comment is not clear to me. You may be confusing IV (sometimes called nonce) and authentication tag. The IV/nonce is required when instantiating the cipher object: cipher = AES.new(key, AES.MODE_GCM, nonce) The tag is required during authentication: dec = cipher.decrypt_and_verify(data[:-16], data[-16:]). Neither IV/nonce nor tag are secret. Commented May 13, 2024 at 15:33
  • 1
    IV/nonce and tag are two completely different things with different purposes: The IV/nonce is specified by the user for the purpose of semantic security, the tag is generated automatically during encryption and is required for authentication during decryption. Your problem is caused by the different handling of the tag in both libraries. WebCrypto automatically concatenates ciphertext and tag, while PyCryptodome processes both separately. Commented May 13, 2024 at 15:38

1 Answer 1

0

As @Topaco suggested, just need a slight adjustment of the python code to use the IV/nonce correctly.

% cat decrypt.py
from Crypto.Cipher import AES

hex_key = '0db43c44650a8b71547dbc6951432f0c545509814d6bca1a10e387ad2d16bc4a';
encrypted_message = '37183efb9e4ce92265d1144110cdbe1e768a85f9630bf872';
iv = '43005ad454f6e3c6e3ead90f7137f007';

key = bytes.fromhex(hex_key)
data = bytes.fromhex(encrypted_message)
nonce = bytes.fromhex(iv)
cipher = AES.new(key, AES.MODE_GCM, nonce)
try:
    dec = cipher.decrypt_and_verify(data[:-16], data[-16:])
    print(dec)
except ValueError:
    print("Decryption failed")

% python decrypt.py
b'Hi there'

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.