timebasedcipher
Version:
Time-based key generation and AES encryption/decryption SDK
209 lines (140 loc) • 5.93 kB
Markdown
# timebasedcipher
A lightweight, isomorphic (**Node + Browser**) library for **time-based rotating AES encryption** with optional **JWT-based key validation**.
Built with [`crypto-js`](https://www.npmjs.com/package/crypto-js), this package provides a simple interface to:
- Generate **time-based rotating AES keys**
- Encrypt and decrypt JavaScript objects or strings
- Validate JWTs before key derivation (optional)
- Works seamlessly in both **frontend** and **backend** environments
## Installation
```bash
npm install timebasedcipher crypto-js
# or
yarn add timebasedcipher crypto-js
# or
pnpm add timebasedcipher crypto-js
```
## Quick Start
```ts
import { generateKey, encrypt, decrypt } from "timebasedcipher";
const sharedSecret = "mySuperSecret";
const intervalSeconds = 60; // key changes every 60 seconds
// Step 1: Generate a rotating key
const key = generateKey(sharedSecret, intervalSeconds);
console.log("Generated Key:", key);
// Step 2: Encrypt some data
const data = { user: "Deb", role: "admin", timestamp: Date.now() };
const cipher = encrypt(data, key);
console.log("Encrypted:", cipher);
// Step 3: Decrypt it
const decrypted = decrypt(cipher, key);
console.log("Decrypted:", decrypted);
```
Works in both **Node.js** and **browser** environments.
## Function Reference
### `generateKey(sharedSecretOrJwt: string, interval: number, isJwt?: boolean): string`
Generates a **SHA-256 key** that rotates every `interval` seconds.
When `isJwt` is set to `true`, the input is treated as a **JWT string** — its `exp` claim is validated before key derivation.
| Parameter | Type | Description |
| -------------------- | --------- | --------------------------------------------------------------------------- |
| `sharedSecretOrJwt` | `string` | A pre-shared secret or a JWT string when `isJwt = true`. |
| `interval` | `number` | Time in seconds after which the key rotates. |
| `isJwt` _(optional)_ | `boolean` | When `true`, validates JWT expiry and uses the raw JWT as the secret input. |
**Returns:** `string` — a 64-character hex string (256-bit key).
**Throws:**
- `Error("Invalid JWT: missing payload part")` — malformed token
- `Error("JWT payload does not contain a valid 'exp' claim")`
- `Error("JWT is expired")`
#### Example (with JWT validation)
```ts
const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."; // your JWT
const interval = 120;
const key = generateKey(jwt, interval, true); // validates exp claim
```
### `encrypt(data: any, encryptionKeyHex: string): string`
Encrypts any serializable JavaScript object or string using **AES-256-CBC**.
| Parameter | Type | Description |
| ------------------ | -------- | ------------------------------------------------- |
| `data` | `any` | The object or value to encrypt. |
| `encryptionKeyHex` | `string` | 64-character hex string key (from `generateKey`). |
**Returns:**
A string in format:
```
cipherHex:ivHex
```
### `decrypt(cipherText: string, encryptionKeyHex: string): any`
Decrypts ciphertext previously produced by `encrypt`.
| Parameter | Type | Description |
| ------------------ | -------- | ------------------------------------------------------- |
| `cipherText` | `string` | Ciphertext in `"cipherHex:ivHex"` format. |
| `encryptionKeyHex` | `string` | 64-character hex string key (same key used to encrypt). |
**Returns:**
The decrypted JavaScript value (automatically parsed from JSON).
**Throws:**
- `Error("Invalid cipher format, expected cipherHex:ivHex")`
- `Error("Decryption produced empty string")`
- `Error("Unable to decrypt: no valid key found or invalid payload")`
## ⚙️ How It Works
- The key **rotates** every defined interval (e.g., every 60 seconds)
- Derived using:
```
SHA256(`${secretInput}:${timeSlot}`)
```
where
`timeSlot = floor(Date.now() / (interval * 1000) + 1000)`
- AES encryption uses:
- **AES-256-CBC**
- **PKCS#7 padding**
- **Random 16-byte IV**
- Encrypted output format:
```
<cipherHex>:<ivHex>
```
**Both ends** must:
- Use the **same secret** (or JWT)
- Use the **same rotation interval**
- Have **roughly synchronized clocks** (within ±1 interval)
## Handling Key Rotation Drift
If decryption fails due to slight clock drift, try nearby intervals:
```ts
const now = Date.now();
const interval = 60;
const secret = "mySuperSecret";
const currentKey = generateKey(secret, interval);
const prevKey = generateKey(secret, interval); // simulate earlier slot
const nextKey = generateKey(secret, interval); // simulate next slot
try {
return decrypt(cipher, currentKey);
} catch {
try {
return decrypt(cipher, prevKey);
} catch {
return decrypt(cipher, nextKey);
}
}
```
## Browser + Node Compatibility
| Environment | Supported | Notes |
| --------------- | --------- | ------------------------------------------ |
| Node.js | ✅ | Uses `crypto-js`, no native crypto needed |
| React / Browser | ✅ | Fully supported |
| Next.js | ✅ | Works in both server and client components |
## Security Notes
- AES-256-CBC provides **confidentiality**, not integrity — consider adding an **HMAC** if you need tamper detection.
- Avoid embedding secrets in frontend code — end users can access them.
- JWT mode only validates the `exp` claim.
## Requirements
- Node.js ≥ 14 or any modern browser
- `crypto-js` dependency (installed automatically)
## License
MIT License © 2025 [Deb Kalyan Mohanty](https://github.com/debkalyanmohanty)