@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.
Files changed (165) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/MERMAID.md +155 -0
  3. package/README.md +365 -199
  4. package/__tests__/INTEROP-RESULTS.md +66 -0
  5. package/__tests__/esm-smoke.test.ts +15 -0
  6. package/__tests__/fixtures/keys.ts +60 -0
  7. package/__tests__/helpers/buffer-utils.ts +11 -0
  8. package/__tests__/helpers/index.ts +2 -0
  9. package/__tests__/helpers/jwe-adapter.ts +117 -0
  10. package/__tests__/jose-jwe.test.ts +463 -0
  11. package/__tests__/noble-ciphers.test.ts +208 -0
  12. package/dist/classes/AesKit.d.ts +10 -8
  13. package/dist/classes/AesKit.d.ts.map +1 -1
  14. package/dist/classes/AesKit.js +73 -34
  15. package/dist/classes/AesKit.js.map +1 -1
  16. package/dist/constants/private/index.d.ts +0 -1
  17. package/dist/constants/private/index.d.ts.map +1 -1
  18. package/dist/constants/private/index.js +0 -1
  19. package/dist/constants/private/index.js.map +1 -1
  20. package/dist/constants/private/version.d.ts +3 -1
  21. package/dist/constants/private/version.d.ts.map +1 -1
  22. package/dist/constants/private/version.js +4 -2
  23. package/dist/constants/private/version.js.map +1 -1
  24. package/dist/interfaces/AesKit.d.ts +12 -7
  25. package/dist/interfaces/AesKit.d.ts.map +1 -1
  26. package/dist/mocks/mock-aes-kit.d.ts.map +1 -1
  27. package/dist/mocks/mock-aes-kit.js +12 -2
  28. package/dist/mocks/mock-aes-kit.js.map +1 -1
  29. package/dist/types/aes-decryption-data.d.ts +26 -17
  30. package/dist/types/aes-decryption-data.d.ts.map +1 -1
  31. package/dist/types/aes-encryption-data.d.ts +7 -17
  32. package/dist/types/aes-encryption-data.d.ts.map +1 -1
  33. package/dist/types/index.d.ts +1 -0
  34. package/dist/types/index.d.ts.map +1 -1
  35. package/dist/types/private/aes-data.d.ts.map +1 -1
  36. package/dist/types/private/aes-key-derivation.d.ts +1 -1
  37. package/dist/types/private/aes-key-derivation.d.ts.map +1 -1
  38. package/dist/types/private/auth-tag.d.ts +6 -3
  39. package/dist/types/private/auth-tag.d.ts.map +1 -1
  40. package/dist/types/private/content-encryption-key.d.ts +4 -2
  41. package/dist/types/private/content-encryption-key.d.ts.map +1 -1
  42. package/dist/types/private/index.d.ts +1 -1
  43. package/dist/types/private/index.d.ts.map +1 -1
  44. package/dist/types/private/index.js +1 -1
  45. package/dist/types/private/index.js.map +1 -1
  46. package/dist/types/private/prepared-encryption.d.ts +35 -0
  47. package/dist/types/private/prepared-encryption.d.ts.map +1 -0
  48. package/dist/types/private/{aes-string.js → prepared-encryption.js} +1 -1
  49. package/dist/types/private/prepared-encryption.js.map +1 -0
  50. package/dist/utils/is-aes.d.ts.map +1 -1
  51. package/dist/utils/is-aes.js +1 -5
  52. package/dist/utils/is-aes.js.map +1 -1
  53. package/dist/utils/parse-aes.js +3 -3
  54. package/dist/utils/parse-aes.js.map +1 -1
  55. package/dist/utils/private/aes-header.d.ts +42 -0
  56. package/dist/utils/private/aes-header.d.ts.map +1 -0
  57. package/dist/utils/private/aes-header.js +75 -0
  58. package/dist/utils/private/aes-header.js.map +1 -0
  59. package/dist/utils/private/calculate/calculate-content-encryption-key-size.js +3 -3
  60. package/dist/utils/private/calculate/calculate-key-wrap-encryption.d.ts.map +1 -1
  61. package/dist/utils/private/calculate/calculate-key-wrap-encryption.js +2 -1
  62. package/dist/utils/private/calculate/calculate-key-wrap-encryption.js.map +1 -1
  63. package/dist/utils/private/data/auth-tag-hmac.d.ts +2 -2
  64. package/dist/utils/private/data/auth-tag-hmac.d.ts.map +1 -1
  65. package/dist/utils/private/data/auth-tag-hmac.js +12 -4
  66. package/dist/utils/private/data/auth-tag-hmac.js.map +1 -1
  67. package/dist/utils/private/data/auth-tag.d.ts +2 -2
  68. package/dist/utils/private/data/auth-tag.d.ts.map +1 -1
  69. package/dist/utils/private/data/auth-tag.js +4 -2
  70. package/dist/utils/private/data/auth-tag.js.map +1 -1
  71. package/dist/utils/private/data/split-content-encryption-key.d.ts.map +1 -1
  72. package/dist/utils/private/data/split-content-encryption-key.js +6 -2
  73. package/dist/utils/private/data/split-content-encryption-key.js.map +1 -1
  74. package/dist/utils/private/diffie-hellman/diffie-hellman-key-wrap.d.ts +2 -2
  75. package/dist/utils/private/diffie-hellman/diffie-hellman-key-wrap.d.ts.map +1 -1
  76. package/dist/utils/private/diffie-hellman/diffie-hellman-key-wrap.js +12 -8
  77. package/dist/utils/private/diffie-hellman/diffie-hellman-key-wrap.js.map +1 -1
  78. package/dist/utils/private/diffie-hellman/diffie-hellman.d.ts +2 -2
  79. package/dist/utils/private/diffie-hellman/diffie-hellman.d.ts.map +1 -1
  80. package/dist/utils/private/diffie-hellman/diffie-hellman.js +12 -8
  81. package/dist/utils/private/diffie-hellman/diffie-hellman.js.map +1 -1
  82. package/dist/utils/private/diffie-hellman/shared-secret.d.ts.map +1 -1
  83. package/dist/utils/private/diffie-hellman/shared-secret.js +5 -1
  84. package/dist/utils/private/diffie-hellman/shared-secret.js.map +1 -1
  85. package/dist/utils/private/encoded-aes.d.ts +2 -2
  86. package/dist/utils/private/encoded-aes.d.ts.map +1 -1
  87. package/dist/utils/private/encoded-aes.js +86 -149
  88. package/dist/utils/private/encoded-aes.js.map +1 -1
  89. package/dist/utils/private/encrypt-content.d.ts +3 -0
  90. package/dist/utils/private/encrypt-content.d.ts.map +1 -0
  91. package/dist/utils/private/encrypt-content.js +35 -0
  92. package/dist/utils/private/encrypt-content.js.map +1 -0
  93. package/dist/utils/private/encrypt-encoded.d.ts +9 -0
  94. package/dist/utils/private/encrypt-encoded.d.ts.map +1 -0
  95. package/dist/utils/private/encrypt-encoded.js +53 -0
  96. package/dist/utils/private/encrypt-encoded.js.map +1 -0
  97. package/dist/utils/private/encrypt-serialised.d.ts +9 -0
  98. package/dist/utils/private/encrypt-serialised.d.ts.map +1 -0
  99. package/dist/utils/private/encrypt-serialised.js +48 -0
  100. package/dist/utils/private/encrypt-serialised.js.map +1 -0
  101. package/dist/utils/private/encrypt-tokenised.d.ts +9 -0
  102. package/dist/utils/private/encrypt-tokenised.d.ts.map +1 -0
  103. package/dist/utils/private/encrypt-tokenised.js +45 -0
  104. package/dist/utils/private/encrypt-tokenised.js.map +1 -0
  105. package/dist/utils/private/encryption.d.ts.map +1 -1
  106. package/dist/utils/private/encryption.js +27 -27
  107. package/dist/utils/private/encryption.js.map +1 -1
  108. package/dist/utils/private/index.d.ts +6 -0
  109. package/dist/utils/private/index.d.ts.map +1 -1
  110. package/dist/utils/private/index.js +6 -0
  111. package/dist/utils/private/index.js.map +1 -1
  112. package/dist/utils/private/key-derivation/concat-kdf.d.ts +14 -0
  113. package/dist/utils/private/key-derivation/concat-kdf.d.ts.map +1 -0
  114. package/dist/utils/private/key-derivation/concat-kdf.js +26 -0
  115. package/dist/utils/private/key-derivation/concat-kdf.js.map +1 -0
  116. package/dist/utils/private/key-derivation/index.d.ts +1 -1
  117. package/dist/utils/private/key-derivation/index.d.ts.map +1 -1
  118. package/dist/utils/private/key-derivation/index.js +1 -1
  119. package/dist/utils/private/key-derivation/index.js.map +1 -1
  120. package/dist/utils/private/key-derivation/pbkdf.d.ts +1 -0
  121. package/dist/utils/private/key-derivation/pbkdf.d.ts.map +1 -1
  122. package/dist/utils/private/key-derivation/pbkdf.js +13 -2
  123. package/dist/utils/private/key-derivation/pbkdf.js.map +1 -1
  124. package/dist/utils/private/key-wrap/ecb-key-wrap.d.ts.map +1 -1
  125. package/dist/utils/private/key-wrap/ecb-key-wrap.js +10 -3
  126. package/dist/utils/private/key-wrap/ecb-key-wrap.js.map +1 -1
  127. package/dist/utils/private/key-wrap/gcm-key-wrap.d.ts.map +1 -1
  128. package/dist/utils/private/key-wrap/gcm-key-wrap.js +6 -0
  129. package/dist/utils/private/key-wrap/gcm-key-wrap.js.map +1 -1
  130. package/dist/utils/private/oct/get-oct-key-key-wrap.d.ts +1 -1
  131. package/dist/utils/private/oct/get-oct-key-key-wrap.d.ts.map +1 -1
  132. package/dist/utils/private/oct/get-oct-key-key-wrap.js +7 -14
  133. package/dist/utils/private/oct/get-oct-key-key-wrap.js.map +1 -1
  134. package/dist/utils/private/oct/get-oct-pbkdf-key-wrap-keys.d.ts.map +1 -1
  135. package/dist/utils/private/oct/get-oct-pbkdf-key-wrap-keys.js +2 -0
  136. package/dist/utils/private/oct/get-oct-pbkdf-key-wrap-keys.js.map +1 -1
  137. package/dist/utils/private/prepare-encryption.d.ts +3 -0
  138. package/dist/utils/private/prepare-encryption.d.ts.map +1 -0
  139. package/dist/utils/private/prepare-encryption.js +27 -0
  140. package/dist/utils/private/prepare-encryption.js.map +1 -0
  141. package/dist/utils/private/serialised-aes.d.ts.map +1 -1
  142. package/dist/utils/private/serialised-aes.js +38 -46
  143. package/dist/utils/private/serialised-aes.js.map +1 -1
  144. package/dist/utils/private/tokenised-aes.d.ts +3 -3
  145. package/dist/utils/private/tokenised-aes.d.ts.map +1 -1
  146. package/dist/utils/private/tokenised-aes.js +73 -55
  147. package/dist/utils/private/tokenised-aes.js.map +1 -1
  148. package/dist/utils/private/validate-version.d.ts +2 -0
  149. package/dist/utils/private/validate-version.d.ts.map +1 -0
  150. package/dist/utils/private/validate-version.js +27 -0
  151. package/dist/utils/private/validate-version.js.map +1 -0
  152. package/jest.config.interop.mjs +24 -0
  153. package/package.json +16 -15
  154. package/tsconfig.interop.json +9 -0
  155. package/dist/constants/private/format.d.ts +0 -2
  156. package/dist/constants/private/format.d.ts.map +0 -1
  157. package/dist/constants/private/format.js +0 -5
  158. package/dist/constants/private/format.js.map +0 -1
  159. package/dist/types/private/aes-string.d.ts +0 -21
  160. package/dist/types/private/aes-string.d.ts.map +0 -1
  161. package/dist/types/private/aes-string.js.map +0 -1
  162. package/dist/utils/private/key-derivation/hkdf.d.ts +0 -13
  163. package/dist/utils/private/key-derivation/hkdf.d.ts.map +0 -1
  164. package/dist/utils/private/key-derivation/hkdf.js +0 -12
  165. 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
- A comprehensive TypeScript library for AES encryption and decryption with support for multiple key algorithms, encryption modes, and flexible output formats.
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
- ## Features
12
-
13
- - **Multiple Key Algorithms**: Support for EC, OKP, RSA, and symmetric keys
14
- - **Key Derivation**: HKDF, PBKDF2, and direct key usage
15
- - **Key Wrapping**: ECB and GCM key wrapping modes
16
- - **Multiple Encryption Modes**: CBC-HMAC and GCM authenticated encryption
17
- - **Flexible Output Formats**: Encoded strings, structured records, serialized objects, and tokenized strings
18
- - **Type Safety**: Full TypeScript support with comprehensive type definitions
19
- - **Content Type Detection**: Automatic detection and handling of different data types
20
- - **Verification Methods**: Built-in verification and assertion utilities
21
-
22
- ## Supported Algorithms
23
-
24
- ### Key Algorithms
25
- - **ECDH-ES**: Elliptic Curve Diffie-Hellman Ephemeral Static
26
- - **ECDH-ES+A128KW**: ECDH-ES with AES-128 Key Wrap
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
- ### Encryption Modes
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
- The AES kit supports four different output formats:
75
+ ### Encrypt any content type
60
76
 
61
- #### 1. Encoded (Default)
62
- Returns a base64-encoded string - simplest format for storage/transmission:
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
- ```typescript
65
- const encrypted = aesKit.encrypt('Hello World'); // Returns base64 string
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
- #### 2. Record
70
- Returns a structured object with Buffer values - useful for direct manipulation:
88
+ ### Verify and assert
71
89
 
72
- ```typescript
73
- const encrypted = aesKit.encrypt('Hello World', 'record');
74
- // Returns: AesEncryptionRecord with Buffer properties
90
+ ```ts
91
+ const cipher = aes.encrypt("secret");
75
92
 
76
- console.log(encrypted.keyId); // Key identifier
77
- console.log(encrypted.algorithm); // 'ECDH-ES'
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
- const decrypted = aesKit.decrypt(encrypted); // 'Hello World'
96
+ aes.assert("secret", cipher); // void — passes silently
97
+ aes.assert("wrong", cipher); // throws AesError("Invalid AES cipher")
82
98
  ```
