@opentdf/sdk 0.2.0 → 0.3.0-beta.2029

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 (96) hide show
  1. package/dist/cjs/src/auth/auth.js +1 -1
  2. package/dist/cjs/src/encodings/base64.js +1 -1
  3. package/dist/cjs/src/nanoclients.js +5 -5
  4. package/dist/cjs/src/nanotdf/Client.js +1 -1
  5. package/dist/cjs/src/nanotdf/NanoTDF.js +1 -1
  6. package/dist/cjs/src/nanotdf/encrypt-dataset.js +1 -1
  7. package/dist/cjs/src/nanotdf/encrypt.js +1 -1
  8. package/dist/cjs/src/nanotdf/helpers/getHkdfSalt.js +1 -1
  9. package/dist/cjs/src/nanotdf-crypto/digest.js +1 -1
  10. package/dist/cjs/src/nanotdf-crypto/pemPublicToCrypto.js +4 -27
  11. package/dist/cjs/src/opentdf.js +175 -45
  12. package/dist/cjs/src/version.js +1 -1
  13. package/dist/cjs/tdf3/index.js +1 -1
  14. package/dist/cjs/tdf3/src/assertions.js +1 -1
  15. package/dist/cjs/tdf3/src/crypto/salt.js +12 -0
  16. package/dist/cjs/tdf3/src/models/key-access.js +3 -2
  17. package/dist/cjs/tdf3/src/tdf.js +8 -3
  18. package/dist/types/src/access.d.ts.map +1 -1
  19. package/dist/types/src/auth/auth.d.ts +2 -2
  20. package/dist/types/src/auth/auth.d.ts.map +1 -1
  21. package/dist/types/src/auth/providers.d.ts.map +1 -1
  22. package/dist/types/src/encodings/base64.d.ts +1 -1
  23. package/dist/types/src/encodings/base64.d.ts.map +1 -1
  24. package/dist/types/src/nanoclients.d.ts +9 -10
  25. package/dist/types/src/nanoclients.d.ts.map +1 -1
  26. package/dist/types/src/nanotdf/Client.d.ts +1 -2
  27. package/dist/types/src/nanotdf/Client.d.ts.map +1 -1
  28. package/dist/types/src/nanotdf/NanoTDF.d.ts +1 -2
  29. package/dist/types/src/nanotdf/NanoTDF.d.ts.map +1 -1
  30. package/dist/types/src/nanotdf/encrypt-dataset.d.ts +1 -2
  31. package/dist/types/src/nanotdf/encrypt-dataset.d.ts.map +1 -1
  32. package/dist/types/src/nanotdf/encrypt.d.ts +1 -2
  33. package/dist/types/src/nanotdf/encrypt.d.ts.map +1 -1
  34. package/dist/types/src/nanotdf/helpers/getHkdfSalt.d.ts +1 -2
  35. package/dist/types/src/nanotdf/helpers/getHkdfSalt.d.ts.map +1 -1
  36. package/dist/types/src/nanotdf-crypto/digest.d.ts +1 -2
  37. package/dist/types/src/nanotdf-crypto/digest.d.ts.map +1 -1
  38. package/dist/types/src/nanotdf-crypto/pemPublicToCrypto.d.ts.map +1 -1
  39. package/dist/types/src/opentdf.d.ts +29 -5
  40. package/dist/types/src/opentdf.d.ts.map +1 -1
  41. package/dist/types/src/seekable.d.ts.map +1 -1
  42. package/dist/types/src/utils.d.ts.map +1 -1
  43. package/dist/types/src/version.d.ts +1 -1
  44. package/dist/types/tdf3/index.d.ts +1 -1
  45. package/dist/types/tdf3/index.d.ts.map +1 -1
  46. package/dist/types/tdf3/src/assertions.d.ts +1 -2
  47. package/dist/types/tdf3/src/assertions.d.ts.map +1 -1
  48. package/dist/types/tdf3/src/client/index.d.ts +1 -6
  49. package/dist/types/tdf3/src/client/index.d.ts.map +1 -1
  50. package/dist/types/tdf3/src/client/validation.d.ts.map +1 -1
  51. package/dist/types/tdf3/src/crypto/crypto-utils.d.ts.map +1 -1
  52. package/dist/types/tdf3/src/crypto/salt.d.ts +2 -0
  53. package/dist/types/tdf3/src/crypto/salt.d.ts.map +1 -0
  54. package/dist/types/tdf3/src/models/key-access.d.ts.map +1 -1
  55. package/dist/types/tdf3/src/tdf.d.ts +4 -2
  56. package/dist/types/tdf3/src/tdf.d.ts.map +1 -1
  57. package/dist/web/src/auth/auth.js +1 -1
  58. package/dist/web/src/encodings/base64.js +1 -1
  59. package/dist/web/src/nanoclients.js +5 -5
  60. package/dist/web/src/nanotdf/Client.js +1 -1
  61. package/dist/web/src/nanotdf/NanoTDF.js +1 -1
  62. package/dist/web/src/nanotdf/encrypt-dataset.js +1 -1
  63. package/dist/web/src/nanotdf/encrypt.js +1 -1
  64. package/dist/web/src/nanotdf/helpers/getHkdfSalt.js +1 -1
  65. package/dist/web/src/nanotdf-crypto/digest.js +1 -1
  66. package/dist/web/src/nanotdf-crypto/pemPublicToCrypto.js +4 -27
  67. package/dist/web/src/opentdf.js +174 -44
  68. package/dist/web/src/version.js +1 -1
  69. package/dist/web/tdf3/index.js +1 -1
  70. package/dist/web/tdf3/src/assertions.js +1 -1
  71. package/dist/web/tdf3/src/crypto/salt.js +9 -0
  72. package/dist/web/tdf3/src/models/key-access.js +3 -2
  73. package/dist/web/tdf3/src/tdf.js +7 -3
  74. package/package.json +12 -12
  75. package/src/auth/auth.ts +2 -2
  76. package/src/encodings/base64.ts +1 -1
  77. package/src/nanoclients.ts +9 -16
  78. package/src/nanotdf/Client.ts +2 -3
  79. package/src/nanotdf/NanoTDF.ts +1 -2
  80. package/src/nanotdf/encrypt-dataset.ts +1 -2
  81. package/src/nanotdf/encrypt.ts +1 -2
  82. package/src/nanotdf/helpers/getHkdfSalt.ts +1 -3
  83. package/src/nanotdf-crypto/digest.ts +1 -3
  84. package/src/nanotdf-crypto/pemPublicToCrypto.ts +3 -33
  85. package/src/opentdf.ts +249 -54
  86. package/src/version.ts +1 -1
  87. package/tdf3/index.ts +2 -1
  88. package/tdf3/src/assertions.ts +2 -2
  89. package/tdf3/src/crypto/salt.ts +11 -0
  90. package/tdf3/src/models/key-access.ts +2 -1
  91. package/tdf3/src/tdf.ts +19 -5
  92. package/dist/cjs/src/tdf/TypedArray.js +0 -3
  93. package/dist/types/src/tdf/TypedArray.d.ts +0 -2
  94. package/dist/types/src/tdf/TypedArray.d.ts.map +0 -1
  95. package/dist/web/src/tdf/TypedArray.js +0 -2
  96. package/src/tdf/TypedArray.ts +0 -10
