From b2fce36bf0635698591c8f7fd444bbd0829b220a Mon Sep 17 00:00:00 2001 From: sujan kota Date: Thu, 5 Dec 2024 19:27:51 -0500 Subject: [PATCH] feat(sdk): remove hex encoding for segment hash --- lib/tdf3/src/models/manifest.ts | 1 + lib/tdf3/src/tdf.ts | 78 ++++++++++++++++++++++++--------- lib/tdf3/src/version.ts | 2 +- 3 files changed, 60 insertions(+), 21 deletions(-) diff --git a/lib/tdf3/src/models/manifest.ts b/lib/tdf3/src/models/manifest.ts index 4ed4ebdd..3b1bb1ff 100644 --- a/lib/tdf3/src/models/manifest.ts +++ b/lib/tdf3/src/models/manifest.ts @@ -6,4 +6,5 @@ export type Manifest = { payload: Payload; encryptionInformation: EncryptionInformation; assertions: Assertion[]; + tdf_spec_version : string; }; diff --git a/lib/tdf3/src/tdf.ts b/lib/tdf3/src/tdf.ts index 56f0c250..28072822 100644 --- a/lib/tdf3/src/tdf.ts +++ b/lib/tdf3/src/tdf.ts @@ -6,6 +6,8 @@ import { EntityObject } from '../../src/tdf/index.js'; import { pemToCryptoPublicKey, validateSecureUrl } from '../../src/utils.js'; import { DecryptParams } from './client/builders.js'; import { AssertionConfig, AssertionKey, AssertionVerificationKeys } from './assertions.js'; +import { version } from './version.js'; +import { hex } from '../../src/encodings/index.js'; import * as assertions from './assertions.js'; import { @@ -457,6 +459,7 @@ async function _generateManifest( // generate the manifest first, then insert integrity information into it encryptionInformation: encryptionInformationStr, assertions: assertions, + tdf_spec_version: version, }; } @@ -616,7 +619,7 @@ export async function writeStream(cfg: EncryptConfiguration): Promise { if (segmentIntegrityAlgorithm !== 'GMAC' && segmentIntegrityAlgorithm !== 'HS256') { } - const segmentHashStr = await getSignature( + const segmentHashAsHex = await getSignature( reconstructedKeyBinary, Binary.fromArrayBuffer(encryptedChunk.buffer), segmentIntegrityAlgorithm, cryptoService ); - if (hash !== btoa(segmentHashStr)) { + + const segmentHash = isLegacyTDF ? btoa(segmentHashAsHex) : + btoa(String.fromCharCode(...new Uint8Array(hex.decodeArrayBuffer(segmentHashAsHex)))); + + if (hash !== segmentHash) { throw new IntegrityError('Failed integrity check on segment hash'); } return await cipher.decrypt(encryptedChunk, reconstructedKeyBinary); @@ -1091,7 +1103,8 @@ async function updateChunkQueue( reconstructedKeyBinary: Binary, cipher: SymmetricCipher, segmentIntegrityAlgorithm: IntegrityAlgorithm, - cryptoService: CryptoService + cryptoService: CryptoService, + isLegacyTDF: boolean, ) { const chunksInOneDownload = 500; let requests = []; @@ -1132,6 +1145,7 @@ async function updateChunkQueue( slice, cipher, segmentIntegrityAlgorithm, + isLegacyTDF }); } })() @@ -1146,6 +1160,7 @@ export async function sliceAndDecrypt({ cipher, cryptoService, segmentIntegrityAlgorithm, + isLegacyTDF }: { buffer: Uint8Array; reconstructedKeyBinary: Binary; @@ -1153,6 +1168,7 @@ export async function sliceAndDecrypt({ cipher: SymmetricCipher; cryptoService: CryptoService; segmentIntegrityAlgorithm: IntegrityAlgorithm; + isLegacyTDF: boolean; }) { for (const index in slice) { const { encryptedOffset, encryptedSegmentSize, _resolve, _reject } = slice[index]; @@ -1170,7 +1186,8 @@ export async function sliceAndDecrypt({ slice[index]['hash'], cipher, segmentIntegrityAlgorithm, - cryptoService + cryptoService, + isLegacyTDF, ); slice[index].decryptedChunk = result; if (_resolve) { @@ -1218,22 +1235,31 @@ export async function readStream(cfg: DecryptConfiguration) { const keyForDecryption = await cfg.keyMiddleware(reconstructedKeyBinary); const encryptedSegmentSizeDefault = defaultSegmentSize || DEFAULT_SEGMENT_SIZE; + // check if the TDF is a legacy TDF + const isLegacyTDF = manifest.tdf_spec_version ? false : true; + // check the combined string of hashes const aggregateHash = segments.map(({ hash }) => base64.decode(hash)).join(''); const integrityAlgorithm = rootSignature.alg; if (integrityAlgorithm !== 'GMAC' && integrityAlgorithm !== 'HS256') { throw new UnsupportedError(`Unsupported integrity alg [${integrityAlgorithm}]`); } - const payloadSigStr = await getSignature( + + const payloadForSigCalculation = isLegacyTDF ? Binary.fromString(hex.encode(aggregateHash)) + : Binary.fromString(aggregateHash); + const payloadSigInHex = await getSignature( keyForDecryption, - Binary.fromString(aggregateHash), + payloadForSigCalculation, integrityAlgorithm, - cfg.cryptoService + cfg.cryptoService, ); + const rootSig = isLegacyTDF ? base64.encode(payloadSigInHex) + : base64.encodeArrayBuffer(hex.decodeArrayBuffer(payloadSigInHex)); + if ( manifest.encryptionInformation.integrityInformation.rootSignature.sig !== - base64.encode(payloadSigStr) + rootSig ) { throw new IntegrityError('Failed integrity check on root signature'); } @@ -1293,7 +1319,8 @@ export async function readStream(cfg: DecryptConfiguration) { keyForDecryption, cipher, segmentIntegrityAlg, - cfg.cryptoService + cfg.cryptoService, + isLegacyTDF, ); let progress = 0; @@ -1328,3 +1355,14 @@ export async function readStream(cfg: DecryptConfiguration) { outputStream.emit('rewrap', metadata); return outputStream; } + +async function concatenateUint8Array(uint8arrays: Uint8Array[]): Promise { + // Put the inputs into a Blob. + const blob = new Blob(uint8arrays); + + // Pull an ArrayBuffer out. (Has to be async.) + const buffer = await blob.arrayBuffer(); + + // Convert that ArrayBuffer to a Uint8Array. + return new Uint8Array(buffer); +} \ No newline at end of file diff --git a/lib/tdf3/src/version.ts b/lib/tdf3/src/version.ts index fb0106b0..ac266508 100644 --- a/lib/tdf3/src/version.ts +++ b/lib/tdf3/src/version.ts @@ -1,2 +1,2 @@ -export const version = '0.1.0'; +export const version = '1.0.0'; export const clientType = 'tdf3-js-client';