Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion examples/cmd/benchmark.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ const (
NanoTDF TDFFormat = "nanotdf"
)

func gfmCellEscape(s string) string {
// Escape pipe characters for GitHub Flavored Markdown tables
pipes := strings.ReplaceAll(s, "|", "\\|")
brs := strings.ReplaceAll(pipes, "\n", "<br>")
return brs
}

func (f *TDFFormat) String() string {
return string(*f)
}
Expand Down Expand Up @@ -232,7 +239,7 @@ func runBenchmark(cmd *cobra.Command, args []string) error {
fmt.Printf("| Error Message | Occurrences |\n")
fmt.Printf("|-----------------------|---------------------------|\n")
for errMsg, count := range errorMsgs {
fmt.Printf("| %s | %d occurrences |\n", errMsg, count)
fmt.Printf("| %s | %d occurrences |\n", gfmCellEscape(errMsg), count)
}
}
return nil
Expand Down
18 changes: 15 additions & 3 deletions lib/ocrypto/asym_decryption.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ type PrivateKeyDecryptor interface {

// FromPrivatePEM creates and returns a new AsymDecryption.
func FromPrivatePEM(privateKeyInPem string) (PrivateKeyDecryptor, error) {
// TK Move salt and info out of library, into API option functions
digest := sha256.New()
digest.Write([]byte("TDF"))
salt := digest.Sum(nil)

return FromPrivatePEMWithSalt(privateKeyInPem, salt, nil)
}

func FromPrivatePEMWithSalt(privateKeyInPem string, salt, info []byte) (PrivateKeyDecryptor, error) {
block, _ := pem.Decode([]byte(privateKeyInPem))
if block == nil {
return AsymDecryption{}, errors.New("failed to parse PEM formatted private key")
Expand Down Expand Up @@ -59,9 +68,9 @@ func FromPrivatePEM(privateKeyInPem string) (PrivateKeyDecryptor, error) {
if err != nil {
return nil, fmt.Errorf("unable to create ECDH key: %w", err)
}
return NewECDecryptor(sk)
return NewSaltedECDecryptor(sk, salt, info)
case *ecdh.PrivateKey:
return NewECDecryptor(privateKey)
return NewSaltedECDecryptor(privateKey, salt, info)
case *rsa.PrivateKey:
return AsymDecryption{privateKey}, nil
default:
Expand All @@ -72,7 +81,7 @@ func FromPrivatePEM(privateKeyInPem string) (PrivateKeyDecryptor, error) {
}

func NewAsymDecryption(privateKeyInPem string) (AsymDecryption, error) {
d, err := FromPrivatePEM(privateKeyInPem)
d, err := FromPrivatePEMWithSalt(privateKeyInPem, nil, nil)
if err != nil {
return AsymDecryption{}, err
}
Expand Down Expand Up @@ -114,6 +123,9 @@ func NewECDecryptor(sk *ecdh.PrivateKey) (ECDecryptor, error) {

return ECDecryptor{sk, salt, nil}, nil
}
func NewSaltedECDecryptor(sk *ecdh.PrivateKey, salt, info []byte) (ECDecryptor, error) {
return ECDecryptor{sk, salt, info}, nil
}

func (e ECDecryptor) Decrypt(_ []byte) ([]byte, error) {
// TK How to get the ephmeral key into here?
Expand Down
26 changes: 23 additions & 3 deletions lib/ocrypto/asym_encrypt_decrypt_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
package ocrypto

import (
"crypto/sha256"
"testing"
)

func salty(s string) []byte {
digest := sha256.New()
digest.Write([]byte(s))
return digest.Sum(nil)
}

func TestAsymEncryptionAndDecryption(t *testing.T) {
var keypairs = []struct {
privateKey string
publicKey string
salt []byte
info []byte
}{
{ // Test 2048 key
`-----BEGIN PRIVATE KEY-----
Expand Down Expand Up @@ -47,6 +56,8 @@ GBKh0CWGAXWRmphzGj7kFpkAxT1b827MrQMYxkn4w2WB8B/bGKz0+dWyqnnzGYAS
hVJ0rIiNE8dDWzQCRBfivLemXhX8UFICyoS5i0IwenFvTr6T85EvMxK3aSAlGya3
3wIDAQAB
-----END PUBLIC KEY-----`,
salty("L1L"),
nil,
},
{ // Test 3072 key
`-----BEGIN PRIVATE KEY-----
Expand Down Expand Up @@ -100,6 +111,8 @@ KdheIKdUG+Ouv+vMTeAYOw9+5OGainDWFJA3LZHid3qbX85Y0as9n1nSKoYXMMQT
88Nx+U7Vv8fTudHUgueYGy7WtE6URRIkI5W94u5jDpcb9DZ90Wv5XhaJdRmQ2BhZ
pCVg892PjJwMcTWhIKJgX+9QEL2bSb2VY3yEpEa2b2LhAgMBAAE=
-----END PUBLIC KEY-----`,
salty("L1L"),
nil,
},
{ // Test 4096 key
`-----BEGIN PRIVATE KEY-----
Expand Down Expand Up @@ -168,6 +181,8 @@ Td+sCeVX1dczquJziOvYwCyC4nM7pY+13+DXgszMydj/jdSshM4p2GRQu/JYDJf+
EfNOjyrEL5+gjYgiQzVVbYkRhr2ZNeNvzdQsM8j+I20ObCNY1RFCmp9//8xNbi1k
ufgiB73q6Fnh5QHf1HNAeMUCAwEAAQ==
-----END PUBLIC KEY-----`,
salty("TDF"),
nil,
},
{ // Test certificate
`-----BEGIN PRIVATE KEY-----
Expand Down Expand Up @@ -214,6 +229,8 @@ I099IoRfC5djHUYYLMU/VkOIHuPC3sb7J65pSN26eR8bTMVNagk187V/xNwUuvkf
+NUxDO615/5BwQKnAu5xiIVagYnDZqKCOtYS5qhxF33Nlnwlm7hH8iVZ1RI+n52l
wVyElqp317Ksz+GtTIc+DE6oryxK3tZd4hrj9fXT4KiJvQ4pcRjpePgH7B8=
-----END CERTIFICATE-----`,
salty("L1L"),
nil,
},
{`-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgwQlQvwfqC0sEaPVi
Expand All @@ -229,11 +246,14 @@ KVoSgWSEfXZpk5Cr+xiup+ndMhXhszsqge5fSzszsJQg3ZvZNIwUAcOfew8suykM
A1UdIwQYMBaAFCAo/c694aHwmw/0kUTKuFvAQ4OcMA8GA1UdEwEB/wQFMAMBAf8w
CgYIKoZIzj0EAwIDSAAwRQIgUzKsJS6Pcu2aZ6BFfuqob552Ebdel4uFGZMqWrwW
bW0CIQDT5QED+8mHFot9JXSx2q1c5mnRvl4yElK0fiHeatBdqw==
-----END CERTIFICATE-----`},
-----END CERTIFICATE-----`,
salty("L1L"),
nil,
},
}

for _, test := range keypairs {
asymEncryptor, err := FromPublicPEM(test.publicKey)
asymEncryptor, err := FromPublicPEMWithSalt(test.publicKey, test.salt, test.info)
if err != nil {
t.Fatalf("NewAsymEncryption - failed: %v", err)
}
Expand All @@ -244,7 +264,7 @@ bW0CIQDT5QED+8mHFot9JXSx2q1c5mnRvl4yElK0fiHeatBdqw==
t.Fatalf("AsymEncryption encrypt failed: %v", err)
}

asymDecryptor, err := FromPrivatePEM(test.privateKey)
asymDecryptor, err := FromPrivatePEMWithSalt(test.privateKey, test.salt, test.info)
if err != nil {
t.Fatalf("NewAsymDecryption - failed: %v", err)
}
Expand Down
20 changes: 12 additions & 8 deletions lib/ocrypto/asym_encryption.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ type ECEncryptor struct {
}

func FromPublicPEM(publicKeyInPem string) (PublicKeyEncryptor, error) {
// TK Move salt and info out of library, into API option functions
digest := sha256.New()
digest.Write([]byte("TDF"))
salt := digest.Sum(nil)

return FromPublicPEMWithSalt(publicKeyInPem, salt, nil)
}
func FromPublicPEMWithSalt(publicKeyInPem string, salt, info []byte) (PublicKeyEncryptor, error) {
pub, err := getPublicPart(publicKeyInPem)
if err != nil {
return nil, err
Expand All @@ -69,23 +77,19 @@ func FromPublicPEM(publicKeyInPem string) (PublicKeyEncryptor, error) {
if err != nil {
return nil, err
}
return newECIES(e)
return newECIES(e, salt, info)
case *ecdh.PublicKey:
return newECIES(pub)
return newECIES(pub, salt, info)
default:
break
}

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

func newECIES(pub *ecdh.PublicKey) (ECEncryptor, error) {
func newECIES(pub *ecdh.PublicKey, salt, info []byte) (ECEncryptor, error) {
ek, err := pub.Curve().GenerateKey(rand.Reader)
// TK Move salt and info out of library, into API option functions
digest := sha256.New()
digest.Write([]byte("TDF"))
salt := digest.Sum(nil)
return ECEncryptor{pub, ek, salt, nil}, err
return ECEncryptor{pub, ek, salt, info}, err
}

// NewAsymEncryption creates and returns a new AsymEncryption.
Expand Down
2 changes: 1 addition & 1 deletion lib/ocrypto/ec_key_pair.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ func VerifyECDSASig(digest, r, s []byte, pubKey *ecdsa.PublicKey) bool {
func ECPubKeyFromPem(pemECPubKey []byte) (*ecdh.PublicKey, error) {
block, _ := pem.Decode(pemECPubKey)
if block == nil {
return nil, fmt.Errorf("failed to parse PEM formatted public key")
return nil, fmt.Errorf("failed to parse PEM formatted public key: %v", pemECPubKey)
}

var pub any
Expand Down
55 changes: 50 additions & 5 deletions sdk/kas_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

const (
secondsPerMinute = 60
statusFail = "fail"
statusPermit = "permit"
)

Expand Down Expand Up @@ -74,7 +75,7 @@ func (k *KASClient) makeRewrapRequest(ctx context.Context, requests []*kas.Unsig

response, err := serviceClient.Rewrap(ctx, rewrapRequest)
if err != nil {
return nil, fmt.Errorf("error making rewrap request: %w", err)
return upgradeRewrapErrorV1(err, requests)
}

upgradeRewrapResponseV1(response, requests)
Expand Down Expand Up @@ -109,6 +110,30 @@ func upgradeRewrapResponseV1(response *kas.RewrapResponse, requests []*kas.Unsig
}
}

// convert v1 errors to v2 responses
func upgradeRewrapErrorV1(err error, requests []*kas.UnsignedRewrapRequest_WithPolicyRequest) (*kas.RewrapResponse, error) {
if len(requests) != 1 {
return nil, fmt.Errorf("error making rewrap request: %w", err)
}

return &kas.RewrapResponse{
Responses: []*kas.PolicyRewrapResult{
{
PolicyId: requests[0].GetPolicy().GetId(),
Results: []*kas.KeyAccessRewrapResult{
{
KeyAccessObjectId: requests[0].GetKeyAccessObjects()[0].GetKeyAccessObjectId(),
Status: statusFail,
Result: &kas.KeyAccessRewrapResult_Error{
Error: err.Error(),
},
},
},
},
},
}, nil
}

func (k *KASClient) nanoUnwrap(ctx context.Context, requests ...*kas.UnsignedRewrapRequest_WithPolicyRequest) (map[string][]kaoResult, error) {
keypair, err := ocrypto.NewECKeyPair(ocrypto.ECCModeSecp256r1)
if err != nil {
Expand All @@ -129,19 +154,39 @@ func (k *KASClient) nanoUnwrap(ctx context.Context, requests ...*kas.UnsignedRew
return nil, err
}

sessionKey, err := ocrypto.ComputeECDHKey([]byte(privateKeyAsPem), []byte(response.GetSessionPublicKey()))
// If the session key is empty, all responses are errors
spk := response.GetSessionPublicKey()
if spk == "" {
policyResults := make(map[string][]kaoResult)
err = errors.New("nanoUnwrap: session public key is empty")
for _, results := range response.GetResponses() {
var kaoKeys []kaoResult
for _, kao := range results.GetResults() {
if kao.GetStatus() == statusPermit {
kaoKeys = append(kaoKeys, kaoResult{KeyAccessObjectID: kao.GetKeyAccessObjectId(), Error: err})
} else {
kaoKeys = append(kaoKeys, kaoResult{KeyAccessObjectID: kao.GetKeyAccessObjectId(), Error: errors.New(kao.GetError())})
}
}
policyResults[results.GetPolicyId()] = kaoKeys
}

return policyResults, nil
}

sessionKey, err := ocrypto.ComputeECDHKey([]byte(privateKeyAsPem), []byte(spk))
if err != nil {
return nil, fmt.Errorf("ocrypto.ComputeECDHKey failed :%w", err)
return nil, fmt.Errorf("nanoUnwrap: ocrypto.ComputeECDHKey failed :%w", err)
}

sessionKey, err = ocrypto.CalculateHKDF(versionSalt(), sessionKey)
if err != nil {
return nil, fmt.Errorf("ocrypto.CalculateHKDF failed:%w", err)
return nil, fmt.Errorf("nanoUnwrap: ocrypto.CalculateHKDF failed:%w", err)
}

aesGcm, err := ocrypto.NewAESGcm(sessionKey)
if err != nil {
return nil, fmt.Errorf("ocrypto.NewAESGcm failed:%w", err)
return nil, fmt.Errorf("nanoUnwrap: ocrypto.NewAESGcm failed:%w", err)
}

policyResults := make(map[string][]kaoResult)
Expand Down
8 changes: 4 additions & 4 deletions sdk/nanotdf.go
Original file line number Diff line number Diff line change
Expand Up @@ -1061,15 +1061,15 @@ func (s SDK) ReadNanoTDFContext(ctx context.Context, writer io.Writer, reader io

symmetricKey, err := s.getNanoRewrapKey(ctx, handler)
if err != nil {
return 0, err
return 0, fmt.Errorf("getNanoRewrapKey: %w", err)
}
return handler.Decrypt(ctx, []kaoResult{{SymmetricKey: symmetricKey}})
}

func (s SDK) getNanoRewrapKey(ctx context.Context, decryptor *NanoTDFDecryptHandler) ([]byte, error) {
req, err := decryptor.CreateRewrapRequest(ctx)
if err != nil {
return nil, err
return nil, fmt.Errorf("CreateRewrapRequest: %w", err)
}

if s.collectionStore != nil {
Expand All @@ -1081,7 +1081,7 @@ func (s SDK) getNanoRewrapKey(ctx context.Context, decryptor *NanoTDFDecryptHand
client := newKASClient(s.dialOptions, s.tokenSource, nil)
kasURL, err := decryptor.header.kasURL.GetURL()
if err != nil {
return nil, err
return nil, fmt.Errorf("nano header kasUrl: %w", err)
}

policyResult, err := client.nanoUnwrap(ctx, req[kasURL])
Expand All @@ -1093,7 +1093,7 @@ func (s SDK) getNanoRewrapKey(ctx context.Context, decryptor *NanoTDFDecryptHand
return nil, errors.New("policy was not found in rewrap response")
}
if result[0].Error != nil {
return nil, result[0].Error
return nil, fmt.Errorf("rewrapError: %w", result[0].Error)
}

if s.collectionStore != nil {
Expand Down
Loading
Loading