Skip to content

crypto.subtle.deriveKey different length arg in deno and browser #16180

@petuhovskiy

Description

@petuhovskiy

Describe the bug

We were playing with https://github.com/denodrivers/postgres and at some point realized that crypto SCRAM implementation works differently in Deno and other environments (browser, cloudflare workers, etc.).

After some debugging, we narrowed the scope to the deriveKeySignatures function, which uses SubtleCrypto with PBKDF2 and HMAC/SHA-256. It turned out that specifying an optional length parameter in a single place can fix the code to work without issues across all environments:

const key = await crypto.subtle.deriveKey(
  {
    hash: "SHA-256",
    iterations,
    name: "PBKDF2",
    salt,
  },
  pbkdf2_password,
  { name: "HMAC", hash: "SHA-256" }, // should be changed to `{ name: "HMAC", hash: "SHA-256", length: 256 }`
  false,
  ["sign"],
);

https://developer.mozilla.org/en-US/docs/Web/API/HmacKeyGenParams says that:

length Optional

    A Number — the length in bits of the key. If this is omitted, the length of the key is equal to the block size of the hash function you have chosen. Unless you have a good reason to use a different length, omit this property and use the default.

It looks like the issue is that Deno implementation for default length is wrong and differs from browser engines.

This issue is probably the same as #14938

Steps to Reproduce

Code sample based on https://github.com/denodrivers/postgres/blob/8a07131efa17f4a6bcab86fd81407f149de93449/connection/scram.ts#L93

Execute this code in deno and browser:

const text_encoder = new TextEncoder();

async function deriveKeySignatures(
  password,
  salt,
  iterations,
) {
  const pbkdf2_password = await crypto.subtle.importKey(
    "raw",
    text_encoder.encode(password),
    "PBKDF2",
    false,
    ["deriveBits", "deriveKey"],
  );
  
  const key = await crypto.subtle.deriveKey(
    {
      hash: "SHA-256",
      iterations,
      name: "PBKDF2",
      salt,
    },
    pbkdf2_password,
    { name: "HMAC", hash: "SHA-256" },
    false,
    ["sign"],
  );

  return new Uint8Array(
    await crypto.subtle.sign("HMAC", key, text_encoder.encode("Client Key")),
  );
}

const password = 'pencil';
const salt = new Uint8Array(16);
const iterations = 4096;

(async () => {
  const arr = await deriveKeySignatures(password, salt, iterations);
  console.log(`Output: \n${arr}`);
})();

Output in Deno:

% deno run abc.js
Output: 
179,101,253,54,56,183,193,229,31,232,75,181,36,12,242,54,206,53,167,9,215,163,190,68,194,32,159,180,206,100,226,35

Output in browser (Firefox 105.0.1, Chrome 105):

Output: 
114,244,238,221,188,128,172,235,219,75,104,188,200,132,225,54,16,132,146,123,247,172,246,49,151,171,157,81,145,58,13,179

Expected behavior

Code in Deno and browser bundle produces the same result.

Environment

  • OS: macOS 11.4
  • deno version: 1.26.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working correctlyext/cryptorelated to the ext/crypto crate

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions