2
2
3
3
import com .connectrpc .ResponseMessage ;
4
4
import com .connectrpc .UnaryBlockingCall ;
5
+ import com .google .protobuf .Struct ;
6
+ import com .google .protobuf .Value ;
5
7
import io .opentdf .platform .policy .KeyAccessServer ;
6
8
import io .opentdf .platform .policy .kasregistry .KeyAccessServerRegistryServiceClient ;
7
9
import io .opentdf .platform .policy .kasregistry .ListKeyAccessServersRequest ;
11
13
12
14
import java .nio .charset .StandardCharsets ;
13
15
16
+ import io .opentdf .platform .wellknownconfiguration .GetWellKnownConfigurationResponse ;
17
+ import io .opentdf .platform .wellknownconfiguration .WellKnownServiceClientInterface ;
14
18
import org .apache .commons .io .output .ByteArrayOutputStream ;
15
19
import org .junit .jupiter .api .BeforeAll ;
16
20
import org .junit .jupiter .api .Test ;
23
27
import java .util .Base64 ;
24
28
import java .util .Collections ;
25
29
import java .util .List ;
30
+ import java .util .Objects ;
26
31
import java .util .Random ;
27
32
28
33
import static org .assertj .core .api .AssertionsForClassTypes .assertThat ;
@@ -45,20 +50,33 @@ public class NanoTDFTest {
45
50
"oVP7Vpcx\n " +
46
51
"-----END PRIVATE KEY-----" ;
47
52
53
+ private static final String BASE_PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\n " +
54
+ "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/NawR/F7RJfX/odyOLPjl+5Ce1Br\n " +
55
+ "QZ/MBCIerHe26HzlBSbpa7HQHZx9PYVamHTw9+iJCY3dm8Uwp4Ab2uehnA==\n " +
56
+ "-----END PUBLIC KEY-----" ;
57
+
58
+ private static final String BASE_PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----\n " +
59
+ "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgB3YtAvS7lctHlPsq\n " +
60
+ "bZI8OX1B9W1c4GAIxzwKzD6iPkqhRANCAAT81rBH8XtEl9f+h3I4s+OX7kJ7UGtB\n " +
61
+ "n8wEIh6sd7bofOUFJulrsdAdnH09hVqYdPD36IkJjd2bxTCngBva56Gc\n " +
62
+ "-----END PRIVATE KEY-----" ;
63
+
48
64
private static final String KID = "r1" ;
65
+ private static final String BASE_KID = "basekid" ;
49
66
50
67
protected static KeyAccessServerRegistryServiceClient kasRegistryService ;
51
68
protected static List <String > registeredKases = List .of (
52
69
"https://api.example.com/kas" ,
53
70
"https://other.org/kas2" ,
54
71
"http://localhost:8181/kas" ,
55
- "https://localhost:8383/kas"
72
+ "https://localhost:8383/kas" ,
73
+ "https://api.kaswithbasekey.example.com"
56
74
);
57
75
protected static String platformUrl = "http://localhost:8080" ;
58
76
59
77
protected static SDK .KAS kas = new SDK .KAS () {
60
78
@ Override
61
- public void close () throws Exception {
79
+ public void close () {
62
80
}
63
81
64
82
@ Override
@@ -82,19 +100,14 @@ public KASInfo getECPublicKey(Config.KASInfo kasInfo, NanoTDFType.ECCurve curve)
82
100
83
101
@ Override
84
102
public byte [] unwrap (Manifest .KeyAccess keyAccess , String policy , KeyType sessionKeyType ) {
85
- int index = Integer .parseInt (keyAccess .url );
86
- var decryptor = new AsymDecryption (keypairs .get (index ).getPrivate ());
87
- var bytes = Base64 .getDecoder ().decode (keyAccess .wrappedKey );
88
- try {
89
- return decryptor .decrypt (bytes );
90
- } catch (Exception e ) {
91
- throw new RuntimeException (e );
92
- }
103
+ throw new UnsupportedOperationException ("no unwrapping ZTDFs here" );
93
104
}
94
105
95
106
@ Override
96
107
public byte [] unwrapNanoTDF (NanoTDFType .ECCurve curve , String header , String kasURL ) {
97
-
108
+ String key = Objects .equals (kasURL , "https://api.kaswithbasekey.example.com" )
109
+ ? BASE_PRIVATE_KEY
110
+ : kasPrivateKey ;
98
111
byte [] headerAsBytes = Base64 .getDecoder ().decode (header );
99
112
Header nTDFHeader = new Header (ByteBuffer .wrap (headerAsBytes ));
100
113
byte [] ephemeralKey = nTDFHeader .getEphemeralKey ();
@@ -103,7 +116,7 @@ public byte[] unwrapNanoTDF(NanoTDFType.ECCurve curve, String header, String kas
103
116
104
117
// Generate symmetric key
105
118
byte [] symmetricKey = ECKeyPair .computeECDHKey (ECKeyPair .publicKeyFromPem (publicKeyAsPem ),
106
- ECKeyPair .privateKeyFromPem (kasPrivateKey ));
119
+ ECKeyPair .privateKeyFromPem (key ));
107
120
108
121
// Generate HKDF key
109
122
MessageDigest digest ;
@@ -113,8 +126,7 @@ public byte[] unwrapNanoTDF(NanoTDFType.ECCurve curve, String header, String kas
113
126
throw new SDKException ("error creating SHA-256 message digest" , e );
114
127
}
115
128
byte [] hashOfSalt = digest .digest (NanoTDF .MAGIC_NUMBER_AND_VERSION );
116
- byte [] key = ECKeyPair .calculateHKDF (hashOfSalt , symmetricKey );
117
- return key ;
129
+ return ECKeyPair .calculateHKDF (hashOfSalt , symmetricKey );
118
130
}
119
131
120
132
@ Override
@@ -203,6 +215,35 @@ void encryptionAndDecryptionWithValidKey() throws Exception {
203
215
}
204
216
}
205
217
218
+ @ Test
219
+ void encryptionAndDecryptWithBaseKey () throws Exception {
220
+ var baseKeyJson = "{\" kas_url\" :\" https://api.kaswithbasekey.example.com\" ,\" public_key\" :{\" algorithm\" :\" ALGORITHM_EC_P256\" ,\" kid\" :\" " + BASE_KID + "\" ,\" pem\" : \" " + BASE_PUBLIC_KEY + "\" }}" ;
221
+ var val = Value .newBuilder ().setStringValue (baseKeyJson ).build ();
222
+ var config = Struct .newBuilder ().putFields ("base_key" , val ).build ();
223
+ WellKnownServiceClientInterface wellknown = mock (WellKnownServiceClientInterface .class );
224
+ GetWellKnownConfigurationResponse response = GetWellKnownConfigurationResponse .newBuilder ().setConfiguration (config ).build ();
225
+ when (wellknown .getWellKnownConfigurationBlocking (any (), any ())).thenReturn (TestUtil .successfulUnaryCall (response ));
226
+ Config .NanoTDFConfig nanoConfig = Config .newNanoTDFConfig (
227
+ Config .witDataAttributes ("https://example.com/attr/Classification/value/S" ,
228
+ "https://example.com/attr/Classification/value/X" )
229
+ );
230
+
231
+ String plainText = "Virtru!!" ;
232
+ ByteBuffer byteBuffer = ByteBuffer .wrap (plainText .getBytes ());
233
+ ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream ();
234
+ NanoTDF nanoTDF = new NanoTDF (new FakeServicesBuilder ().setKas (kas ).setKeyAccessServerRegistryService (kasRegistryService ).setWellknownService (wellknown ).build ());
235
+ nanoTDF .createNanoTDF (byteBuffer , tdfOutputStream , nanoConfig );
236
+
237
+ byte [] nanoTDFBytes = tdfOutputStream .toByteArray ();
238
+ ByteArrayOutputStream plainTextStream = new ByteArrayOutputStream ();
239
+ nanoTDF = new NanoTDF (new FakeServicesBuilder ().setKas (kas ).setKeyAccessServerRegistryService (kasRegistryService ).build ());
240
+ nanoTDF .readNanoTDF (ByteBuffer .wrap (nanoTDFBytes ), plainTextStream , platformUrl );
241
+ String out = new String (plainTextStream .toByteArray (), StandardCharsets .UTF_8 );
242
+ assertThat (out ).isEqualTo (plainText );
243
+ // KAS KID
244
+ assertThat (new Header (ByteBuffer .wrap (nanoTDFBytes )).getKasLocator ().getIdentifierString ()).isEqualTo (BASE_KID );
245
+ }
246
+
206
247
@ Test
207
248
void testWithDifferentConfigAndKeyValues () throws Exception {
208
249
var kasInfos = new ArrayList <>();
0 commit comments