Skip to content

Commit b10a61e

Browse files
biscoe916mkleeneelizabethhealypflynn-virtru
authored
fix: policy-binding new structure (#95)
Co-authored-by: Morgan Kleene <mkleene@virtru.com> Co-authored-by: Elizabeth Healy <35498075+elizabethhealy@users.noreply.github.com> Co-authored-by: Paul Flynn <43211074+pflynn-virtru@users.noreply.github.com>
1 parent 9579c42 commit b10a61e

File tree

4 files changed

+188
-33
lines changed

4 files changed

+188
-33
lines changed

sdk/src/main/java/io/opentdf/platform/sdk/Manifest.java

Lines changed: 153 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,87 @@
11
package io.opentdf.platform.sdk;
22

3+
import com.google.gson.JsonDeserializationContext;
4+
import com.google.gson.JsonDeserializer;
5+
import com.google.gson.JsonElement;
6+
import com.google.gson.JsonParseException;
7+
import com.google.gson.JsonSerializationContext;
8+
import com.google.gson.JsonSerializer;
9+
import com.google.gson.annotations.JsonAdapter;
310
import com.google.gson.annotations.SerializedName;
411

12+
import java.lang.reflect.Type;
513
import java.util.ArrayList;
614
import java.util.List;
15+
import java.util.Objects;
716

817
public class Manifest {
18+
@Override
19+
public boolean equals(Object o) {
20+
if (this == o) return true;
21+
if (o == null || getClass() != o.getClass()) return false;
22+
Manifest manifest = (Manifest) o;
23+
return Objects.equals(encryptionInformation, manifest.encryptionInformation) && Objects.equals(payload, manifest.payload) && Objects.equals(assertions, manifest.assertions);
24+
}
25+
26+
@Override
27+
public int hashCode() {
28+
return Objects.hash(encryptionInformation, payload, assertions);
29+
}
30+
31+
private static class PolicyBindingSerializer implements JsonDeserializer<Object>, JsonSerializer<Object> {
32+
@Override
33+
public Object deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
34+
if (json.isJsonObject()) {
35+
return context.deserialize(json, Manifest.PolicyBinding.class);
36+
} else if (json.isJsonPrimitive() && json.getAsJsonPrimitive().isString()) {
37+
return json.getAsString();
38+
} else {
39+
throw new JsonParseException("Unexpected type for policyBinding");
40+
}
41+
}
42+
43+
@Override
44+
public JsonElement serialize(Object src, Type typeOfSrc, JsonSerializationContext context) {
45+
return context.serialize(src, typeOfSrc);
46+
}
47+
}
948
static public class Segment {
1049
public String hash;
1150
public long segmentSize;
1251
public long encryptedSegmentSize;
52+
53+
@Override
54+
public boolean equals(Object o) {
55+
if (this == o) return true;
56+
if (o == null || getClass() != o.getClass()) return false;
57+
Segment segment = (Segment) o;
58+
return segmentSize == segment.segmentSize && encryptedSegmentSize == segment.encryptedSegmentSize && Objects.equals(hash, segment.hash);
59+
}
60+
61+
@Override
62+
public int hashCode() {
63+
return Objects.hash(hash, segmentSize, encryptedSegmentSize);
64+
}
1365
}
1466

1567
static public class RootSignature {
1668
@SerializedName(value = "alg")
1769
public String algorithm;
1870
@SerializedName(value = "sig")
1971
public String signature;
72+
73+
@Override
74+
public boolean equals(Object o) {
75+
if (this == o) return true;
76+
if (o == null || getClass() != o.getClass()) return false;
77+
RootSignature that = (RootSignature) o;
78+
return Objects.equals(algorithm, that.algorithm) && Objects.equals(signature, that.signature);
79+
}
80+
81+
@Override
82+
public int hashCode() {
83+
return Objects.hash(algorithm, signature);
84+
}
2085
}
2186

2287
static public class IntegrityInformation {
@@ -25,6 +90,37 @@ static public class IntegrityInformation {
2590
public int segmentSizeDefault;
2691
public int encryptedSegmentSizeDefault;
2792
public List<Segment> segments;
93+
94+
@Override
95+
public boolean equals(Object o) {
96+
if (this == o) return true;
97+
if (o == null || getClass() != o.getClass()) return false;
98+
IntegrityInformation that = (IntegrityInformation) o;
99+
return segmentSizeDefault == that.segmentSizeDefault && encryptedSegmentSizeDefault == that.encryptedSegmentSizeDefault && Objects.equals(rootSignature, that.rootSignature) && Objects.equals(segmentHashAlg, that.segmentHashAlg) && Objects.equals(segments, that.segments);
100+
}
101+
102+
@Override
103+
public int hashCode() {
104+
return Objects.hash(rootSignature, segmentHashAlg, segmentSizeDefault, encryptedSegmentSizeDefault, segments);
105+
}
106+
}
107+
108+
static public class PolicyBinding {
109+
public String alg;
110+
public String hash;
111+
112+
@Override
113+
public boolean equals(Object o) {
114+
if (this == o) return true;
115+
if (o == null || getClass() != o.getClass()) return false;
116+
PolicyBinding that = (PolicyBinding) o;
117+
return Objects.equals(alg, that.alg) && Objects.equals(hash, that.hash);
118+
}
119+
120+
@Override
121+
public int hashCode() {
122+
return Objects.hash(alg, hash);
123+
}
28124
}
29125

30126
static public class KeyAccess {
@@ -33,17 +129,47 @@ static public class KeyAccess {
33129
public String url;
34130
public String protocol;
35131
public String wrappedKey;
36-
public String policyBinding;
132+
@JsonAdapter(PolicyBindingSerializer.class)
133+
public Object policyBinding;
134+
37135
public String encryptedMetadata;
38136
public String kid;
137+
138+
@Override
139+
public boolean equals(Object o) {
140+
if (this == o) return true;
141+
if (o == null || getClass() != o.getClass()) return false;
142+
KeyAccess keyAccess = (KeyAccess) o;
143+
return Objects.equals(keyType, keyAccess.keyType) && Objects.equals(url, keyAccess.url) && Objects.equals(protocol, keyAccess.protocol) && Objects.equals(wrappedKey, keyAccess.wrappedKey) && Objects.equals(policyBinding, keyAccess.policyBinding) && Objects.equals(encryptedMetadata, keyAccess.encryptedMetadata) && Objects.equals(kid, keyAccess.kid);
144+
}
145+
146+
@Override
147+
public int hashCode() {
148+
return Objects.hash(keyType, url, protocol, wrappedKey, policyBinding, encryptedMetadata, kid);
149+
}
39150
}
40151

41152
static public class Method {
42153
public String algorithm;
43154
public String iv;
44155
public Boolean IsStreamable;
156+
157+
@Override
158+
public boolean equals(Object o) {
159+
if (this == o) return true;
160+
if (o == null || getClass() != o.getClass()) return false;
161+
Method method = (Method) o;
162+
return Objects.equals(algorithm, method.algorithm) && Objects.equals(iv, method.iv) && Objects.equals(IsStreamable, method.IsStreamable);
163+
}
164+
165+
@Override
166+
public int hashCode() {
167+
return Objects.hash(algorithm, iv, IsStreamable);
168+
}
45169
}
46170

171+
172+
47173
static public class EncryptionInformation {
48174
@SerializedName(value = "type")
49175
public String keyAccessType;
@@ -53,6 +179,19 @@ static public class EncryptionInformation {
53179
public List<KeyAccess> keyAccessObj;
54180
public Method method;
55181
public IntegrityInformation integrityInformation;
182+
183+
@Override
184+
public boolean equals(Object o) {
185+
if (this == o) return true;
186+
if (o == null || getClass() != o.getClass()) return false;
187+
EncryptionInformation that = (EncryptionInformation) o;
188+
return Objects.equals(keyAccessType, that.keyAccessType) && Objects.equals(policy, that.policy) && Objects.equals(keyAccessObj, that.keyAccessObj) && Objects.equals(method, that.method) && Objects.equals(integrityInformation, that.integrityInformation);
189+
}
190+
191+
@Override
192+
public int hashCode() {
193+
return Objects.hash(keyAccessType, policy, keyAccessObj, method, integrityInformation);
194+
}
56195
}
57196

58197
static public class Payload {
@@ -61,6 +200,19 @@ static public class Payload {
61200
public String protocol;
62201
public String mimeType;
63202
public Boolean isEncrypted;
203+
204+
@Override
205+
public boolean equals(Object o) {
206+
if (this == o) return true;
207+
if (o == null || getClass() != o.getClass()) return false;
208+
Payload payload = (Payload) o;
209+
return Objects.equals(type, payload.type) && Objects.equals(url, payload.url) && Objects.equals(protocol, payload.protocol) && Objects.equals(mimeType, payload.mimeType) && Objects.equals(isEncrypted, payload.isEncrypted);
210+
}
211+
212+
@Override
213+
public int hashCode() {
214+
return Objects.hash(type, url, protocol, mimeType, isEncrypted);
215+
}
64216
}
65217

66218
public EncryptionInformation encryptionInformation;

sdk/src/main/java/io/opentdf/platform/sdk/TDF.java

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,34 @@
22

33

44
import com.google.gson.Gson;
5-
import com.google.gson.GsonBuilder;
5+
import com.google.gson.JsonDeserializationContext;
6+
import com.google.gson.JsonDeserializer;
7+
import com.google.gson.JsonElement;
8+
import com.google.gson.JsonParseException;
9+
import com.google.gson.JsonSerializationContext;
10+
import com.google.gson.JsonSerializer;
611
import com.nimbusds.jose.*;
712
import com.nimbusds.jose.crypto.MACVerifier;
813
import com.nimbusds.jose.crypto.RSASSAVerifier;
914
import com.nimbusds.jwt.JWTClaimsSet;
1015
import com.nimbusds.jwt.SignedJWT;
1116
import com.nimbusds.jose.crypto.RSASSASigner;
1217
import com.nimbusds.jose.crypto.MACSigner;
13-
import com.nimbusds.jose.jwk.RSAKey;
14-
import io.opentdf.platform.kas.RewrapRequest;
1518
import org.apache.commons.codec.DecoderException;
1619
import org.apache.commons.codec.binary.Hex;
17-
import org.bouncycastle.pqc.crypto.lms.HSSSigner;
1820
import org.erdtman.jcs.JsonCanonicalizer;
1921
import org.slf4j.Logger;
2022
import org.slf4j.LoggerFactory;
2123

22-
import javax.crypto.BadPaddingException;
23-
import javax.crypto.IllegalBlockSizeException;
24-
import javax.crypto.NoSuchPaddingException;
2524
import java.io.ByteArrayOutputStream;
2625
import java.io.IOException;
2726
import java.io.InputStream;
2827
import java.io.OutputStream;
28+
import java.lang.reflect.Type;
2929
import java.nio.channels.SeekableByteChannel;
3030
import java.nio.charset.StandardCharsets;
3131
import java.security.*;
3232
import java.text.ParseException;
33-
import java.time.Duration;
34-
import java.time.Instant;
3533
import java.util.*;
3634

3735
public class TDF {
@@ -68,7 +66,8 @@ public TDF() {
6866
private static final String kAssertionHash = "assertionHash";
6967

7068
private static final SecureRandom sRandom = new SecureRandom();
71-
private static final Gson gson = new GsonBuilder().create();
69+
70+
private static final Gson gson = new Gson();
7271

7372
public static class DataSizeNotSupported extends RuntimeException {
7473
public DataSizeNotSupported(String errorMessage) {
@@ -191,7 +190,10 @@ private void prepareManifest(Config.TDFConfig tdfConfig) {
191190

192191
// Add policyBinding
193192
var hexBinding = Hex.encodeHexString(CryptoUtils.CalculateSHA256Hmac(symKey, base64PolicyObject.getBytes(StandardCharsets.UTF_8)));
194-
keyAccess.policyBinding = encoder.encodeToString(hexBinding.getBytes(StandardCharsets.UTF_8));
193+
var policyBinding = new Manifest.PolicyBinding();
194+
policyBinding.alg = kHmacIntegrityAlgorithm;
195+
policyBinding.hash = encoder.encodeToString(hexBinding.getBytes(StandardCharsets.UTF_8));
196+
keyAccess.policyBinding = policyBinding;
195197

196198
// Wrap the key with kas public key
197199
AsymEncryption asymmetricEncrypt = new AsymEncryption(kasInfo.PublicKey);
@@ -206,7 +208,7 @@ private void prepareManifest(Config.TDFConfig tdfConfig) {
206208

207209
EncryptedMetadata encryptedMetadata = new EncryptedMetadata();
208210
encryptedMetadata.iv = encoder.encodeToString(encrypted.getIv());
209-
encryptedMetadata.ciphertext = encoder.encodeToString(encrypted.getCiphertext());
211+
encryptedMetadata.ciphertext = encoder.encodeToString(encrypted.asBytes());
210212

211213
var metadata = gson.toJson(encryptedMetadata);
212214
keyAccess.encryptedMetadata = encoder.encodeToString(metadata.getBytes(StandardCharsets.UTF_8));
@@ -514,7 +516,6 @@ public Reader loadTDF(SeekableByteChannel tdf, Config.AssertionConfig assertionC
514516
EncryptedMetadata encryptedMetadata = gson.fromJson(decodedMetadata, EncryptedMetadata.class);
515517

516518
var encryptedData = new AesGcm.Encrypted(
517-
decoder.decode(encryptedMetadata.iv),
518519
decoder.decode(encryptedMetadata.ciphertext)
519520
);
520521

@@ -604,4 +605,4 @@ public Reader loadTDF(SeekableByteChannel tdf, Config.AssertionConfig assertionC
604605

605606
return new Reader(tdfReader, manifest, payloadKey, unencryptedMetadata);
606607
}
607-
}
608+
}

sdk/src/test/java/io/opentdf/platform/sdk/ManifestTest.java

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44
import com.google.gson.Gson;
55
import com.google.gson.GsonBuilder;
66

7+
import java.nio.charset.Charset;
8+
import java.nio.charset.StandardCharsets;
79
import java.util.List;
10+
import java.util.Map;
811

912
import static org.junit.jupiter.api.Assertions.assertEquals;
1013

1114
public class ManifestTest {
1215
@Test
13-
void testManifestMarshalAndUnMarshal() {
16+
void testManifestMarshalAndUnMarshal() {
1417
String kManifestJsonFromTDF = "{\n" +
1518
" \"encryptionInformation\": {\n" +
1619
" \"integrityInformation\": {\n" +
@@ -31,7 +34,10 @@ void testManifestMarshalAndUnMarshal() {
3134
" },\n" +
3235
" \"keyAccess\": [\n" +
3336
" {\n" +
34-
" \"policyBinding\": \"YTgzNThhNzc5NWRhMjdjYThlYjk4ZmNmODliNzc2Y2E5ZmZiZDExZDQ3OTM5ODFjZTRjNmE3MmVjOTUzZTFlMA==\",\n" +
37+
" \"policyBinding\": {\n" +
38+
" \"alg\": \"HS256\",\n" +
39+
" \"hash\": \"YTgzNThhNzc5NWRhMjdjYThlYjk4ZmNmODliNzc2Y2E5ZmZiZDExZDQ3OTM5ODFjZTRjNmE3MmVjOTUzZTFlMA==\"\n" +
40+
" },\n" +
3541
" \"protocol\": \"kas\",\n" +
3642
" \"type\": \"wrapped\",\n" +
3743
" \"url\": \"http://localhost:65432/kas\",\n" +
@@ -70,25 +76,18 @@ void testManifestMarshalAndUnMarshal() {
7076
List<Manifest.KeyAccess> keyAccess = manifest.encryptionInformation.keyAccessObj;
7177
assertEquals(keyAccess.get(0).keyType, "wrapped");
7278
assertEquals(keyAccess.get(0).protocol, "kas");
73-
79+
assertEquals(Manifest.PolicyBinding.class, keyAccess.get(0).policyBinding.getClass());
80+
var policyBinding = (Manifest.PolicyBinding) keyAccess.get(0).policyBinding;
81+
assertEquals(policyBinding.alg, "HS256");
82+
assertEquals(policyBinding.hash, "YTgzNThhNzc5NWRhMjdjYThlYjk4ZmNmODliNzc2Y2E5ZmZiZDExZDQ3OTM5ODFjZTRjNmE3MmVjOTUzZTFlMA==");
7483
assertEquals(manifest.encryptionInformation.method.algorithm, "AES-256-GCM");
7584
assertEquals(manifest.encryptionInformation.integrityInformation.rootSignature.algorithm, "HS256");
7685
assertEquals(manifest.encryptionInformation.integrityInformation.segmentHashAlg, "GMAC");
7786
assertEquals(manifest.encryptionInformation.integrityInformation.segments.get(0).segmentSize, 1048576);
7887

79-
System.out.println(gson.toJson(manifest));
80-
81-
82-
Manifest.Payload payload = new Manifest.Payload();
83-
payload.protocol = "zip";
84-
85-
Manifest.EncryptionInformation encryptionInformation = manifest.encryptionInformation;
86-
encryptionInformation.policy = "updated policy";
87-
88-
Manifest newManifest = new Manifest();
89-
newManifest.payload = payload;
90-
newManifest.encryptionInformation = encryptionInformation;
88+
var serialized = gson.toJson(manifest);
89+
var deserializedAgain = gson.fromJson(serialized, Manifest.class);
9190

92-
System.out.println(gson.toJson(newManifest));
91+
assertEquals(manifest, deserializedAgain, "something changed when we deserialized -> serialized -> deserialized");
9392
}
9493
}

sdk/src/test/java/io/opentdf/platform/sdk/TDFWriterTest.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class TDFWriterTest {
1616
@Test
1717
void simpleTDFCreate() throws IOException {
1818

19-
String kManifestJsonFromTDF = "{\n" +
19+
String kManifestJsonFromTDF = "{\n" +
2020
" \"encryptionInformation\": {\n" +
2121
" \"integrityInformation\": {\n" +
2222
" \"encryptedSegmentSizeDefault\": 1048604,\n" +
@@ -36,7 +36,10 @@ void simpleTDFCreate() throws IOException {
3636
" },\n" +
3737
" \"keyAccess\": [\n" +
3838
" {\n" +
39-
" \"policyBinding\": \"YTgzNThhNzc5NWRhMjdjYThlYjk4ZmNmODliNzc2Y2E5ZmZiZDExZDQ3OTM5ODFjZTRjNmE3MmVjOTUzZTFlMA==\",\n" +
39+
" \"policyBinding\": {\n" +
40+
" \"alg\": \"HS256\",\n" +
41+
" \"hash\": \"YTgzNThhNzc5NWRhMjdjYThlYjk4ZmNmODliNzc2Y2E5ZmZiZDExZDQ3OTM5ODFjZTRjNmE3MmVjOTUzZTFlMA==\"\n" +
42+
" },\n" +
4043
" \"protocol\": \"kas\",\n" +
4144
" \"type\": \"wrapped\",\n" +
4245
" \"url\": \"http://localhost:65432/kas\",\n" +

0 commit comments

Comments
 (0)