Skip to content

Commit 4fef87d

Browse files
committed
docs: add README with usage documentation
1 parent 473c545 commit 4fef87d

1 file changed

Lines changed: 244 additions & 0 deletions

File tree

README.md

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
# @boringnode/encryption
2+
3+
<div align="center">
4+
5+
[![typescript-image]][typescript-url]
6+
[![gh-workflow-image]][gh-workflow-url]
7+
[![npm-image]][npm-url]
8+
[![npm-download-image]][npm-download-url]
9+
[![license-image]][license-url]
10+
11+
</div>
12+
13+
A framework-agnostic encryption library for Node.js. Built with simplicity and security in mind, `@boringnode/encryption` provides a unified API for encrypting and signing data with support for multiple encryption algorithms and key rotation.
14+
15+
## Installation
16+
17+
```bash
18+
npm install @boringnode/encryption
19+
```
20+
21+
## Features
22+
23+
- **Multiple Algorithms**: ChaCha20-Poly1305, AES-256-GCM, AES-256-CBC
24+
- **Key Rotation**: Encrypt with new keys, decrypt with old ones
25+
- **Purpose-Bound Encryption**: Ensure encrypted values are used for their intended purpose
26+
- **Expiration Support**: Set time-to-live on encrypted values
27+
- **Message Verification**: Sign data without encrypting (HMAC-based)
28+
- **Type-Safe**: Full TypeScript support with typed payloads
29+
30+
## Quick Start
31+
32+
### 1. Configure Encryption
33+
34+
```typescript
35+
import { Encryption } from '@boringnode/encryption'
36+
import { chacha20poly1305 } from '@boringnode/encryption/drivers/chacha20_poly1305'
37+
38+
const encryption = new Encryption(
39+
chacha20poly1305({
40+
id: 'app',
41+
keys: [process.env.APP_KEY],
42+
})
43+
)
44+
```
45+
46+
### 2. Encrypt & Decrypt
47+
48+
```typescript
49+
// Encrypt any value
50+
const encrypted = encryption.encrypt({ userId: 1, role: 'admin' })
51+
// => "app.base64EncodedCipherText.base64EncodedIv.base64EncodedTag"
52+
53+
// Decrypt the value
54+
const decrypted = encryption.decrypt(encrypted)
55+
// => { userId: 1, role: 'admin' }
56+
```
57+
58+
## Supported Data Types
59+
60+
The library supports encrypting a wide range of data types:
61+
62+
- Strings
63+
- Numbers
64+
- Booleans
65+
- Arrays
66+
- Objects
67+
- Dates
68+
69+
## Encryption Drivers
70+
71+
### ChaCha20-Poly1305 (recommended)
72+
73+
Modern, fast, and secure. Recommended for most use cases.
74+
75+
```typescript
76+
import { chacha20poly1305 } from '@boringnode/encryption/drivers/chacha20_poly1305'
77+
78+
const config = chacha20poly1305({
79+
id: 'app',
80+
keys: ['your-32-character-secret-key-here'],
81+
})
82+
```
83+
84+
### AES-256-GCM
85+
86+
Industry-standard authenticated encryption.
87+
88+
```typescript
89+
import { aes256gcm } from '@boringnode/encryption/drivers/aes_256_gcm'
90+
91+
const config = aes256gcm({
92+
id: 'app',
93+
keys: ['your-32-character-secret-key-here'],
94+
})
95+
```
96+
97+
### AES-256-CBC
98+
99+
Legacy support with HMAC authentication.
100+
101+
```typescript
102+
import { aes256cbc } from '@boringnode/encryption/drivers/aes_256_cbc'
103+
104+
const config = aes256cbc({
105+
id: 'app',
106+
keys: ['your-32-character-secret-key-here'],
107+
})
108+
```
109+
110+
## Key Rotation
111+
112+
The library supports multiple keys for seamless key rotation. The first key is used for encryption, while all keys are tried during decryption.
113+
114+
```typescript
115+
const encryption = new Encryption(
116+
chacha20poly1305({
117+
id: 'app',
118+
keys: [
119+
process.env.NEW_APP_KEY, // Used for encryption
120+
process.env.OLD_APP_KEY, // Still valid for decryption
121+
],
122+
})
123+
)
124+
125+
// New encryptions use NEW_APP_KEY
126+
const encrypted = encryption.encrypt('secret')
127+
128+
// Decryption works with both keys
129+
encryption.decrypt(encryptedWithOldKey) // Works
130+
encryption.decrypt(encryptedWithNewKey) // Works
131+
```
132+
133+
## Purpose-Bound Encryption
134+
135+
Ensure encrypted values are only used for their intended purpose:
136+
137+
```typescript
138+
// Encrypt with a purpose
139+
const token = encryption.encrypt({ userId: 1 }, undefined, 'password-reset')
140+
141+
// Must provide same purpose to decrypt
142+
encryption.decrypt(token, 'password-reset') // => { userId: 1 }
143+
encryption.decrypt(token, 'email-verify') // => null
144+
encryption.decrypt(token) // => null
145+
```
146+
147+
## Expiration Support
148+
149+
Set a time-to-live on encrypted values:
150+
151+
```typescript
152+
// Expires in 1 hour
153+
const token = encryption.encrypt({ userId: 1 }, '1h')
154+
155+
// Expires in 30 minutes
156+
const token = encryption.encrypt({ userId: 1 }, '30m')
157+
158+
// Expires in 7 days
159+
const token = encryption.encrypt({ userId: 1 }, '7d')
160+
161+
// After expiration, decrypt returns null
162+
encryption.decrypt(expiredToken) // => null
163+
```
164+
165+
## Message Verifier
166+
167+
When you need to ensure data integrity without hiding the content, use the `MessageVerifier`. The payload is base64-encoded (not encrypted) and signed with HMAC.
168+
169+
```typescript
170+
import { MessageVerifier } from '@boringnode/encryption/message_verifier'
171+
172+
const verifier = new MessageVerifier(['your-secret-key'])
173+
174+
// Sign a value
175+
const signed = verifier.sign({ userId: 1 })
176+
177+
// Verify and retrieve the value
178+
const payload = verifier.unsign(signed)
179+
// => { userId: 1 }
180+
181+
// Tampered values return null
182+
verifier.unsign('tampered.value') // => null
183+
```
184+
185+
The verifier also supports purpose and expiration:
186+
187+
```typescript
188+
// With expiration
189+
const signed = verifier.sign({ userId: 1 }, '1h')
190+
191+
// With purpose
192+
const signed = verifier.sign({ userId: 1 }, undefined, 'api-token')
193+
const payload = verifier.unsign(signed, 'api-token')
194+
```
195+
196+
## Base64 Utilities
197+
198+
URL-safe base64 encoding/decoding utilities are available:
199+
200+
```typescript
201+
import { base64UrlEncode, base64UrlDecode } from '@boringnode/encryption/base64'
202+
203+
const encoded = base64UrlEncode('Hello World')
204+
const decoded = base64UrlDecode(encoded, 'utf8')
205+
```
206+
207+
## HMAC
208+
209+
Generate and verify HMAC signatures:
210+
211+
```typescript
212+
import { Hmac } from '@boringnode/encryption'
213+
214+
const hmac = new Hmac(secretKey)
215+
216+
// Generate HMAC
217+
const hash = hmac.generate('data to sign')
218+
219+
// Verify HMAC (timing-safe comparison)
220+
const isValid = hmac.compare('data to sign', hash)
221+
```
222+
223+
## Error Handling
224+
225+
The library is designed to return `null` on decryption failures rather than throwing exceptions. This prevents timing attacks and simplifies error handling:
226+
227+
```typescript
228+
const result = encryption.decrypt(maybeInvalidValue)
229+
230+
if (result === null) {
231+
// Invalid, expired, wrong purpose, or tampered
232+
}
233+
```
234+
235+
[gh-workflow-image]: https://img.shields.io/github/actions/workflow/status/boringnode/encryption/checks.yml?branch=main&style=for-the-badge
236+
[gh-workflow-url]: https://github.com/boringnode/encryption/actions/workflows/checks.yml
237+
[npm-image]: https://img.shields.io/npm/v/@boringnode/encryption.svg?style=for-the-badge&logo=npm
238+
[npm-url]: https://www.npmjs.com/package/@boringnode/encryption
239+
[npm-download-image]: https://img.shields.io/npm/dm/@boringnode/encryption?style=for-the-badge
240+
[npm-download-url]: https://www.npmjs.com/package/@boringnode/encryption
241+
[typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge&logo=typescript
242+
[typescript-url]: https://www.typescriptlang.org
243+
[license-image]: https://img.shields.io/npm/l/@boringnode/encryption?color=blueviolet&style=for-the-badge
244+
[license-url]: LICENSE.md

0 commit comments

Comments
 (0)