83
99
 
84
- #### 3. Serialised
85
- Returns a structured object with string values - JSON-serializable:
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
- ```typescript
88
- const encrypted = aesKit.encrypt('Hello World', 'serialised');
89
- // Returns: SerialisedAesEncryption with base64 string properties
106
+ ```ts
107
+ const aad = Buffer.from("request-id:abc-123");
90
108
 
91
- const json = JSON.stringify(encrypted); // Can be serialized
92
- const parsed = JSON.parse(json);
93
- const decrypted = aesKit.decrypt(parsed); // 'Hello World'
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
- #### 4. Tokenised
97
- Returns a compact token format with embedded metadata:
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
- ```typescript
100
- const encrypted = aesKit.encrypt('Hello World', 'tokenised');
101
- // Returns: '$A256GCM$v=1&alg=ECDH-ES&...$base64content$'
148
+ ### `new AesKit(options)`
102
149
 
103
- const decrypted = aesKit.decrypt(encrypted); // 'Hello World'
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
- ### Content Types
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
- The library automatically handles different content types:
162
+ ```ts
163
+ const imported = KryptosKit.from.jwk(externalJwk);
164
+ const aes = new AesKit({ kryptos: imported, encryption: "A128GCM" });
165
+ ```
109
166
 
110
- ```typescript
111
- // String content
112
- const text = aesKit.encrypt('Hello World');
167
+ ### `aes.encrypt(data, mode?, options?)`
113
168
 
