@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 CHANGED
@@ -1,15 +1,365 @@
1
- # @quantropic/security
1
+ # Crossdyne.Security
2
2
 
3
- Security library with SRP authentication and cryptography utilities.
3
+ > Cross-platform cryptographic library — [.NET](https://github.com/crossdyne/dotnet-security) | [TypeScript](https://github.com/crossdyne/typescript-security)
4
+ >
5
+ > [English](#english) | [Русский](#русский)
4
6
 
5
- ## Features
7
+ ---
6
8
 
7
- - SRP (Secure Remote Password) protocol implementation
8
- - Cryptographic utilities
9
- - Key derivation functions
9
+ <a name="english"></a>
10
+ ## English
11
+
12
+ TypeScript / JavaScript implementation of the Crossdyne.Security cryptographic library via the native **Web Crypto API**.
10
13
 
11
14
  ## Installation
12
15
 
13
16
  ```bash
14
- npm install @quantropic/security
15
- ```
17
+ npm install @crossdyne/security
18
+ ```
19
+
20
+ ### Features
21
+
22
+ - **AES-256-GCM** — authenticated symmetric encryption via `crypto.subtle`
23
+ - **PBKDF2 + HKDF** — secure key derivation from passwords
24
+ - **SRP-6a** — password-authenticated key exchange without sending the password to the server
25
+ - **Zero-memory** — sensitive buffers are explicitly overwritten after use
26
+ - **No custom crypto primitives** — relies entirely on the browser / Node.js Web Crypto API
27
+ - **Cross-platform** — compatible with the [.NET implementation](https://github.com/crossdyne/dotnet-security). Encrypted payloads and SRP messages are interchangeable between TS and .NET.
28
+
29
+ ### Requirements
30
+
31
+ - TypeScript 5.3+
32
+ - Web Crypto API (`crypto.subtle`) — modern browsers and Node.js 18+
33
+
34
+ ### Project Structure
35
+
36
+ ```
37
+ crossdyne-security/
38
+ ├── crypto/
39
+ │ ├── crypto-service.ts # AES-GCM encrypt / decrypt
40
+ │ ├── key-derivation-service.ts # Derive KEK and AuthHash (PBKDF2 → HKDF)
41
+ │ ├── crypto-profile-registry.ts # Versioned crypto profiles
42
+ │ ├── crypto-version.ts # Enum: V1, V2, ...
43
+ │ └── kdf-options.ts # PBKDF2 / HKDF parameters
44
+ ├── srp/
45
+ │ ├── srp-server-service.ts # SRP server (challenge, verify)
46
+ │ ├── srp-client-service.ts # SRP client (proof, verifier)
47
+ │ ├── srp-key-derivation-service.ts # Derive auth-hash for SRP
48
+ │ ├── srp-context-factory.ts # Creates SRP context (N, g, k, hash)
49
+ │ ├── srp-group.ts # Enum: Group2048, Group4096, ...
50
+ │ └── srp-encoding.ts # BigInt ↔ bytes helpers
51
+ └── utils/
52
+ ├── security-utils.ts # Base64, BigInt, fixed-time compare
53
+ └── srp-encoding.ts # Hash moduli, session key, M1 / M2
54
+ ```
55
+
56
+ ### Quick Start
57
+
58
+ #### Encrypt / Decrypt
59
+
60
+ ```typescript
61
+ import { CryptoService } from './crypto/crypto-service.js';
62
+ import { CryptoVersion } from './crypto/crypto-version.js';
63
+
64
+ const crypto = new CryptoService();
65
+ const key = crypto.generateRandomBytes(32); // AES-256
66
+
67
+ const encrypted = await crypto.encryptData(
68
+ { message: "Hello, World!" },
69
+ key,
70
+ CryptoVersion.V1
71
+ );
72
+
73
+ const decrypted = await crypto.decryptData<MyData>(encrypted, key);
74
+ ```
75
+
76
+ Encrypted payload format (Base64):
77
+
78
+ ```
79
+ [Version (1 byte)][Nonce (N bytes)][Ciphertext + Tag]
80
+ ```
81
+
82
+ #### Key Derivation from Password
83
+
84
+ ```typescript
85
+ import { KeyDerivationService } from './crypto/key-derivation-service.js';
86
+ import { CryptoVersion } from './crypto/crypto-version.js';
87
+
88
+ const kdf = new KeyDerivationService();
89
+ const salt = crypto.getRandomValues(new Uint8Array(16));
90
+
91
+ const { kek, authHash } = await kdf.deriveKeysFromPassword(
92
+ "user@example.com",
93
+ "SuperSecret123!",
94
+ salt,
95
+ CryptoVersion.V1
96
+ );
97
+ ```
98
+
99
+ - `kek` — `Uint8Array` key for AES-GCM
100
+ - `authHash` — Base64 string for SRP authentication
101
+
102
+ #### SRP Authentication Flow
103
+
104
+ **Server — generate challenge**
105
+
106
+ ```typescript
107
+ import { SrpServerService } from './srp/srp-server-service.js';
108
+ import { SrpGroup } from './srp/srp-group.js';
109
+
110
+ const server = new SrpServerService();
111
+
112
+ const session = await server.getSrpChallenge(
113
+ "user@example.com",
114
+ verifierBytes,
115
+ salt,
116
+ SrpGroup.Group2048
117
+ );
118
+
119
+ // Send to client: salt + session.publicKeyB (B)
120
+ ```
121
+
122
+ **Client — generate proof**
123
+
124
+ ```typescript
125
+ import { SrpClientService } from './srp/srp-client-service.js';
126
+ import { SrpKeyDerivationService } from './srp/srp-key-derivation-service.js';
127
+ import { SrpGroup } from './srp/srp-group.js';
128
+ import { CryptoVersion } from './crypto/crypto-version.js';
129
+
130
+ const client = new SrpClientService();
131
+ const kdf = new SrpKeyDerivationService();
132
+
133
+ // 1. Derive auth-hash from password
134
+ const authHash = await kdf.deriveAuthHashForSrp(
135
+ identity, password, salt, SrpGroup.Group2048, CryptoVersion.V1
136
+ );
137
+
138
+ // 2. Generate proof
139
+ const { A, M1, SessionKeyK } = await client.generateSrpProof(
140
+ identity,
141
+ authHash,
142
+ btoa(String.fromCharCode(...salt)),
143
+ btoa(String.fromCharCode(...session.publicKeyB)),
144
+ SrpGroup.Group2048
145
+ );
146
+
147
+ // Send to server: A + M1
148
+ ```
149
+
150
+ **Server — verify proof**
151
+
152
+ ```typescript
153
+ const serverM2 = await server.verifySrpProof(
154
+ session,
155
+ A,
156
+ M1,
157
+ SrpGroup.Group2048
158
+ );
159
+
160
+ // Send to client: serverM2
161
+ ```
162
+
163
+ **Client — verify server proof**
164
+
165
+ ```typescript
166
+ const isValid = await client.verifyServerM2(
167
+ A,
168
+ M1,
169
+ SessionKeyK,
170
+ serverM2,
171
+ SrpGroup.Group2048
172
+ );
173
+ ```
174
+
175
+ ### Security Notes
176
+
177
+ - All sensitive buffers are explicitly overwritten after use
178
+ - Salt must be **at least 16 bytes**
179
+ - AES-256 key must be **exactly 32 bytes**
180
+ - SRP groups are protected against small-subgroup attacks (checks `A % N != 0`, `B != 0`)
181
+ - Relies entirely on the native Web Crypto API — no custom crypto primitives
182
+
183
+ ### License
184
+
185
+ MIT
186
+
187
+ ---
188
+
189
+ <a name="русский"></a>
190
+ ## Русский
191
+
192
+ Реализация криптографической библиотеки Crossdyne.Security для TypeScript / JavaScript через нативный **Web Crypto API**.
193
+
194
+ ## Установка
195
+
196
+ ```bash
197
+ npm install @crossdyne/security
198
+ ```
199
+
200
+ ### Возможности
201
+
202
+ - **AES-256-GCM** — симметричное шифрование с аутентификацией через `crypto.subtle`
203
+ - **PBKDF2 + HKDF** — надёжный вывод ключей из пароля
204
+ - **SRP-6a** — протокол аутентификации без передачи пароля на сервер
205
+ - **Zero-memory** — чувствительные буферы явно перезаписываются после использования
206
+ - **Никаких самописных криптопримитивов** — только нативный Web Crypto API браузера / Node.js
207
+ - **Кроссплатформенность** — совместима с [.NET-реализацией](https://github.com/crossdyne/dotnet-security). Зашифрованные данные и SRP-сообщения взаимозаменяемы между TS и .NET.
208
+
209
+ ### Требования
210
+
211
+ - TypeScript 5.3+
212
+ - Web Crypto API (`crypto.subtle`) — современные браузеры и Node.js 18+
213
+
214
+ ### Структура проекта
215
+
216
+ ```
217
+ crossdyne-security/
218
+ ├── crypto/
219
+ │ ├── crypto-service.ts # AES-GCM шифрование / дешифрование
220
+ │ ├── key-derivation-service.ts # Вывод KEK и AuthHash (PBKDF2 → HKDF)
221
+ │ ├── crypto-profile-registry.ts # Версионированные криптопрофили
222
+ │ ├── crypto-version.ts # Enum: V1, V2, ...
223
+ │ └── kdf-options.ts # Параметры PBKDF2 / HKDF
224
+ ├── srp/
225
+ │ ├── srp-server-service.ts # SRP сервер (challenge, проверка)
226
+ │ ├── srp-client-service.ts # SRP клиент (proof, верификатор)
227
+ │ ├── srp-key-derivation-service.ts # Вывод auth-hash для SRP
228
+ │ ├── srp-context-factory.ts # Создание SRP-контекста (N, g, k, hash)
229
+ │ ├── srp-group.ts # Enum: Group2048, Group4096, ...
230
+ │ └── srp-encoding.ts # BigInt ↔ bytes хелперы
231
+ └── utils/
232
+ ├── security-utils.ts # Base64, BigInt, сравнение в постоянное время
233
+ └── srp-encoding.ts # Хеш модулей, сессионный ключ, M1 / M2
234
+ ```
235
+
236
+ ### Быстрый старт
237
+
238
+ #### Шифрование / дешифрование
239
+
240
+ ```typescript
241
+ import { CryptoService } from './crypto/crypto-service.js';
242
+ import { CryptoVersion } from './crypto/crypto-version.js';
243
+
244
+ const crypto = new CryptoService();
245
+ const key = crypto.generateRandomBytes(32); // AES-256
246
+
247
+ const encrypted = await crypto.encryptData(
248
+ { message: "Hello, World!" },
249
+ key,
250
+ CryptoVersion.V1
251
+ );
252
+
253
+ const decrypted = await crypto.decryptData<MyData>(encrypted, key);
254
+ ```
255
+
256
+ Формат зашифрованных данных (Base64):
257
+
258
+ ```
259
+ [Version (1 byte)][Nonce (N bytes)][Ciphertext + Tag]
260
+ ```
261
+
262
+ #### Вывод ключей из пароля
263
+
264
+ ```typescript
265
+ import { KeyDerivationService } from './crypto/key-derivation-service.js';
266
+ import { CryptoVersion } from './crypto/crypto-version.js';
267
+
268
+ const kdf = new KeyDerivationService();
269
+ const salt = crypto.getRandomValues(new Uint8Array(16));
270
+
271
+ const { kek, authHash } = await kdf.deriveKeysFromPassword(
272
+ "user@example.com",
273
+ "SuperSecret123!",
274
+ salt,
275
+ CryptoVersion.V1
276
+ );
277
+ ```
278
+
279
+ - `kek` — `Uint8Array` ключ для AES-GCM
280
+ - `authHash` — Base64-строка для SRP-аутентификации
281
+
282
+ #### SRP-аутентификация
283
+
284
+ **Сервер — генерация challenge**
285
+
286
+ ```typescript
287
+ import { SrpServerService } from './srp/srp-server-service.js';
288
+ import { SrpGroup } from './srp/srp-group.js';
289
+
290
+ const server = new SrpServerService();
291
+
292
+ const session = await server.getSrpChallenge(
293
+ "user@example.com",
294
+ verifierBytes,
295
+ salt,
296
+ SrpGroup.Group2048
297
+ );
298
+
299
+ // Отправить клиенту: salt + session.publicKeyB (B)
300
+ ```
301
+
302
+ **Клиент — генерация proof**
303
+
304
+ ```typescript
305
+ import { SrpClientService } from './srp/srp-client-service.js';
306
+ import { SrpKeyDerivationService } from './srp/srp-key-derivation-service.js';
307
+ import { SrpGroup } from './srp/srp-group.js';
308
+ import { CryptoVersion } from './crypto/crypto-version.js';
309
+
310
+ const client = new SrpClientService();
311
+ const kdf = new SrpKeyDerivationService();
312
+
313
+ // 1. Вывести auth-hash из пароля
314
+ const authHash = await kdf.deriveAuthHashForSrp(
315
+ identity, password, salt, SrpGroup.Group2048, CryptoVersion.V1
316
+ );
317
+
318
+ // 2. Сгенерировать proof
319
+ const { A, M1, SessionKeyK } = await client.generateSrpProof(
320
+ identity,
321
+ authHash,
322
+ btoa(String.fromCharCode(...salt)),
323
+ btoa(String.fromCharCode(...session.publicKeyB)),
324
+ SrpGroup.Group2048
325
+ );
326
+
327
+ // Отправить серверу: A + M1
328
+ ```
329
+
330
+ **Сервер — проверка proof**
331
+
332
+ ```typescript
333
+ const serverM2 = await server.verifySrpProof(
334
+ session,
335
+ A,
336
+ M1,
337
+ SrpGroup.Group2048
338
+ );
339
+
340
+ // Отправить клиенту: serverM2
341
+ ```
342
+
343
+ **Клиент — проверка server proof**
344
+
345
+ ```typescript
346
+ const isValid = await client.verifyServerM2(
347
+ A,
348
+ M1,
349
+ SessionKeyK,
350
+ serverM2,
351
+ SrpGroup.Group2048
352
+ );
353
+ ```
354
+
355
+ ### Безопасность
356
+
357
+ - Все чувствительные буферы явно перезаписываются после использования
358
+ - Соль должна быть **минимум 16 байт**
359
+ - Ключ AES-256 — **строго 32 байта**
360
+ - SRP-группы защищены от атак на малые подгруппы (проверки `A % N != 0`, `B != 0`)
361
+ - Полностью опирается на нативный Web Crypto API — никаких самописных криптопримитивов
362
+
363
+ ### Лицензия
364
+
365
+ MIT
package/dist/index.cjs CHANGED
@@ -2,13 +2,15 @@
2
2
 
3
3
  // src/configurations/security-constants.ts
4
4
  var SecurityConstants = {
5
+ /** Standard nonce size for AES-GCM (96 bits). Fixed by NIST SP 800-38D. */
5
6
  AesGcmNonceSize: 12,
6
- AesGcmTagSize: 16,
7
+ /** Minimum allowed authentication tag size (96 bits). */
7
8
  AesGcmTagSizeMin: 12,
9
+ /** Maximum allowed authentication tag size (128 bits). */
8
10
  AesGcmTagSizeMax: 16,
11
+ /** Key size for AES-256 (256 bits). */
9
12
  KeySizeBytes: 32,
10
- // 256 bits
11
- Pbkdf2IterationsDefault: 6e5,
13
+ /** Absolute minimum PBKDF2 iterations for any profile version. */
12
14
  Pbkdf2IterationsMinimum: 1e5
13
15
  };
14
16
  var HashSizes = {
@@ -110,54 +112,36 @@ var SecurityUtils = class {
110
112
  };
111
113
 
112
114
  // src/crypto/aes-gcm-options.ts
113
- var AesGcmOptions = class _AesGcmOptions {
114
- constructor() {
115
- this._nonceSize = SecurityConstants.AesGcmNonceSize;
116
- this._tagSize = SecurityConstants.AesGcmTagSize;
117
- }
118
- /** Nonce size in bytes (must be 12). */
119
- get nonceSize() {
120
- return this._nonceSize;
121
- }
122
- set nonceSize(v) {
123
- if (v !== SecurityConstants.AesGcmNonceSize) throw new RangeError(`AES-GCM requires ${SecurityConstants.AesGcmNonceSize}-byte nonce`);
124
- this._nonceSize = v;
125
- }
126
- /** Tag size in bytes (12–16, default 16). */
127
- get tagSize() {
128
- return this._tagSize;
129
- }
130
- set tagSize(v) {
131
- if (v < SecurityConstants.AesGcmTagSizeMin || v > SecurityConstants.AesGcmTagSizeMax) throw new RangeError(`Tag size must be between ${SecurityConstants.AesGcmTagSizeMin} and ${SecurityConstants.AesGcmTagSizeMax}`);
132
- this._tagSize = v;
115
+ var _AesGcmOptions = class _AesGcmOptions {
116
+ constructor(nonceSize, tagSize) {
117
+ if (nonceSize !== SecurityConstants.AesGcmNonceSize) {
118
+ throw new RangeError(
119
+ `AES-GCM requires exactly ${SecurityConstants.AesGcmNonceSize}-byte nonce per NIST SP 800-38D.`
120
+ );
121
+ }
122
+ if (tagSize < SecurityConstants.AesGcmTagSizeMin || tagSize > SecurityConstants.AesGcmTagSizeMax) {
123
+ throw new RangeError(
124
+ `Tag size must be between ${SecurityConstants.AesGcmTagSizeMin} and ${SecurityConstants.AesGcmTagSizeMax} bytes.`
125
+ );
126
+ }
127
+ this.nonceSize = nonceSize;
128
+ this.tagSize = tagSize;
129
+ Object.freeze(this);
133
130
  }
134
131
  /** Validates that {@link tagSize} is in the allowed range. */
135
132
  validate() {
136
- if (this.tagSize < SecurityConstants.AesGcmTagSizeMin || this.tagSize > SecurityConstants.AesGcmTagSizeMax) throw new Error(`Invalid Tag Size: ${this.tagSize}`);
137
- }
138
- /** Default preset: nonce=12, tag=16, no AAD. */
139
- static get default() {
140
- return new _AesGcmOptions();
141
- }
142
- /** Fluent setter for {@link tagSize}. */
143
- withTagSize(s) {
144
- this.tagSize = s;
145
- return this;
146
- }
147
- /**
148
- * Fluent setter for {@link associatedData}.
149
- * Accepts a byte array or a UTF-8 string (encoded internally).
150
- */
151
- withAssociatedData(aad) {
152
- this.associatedData = typeof aad === "string" ? new TextEncoder().encode(aad) : aad;
153
- return this;
154
- }
155
- /** Validates and returns this instance. */
156
- build() {
157
- this.validate();
158
- return this;
133
+ if (this.tagSize < SecurityConstants.AesGcmTagSizeMin || this.tagSize > SecurityConstants.AesGcmTagSizeMax)
134
+ throw new Error(`Invalid Tag Size: ${this.tagSize}`);
159
135
  }
160
136
  };
137
+ /**
138
+ * V1 preset: nonce=12, tag=16, no AAD.
139
+ * These exact values are frozen for all V1-encrypted payloads.
140
+ */
141
+ _AesGcmOptions.V1 = Object.freeze(
142
+ new _AesGcmOptions(12, 16)
143
+ );
144
+ var AesGcmOptions = _AesGcmOptions;
161
145
 
162
146
  // src/crypto/crypto-profile.ts
163
147
  var CryptoProfile = class {
@@ -179,28 +163,21 @@ var CryptoVersion = /* @__PURE__ */ ((CryptoVersion2) => {
179
163
  })(CryptoVersion || {});
180
164
 
181
165
  // src/crypto/kdf-options.ts
182
- var KdfOptions = class _KdfOptions {
183
- constructor() {
184
- this._pbkdf2Iterations = SecurityConstants.Pbkdf2IterationsDefault;
185
- this._hashAlgorithm = "SHA-256";
186
- }
187
- /** PBKDF2 iteration count (minimum 100_000). */
188
- get pbkdf2Iterations() {
189
- return this._pbkdf2Iterations;
190
- }
191
- set pbkdf2Iterations(v) {
192
- if (v < SecurityConstants.Pbkdf2IterationsMinimum)
193
- throw new RangeError(`PBKDF2 iterations must be \u2265 ${SecurityConstants.Pbkdf2IterationsMinimum}`);
194
- this._pbkdf2Iterations = v;
195
- }
196
- /** Hash algorithm used by PBKDF2. Must be one of {@link SupportedHashAlgorithms}. */
197
- get hashAlgorithm() {
198
- return this._hashAlgorithm;
199
- }
200
- set hashAlgorithm(v) {
201
- if (!SupportedHashAlgorithms.includes(v))
202
- throw new RangeError(`Unsupported hash: ${v}`);
203
- this._hashAlgorithm = v;
166
+ var _KdfOptions = class _KdfOptions {
167
+ constructor(pbkdf2Iterations, hashAlgorithm) {
168
+ if (pbkdf2Iterations < SecurityConstants.Pbkdf2IterationsMinimum) {
169
+ throw new RangeError(
170
+ `PBKDF2 iterations must be at least ${SecurityConstants.Pbkdf2IterationsMinimum}.`
171
+ );
172
+ }
173
+ if (!SupportedHashAlgorithms.includes(hashAlgorithm)) {
174
+ throw new RangeError(
175
+ `Unsupported hash algorithm: ${hashAlgorithm}. Supported: ${SupportedHashAlgorithms.join(", ")}.`
176
+ );
177
+ }
178
+ this.pbkdf2Iterations = pbkdf2Iterations;
179
+ this.hashAlgorithm = hashAlgorithm;
180
+ Object.freeze(this);
204
181
  }
205
182
  /** Validates iterations and hash algorithm. */
206
183
  validate() {
@@ -209,26 +186,15 @@ var KdfOptions = class _KdfOptions {
209
186
  if (!SupportedHashAlgorithms.includes(this.hashAlgorithm))
210
187
  throw new Error(`Invalid hash algorithm: ${this.hashAlgorithm}`);
211
188
  }
212
- /** Default preset: SHA-256, 600_000 iterations. */
213
- static get default() {
214
- return new _KdfOptions();
215
- }
216
- /** Fluent setter for {@link pbkdf2Iterations}. */
217
- withPbkdf2Iterations(i) {
218
- this.pbkdf2Iterations = i;
219
- return this;
220
- }
221
- /** Fluent setter for {@link hashAlgorithm}. */
222
- withHashAlgorithm(h) {
223
- this.hashAlgorithm = h;
224
- return this;
225
- }
226
- /** Validates and returns this instance. */
227
- build() {
228
- this.validate();
229
- return this;
230
- }
231
189
  };
190
+ /**
191
+ * V1 preset: SHA-256, 600_000 iterations.
192
+ * These exact values are frozen for all V1-derived keys.
193
+ */
194
+ _KdfOptions.V1 = Object.freeze(
195
+ new _KdfOptions(6e5, "SHA-256")
196
+ );
197
+ var KdfOptions = _KdfOptions;
232
198
 
233
199
  // src/crypto/crypto-profile-registry.ts
234
200
  var CryptoProfileRegistry = class {
@@ -237,8 +203,8 @@ var CryptoProfileRegistry = class {
237
203
  case 1 /* V1 */:
238
204
  return new CryptoProfile({
239
205
  version: 1 /* V1 */,
240
- kdfOptions: KdfOptions.default,
241
- aesGcmOptions: AesGcmOptions.default
206
+ kdfOptions: KdfOptions.V1,
207
+ aesGcmOptions: AesGcmOptions.V1
242
208
  });
243
209
  default:
244
210
  throw new Error(`Unsupported crypto version: ${version}`);
@@ -286,16 +252,11 @@ var CryptoService = class {
286
252
  false,
287
253
  ["encrypt"]
288
254
  );
289
- let associatedData = new Uint8Array(0);
290
- if (opts.associatedData != null) {
291
- associatedData = opts.associatedData;
292
- }
293
255
  const encryptedContent = await crypto.subtle.encrypt(
294
256
  {
295
257
  name: "AES-GCM",
296
258
  iv: nonce,
297
- tagLength: opts.tagSize * 8,
298
- additionalData: associatedData
259
+ tagLength: opts.tagSize * 8
299
260
  },
300
261
  cryptoKey,
301
262
  plainBytes
@@ -334,15 +295,11 @@ var CryptoService = class {
334
295
  ["decrypt"]
335
296
  );
336
297
  try {
337
- let associatedData = new Uint8Array(0);
338
- if (opts.associatedData != null)
339
- associatedData = opts.associatedData;
340
298
  const decryptedBuffer = await crypto.subtle.decrypt(
341
299
  {
342
300
  name: "AES-GCM",
343
301
  iv: nonce,
344
- tagLength: opts.tagSize * 8,
345
- additionalData: associatedData
302
+ tagLength: opts.tagSize * 8
346
303
  },
347
304
  cryptoKey,
348
305
  ciphertextWithTag