@lindorm/aes 0.5.4 → 0.6.0
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.
- package/CHANGELOG.md +30 -0
- package/MERMAID.md +155 -0
- package/README.md +365 -199
- package/__tests__/INTEROP-RESULTS.md +66 -0
- package/__tests__/esm-smoke.test.ts +15 -0
- package/__tests__/fixtures/keys.ts +60 -0
- package/__tests__/helpers/buffer-utils.ts +11 -0
- package/__tests__/helpers/index.ts +2 -0
- package/__tests__/helpers/jwe-adapter.ts +117 -0
- package/__tests__/jose-jwe.test.ts +463 -0
- package/__tests__/noble-ciphers.test.ts +208 -0
- package/dist/classes/AesKit.d.ts +10 -8
- package/dist/classes/AesKit.d.ts.map +1 -1
- package/dist/classes/AesKit.js +73 -34
- package/dist/classes/AesKit.js.map +1 -1
- package/dist/constants/private/index.d.ts +0 -1
- package/dist/constants/private/index.d.ts.map +1 -1
- package/dist/constants/private/index.js +0 -1
- package/dist/constants/private/index.js.map +1 -1
- package/dist/constants/private/version.d.ts +3 -1
- package/dist/constants/private/version.d.ts.map +1 -1
- package/dist/constants/private/version.js +4 -2
- package/dist/constants/private/version.js.map +1 -1
- package/dist/interfaces/AesKit.d.ts +12 -7
- package/dist/interfaces/AesKit.d.ts.map +1 -1
- package/dist/mocks/mock-aes-kit.d.ts.map +1 -1
- package/dist/mocks/mock-aes-kit.js +12 -2
- package/dist/mocks/mock-aes-kit.js.map +1 -1
- package/dist/types/aes-decryption-data.d.ts +26 -17
- package/dist/types/aes-decryption-data.d.ts.map +1 -1
- package/dist/types/aes-encryption-data.d.ts +7 -17
- package/dist/types/aes-encryption-data.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/private/aes-data.d.ts.map +1 -1
- package/dist/types/private/aes-key-derivation.d.ts +1 -1
- package/dist/types/private/aes-key-derivation.d.ts.map +1 -1
- package/dist/types/private/auth-tag.d.ts +6 -3
- package/dist/types/private/auth-tag.d.ts.map +1 -1
- package/dist/types/private/content-encryption-key.d.ts +4 -2
- package/dist/types/private/content-encryption-key.d.ts.map +1 -1
- package/dist/types/private/index.d.ts +1 -1
- package/dist/types/private/index.d.ts.map +1 -1
- package/dist/types/private/index.js +1 -1
- package/dist/types/private/index.js.map +1 -1
- package/dist/types/private/prepared-encryption.d.ts +35 -0
- package/dist/types/private/prepared-encryption.d.ts.map +1 -0
- package/dist/types/private/{aes-string.js → prepared-encryption.js} +1 -1
- package/dist/types/private/prepared-encryption.js.map +1 -0
- package/dist/utils/is-aes.d.ts.map +1 -1
- package/dist/utils/is-aes.js +1 -5
- package/dist/utils/is-aes.js.map +1 -1
- package/dist/utils/parse-aes.js +3 -3
- package/dist/utils/parse-aes.js.map +1 -1
- package/dist/utils/private/aes-header.d.ts +42 -0
- package/dist/utils/private/aes-header.d.ts.map +1 -0
- package/dist/utils/private/aes-header.js +75 -0
- package/dist/utils/private/aes-header.js.map +1 -0
- package/dist/utils/private/calculate/calculate-content-encryption-key-size.js +3 -3
- package/dist/utils/private/calculate/calculate-key-wrap-encryption.d.ts.map +1 -1
- package/dist/utils/private/calculate/calculate-key-wrap-encryption.js +2 -1
- package/dist/utils/private/calculate/calculate-key-wrap-encryption.js.map +1 -1
- package/dist/utils/private/data/auth-tag-hmac.d.ts +2 -2
- package/dist/utils/private/data/auth-tag-hmac.d.ts.map +1 -1
- package/dist/utils/private/data/auth-tag-hmac.js +12 -4
- package/dist/utils/private/data/auth-tag-hmac.js.map +1 -1
- package/dist/utils/private/data/auth-tag.d.ts +2 -2
- package/dist/utils/private/data/auth-tag.d.ts.map +1 -1
- package/dist/utils/private/data/auth-tag.js +4 -2
- package/dist/utils/private/data/auth-tag.js.map +1 -1
- package/dist/utils/private/data/split-content-encryption-key.d.ts.map +1 -1
- package/dist/utils/private/data/split-content-encryption-key.js +6 -2
- package/dist/utils/private/data/split-content-encryption-key.js.map +1 -1
- package/dist/utils/private/diffie-hellman/diffie-hellman-key-wrap.d.ts +2 -2
- package/dist/utils/private/diffie-hellman/diffie-hellman-key-wrap.d.ts.map +1 -1
- package/dist/utils/private/diffie-hellman/diffie-hellman-key-wrap.js +12 -8
- package/dist/utils/private/diffie-hellman/diffie-hellman-key-wrap.js.map +1 -1
- package/dist/utils/private/diffie-hellman/diffie-hellman.d.ts +2 -2
- package/dist/utils/private/diffie-hellman/diffie-hellman.d.ts.map +1 -1
- package/dist/utils/private/diffie-hellman/diffie-hellman.js +12 -8
- package/dist/utils/private/diffie-hellman/diffie-hellman.js.map +1 -1
- package/dist/utils/private/diffie-hellman/shared-secret.d.ts.map +1 -1
- package/dist/utils/private/diffie-hellman/shared-secret.js +5 -1
- package/dist/utils/private/diffie-hellman/shared-secret.js.map +1 -1
- package/dist/utils/private/encoded-aes.d.ts +2 -2
- package/dist/utils/private/encoded-aes.d.ts.map +1 -1
- package/dist/utils/private/encoded-aes.js +86 -149
- package/dist/utils/private/encoded-aes.js.map +1 -1
- package/dist/utils/private/encrypt-content.d.ts +3 -0
- package/dist/utils/private/encrypt-content.d.ts.map +1 -0
- package/dist/utils/private/encrypt-content.js +35 -0
- package/dist/utils/private/encrypt-content.js.map +1 -0
- package/dist/utils/private/encrypt-encoded.d.ts +9 -0
- package/dist/utils/private/encrypt-encoded.d.ts.map +1 -0
- package/dist/utils/private/encrypt-encoded.js +53 -0
- package/dist/utils/private/encrypt-encoded.js.map +1 -0
- package/dist/utils/private/encrypt-serialised.d.ts +9 -0
- package/dist/utils/private/encrypt-serialised.d.ts.map +1 -0
- package/dist/utils/private/encrypt-serialised.js +48 -0
- package/dist/utils/private/encrypt-serialised.js.map +1 -0
- package/dist/utils/private/encrypt-tokenised.d.ts +9 -0
- package/dist/utils/private/encrypt-tokenised.d.ts.map +1 -0
- package/dist/utils/private/encrypt-tokenised.js +45 -0
- package/dist/utils/private/encrypt-tokenised.js.map +1 -0
- package/dist/utils/private/encryption.d.ts.map +1 -1
- package/dist/utils/private/encryption.js +27 -27
- package/dist/utils/private/encryption.js.map +1 -1
- package/dist/utils/private/index.d.ts +6 -0
- package/dist/utils/private/index.d.ts.map +1 -1
- package/dist/utils/private/index.js +6 -0
- package/dist/utils/private/index.js.map +1 -1
- package/dist/utils/private/key-derivation/concat-kdf.d.ts +14 -0
- package/dist/utils/private/key-derivation/concat-kdf.d.ts.map +1 -0
- package/dist/utils/private/key-derivation/concat-kdf.js +26 -0
- package/dist/utils/private/key-derivation/concat-kdf.js.map +1 -0
- package/dist/utils/private/key-derivation/index.d.ts +1 -1
- package/dist/utils/private/key-derivation/index.d.ts.map +1 -1
- package/dist/utils/private/key-derivation/index.js +1 -1
- package/dist/utils/private/key-derivation/index.js.map +1 -1
- package/dist/utils/private/key-derivation/pbkdf.d.ts +1 -0
- package/dist/utils/private/key-derivation/pbkdf.d.ts.map +1 -1
- package/dist/utils/private/key-derivation/pbkdf.js +13 -2
- package/dist/utils/private/key-derivation/pbkdf.js.map +1 -1
- package/dist/utils/private/key-wrap/ecb-key-wrap.d.ts.map +1 -1
- package/dist/utils/private/key-wrap/ecb-key-wrap.js +10 -3
- package/dist/utils/private/key-wrap/ecb-key-wrap.js.map +1 -1
- package/dist/utils/private/key-wrap/gcm-key-wrap.d.ts.map +1 -1
- package/dist/utils/private/key-wrap/gcm-key-wrap.js +6 -0
- package/dist/utils/private/key-wrap/gcm-key-wrap.js.map +1 -1
- package/dist/utils/private/oct/get-oct-key-key-wrap.d.ts +1 -1
- package/dist/utils/private/oct/get-oct-key-key-wrap.d.ts.map +1 -1
- package/dist/utils/private/oct/get-oct-key-key-wrap.js +7 -14
- package/dist/utils/private/oct/get-oct-key-key-wrap.js.map +1 -1
- package/dist/utils/private/oct/get-oct-pbkdf-key-wrap-keys.d.ts.map +1 -1
- package/dist/utils/private/oct/get-oct-pbkdf-key-wrap-keys.js +2 -0
- package/dist/utils/private/oct/get-oct-pbkdf-key-wrap-keys.js.map +1 -1
- package/dist/utils/private/prepare-encryption.d.ts +3 -0
- package/dist/utils/private/prepare-encryption.d.ts.map +1 -0
- package/dist/utils/private/prepare-encryption.js +27 -0
- package/dist/utils/private/prepare-encryption.js.map +1 -0
- package/dist/utils/private/serialised-aes.d.ts.map +1 -1
- package/dist/utils/private/serialised-aes.js +38 -46
- package/dist/utils/private/serialised-aes.js.map +1 -1
- package/dist/utils/private/tokenised-aes.d.ts +3 -3
- package/dist/utils/private/tokenised-aes.d.ts.map +1 -1
- package/dist/utils/private/tokenised-aes.js +73 -55
- package/dist/utils/private/tokenised-aes.js.map +1 -1
- package/dist/utils/private/validate-version.d.ts +2 -0
- package/dist/utils/private/validate-version.d.ts.map +1 -0
- package/dist/utils/private/validate-version.js +27 -0
- package/dist/utils/private/validate-version.js.map +1 -0
- package/jest.config.interop.mjs +24 -0
- package/package.json +16 -15
- package/tsconfig.interop.json +9 -0
- package/dist/constants/private/format.d.ts +0 -2
- package/dist/constants/private/format.d.ts.map +0 -1
- package/dist/constants/private/format.js +0 -5
- package/dist/constants/private/format.js.map +0 -1
- package/dist/types/private/aes-string.d.ts +0 -21
- package/dist/types/private/aes-string.d.ts.map +0 -1
- package/dist/types/private/aes-string.js.map +0 -1
- package/dist/utils/private/key-derivation/hkdf.d.ts +0 -13
- package/dist/utils/private/key-derivation/hkdf.d.ts.map +0 -1
- package/dist/utils/private/key-derivation/hkdf.js +0 -12
- package/dist/utils/private/key-derivation/hkdf.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,286 +1,452 @@
|
|
|
1
1
|
# @lindorm/aes
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
High-level **AES encryption & decryption** for Node.js with first-class
|
|
4
|
+
TypeScript support. `@lindorm/aes` wraps key derivation, key wrapping, and
|
|
5
|
+
authenticated content encryption behind a single `AesKit` class — so you can
|
|
6
|
+
encrypt any data type in one call and get back a string, a structured record, or
|
|
7
|
+
a compact token.
|
|
8
|
+
|
|
9
|
+
It includes:
|
|
10
|
+
|
|
11
|
+
- `AesKit` — encrypt / decrypt / verify / assert in four output formats
|
|
12
|
+
- **28 algorithm x encryption combinations** out of the box (EC, OKP, RSA, oct)
|
|
13
|
+
- JWE-aligned key derivation: ECDH-ES, AES-KW, AES-GCM-KW, PBKDF2, RSA-OAEP
|
|
14
|
+
- Automatic content-type detection (string, Buffer, object, array, number)
|
|
15
|
+
- Unified header model with always-on AAD across all formats
|
|
16
|
+
- Static helpers for format detection and parsing
|
|
17
|
+
|
|
18
|
+
---
|
|
4
19
|
|
|
5
20
|
## Installation
|
|
6
21
|
|
|
7
22
|
```bash
|
|
8
23
|
npm install @lindorm/aes
|
|
24
|
+
# or
|
|
25
|
+
yarn add @lindorm/aes
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Requires Node >= 18 and `@lindorm/kryptos` for key management.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Quick start
|
|
33
|
+
|
|
34
|
+
### Encrypt and decrypt
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import { AesKit } from "@lindorm/aes";
|
|
38
|
+
import { KryptosKit } from "@lindorm/kryptos";
|
|
39
|
+
|
|
40
|
+
// Generate an encryption key — Kryptos defaults to A256GCM content encryption
|
|
41
|
+
const kryptos = KryptosKit.generate.enc.oct({ algorithm: "A256KW" });
|
|
42
|
+
const aes = new AesKit({ kryptos });
|
|
43
|
+
|
|
44
|
+
const encrypted = aes.encrypt("Hello World"); // base64url string
|
|
45
|
+
const decrypted = aes.decrypt(encrypted); // "Hello World"
|
|
9
46
|
```
|
|
10
47
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
- **ECDH-ES+A128GCMKW**: ECDH-ES with AES-128 GCM Key Wrap
|
|
28
|
-
- **A128KW**: AES-128 Key Wrap
|
|
29
|
-
- **A128GCMKW**: AES-128 GCM Key Wrap
|
|
30
|
-
- **PBES2-HS256+A128KW**: PBKDF2 with HMAC SHA-256 and AES-128 Key Wrap
|
|
31
|
-
- **RSA-OAEP-256**: RSA OAEP with SHA-256
|
|
32
|
-
|
|
33
|
-
### Encryption Modes
|
|
34
|
-
- **A128CBC-HS256**: AES-128 CBC with HMAC SHA-256
|
|
35
|
-
- **A128GCM**: AES-128 GCM (default)
|
|
36
|
-
- **A192GCM**: AES-192 GCM
|
|
37
|
-
- **A256GCM**: AES-256 GCM
|
|
38
|
-
|
|
39
|
-
## Basic Usage
|
|
40
|
-
|
|
41
|
-
### Setup
|
|
42
|
-
|
|
43
|
-
```typescript
|
|
44
|
-
import { AesKit } from '@lindorm/aes';
|
|
45
|
-
import { KryptosKit } from '@lindorm/kryptos';
|
|
46
|
-
|
|
47
|
-
// Generate or load a cryptographic key
|
|
48
|
-
const kryptos = KryptosKit.generate.auto({ algorithm: 'ECDH-ES' });
|
|
49
|
-
|
|
50
|
-
// Create AES kit instance
|
|
51
|
-
const aesKit = new AesKit({
|
|
52
|
-
kryptos,
|
|
53
|
-
encryption: 'A256GCM' // Optional: defaults to A256GCM or kryptos.encryption
|
|
54
|
-
});
|
|
48
|
+
### Choose an output format
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
// Encoded — single base64url string (default)
|
|
52
|
+
const encoded = aes.encrypt("secret");
|
|
53
|
+
|
|
54
|
+
// Record — object with raw Buffer values
|
|
55
|
+
const record = aes.encrypt("secret", "record");
|
|
56
|
+
|
|
57
|
+
// Serialised — JWE-like object with base64url strings (JSON-safe)
|
|
58
|
+
const serialised = aes.encrypt("secret", "serialised");
|
|
59
|
+
const json = JSON.stringify(serialised);
|
|
60
|
+
|
|
61
|
+
// Tokenised — human-readable $-delimited string
|
|
62
|
+
const token = aes.encrypt("secret", "tokenised");
|
|
63
|
+
// "aes:<base64url(header)>$<iv>$<tag>$<ciphertext>"
|
|
55
64
|
```
|
|
56
65
|
|
|
57
|
-
|
|
66
|
+
All four formats are accepted by `decrypt`, `verify`, and `assert`:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
aes.decrypt(encoded); // works
|
|
70
|
+
aes.decrypt(record); // works
|
|
71
|
+
aes.decrypt(JSON.parse(json)); // works
|
|
72
|
+
aes.decrypt(token); // works
|
|
73
|
+
```
|
|
58
74
|
|
|
59
|
-
|
|
75
|
+
### Encrypt any content type
|
|
60
76
|
|
|
61
|
-
|
|
62
|
-
|
|
77
|
+
```ts
|
|
78
|
+
aes.encrypt("plain text"); // string
|
|
79
|
+
aes.encrypt(Buffer.from("binary")); // Buffer
|
|
80
|
+
aes.encrypt({ user: "alice", role: "admin" }); // object
|
|
81
|
+
aes.encrypt([1, 2, 3]); // array
|
|
82
|
+
aes.encrypt(42); // number
|
|
63
83
|
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
const decrypted = aesKit.decrypt(encrypted); // 'Hello World'
|
|
84
|
+
// Content type is tracked — decrypt returns the original type
|
|
85
|
+
const obj = aes.decrypt<{ user: string }>(cipher); // { user: "alice", ... }
|
|
67
86
|
```
|
|
68
87
|
|
|
69
|
-
|
|
70
|
-
Returns a structured object with Buffer values - useful for direct manipulation:
|
|
88
|
+
### Verify and assert
|
|
71
89
|
|
|
72
|
-
```
|
|
73
|
-
const
|
|
74
|
-
// Returns: AesEncryptionRecord with Buffer properties
|
|
90
|
+
```ts
|
|
91
|
+
const cipher = aes.encrypt("secret");
|
|
75
92
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
console.log(encrypted.encryption); // 'A256GCM'
|
|
79
|
-
console.log(encrypted.content); // Buffer containing encrypted data
|
|
93
|
+
aes.verify("secret", cipher); // true
|
|
94
|
+
aes.verify("wrong", cipher); // false
|
|
80
95
|
|
|
81
|
-
|
|
96
|
+
aes.assert("secret", cipher); // void — passes silently
|
|
97
|
+
aes.assert("wrong", cipher); // throws AesError("Invalid AES cipher")
|
|
82
98
|
```
|
|
83
99
|
|
|
84
|
-
|
|
85
|
-
|
|
100
|
+
### Additional Authenticated Data (AAD)
|
|
101
|
+
|
|
102
|
+
All formats automatically compute AAD from the base64url-encoded header,
|
|
103
|
+
ensuring metadata integrity. For the raw record format you can also supply
|
|
104
|
+
custom AAD:
|
|
86
105
|
|
|
87
|
-
```
|
|
88
|
-
const
|
|
89
|
-
// Returns: SerialisedAesEncryption with base64 string properties
|
|
106
|
+
```ts
|
|
107
|
+
const aad = Buffer.from("request-id:abc-123");
|
|
90
108
|
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
109
|
+
const cipher = aes.encrypt("payload", "record", { aad });
|
|
110
|
+
|
|
111
|
+
aes.decrypt(cipher, { aad }); // "payload"
|
|
112
|
+
aes.decrypt(cipher); // throws — AAD mismatch
|
|
113
|
+
aes.decrypt(cipher, { aad: Buffer.from("wrong") }); // throws
|
|
94
114
|
```
|
|
95
115
|
|
|
96
|
-
|
|
97
|
-
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Supported algorithms
|
|
119
|
+
|
|
120
|
+
### Content encryption
|
|
121
|
+
|
|
122
|
+
| Encryption | Mode | Key bits | Auth |
|
|
123
|
+
| --------------- | ---- | -------- | --------------------------- |
|
|
124
|
+
| `A128GCM` | GCM | 128 | built-in auth tag |
|
|
125
|
+
| `A192GCM` | GCM | 192 | built-in auth tag |
|
|
126
|
+
| `A256GCM` | GCM | 256 | built-in auth tag (default) |
|
|
127
|
+
| `A128CBC-HS256` | CBC | 128 | HMAC-SHA256 |
|
|
128
|
+
| `A192CBC-HS384` | CBC | 192 | HMAC-SHA384 |
|
|
129
|
+
| `A256CBC-HS512` | CBC | 256 | HMAC-SHA512 |
|
|
130
|
+
|
|
131
|
+
### Key algorithms
|
|
132
|
+
|
|
133
|
+
| Key type | Algorithms |
|
|
134
|
+
| --------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
|
135
|
+
| EC | `ECDH-ES`, `ECDH-ES+A128KW`, `ECDH-ES+A192KW`, `ECDH-ES+A256KW`, `ECDH-ES+A128GCMKW`, `ECDH-ES+A192GCMKW`, `ECDH-ES+A256GCMKW` |
|
|
136
|
+
| OKP | `ECDH-ES`, `ECDH-ES+A128KW`, `ECDH-ES+A192KW`, `ECDH-ES+A256KW`, `ECDH-ES+A128GCMKW`, `ECDH-ES+A192GCMKW`, `ECDH-ES+A256GCMKW` |
|
|
137
|
+
| RSA | `RSA-OAEP-256`, `RSA-OAEP-384`, `RSA-OAEP-512` |
|
|
138
|
+
| oct (symmetric) | `A128KW`, `A192KW`, `A256KW`, `A128GCMKW`, `A192GCMKW`, `A256GCMKW`, `dir` |
|
|
139
|
+
| oct (password) | `PBES2-HS256+A128KW`, `PBES2-HS384+A192KW`, `PBES2-HS512+A256KW` |
|
|
140
|
+
|
|
141
|
+
Every key algorithm can be combined with every content encryption — giving you
|
|
142
|
+
28+ working combinations.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## API reference
|
|
98
147
|
|
|
99
|
-
|
|
100
|
-
const encrypted = aesKit.encrypt('Hello World', 'tokenised');
|
|
101
|
-
// Returns: '$A256GCM$v=1&alg=ECDH-ES&...$base64content$'
|
|
148
|
+
### `new AesKit(options)`
|
|
102
149
|
|
|
103
|
-
|
|
150
|
+
```ts
|
|
151
|
+
import { AesKit } from "@lindorm/aes";
|
|
152
|
+
|
|
153
|
+
const aes = new AesKit({ kryptos });
|
|
154
|
+
|
|
155
|
+
aes.kryptos; // the IKryptos instance (public readonly)
|
|
104
156
|
```
|
|
105
157
|
|
|
106
|
-
|
|
158
|
+
The content encryption algorithm is read from `kryptos.encryption` (defaults to
|
|
159
|
+
`A256GCM` for generated enc keys). You can override it for imported keys that
|
|
160
|
+
lack an encryption preference:
|
|
107
161
|
|
|
108
|
-
|
|
162
|
+
```ts
|
|
163
|
+
const imported = KryptosKit.from.jwk(externalJwk);
|
|
164
|
+
const aes = new AesKit({ kryptos: imported, encryption: "A128GCM" });
|
|
165
|
+
```
|
|
109
166
|
|
|
110
|
-
|
|
111
|
-
// String content
|
|
112
|
-
const text = aesKit.encrypt('Hello World');
|
|
167
|
+
### `aes.encrypt(data, mode?, options?)`
|
|
113
168
|
|
|
114
|
-
|
|
115
|
-
const obj = aesKit.encrypt({ message: 'Hello', count: 42 });
|
|
169
|
+
Encrypts data and returns one of four formats depending on `mode`:
|
|
116
170
|
|
|
117
|
-
|
|
118
|
-
|
|
171
|
+
| Mode | Return type | Description |
|
|
172
|
+
| --------------------- | ------------------------- | ----------------------------------------- |
|
|
173
|
+
| `"encoded"` (default) | `string` | Base64url-encoded binary blob |
|
|
174
|
+
| `"record"` | `AesEncryptionRecord` | Object with raw `Buffer` values |
|
|
175
|
+
| `"serialised"` | `SerialisedAesEncryption` | Object with base64url strings (JSON-safe) |
|
|
176
|
+
| `"tokenised"` | `string` | `$`-delimited human-readable token |
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
encrypt(data: AesContent, mode?: "encoded", options?: AesOperationOptions): string;
|
|
180
|
+
encrypt(data: AesContent, mode: "record", options?: AesOperationOptions): AesEncryptionRecord;
|
|
181
|
+
encrypt(data: AesContent, mode: "serialised", options?: AesOperationOptions): SerialisedAesEncryption;
|
|
182
|
+
encrypt(data: AesContent, mode: "tokenised", options?: AesOperationOptions): string;
|
|
183
|
+
```
|
|
119
184
|
|
|
120
|
-
|
|
121
|
-
const buf = aesKit.encrypt(Buffer.from('binary data'));
|
|
185
|
+
### `aes.decrypt<T>(data, options?)`
|
|
122
186
|
|
|
123
|
-
|
|
124
|
-
|
|
187
|
+
Decrypts any supported format back to the original content.
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
decrypt<T extends AesContent = string>(
|
|
191
|
+
data: AesDecryptionRecord | SerialisedAesDecryption | string,
|
|
192
|
+
options?: AesOperationOptions,
|
|
193
|
+
): T;
|
|
125
194
|
```
|
|
126
195
|
|
|
127
|
-
|
|
196
|
+
Format is auto-detected: encoded string, tokenised string, record object, or
|
|
197
|
+
serialised object all work transparently.
|
|
128
198
|
|
|
129
|
-
|
|
130
|
-
const encrypted = aesKit.encrypt('Hello World');
|
|
199
|
+
### `aes.verify(input, data, options?)`
|
|
131
200
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const isInvalid = aesKit.verify('Wrong data', encrypted); // false
|
|
201
|
+
Returns `true` if decrypted data deeply equals `input`, `false` otherwise.
|
|
202
|
+
Never throws.
|
|
135
203
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
204
|
+
```ts
|
|
205
|
+
verify(
|
|
206
|
+
input: AesContent,
|
|
207
|
+
data: AesDecryptionRecord | SerialisedAesDecryption | string,
|
|
208
|
+
options?: AesOperationOptions,
|
|
209
|
+
): boolean;
|
|
139
210
|
```
|
|
140
211
|
|
|
141
|
-
|
|
212
|
+
### `aes.assert(input, data, options?)`
|
|
142
213
|
|
|
143
|
-
|
|
214
|
+
Throws `AesError("Invalid AES cipher")` if decrypted data does not match
|
|
215
|
+
`input`.
|
|
144
216
|
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
|
|
217
|
+
```ts
|
|
218
|
+
assert(
|
|
219
|
+
input: AesContent,
|
|
220
|
+
data: AesDecryptionRecord | SerialisedAesDecryption | string,
|
|
221
|
+
options?: AesOperationOptions,
|
|
222
|
+
): void;
|
|
223
|
+
```
|
|
148
224
|
|
|
149
|
-
|
|
150
|
-
const rsaKey = KryptosKit.generate.rsa({
|
|
151
|
-
algorithm: 'RSA-OAEP-256',
|
|
152
|
-
modulusLength: 2048
|
|
153
|
-
});
|
|
225
|
+
### `aes.prepareEncryption()`
|
|
154
226
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
encryption: 'A256GCM'
|
|
158
|
-
});
|
|
227
|
+
Two-step JWE-compliant encryption. Returns key management parameters and an
|
|
228
|
+
`encrypt()` closure that can be called later with the plaintext.
|
|
159
229
|
|
|
160
|
-
|
|
161
|
-
const
|
|
162
|
-
algorithm: 'PBES2-HS256+A128KW',
|
|
163
|
-
length: 256
|
|
164
|
-
});
|
|
230
|
+
```ts
|
|
231
|
+
const prepared = aes.prepareEncryption();
|
|
165
232
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
});
|
|
233
|
+
// prepared.headerParams — key exchange / PBKDF2 params for the header
|
|
234
|
+
// prepared.publicEncryptionKey — wrapped CEK (if applicable)
|
|
235
|
+
// prepared.encrypt(data, { aad? }) — encrypts with the pre-derived key
|
|
170
236
|
```
|
|
171
237
|
|
|
172
|
-
###
|
|
173
|
-
|
|
174
|
-
```
|
|
175
|
-
//
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
//
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const symmetricKey = KryptosKit.generate.oct({
|
|
189
|
-
algorithm: 'A128GCMKW',
|
|
190
|
-
length: 256
|
|
191
|
-
});
|
|
238
|
+
### Static methods
|
|
239
|
+
|
|
240
|
+
```ts
|
|
241
|
+
// Detect content type of any input
|
|
242
|
+
AesKit.contentType("hello"); // "text/plain"
|
|
243
|
+
AesKit.contentType(Buffer.from("")); // "application/octet-stream"
|
|
244
|
+
AesKit.contentType({ a: 1 }); // "application/json"
|
|
245
|
+
|
|
246
|
+
// Check if a string is in tokenised format
|
|
247
|
+
AesKit.isAesTokenised("aes:eyJhbGci...$...$...$..."); // true
|
|
248
|
+
AesKit.isAesTokenised("base64string"); // false
|
|
249
|
+
|
|
250
|
+
// Parse any format into an AesDecryptionRecord (Buffer values)
|
|
251
|
+
const record = AesKit.parse(encodedString);
|
|
252
|
+
const record2 = AesKit.parse(tokenisedString);
|
|
253
|
+
const record3 = AesKit.parse(serialisedObject);
|
|
192
254
|
```
|
|
193
255
|
|
|
194
|
-
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Format version 1.0
|
|
259
|
+
|
|
260
|
+
All output formats share a **unified header model** — a JSON object containing
|
|
261
|
+
the algorithm, encryption, content type, key ID, version, and any key-exchange
|
|
262
|
+
parameters. The header is base64url-encoded and used as AAD for authenticated
|
|
263
|
+
encryption, binding the metadata to the ciphertext.
|
|
264
|
+
|
|
265
|
+
### Header structure
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
type AesHeader = {
|
|
269
|
+
alg: KryptosAlgorithm; // key management algorithm
|
|
270
|
+
cty: AesContentType; // content type
|
|
271
|
+
enc: KryptosEncryption; // content encryption
|
|
272
|
+
epk?: PublicEncryptionJwk; // ephemeral public key (ECDH)
|
|
273
|
+
iv?: string; // public encryption IV (base64url, GCMKW)
|
|
274
|
+
kid: string; // key ID
|
|
275
|
+
p2c?: number; // PBKDF2 iterations
|
|
276
|
+
p2s?: string; // PBKDF2 salt (base64url)
|
|
277
|
+
tag?: string; // public encryption tag (base64url, GCMKW)
|
|
278
|
+
v: string; // format version ("1.0")
|
|
279
|
+
};
|
|
280
|
+
```
|
|
195
281
|
|
|
196
|
-
|
|
197
|
-
import { parseAes, isAesTokenised } from '@lindorm/aes';
|
|
282
|
+
### Encoded
|
|
198
283
|
|
|
199
|
-
|
|
200
|
-
const record = parseAes(encryptedData); // Works with any format
|
|
284
|
+
A single base64url string wrapping a binary layout:
|
|
201
285
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const isNotToken = isAesTokenised('base64string'); // false
|
|
286
|
+
```
|
|
287
|
+
[2B header length][header JSON][2B CEK length][CEK][IV][Tag][Ciphertext]
|
|
205
288
|
```
|
|
206
289
|
|
|
207
|
-
|
|
290
|
+
IV and tag sizes are derived from the encryption algorithm (e.g. 12B IV + 16B
|
|
291
|
+
tag for GCM).
|
|
208
292
|
|
|
209
|
-
###
|
|
293
|
+
### Serialised
|
|
210
294
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
295
|
+
A JSON-safe object with base64url-encoded fields:
|
|
296
|
+
|
|
297
|
+
```ts
|
|
298
|
+
{
|
|
299
|
+
header: string; // base64url(JSON(header))
|
|
300
|
+
cek?: string; // base64url — undefined for dir/ECDH-ES
|
|
301
|
+
iv: string; // base64url
|
|
302
|
+
tag: string; // base64url
|
|
303
|
+
ciphertext: string; // base64url
|
|
304
|
+
v: string; // "1.0"
|
|
215
305
|
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Tokenised
|
|
309
|
+
|
|
310
|
+
A human-readable `$`-delimited string:
|
|
311
|
+
|
|
312
|
+
```
|
|
313
|
+
aes:<header>$[<cek>$]<iv>$<tag>$<ciphertext>
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
All segments are base64url-encoded. The CEK segment is present for key-wrap
|
|
317
|
+
algorithms and omitted for `dir` and `ECDH-ES`.
|
|
318
|
+
|
|
319
|
+
### Record
|
|
320
|
+
|
|
321
|
+
A plain object with raw `Buffer` values for all binary fields (`authTag`,
|
|
322
|
+
`content`, `initialisationVector`, etc.). Useful when you need programmatic
|
|
323
|
+
access to individual encryption components.
|
|
324
|
+
|
|
325
|
+
---
|
|
216
326
|
|
|
327
|
+
## Type definitions
|
|
328
|
+
|
|
329
|
+
### Core types
|
|
330
|
+
|
|
331
|
+
```ts
|
|
217
332
|
type AesContent = Array<any> | Buffer | Dict | number | string;
|
|
218
333
|
|
|
219
|
-
type
|
|
334
|
+
type AesContentType = "application/json" | "application/octet-stream" | "text/plain";
|
|
335
|
+
|
|
336
|
+
type AesEncryptionMode = "encoded" | "record" | "serialised" | "tokenised";
|
|
337
|
+
|
|
338
|
+
type AesKitOptions = {
|
|
339
|
+
encryption?: KryptosEncryption;
|
|
340
|
+
kryptos: IKryptos;
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
type AesOperationOptions = {
|
|
344
|
+
aad?: Buffer;
|
|
345
|
+
};
|
|
220
346
|
```
|
|
221
347
|
|
|
222
|
-
###
|
|
223
|
-
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
algorithm: KryptosAlgorithm;
|
|
227
|
-
authTag: Buffer;
|
|
228
|
-
content: Buffer;
|
|
229
|
-
contentType: AesContentType;
|
|
230
|
-
encryption: KryptosEncryption;
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
publicEncryptionJwk
|
|
237
|
-
|
|
238
|
-
|
|
348
|
+
### Encryption record
|
|
349
|
+
|
|
350
|
+
```ts
|
|
351
|
+
type AesEncryptionRecord = {
|
|
352
|
+
algorithm: KryptosAlgorithm;
|
|
353
|
+
authTag: Buffer;
|
|
354
|
+
content: Buffer;
|
|
355
|
+
contentType: AesContentType;
|
|
356
|
+
encryption: KryptosEncryption;
|
|
357
|
+
initialisationVector: Buffer;
|
|
358
|
+
keyId: string;
|
|
359
|
+
pbkdfIterations: number | undefined;
|
|
360
|
+
pbkdfSalt: Buffer | undefined;
|
|
361
|
+
publicEncryptionIv: Buffer | undefined;
|
|
362
|
+
publicEncryptionJwk: PublicEncryptionJwk | undefined;
|
|
363
|
+
publicEncryptionKey: Buffer | undefined;
|
|
364
|
+
publicEncryptionTag: Buffer | undefined;
|
|
365
|
+
version: string;
|
|
366
|
+
};
|
|
367
|
+
```
|
|
239
368
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
369
|
+
### Serialised encryption
|
|
370
|
+
|
|
371
|
+
```ts
|
|
372
|
+
type SerialisedAesEncryption = {
|
|
373
|
+
cek: string | undefined;
|
|
374
|
+
ciphertext: string;
|
|
375
|
+
header: string;
|
|
376
|
+
iv: string;
|
|
377
|
+
tag: string;
|
|
378
|
+
v: string;
|
|
379
|
+
};
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### Decryption records
|
|
383
|
+
|
|
384
|
+
`AesDecryptionRecord` mirrors `AesEncryptionRecord` with most fields optional —
|
|
385
|
+
only `content`, `encryption`, and `initialisationVector` are required.
|
|
386
|
+
|
|
387
|
+
`SerialisedAesDecryption` mirrors `SerialisedAesEncryption` with `cek` optional.
|
|
388
|
+
|
|
389
|
+
`ParsedAesDecryptionRecord` is a stricter variant returned by parsers where all
|
|
390
|
+
parsed fields are guaranteed non-optional.
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## Utility functions
|
|
395
|
+
|
|
396
|
+
```ts
|
|
397
|
+
import {
|
|
398
|
+
isAesBufferData,
|
|
399
|
+
isAesSerialisedData,
|
|
400
|
+
isAesTokenised,
|
|
401
|
+
parseAes,
|
|
402
|
+
} from "@lindorm/aes";
|
|
403
|
+
|
|
404
|
+
// Type guard — record with Buffer values
|
|
405
|
+
isAesBufferData(data); // data is AesDecryptionRecord
|
|
406
|
+
|
|
407
|
+
// Type guard — record with string values
|
|
408
|
+
isAesSerialisedData(data); // data is SerialisedAesDecryption
|
|
409
|
+
|
|
410
|
+
// Format check — tokenised string (starts with "aes:")
|
|
411
|
+
isAesTokenised(str); // boolean
|
|
412
|
+
|
|
413
|
+
// Parse any format into AesDecryptionRecord
|
|
414
|
+
const record = parseAes(anyEncryptedData);
|
|
243
415
|
```
|
|
244
416
|
|
|
245
|
-
|
|
417
|
+
---
|
|
246
418
|
|
|
247
|
-
|
|
248
|
-
|
|
419
|
+
## Error handling
|
|
420
|
+
|
|
421
|
+
```ts
|
|
422
|
+
import { AesError } from "@lindorm/aes";
|
|
249
423
|
|
|
250
424
|
try {
|
|
251
|
-
|
|
425
|
+
aes.decrypt(corruptedData);
|
|
252
426
|
} catch (error) {
|
|
253
427
|
if (error instanceof AesError) {
|
|
254
|
-
console.
|
|
428
|
+
console.error("AES operation failed:", error.message);
|
|
255
429
|
}
|
|
256
430
|
}
|
|
257
431
|
```
|
|
258
432
|
|
|
259
|
-
|
|
433
|
+
`AesError` extends `LindormError` from `@lindorm/errors`.
|
|
260
434
|
|
|
261
|
-
|
|
435
|
+
---
|
|
262
436
|
|
|
263
|
-
|
|
264
|
-
import { AesKit } from '@lindorm/aes';
|
|
265
|
-
import { Amphora } from '@lindorm/amphora';
|
|
437
|
+
## Testing helpers
|
|
266
438
|
|
|
267
|
-
|
|
268
|
-
const amphora = new Amphora({ domain: 'https://example.com', logger });
|
|
269
|
-
await amphora.setup();
|
|
439
|
+
A mock factory is exported for unit tests:
|
|
270
440
|
|
|
271
|
-
|
|
272
|
-
|
|
441
|
+
```ts
|
|
442
|
+
import { createMockAesKit } from "@lindorm/aes";
|
|
273
443
|
|
|
274
|
-
const
|
|
444
|
+
const mock = createMockAesKit();
|
|
445
|
+
// mock.encrypt, mock.decrypt, mock.verify, mock.assert — all jest.fn()
|
|
275
446
|
```
|
|
276
447
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
- **Encoded mode**: Fastest for simple use cases
|
|
280
|
-
- **Record mode**: Best for direct Buffer manipulation
|
|
281
|
-
- **Serialised mode**: Optimal for JSON storage/transmission
|
|
282
|
-
- **Tokenised mode**: Most compact for URL-safe tokens
|
|
448
|
+
---
|
|
283
449
|
|
|
284
450
|
## License
|
|
285
451
|
|
|
286
|
-
AGPL-3.0-or-later
|
|
452
|
+
AGPL-3.0-or-later
|