@@ -6,6 +6,8 @@ import { EntityObject } from '../../src/tdf/index.js';
6
6
import { pemToCryptoPublicKey , validateSecureUrl } from '../../src/utils.js' ;
7
7
import { DecryptParams } from './client/builders.js' ;
8
8
import { AssertionConfig , AssertionKey , AssertionVerificationKeys } from './assertions.js' ;
9
+ import { version } from './version.js' ;
10
+ import { hex } from '../../src/encodings/index.js' ;
9
11
import * as assertions from './assertions.js' ;
10
12
11
13
import {
@@ -457,6 +459,7 @@ async function _generateManifest(
457
459
// generate the manifest first, then insert integrity information into it
458
460
encryptionInformation : encryptionInformationStr ,
459
461
assertions : assertions ,
462
+ tdf_spec_version : version ,
460
463
} ;
461
464
}
462
465
@@ -616,7 +619,7 @@ export async function writeStream(cfg: EncryptConfiguration): Promise<DecoratedR
616
619
let bytesProcessed = 0 ;
617
620
let crcCounter = 0 ;
618
621
let fileByteCount = 0 ;
619
- let aggregateHash = '' ;
622
+ let segmentHashList : Uint8Array [ ] = [ ] ;
620
623
621
624
const zipWriter = new ZipWriter ( ) ;
622
625
const manifest = await _generateManifest (
@@ -720,14 +723,17 @@ export async function writeStream(cfg: EncryptConfiguration): Promise<DecoratedR
720
723
fileByteCount = 0 ;
721
724
722
725
// hash the concat of all hashes
723
- const payloadSigStr = await getSignature (
726
+ const aggregateHash = await concatenateUint8Array ( segmentHashList ) ;
727
+ const payloadSigInHex = await getSignature (
724
728
cfg . keyForEncryption . unwrappedKeyBinary ,
725
- Binary . fromString ( aggregateHash ) ,
729
+ Binary . fromArrayBuffer ( aggregateHash . buffer ) ,
726
730
cfg . integrityAlgorithm ,
727
731
cfg . cryptoService
728
732
) ;
733
+
734
+ const payloadSig = hex . decodeArrayBuffer ( payloadSigInHex ) ;
729
735
manifest . encryptionInformation . integrityInformation . rootSignature . sig =
730
- base64 . encode ( payloadSigStr ) ;
736
+ base64 . encodeArrayBuffer ( payloadSig ) ;
731
737
manifest . encryptionInformation . integrityInformation . rootSignature . alg =
732
738
cfg . integrityAlgorithm ;
733
739
@@ -749,7 +755,8 @@ export async function writeStream(cfg: EncryptConfiguration): Promise<DecoratedR
749
755
alg : 'HS256' ,
750
756
key : new Uint8Array ( cfg . keyForEncryption . unwrappedKeyBinary . asArrayBuffer ( ) ) ,
751
757
} ;
752
- const assertion = await assertions . CreateAssertion ( aggregateHash , {
758
+ const combinedHashString = new TextDecoder ( ) . decode ( aggregateHash ) ;
759
+ const assertion = await assertions . CreateAssertion ( combinedHashString , {
753
760
...assertionConfig ,
754
761
signingKey,
755
762
} ) ;
@@ -839,18 +846,18 @@ export async function writeStream(cfg: EncryptConfiguration): Promise<DecoratedR
839
846
cfg . keyForEncryption . unwrappedKeyBinary
840
847
) ;
841
848
const payloadBuffer = new Uint8Array ( encryptedResult . payload . asByteArray ( ) ) ;
842
- const payloadSigStr = await getSignature (
849
+ const payloadSigInHex = await getSignature (
843
850
cfg . keyForEncryption . unwrappedKeyBinary ,
844
851
encryptedResult . payload ,
845
852
cfg . segmentIntegrityAlgorithm ,
846
853
cfg . cryptoService
847
854
) ;
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 ) ) ;
851
858
852
859
segmentInfos . push ( {
853
- hash : base64 . encode ( payloadSigStr ) ,
860
+ hash : base64 . encodeArrayBuffer ( payloadSig ) ,
854
861
segmentSize : chunk . length === segmentSizeDefault ? undefined : chunk . length ,
855
862
encryptedSegmentSize :
856
863
payloadBuffer . length === encryptedSegmentSizeDefault ? undefined : payloadBuffer . length ,
@@ -1068,17 +1075,22 @@ async function decryptChunk(
1068
1075
hash : string ,
1069
1076
cipher : SymmetricCipher ,
1070
1077
segmentIntegrityAlgorithm : IntegrityAlgorithm ,
1071
- cryptoService : CryptoService
1078
+ cryptoService : CryptoService ,
1079
+ isLegacyTDF : boolean
1072
1080
) : Promise < DecryptResult > {
1073
1081
if ( segmentIntegrityAlgorithm !== 'GMAC' && segmentIntegrityAlgorithm !== 'HS256' ) {
1074
1082
}
1075
- const segmentHashStr = await getSignature (
1083
+ const segmentHashAsHex = await getSignature (
1076
1084
reconstructedKeyBinary ,
1077
1085
Binary . fromArrayBuffer ( encryptedChunk . buffer ) ,
1078
1086
segmentIntegrityAlgorithm ,
1079
1087
cryptoService
1080
1088
) ;
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 ) {
1082
1094
throw new IntegrityError ( 'Failed integrity check on segment hash' ) ;
1083
1095
}
1084
1096
return await cipher . decrypt ( encryptedChunk , reconstructedKeyBinary ) ;
@@ -1091,7 +1103,8 @@ async function updateChunkQueue(
1091
1103
reconstructedKeyBinary : Binary ,
1092
1104
cipher : SymmetricCipher ,
1093
1105
segmentIntegrityAlgorithm : IntegrityAlgorithm ,
1094
- cryptoService : CryptoService
1106
+ cryptoService : CryptoService ,
1107
+ isLegacyTDF : boolean ,
1095
1108
) {
1096
1109
const chunksInOneDownload = 500 ;
1097
1110
let requests = [ ] ;
@@ -1132,6 +1145,7 @@ async function updateChunkQueue(
1132
1145
slice,
1133
1146
cipher,
1134
1147
segmentIntegrityAlgorithm,
1148
+ isLegacyTDF
1135
1149
} ) ;
1136
1150
}
1137
1151
} ) ( )
@@ -1146,13 +1160,15 @@ export async function sliceAndDecrypt({
1146
1160
cipher,
1147
1161
cryptoService,
1148
1162
segmentIntegrityAlgorithm,
1163
+ isLegacyTDF
1149
1164
} : {
1150
1165
buffer : Uint8Array ;
1151
1166
reconstructedKeyBinary : Binary ;
1152
1167
slice : Chunk [ ] ;
1153
1168
cipher : SymmetricCipher ;
1154
1169
cryptoService : CryptoService ;
1155
1170
segmentIntegrityAlgorithm : IntegrityAlgorithm ;
1171
+ isLegacyTDF : boolean ;
1156
1172
} ) {
1157
1173
for ( const index in slice ) {
1158
1174
const { encryptedOffset, encryptedSegmentSize, _resolve, _reject } = slice [ index ] ;
@@ -1170,7 +1186,8 @@ export async function sliceAndDecrypt({
1170
1186
slice [ index ] [ 'hash' ] ,
1171
1187
cipher ,
1172
1188
segmentIntegrityAlgorithm ,
1173
- cryptoService
1189
+ cryptoService ,
1190
+ isLegacyTDF ,
1174
1191
) ;
1175
1192
slice [ index ] . decryptedChunk = result ;
1176
1193
if ( _resolve ) {
@@ -1218,22 +1235,31 @@ export async function readStream(cfg: DecryptConfiguration) {
1218
1235
const keyForDecryption = await cfg . keyMiddleware ( reconstructedKeyBinary ) ;
1219
1236
const encryptedSegmentSizeDefault = defaultSegmentSize || DEFAULT_SEGMENT_SIZE ;
1220
1237
1238
+ // check if the TDF is a legacy TDF
1239
+ const isLegacyTDF = manifest . tdf_spec_version ? false : true ;
1240
+
1221
1241
// check the combined string of hashes
1222
1242
const aggregateHash = segments . map ( ( { hash } ) => base64 . decode ( hash ) ) . join ( '' ) ;
1223
1243
const integrityAlgorithm = rootSignature . alg ;
1224
1244
if ( integrityAlgorithm !== 'GMAC' && integrityAlgorithm !== 'HS256' ) {
1225
1245
throw new UnsupportedError ( `Unsupported integrity alg [${ integrityAlgorithm } ]` ) ;
1226
1246
}
1227
- const payloadSigStr = await getSignature (
1247
+
1248
+ const payloadForSigCalculation = isLegacyTDF ? Binary . fromString ( hex . encode ( aggregateHash ) )
1249
+ : Binary . fromString ( aggregateHash ) ;
1250
+ const payloadSigInHex = await getSignature (
1228
1251
keyForDecryption ,
1229
- Binary . fromString ( aggregateHash ) ,
1252
+ payloadForSigCalculation ,
1230
1253
integrityAlgorithm ,
1231
- cfg . cryptoService
1254
+ cfg . cryptoService ,
1232
1255
) ;
1233
1256
1257
+ const rootSig = isLegacyTDF ? base64 . encode ( payloadSigInHex )
1258
+ : base64 . encodeArrayBuffer ( hex . decodeArrayBuffer ( payloadSigInHex ) ) ;
1259
+
1234
1260
if (
1235
1261
manifest . encryptionInformation . integrityInformation . rootSignature . sig !==
1236
- base64 . encode ( payloadSigStr )
1262
+ rootSig
1237
1263
) {
1238
1264
throw new IntegrityError ( 'Failed integrity check on root signature' ) ;
1239
1265
}
@@ -1293,7 +1319,8 @@ export async function readStream(cfg: DecryptConfiguration) {
1293
1319
keyForDecryption ,
1294
1320
cipher ,
1295
1321
segmentIntegrityAlg ,
1296
- cfg . cryptoService
1322
+ cfg . cryptoService ,
1323
+ isLegacyTDF ,
1297
1324
) ;
1298
1325
1299
1326
let progress = 0 ;
@@ -1328,3 +1355,14 @@ export async function readStream(cfg: DecryptConfiguration) {
1328
1355
outputStream . emit ( 'rewrap' , metadata ) ;
1329
1356
return outputStream ;
1330
1357
}
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
+ }
0 commit comments