@@ -4,10 +4,13 @@ import (
4
4
"archive/zip"
5
5
"bytes"
6
6
"context"
7
+ "crypto/ecdsa"
7
8
"crypto/rand"
8
9
"crypto/rsa"
9
10
"crypto/sha256"
11
+ "crypto/x509"
10
12
"encoding/json"
13
+ "encoding/pem"
11
14
"fmt"
12
15
"io"
13
16
"log/slog"
@@ -204,6 +207,36 @@ Iuxu2zA7cGQNhhUi6MKr5cUWl6tBprAghzdwEH1cZQsBiV3ki7fCCiDURIJaTlNq
204
207
uOnQR2c7Dix39LZQCiEfPSUnTAKJCyMpolky7Vq31PsPKk+gK19XftfH/Aul21vt
205
208
ZwVW7fLwZ2SSmC9cOjSkzZw/eDwwIRNgo94OL4mw5cXSPOuMeO8Tugc6LO4v91SO
206
209
yg==
210
+ -----END CERTIFICATE-----`
211
+ mockECPrivateKey1 = `-----BEGIN PRIVATE KEY-----
212
+ MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgokydHKV9HW88nqn9
213
+ 2U2J1AqvcjrLDRCH6NBdNVqYLJOhRANCAASu1haeL6ckVfALALUlJKsehW8xomA9
214
+ dcWMuYTECCukuGCklqiD0ofQAo+stVTRjen+zxM7C6MJaHdsbE4Pf088
215
+ -----END PRIVATE KEY-----`
216
+ mockECPublicKey1 = `-----BEGIN CERTIFICATE-----
217
+ MIIBcTCCARegAwIBAgIURFydDqs4150ytI73sMRmya2fvTMwCgYIKoZIzj0EAwIw
218
+ DjEMMAoGA1UEAwwDa2FzMB4XDTI0MDYxMTAxNTU0N1oXDTI1MDYxMTAxNTU0N1ow
219
+ DjEMMAoGA1UEAwwDa2FzMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAErtYWni+n
220
+ JFXwCwC1JSSrHoVvMaJgPXXFjLmExAgrpLhgpJaog9KH0AKPrLVU0Y3p/s8TOwuj
221
+ CWh3bGxOD39PPKNTMFEwHQYDVR0OBBYEFLg9mMeD25ZGvmjSYaunIPoeekzlMB8G
222
+ A1UdIwQYMBaAFLg9mMeD25ZGvmjSYaunIPoeekzlMA8GA1UdEwEB/wQFMAMBAf8w
223
+ CgYIKoZIzj0EAwIDSAAwRQIhALYXC70t37RlmIkRDlUTehiVEHpSQXz04wQ9Ivw+
224
+ 4h4hAiBNR3rD3KieiJaiJrCfM6TPJL7TIch7pAhMHdG6IPJMoQ==
225
+ -----END CERTIFICATE-----`
226
+ mockECPrivateKey2 = `-----BEGIN PRIVATE KEY-----
227
+ MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgokydHKV9HW88nqn9
228
+ 2U2J1AqvcjrLDRCH6NBdNVqYLJOhRANCAASu1haeL6ckVfALALUlJKsehW8xomA9
229
+ dcWMuYTECCukuGCklqiD0ofQAo+stVTRjen+zxM7C6MJaHdsbE4Pf088
230
+ -----END PRIVATE KEY-----`
231
+ mockECPublicKey2 = `-----BEGIN CERTIFICATE-----
232
+ MIIBcTCCARegAwIBAgIURFydDqs4150ytI73sMRmya2fvTMwCgYIKoZIzj0EAwIw
233
+ DjEMMAoGA1UEAwwDa2FzMB4XDTI0MDYxMTAxNTU0N1oXDTI1MDYxMTAxNTU0N1ow
234
+ DjEMMAoGA1UEAwwDa2FzMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAErtYWni+n
235
+ JFXwCwC1JSSrHoVvMaJgPXXFjLmExAgrpLhgpJaog9KH0AKPrLVU0Y3p/s8TOwuj
236
+ CWh3bGxOD39PPKNTMFEwHQYDVR0OBBYEFLg9mMeD25ZGvmjSYaunIPoeekzlMB8G
237
+ A1UdIwQYMBaAFLg9mMeD25ZGvmjSYaunIPoeekzlMA8GA1UdEwEB/wQFMAMBAf8w
238
+ CgYIKoZIzj0EAwIDSAAwRQIhALYXC70t37RlmIkRDlUTehiVEHpSQXz04wQ9Ivw+
239
+ 4h4hAiBNR3rD3KieiJaiJrCfM6TPJL7TIch7pAhMHdG6IPJMoQ==
207
240
-----END CERTIFICATE-----`
208
241
)
209
242
@@ -263,6 +296,11 @@ func TestTDF(t *testing.T) {
263
296
}
264
297
265
298
func (s * TDFSuite ) Test_SimpleTDF () {
299
+ type TestConfig struct {
300
+ tdfOptions []TDFOption
301
+ tdfReadOptions []TDFReaderOption
302
+ }
303
+
266
304
metaData := []byte (`{"displayName" : "openTDF go sdk"}` )
267
305
attributes := []string {
268
306
"https://example.com/attr/Classification/value/S" ,
@@ -272,14 +310,37 @@ func (s *TDFSuite) Test_SimpleTDF() {
272
310
expectedTdfSize := int64 (2058 )
273
311
tdfFilename := "secure-text.tdf"
274
312
plainText := "Virtru"
275
- {
276
- kasURLs := []KASInfo {
277
- {
278
- URL : "https://a.kas/" ,
279
- PublicKey : "" ,
313
+
314
+ // add opts ...TDFOption to TestConfig
315
+ testConfigs := []TestConfig {
316
+ {
317
+ tdfOptions : []TDFOption {
318
+ WithKasInformation (KASInfo {
319
+ URL : "https://a.kas/" ,
320
+ PublicKey : "" ,
321
+ }),
322
+ WithMetaData (string (metaData )),
323
+ WithDataAttributes (attributes ... ),
280
324
},
281
- }
325
+ tdfReadOptions : []TDFReaderOption {},
326
+ },
327
+ {
328
+ tdfOptions : []TDFOption {
329
+ WithKasInformation (KASInfo {
330
+ URL : "https://d.kas/" ,
331
+ PublicKey : "" ,
332
+ }),
333
+ WithMetaData (string (metaData )),
334
+ WithDataAttributes (attributes ... ),
335
+ WithWrappingKeyAlg (ocrypto .EC256Key ),
336
+ },
337
+ tdfReadOptions : []TDFReaderOption {
338
+ WithSessionKeyType (ocrypto .EC256Key ),
339
+ },
340
+ },
341
+ }
282
342
343
+ for _ , config := range testConfigs {
283
344
inBuf := bytes .NewBufferString (plainText )
284
345
bufReader := bytes .NewReader (inBuf .Bytes ())
285
346
@@ -291,18 +352,12 @@ func (s *TDFSuite) Test_SimpleTDF() {
291
352
s .Require ().NoError (err )
292
353
}(fileWriter )
293
354
294
- tdfObj , err := s .sdk .CreateTDF (fileWriter , bufReader ,
295
- WithKasInformation (kasURLs ... ),
296
- WithMetaData (string (metaData )),
297
- WithDataAttributes (attributes ... ),
298
- )
355
+ tdfObj , err := s .sdk .CreateTDF (fileWriter , bufReader , config .tdfOptions ... )
299
356
300
357
s .Require ().NoError (err )
301
- s .InDelta (float64 (expectedTdfSize ), float64 (tdfObj .size ), 32.0 )
302
- }
358
+ s .InDelta (float64 (expectedTdfSize ), float64 (tdfObj .size ), 36.0 )
303
359
304
- // test meta data and build meta data
305
- {
360
+ // test meta data and build meta data
306
361
readSeeker , err := os .Open (tdfFilename )
307
362
s .Require ().NoError (err )
308
363
@@ -311,28 +366,23 @@ func (s *TDFSuite) Test_SimpleTDF() {
311
366
s .Require ().NoError (err )
312
367
}(readSeeker )
313
368
314
- r , err := s .sdk .LoadTDF (readSeeker )
315
-
369
+ r , err := s .sdk .LoadTDF (readSeeker , config .tdfReadOptions ... )
316
370
s .Require ().NoError (err )
317
371
318
372
unencryptedMetaData , err := r .UnencryptedMetadata ()
319
373
s .Require ().NoError (err )
320
-
321
374
s .EqualValues (metaData , unencryptedMetaData )
322
375
323
376
dataAttributes , err := r .DataAttributes ()
324
377
s .Require ().NoError (err )
325
-
326
378
s .Equal (attributes , dataAttributes )
327
379
328
380
payloadKey , err := r .UnsafePayloadKeyRetrieval ()
329
381
s .Require ().NoError (err )
330
382
s .Len (payloadKey , kKeySize )
331
- }
332
383
333
- // test reader
334
- {
335
- readSeeker , err := os .Open (tdfFilename )
384
+ // test reader
385
+ readSeeker , err = os .Open (tdfFilename )
336
386
s .Require ().NoError (err )
337
387
338
388
defer func (readSeeker * os.File ) {
@@ -341,8 +391,7 @@ func (s *TDFSuite) Test_SimpleTDF() {
341
391
}(readSeeker )
342
392
343
393
buf := make ([]byte , 8 )
344
-
345
- r , err := s .sdk .LoadTDF (readSeeker )
394
+ r , err = s .sdk .LoadTDF (readSeeker , config .tdfReadOptions ... )
346
395
s .Require ().NoError (err )
347
396
348
397
offset := 2
@@ -353,9 +402,9 @@ func (s *TDFSuite) Test_SimpleTDF() {
353
402
354
403
expectedPlainTxt := plainText [offset : offset + n ]
355
404
s .Equal (expectedPlainTxt , string (buf [:n ]))
356
- }
357
405
358
- _ = os .Remove (tdfFilename )
406
+ _ = os .Remove (tdfFilename )
407
+ }
359
408
}
360
409
361
410
func (s * TDFSuite ) Test_TDFWithAssertion () {
@@ -1613,7 +1662,7 @@ func (s *TDFSuite) startBackend() {
1613
1662
return l .Dial ()
1614
1663
}
1615
1664
1616
- s .kases = make ([]FakeKas , 10 )
1665
+ s .kases = make ([]FakeKas , 12 )
1617
1666
1618
1667
for i , ki := range []struct {
1619
1668
url , private , public string
@@ -1623,6 +1672,8 @@ func (s *TDFSuite) startBackend() {
1623
1672
{"https://a.kas/" , mockRSAPrivateKey1 , mockRSAPublicKey1 },
1624
1673
{"https://b.kas/" , mockRSAPrivateKey2 , mockRSAPublicKey2 },
1625
1674
{"https://c.kas/" , mockRSAPrivateKey3 , mockRSAPublicKey3 },
1675
+ {"https://d.kas/" , mockECPrivateKey1 , mockECPublicKey1 },
1676
+ {"https://e.kas/" , mockECPrivateKey2 , mockECPublicKey2 },
1626
1677
{kasAu , mockRSAPrivateKey1 , mockRSAPublicKey1 },
1627
1678
{kasCa , mockRSAPrivateKey2 , mockRSAPublicKey2 },
1628
1679
{kasUk , mockRSAPrivateKey2 , mockRSAPublicKey2 },
@@ -1757,22 +1808,83 @@ func (f *FakeKas) getRewrapResponse(rewrapRequest string) *kaspb.RewrapResponse
1757
1808
kao := kaoReq .GetKeyAccessObject ()
1758
1809
wrappedKey := kaoReq .GetKeyAccessObject ().GetWrappedKey ()
1759
1810
1760
- kasPrivateKey := strings .ReplaceAll (f .privateKey , "\n \t " , "\n " )
1761
- if kao .GetKid () != "" && kao .GetKid () != f .KID {
1762
- // old kid
1763
- lk , ok := f .legakeys [kaoReq .GetKeyAccessObject ().GetKid ()]
1764
- f .s .Require ().True (ok , "unable to find key [%s]" , kao .GetKid ())
1765
- kasPrivateKey = strings .ReplaceAll (lk .private , "\n \t " , "\n " )
1811
+ var entityWrappedKey []byte
1812
+ switch kaoReq .GetKeyAccessObject ().GetKeyType () {
1813
+ case "ec-wrapped" :
1814
+ // Get the ephemeral public key in PEM format
1815
+ ephemeralPubKeyPEM := kaoReq .GetKeyAccessObject ().GetEphemeralPublicKey ()
1816
+
1817
+ // Get EC key size and convert to mode
1818
+ keySize , err := ocrypto .GetECKeySize ([]byte (ephemeralPubKeyPEM ))
1819
+ f .s .Require ().NoError (err , "failed to get EC key size" )
1820
+
1821
+ mode , err := ocrypto .ECSizeToMode (keySize )
1822
+ f .s .Require ().NoError (err , "failed to convert key size to mode" )
1823
+
1824
+ // Parse the PEM public key
1825
+ block , _ := pem .Decode ([]byte (ephemeralPubKeyPEM ))
1826
+ f .s .Require ().NoError (err , "failed to decode PEM block" )
1827
+
1828
+ pub , err := x509 .ParsePKIXPublicKey (block .Bytes )
1829
+ f .s .Require ().NoError (err , "failed to parse public key" )
1830
+
1831
+ ecPub , ok := pub .(* ecdsa.PublicKey )
1832
+ if ! ok {
1833
+ f .s .Require ().Error (err , "not an EC public key" )
1834
+ }
1835
+
1836
+ // Compress the public key
1837
+ compressedKey , err := ocrypto .CompressedECPublicKey (mode , * ecPub )
1838
+ f .s .Require ().NoError (err , "failed to compress public key" )
1839
+
1840
+ kasPrivateKey := strings .ReplaceAll (f .privateKey , "\n \t " , "\n " )
1841
+ if kao .GetKid () != "" && kao .GetKid () != f .KID {
1842
+ // old kid
1843
+ lk , found := f .legakeys [kaoReq .GetKeyAccessObject ().GetKid ()]
1844
+ f .s .Require ().True (found , "unable to find key [%s]" , kao .GetKid ())
1845
+ kasPrivateKey = strings .ReplaceAll (lk .private , "\n \t " , "\n " )
1846
+ }
1847
+
1848
+ privateKey , err := ocrypto .ECPrivateKeyFromPem ([]byte (kasPrivateKey ))
1849
+ f .s .Require ().NoError (err , "failed to extract private key from PEM" )
1850
+
1851
+ ed , err := ocrypto .NewECDecryptor (privateKey )
1852
+ f .s .Require ().NoError (err , "failed to create EC decryptor" )
1853
+
1854
+ symmetricKey , err := ed .DecryptWithEphemeralKey (wrappedKey , compressedKey )
1855
+ f .s .Require ().NoError (err , "failed to decrypt" )
1856
+
1857
+ asymEncrypt , err := ocrypto .FromPublicPEM (bodyData .GetClientPublicKey ())
1858
+ f .s .Require ().NoError (err , "ocrypto.FromPublicPEM failed" )
1859
+
1860
+ var sessionKey string
1861
+ if e , found := asymEncrypt .(ocrypto.ECEncryptor ); found {
1862
+ sessionKey , err = e .PublicKeyInPemFormat ()
1863
+ f .s .Require ().NoError (err , "unable to serialize ephemeral key" )
1864
+ }
1865
+ resp .SessionPublicKey = sessionKey
1866
+ entityWrappedKey , err = asymEncrypt .Encrypt (symmetricKey )
1867
+ f .s .Require ().NoError (err , "ocrypto.AsymEncryption.encrypt failed" )
1868
+
1869
+ case "wrapped" :
1870
+ kasPrivateKey := strings .ReplaceAll (f .privateKey , "\n \t " , "\n " )
1871
+ if kao .GetKid () != "" && kao .GetKid () != f .KID {
1872
+ // old kid
1873
+ lk , ok := f .legakeys [kaoReq .GetKeyAccessObject ().GetKid ()]
1874
+ f .s .Require ().True (ok , "unable to find key [%s]" , kao .GetKid ())
1875
+ kasPrivateKey = strings .ReplaceAll (lk .private , "\n \t " , "\n " )
1876
+ }
1877
+
1878
+ asymDecrypt , err := ocrypto .NewAsymDecryption (kasPrivateKey )
1879
+ f .s .Require ().NoError (err , "ocrypto.NewAsymDecryption failed" )
1880
+ symmetricKey , err := asymDecrypt .Decrypt (wrappedKey )
1881
+ f .s .Require ().NoError (err , "ocrypto.Decrypt failed" )
1882
+ asymEncrypt , err := ocrypto .NewAsymEncryption (bodyData .GetClientPublicKey ())
1883
+ f .s .Require ().NoError (err , "ocrypto.NewAsymEncryption failed" )
1884
+ entityWrappedKey , err = asymEncrypt .Encrypt (symmetricKey )
1885
+ f .s .Require ().NoError (err , "ocrypto.encrypt failed" )
1766
1886
}
1767
1887
1768
- asymDecrypt , err := ocrypto .NewAsymDecryption (kasPrivateKey )
1769
- f .s .Require ().NoError (err , "ocrypto.NewAsymDecryption failed" )
1770
- symmetricKey , err := asymDecrypt .Decrypt (wrappedKey )
1771
- f .s .Require ().NoError (err , "ocrypto.Decrypt failed" )
1772
- asymEncrypt , err := ocrypto .NewAsymEncryption (bodyData .GetClientPublicKey ())
1773
- f .s .Require ().NoError (err , "ocrypto.NewAsymEncryption failed" )
1774
- entityWrappedKey , err := asymEncrypt .Encrypt (symmetricKey )
1775
- f .s .Require ().NoError (err , "ocrypto.encrypt failed" )
1776
1888
kaoResult := & kaspb.KeyAccessRewrapResult {
1777
1889
Result : & kaspb.KeyAccessRewrapResult_KasWrappedKey {KasWrappedKey : entityWrappedKey },
1778
1890
Status : "permit" ,
0 commit comments