json-seal 0.9.1 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +68 -163
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -1,77 +1,65 @@
1
- # **jsonseal**
2
- ![npm version](https://img.shields.io/npm/v/json-seal)
3
- ![license](https://img.shields.io/npm/l/json-seal)
4
- ![types](https://img.shields.io/badge/types-TypeScript-blue)
5
- ![bundle size](https://img.shields.io/bundlephobia/minzip/json-seal)
6
- ![crypto](https://img.shields.io/badge/crypto-RSA--PSS-green)
7
- ![npm downloads](https://img.shields.io/npm/dm/json-seal)
1
+ <h1 align="center">json-seal</h1>
8
2
 
9
- Cryptographically signed, tamper‑proof JSON backups for apps - zero dependencies and a tiny footprint under 5 kB.
3
+ <p align="center">
4
+ <img src="https://github.com/cmyers/json-seal/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI" />
5
+ <img src="https://img.shields.io/npm/v/json-seal" alt="npm version" />
6
+ <img src="https://img.shields.io/badge/Deterministic%20JSON-RFC%208785%20Compliant-success" alt="Deterministic JSON" />
7
+ <img src="https://img.shields.io/badge/dependencies-0-brightgreen" alt="dependencies" />
8
+ <img src="https://img.shields.io/badge/types-TypeScript-blue" alt="types" />
9
+ <img src="https://img.shields.io/bundlephobia/minzip/json-seal" alt="bundle size" />
10
+ <img src="https://img.shields.io/github/license/cmyers/json-seal" alt="license" />
11
+ </p>
10
12
 
11
- json‑seal lets you:
13
+ <h3 align="center">
14
+ A lightweight, zero‑dependency library for creating cryptographically signed, tamper‑proof JSON backups.
15
+ </h3>
12
16
 
13
- - Canonicalize any JSON object
14
- - Sign it with a private key
15
- - Embed the public key
16
- - Verify integrity later
17
- - Detect any tampering - even a single character
18
-
19
- It’s like JWS, but for **arbitrary JSON documents**, without JWT complexity, and designed for **offline‑first apps**, **local backups**, and **portable integrity checks**.
20
-
21
- ---
22
-
23
- ## **Why json‑seal exists**
24
-
25
- Most security libraries focus on:
17
+ ## **Why json‑seal**
26
18
 
27
- - encrypted blobs (ironwebcrypto)
28
- - authentication tokens (JOSE/JWS/JWT)
29
- - low‑level primitives (WebCrypto, libsodium)
19
+ Apps often need to store or transmit JSON in a way that guarantees it hasn’t been tampered with — without relying on servers, tokens, or opaque binary formats. Most security libraries focus on encrypted blobs, authentication tokens, or lowlevel crypto primitives, but none solve the simple problem:
30
20
 
31
- None of these solves the problem of:
21
+ **“I need to store JSON in a way that guarantees integrity — while keeping it readable, portable, and framework‑agnostic.”**
32
22
 
33
- **“I need to store or transmit JSON in a way that guarantees it hasn’t been tampered with - while keeping it readable, portable, and framework‑agnostic.”**
23
+ json‑seal fills that gap. It lets you:
34
24
 
35
- json‑seal fills that gap.
25
+ - Canonicalize any JSON value
26
+ - Sign it with a private key
27
+ - Embed the public key
28
+ - Verify integrity later
29
+ - Detect any tampering — even a single character
36
30
 
37
- It turns any JSON object into a **sealed artifact** that can be verified anywhere, on any device, without servers, shared secrets, or opaque binary formats. The result is a portable, human‑readable, cryptographically signed JSON document that remains trustworthy for years.
31
+ It’s like JWS, but for **arbitrary JSON documents**, without JWT complexity, and designed for **offline‑first apps**, **local backups**, and **portable integrity checks**. It turns any JSON value into a **portable, human‑readable, cryptographically signed artifact** that can be verified anywhere, on any device, with no external dependencies.
38
32
 
39
33
  ---
40
34
 
41
35
  ## **Features**
42
36
 
43
- ### **Deterministic canonicalization**
44
- Stable, cross‑platform byte representation of JSON for reliable signatures.
45
- If the JSON changes - even whitespace - verification fails.
37
+ ### **RFC 8785 Canonical JSON**
38
+ Deterministic, cross‑runtime canonicalization:
46
39
 
47
- ### **RSA‑PSS digital signatures**
48
- Modern, secure, asymmetric signing using the WebCrypto API.
49
- No shared passwords, no symmetric secrets, no server dependency.
40
+ - sorted keys
41
+ - strict number formatting
42
+ - ECMAScript string escaping
43
+ - duplicate‑key rejection
44
+ - stable UTF‑8 output
50
45
 
51
- ### **Pure JSON seal format**
52
- Human‑readable, portable, and easy to store, sync, export, or transmit.
53
- Everything needed for verification is embedded.
46
+ ### **RSA‑PSS Signatures**
47
+ Modern asymmetric signing using WebCrypto.
48
+ No shared secrets. No servers. No dependencies.
54
49
 
55
- ### **Browser + Node support**
56
- Works anywhere `crypto.subtle` is available - modern browsers, PWAs, Node 18+, Bun, Deno, and edge runtimes.
50
+ ### **Portable JSON Backup Format**
51
+ Everything needed for verification is embedded:
57
52
 
58
- ### **Framework‑agnostic**
59
- Angular, React, Vue, Svelte, Ionic, Capacitor, PWAs, Node, Bun, Deno - json‑seal fits everywhere.
53
+ - payload
54
+ - timestamp
55
+ - signature
56
+ - public key
60
57
 
61
- ### **Zero dependencies**
62
- Small, auditable, and safe for long‑term use.
63
- No polyfills, no crypto libraries, no runtime baggage.
58
+ ### **Works Everywhere**
59
+ Browsers, PWAs, Node 18+, Bun, Deno, and mobile runtimes.
64
60
 
65
- ### **Perfect for offline‑first apps**
66
- Protects:
67
-
68
- - Local storage
69
- - IndexedDB
70
- - Sync engines
71
- - User‑exported backups
72
- - Cross‑device data portability
73
-
74
- json‑seal is built for apps that need **trustworthy, tamper‑proof JSON**, not tokens or encrypted blobs.
61
+ ### **Zero Dependencies**
62
+ Small, auditable, and safe for long‑term use.
75
63
 
76
64
  ---
77
65
 
@@ -117,7 +105,7 @@ if (result.valid) {
117
105
 
118
106
  ---
119
107
 
120
- ## **What a signed backup looks like**
108
+ ## **Example Backup**
121
109
 
122
110
  ```json
123
111
  {
@@ -132,13 +120,11 @@ if (result.valid) {
132
120
  }
133
121
  ```
134
122
 
135
- Everything needed to verify the backup is embedded.
136
-
137
123
  ---
138
124
 
139
125
  ## **Tamper Detection**
140
126
 
141
- Any modification - even deep inside nested objects - invalidates the signature.
127
+ Any modification even deep inside nested objects invalidates the signature.
142
128
 
143
129
  ```ts
144
130
  const tampered = { ...backup, payload: { id: 1, data: "hacked" } };
@@ -148,123 +134,44 @@ verifyBackup(tampered).valid; // false
148
134
 
149
135
  ---
150
136
 
151
- ## **Key Management**
152
-
153
- `generateKeyPair()` should be called **once**, not on every backup.
154
- Apps are expected to generate or receive a keypair during onboarding and store it securely.
155
-
156
- ---
157
-
158
- ### **App‑generated keys**
159
-
160
- Most offline‑first apps generate a keypair on first launch:
161
-
162
- ```ts
163
- import { generateKeyPair } from "json-seal";
164
-
165
- const { privateKey, publicKey } = await generateKeyPair();
166
-
167
- secureStore.set("privateKey", privateKey);
168
- secureStore.set("publicKey", publicKey);
169
- ```
170
-
171
- On subsequent runs:
172
-
173
- ```ts
174
- const privateKey = secureStore.get("privateKey");
175
- const publicKey = secureStore.get("publicKey");
176
-
177
- const backup = await signPayload(data, privateKey, publicKey);
178
- ```
179
-
180
- ---
181
-
182
- ### **Where to store keys**
183
-
184
- Storage depends on the platform:
185
-
186
- - iOS → Keychain
187
- - Android → Keystore
188
- - Web → IndexedDB + WebCrypto
189
- - Desktop → OS keyring or encrypted local file
190
- - Node → environment variables or encrypted file
191
-
192
- json‑seal intentionally does **not** handle storage so it can remain environment‑agnostic.
193
-
194
- ---
195
-
196
- ### **Server‑generated keys**
197
-
198
- Some architectures prefer the backend to generate and manage keys:
199
-
200
- 1. Server generates keypair
201
- 2. Server stores private key
202
- 3. Server sends public key to the app
203
- 4. App signs backups using the server’s public key
204
- 5. Server verifies integrity later
205
-
206
- Useful for multi‑device accounts or enterprise systems.
207
-
208
- ---
209
-
210
- ### **Key rotation**
211
-
212
- json‑seal embeds the public key inside each backup, so old backups remain verifiable even after rotation.
213
-
214
- Typical strategy:
215
-
216
- - generate a new keypair yearly
217
- - store the new private key
218
- - keep old public keys for verification
219
- - continue verifying old backups without breaking anything
220
-
221
- ---
137
+ ## **API**
222
138
 
223
- ### **Importing and exporting keys**
139
+ ### **`generateKeyPair()`**
140
+ Generates a 2048‑bit RSA‑PSS keypair.
224
141
 
225
- Keys are standard PEM strings, so they can be:
142
+ ### **`signPayload(payload, privateKey, publicKey)`**
143
+ Canonicalizes the payload, signs it, and returns a sealed backup object.
226
144
 
227
- - backed up
228
- - migrated
229
- - synced
230
- - exported/imported
145
+ ### **`verifyBackup(backup)`**
146
+ Verifies the signature and returns `{ valid, payload? }`.
231
147
 
232
- Perfect for long‑term, portable backup formats.
148
+ ### **`canonicalize(value)`**
149
+ Full RFC 8785 Canonical JSON implementation.
233
150
 
234
151
  ---
235
152
 
236
- ## **API**
237
-
238
- ### **`generateKeyPair()`**
239
- Generates a 2048‑bit RSA‑PSS keypair using WebCrypto.
153
+ ## **Prior Art**
240
154
 
241
- ### **`signPayload(payload, privateKey, publicKey)`**
242
- - Canonicalizes the JSON
243
- - Signs it using RSA‑PSS SHA‑256
244
- - Embeds the public key
245
- - Returns a portable backup object
155
+ json‑seal builds on ideas from:
246
156
 
247
- ### **`verifyBackup(backup)`**
248
- - Re‑canonicalizes the payload
249
- - Verifies the signature
250
- - Returns `{ valid: boolean, payload?: any }`
157
+ - **json-canonicalize** — RFC 8785 canonicalization (no signing or backup format)
158
+ - **rfc8785 (Python)** — pure Python canonicalizer
159
+ - **jcs (Elixir)** — Elixir implementation of JCS
160
+ - **JOSE / JWS / JWT** — signing standards focused on tokens, not arbitrary JSON
251
161
 
252
- ### **`canonicalize(obj)`**
253
- Deterministic JSON serializer with sorted keys.
162
+ json‑seal combines **canonicalization + signing + verification** into a single, zero‑dependency library designed for **offline‑first, portable JSON integrity**.
254
163
 
255
164
  ---
256
165
 
257
166
  ## **Testing**
258
167
 
259
- json‑seal ships with a full Vitest suite covering:
168
+ The test suite covers:
260
169
 
170
+ - RFC 8785 canonicalization
171
+ - Unicode and number edge cases
261
172
  - Valid signatures
262
- - Shallow tampering
263
- - Deep tampering
264
- - Missing signature
265
- - Wrong public key
266
- - Corrupted signature
267
- - Canonicalization stability
173
+ - Shallow and deep tampering
174
+ - Missing / wrong / corrupted signatures
268
175
  - Large payloads
269
176
  - Arrays and primitives
270
177
  - RSA‑PSS non‑determinism
@@ -276,11 +183,9 @@ npm test
276
183
  ```
277
184
 
278
185
  ---
279
-
280
- Pull Requests welcome.
281
-
186
+ Pull Requests are welcome.
282
187
  ## **License**
283
188
 
284
189
  MIT
285
190
 
286
- ---
191
+ ---
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "json-seal",
3
- "version": "0.9.1",
4
- "description": "Cryptographically signed, tamper‑proof JSON backups with deterministic canonicalization and RSA‑PSS signatures.",
3
+ "version": "0.10.1",
4
+ "description": "Create cryptographically signed, tamper‑proof JSON backups with zero dependencies.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.js",