@opentdf/sdk 0.3.0-beta.2050 → 0.3.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/tdf3/src/tdf.ts CHANGED
@@ -51,7 +51,7 @@ import {
51
51
  SplitType,
52
52
  } from './models/index.js';
53
53
  import { unsigned } from './utils/buffer-crc32.js';
54
- import { ZipReader, ZipWriter, keyMerge, concatUint8 } from './utils/index.js';
54
+ import { ZipReader, ZipWriter, keyMerge, concatUint8, buffToString } from './utils/index.js';
55
55
  import { CentralDirectory } from './utils/zip-reader.js';
56
56
  import { ztdfSalt } from './crypto/salt.js';
57
57
  import { Payload } from './models/payload.js';
@@ -59,6 +59,8 @@ import { Payload } from './models/payload.js';
59
59
  // TODO: input validation on manifest JSON
60
60
  const DEFAULT_SEGMENT_SIZE = 1024 * 1024;
61
61
 
62
+ const HEX_SEMVER_VERSION = '4.2.2';
63
+
62
64
  /**
63
65
  * Configuration for TDF3
64
66
  */
@@ -145,6 +147,7 @@ export type EncryptConfiguration = {
145
147
  keyForEncryption: KeyInfo;
146
148
  keyForManifest: KeyInfo;
147
149
  assertionConfigs?: AssertionConfig[];
150
+ tdfSpecVersion?: string;
148
151
  };
149
152
 