114
- // JSON objects
115
- const obj = aesKit.encrypt({ message: 'Hello', count: 42 });
169
+ Encrypts data and returns one of four formats depending on `mode`:
116
170
 
117
- // Arrays
118
- const arr = aesKit.encrypt([1, 2, 3]);
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
- // Buffers
121
- const buf = aesKit.encrypt(Buffer.from('binary data'));
185
+ ### `aes.decrypt<T>(data, options?)`
122
186
 
123
- // Numbers
124
- const num = aesKit.encrypt(42);
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
- ### Verification and Assertion
196
+ Format is auto-detected: encoded string, tokenised string, record object, or
197
+ serialised object all work transparently.
128
198
 
129
- ```typescript
130
- const encrypted = aesKit.encrypt('Hello World');
199
+ ### `aes.verify(input, data, options?)`
131
200
 
132
- // Verify without throwing
133
- const isValid = aesKit.verify('Hello World', encrypted); // true
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
- // Assert with exception on failure
137
- aesKit.assert('Hello World', encrypted); // Passes
138
- aesKit.assert('Wrong data', encrypted); // Throws AesError
204
+ ```ts
205
+ verify(
206
+ input: AesContent,
207
+ data: AesDecryptionRecord | SerialisedAesDecryption | string,
208
+ options?: AesOperationOptions,
209
+ ): boolean;
139
210
  ```
140
211
 
141
- ## Advanced Usage
212
+ ### `aes.assert(input, data, options?)`
142
213
 
143
- ### Custom Encryption Configuration
214
+ Throws `AesError("Invalid AES cipher")` if decrypted data does not match
215
+ `input`.
144
216
 
145
- ```typescript
146
- import { AesKit } from '@lindorm/aes';
147
- import { KryptosKit } from '@lindorm/kryptos';
217
+ ```ts
218
+ assert(
219
+ input: AesContent,
220
+ data: AesDecryptionRecord | SerialisedAesDecryption | string,
221
+ options?: AesOperationOptions,
222
+ ): void;
223
+ ```
148
224
 
