# crypto-shelf

A lightweight and secure Node.js library collection for password hashing and comparison, HMAC-based signature generation and verification, and symmetric encryption and decryption using modern authenticated encryption algorithms powered by Node's built-in `crypto` module.

## Table of Contents
**[Installation](#installation)**<br>
**[crypto-shelf/authentication](#crypto-shelfauthentication)**<br>
**[crypto-shelf/hash](#crypto-shelfhash)**<br>
**[crypto-shelf/signature](#crypto-shelfsignature)**<br>
**[crypto-shelf/symmetric](#crypto-shelfsymmetric)**<br>

## Installation

```bash
npm i crypto-shelf
```

&nbsp;

## crypto-shelf/authentication

Provides secure password hashing and verification using Node's native `crypto` module.

### Features

- Password hashing with `scrypt`
- Configurable salt and key lengths
- Default `base64url` encoding
- Timing-safe password comparison
- No external dependencies

### Usage

#### Import

```js
import { hashPassword, comparePassword } from 'crypto-shelf/authentication';
```

#### Hash a Password

```js
const hash = await hashPassword('SomePassword!1!11');
console.log(hash); // base64url string
```

#### Compare a Password

```js
const isCorrect = await comparePassword('SomePassword!1!11', hash);
console.log(isCorrect); // true or false
```

### Configuration

| Option       | Type     | Default      | Description                                   |
|--------------|----------|--------------|-----------------------------------------------|
| keyLength    | number   | 48           | Length of the derived key (1–255)             |
| saltLength   | number   | 16           | Length of the salt (1–255)                    |
| encoding     | string   | 'base64url'  | Output encoding (e.g., 'hex', 'base64')       |
&nbsp;

Global defaults can be adjusted via the `defaults` object:

```js
import { defaults } from 'crypto-shelf/authentication';

defaults.keyLength = 42;
defaults.saltLength = 13;
defaults.encoding = 'base64';
```

Or override per call:

```js
const keyLength = 32;
const saltLength = 12;
const encoding = 'hex';

const hash = await hashPassword('SomePassword!1!11', { keyLength, saltLength, encoding});
const isValid = await comparePassword('SomePassword!1!11', hash, { encoding });
```

### How It Works

- Generates a random salt
- Derives a key with `scrypt`
- Combines salt length, key length, salt, and key into a single encoded string
- Comparison re-derives the key and uses `timingSafeEqual` for verification

### Security

- Relies on `scrypt` for password hashing
- Mitigates timing attacks with `timingSafeEqual`
- Secure defaults with customizable options


&nbsp;

## crypto-shelf/hash

Provides hashing using Node's native `crypto` module.

### Features

- Supports common hash algorithms: `md5`, `sha1`, `sha256`, `sha512`, etc. with short cut functions
- Customizable output encodings in `hex`, `base64`, `base64url`, or raw `Buffer` (Default `hex`)

### Usage

#### Import

```js
import hash, { md5, sha256 } from 'crypto-shelf/hash';
```

#### Use shortcut functions

```js
const hash = md5('Lorem Ipsum');
console.log(hash); // hex
```

#### Use shortcut functions with custom output encoding

```js
const hash = sha256('Lorem Ipsum', { encoding: 'base64' });
console.log(hash); // base64
```

#### Use general hash function with or without custom output encoding

```js
hash('shake256', 'Lorem Ipsum'); // hex
hash('sha3-384', 'Lorem Ipsum', { encoding: 'base64url' }); // base64url
hash('RIPEMD160', 'Lorem Ipsum', { encoding: null }); // Returns a buffer if encoding is null
```

### How It Works

- Generates hashes based on node's `createHash`

### Supported Algorithms

Compatible with all algorithms listed by `crypto.getHashes()`. Common ones include:

- `md5`
- `sha1`, `sha224`, `sha256`, `sha384`, `sha512`, `sha512-224`, `sha512-256`
- `sha3-224`, `sha3-256`, `sha3-384`, `sha3-512`
- `shake128`, `shake256`
- `blake2b512`, `blake2s256`
- `sm3`


&nbsp;

## crypto-shelf/signature

Generates and verifies HMAC-based signatures using Node’s `crypto` module.

### Features

- Secret key generation with customizable algorithm and encoding
- HMAC signature creation and verification
- Constant-time comparison using Buffers
- Easy global or per-call configuration

### Usage

#### Import

```js
import { generateSecret, createSignature, verifySignature } from 'crypto-shelf/signature';
```

#### Generate a Secret

Generate a new cryptographic secret key and share it with the future receiver of data + signature.

```js
const secret = await generateSecret();
console.log(secret); // base64 string
```

#### Create a Signature

Creates an HMAC signature.

```js
const data = 'e.g. some JSON payload to send via webhook';
const signature = createSignature(secret, data);
console.log(signature); // base64 string

// Send signature + data to the receiver
```

#### Verify received Data + Signature

Verifies a given signature by recreating it and comparing.

```js
const isValid = verifySignature(secret, signature, data);
console.log('Data valid:', isValid);
```

### Configuration

| Option     | Type     | Default      | Description                                   |
|------------|----------|--------------|-----------------------------------------------|
| algorithm  | string   | 'sha256'     | HMAC algorithm (e.g., 'sha384')               |
| keyLength  | number   | null         | Derived key length (defaults to algorithm safe value) |
| encoding   | string   | 'base64url'  | Output encoding                               |
&nbsp;

Global configuration via `defaults`:

```js
import { defaults } from 'crypto-shelf/signature';

defaults.algorithm = 'md5';
defaults.keyLength = 512;
defaults.encoding = 'hex';
```

Or per call:

```js
const algorithm = 'sha512';
const keyLength = 1024;
const encoding = 'base64url';

const secret = await generateSecret({ algorithm, keyLength, encoding });
const signature = createSignature(secret, data, { algorithm, encoding });
const isValid = verifySignature(secret, signature, data, { algorithm, encoding });
```

### How It Works

- Secret generated via `generateKey`
- HMAC created with `createHmac`
- Signature verified by re-creating and comparing to original

### Security

- Uses native `generateKey` and `createHmac`
- Constant-time comparison prevents signature leakage
- Customizable with secure defaults


&nbsp;

## crypto-shelf/symmetric

A minimalistic API for symmetric encryption and decryption supporting strong modern authenticated encryption algorithms like `ChaCha20-Poly1305`, `AES-*-GCM` and `AES-*-OCB`.

### Features

- Support for almost all available encryption ciphers
- Secure key derivation with `scrypt`
- AAD support for additional integrity checks
- Encoded output with `base64url` by default
- Easy to use with sane defaults

### Usage

#### Import

```js
import {
  generateSalt,
  generateKey,
  encrypt,
  decrypt,
} from 'crypto-shelf/symmetric';
```

#### Encrypt and Decrypt Data

If the passphrase/password comes from a user, generate a salt and save only the salt per user (e. g. a user vault). If it is a system password saved e. g. in a .env file, salt can be omitted if password is strong enough (have a high entropy).

```js
const passphrase = 'strong password';
// const salt = generateSalt(); // optional

// The actual key for the encryption and decryption needs to be generated from the passphrase
const key = await generateKey(passphrase /*, salt */);

const plaintext = 'Hello world!';
const encrypted = encrypt(key, plaintext);
const decrypted = decrypt(key, encrypted);

console.log({ encrypted, decrypted });
```

### Configuration

| Option     | Type     | Default        | Description                                  |
|------------|----------|----------------|----------------------------------------------|
| algorithm  | string   | 'aes-256-gcm'  | Encryption algorithm to use                  |
| encoding   | string   | 'base64url'    | Output encoding                              |
| aad        | string   | null           | Additional Authenticated Data                |
&nbsp;

Adjust global defaults:

```js
import { defaults } from 'crypto-shelf/symmetric';

defaults.algorithm = 'chacha20-poly1305';
defaults.encoding = 'base64';
defaults.aad = 'foobar';
```

Or configure per call:

```js
const algorithm = 'aes-192-gcm';
const encoding = 'hex';
const aad = 'Additional authentication data';

const passphrase = 'SomeVeryLongPassphrase!1!11';
const plaintext = 'Sensitive data the needs to be stored encrypted';

const salt = generateSalt({ encoding, saltLength: 32 }); // saltLength = 32 by default
const key = await generateKey(passphrase, salt, { algorithm, encoding });
const encrypted = encrypt(key, plaintext, { algorithm, encoding, aad });
const decrypted = decrypt(key, encrypted, { algorithm, encoding, aad });
```

### How It Works

- A salt is generated using `randomBytes`.
- `scrypt` is used to derive a secure key from the passphrase/password and salt.
- `createCipheriv` is used to create encryption data.
- The output includes:
  - Initialization vector (If supported from the algorithm)
  - The encrypted data
  - Authentication Tag (If supported from the algorithm)
- When decrypted, the initialization vector and auth tag are extracted from the encryption data.
- `createDecipheriv` is used to decrypt the encrypted data.

### Recommended Algorithms

- ChaCha20-Poly1305
- AES-*-GCM (AES-256-GCM default)
- AES-*-OCB

Verify Node.js version compatibility for algorithm support.

### Security Notes

- Use strong passphrases and unique salts when possible
- AAD must be the same for encryption and decryption
- Output includes IV, auth tag, and encrypted data

### Security

- Uses core `createCipheriv`/`createDecipheriv`
- Supports authenticated encryption modes
- Secure out of the box with override flexibility
