17
17
import org .slf4j .Logger ;
18
18
import org .slf4j .LoggerFactory ;
19
19
20
- import java .io .BufferedReader ;
21
20
import java .io .ByteArrayOutputStream ;
22
21
import java .io .IOException ;
23
22
import java .io .InputStream ;
24
- import java .io .InputStreamReader ;
25
23
import java .io .OutputStream ;
26
24
import java .io .StringReader ;
27
25
import java .nio .channels .SeekableByteChannel ;
39
37
*/
40
38
public class TDF {
41
39
40
+ private static final String TDF_VERSION = "4.3.0" ;
41
+ private static final String KEY_ACCESS_SECHMA_VERSION = "4.3.0" ;
42
42
private final long maximumSize ;
43
43
44
44
/**
@@ -203,6 +203,7 @@ PolicyObject createPolicyObject(List<Autoconfigure.AttributeValueFQN> attributes
203
203
private static final Base64 .Encoder encoder = Base64 .getEncoder ();
204
204
205
205
private void prepareManifest (Config .TDFConfig tdfConfig , SDK .KAS kas ) {
206
+ manifest .tdfVersion = TDF_VERSION ;
206
207
manifest .encryptionInformation .keyAccessType = kSplitKeyType ;
207
208
manifest .encryptionInformation .keyAccessObj = new ArrayList <>();
208
209
@@ -307,6 +308,7 @@ private void prepareManifest(Config.TDFConfig tdfConfig, SDK.KAS kas) {
307
308
keyAccess .wrappedKey = encoder .encodeToString (wrappedKey );
308
309
keyAccess .encryptedMetadata = encryptedMetadata ;
309
310
keyAccess .sid = splitID ;
311
+ keyAccess .schemaVersion = KEY_ACCESS_SECHMA_VERSION ;
310
312
311
313
manifest .encryptionInformation .keyAccessObj .add (keyAccess );
312
314
}
@@ -370,6 +372,8 @@ public void readPayload(OutputStream outputStream) throws TDFReadFailed,
370
372
throw new TDFReadFailed ("failed to read payload" );
371
373
}
372
374
375
+ var isLegacyTdf = manifest .tdfVersion == null || manifest .tdfVersion .isEmpty ();
376
+
373
377
if (manifest .payload .isEncrypted ) {
374
378
String segHashAlg = manifest .encryptionInformation .integrityInformation .segmentHashAlg ;
375
379
Config .IntegrityAlgorithm sigAlg = Config .IntegrityAlgorithm .HS256 ;
@@ -378,9 +382,11 @@ public void readPayload(OutputStream outputStream) throws TDFReadFailed,
378
382
}
379
383
380
384
var payloadSig = calculateSignature (readBuf , payloadKey , sigAlg );
381
- byte [] payloadSigAsBytes = payloadSig .getBytes (StandardCharsets .UTF_8 );
385
+ if (isLegacyTdf ) {
386
+ payloadSig = Hex .encodeHexString (payloadSig ).getBytes (StandardCharsets .UTF_8 );
387
+ }
382
388
383
- if (segment .hash .compareTo (Base64 .getEncoder ().encodeToString (payloadSigAsBytes )) != 0 ) {
389
+ if (segment .hash .compareTo (Base64 .getEncoder ().encodeToString (payloadSig )) != 0 ) {
384
390
throw new SegmentSignatureMismatch ("segment signature miss match" );
385
391
}
386
392
@@ -403,24 +409,22 @@ public PolicyObject readPolicyObject() {
403
409
}
404
410
}
405
411
406
- private static String calculateSignature (byte [] data , byte [] secret , Config .IntegrityAlgorithm algorithm ) {
412
+ private static byte [] calculateSignature (byte [] data , byte [] secret , Config .IntegrityAlgorithm algorithm ) {
407
413
if (algorithm == Config .IntegrityAlgorithm .HS256 ) {
408
- byte [] hmac = CryptoUtils .CalculateSHA256Hmac (secret , data );
409
- return Hex .encodeHexString (hmac );
414
+ return CryptoUtils .CalculateSHA256Hmac (secret , data );
410
415
}
411
416
412
417
if (kGMACPayloadLength > data .length ) {
413
418
throw new FailedToCreateGMAC ("fail to create gmac signature" );
414
419
}
415
420
416
- byte [] gmacPayload = Arrays .copyOfRange (data , data .length - kGMACPayloadLength , data .length );
417
- return Hex .encodeHexString (gmacPayload );
421
+ return Arrays .copyOfRange (data , data .length - kGMACPayloadLength , data .length );
418
422
}
419
423
420
424
public TDFObject createTDF (InputStream payload ,
421
425
OutputStream outputStream ,
422
426
Config .TDFConfig tdfConfig , SDK .KAS kas , AttributesServiceFutureStub attrService )
423
- throws IOException , JOSEException , AutoConfigureException , InterruptedException , ExecutionException {
427
+ throws IOException , JOSEException , AutoConfigureException , InterruptedException , ExecutionException , DecoderException {
424
428
425
429
if (tdfConfig .autoconfigure ) {
426
430
Autoconfigure .Granter granter = new Autoconfigure .Granter (new ArrayList <>());
@@ -454,7 +458,7 @@ public TDFObject createTDF(InputStream payload,
454
458
long encryptedSegmentSize = tdfConfig .defaultSegmentSize + kGcmIvSize + kAesBlockSize ;
455
459
TDFWriter tdfWriter = new TDFWriter (outputStream );
456
460
457
- StringBuilder aggregateHash = new StringBuilder ();
461
+ ByteArrayOutputStream aggregateHash = new ByteArrayOutputStream ();
458
462
byte [] readBuf = new byte [tdfConfig .defaultSegmentSize ];
459
463
460
464
tdfObject .manifest .encryptionInformation .integrityInformation .segments = new ArrayList <>();
@@ -476,17 +480,17 @@ public TDFObject createTDF(InputStream payload,
476
480
}
477
481
478
482
byte [] cipherData ;
479
- String segmentSig ;
483
+ byte [] segmentSig ;
480
484
Manifest .Segment segmentInfo = new Manifest .Segment ();
481
485
482
486
// encrypt
483
487
cipherData = tdfObject .aesGcm .encrypt (readBuf , 0 , readThisLoop ).asBytes ();
484
488
payloadOutput .write (cipherData );
485
489
486
490
segmentSig = calculateSignature (cipherData , tdfObject .payloadKey , tdfConfig .segmentIntegrityAlgorithm );
487
- segmentInfo .hash = Base64 .getEncoder ().encodeToString (segmentSig . getBytes ( StandardCharsets . UTF_8 ) );
491
+ segmentInfo .hash = Base64 .getEncoder ().encodeToString (segmentSig );
488
492
489
- aggregateHash .append (segmentSig );
493
+ aggregateHash .write (segmentSig );
490
494
segmentInfo .segmentSize = readThisLoop ;
491
495
segmentInfo .encryptedSegmentSize = cipherData .length ;
492
496
@@ -496,9 +500,9 @@ public TDFObject createTDF(InputStream payload,
496
500
497
501
Manifest .RootSignature rootSignature = new Manifest .RootSignature ();
498
502
499
- String rootSig = calculateSignature (aggregateHash .toString (). getBytes (),
503
+ byte [] rootSig = calculateSignature (aggregateHash .toByteArray (),
500
504
tdfObject .payloadKey , tdfConfig .integrityAlgorithm );
501
- rootSignature .signature = Base64 .getEncoder ().encodeToString (rootSig . getBytes ( StandardCharsets . UTF_8 ) );
505
+ rootSignature .signature = Base64 .getEncoder ().encodeToString (rootSig );
502
506
503
507
String alg = kGmacIntegrityAlgorithm ;
504
508
if (tdfConfig .integrityAlgorithm == Config .IntegrityAlgorithm .HS256 ) {
@@ -534,19 +538,24 @@ public TDFObject createTDF(InputStream payload,
534
538
assertion .statement = assertionConfig .statement ;
535
539
assertion .appliesToState = assertionConfig .appliesToState .toString ();
536
540
537
- var assertionHash = assertion .hash ();
538
- var completeHashBuilder = new StringBuilder (aggregateHash );
539
- completeHashBuilder .append (assertionHash );
541
+ var assertionHashAsHex = assertion .hash ();
542
+ var assertionHash = Hex .decodeHex (assertionHashAsHex );
543
+ byte [] completeHash = new byte [aggregateHash .size () + assertionHash .length ];
544
+ System .arraycopy (aggregateHash .toByteArray (), 0 , completeHash , 0 , aggregateHash .size ());
545
+ System .arraycopy (assertionHash , 0 , completeHash , aggregateHash .size (), assertionHash .length );
540
546
541
- var encodedHash = Base64 .getEncoder ().encodeToString (completeHashBuilder . toString (). getBytes () );
547
+ var encodedHash = Base64 .getEncoder ().encodeToString (completeHash );
542
548
543
549
var assertionSigningKey = new AssertionConfig .AssertionKey (AssertionConfig .AssertionKeyAlg .HS256 ,
544
550
tdfObject .aesGcm .getKey ());
545
551
if (assertionConfig .signingKey != null && assertionConfig .signingKey .isDefined ()) {
546
552
assertionSigningKey = assertionConfig .signingKey ;
547
553
}
548
-
549
- assertion .sign (new Manifest .Assertion .HashValues (assertionHash , encodedHash ), assertionSigningKey );
554
+ var hashValues = new Manifest .Assertion .HashValues (
555
+ assertionHashAsHex ,
556
+ encodedHash
557
+ );
558
+ assertion .sign (hashValues , assertionSigningKey );
550
559
signedAssertions .add (assertion );
551
560
}
552
561
@@ -594,7 +603,7 @@ public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas,
594
603
byte [] payloadKey = new byte [GCM_KEY_SIZE ];
595
604
String unencryptedMetadata = null ;
596
605
597
- Set <String > knownSplits = new HashSet <String >();
606
+ Set <String > knownSplits = new HashSet <>();
598
607
Set <String > foundSplits = new HashSet <>();
599
608
600
609
Map <Autoconfigure .KeySplitStep , Exception > skippedSplits = new HashMap <>();
@@ -632,7 +641,7 @@ public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas,
632
641
AesGcm aesGcm = new AesGcm (unwrappedKey );
633
642
634
643
String decodedMetadata = new String (Base64 .getDecoder ().decode (keyAccess .encryptedMetadata ),
635
- "UTF-8" );
644
+ StandardCharsets . UTF_8 );
636
645
EncryptedMetadata encryptedMetadata = gson .fromJson (decodedMetadata , EncryptedMetadata .class );
637
646
638
647
var encryptedData = new AesGcm .Encrypted (
@@ -679,15 +688,18 @@ public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas,
679
688
}
680
689
681
690
String rootSigValue ;
691
+ boolean isLegacyTdf = manifest .tdfVersion == null || manifest .tdfVersion .isEmpty ();
682
692
if (manifest .payload .isEncrypted ) {
683
693
Config .IntegrityAlgorithm sigAlg = Config .IntegrityAlgorithm .HS256 ;
684
694
if (rootAlgorithm .compareToIgnoreCase (kGmacIntegrityAlgorithm ) == 0 ) {
685
695
sigAlg = Config .IntegrityAlgorithm .GMAC ;
686
696
}
687
697
688
698
var sig = calculateSignature (aggregateHash .toByteArray (), payloadKey , sigAlg );
689
- rootSigValue = Base64 .getEncoder ().encodeToString (sig .getBytes (StandardCharsets .UTF_8 ));
690
-
699
+ if (isLegacyTdf ) {
700
+ sig = Hex .encodeHexString (sig ).getBytes ();
701
+ }
702
+ rootSigValue = Base64 .getEncoder ().encodeToString (sig );
691
703
} else {
692
704
rootSigValue = Base64 .getEncoder ().encodeToString (digest .digest (aggregateHash .toString ().getBytes ()));
693
705
}
@@ -703,6 +715,7 @@ public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas,
703
715
throw new SegmentSizeMismatch ("mismatch encrypted segment size in manifest" );
704
716
}
705
717
718
+ var aggregateHashByteArrayBytes = aggregateHash .toByteArray ();
706
719
// Validate assertions
707
720
for (var assertion : manifest .assertions ) {
708
721
// Skip assertion verification if disabled
@@ -721,16 +734,18 @@ public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas,
721
734
}
722
735
723
736
var hashValues = assertion .verify (assertionKey );
724
- var assertionAsJson = gson .toJson (assertion );
725
- JsonCanonicalizer jc = new JsonCanonicalizer (assertionAsJson );
726
- var hashOfAssertion = Hex .encodeHexString (digest .digest (jc .getEncodedUTF8 ()));
727
- var signature = aggregateHash + hashOfAssertion ;
728
- var encodeSignature = Base64 .getEncoder ().encodeToString (signature .getBytes ());
737
+ var hashOfAssertionAsHex = assertion .hash ();
729
738
730
- if (!Objects .equals (hashOfAssertion , hashValues .getAssertionHash ())) {
739
+ if (!Objects .equals (hashOfAssertionAsHex , hashValues .getAssertionHash ())) {
731
740
throw new AssertionException ("assertion hash mismatch" , assertion .id );
732
741
}
733
742
743
+ byte [] hashOfAssertion = isLegacyTdf ? hashOfAssertionAsHex .getBytes (StandardCharsets .UTF_8 ) : Hex .decodeHex (hashOfAssertionAsHex );
744
+ var signature = new byte [aggregateHashByteArrayBytes .length + hashOfAssertion .length ];
745
+ System .arraycopy (aggregateHashByteArrayBytes , 0 , signature , 0 , aggregateHashByteArrayBytes .length );
746
+ System .arraycopy (hashOfAssertion , 0 , signature , aggregateHashByteArrayBytes .length , hashOfAssertion .length );
747
+ var encodeSignature = Base64 .getEncoder ().encodeToString (signature );
748
+
734
749
if (!Objects .equals (encodeSignature , hashValues .getSignature ())) {
735
750
throw new AssertionException ("failed integrity check on assertion signature" , assertion .id );
736
751
}
0 commit comments