150
153
  export type DecryptConfiguration = {
@@ -284,7 +287,8 @@ async function _generateManifest(
284
287
  keyInfo: KeyInfo,
285
288
  encryptionInformation: SplitKey,
286
289
  policy: Policy,
287
- mimeType: string | undefined
290
+ mimeType: string | undefined,
291
+ targetSpecVersion: string | undefined
288
292
  ): Promise<Manifest> {
289
293
  // (maybe) Fields are quoted to avoid renaming
290
294
  const payload: Payload = {
@@ -302,7 +306,8 @@ async function _generateManifest(
302
306
  // generate the manifest first, then insert integrity information into it
303
307
  encryptionInformation: encryptionInformationStr,
304
308
  assertions: assertions,
305
- schemaVersion: tdfSpecVersion,
309
+ // when `targetSpecVersion` is provided, overrides the tdfSpecVersion
310
+ schemaVersion: targetSpecVersion || tdfSpecVersion,
306
311
  };
307
312
  }
308
313
 
@@ -335,6 +340,30 @@ async function getSignature(
335
340
  }
336
341
  }
337
342
 
343
+ async function getSignatureVersion422(
344
+ unwrappedKeyBinary: Binary,
345
+ payloadBinary: Binary,
346
+ algorithmType: IntegrityAlgorithm,
347
+ cryptoService: CryptoService
348
+ ): Promise<string> {
349
+ switch (algorithmType.toUpperCase()) {
350
+ case 'GMAC':
351
+ // use the auth tag baked into the encrypted payload
352
+ return buffToString(Uint8Array.from(payloadBinary.asByteArray()).slice(-16), 'hex');
353
+ case 'HS256':
354
+ return await cryptoService.hmac(
355
+ buffToString(new Uint8Array(unwrappedKeyBinary.asArrayBuffer()), 'hex'),
356
+ buffToString(new Uint8Array(payloadBinary.asArrayBuffer()), 'utf-8')
357
+ );
358
+ default:
359
+ throw new ConfigurationError(`Unsupported signature alg [${algorithmType}]`);
360
+ }
361
+ }
362
+
363
+ function isTargetSpecLegacyTDF(targetSpecVersion?: string): boolean {
364
+ return targetSpecVersion === HEX_SEMVER_VERSION;
365
+ }
366
+
338
367
  export async function writeStream(cfg: EncryptConfiguration): Promise<DecoratedReadableStream> {
339
368
  if (!cfg.authProvider) {
340
369
  throw new ConfigurationError('No authorization middleware defined');
@@ -363,6 +392,7 @@ export async function writeStream(cfg: EncryptConfiguration): Promise<DecoratedR
363
392
  let bytesProcessed = 0;
364
393
  let crcCounter = 0;
365
394
  let fileByteCount = 0;
395
+ let aggregateHash422 = '';
366
396
  const segmentHashList: Uint8Array[] = [];
367
397
 
368
398
  const zipWriter = new ZipWriter();
@@ -370,7 +400,8 @@ export async function writeStream(cfg: EncryptConfiguration): Promise<DecoratedR
370
400
  cfg.keyForManifest,
371
401
  cfg.encryptionInformation,
372
402
  cfg.policy,
373
- cfg.mimeType
403
+ cfg.mimeType,
404
+ cfg.tdfSpecVersion ?? '4.3.0'
374
405
  );
375
406
 
376
407
  if (!manifest) {
@@ -455,17 +486,30 @@ export async function writeStream(cfg: EncryptConfiguration): Promise<DecoratedR
455
486
  crcCounter = 0;
456
487
  fileByteCount = 0;
457
488
 
458
- // hash the concat of all hashes
459
- const aggregateHash = await concatenateUint8Array(segmentHashList);
460
-
461
- const payloadSig = await getSignature(
462
- new Uint8Array(cfg.keyForEncryption.unwrappedKeyBinary.asArrayBuffer()),
463
- aggregateHash,
464
- cfg.integrityAlgorithm
465
- );
489
+ let aggregateHash: string | Uint8Array;
490
+ if (isTargetSpecLegacyTDF(cfg.tdfSpecVersion)) {
491
+ aggregateHash = aggregateHash422;
492
+ const payloadSigStr = await getSignatureVersion422(
493
+ cfg.keyForEncryption.unwrappedKeyBinary,
494
+ Binary.fromString(aggregateHash),
495
+ cfg.integrityAlgorithm,
496
+ cfg.cryptoService
497
+ );
498
+ manifest.encryptionInformation.integrityInformation.rootSignature.sig =
499
+ base64.encode(payloadSigStr);
500
+ } else {
501
+ // hash the concat of all hashes
502
+ aggregateHash = await concatenateUint8Array(segmentHashList);
503
+
504
+ const payloadSig = await getSignature(
505
+ new Uint8Array(cfg.keyForEncryption.unwrappedKeyBinary.asArrayBuffer()),
506
+ aggregateHash,
507
+ cfg.integrityAlgorithm
508
+ );
466
509
 
467
- const rootSig = base64.encodeArrayBuffer(payloadSig);
468
- manifest.encryptionInformation.integrityInformation.rootSignature.sig = rootSig;
510
+ const rootSig = base64.encodeArrayBuffer(payloadSig);
511
+ manifest.encryptionInformation.integrityInformation.rootSignature.sig = rootSig;
512
+ }
469
513
  manifest.encryptionInformation.integrityInformation.rootSignature.alg =
470
514
  cfg.integrityAlgorithm;
471
515
 
@@ -571,16 +615,30 @@ export async function writeStream(cfg: EncryptConfiguration): Promise<DecoratedR
571
615
  cfg.keyForEncryption.unwrappedKeyBinary
572
616
  );
573
617
  const payloadBuffer = new Uint8Array(encryptedResult.payload.asByteArray());
574
- const payloadSig = await getSignature(
575
- new Uint8Array(cfg.keyForEncryption.unwrappedKeyBinary.asArrayBuffer()),
576
- new Uint8Array(encryptedResult.payload.asArrayBuffer()),
577
- cfg.segmentIntegrityAlgorithm
578
- );
618
+ let hash: string;
619
+ if (isTargetSpecLegacyTDF(cfg.tdfSpecVersion)) {
620
+ const payloadSigStr = await getSignatureVersion422(
621
+ cfg.keyForEncryption.unwrappedKeyBinary,
622
+ encryptedResult.payload,
623
+ cfg.segmentIntegrityAlgorithm,
624
+ cfg.cryptoService
625
+ );
626
+ // combined string of all hashes for root signature
627
+ aggregateHash422 += payloadSigStr;
628
+ hash = base64.encode(payloadSigStr);
629
+ } else {
630
+ const payloadSig = await getSignature(
631
+ new Uint8Array(cfg.keyForEncryption.unwrappedKeyBinary.asArrayBuffer()),
632
+ new Uint8Array(encryptedResult.payload.asArrayBuffer()),
633
+ cfg.segmentIntegrityAlgorithm
634
+ );
579
635
 
580
- segmentHashList.push(new Uint8Array(payloadSig));
636
+ segmentHashList.push(new Uint8Array(payloadSig));
637
+ hash = base64.encodeArrayBuffer(payloadSig);
638
+ }
581
639
 
582
640
  segmentInfos.push({
583
- hash: base64.encodeArrayBuffer(payloadSig),
641
+ hash,
584
642
  segmentSize: chunk.length === segmentSizeDefault ? undefined : chunk.length,
585
643
  encryptedSegmentSize:
586
644
  payloadBuffer.length === encryptedSegmentSizeDefault ? undefined : payloadBuffer.length,
@@ -790,7 +848,7 @@ async function decryptChunk(
790
848
  hash: string,
791
849
  cipher: SymmetricCipher,
792
850
  segmentIntegrityAlgorithm: IntegrityAlgorithm,
793
- isLegacyTDF: boolean
851
+ specVersion: string
794
852
  ): Promise<DecryptResult> {
795
853
  if (segmentIntegrityAlgorithm !== 'GMAC' && segmentIntegrityAlgorithm !== 'HS256') {
796
854
  throw new UnsupportedError(`Unsupported integrity alg [${segmentIntegrityAlgorithm}]`);
@@ -801,7 +859,7 @@ async function decryptChunk(
801
859
  segmentIntegrityAlgorithm
802
860
  );
803
861
 
804
- const segmentHash = isLegacyTDF
862
+ const segmentHash = isTargetSpecLegacyTDF(specVersion)
805
863
  ? base64.encode(hex.encodeArrayBuffer(segmentSig))
806
864
  : base64.encodeArrayBuffer(segmentSig);
807
865
 
@@ -819,7 +877,7 @@ async function updateChunkQueue(
819
877
  cipher: SymmetricCipher,
820
878
  segmentIntegrityAlgorithm: IntegrityAlgorithm,
821
879
  cryptoService: CryptoService,
822
- isLegacyTDF: boolean
880
+ specVersion: string
823
881
  ) {
824
882
  const chunksInOneDownload = 500;
825
883
  let requests = [];
@@ -860,7 +918,7 @@ async function updateChunkQueue(
860
918
  slice,
861
919
  cipher,
862
920
  segmentIntegrityAlgorithm,
863
- isLegacyTDF,
921
+ specVersion,
864
922
  });
865
923
  }
866
924
  })()
@@ -874,7 +932,7 @@ export async function sliceAndDecrypt({
874
932
  slice,
875
933
  cipher,
876
934
  segmentIntegrityAlgorithm,
877
- isLegacyTDF,
935
+ specVersion,
878
936
  }: {
879
937
  buffer: Uint8Array;
880
938
  reconstructedKeyBinary: Binary;
@@ -882,7 +940,7 @@ export async function sliceAndDecrypt({
882
940
  cipher: SymmetricCipher;
883
941
  cryptoService: CryptoService;
884
942
  segmentIntegrityAlgorithm: IntegrityAlgorithm;
885
- isLegacyTDF: boolean;
943
+ specVersion: string;
886
944
  }) {
887
945
  for (const index in slice) {
888
946
  const { encryptedOffset, encryptedSegmentSize, plainSegmentSize } = slice[index];
@@ -904,7 +962,7 @@ export async function sliceAndDecrypt({
904
962
  slice[index]['hash'],
905
963
  cipher,
906
964
  segmentIntegrityAlgorithm,
907
- isLegacyTDF
965
+ specVersion
908
966
  );
909
967
  if (plainSegmentSize && result.payload.length() !== plainSegmentSize) {
910
968
  throw new DecryptError(
@@ -960,8 +1018,8 @@ export async function decryptStreamFrom(
960
1018
  const encryptedSegmentSizeDefault = defaultSegmentSize || DEFAULT_SEGMENT_SIZE;
961
1019
 
962
1020
  // check if the TDF is a legacy TDF
963
- const specVersion = manifest.schemaVersion || manifest.tdf_spec_version;
964
- const isLegacyTDF = !specVersion || specVersion.startsWith('3.');
1021
+ const specVersion = manifest.schemaVersion || manifest.tdf_spec_version || '4.2.2';
1022
+ const isLegacyTDF = isTargetSpecLegacyTDF(specVersion);
965
1023
 
966
1024
  // Decode each hash and store it in an array of Uint8Array
967
1025
  const segmentHashList = segments.map(
@@ -1047,7 +1105,7 @@ export async function decryptStreamFrom(
1047
1105
  cipher,
1048
1106
  segmentIntegrityAlg,
1049
1107
  cfg.cryptoService,
1050
- isLegacyTDF
1108
+ specVersion
1051
1109
  );
1052
1110
 
1053
1111
  let progress = 0;