package/src/opentdf.ts CHANGED
@@ -6,7 +6,7 @@ import NanoTDF from './nanotdf/NanoTDF.js';
6
6
  import decryptNanoTDF from './nanotdf/decrypt.js';
7
7
  import Client from './nanotdf/Client.js';
8
8
  import Header from './nanotdf/models/Header.js';
9
- import { fromSource, sourceToStream, type Source } from './seekable.js';
9
+ import { Chunker, fromSource, sourceToStream, type Source } from './seekable.js';
10
10
  import { Client as TDF3Client } from '../tdf3/src/client/index.js';
11
11
  import {
12
12
  type Assertion,
@@ -22,7 +22,15 @@ import {
22
22
  type EncryptionInformation,
23
23
  } from '../tdf3/src/models/encryption-information.js';
24
24
  import { type KeyAccessObject } from '../tdf3/src/models/key-access.js';
25
- import { type IntegrityAlgorithm } from '../tdf3/src/tdf.js';
25
+ import {
26
+ decryptStreamFrom,
27
+ InspectedTDFOverview,
28
+ loadTDFStream,
29
+ type IntegrityAlgorithm,
30
+ } from '../tdf3/src/tdf.js';
31
+ import { base64 } from './encodings/index.js';
32
+ import { PolicyObject } from './tdf/PolicyObject.js';
33
+ import PolicyType from './nanotdf/enum/PolicyTypeEnum.js';
26
34
 
27
35
  export {
28
36
  type Assertion,
@@ -248,6 +256,30 @@ export class RewrapCache {
248
256
  }
249
257
  }
250
258
 
259
+ /**
260
+ * A TDF reader that can decrypt and inspect a TDF file.
261
+ */
262
+ export type TDFReader = {
263
+ /**
264
+ * Decrypt the payload.
265
+ */
266
+ decrypt: () => Promise<DecoratedStream>;
267
+ /**
268
+ * Mark this reader as closed and release any resources, such as open files.
269
+ */
270
+ close: () => Promise<void>;
271
+
272
+ /**
273
+ * Only present on ZTDF files
274
+ */
275
+ manifest: () => Promise<Manifest>;
276
+
277
+ /**
278
+ * @returns Any data attributes found in the policy. Currently only works for plain text, embedded policies (not remote or encrypted policies)
279
+ */
280
+ attributes: () => Promise<string[]>;
281
+ };
282
+
251
283
  // SDK for dealing with OpenTDF data and policy services.
252
284
  export class OpenTDF {
253
285
  // Configuration service and more is at this URL/connectRPC endpoint
@@ -260,7 +292,7 @@ export class OpenTDF {
260
292
 
261
293
  // Header cache for reading nanotdf collections
262
294
  private readonly rewrapCache: RewrapCache;
263
- private tdf3Client: TDF3Client;
295
+ readonly tdf3Client: TDF3Client;
264
296
 
265
297
  constructor({
266
298
  authProvider,
@@ -311,7 +343,9 @@ export class OpenTDF {
311
343
  * Creates a new collection object, which can be used to encrypt a series of data with the same policy.
312
344
  * @returns
313
345
  */
314
- async createNanoTDFCollection(opts: CreateNanoTDFCollectionOptions): Promise<NanoTDFCollection> {
346
+ async createNanoTDFCollection(
347
+ opts: CreateNanoTDFCollectionOptions
348
+ ): Promise<NanoTDFCollectionWriter> {
315
349
  opts = { ...this.defaultCreateOptions, ...opts };
316
350
  return new Collection(this.authProvider, opts);
317
351
  }
@@ -340,66 +374,227 @@ export class OpenTDF {
340
374
  }
341
375
 
342
376
  /**
343
- * Decrypts a nanotdf object. Optionally, stores the collection header and its DEK.
344
- * @param ciphertext
377
+ * Opens a TDF file for inspection and decryption.
378
+ * @param opts the file to open, and any appropriate configuration options
379
+ * @returns
345
380
  */
346
- async read(opts: ReadOptions): Promise<DecoratedStream> {
381
+ open(opts: ReadOptions): TDFReader {
347
382
  opts = { ...this.defaultReadOptions, ...opts };
348
- const chunker = await fromSource(opts.source);
383
+ return new UnknownTypeReader(this, opts, this.rewrapCache);
384
+ }
385
+
386
+ async read(opts: ReadOptions): Promise<DecoratedStream> {
387
+ const reader = this.open(opts);
388
+ return reader.decrypt();
389
+ }
390
+
391
+ close() {
392
+ this.rewrapCache.close();
393
+ }
394
+ }
395
+
396
+ class UnknownTypeReader {
397
+ delegate: Promise<TDFReader>;
398
+ state: 'init' | 'resolving' | 'loaded' | 'decrypting' | 'closing' | 'done' | 'error' = 'init';
399
+ constructor(
400
+ readonly outer: OpenTDF,
401
+ readonly opts: ReadOptions,
402
+ private readonly rewrapCache: RewrapCache
403
+ ) {
404
+ this.delegate = this.resolveType();
405
+ }
406
+
407
+ async resolveType(): Promise<TDFReader> {
408
+ if (this.state === 'done') {
409
+ throw new ConfigurationError('reader is closed');
410
+ }
411
+ this.state = 'resolving';
412
+ const chunker = await fromSource(this.opts.source);
349
413
  const prefix = await chunker(0, 3);
350
- // switch for prefix, if starts with `PK` in ascii, or `L1L` in ascii:
351
414
  if (prefix[0] === 0x50 && prefix[1] === 0x4b) {
352
- const allowList = new OriginAllowList(opts.allowedKASEndpoints ?? [], opts.ignoreAllowlist);
353
- const oldStream = await this.tdf3Client.decrypt({
354
- source: opts.source,
355
- allowList,
356
- assertionVerificationKeys: opts.assertionVerificationKeys,
357
- noVerifyAssertions: opts.noVerify,
358
- wrappingKeyAlgorithm: opts.wrappingKeyAlgorithm,
359
- });
360
- const stream: DecoratedStream = oldStream.stream;
361
- stream.metadata = Promise.resolve(oldStream.metadata);
362
- return stream;
415
+ this.state = 'loaded';
416
+ return new ZTDFReader(this.outer.tdf3Client, this.opts, chunker);
363
417
  } else if (prefix[0] === 0x4c && prefix[1] === 0x31 && prefix[2] === 0x4c) {
364
- const ciphertext = await chunker();
365
- const nanotdf = NanoTDF.from(ciphertext);
366
- const cachedDEK = this.rewrapCache.get(nanotdf.header.ephemeralPublicKey);
367
- if (cachedDEK) {
368
- const r: DecoratedStream = await streamify(decryptNanoTDF(cachedDEK, nanotdf));
369
- r.header = nanotdf.header;
370
- return r;
371
- }
372
- const nc = new Client({
373
- allowedKases: opts.allowedKASEndpoints,
374
- authProvider: this.authProvider,
375
- ignoreAllowList: opts.ignoreAllowlist,
376
- dpopEnabled: this.dpopEnabled,
377
- dpopKeys: this.dpopKeys,
378
- kasEndpoint: opts.allowedKASEndpoints?.[0] || 'https://disallow.all.invalid',
379
- });
380
- // TODO: The version number should be fetched from the API
381
- const version = '0.0.1';
382
- // Rewrap key on every request
383
- const dek = await nc.rewrapKey(
384
- nanotdf.header.toBuffer(),
385
- nanotdf.header.getKasRewrapUrl(),
386
- nanotdf.header.magicNumberVersion,
387
- version
388
- );
389
- if (!dek) {
390
- // These should have thrown already.
391
- throw new Error('internal: key rewrap failure');
418
+ this.state = 'loaded';
419
+ return new NanoTDFReader(this.outer, this.opts, chunker, this.rewrapCache);
420
+ }
421
+ this.state = 'done';
422
+ throw new InvalidFileError(`unsupported format; prefix not recognized ${prefix}`);
423
+ }
424
+
425
+ async decrypt(): Promise<DecoratedStream> {
426
+ const actual = await this.delegate;
427
+ return actual.decrypt();
428
+ }
429
+
430
+ async attributes(): Promise<string[]> {
431
+ const actual = await this.delegate;
432
+ return actual.attributes();
433
+ }
434
+
435
+ async manifest(): Promise<Manifest> {
436
+ const actual = await this.delegate;
437
+ return actual.manifest();
438
+ }
439
+
440
+ async close() {
441
+ if (this.state === 'done') {
442
+ return;
443
+ }
444
+ if (this.state === 'init') {
445
+ // delegate resolve never started
446
+ this.state = 'done';
447
+ return;
448
+ }
449
+ this.state = 'closing';
450
+ const actual = await this.delegate;
451
+ return actual.close().then(() => {
452
+ this.state = 'done';
453
+ });
454
+ }
455
+ }
456
+
457
+ class NanoTDFReader {
458
+ container: Promise<NanoTDF>;
459
+ constructor(
460
+ readonly outer: OpenTDF,
461
+ readonly opts: ReadOptions,
462
+ readonly chunker: Chunker,
463
+ private readonly rewrapCache: RewrapCache
464
+ ) {
465
+ // lazily load the container
466
+ this.container = new Promise(async (resolve, reject) => {
467
+ try {
468
+ const ciphertext = await chunker();
469
+ const nanotdf = NanoTDF.from(ciphertext);
470
+ resolve(nanotdf);
471
+ } catch (e) {
472
+ reject(e);
392
473
  }
393
- this.rewrapCache.set(nanotdf.header.ephemeralPublicKey, dek);
394
- const r: DecoratedStream = await streamify(decryptNanoTDF(dek, nanotdf));
474
+ });
475
+ }
476
+
477
+ async decrypt(): Promise<DecoratedStream> {
478
+ const nanotdf = await this.container;
479
+ const cachedDEK = this.rewrapCache.get(nanotdf.header.ephemeralPublicKey);
480
+ if (cachedDEK) {
481
+ const r: DecoratedStream = await streamify(decryptNanoTDF(cachedDEK, nanotdf));
395
482
  r.header = nanotdf.header;
396
483
  return r;
397
484
  }
398
- throw new InvalidFileError(`unsupported format; prefix not recognized ${prefix}`);
485
+ const nc = new Client({
486
+ allowedKases: this.opts.allowedKASEndpoints,
487
+ authProvider: this.outer.authProvider,
488
+ ignoreAllowList: this.opts.ignoreAllowlist,
489
+ dpopEnabled: this.outer.dpopEnabled,
490
+ dpopKeys: this.outer.dpopKeys,
491
+ kasEndpoint: this.opts.allowedKASEndpoints?.[0] || 'https://disallow.all.invalid',
492
+ });
493
+ // TODO: The version number should be fetched from the API
494
+ const version = '0.0.1';
495
+ // Rewrap key on every request
496
+ const dek = await nc.rewrapKey(
497
+ nanotdf.header.toBuffer(),
498
+ nanotdf.header.getKasRewrapUrl(),
499
+ nanotdf.header.magicNumberVersion,
500
+ version
501
+ );
502
+ if (!dek) {
503
+ // These should have thrown already.
504
+ throw new Error('internal: key rewrap failure');
505
+ }
506
+ this.rewrapCache.set(nanotdf.header.ephemeralPublicKey, dek);
507
+ const r: DecoratedStream = await streamify(decryptNanoTDF(dek, nanotdf));
508
+ // TODO figure out how to attach policy and metadata to the stream
509
+ r.header = nanotdf.header;
510
+ return r;
399
511
  }
400
512
 
401
- close() {
402
- this.rewrapCache.close();
513
+ async close() {}
514
+
515
+ async manifest(): Promise<Manifest> {
516
+ return {} as Manifest;
517
+ }
518
+
519
+ async attributes(): Promise<string[]> {
520
+ const nanotdf = await this.container;
521
+ if (!nanotdf.header.policy?.content) {
522
+ return [];
523
+ }
524
+ if (nanotdf.header.policy.type !== PolicyType.EmbeddedText) {
525
+ throw new Error('unsupported policy type');
526
+ }
527
+ const policyString = new TextDecoder().decode(nanotdf.header.policy.content);
528
+ const policy = JSON.parse(policyString) as PolicyObject;
529
+ return policy.body.dataAttributes.map((a) => a.attribute);
530
+ }
531
+ }
532
+
533
+ class ZTDFReader {
534
+ overview: Promise<InspectedTDFOverview>;
535
+ constructor(
536
+ readonly client: TDF3Client,
537
+ readonly opts: ReadOptions,
538
+ readonly source: Chunker
539
+ ) {
540
+ this.overview = loadTDFStream(source);
541
+ }
542
+
543
+ async decrypt(): Promise<DecoratedStream> {
544
+ const {
545
+ assertionVerificationKeys,
546
+ noVerify: noVerifyAssertions,
547
+ wrappingKeyAlgorithm,
548
+ } = this.opts;
549
+ const allowList = new OriginAllowList(
550
+ this.opts.allowedKASEndpoints ?? [],
551
+ this.opts.ignoreAllowlist
552
+ );
553
+ const dpopKeys = await this.client.dpopKeys;
554
+
555
+ const { authProvider, cryptoService } = this.client;
556
+ if (!authProvider) {
557
+ throw new ConfigurationError('authProvider is required');
558
+ }
559
+
560
+ const overview = await this.overview;
561
+ const oldStream = await decryptStreamFrom(
562
+ {
563
+ allowList,
564
+ authProvider,
565
+ chunker: this.source,
566
+ concurrencyLimit: 1,
567
+ cryptoService,
568
+ dpopKeys,
569
+ fileStreamServiceWorker: this.client.clientConfig.fileStreamServiceWorker,
570
+ keyMiddleware: async (k) => k,
571
+ progressHandler: this.client.clientConfig.progressHandler,
572
+ assertionVerificationKeys,
573
+ noVerifyAssertions,
574
+ wrappingKeyAlgorithm,
575
+ },
576
+ overview
577
+ );
578
+ const stream: DecoratedStream = oldStream.stream;
579
+ stream.manifest = Promise.resolve(overview.manifest);
580
+ stream.metadata = Promise.resolve(oldStream.metadata);
581
+ return stream;
582
+ }
583
+
584
+ async close() {
585
+ // TODO figure out how to close a chunker, if we want to.
586
+ }
587
+
588
+ async manifest(): Promise<Manifest> {
589
+ const overview = await this.overview;
590
+ return overview.manifest;
591
+ }
592
+
593
+ async attributes(): Promise<string[]> {
594
+ const manifest = await this.manifest();
595
+ const policyJSON = base64.decode(manifest.encryptionInformation.policy);
596
+ const policy = JSON.parse(policyJSON) as PolicyObject;
597
+ return policy.body.dataAttributes.map((a) => a.attribute);
403
598
  }
404
599
  }
405
600
 
@@ -415,7 +610,7 @@ async function streamify(ab: Promise<ArrayBuffer>): Promise<ReadableStream<Uint8
415
610
  return stream;
416
611
  }
417
612
 
418
- export type NanoTDFCollection = {
613
+ export type NanoTDFCollectionWriter = {
419
614
  encrypt: (source: Source) => Promise<ReadableStream<Uint8Array>>;
420
615
  close: () => Promise<void>;
421
616
  };
package/src/version.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Exposes the released version number of the `@opentdf/sdk` package
3
3
  */
4
- export const version = '0.2.0';
4
+ export const version = '0.3.0';
5
5
 
6
6
  /**
7
7
  * A string name used to label requests as coming from this library client.
package/tdf3/index.ts CHANGED
@@ -90,7 +90,8 @@ export {
90
90
  type DecoratedStream,
91
91
  type Keys,
92
92
  type OpenTDFOptions,
93
- type NanoTDFCollection,
93
+ type NanoTDFCollectionWriter,
94
94
  type ReadOptions,
95
+ type TDFReader,
95
96
  OpenTDF,
96
97
  } from '../src/opentdf.js';
@@ -1,5 +1,5 @@
1
1
  import { canonicalizeEx } from 'json-canonicalize';
2
- import { type KeyLike, SignJWT, jwtVerify } from 'jose';
2
+ import { SignJWT, jwtVerify } from 'jose';
3
3
  import { base64, hex } from '../../src/encodings/index.js';
4
4
  import { ConfigurationError, IntegrityError, InvalidFileError } from '../../src/errors.js';
5
5
 
@@ -185,7 +185,7 @@ export async function CreateAssertion(
185
185
 
186
186
  export type AssertionKey = {
187
187
  alg: AssertionKeyAlg;
188
- key: KeyLike | Uint8Array;
188
+ key: CryptoKey | Uint8Array;
189
189
  };
190
190
 
191
191
  // AssertionConfig is a shadow of Assertion with the addition of the signing key.
@@ -0,0 +1,11 @@
1
+ const generateSalt = async () => {
2
+ const encoder = new TextEncoder();
3
+ const data = encoder.encode('TDF');
4
+
5
+ // Generate hash
6
+ const hashBuffer = await crypto.subtle.digest('SHA-256', data);
7
+
8
+ return new Uint8Array(hashBuffer);
9
+ };
10
+
11
+ export const ztdfSalt = generateSalt();
@@ -5,6 +5,7 @@ import { pemPublicToCrypto } from '../../../src/nanotdf-crypto/pemPublicToCrypto
5
5
  import { cryptoPublicToPem } from '../../../src/utils.js';
6
6
  import { Binary } from '../binary.js';
7
7
  import * as cryptoService from '../crypto/index.js';
8
+ import { ztdfSalt } from '../crypto/salt.js';
8
9
  import { Policy } from './policy.js';
9
10
 
10
11
  export type KeyAccessType = 'remote' | 'wrapped' | 'ec-wrapped';
@@ -44,7 +45,7 @@ export class ECWrapped {
44
45
  pemPublicToCrypto(this.publicKey),
45
46
  ]);
46
47
  const kek = await keyAgreement(ek.privateKey, clientPublicKey, {
47
- hkdfSalt: new TextEncoder().encode('salt'),
48
+ hkdfSalt: await ztdfSalt,
48
49
  hkdfHash: 'SHA-256',
49
50
  });
50
51
  const iv = generateRandomNumber(12);
package/tdf3/src/tdf.ts CHANGED
@@ -54,6 +54,7 @@ import {
54
54
  import { unsigned } from './utils/buffer-crc32.js';
55
55
  import { ZipReader, ZipWriter, keyMerge, concatUint8 } from './utils/index.js';
56
56
  import { CentralDirectory } from './utils/zip-reader.js';
57
+ import { ztdfSalt } from './crypto/salt.js';
57
58
 
58
59
  // TODO: input validation on manifest JSON
59
60
  const DEFAULT_SEGMENT_SIZE = 1024 * 1024;
@@ -596,10 +597,14 @@ export async function writeStream(cfg: EncryptConfiguration): Promise<DecoratedR
596
597
  }
597
598
  }
598
599
 
600
+ export type InspectedTDFOverview = {
601
+ manifest: Manifest;
602
+ zipReader: ZipReader;
603
+ centralDirectory: CentralDirectory[];
604
+ };
605
+
599
606
  // load the TDF as a stream in memory, for further use in reading and key syncing
600
- export async function loadTDFStream(
601
- chunker: Chunker
602
- ): Promise<{ manifest: Manifest; zipReader: ZipReader; centralDirectory: CentralDirectory[] }> {
607
+ export async function loadTDFStream(chunker: Chunker): Promise<InspectedTDFOverview> {
603
608
  const zipReader = new ZipReader(chunker);
604
609
  const centralDirectory = await zipReader.getCentralDirectory();
605
610
  const manifest = await zipReader.getManifest(centralDirectory, '0.manifest.json');
@@ -707,7 +712,7 @@ async function unwrapKey({
707
712
  const serverEphemeralKey: CryptoKey = await pemPublicToCrypto(sessionPublicKey);
708
713
  const ekr = ephemeralEncryptionKeysRaw as CryptoKeyPair;
709
714
  const kek = await keyAgreement(ekr.privateKey, serverEphemeralKey, {
710
- hkdfSalt: new TextEncoder().encode('salt'),
715
+ hkdfSalt: await ztdfSalt,
711
716
  hkdfHash: 'SHA-256',
712
717
  });
713
718
  const wrappedKeyAndNonce = base64.decodeArrayBuffer(entityWrappedKey);
@@ -919,6 +924,14 @@ export async function sliceAndDecrypt({
919
924
  }
920
925
 
921
926
  export async function readStream(cfg: DecryptConfiguration) {
927
+ const overview = await loadTDFStream(cfg.chunker);
928
+ return decryptStreamFrom(cfg, overview);
929
+ }
930
+
931
+ export async function decryptStreamFrom(
932
+ cfg: DecryptConfiguration,
933
+ { manifest, zipReader, centralDirectory }: InspectedTDFOverview
934
+ ) {
922
935
  let { allowList } = cfg;
923
936
  if (!allowList) {
924
937
  if (!cfg.allowedKases) {
@@ -926,10 +939,11 @@ export async function readStream(cfg: DecryptConfiguration) {
926
939
  }
927
940
  allowList = new OriginAllowList(cfg.allowedKases);
928
941
  }
929
- const { manifest, zipReader, centralDirectory } = await loadTDFStream(cfg.chunker);
942
+
930
943
  if (!manifest) {
931
944
  throw new InvalidFileError('Missing manifest data');
932
945
  }
946
+
933
947
  cfg.keyMiddleware ??= async (key) => key;
934
948
 
935
949
  const {
@@ -1,3 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVHlwZWRBcnJheS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy90ZGYvVHlwZWRBcnJheS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiIn0=
@@ -1,2 +0,0 @@
1
- export type TypedArray = Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array | Uint8ClampedArray | Float32Array | Float64Array;
2
- //# sourceMappingURL=TypedArray.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"TypedArray.d.ts","sourceRoot":"","sources":["../../../../src/tdf/TypedArray.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAClB,SAAS,GACT,UAAU,GACV,UAAU,GACV,WAAW,GACX,UAAU,GACV,WAAW,GACX,iBAAiB,GACjB,YAAY,GACZ,YAAY,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVHlwZWRBcnJheS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy90ZGYvVHlwZWRBcnJheS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiIn0=
@@ -1,10 +0,0 @@
1
- export type TypedArray =
2
- | Int8Array
3
- | Uint8Array
4
- | Int16Array
5
- | Uint16Array
6
- | Int32Array
7
- | Uint32Array
8
- | Uint8ClampedArray
9
- | Float32Array
10
- | Float64Array;