@crossdyne/security 0.5.0-beta.2 → 1.0.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/README.md +358 -8
- package/dist/index.cjs +57 -100
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -41
- package/dist/index.d.ts +37 -41
- package/dist/index.js +57 -100
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
type HashAlgorithm = 'SHA-256' | 'SHA-384' | 'SHA-512';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Algorithmic constraints that are physically immutable.
|
|
5
|
+
* These do not change between crypto versions.
|
|
6
|
+
*/
|
|
3
7
|
declare const SecurityConstants: {
|
|
8
|
+
/** Standard nonce size for AES-GCM (96 bits). Fixed by NIST SP 800-38D. */
|
|
4
9
|
readonly AesGcmNonceSize: 12;
|
|
5
|
-
|
|
10
|
+
/** Minimum allowed authentication tag size (96 bits). */
|
|
6
11
|
readonly AesGcmTagSizeMin: 12;
|
|
12
|
+
/** Maximum allowed authentication tag size (128 bits). */
|
|
7
13
|
readonly AesGcmTagSizeMax: 16;
|
|
14
|
+
/** Key size for AES-256 (256 bits). */
|
|
8
15
|
readonly KeySizeBytes: 32;
|
|
9
|
-
|
|
16
|
+
/** Absolute minimum PBKDF2 iterations for any profile version. */
|
|
10
17
|
readonly Pbkdf2IterationsMinimum: 100000;
|
|
11
18
|
};
|
|
12
19
|
|
|
@@ -70,58 +77,47 @@ declare class KeyDerivationService {
|
|
|
70
77
|
}
|
|
71
78
|
|
|
72
79
|
/**
|
|
73
|
-
* AES-GCM
|
|
74
|
-
*
|
|
80
|
+
* Immutable AES-GCM configuration. All parameters are validated at creation.
|
|
81
|
+
*
|
|
82
|
+
* @remarks
|
|
83
|
+
* Do not construct manually. Use versioned presets such as {@link AesGcmOptions.V1}
|
|
84
|
+
* or the {@link AesGcmOptions.create} factory for custom (non-standard) configurations.
|
|
75
85
|
*/
|
|
76
86
|
declare class AesGcmOptions {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
get nonceSize(): number;
|
|
83
|
-
set nonceSize(v: number);
|
|
84
|
-
/** Tag size in bytes (12–16, default 16). */
|
|
85
|
-
get tagSize(): number;
|
|
86
|
-
set tagSize(v: number);
|
|
87
|
+
/** Nonce size in bytes. Must equal 12 (NIST SP 800-38D). */
|
|
88
|
+
readonly nonceSize: number;
|
|
89
|
+
/** Authentication tag size in bytes. Allowed range: 12–16. */
|
|
90
|
+
readonly tagSize: number;
|
|
91
|
+
private constructor();
|
|
87
92
|
/** Validates that {@link tagSize} is in the allowed range. */
|
|
88
93
|
validate(): void;
|
|
89
|
-
/** Default preset: nonce=12, tag=16, no AAD. */
|
|
90
|
-
static get default(): AesGcmOptions;
|
|
91
|
-
/** Fluent setter for {@link tagSize}. */
|
|
92
|
-
withTagSize(s: number): this;
|
|
93
94
|
/**
|
|
94
|
-
*
|
|
95
|
-
*
|
|
95
|
+
* V1 preset: nonce=12, tag=16, no AAD.
|
|
96
|
+
* These exact values are frozen for all V1-encrypted payloads.
|
|
96
97
|
*/
|
|
97
|
-
|
|
98
|
-
/** Validates and returns this instance. */
|
|
99
|
-
build(): AesGcmOptions;
|
|
98
|
+
static readonly V1: AesGcmOptions;
|
|
100
99
|
}
|
|
101
100
|
|
|
102
101
|
/**
|
|
103
|
-
* PBKDF2
|
|
104
|
-
*
|
|
102
|
+
* Immutable PBKDF2/HKDF configuration. All parameters are validated at creation.
|
|
103
|
+
*
|
|
104
|
+
* @remarks
|
|
105
|
+
* Do not construct manually. Use versioned presets such as {@link KdfOptions.V1}
|
|
106
|
+
* or the {@link KdfOptions.create} factory for custom (non-standard) configurations.
|
|
105
107
|
*/
|
|
106
108
|
declare class KdfOptions {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
/** PBKDF2
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
/** Hash algorithm used by PBKDF2. Must be one of {@link SupportedHashAlgorithms}. */
|
|
113
|
-
get hashAlgorithm(): HashAlgorithm;
|
|
114
|
-
set hashAlgorithm(v: HashAlgorithm);
|
|
109
|
+
/** PBKDF2 iteration count. Must be at least 100_000. */
|
|
110
|
+
readonly pbkdf2Iterations: number;
|
|
111
|
+
/** Hash algorithm used by PBKDF2 and HKDF. Supported: SHA-256, SHA-384, SHA-512. */
|
|
112
|
+
readonly hashAlgorithm: HashAlgorithm;
|
|
113
|
+
private constructor();
|
|
115
114
|
/** Validates iterations and hash algorithm. */
|
|
116
115
|
validate(): void;
|
|
117
|
-
/**
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
withHashAlgorithm(h: HashAlgorithm): this;
|
|
123
|
-
/** Validates and returns this instance. */
|
|
124
|
-
build(): KdfOptions;
|
|
116
|
+
/**
|
|
117
|
+
* V1 preset: SHA-256, 600_000 iterations.
|
|
118
|
+
* These exact values are frozen for all V1-derived keys.
|
|
119
|
+
*/
|
|
120
|
+
static readonly V1: KdfOptions;
|
|
125
121
|
}
|
|
126
122
|
|
|
127
123
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
// src/configurations/security-constants.ts
|
|
2
2
|
var SecurityConstants = {
|
|
3
|
+
/** Standard nonce size for AES-GCM (96 bits). Fixed by NIST SP 800-38D. */
|
|
3
4
|
AesGcmNonceSize: 12,
|
|
4
|
-
|
|
5
|
+
/** Minimum allowed authentication tag size (96 bits). */
|
|
5
6
|
AesGcmTagSizeMin: 12,
|
|
7
|
+
/** Maximum allowed authentication tag size (128 bits). */
|
|
6
8
|
AesGcmTagSizeMax: 16,
|
|
9
|
+
/** Key size for AES-256 (256 bits). */
|
|
7
10
|
KeySizeBytes: 32,
|
|
8
|
-
|
|
9
|
-
Pbkdf2IterationsDefault: 6e5,
|
|
11
|
+
/** Absolute minimum PBKDF2 iterations for any profile version. */
|
|
10
12
|
Pbkdf2IterationsMinimum: 1e5
|
|
11
13
|
};
|
|
12
14
|
var HashSizes = {
|
|
@@ -108,54 +110,36 @@ var SecurityUtils = class {
|
|
|
108
110
|
};
|
|
109
111
|
|
|
110
112
|
// src/crypto/aes-gcm-options.ts
|
|
111
|
-
var
|
|
112
|
-
constructor() {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return this._tagSize;
|
|
127
|
-
}
|
|
128
|
-
set tagSize(v) {
|
|
129
|
-
if (v < SecurityConstants.AesGcmTagSizeMin || v > SecurityConstants.AesGcmTagSizeMax) throw new RangeError(`Tag size must be between ${SecurityConstants.AesGcmTagSizeMin} and ${SecurityConstants.AesGcmTagSizeMax}`);
|
|
130
|
-
this._tagSize = v;
|
|
113
|
+
var _AesGcmOptions = class _AesGcmOptions {
|
|
114
|
+
constructor(nonceSize, tagSize) {
|
|
115
|
+
if (nonceSize !== SecurityConstants.AesGcmNonceSize) {
|
|
116
|
+
throw new RangeError(
|
|
117
|
+
`AES-GCM requires exactly ${SecurityConstants.AesGcmNonceSize}-byte nonce per NIST SP 800-38D.`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
if (tagSize < SecurityConstants.AesGcmTagSizeMin || tagSize > SecurityConstants.AesGcmTagSizeMax) {
|
|
121
|
+
throw new RangeError(
|
|
122
|
+
`Tag size must be between ${SecurityConstants.AesGcmTagSizeMin} and ${SecurityConstants.AesGcmTagSizeMax} bytes.`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
this.nonceSize = nonceSize;
|
|
126
|
+
this.tagSize = tagSize;
|
|
127
|
+
Object.freeze(this);
|
|
131
128
|
}
|
|
132
129
|
/** Validates that {@link tagSize} is in the allowed range. */
|
|
133
130
|
validate() {
|
|
134
|
-
if (this.tagSize < SecurityConstants.AesGcmTagSizeMin || this.tagSize > SecurityConstants.AesGcmTagSizeMax)
|
|
135
|
-
|
|
136
|
-
/** Default preset: nonce=12, tag=16, no AAD. */
|
|
137
|
-
static get default() {
|
|
138
|
-
return new _AesGcmOptions();
|
|
139
|
-
}
|
|
140
|
-
/** Fluent setter for {@link tagSize}. */
|
|
141
|
-
withTagSize(s) {
|
|
142
|
-
this.tagSize = s;
|
|
143
|
-
return this;
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Fluent setter for {@link associatedData}.
|
|
147
|
-
* Accepts a byte array or a UTF-8 string (encoded internally).
|
|
148
|
-
*/
|
|
149
|
-
withAssociatedData(aad) {
|
|
150
|
-
this.associatedData = typeof aad === "string" ? new TextEncoder().encode(aad) : aad;
|
|
151
|
-
return this;
|
|
152
|
-
}
|
|
153
|
-
/** Validates and returns this instance. */
|
|
154
|
-
build() {
|
|
155
|
-
this.validate();
|
|
156
|
-
return this;
|
|
131
|
+
if (this.tagSize < SecurityConstants.AesGcmTagSizeMin || this.tagSize > SecurityConstants.AesGcmTagSizeMax)
|
|
132
|
+
throw new Error(`Invalid Tag Size: ${this.tagSize}`);
|
|
157
133
|
}
|
|
158
134
|
};
|
|
135
|
+
/**
|
|
136
|
+
* V1 preset: nonce=12, tag=16, no AAD.
|
|
137
|
+
* These exact values are frozen for all V1-encrypted payloads.
|
|
138
|
+
*/
|
|
139
|
+
_AesGcmOptions.V1 = Object.freeze(
|
|
140
|
+
new _AesGcmOptions(12, 16)
|
|
141
|
+
);
|
|
142
|
+
var AesGcmOptions = _AesGcmOptions;
|
|
159
143
|
|
|
160
144
|
// src/crypto/crypto-profile.ts
|
|
161
145
|
var CryptoProfile = class {
|
|
@@ -177,28 +161,21 @@ var CryptoVersion = /* @__PURE__ */ ((CryptoVersion2) => {
|
|
|
177
161
|
})(CryptoVersion || {});
|
|
178
162
|
|
|
179
163
|
// src/crypto/kdf-options.ts
|
|
180
|
-
var
|
|
181
|
-
constructor() {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
this.
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
get hashAlgorithm() {
|
|
196
|
-
return this._hashAlgorithm;
|
|
197
|
-
}
|
|
198
|
-
set hashAlgorithm(v) {
|
|
199
|
-
if (!SupportedHashAlgorithms.includes(v))
|
|
200
|
-
throw new RangeError(`Unsupported hash: ${v}`);
|
|
201
|
-
this._hashAlgorithm = v;
|
|
164
|
+
var _KdfOptions = class _KdfOptions {
|
|
165
|
+
constructor(pbkdf2Iterations, hashAlgorithm) {
|
|
166
|
+
if (pbkdf2Iterations < SecurityConstants.Pbkdf2IterationsMinimum) {
|
|
167
|
+
throw new RangeError(
|
|
168
|
+
`PBKDF2 iterations must be at least ${SecurityConstants.Pbkdf2IterationsMinimum}.`
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
if (!SupportedHashAlgorithms.includes(hashAlgorithm)) {
|
|
172
|
+
throw new RangeError(
|
|
173
|
+
`Unsupported hash algorithm: ${hashAlgorithm}. Supported: ${SupportedHashAlgorithms.join(", ")}.`
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
this.pbkdf2Iterations = pbkdf2Iterations;
|
|
177
|
+
this.hashAlgorithm = hashAlgorithm;
|
|
178
|
+
Object.freeze(this);
|
|
202
179
|
}
|
|
203
180
|
/** Validates iterations and hash algorithm. */
|
|
204
181
|
validate() {
|
|
@@ -207,26 +184,15 @@ var KdfOptions = class _KdfOptions {
|
|
|
207
184
|
if (!SupportedHashAlgorithms.includes(this.hashAlgorithm))
|
|
208
185
|
throw new Error(`Invalid hash algorithm: ${this.hashAlgorithm}`);
|
|
209
186
|
}
|
|
210
|
-
/** Default preset: SHA-256, 600_000 iterations. */
|
|
211
|
-
static get default() {
|
|
212
|
-
return new _KdfOptions();
|
|
213
|
-
}
|
|
214
|
-
/** Fluent setter for {@link pbkdf2Iterations}. */
|
|
215
|
-
withPbkdf2Iterations(i) {
|
|
216
|
-
this.pbkdf2Iterations = i;
|
|
217
|
-
return this;
|
|
218
|
-
}
|
|
219
|
-
/** Fluent setter for {@link hashAlgorithm}. */
|
|
220
|
-
withHashAlgorithm(h) {
|
|
221
|
-
this.hashAlgorithm = h;
|
|
222
|
-
return this;
|
|
223
|
-
}
|
|
224
|
-
/** Validates and returns this instance. */
|
|
225
|
-
build() {
|
|
226
|
-
this.validate();
|
|
227
|
-
return this;
|
|
228
|
-
}
|
|
229
187
|
};
|
|
188
|
+
/**
|
|
189
|
+
* V1 preset: SHA-256, 600_000 iterations.
|
|
190
|
+
* These exact values are frozen for all V1-derived keys.
|
|
191
|
+
*/
|
|
192
|
+
_KdfOptions.V1 = Object.freeze(
|
|
193
|
+
new _KdfOptions(6e5, "SHA-256")
|
|
194
|
+
);
|
|
195
|
+
var KdfOptions = _KdfOptions;
|
|
230
196
|
|
|
231
197
|
// src/crypto/crypto-profile-registry.ts
|
|
232
198
|
var CryptoProfileRegistry = class {
|
|
@@ -235,8 +201,8 @@ var CryptoProfileRegistry = class {
|
|
|
235
201
|
case 1 /* V1 */:
|
|
236
202
|
return new CryptoProfile({
|
|
237
203
|
version: 1 /* V1 */,
|
|
238
|
-
kdfOptions: KdfOptions.
|
|
239
|
-
aesGcmOptions: AesGcmOptions.
|
|
204
|
+
kdfOptions: KdfOptions.V1,
|
|
205
|
+
aesGcmOptions: AesGcmOptions.V1
|
|
240
206
|
});
|
|
241
207
|
default:
|
|
242
208
|
throw new Error(`Unsupported crypto version: ${version}`);
|
|
@@ -284,16 +250,11 @@ var CryptoService = class {
|
|
|
284
250
|
false,
|
|
285
251
|
["encrypt"]
|
|
286
252
|
);
|
|
287
|
-
let associatedData = new Uint8Array(0);
|
|
288
|
-
if (opts.associatedData != null) {
|
|
289
|
-
associatedData = opts.associatedData;
|
|
290
|
-
}
|
|
291
253
|
const encryptedContent = await crypto.subtle.encrypt(
|
|
292
254
|
{
|
|
293
255
|
name: "AES-GCM",
|
|
294
256
|
iv: nonce,
|
|
295
|
-
tagLength: opts.tagSize * 8
|
|
296
|
-
additionalData: associatedData
|
|
257
|
+
tagLength: opts.tagSize * 8
|
|
297
258
|
},
|
|
298
259
|
cryptoKey,
|
|
299
260
|
plainBytes
|
|
@@ -332,15 +293,11 @@ var CryptoService = class {
|
|
|
332
293
|
["decrypt"]
|
|
333
294
|
);
|
|
334
295
|
try {
|
|
335
|
-
let associatedData = new Uint8Array(0);
|
|
336
|
-
if (opts.associatedData != null)
|
|
337
|
-
associatedData = opts.associatedData;
|
|
338
296
|
const decryptedBuffer = await crypto.subtle.decrypt(
|
|
339
297
|
{
|
|
340
298
|
name: "AES-GCM",
|
|
341
299
|
iv: nonce,
|
|
342
|
-
tagLength: opts.tagSize * 8
|
|
343
|
-
additionalData: associatedData
|
|
300
|
+
tagLength: opts.tagSize * 8
|
|
344
301
|
},
|
|
345
302
|
cryptoKey,
|
|
346
303
|
ciphertextWithTag
|