Skip to content

Commit b2fce36

Browse files
committed
feat(sdk): remove hex encoding for segment hash
1 parent ab40664 commit b2fce36

File tree

3 files changed

+60
-21
lines changed

3 files changed

+60
-21
lines changed

lib/tdf3/src/models/manifest.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export type Manifest = {
66
payload: Payload;
77
encryptionInformation: EncryptionInformation;
88
assertions: Assertion[];
9+
tdf_spec_version : string;
910
};

lib/tdf3/src/tdf.ts

Lines changed: 58 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { EntityObject } from '../../src/tdf/index.js';
66
import { pemToCryptoPublicKey, validateSecureUrl } from '../../src/utils.js';
77
import { DecryptParams } from './client/builders.js';
88
import { AssertionConfig, AssertionKey, AssertionVerificationKeys } from './assertions.js';
9+
import { version } from './version.js';
10+
import { hex } from '../../src/encodings/index.js';
911
import * as assertions from './assertions.js';
1012

1113
import {
@@ -457,6 +459,7 @@ async function _generateManifest(
457459
// generate the manifest first, then insert integrity information into it
458460
encryptionInformation: encryptionInformationStr,
459461
assertions: assertions,
462+
tdf_spec_version: version,
460463
};
461464
}
462465

@@ -616,7 +619,7 @@ export async function writeStream(cfg: EncryptConfiguration): Promise<DecoratedR
616619
let bytesProcessed = 0;
617620
let crcCounter = 0;
618621
let fileByteCount = 0;
619-
let aggregateHash = '';
622+
let segmentHashList:Uint8Array[] = [];
620623

