Skip to content

Commit d7179c2

Browse files
feat(sdk): remove hex encoding for segment hash (#1805)
### Proposed Changes * ### Checklist - [ ] I have added or updated unit tests - [ ] I have added or updated integration tests (if appropriate) - [ ] I have added or updated documentation ### Testing Instructions --------- Co-authored-by: David Mihalcik <dmihalcik@virtru.com>
1 parent e50a0ff commit d7179c2

File tree

4 files changed

+71
-31
lines changed

4 files changed

+71
-31
lines changed

sdk/manifest.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type KeyAccess struct {
2828
EncryptedMetadata string `json:"encryptedMetadata,omitempty"`
2929
KID string `json:"kid,omitempty"`
3030
SplitID string `json:"sid,omitempty"`
31+
SchemaVersion string `json:"schemaVersion,omitempty"`
3132
}
3233

3334
type PolicyBinding struct {
@@ -62,6 +63,7 @@ type Manifest struct {
6263
EncryptionInformation `json:"encryptionInformation"`
6364
Payload `json:"payload"`
6465
Assertions []Assertion `json:"assertions,omitempty"`
66+
TDFVersion string `json:"schemaVersion,omitempty"`
6567
}
6668

6769
type attributeObject struct {

sdk/tdf.go

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
)
2424

2525
const (
26+
keyAccessSchemaVersion = "1.0"
2627
maxFileSizeSupported = 68719476736 // 64gb
2728
defaultMimeType = "application/octet-stream"
2829
tdfAsZip = "zip"
@@ -234,7 +235,8 @@ func (s SDK) CreateTDFContext(ctx context.Context, writer io.Writer, reader io.R
234235
return nil, fmt.Errorf("io.writer.Write failed: %w", err)
235236
}
236237

237-
segmentSig, err := calculateSignature(cipherData, tdfObject.payloadKey[:], tdfConfig.segmentIntegrityAlgorithm)
238+
segmentSig, err := calculateSignature(cipherData, tdfObject.payloadKey[:],
239+
tdfConfig.segmentIntegrityAlgorithm, false)
238240
if err != nil {
239241
return nil, fmt.Errorf("splitKey.GetSignaturefailed: %w", err)
240242
}
@@ -252,7 +254,8 @@ func (s SDK) CreateTDFContext(ctx context.Context, writer io.Writer, reader io.R
252254
readPos += readSize
253255
}
254256

255-
rootSignature, err := calculateSignature([]byte(aggregateHash), tdfObject.payloadKey[:], tdfConfig.integrityAlgorithm)
257+
rootSignature, err := calculateSignature([]byte(aggregateHash), tdfObject.payloadKey[:],
258+
tdfConfig.integrityAlgorithm, false)
256259
if err != nil {
257260
return nil, fmt.Errorf("splitKey.GetSignaturefailed: %w", err)
258261
}
@@ -299,11 +302,17 @@ func (s SDK) CreateTDFContext(ctx context.Context, writer io.Writer, reader io.R
299302
tmpAssertion.Statement = assertion.Statement
300303
tmpAssertion.AppliesToState = assertion.AppliesToState
301304

302-
hashOfAssertion, err := tmpAssertion.GetHash()
305+
hashOfAssertionAsHex, err := tmpAssertion.GetHash()
303306
if err != nil {
304307
return nil, err
305308
}
306309

310+
hashOfAssertion := make([]byte, hex.DecodedLen(len(hashOfAssertionAsHex)))
311+
_, err = hex.Decode(hashOfAssertion, hashOfAssertionAsHex)
312+
if err != nil {
313+
return nil, fmt.Errorf("error decoding hex string: %w", err)
314+
}
315+
307316
var completeHashBuilder strings.Builder
308317
completeHashBuilder.WriteString(aggregateHash)
309318
completeHashBuilder.Write(hashOfAssertion)
@@ -320,7 +329,7 @@ func (s SDK) CreateTDFContext(ctx context.Context, writer io.Writer, reader io.R
320329
assertionSigningKey = assertion.SigningKey
321330
}
322331

323-
if err := tmpAssertion.Sign(string(hashOfAssertion), string(encoded), assertionSigningKey); err != nil {
332+
if err := tmpAssertion.Sign(string(hashOfAssertionAsHex), string(encoded), assertionSigningKey); err != nil {
324333
return nil, fmt.Errorf("failed to sign assertion: %w", err)
325334
}
326335

@@ -358,6 +367,8 @@ func (r *Reader) Manifest() Manifest {
358367
// prepare the manifest for TDF
359368
func (s SDK) prepareManifest(ctx context.Context, t *TDFObject, tdfConfig TDFConfig) error { //nolint:funlen,gocognit // Better readability keeping it as is
360369
manifest := Manifest{}
370+
371+
manifest.TDFVersion = TDFSpecVersion
361372
if len(tdfConfig.splitPlan) == 0 && len(tdfConfig.kasInfoList) == 0 {
362373
return fmt.Errorf("%w: no key access template specified or inferred", errInvalidKasInfo)
363374
}
@@ -488,6 +499,7 @@ func (s SDK) prepareManifest(ctx context.Context, t *TDFObject, tdfConfig TDFCon
488499
EncryptedMetadata: encryptedMetadata,
489500
SplitID: splitID,
490501
WrappedKey: string(ocrypto.Base64Encode(wrappedKey)),
502+
SchemaVersion: keyAccessSchemaVersion,
491503
}
492504

493505
manifest.EncryptionInformation.KeyAccessObjs = append(manifest.EncryptionInformation.KeyAccessObjs, keyAccess)
@@ -603,6 +615,8 @@ func (r *Reader) WriteTo(writer io.Writer) (int64, error) {
603615
}
604616
}
605617

618+
isLegacyTDF := r.manifest.TDFVersion == ""
619+
606620
var totalBytes int64
607621
var payloadReadOffset int64
608622
for _, seg := range r.manifest.EncryptionInformation.IntegrityInformation.Segments {
@@ -621,7 +635,7 @@ func (r *Reader) WriteTo(writer io.Writer) (int64, error) {
621635
sigAlg = GMAC
622636
}
623637

624-
payloadSig, err := calculateSignature(readBuf, r.payloadKey, sigAlg)
638+
payloadSig, err := calculateSignature(readBuf, r.payloadKey, sigAlg, isLegacyTDF)
625639
if err != nil {
626640
return totalBytes, fmt.Errorf("splitKey.GetSignaturefailed: %w", err)
627641
}
@@ -682,6 +696,7 @@ func (r *Reader) ReadAt(buf []byte, offset int64) (int, error) { //nolint:funlen
682696
return 0, ErrTDFPayloadReadFail
683697
}
684698

699+
isLegacyTDF := r.manifest.TDFVersion == ""
685700
var decryptedBuf bytes.Buffer
686701
var payloadReadOffset int64
687702
for index, seg := range r.manifest.EncryptionInformation.IntegrityInformation.Segments {
@@ -705,7 +720,7 @@ func (r *Reader) ReadAt(buf []byte, offset int64) (int, error) { //nolint:funlen
705720
sigAlg = GMAC
706721
}
707722

708-
payloadSig, err := calculateSignature(readBuf, r.payloadKey, sigAlg)
723+
payloadSig, err := calculateSignature(readBuf, r.payloadKey, sigAlg, isLegacyTDF)
709724
if err != nil {
710725
return 0, fmt.Errorf("splitKey.GetSignaturefailed: %w", err)
711726
}
@@ -1019,18 +1034,29 @@ func (r *Reader) buildKey(_ context.Context, results []kaoResult) error {
10191034
}
10201035

10211036
// Get the hash of the assertion
1022-
hashOfAssertion, err := assertion.GetHash()
1037+
hashOfAssertionAsHex, err := assertion.GetHash()
10231038
if err != nil {
10241039
return fmt.Errorf("%w: failed to get hash of assertion: %w", ErrAssertionFailure{ID: assertion.ID}, err)
10251040
}
10261041

1042+
hashOfAssertion := make([]byte, hex.DecodedLen(len(hashOfAssertionAsHex)))
1043+
_, err = hex.Decode(hashOfAssertion, hashOfAssertionAsHex)
1044+
if err != nil {
1045+
return fmt.Errorf("error decoding hex string: %w", err)
1046+
}
1047+
1048+
isLegacyTDF := r.manifest.TDFVersion == ""
1049+
if isLegacyTDF {
1050+
hashOfAssertion = hashOfAssertionAsHex
1051+
}
1052+
10271053
var completeHashBuilder bytes.Buffer
10281054
completeHashBuilder.Write(aggregateHash.Bytes())
10291055
completeHashBuilder.Write(hashOfAssertion)
10301056

10311057
base64Hash := ocrypto.Base64Encode(completeHashBuilder.Bytes())
10321058

1033-
if string(hashOfAssertion) != assertionHash {
1059+
if string(hashOfAssertionAsHex) != assertionHash {
10341060
return fmt.Errorf("%w: assertion hash missmatch", ErrAssertionFailure{ID: assertion.ID})
10351061
}
10361062

@@ -1092,29 +1118,36 @@ func (r *Reader) doPayloadKeyUnwrap(ctx context.Context) error { //nolint:gocogn
10921118
}
10931119

10941120
// calculateSignature calculate signature of data of the given algorithm.
1095-
func calculateSignature(data []byte, secret []byte, alg IntegrityAlgorithm) (string, error) {
1121+
func calculateSignature(data []byte, secret []byte, alg IntegrityAlgorithm, isLegacyTDF bool) (string, error) {
10961122
if alg == HS256 {
10971123
hmac := ocrypto.CalculateSHA256Hmac(secret, data)
1098-
return hex.EncodeToString(hmac), nil
1124+
if isLegacyTDF {
1125+
return hex.EncodeToString(hmac), nil
1126+
}
1127+
return string(hmac), nil
10991128
}
11001129
if kGMACPayloadLength > len(data) {
11011130
return "", fmt.Errorf("fail to create gmac signature")
11021131
}
11031132

1104-
return hex.EncodeToString(data[len(data)-kGMACPayloadLength:]), nil
1133+
if isLegacyTDF {
1134+
return hex.EncodeToString(data[len(data)-kGMACPayloadLength:]), nil
1135+
}
1136+
return string(data[len(data)-kGMACPayloadLength:]), nil
11051137
}
11061138

11071139
// validate the root signature
11081140
func validateRootSignature(manifest Manifest, aggregateHash, secret []byte) (bool, error) {
11091141
rootSigAlg := manifest.EncryptionInformation.IntegrityInformation.RootSignature.Algorithm
11101142
rootSigValue := manifest.EncryptionInformation.IntegrityInformation.RootSignature.Signature
1143+
isLegacyTDF := manifest.TDFVersion == ""
11111144

11121145
sigAlg := HS256
11131146
if strings.EqualFold(gmacIntegrityAlgorithm, rootSigAlg) {
11141147
sigAlg = GMAC
11151148
}
11161149

1117-
sig, err := calculateSignature(aggregateHash, secret, sigAlg)
1150+
sig, err := calculateSignature(aggregateHash, secret, sigAlg, isLegacyTDF)
11181151
if err != nil {
11191152
return false, fmt.Errorf("splitkey.getSignature failed:%w", err)
11201153
}

sdk/tdf_test.go

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ func (s *TDFSuite) Test_SimpleTDF() {
265265
"https://example.com/attr/Classification/value/X",
266266
}
267267

268-
expectedTdfSize := int64(2095)
268+
expectedTdfSize := int64(2058)
269269
tdfFilename := "secure-text.tdf"
270270
plainText := "Virtru"
271271
{
@@ -297,7 +297,7 @@ func (s *TDFSuite) Test_SimpleTDF() {
297297
s.InDelta(float64(expectedTdfSize), float64(tdfObj.size), 32.0)
298298
}
299299

300-
// test meta data
300+
// test meta data and build meta data
301301
{
302302
readSeeker, err := os.Open(tdfFilename)
303303
s.Require().NoError(err)
@@ -395,7 +395,7 @@ func (s *TDFSuite) Test_TDFWithAssertion() {
395395
},
396396
assertionVerificationKeys: nil,
397397
disableAssertionVerification: false,
398-
expectedSize: 2896,
398+
expectedSize: 2689,
399399
},
400400
{
401401
assertions: []AssertionConfig{
@@ -428,7 +428,7 @@ func (s *TDFSuite) Test_TDFWithAssertion() {
428428
DefaultKey: defaultKey,
429429
},
430430
disableAssertionVerification: false,
431-
expectedSize: 2896,
431+
expectedSize: 2689,
432432
},
433433
{
434434
assertions: []AssertionConfig{
@@ -477,7 +477,7 @@ func (s *TDFSuite) Test_TDFWithAssertion() {
477477
},
478478
},
479479
disableAssertionVerification: false,
480-
expectedSize: 3195,
480+
expectedSize: 2988,
481481
},
482482
{
483483
assertions: []AssertionConfig{
@@ -517,7 +517,7 @@ func (s *TDFSuite) Test_TDFWithAssertion() {
517517
},
518518
},
519519
disableAssertionVerification: false,
520-
expectedSize: 2896,
520+
expectedSize: 2689,
521521
},
522522
{
523523
assertions: []AssertionConfig{
@@ -534,7 +534,7 @@ func (s *TDFSuite) Test_TDFWithAssertion() {
534534
},
535535
},
536536
disableAssertionVerification: true,
537-
expectedSize: 2302,
537+
expectedSize: 2180,
538538
},
539539
} {
540540
expectedTdfSize := test.expectedSize
@@ -643,7 +643,7 @@ func (s *TDFSuite) Test_TDFWithAssertionNegativeTests() {
643643
SigningKey: defaultKey,
644644
},
645645
},
646-
expectedSize: 2896,
646+
expectedSize: 2689,
647647
},
648648
{
649649
assertions: []AssertionConfig{
@@ -691,7 +691,7 @@ func (s *TDFSuite) Test_TDFWithAssertionNegativeTests() {
691691
},
692692
},
693693
},
694-
expectedSize: 3195,
694+
expectedSize: 2988,
695695
},
696696
{
697697
assertions: []AssertionConfig{
@@ -725,7 +725,7 @@ func (s *TDFSuite) Test_TDFWithAssertionNegativeTests() {
725725
assertionVerificationKeys: &AssertionVerificationKeys{
726726
DefaultKey: defaultKey,
727727
},
728-
expectedSize: 2896,
728+
expectedSize: 2689,
729729
},
730730
} {
731731
expectedTdfSize := test.expectedSize
@@ -940,26 +940,26 @@ func (s *TDFSuite) Test_TDF() {
940940
{
941941
n: "small",
942942
fileSize: 5,
943-
tdfFileSize: 1557,
943+
tdfFileSize: 1560,
944944
checksum: "ed968e840d10d2d313a870bc131a4e2c311d7ad09bdf32b3418147221f51a6e2",
945945
},
946946
{
947947
n: "small-with-mime-type",
948948
fileSize: 5,
949-
tdfFileSize: 1557,
949+
tdfFileSize: 1560,
950950
checksum: "ed968e840d10d2d313a870bc131a4e2c311d7ad09bdf32b3418147221f51a6e2",
951951
mimeType: "text/plain",
952952
},
953953
{
954954
n: "1-kiB",
955955
fileSize: oneKB,
956-
tdfFileSize: 2581,
956+
tdfFileSize: 2598,
957957
checksum: "2edc986847e209b4016e141a6dc8716d3207350f416969382d431539bf292e4a",
958958
},
959959
{
960960
n: "medium",
961961
fileSize: hundredMB,
962-
tdfFileSize: 104866410,
962+
tdfFileSize: 104866427,
963963
checksum: "cee41e98d0a6ad65cc0ec77a2ba50bf26d64dc9007f7f1c7d7df68b8b71291a6",
964964
},
965965
} {
@@ -1041,7 +1041,7 @@ func (s *TDFSuite) Test_KeySplits() {
10411041
{
10421042
n: "shared",
10431043
fileSize: 5,
1044-
tdfFileSize: 2664,
1044+
tdfFileSize: 2759,
10451045
checksum: "ed968e840d10d2d313a870bc131a4e2c311d7ad09bdf32b3418147221f51a6e2",
10461046
splitPlan: []keySplitStep{
10471047
{KAS: "https://a.kas/", SplitID: "a"},
@@ -1052,7 +1052,7 @@ func (s *TDFSuite) Test_KeySplits() {
10521052
{
10531053
n: "split",
10541054
fileSize: 5,
1055-
tdfFileSize: 2664,
1055+
tdfFileSize: 2759,
10561056
checksum: "ed968e840d10d2d313a870bc131a4e2c311d7ad09bdf32b3418147221f51a6e2",
10571057
splitPlan: []keySplitStep{
10581058
{KAS: "https://a.kas/", SplitID: "a"},
@@ -1063,7 +1063,7 @@ func (s *TDFSuite) Test_KeySplits() {
10631063
{
10641064
n: "mixture",
10651065
fileSize: 5,
1066-
tdfFileSize: 3211,
1066+
tdfFileSize: 3351,
10671067
checksum: "ed968e840d10d2d313a870bc131a4e2c311d7ad09bdf32b3418147221f51a6e2",
10681068
splitPlan: []keySplitStep{
10691069
{KAS: "https://a.kas/", SplitID: "a"},

sdk/version.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
package sdk
22

33
const (
4-
Version = "0.3.26" // SDK version // x-release-please-version
5-
TDFSpecVersion = "4.2.2" // Vesion of TDF Spec currently targeted by the SDK
4+
// The latest version of TDF Spec currently targeted by the SDK.
5+
// By default, new files will conform to this version of the spec
6+
// and, where possible, older versions will still be readable.
7+
TDFSpecVersion = "4.3.0"
8+
9+
// The three-part semantic version number of this SDK
10+
Version = "0.3.26" // x-release-please-version
611
)

0 commit comments

Comments
 (0)