Skip to content

Commit f77d110

Browse files
mustyantsevmkleene
andauthored
feat(sdk): MIC-1436: User can decrypt TDF files created with FileWatcher2.0.8 and older. (#1833)
### Proposed Changes *We need decrypt old FW tdf files. Assertions are represented as JSON object ### Checklist - [x ] 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: Morgan Kleene <mkleene@virtru.com>
1 parent 1218d52 commit f77d110

File tree

2 files changed

+184
-0
lines changed

2 files changed

+184
-0
lines changed

sdk/assertion.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,41 @@ func (a Assertion) GetHash() ([]byte, error) {
123123
return ocrypto.SHA256AsHex(transformedJSON), nil
124124
}
125125

126+
func (s *Statement) UnmarshalJSON(data []byte) error {
127+
// Define a custom struct for deserialization
128+
type Alias Statement
129+
aux := &struct {
130+
Value json.RawMessage `json:"value,omitempty"`
131+
*Alias
132+
}{
133+
Alias: (*Alias)(s),
134+
}
135+
136+
if err := json.Unmarshal(data, &aux); err != nil {
137+
return err
138+
}
139+
140+
// Attempt to decode Value as an object
141+
var temp map[string]interface{}
142+
if json.Unmarshal(aux.Value, &temp) == nil {
143+
// Re-encode the object as a string and assign to Value
144+
objAsString, err := json.Marshal(temp)
145+
if err != nil {
146+
return err
147+
}
148+
s.Value = string(objAsString)
149+
} else {
150+
// Assign raw string to Value
151+
var str string
152+
if err := json.Unmarshal(aux.Value, &str); err != nil {
153+
return fmt.Errorf("value is neither a valid JSON object nor a string: %s", string(aux.Value))
154+
}
155+
s.Value = str
156+
}
157+
158+
return nil
159+
}
160+
126161
// Statement includes information applying to the scope of the assertion.
127162
// It could contain rights, handling instructions, or general metadata.
128163
type Statement struct {

sdk/assertion_test.go

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package sdk
22

33
import (
4+
"encoding/json"
45
"testing"
56

7+
"github.com/gowebpki/jcs"
68
"github.com/stretchr/testify/assert"
79
"github.com/stretchr/testify/require"
810
)
@@ -33,3 +35,150 @@ func TestTDFWithAssertion(t *testing.T) {
3335

3436
assert.Equal(t, "4a447a13c5a32730d20bdf7feecb9ffe16649bc731914b574d80035a3927f860", string(hashOfAssertion))
3537
}
38+
39+
func TestTDFWithAssertionJsonObject(t *testing.T) {
40+
// Define the assertion config with a JSON object in the statement value
41+
value := `{
42+
"ocl": {
43+
"pol": "2ccf11cb-6c9a-4e49-9746-a7f0a295945d",
44+
"cls": "SECRET",
45+
"catl": [
46+
{
47+
"type": "P",
48+
"name": "Releasable To",
49+
"vals": ["usa"]
50+
}
51+
],
52+
"dcr": "2024-12-17T13:00:52Z"
53+
},
54+
"context": {
55+
"@base": "urn:nato:stanag:5636:A:1:elements:json"
56+
}
57+
}`
58+
assertionConfig := AssertionConfig{
59+
ID: "ab43266781e64b51a4c52ffc44d6152c",
60+
Type: "handling",
61+
Scope: "payload",
62+
AppliesToState: "", // Use "" or a pointer to a string if necessary
63+
Statement: Statement{
64+
Format: "json-structured",
65+
Value: value,
66+
},
67+
}
68+
69+
// Set up the assertion
70+
assertion := Assertion{
71+
ID: assertionConfig.ID,
72+
Type: assertionConfig.Type,
73+
Scope: assertionConfig.Scope,
74+
AppliesToState: assertionConfig.AppliesToState,
75+
Statement: assertionConfig.Statement,
76+
}
77+
78+
var obj map[string]interface{}
79+
err := json.Unmarshal([]byte(assertionConfig.Statement.Value), &obj)
80+
require.NoError(t, err, "Unmarshaling the Value into a map should succeed")
81+
82+
ocl, ok := obj["ocl"].(map[string]interface{})
83+
require.True(t, ok, "Parsed Value should contain 'ocl' as an object")
84+
require.Equal(t, "SECRET", ocl["cls"], "'cls' field should match")
85+
require.Equal(t, "2ccf11cb-6c9a-4e49-9746-a7f0a295945d", ocl["pol"], "'pol' field should match")
86+
87+
context, ok := obj["context"].(map[string]interface{})
88+
require.True(t, ok, "Parsed Value should contain 'context' as an object")
89+
require.Equal(t, "urn:nato:stanag:5636:A:1:elements:json", context["@base"], "'@base' field should match")
90+
91+
// Calculate the hash of the assertion
92+
hashOfAssertion, err := assertion.GetHash()
93+
require.NoError(t, err)
94+
95+
expectedHash := "722dd40a90a0f7ec718fb156207a647e64daa43c0ae1f033033473a172c72aee"
96+
assert.Equal(t, expectedHash, string(hashOfAssertion))
97+
}
98+
99+
func TestDeserializingAssertionWithJSONInStatementValue(t *testing.T) {
100+
// the assertion has a JSON object in the statement value
101+
assertionVal := ` {
102+
"id": "bacbe31eab384df39d35a5fbe83778de",
103+
"type": "handling",
104+
"scope": "tdo",
105+
"appliesToState": null,
106+
"statement": {
107+
"format": "json-structured",
108+
"value": {
109+
"ocl": {
110+
"pol": "2ccf11cb-6c9a-4e49-9746-a7f0a295945d",
111+
"cls": "SECRET",
112+
"catl": [
113+
{
114+
"type": "P",
115+
"name": "Releasable To",
116+
"vals": [
117+
"usa"
118+
]
119+
}
120+
],
121+
"dcr": "2024-12-17T13:00:52Z"
122+
},
123+
"context": {
124+
"@base": "urn:nato:stanag:5636:A:1:elements:json"
125+
}
126+
}
127+
},
128+
"binding": {
129+
"method": "jws",
130+
"signature": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJDb25maWRlbnRpYWxpdHlJbmZvcm1hdGlvbiI6InsgXCJvY2xcIjogeyBcInBvbFwiOiBcIjJjY2YxMWNiLTZjOWEtNGU0OS05NzQ2LWE3ZjBhMjk1OTQ1ZFwiLCBcImNsc1wiOiBcIlNFQ1JFVFwiLCBcImNhdGxcIjogWyB7IFwidHlwZVwiOiBcIlBcIiwgXCJuYW1lXCI6IFwiUmVsZWFzYWJsZSBUb1wiLCBcInZhbHNcIjogWyBcInVzYVwiIF0gfSBdLCBcImRjclwiOiBcIjIwMjQtMTItMTdUMTM6MDA6NTJaXCIgfSwgXCJjb250ZXh0XCI6IHsgXCJAYmFzZVwiOiBcInVybjpuYXRvOnN0YW5hZzo1NjM2OkE6MTplbGVtZW50czpqc29uXCIgfSB9In0.LlOzRLKKXMAqXDNsx9Ha5915CGcAkNLuBfI7jJmx6CnfQrLXhlRHWW3_aLv5DPsKQC6vh9gDQBH19o7q7EcukvK4IabA4l0oP8ePgHORaajyj7ONjoeudv_zQ9XN7xU447S3QznzOoasuWAFoN4682Fhf99Kjl6rhDCzmZhTwQw9drP7s41nNA5SwgEhoZj-X9KkNW5GbWjA95eb8uVRRWk8dOnVje6j8mlJuOtKdhMxQ8N5n0vBYYhiss9c4XervBjWAxwAMdbRaQN0iPZtMzIkxKLYxBZDvTnYSAqzpvfGPzkSI-Ze_hUZs2hp-ADNnYUJBf_LzFmKyqHjPSFQ7A"
131+
}
132+
}`
133+
134+
var assertion Assertion
135+
err := json.Unmarshal([]byte(assertionVal), &assertion)
136+
require.NoError(t, err, "Error deserializing the assertion with a JSON object in the statement value")
137+
138+
var expectedAssertionValue, _ = jcs.Transform([]byte(`{
139+
"ocl": {
140+
"pol": "2ccf11cb-6c9a-4e49-9746-a7f0a295945d",
141+
"cls": "SECRET",
142+
"catl": [
143+
{
144+
"type": "P",
145+
"name": "Releasable To",
146+
"vals": [
147+
"usa"
148+
]
149+
}
150+
],
151+
"dcr": "2024-12-17T13:00:52Z"
152+
},
153+
"context": {
154+
"@base": "urn:nato:stanag:5636:A:1:elements:json"
155+
}
156+
}`))
157+
actualAssertionValue, err := jcs.Transform([]byte(assertion.Statement.Value))
158+
require.NoError(t, err, "Error transforming the assertion statement value")
159+
assert.Equal(t, expectedAssertionValue, actualAssertionValue)
160+
}
161+
162+
func TestDeserializingAssertionWithStringInStatementValue(t *testing.T) {
163+
// the assertion has a JSON object in the statement value
164+
assertionVal := ` {
165+
"id": "bacbe31eab384df39d35a5fbe83778de",
166+
"type": "handling",
167+
"scope": "tdo",
168+
"appliesToState": null,
169+
"statement": {
170+
"format": "json-structured",
171+
"value": "this is a value"
172+
},
173+
"binding": {
174+
"method": "jws",
175+
"signature": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJDb25maWRlbnRpYWxpdHlJbmZvcm1hdGlvbiI6InsgXCJvY2xcIjogeyBcInBvbFwiOiBcIjJjY2YxMWNiLTZjOWEtNGU0OS05NzQ2LWE3ZjBhMjk1OTQ1ZFwiLCBcImNsc1wiOiBcIlNFQ1JFVFwiLCBcImNhdGxcIjogWyB7IFwidHlwZVwiOiBcIlBcIiwgXCJuYW1lXCI6IFwiUmVsZWFzYWJsZSBUb1wiLCBcInZhbHNcIjogWyBcInVzYVwiIF0gfSBdLCBcImRjclwiOiBcIjIwMjQtMTItMTdUMTM6MDA6NTJaXCIgfSwgXCJjb250ZXh0XCI6IHsgXCJAYmFzZVwiOiBcInVybjpuYXRvOnN0YW5hZzo1NjM2OkE6MTplbGVtZW50czpqc29uXCIgfSB9In0.LlOzRLKKXMAqXDNsx9Ha5915CGcAkNLuBfI7jJmx6CnfQrLXhlRHWW3_aLv5DPsKQC6vh9gDQBH19o7q7EcukvK4IabA4l0oP8ePgHORaajyj7ONjoeudv_zQ9XN7xU447S3QznzOoasuWAFoN4682Fhf99Kjl6rhDCzmZhTwQw9drP7s41nNA5SwgEhoZj-X9KkNW5GbWjA95eb8uVRRWk8dOnVje6j8mlJuOtKdhMxQ8N5n0vBYYhiss9c4XervBjWAxwAMdbRaQN0iPZtMzIkxKLYxBZDvTnYSAqzpvfGPzkSI-Ze_hUZs2hp-ADNnYUJBf_LzFmKyqHjPSFQ7A"
176+
}
177+
}`
178+
179+
var assertion Assertion
180+
err := json.Unmarshal([]byte(assertionVal), &assertion)
181+
require.NoError(t, err, "Error deserializing the assertion with a JSON object in the statement value")
182+
183+
assert.Equal(t, "this is a value", assertion.Statement.Value)
184+
}

0 commit comments

Comments
 (0)