Skip to content

Commit 3c5a614

Browse files
feat(core): Encapsulate>Encrypt (#2676)
### Proposed Changes - Prefer using the Encapsulate interface method, when possible - Deprecate the Key's Export method, since we generally don't want to export the keys and Encapsulate is more accurate for what we want. - The goal here is to present an interface that supports a fully delegated rewrap, e.g. using a [Luna Functionality Module](https://thalesdocs.com/gphsm/luna/7/docs/network/Content/FM_SDK/preface.htm) or other KAS. In this case, the call to Encapsulate would trigger the external rewrap. ### Checklist - [ ] 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 0d3dd94 commit 3c5a614

File tree

9 files changed

+60
-17
lines changed

9 files changed

+60
-17
lines changed

lib/ocrypto/ec_key_pair.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -446,15 +446,15 @@ func ECPrivateKeyInPemFormat(privateKey ecdsa.PrivateKey) (string, error) {
446446

447447
// ECPublicKeyInPemFormat Returns public key in pem format.
448448
func ECPublicKeyInPemFormat(publicKey ecdsa.PublicKey) (string, error) {
449-
publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey)
449+
pkb, err := x509.MarshalPKIXPublicKey(publicKey)
450450
if err != nil {
451451
return "", fmt.Errorf("x509.MarshalPKIXPublicKey failed: %w", err)
452452
}
453453

454454
publicKeyPem := pem.EncodeToMemory(
455455
&pem.Block{
456456
Type: "PUBLIC KEY",
457-
Bytes: publicKeyBytes,
457+
Bytes: pkb,
458458
},
459459
)
460460

lib/ocrypto/interfaces.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ type ProtectedKey interface {
2727
VerifyBinding(ctx context.Context, policy, policyBinding []byte) error
2828

2929
// Export returns the raw key data, optionally encrypting it with the provided encapsulator
30+
// Deprecated: Use the Encapsulator's Encapsulate method instead
3031
Export(encapsulator Encapsulator) ([]byte, error)
3132

3233
// DecryptAESGCM decrypts encrypted policies and metadata

lib/ocrypto/protected_key.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,15 @@ func (k *AESProtectedKey) DecryptAESGCM(iv []byte, body []byte, tagSize int) ([]
5555
}
5656

5757
// Export returns the raw key data, optionally encrypting it with the provided Encapsulator
58+
// Deprecated: Use the Encapsulator's Encapsulate method instead
5859
func (k *AESProtectedKey) Export(encapsulator Encapsulator) ([]byte, error) {
5960
if encapsulator == nil {
6061
// Return error if encapsulator is nil
6162
return nil, errors.New("encapsulator cannot be nil")
6263
}
6364

6465
// Encrypt the key data before returning
65-
encryptedKey, err := encapsulator.Encrypt(k.rawKey)
66+
encryptedKey, err := encapsulator.Encapsulate(k)
6667
if err != nil {
6768
return nil, fmt.Errorf("failed to encrypt key data for export: %w", err)
6869
}

lib/ocrypto/protected_key_test.go

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,22 @@ func TestAESProtectedKey_Export_WithEncapsulator(t *testing.T) {
7575
require.NoError(t, err)
7676

7777
// Mock encapsulator
78+
mockEncryptFunc := func(data []byte) ([]byte, error) {
79+
// Simple XOR encryption for testing
80+
result := make([]byte, len(data))
81+
for i, b := range data {
82+
result[i] = b ^ 0xFF
83+
}
84+
return result, nil
85+
}
7886
mockEncapsulator := &mockEncapsulator{
79-
encryptFunc: func(data []byte) ([]byte, error) {
80-
// Simple XOR encryption for testing
81-
result := make([]byte, len(data))
82-
for i, b := range data {
83-
result[i] = b ^ 0xFF
87+
encryptFunc: mockEncryptFunc,
88+
encapsulateFunc: func(k ProtectedKey) ([]byte, error) {
89+
aeskey, ok := k.(*AESProtectedKey)
90+
if !ok {
91+
return nil, assert.AnError
8492
}
85-
return result, nil
93+
return mockEncryptFunc(aeskey.rawKey)
8694
},
8795
}
8896

@@ -105,7 +113,7 @@ func TestAESProtectedKey_Export_EncapsulatorError(t *testing.T) {
105113
require.NoError(t, err)
106114

107115
mockEncapsulator := &mockEncapsulator{
108-
encryptFunc: func(_ []byte) ([]byte, error) {
116+
encapsulateFunc: func(_ ProtectedKey) ([]byte, error) {
109117
return nil, assert.AnError
110118
},
111119
}
@@ -175,12 +183,13 @@ func TestAESProtectedKey_InterfaceCompliance(t *testing.T) {
175183
// Mock encapsulator for testing
176184
type mockEncapsulator struct {
177185
encryptFunc func([]byte) ([]byte, error)
186+
encapsulateFunc func(ProtectedKey) ([]byte, error)
178187
publicKeyPEMFunc func() (string, error)
179188
ephemeralKeyFunc func() []byte
180189
}
181190

182-
func (m *mockEncapsulator) Encapsulate(_ ProtectedKey) ([]byte, error) {
183-
return nil, nil
191+
func (m *mockEncapsulator) Encapsulate(pk ProtectedKey) ([]byte, error) {
192+
return m.encapsulateFunc(pk)
184193
}
185194

186195
func (m *mockEncapsulator) Encrypt(data []byte) ([]byte, error) {

service/internal/security/basic_manager.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,24 @@ func (b *BasicManager) DeriveKey(ctx context.Context, keyDetails trust.KeyDetail
133133
return NewInProcessAESKey(key), nil
134134
}
135135

136+
type OCEncapsulator struct {
137+
ocrypto.PublicKeyEncryptor
138+
}
139+
140+
func (e *OCEncapsulator) Encapsulate(dek trust.ProtectedKey) ([]byte, error) {
141+
ipk, ok := dek.(*InProcessAESKey)
142+
if !ok {
143+
return nil, errors.New("invalid DEK type for encapsulation")
144+
}
145+
return e.Encrypt(ipk.rawKey)
146+
}
147+
136148
func (b *BasicManager) GenerateECSessionKey(_ context.Context, ephemeralPublicKey string) (trust.Encapsulator, error) {
137-
// Implementation of GenerateECSessionKey method
138-
return ocrypto.FromPublicPEMWithSalt(ephemeralPublicKey, NanoVersionSalt(), nil)
149+
pke, err := ocrypto.FromPublicPEMWithSalt(ephemeralPublicKey, NanoVersionSalt(), nil)
150+
if err != nil {
151+
return nil, fmt.Errorf("failed to create public key encryptor: %w", err)
152+
}
153+
return &OCEncapsulator{PublicKeyEncryptor: pke}, nil
139154
}
140155

141156
func (b *BasicManager) Close() {

service/internal/security/in_process_provider.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func (k *InProcessAESKey) Export(encapsulator trust.Encapsulator) ([]byte, error
5757
}
5858

5959
// If an encryptor is provided, encrypt the key data before returning
60-
encryptedKey, err := encapsulator.Encrypt(k.rawKey)
60+
encryptedKey, err := encapsulator.Encapsulate(k)
6161
if err != nil {
6262
if k.logger != nil {
6363
k.logger.Warn("failed to encrypt key data for export", slog.Any("err", err))
@@ -349,7 +349,11 @@ func (a *InProcessProvider) DeriveKey(_ context.Context, keyDetails trust.KeyDet
349349

350350
// GenerateECSessionKey generates a session key for NanoTDF
351351
func (a *InProcessProvider) GenerateECSessionKey(_ context.Context, ephemeralPublicKey string) (trust.Encapsulator, error) {
352-
return ocrypto.FromPublicPEMWithSalt(ephemeralPublicKey, NanoVersionSalt(), nil)
352+
pke, err := ocrypto.FromPublicPEMWithSalt(ephemeralPublicKey, NanoVersionSalt(), nil)
353+
if err != nil {
354+
return nil, fmt.Errorf("session key generation failed to create public key encryptor: %w", err)
355+
}
356+
return &OCEncapsulator{PublicKeyEncryptor: pke}, nil
353357
}
354358

355359
// Close releases any resources held by the provider

service/kas/access/rewrap.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,7 @@ func (p *Provider) tdf3Rewrap(ctx context.Context, requests []*kaspb.UnsignedRew
712712
failAllKaos(requests, results, err400("invalid request"))
713713
return "", results
714714
}
715+
encap := security.OCEncapsulator{PublicKeyEncryptor: asymEncrypt}
715716

716717
var sessionKey string
717718
if e, ok := asymEncrypt.(ocrypto.ECEncryptor); ok {
@@ -771,7 +772,7 @@ func (p *Provider) tdf3Rewrap(ctx context.Context, requests []*kaspb.UnsignedRew
771772
}
772773

773774
// Use the Export method with the asymEncrypt encryptor
774-
encryptedKey, err := kaoRes.DEK.Export(asymEncrypt)
775+
encryptedKey, err := encap.Encapsulate(kaoRes.DEK)
775776
if err != nil {
776777
//nolint:sloglint // reference to camelcase key is intentional
777778
p.Logger.WarnContext(ctx, "rewrap: Export with encryptor failed", slog.String("clientPublicKey", clientPublicKey), slog.Any("error", err))

service/trust/delegating_key_service_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,14 @@ type MockEncapsulator struct {
172172
mock.Mock
173173
}
174174

175+
func (m *MockEncapsulator) Encapsulate(dek ProtectedKey) ([]byte, error) {
176+
args := m.Called(dek)
177+
if a0, ok := args.Get(0).([]byte); ok {
178+
return a0, args.Error(1)
179+
}
180+
return nil, args.Error(1)
181+
}
182+
175183
func (m *MockEncapsulator) Encrypt(data []byte) ([]byte, error) {
176184
args := m.Called(data)
177185
if a0, ok := args.Get(0).([]byte); ok {

service/trust/key_manager.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import (
66
)
77

88
type Encapsulator interface {
9+
// Encapsulate wraps a secret key with the encapsulation key
10+
Encapsulate(dek ProtectedKey) ([]byte, error)
11+
912
// Encrypt wraps a secret key with the encapsulation key
1013
Encrypt(data []byte) ([]byte, error)
1114

@@ -23,6 +26,7 @@ type ProtectedKey interface {
2326
VerifyBinding(ctx context.Context, policy, binding []byte) error
2427

2528
// Export returns the raw key data, optionally encrypting it with the provided encryptor
29+
// Deprecated: Use the Encapsulator's Encapsulate method instead
2630
Export(encryptor Encapsulator) ([]byte, error)
2731

2832
// Used to decrypt encrypted policies and metadata

0 commit comments

Comments
 (0)