# @jscrypto/hkdf
[![CI](https://github.com/emn178/jscrypto-hkdf/actions/workflows/ci.yml/badge.svg)](https://github.com/emn178/jscrypto-hkdf/actions/workflows/ci.yml)
[![Coverage Status](https://coveralls.io/repos/emn178/jscrypto-hkdf/badge.svg?branch=main)](https://coveralls.io/r/emn178/jscrypto-hkdf?branch=main)
[![NPM](https://img.shields.io/npm/v/@jscrypto/hkdf)](https://www.npmjs.com/package/@jscrypto/hkdf)
[![CDNJS](https://img.shields.io/jsdelivr/npm/hm/@jscrypto/hkdf)](https://www.jsdelivr.com/package/npm/@jscrypto/hkdf)

RFC 5869 HKDF key derivation components for [`@jscrypto/core`](https://www.npmjs.com/package/@jscrypto/core).

HKDF is **not** a password hashing function. Use PBKDF2, scrypt, Argon2id, or similar KDFs for passwords. HKDF derives keys from existing high-entropy or protocol-provided input keying material such as a shared secret, PSK, or master secret.

This package does not include hash implementations. Register hashes through the core registry (for example from [`@jscrypto/classic/hashes`](https://www.npmjs.com/package/@jscrypto/classic)).

`hash` is required. `salt` is optional by RFC 5869, but recommended when available. `info` should bind derived output to protocol or application context.

The package exposes three RFC 5869 operation forms:

- `HKDF`: Extract + Expand
- `HKDF-Extract`: IKM + salt → PRK
- `HKDF-Expand`: PRK + info → OKM

For Expand, `input` is a PRK, not raw IKM.

## Install

```sh
npm install @jscrypto/hkdf @jscrypto/core
```

Optional classic hashes for registry use:

```sh
npm install @jscrypto/classic
```

## Quick Start

```ts
import { createRegistry } from '@jscrypto/core';
import { classicHashesPreset } from '@jscrypto/classic/hashes';
import { hkdfPreset } from '@jscrypto/hkdf';

const registry = createRegistry()
  .use(classicHashesPreset)
  .use(hkdfPreset);

const okm = registry.derive({
  name: 'HKDF',
  input: sharedSecret,
  salt,
  info: new TextEncoder().encode('example:v1'),
  hash: 'SHA256',
  length: 32,
});

const prk = registry.derive({
  name: 'HKDF-Extract',
  input: sharedSecret,
  salt,
  hash: 'SHA256',
});

const expanded = registry.derive({
  name: 'HKDF-Expand',
  input: prk,
  info: new TextEncoder().encode('example:v1'),
  hash: 'SHA256',
  length: 32,
});
```

## Direct Helpers

```ts
import { sha256 } from '@jscrypto/classic/hashes';
import { deriveHkdf, expandHkdf, extractHkdf } from '@jscrypto/hkdf';

const okm = deriveHkdf({
  input: sharedSecret,
  salt,
  info: new TextEncoder().encode('example:v1'),
  hash: sha256,
  length: 32,
});

const prk = extractHkdf({
  input: sharedSecret,
  salt,
  hash: sha256,
});

const expanded = expandHkdf({
  input: prk,
  info: new TextEncoder().encode('example:v1'),
  hash: sha256,
  length: 32,
});
```

## Derived-Key Cipher

Compose full HKDF with AES modes from `@jscrypto/classic`. GCM derives key material only and requires a unique per-operation nonce:

```ts
const cipher = registry.createDerivedKeyCipher({
  cipher: 'AES',
  mode: 'GCM',
  kdf: {
    name: 'HKDF',
    input: sharedSecret,
    salt,
    info,
    hash: 'SHA256',
  },
  keySize: 32,
});

const sealed = cipher.encrypt(plaintext, {
  nonce,
  aad,
  tagLength: 16,
});
```

Legacy non-AEAD modes such as CBC can derive an IV through the mode contract:

```ts
const cipher = registry.createDerivedKeyCipher({
  cipher: 'AES',
  mode: 'CBC',
  padding: 'Pkcs7',
  kdf: {
    name: 'HKDF',
    input: sharedSecret,
    info,
    hash: 'SHA256',
  },
  keySize: 32,
});

const ciphertext = cipher.encrypt(plaintext, { salt });
```

## Browser

Browser builds depend on `@jscrypto/core`. Load core first, then HKDF:

```html
<script src="node_modules/@jscrypto/core/dist/jscrypto-core.iife.min.js"></script>
<script src="node_modules/@jscrypto/hkdf/dist/jscrypto-hkdf.iife.min.js"></script>
<script>
  const registry = jscryptoCore.createRegistry();
  registry.use(jscryptoHkdf.hkdfPreset);
</script>
```

Package export paths:

- `@jscrypto/hkdf/browser`
- `@jscrypto/hkdf/umd`

CommonJS users can require the regular UMD `.js` builds because this package uses explicit `.mjs` and `.cjs` entry files instead of package-level `"type": "module"`.

## License

MIT