621624
const zipWriter = new ZipWriter();
622625
const manifest = await _generateManifest(
@@ -720,14 +723,17 @@ export async function writeStream(cfg: EncryptConfiguration): Promise<DecoratedR
720723
fileByteCount = 0;
721724

722725
// hash the concat of all hashes
723-
const payloadSigStr = await getSignature(
726+
const aggregateHash = await concatenateUint8Array(segmentHashList);
727+
const payloadSigInHex = await getSignature(
724728
cfg.keyForEncryption.unwrappedKeyBinary,
725-
Binary.fromString(aggregateHash),
729+
Binary.fromArrayBuffer(aggregateHash.buffer),
726730
cfg.integrityAlgorithm,
727731
cfg.cryptoService
728732
);
733+
734+
const payloadSig = hex.decodeArrayBuffer(payloadSigInHex);
729735
manifest.encryptionInformation.integrityInformation.rootSignature.sig =
730-
base64.encode(payloadSigStr);
736+
base64.encodeArrayBuffer(payloadSig);
731737
manifest.encryptionInformation.integrityInformation.rootSignature.alg =
732738
cfg.integrityAlgorithm;
733739

@@ -749,7 +755,8 @@ export async function writeStream(cfg: EncryptConfiguration): Promise<DecoratedR
749755
alg: 'HS256',
750756
key: new Uint8Array(cfg.keyForEncryption.unwrappedKeyBinary.asArrayBuffer()),
751757
};
752-
const assertion = await assertions.CreateAssertion(aggregateHash, {
758+
const combinedHashString = new TextDecoder().decode(aggregateHash);
759+
const assertion = await assertions.CreateAssertion(combinedHashString, {
753760
...assertionConfig,
754761
signingKey,
755762
});
@@ -839,18 +846,18 @@ export async function writeStream(cfg: EncryptConfiguration): Promise<DecoratedR
839846
cfg.keyForEncryption.unwrappedKeyBinary
840847
);
841848
const payloadBuffer = new Uint8Array(encryptedResult.payload.asByteArray());
842-
const payloadSigStr = await getSignature(
849+
const payloadSigInHex = await getSignature(
843850
cfg.keyForEncryption.unwrappedKeyBinary,
844851
encryptedResult.payload,
845852
cfg.segmentIntegrityAlgorithm,
846853
cfg.cryptoService
847854
);
848-
849-
// combined string of all hashes for root signature
850-
aggregateHash += payloadSigStr;
855+
856+
const payloadSig = hex.decodeArrayBuffer(payloadSigInHex);
857+
segmentHashList.push(new Uint8Array(payloadSig));
851858

852859
segmentInfos.push({
853-
hash: base64.encode(payloadSigStr),
860+
hash: base64.encodeArrayBuffer(payloadSig),
854861
segmentSize: chunk.length === segmentSizeDefault ? undefined : chunk.length,
855862
encryptedSegmentSize:
856863
payloadBuffer.length === encryptedSegmentSizeDefault ? undefined : payloadBuffer.length,
@@ -1068,17 +1075,22 @@ async function decryptChunk(
10681075
hash: string,
10691076
cipher: SymmetricCipher,
10701077
segmentIntegrityAlgorithm: IntegrityAlgorithm,
1071-
cryptoService: CryptoService
1078+
cryptoService: CryptoService,
1079+
isLegacyTDF: boolean
10721080
): Promise<DecryptResult> {
10731081
if (segmentIntegrityAlgorithm !== 'GMAC' && segmentIntegrityAlgorithm !== 'HS256') {
10741082
}
1075-
const segmentHashStr = await getSignature(
1083+
const segmentHashAsHex = await getSignature(
10761084
reconstructedKeyBinary,
10771085
Binary.fromArrayBuffer(encryptedChunk.buffer),
10781086
segmentIntegrityAlgorithm,
10791087
cryptoService
10801088
);
1081-
if (hash !== btoa(segmentHashStr)) {
1089+
1090+
const segmentHash = isLegacyTDF ? btoa(segmentHashAsHex) :
1091+
btoa(String.fromCharCode(...new Uint8Array(hex.decodeArrayBuffer(segmentHashAsHex))));
1092+
1093+
if (hash !== segmentHash) {
10821094
throw new IntegrityError('Failed integrity check on segment hash');
10831095
}
10841096
return await cipher.decrypt(encryptedChunk, reconstructedKeyBinary);
@@ -1091,7 +1103,8 @@ async function updateChunkQueue(
10911103
reconstructedKeyBinary: Binary,
10921104
cipher: SymmetricCipher,
10931105
segmentIntegrityAlgorithm: IntegrityAlgorithm,
1094-
cryptoService: CryptoService
1106+
cryptoService: CryptoService,
1107+
isLegacyTDF: boolean,
10951108
) {
10961109
const chunksInOneDownload = 500;
10971110
let requests = [];
@@ -1132,6 +1145,7 @@ async function updateChunkQueue(
11321145
slice,
11331146
cipher,
11341147
segmentIntegrityAlgorithm,
1148+
isLegacyTDF
11351149
});
11361150
}
11371151
})()
@@ -1146,13 +1160,15 @@ export async function sliceAndDecrypt({
11461160
cipher,
11471161
cryptoService,
11481162
segmentIntegrityAlgorithm,
1163+
isLegacyTDF
11491164
}: {
11501165
buffer: Uint8Array;
11511166
reconstructedKeyBinary: Binary;
11521167
slice: Chunk[];
11531168
cipher: SymmetricCipher;
11541169
cryptoService: CryptoService;
11551170
segmentIntegrityAlgorithm: IntegrityAlgorithm;
1171+
isLegacyTDF: boolean;
11561172
}) {
11571173
for (const index in slice) {
11581174
const { encryptedOffset, encryptedSegmentSize, _resolve, _reject } = slice[index];
@@ -1170,7 +1186,8 @@ export async function sliceAndDecrypt({
11701186
slice[index]['hash'],
11711187
cipher,
11721188
segmentIntegrityAlgorithm,
1173-
cryptoService
1189+
cryptoService,
1190+
isLegacyTDF,
11741191
);
11751192
slice[index].decryptedChunk = result;
11761193
if (_resolve) {
@@ -1218,22 +1235,31 @@ export async function readStream(cfg: DecryptConfiguration) {
12181235
const keyForDecryption = await cfg.keyMiddleware(reconstructedKeyBinary);
12191236
const encryptedSegmentSizeDefault = defaultSegmentSize || DEFAULT_SEGMENT_SIZE;
12201237

1238+
// check if the TDF is a legacy TDF
1239+
const isLegacyTDF = manifest.tdf_spec_version ? false : true;
1240+
12211241
// check the combined string of hashes
12221242
const aggregateHash = segments.map(({ hash }) => base64.decode(hash)).join('');
12231243
const integrityAlgorithm = rootSignature.alg;
12241244
if (integrityAlgorithm !== 'GMAC' && integrityAlgorithm !== 'HS256') {
12251245
throw new UnsupportedError(`Unsupported integrity alg [${integrityAlgorithm}]`);
12261246
}
1227-
const payloadSigStr = await getSignature(
1247+
1248+
const payloadForSigCalculation = isLegacyTDF ? Binary.fromString(hex.encode(aggregateHash))
1249+
: Binary.fromString(aggregateHash);
1250+
const payloadSigInHex = await getSignature(
12281251
keyForDecryption,
1229-
Binary.fromString(aggregateHash),
1252+
payloadForSigCalculation,
12301253
integrityAlgorithm,
1231-
cfg.cryptoService
1254+
cfg.cryptoService,
12321255
);
12331256

1257+
const rootSig = isLegacyTDF ? base64.encode(payloadSigInHex)
1258+
: base64.encodeArrayBuffer(hex.decodeArrayBuffer(payloadSigInHex));
1259+
12341260
if (
12351261
manifest.encryptionInformation.integrityInformation.rootSignature.sig !==
1236-
base64.encode(payloadSigStr)
1262+
rootSig
12371263
) {
12381264
throw new IntegrityError('Failed integrity check on root signature');
12391265
}
@@ -1293,7 +1319,8 @@ export async function readStream(cfg: DecryptConfiguration) {
12931319
keyForDecryption,
12941320
cipher,
12951321
segmentIntegrityAlg,
1296-
cfg.cryptoService
1322+
cfg.cryptoService,
1323+
isLegacyTDF,
12971324
);
12981325

12991326
let progress = 0;
@@ -1328,3 +1355,14 @@ export async function readStream(cfg: DecryptConfiguration) {
13281355
outputStream.emit('rewrap', metadata);
13291356
return outputStream;
13301357
}
1358+
1359+
async function concatenateUint8Array(uint8arrays: Uint8Array[]): Promise<Uint8Array> {
1360+
// Put the inputs into a Blob.
1361+
const blob = new Blob(uint8arrays);
1362+
1363+
// Pull an ArrayBuffer out. (Has to be async.)
1364+
const buffer = await blob.arrayBuffer();
1365+
1366+
// Convert that ArrayBuffer to a Uint8Array.
1367+
return new Uint8Array(buffer);
1368+
}

lib/tdf3/src/version.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
export const version = '0.1.0';
1+
export const version = '1.0.0';
22
export const clientType = 'tdf3-js-client';

0 commit comments

Comments
 (0)