149
- // RSA with OAEP padding
150
- const rsaKey = KryptosKit.generate.rsa({
151
- algorithm: 'RSA-OAEP-256',
152
- modulusLength: 2048
153
- });
225
+ ### `aes.prepareEncryption()`
154
226
 
155
- const aesKit = new AesKit({
156
- kryptos: rsaKey,
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
- // PBKDF2-based key derivation
161
- const pbkdfKey = KryptosKit.generate.oct({
162
- algorithm: 'PBES2-HS256+A128KW',
163
- length: 256
164
- });
230
+ ```ts
231
+ const prepared = aes.prepareEncryption();
165
232
 
166
- const pbkdfAes = new AesKit({
167
- kryptos: pbkdfKey,
168
- encryption: 'A128CBC-HS256'
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
- ### Working with Different Key Types
173
-
174
- ```typescript
175
- // Elliptic Curve keys
176
- const ecKey = KryptosKit.generate.ec({
177
- algorithm: 'ECDH-ES+A128GCMKW',
178
- curve: 'P-256'
179
- });
180
-
181
- // Edwards Curve keys (OKP)
182
- const okpKey = KryptosKit.generate.okp({
183
- algorithm: 'ECDH-ES',
184
- curve: 'X25519'
185
- });
186
-
187
- // Symmetric keys
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
- ### Utility Functions
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
- ```typescript
197
- import { parseAes, isAesTokenised } from '@lindorm/aes';
282
+ ### Encoded
198
283
 
199
- // Parse any AES format to standard record
200
- const record = parseAes(encryptedData); // Works with any format
284
+ A single base64url string wrapping a binary layout:
201
285
 
202
- // Check if string is tokenised format
203
- const isToken = isAesTokenised('$A256GCM$v=1&alg=ECDH-ES$...$'); // true
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
- ## Type Definitions
290
+ IV and tag sizes are derived from the encryption algorithm (e.g. 12B IV + 16B
291
+ tag for GCM).
208
292
 
209
- ### Core Types
293
+ ### Serialised
210
294
 
211
- ```typescript
212
- interface AesKitOptions {
213
- kryptos: IKryptos; // Cryptographic key instance
214
- encryption?: KryptosEncryption; // Encryption mode (defaults to A256GCM)
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 AesEncryptionMode = 'encoded' | 'record' | 'serialised' | 'tokenised';
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
- ### Record Types
223
-
224
- ```typescript
225
- interface AesEncryptionRecord {
226
- algorithm: KryptosAlgorithm; // Key algorithm used
227
- authTag: Buffer; // Authentication tag
228
- content: Buffer; // Encrypted content
229
- contentType: AesContentType; // Original content type
230
- encryption: KryptosEncryption; // Encryption mode
231
- hkdfSalt?: Buffer; // HKDF salt (if applicable)
232
- initialisationVector: Buffer; // IV for encryption
233
- keyId: string; // Key identifier
234
- pbkdfIterations?: number; // PBKDF2 iterations (if applicable)
235
- pbkdfSalt?: Buffer; // PBKDF2 salt (if applicable)
236
- publicEncryptionJwk?: PublicEncryptionJwk; // Public key (for ECDH)
237
- version: number; // Format version
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
- interface SerialisedAesEncryption {
241
- // Same as AesEncryptionRecord but with base64 strings instead of Buffers
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
- ## Error Handling
417
+ ---
246
418
 
247
- ```typescript
248
- import { AesError } from '@lindorm/aes';
419
+ ## Error handling
420
+
421
+ ```ts
422
+ import { AesError } from "@lindorm/aes";
249
423
 
250
424
  try {
251
- const decrypted = aesKit.decrypt(invalidData);
425
+ aes.decrypt(corruptedData);
252
426
  } catch (error) {
253
427
  if (error instanceof AesError) {
254
- console.log('AES operation failed:', error.message);
428
+ console.error("AES operation failed:", error.message);
255
429
  }
256
430
  }
257
431
  ```
258
432
 
259
- ## Integration with Kryptos
433
+ `AesError` extends `LindormError` from `@lindorm/errors`.
260
434
 
261
- The AES library integrates seamlessly with `@lindorm/kryptos` for key management:
435
+ ---
262
436
 
263
- ```typescript
264
- import { AesKit } from '@lindorm/aes';
265
- import { Amphora } from '@lindorm/amphora';
437
+ ## Testing helpers
266
438
 
267
- // Use with key storage
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
- // Find encryption key
272
- const kryptos = await amphora.find({ use: 'enc', algorithm: 'ECDH-ES' });
441
+ ```ts
442
+ import { createMockAesKit } from "@lindorm/aes";
273
443
 
274
- const aesKit = new AesKit({ kryptos });
444
+ const mock = createMockAesKit();
445
+ // mock.encrypt, mock.decrypt, mock.verify, mock.assert — all jest.fn()
275
446
  ```
276
447
 
277
- ## Performance Considerations
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