@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/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
- readonly AesGcmTagSize: 16;
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
- readonly Pbkdf2IterationsDefault: 600000;
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 encryption options: nonce size, tag size, and optional AAD.
74
- * Mutable builder-style; call {@link build} to validate.
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
- private _nonceSize;
78
- private _tagSize;
79
- /** Optional Additional Authenticated Data (not encrypted). */
80
- associatedData?: Uint8Array;
81
- /** Nonce size in bytes (must be 12). */
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
- * Fluent setter for {@link associatedData}.
95
- * Accepts a byte array or a UTF-8 string (encoded internally).
95
+ * V1 preset: nonce=12, tag=16, no AAD.
96
+ * These exact values are frozen for all V1-encrypted payloads.
96
97
  */
97
- withAssociatedData(aad: Uint8Array | string | undefined): this;
98
- /** Validates and returns this instance. */
99
- build(): AesGcmOptions;
98
+ static readonly V1: AesGcmOptions;
100
99
  }
101
100
 
102
101
  /**
103
- * PBKDF2 key derivation options: iterations count and hash algorithm.
104
- * Mutable builder-style; call {@link build} to validate.
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
- private _pbkdf2Iterations;
108
- private _hashAlgorithm;
109
- /** PBKDF2 iteration count (minimum 100_000). */
110
- get pbkdf2Iterations(): number;
111
- set pbkdf2Iterations(v: number);
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
- /** Default preset: SHA-256, 600_000 iterations. */
118
- static get default(): KdfOptions;
119
- /** Fluent setter for {@link pbkdf2Iterations}. */
120
- withPbkdf2Iterations(i: number): this;
121
- /** Fluent setter for {@link hashAlgorithm}. */
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
- AesGcmTagSize: 16,
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
- // 256 bits
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 AesGcmOptions = class _AesGcmOptions {
112
- constructor() {
113
- this._nonceSize = SecurityConstants.AesGcmNonceSize;
114
- this._tagSize = SecurityConstants.AesGcmTagSize;
115
- }
116
- /** Nonce size in bytes (must be 12). */
117
- get nonceSize() {
118
- return this._nonceSize;
119
- }
120
- set nonceSize(v) {
121
- if (v !== SecurityConstants.AesGcmNonceSize) throw new RangeError(`AES-GCM requires ${SecurityConstants.AesGcmNonceSize}-byte nonce`);
122
- this._nonceSize = v;
123
- }
124
- /** Tag size in bytes (12–16, default 16). */
125
- get tagSize() {
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) throw new Error(`Invalid Tag Size: ${this.tagSize}`);
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 KdfOptions = class _KdfOptions {
181
- constructor() {
182
- this._pbkdf2Iterations = SecurityConstants.Pbkdf2IterationsDefault;
183
- this._hashAlgorithm = "SHA-256";
184
- }
185
- /** PBKDF2 iteration count (minimum 100_000). */
186
- get pbkdf2Iterations() {
187
- return this._pbkdf2Iterations;
188
- }
189
- set pbkdf2Iterations(v) {
190
- if (v < SecurityConstants.Pbkdf2IterationsMinimum)
191
- throw new RangeError(`PBKDF2 iterations must be \u2265 ${SecurityConstants.Pbkdf2IterationsMinimum}`);
192
- this._pbkdf2Iterations = v;
193
- }
194
- /** Hash algorithm used by PBKDF2. Must be one of {@link SupportedHashAlgorithms}. */
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.default,
239
- aesGcmOptions: AesGcmOptions.default
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