Skip to content

Commit 67b6fb8

Browse files
feat(core): Adds EC withSalt options (#2126)
### Proposed Changes * Allows apps and libs to specity their own salt (and info) values during the HKDF for EC crypto operations. * This refactoring allows TDF and NanoTDF implementations to share more code, since they use different salts for their EC operations. * I would like to add experimental support for random, per-TDF salts, and this will enable those spikes ### 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
1 parent 9c981a3 commit 67b6fb8

File tree

7 files changed

+70
-22
lines changed

7 files changed

+70
-22
lines changed

lib/ocrypto/asym_decryption.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@ type PrivateKeyDecryptor interface {
3030

3131
// FromPrivatePEM creates and returns a new AsymDecryption.
3232
func FromPrivatePEM(privateKeyInPem string) (PrivateKeyDecryptor, error) {
33+
// TK Move salt and info out of library, into API option functions
34+
digest := sha256.New()
35+
digest.Write([]byte("TDF"))
36+
salt := digest.Sum(nil)
37+
38+
return FromPrivatePEMWithSalt(privateKeyInPem, salt, nil)
39+
}
40+
41+
func FromPrivatePEMWithSalt(privateKeyInPem string, salt, info []byte) (PrivateKeyDecryptor, error) {
3342
block, _ := pem.Decode([]byte(privateKeyInPem))
3443
if block == nil {
3544
return AsymDecryption{}, errors.New("failed to parse PEM formatted private key")
@@ -59,9 +68,9 @@ func FromPrivatePEM(privateKeyInPem string) (PrivateKeyDecryptor, error) {
5968
if err != nil {
6069
return nil, fmt.Errorf("unable to create ECDH key: %w", err)
6170
}
62-
return NewECDecryptor(sk)
71+
return NewSaltedECDecryptor(sk, salt, info)
6372
case *ecdh.PrivateKey:
64-
return NewECDecryptor(privateKey)
73+
return NewSaltedECDecryptor(privateKey, salt, info)
6574
case *rsa.PrivateKey:
6675
return AsymDecryption{privateKey}, nil
6776
default:
@@ -72,7 +81,7 @@ func FromPrivatePEM(privateKeyInPem string) (PrivateKeyDecryptor, error) {
7281
}
7382

7483
func NewAsymDecryption(privateKeyInPem string) (AsymDecryption, error) {
75-
d, err := FromPrivatePEM(privateKeyInPem)
84+
d, err := FromPrivatePEMWithSalt(privateKeyInPem, nil, nil)
7685
if err != nil {
7786
return AsymDecryption{}, err
7887
}
@@ -114,6 +123,9 @@ func NewECDecryptor(sk *ecdh.PrivateKey) (ECDecryptor, error) {
114123

115124
return ECDecryptor{sk, salt, nil}, nil
116125
}
126+
func NewSaltedECDecryptor(sk *ecdh.PrivateKey, salt, info []byte) (ECDecryptor, error) {
127+
return ECDecryptor{sk, salt, info}, nil
128+
}
117129

118130
func (e ECDecryptor) Decrypt(_ []byte) ([]byte, error) {
119131
// TK How to get the ephmeral key into here?

lib/ocrypto/asym_encrypt_decrypt_test.go

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
package ocrypto
22

33
import (
4+
"crypto/sha256"
45
"testing"
56
)
67

8+
func salty(s string) []byte {
9+
digest := sha256.New()
10+
digest.Write([]byte(s))
11+
return digest.Sum(nil)
12+
}
13+
714
func TestAsymEncryptionAndDecryption(t *testing.T) {
815
var keypairs = []struct {
916
privateKey string
1017
publicKey string
18+
salt []byte
19+
info []byte
1120
}{
1221
{ // Test 2048 key
1322
`-----BEGIN PRIVATE KEY-----
@@ -47,6 +56,8 @@ GBKh0CWGAXWRmphzGj7kFpkAxT1b827MrQMYxkn4w2WB8B/bGKz0+dWyqnnzGYAS
4756
hVJ0rIiNE8dDWzQCRBfivLemXhX8UFICyoS5i0IwenFvTr6T85EvMxK3aSAlGya3
4857
3wIDAQAB
4958
-----END PUBLIC KEY-----`,
59+
salty("L1L"),
60+
nil,
5061
},
5162
{ // Test 3072 key
5263
`-----BEGIN PRIVATE KEY-----
@@ -100,6 +111,8 @@ KdheIKdUG+Ouv+vMTeAYOw9+5OGainDWFJA3LZHid3qbX85Y0as9n1nSKoYXMMQT
100111
88Nx+U7Vv8fTudHUgueYGy7WtE6URRIkI5W94u5jDpcb9DZ90Wv5XhaJdRmQ2BhZ
101112
pCVg892PjJwMcTWhIKJgX+9QEL2bSb2VY3yEpEa2b2LhAgMBAAE=
102113
-----END PUBLIC KEY-----`,
114+
salty("L1L"),
115+
nil,
103116
},
104117
{ // Test 4096 key
105118
`-----BEGIN PRIVATE KEY-----
@@ -168,6 +181,8 @@ Td+sCeVX1dczquJziOvYwCyC4nM7pY+13+DXgszMydj/jdSshM4p2GRQu/JYDJf+
168181
EfNOjyrEL5+gjYgiQzVVbYkRhr2ZNeNvzdQsM8j+I20ObCNY1RFCmp9//8xNbi1k
169182
ufgiB73q6Fnh5QHf1HNAeMUCAwEAAQ==
170183
-----END PUBLIC KEY-----`,
184+
salty("TDF"),
185+
nil,
171186
},
172187
{ // Test certificate
173188
`-----BEGIN PRIVATE KEY-----
@@ -214,6 +229,8 @@ I099IoRfC5djHUYYLMU/VkOIHuPC3sb7J65pSN26eR8bTMVNagk187V/xNwUuvkf
214229
+NUxDO615/5BwQKnAu5xiIVagYnDZqKCOtYS5qhxF33Nlnwlm7hH8iVZ1RI+n52l
215230
wVyElqp317Ksz+GtTIc+DE6oryxK3tZd4hrj9fXT4KiJvQ4pcRjpePgH7B8=
216231
-----END CERTIFICATE-----`,
232+
salty("L1L"),
233+
nil,
217234
},
218235
{`-----BEGIN PRIVATE KEY-----
219236
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgwQlQvwfqC0sEaPVi
@@ -229,11 +246,14 @@ KVoSgWSEfXZpk5Cr+xiup+ndMhXhszsqge5fSzszsJQg3ZvZNIwUAcOfew8suykM
229246
A1UdIwQYMBaAFCAo/c694aHwmw/0kUTKuFvAQ4OcMA8GA1UdEwEB/wQFMAMBAf8w
230247
CgYIKoZIzj0EAwIDSAAwRQIgUzKsJS6Pcu2aZ6BFfuqob552Ebdel4uFGZMqWrwW
231248
bW0CIQDT5QED+8mHFot9JXSx2q1c5mnRvl4yElK0fiHeatBdqw==
232-
-----END CERTIFICATE-----`},
249+
-----END CERTIFICATE-----`,
250+
salty("L1L"),
251+
nil,
252+
},
233253
}
234254

235255
for _, test := range keypairs {
236-
asymEncryptor, err := FromPublicPEM(test.publicKey)
256+
asymEncryptor, err := FromPublicPEMWithSalt(test.publicKey, test.salt, test.info)
237257
if err != nil {
238258
t.Fatalf("NewAsymEncryption - failed: %v", err)
239259
}
@@ -244,7 +264,7 @@ bW0CIQDT5QED+8mHFot9JXSx2q1c5mnRvl4yElK0fiHeatBdqw==
244264
t.Fatalf("AsymEncryption encrypt failed: %v", err)
245265
}
246266

247-
asymDecryptor, err := FromPrivatePEM(test.privateKey)
267+
asymDecryptor, err := FromPrivatePEMWithSalt(test.privateKey, test.salt, test.info)
248268
if err != nil {
249269
t.Fatalf("NewAsymDecryption - failed: %v", err)
250270
}

lib/ocrypto/asym_encryption.go

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ type ECEncryptor struct {
5656
}
5757

5858
func FromPublicPEM(publicKeyInPem string) (PublicKeyEncryptor, error) {
59+
// TK Move salt and info out of library, into API option functions
60+
digest := sha256.New()
61+
digest.Write([]byte("TDF"))
62+
salt := digest.Sum(nil)
63+
64+
return FromPublicPEMWithSalt(publicKeyInPem, salt, nil)
65+
}
66+
func FromPublicPEMWithSalt(publicKeyInPem string, salt, info []byte) (PublicKeyEncryptor, error) {
5967
pub, err := getPublicPart(publicKeyInPem)
6068
if err != nil {
6169
return nil, err
@@ -69,23 +77,19 @@ func FromPublicPEM(publicKeyInPem string) (PublicKeyEncryptor, error) {
6977
if err != nil {
7078
return nil, err
7179
}
72-
return newECIES(e)
80+
return newECIES(e, salt, info)
7381
case *ecdh.PublicKey:
74-
return newECIES(pub)
82+
return newECIES(pub, salt, info)
7583
default:
7684
break
7785
}
7886

7987
return nil, errors.New("not an supported type of public key")
8088
}
8189

82-
func newECIES(pub *ecdh.PublicKey) (ECEncryptor, error) {
90+
func newECIES(pub *ecdh.PublicKey, salt, info []byte) (ECEncryptor, error) {
8391
ek, err := pub.Curve().GenerateKey(rand.Reader)
84-
// TK Move salt and info out of library, into API option functions
85-
digest := sha256.New()
86-
digest.Write([]byte("TDF"))
87-
salt := digest.Sum(nil)
88-
return ECEncryptor{pub, ek, salt, nil}, err
92+
return ECEncryptor{pub, ek, salt, info}, err
8993
}
9094

9195
// NewAsymEncryption creates and returns a new AsymEncryption.

sdk/tdf.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,13 @@ func createKeyAccess(tdfConfig TDFConfig, kasInfo KASInfo, symKey []byte, policy
570570
return keyAccess, nil
571571
}
572572

573+
func tdfSalt() []byte {
574+
digest := sha256.New()
575+
digest.Write([]byte("TDF"))
576+
salt := digest.Sum(nil)
577+
return salt
578+
}
579+
573580
func generateWrapKeyWithEC(mode ocrypto.ECCMode, kasPublicKey string, symKey []byte) (ecKeyWrappedKeyInfo, error) {
574581
ecKeyPair, err := ocrypto.NewECKeyPair(mode)
575582
if err != nil {
@@ -591,9 +598,7 @@ func generateWrapKeyWithEC(mode ocrypto.ECCMode, kasPublicKey string, symKey []b
591598
return ecKeyWrappedKeyInfo{}, fmt.Errorf("ocrypto.ComputeECDHKey failed:%w", err)
592599
}
593600

594-
digest := sha256.New()
595-
digest.Write([]byte("TDF"))
596-
salt := digest.Sum(nil)
601+
salt := tdfSalt()
597602
sessionKey, err := ocrypto.CalculateHKDF(salt, ecdhKey)
598603
if err != nil {
599604
return ecKeyWrappedKeyInfo{}, fmt.Errorf("ocrypto.CalculateHKDF failed:%w", err)

sdk/tdf_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2103,14 +2103,14 @@ func (f *FakeKas) getRewrapResponse(rewrapRequest string) *kaspb.RewrapResponse
21032103
privateKey, err := ocrypto.ECPrivateKeyFromPem([]byte(kasPrivateKey))
21042104
f.s.Require().NoError(err, "failed to extract private key from PEM")
21052105

2106-
ed, err := ocrypto.NewECDecryptor(privateKey)
2106+
ed, err := ocrypto.NewSaltedECDecryptor(privateKey, tdfSalt(), nil)
21072107
f.s.Require().NoError(err, "failed to create EC decryptor")
21082108

21092109
symmetricKey, err := ed.DecryptWithEphemeralKey(wrappedKey, compressedKey)
21102110
f.s.Require().NoError(err, "failed to decrypt")
21112111

2112-
asymEncrypt, err := ocrypto.FromPublicPEM(bodyData.GetClientPublicKey())
2113-
f.s.Require().NoError(err, "ocrypto.FromPublicPEM failed")
2112+
asymEncrypt, err := ocrypto.FromPublicPEMWithSalt(bodyData.GetClientPublicKey(), tdfSalt(), nil)
2113+
f.s.Require().NoError(err, "ocrypto.FromPublicPEMWithSalt failed")
21142114

21152115
var sessionKey string
21162116
if e, found := asymEncrypt.(ocrypto.ECEncryptor); found {

service/internal/security/standard_crypto.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,13 @@ func (s StandardCrypto) GenerateNanoTDFSessionKey(privateKey any, ephemeralPubli
426426
func (s StandardCrypto) Close() {
427427
}
428428

429+
func TDFSalt() []byte {
430+
digest := sha256.New()
431+
digest.Write([]byte("TDF"))
432+
salt := digest.Sum(nil)
433+
return salt
434+
}
435+
429436
func versionSalt() []byte {
430437
digest := sha256.New()
431438
digest.Write([]byte(kNanoTDFMagicStringAndVersion))
@@ -451,7 +458,7 @@ func (s *StandardCrypto) ECDecrypt(keyID string, ephemeralPublicKey, ciphertext
451458
sk.sk = loaded
452459
}
453460

454-
ed, err := ocrypto.NewECDecryptor(sk.sk)
461+
ed, err := ocrypto.NewSaltedECDecryptor(sk.sk, TDFSalt(), nil)
455462
if err != nil {
456463
return nil, fmt.Errorf("failed to create EC decryptor: %w", err)
457464
}

service/kas/access/rewrap.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ func (p *Provider) tdf3Rewrap(ctx context.Context, requests []*kaspb.UnsignedRew
610610
return "", results
611611
}
612612

613-
asymEncrypt, err := ocrypto.FromPublicPEM(clientPublicKey)
613+
asymEncrypt, err := ocrypto.FromPublicPEMWithSalt(clientPublicKey, security.TDFSalt(), nil)
614614
if err != nil {
615615
p.Logger.WarnContext(ctx, "ocrypto.NewAsymEncryption:", "err", err)
616616
failAllKaos(requests, results, err400("invalid request"))

0 commit comments

Comments
 (0)