Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Node compat error: Uncaught (in promise) TypeError: IV length must be 12 bytes #27441

Open
tarasglek opened this issue Dec 21, 2024 · 0 comments

Comments

@tarasglek
Copy link

tarasglek commented Dec 21, 2024

  • Node.js version: 20.0.0
  • Deno version: 2.1.4

I am trying to use deno to decrypt a sops file in humphd/sops-age#4

I encountered an issue when running a script that uses the createDecipheriv function with AES-256-GCM in both Node.js and Deno. The script works as expected in Node.js but throws an error in Deno due to Deno being stricter re IV length.

I got into this problem because go is also ok with non-standard IV lengths and https://github.com/getsops/sops uses them: https://github.com/getsops/sops/blob/73fadcf6b49006b0b77ba811f05eae8d740ed511/aes/cipher.go#L92C2-L92C75

Steps to Reproduce:

  1. Use the following TypeScript code:

    import { createDecipheriv } from "crypto";
    
    const uintDecryptionKey = new Uint8Array([
      0x79, 0x82, 0xC4, 0x88, 0xB1, 0x50, 0x9E, 0x98,
      0xD8, 0x92, 0xC5, 0x93, 0x88, 0xAA, 0x70, 0xBF,
      0x6B, 0x0A, 0x87, 0x0F, 0x96, 0x25, 0xBE, 0x45,
      0xA3, 0xF6, 0x98, 0xD9, 0x8A, 0x97, 0xB3, 0x07
    ]);
    const eValue = "ENC[AES256_GCM,data:s0/KBsFec29XLrGbAnLiNA==,iv:k5oP3kb8tTbZaL3PxbFWN8ToOb8vfv2b1EuPz1LbmYU=,tag:n1RlTSRKGgoB909D4I3n+A==,type:str]"
    const decryptionKey = Buffer.from(uintDecryptionKey);
    const match = eValue.match(
      /^ENC\[AES256_GCM,data:(.+),iv:(.+),tag:(.+),type:(.+)\]/,
    );
    if (match) {
      const [, encValue, ivBase64, tagBase64, dataType] = match;
      console.log(dataType);
      if (!encValue || !ivBase64 || !tagBase64) {
        throw new Error("Invalid ENC format");
      }
    
      const iv = Buffer.from(ivBase64, "base64");
      const tag = Buffer.from(tagBase64, "base64");
      console.log(`iv length: ${iv.length}, value: ${iv.toString("base64")}`);
      const decipher = createDecipheriv("aes-256-gcm", decryptionKey, iv);
      decipher.setAuthTag(tag);
      decipher.setAAD(Buffer.from(""));
      const decrypted = decipher.update(encValue, "base64", "utf8");
      console.log(decrypted);
    }
  2. Use the following import_map.json:

    {
      "imports": {
        "crypto": "node:crypto"
      }
    }
  3. Run the script in Node.js and Deno:

    • Node.js output:

      str
      iv length: 32, value: k5oP3kb8tTbZaL3PxbFWN8ToOb8vfv2b1EuPz1LbmYU=
      this is a secret
      
    • Deno output:

      deno run --import-map=import_map.json --unstable-node-globals src/reduced.ts 
      str
      iv length: 32, value: k5oP3kb8tTbZaL3PxbFWN8ToOb8vfv2b1EuPz1LbmYU=
      error: Uncaught (in promise) TypeError: IV length must be 12 bytes
          at new Decipheriv (ext:deno_node/internal/crypto/cipher.ts:150:21)
          at createDecipheriv (node:crypto:32:10)
          at file:///Users/taras/Documents/sops-age/src/reduced.ts:29:20
      

Expected Behavior:

The script should run without errors in both Node.js and Deno, producing the same output.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant