npm package: @glorhythm/crypt
Description: Glorhythm library for encryption and decryption
Author: glorhythm
Latest version: 1.0.0-beta

# @glorhythm/crypt

A small, opinionated encryption/decryption helper for Node.js using **AES-256-GCM**.  
It derives a 256-bit key from your application secret via `@glorhythm/config-loader` and exposes a simple static API.

---

## Installation

```bash
npm install @glorhythm/crypt @glorhythm/config-loader
```

or with yarn:

```bash
yarn add @glorhythm/crypt @glorhythm/config-loader
```

> **Note:** `@glorhythm/config-loader` must be configured so that `Config("app.key")` returns a strong secret string.

---

## 🧱 Folder Structure

Expected project layout:

```
<project>/
│── src/
|   ├── configs/
|   │   └── app.ts
|   └── index.ts
├── .env
├── package.json
```

---

## How it works

- Uses the Node.js `crypto` module with:
  - Algorithm: `aes-256-gcm`
  - IV: 12 random bytes per encryption
  - Auth tag: GCM authentication tag for integrity
- Key derivation:
  - `key = sha256(Config("app.key"))`
  - Result is a 32-byte buffer used directly as the AES key
- Output format:
  - `ivHex:encryptedHex:authTagHex` (all hex-encoded, colon-separated)

This makes it easy to store encrypted values in a single string (DB column, config value, etc.).

---

## Basic Usage

```ts
import Crypt from "@glorhythm/crypt";

// Encrypt a string
const plainText = "my-secret-value";
const encrypted = Crypt.encrypt(plainText);

console.log("Encrypted:", encrypted);
// Example: 9a3f...:d823...:a1b2...

// Decrypt back
const decrypted = Crypt.decrypt(encrypted);
console.log("Decrypted:", decrypted); // "my-secret-value"
```

---

## API

### `Crypt.encrypt(data: string): string`

Encrypts a UTF-8 string and returns a single colon-separated string containing:

```text
<ivHex>:<cipherTextHex>:<authTagHex>
```

**Parameters:**

- `data` – Plain text string to encrypt.

**Returns:**

- A string that contains the IV, encrypted data, and auth tag, all hex-encoded, separated by `:`.

**Behavior:**

1. Get key via `Config("app.key")`, hashed with SHA-256.
2. Generate a random 12-byte IV with `randomBytes(12)`.
3. Create an AES-256-GCM cipher with `createCipheriv`.
4. Encrypt the data and retrieve the auth tag via `cipher.getAuthTag()`.
5. Serialize as `iv:encrypted:authTag` (hex).

---

### `Crypt.decrypt(data: string): string`

Decrypts a string produced by `Crypt.encrypt` and returns the original UTF-8 string.

**Parameters:**

- `data` – Encrypted string in the format `ivHex:encryptedHex:authTagHex`.

**Returns:**

- The decrypted UTF-8 string.

**Behavior:**

1. Split the input by `:`.
2. Decode `ivHex`, `encryptedHex`, and `authTagHex` from hex into buffers.
3. Create an AES-256-GCM decipher with the same derived key and IV.
4. Set the auth tag via `decipher.setAuthTag`.
5. Decrypt and return the plain text string.

If the ciphertext is tampered with or the key/IV/auth tag does not match, `decipher.final()` will throw.

---

## Configuration & Key Management

This library expects `@glorhythm/config-loader` to provide the encryption key:

```ts
import Config from "@glorhythm/config-loader";

const secret = Config("app.key");
```

**Recommendations:**

- Use a long, random string (at least 32+ characters) for `app.key`.
- Store it in a secure location:
  - Environment variable (`APP_KEY`)
  - Secrets manager (AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault, etc.)
- **Do not** commit the key to version control.
- Changing `app.key` will make previously encrypted data undecryptable.

---

## Example: Encrypting Data Before Saving to DB

```ts
import Crypt from "@glorhythm/crypt";
import db from "./db"; // your DB client

interface User {
  id: number;
  email: string;
  secretTokenEncrypted: string;
}

async function saveToken(userId: number, token: string) {
  const encrypted = Crypt.encrypt(token);
  await db.user.update({
    where: { id: userId },
    data: { secretTokenEncrypted: encrypted },
  });
}

async function loadToken(userId: number): Promise<string | null> {
  const user: User | null = await db.user.findUnique({ where: { id: userId } });
  if (!user?.secretTokenEncrypted) return null;

  return Crypt.decrypt(user.secretTokenEncrypted);
}
```

---

## Error Handling

Decryption can throw if:

- The input is not in the correct `iv:cipher:tag` format.
- The hex strings are invalid.
- The key differs from the one used in encryption.
- The ciphertext or auth tag has been modified.

Wrap decryption in `try/catch` if you expect potentially invalid or untrusted input:

```ts
try {
  const decrypted = Crypt.decrypt(encryptedStringFromClient);
  // use decrypted value
} catch (err) {
  console.error("Failed to decrypt:", err);
  // handle error (e.g., reject request, mark data as invalid)
}
```

---

## TypeScript

The package ships with type declarations:

- `main`: `dist/index.js`
- `types`: `dist/index.d.ts`
- `exports` configured for both CJS and ESM.

Example TS usage:

```ts
import Crypt from "@glorhythm/crypt";

const encrypted = Crypt.encrypt("hello");
const decrypted = Crypt.decrypt(encrypted);
```

---

## Security Notes

- AES-256-GCM provides both confidentiality and integrity (via auth tag).
- The strength of your encryption depends heavily on the secrecy and randomness of `app.key`.
- Do **not** reuse or log the encryption key or decrypted sensitive data.
- For very high-security use cases, consider:
  - Rotating keys and storing key metadata.
  - Using a dedicated KMS (Key Management Service).
  - Adding versioning to your encrypted payloads.

---

## License

MIT © Glorhythm