summaryrefslogtreecommitdiffstats
path: root/keywrap/keyprovider/keyprovider_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'keywrap/keyprovider/keyprovider_test.go')
-rw-r--r--keywrap/keyprovider/keyprovider_test.go378
1 files changed, 378 insertions, 0 deletions
diff --git a/keywrap/keyprovider/keyprovider_test.go b/keywrap/keyprovider/keyprovider_test.go
new file mode 100644
index 0000000..8d66729
--- /dev/null
+++ b/keywrap/keyprovider/keyprovider_test.go
@@ -0,0 +1,378 @@
+/*
+ Copyright The ocicrypt Authors.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package keyprovider
+
+import (
+ "context"
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/rand"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "os"
+ "testing"
+
+ "github.com/containers/ocicrypt/config"
+ keyprovider_config "github.com/containers/ocicrypt/config/keyprovider-config"
+ keyproviderpb "github.com/containers/ocicrypt/utils/keyprovider"
+ "github.com/stretchr/testify/assert"
+ "google.golang.org/grpc"
+)
+
+// TestRunner mocks binary executable for key wrapping and unwrapping
+type TestRunner struct{}
+
+// Mock annotation packet, which goes into container image manifest
+type annotationPacket struct {
+ KeyUrl string `json:"key_url"`
+ WrappedKey []byte `json:"wrapped_key"`
+ WrapType string `json:"wrap_type"`
+}
+
+// grpc server with mock api implementation for serving the clients with mock WrapKey and Unwrapkey grpc method implementations
+type server struct {
+ keyproviderpb.UnimplementedKeyProviderServiceServer
+}
+
+var encryptingKey []byte
+var decryptingKey []byte
+
+func init() {
+ lis, _ := net.Listen("tcp", ":50051")
+ s := grpc.NewServer()
+ keyproviderpb.RegisterKeyProviderServiceServer(s, &server{})
+ go func() {
+ if err := s.Serve(lis); err != nil {
+ fmt.Println(err)
+ }
+ }()
+}
+
+// Mock grpc method which returns the wrapped key encapsulated in annotation packet in grpc response for a given key in grpc request
+func (*server) WrapKey(ctx context.Context, request *keyproviderpb.KeyProviderKeyWrapProtocolInput) (*keyproviderpb.KeyProviderKeyWrapProtocolOutput, error) {
+ var keyP KeyProviderKeyWrapProtocolInput
+ err := json.Unmarshal(request.KeyProviderKeyWrapProtocolInput, &keyP)
+ if err != nil {
+ return nil, err
+ }
+ c, _ := aes.NewCipher(encryptingKey)
+ gcm, _ := cipher.NewGCM(c)
+ nonce := make([]byte, gcm.NonceSize())
+ _, err = io.ReadFull(rand.Reader, nonce)
+ if err != nil {
+ return nil, err
+ }
+
+ wrappedKey := gcm.Seal(nonce, nonce, keyP.KeyWrapParams.OptsData, nil)
+
+ jsonString, _ := json.Marshal(annotationPacket{
+ KeyUrl: "https://key-provider/key-uuid",
+ WrappedKey: wrappedKey,
+ WrapType: "AES",
+ })
+
+ protocolOuputSerialized, _ := json.Marshal(KeyProviderKeyWrapProtocolOutput{
+ KeyWrapResults: KeyWrapResults{Annotation: jsonString},
+ })
+
+ return &keyproviderpb.KeyProviderKeyWrapProtocolOutput{
+ KeyProviderKeyWrapProtocolOutput: protocolOuputSerialized,
+ }, nil
+}
+
+// Mock grpc method which returns the unwrapped key encapsulated in grpc response for a given wrapped key encapsulated in annotation packet in grpc request
+func (*server) UnWrapKey(ctx context.Context, request *keyproviderpb.KeyProviderKeyWrapProtocolInput) (*keyproviderpb.KeyProviderKeyWrapProtocolOutput, error) {
+ var keyP KeyProviderKeyWrapProtocolInput
+ err := json.Unmarshal(request.KeyProviderKeyWrapProtocolInput, &keyP)
+ if err != nil {
+ return nil, err
+ }
+ apkt := annotationPacket{}
+ err = json.Unmarshal(keyP.KeyUnwrapParams.Annotation, &apkt)
+ if err != nil {
+ return nil, err
+ }
+ ciphertext := apkt.WrappedKey
+
+ c, _ := aes.NewCipher(decryptingKey)
+ gcm, _ := cipher.NewGCM(c)
+ nonceSize := gcm.NonceSize()
+ nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
+ unwrappedKey, err := gcm.Open(nil, nonce, ciphertext, nil)
+
+ if err != nil {
+ return nil, err
+ }
+
+ protocolOuputSerialized, _ := json.Marshal(KeyProviderKeyWrapProtocolOutput{
+ KeyUnwrapResults: KeyUnwrapResults{OptsData: unwrappedKey},
+ })
+ return &keyproviderpb.KeyProviderKeyWrapProtocolOutput{
+ KeyProviderKeyWrapProtocolOutput: protocolOuputSerialized,
+ }, nil
+}
+
+// Mock Exec Command for wrapping and unwrapping executables
+func (r TestRunner) Exec(cmdName string, args []string, input []byte) ([]byte, error) {
+ if cmdName == "/usr/lib/keyprovider-1-wrapkey" {
+ var keyP KeyProviderKeyWrapProtocolInput
+ err := json.Unmarshal(input, &keyP)
+ if err != nil {
+ return nil, err
+ }
+ c, _ := aes.NewCipher(encryptingKey)
+ gcm, _ := cipher.NewGCM(c)
+
+ nonce := make([]byte, gcm.NonceSize())
+ _, err = io.ReadFull(rand.Reader, nonce)
+ if err != nil {
+ return nil, err
+ }
+ wrappedKey := gcm.Seal(nonce, nonce, keyP.KeyWrapParams.OptsData, nil)
+
+ jsonString, _ := json.Marshal(annotationPacket{
+ KeyUrl: "https://key-provider/key-uuid",
+ WrappedKey: wrappedKey,
+ WrapType: "AES",
+ })
+
+ return json.Marshal(KeyProviderKeyWrapProtocolOutput{
+ KeyWrapResults: KeyWrapResults{
+ Annotation: jsonString,
+ },
+ })
+ } else if cmdName == "/usr/lib/keyprovider-1-unwrapkey" {
+ var keyP KeyProviderKeyWrapProtocolInput
+ err := json.Unmarshal(input, &keyP)
+ if err != nil {
+ return nil, err
+ }
+ apkt := annotationPacket{}
+ err = json.Unmarshal(keyP.KeyUnwrapParams.Annotation, &apkt)
+ if err != nil {
+ return nil, err
+ }
+ ciphertext := apkt.WrappedKey
+
+ c, _ := aes.NewCipher(decryptingKey)
+ gcm, _ := cipher.NewGCM(c)
+ nonceSize := gcm.NonceSize()
+ nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
+ unwrappedKey, err := gcm.Open(nil, nonce, ciphertext, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ return json.Marshal(KeyProviderKeyWrapProtocolOutput{
+ KeyUnwrapResults: KeyUnwrapResults{OptsData: unwrappedKey},
+ })
+ }
+ return nil, errors.New("unknown protocol")
+}
+
+func TestKeyWrapKeyProviderCommandSuccess(t *testing.T) {
+ testConfigFile := "config.json"
+ os.Setenv("OCICRYPT_KEYPROVIDER_CONFIG", testConfigFile)
+ //Config File with executable for key wrap
+ configFile1 := `{"key-providers": {
+ "keyprovider-1": {
+ "cmd": {
+ "path": "/usr/lib/keyprovider-1-wrapkey",
+ "args": []
+ }
+ }
+ }}
+ `
+ //Config File with executable for key unwrap
+ configFile2 := `{"key-providers": {
+ "keyprovider-1": {
+ "cmd": {
+ "path": "/usr/lib/keyprovider-1-unwrapkey",
+ "args": []
+ }
+ }
+ }}
+ `
+ configFile, _ := os.OpenFile(testConfigFile, os.O_CREATE|os.O_WRONLY, 0644)
+ _, err := configFile.Write([]byte(configFile1))
+ assert.NoError(t, err)
+ configFile.Close()
+
+ optsData := []byte("data to be encrypted")
+
+ ic, _ := keyprovider_config.GetConfiguration()
+ keyWrapper := NewKeyWrapper("keyprovider-1", ic.KeyProviderConfig["keyprovider-1"])
+
+ parameters := make(map[string][][]byte)
+ parameters["keyprovider-1"] = nil
+ ec := config.EncryptConfig{
+ Parameters: parameters,
+ DecryptConfig: config.DecryptConfig{},
+ }
+ encryptingKey = []byte("passphrasewhichneedstobe32bytes!")
+ decryptingKey = []byte("passphrasewhichneedstobe32bytes!")
+ runner = TestRunner{}
+ keyWrapOutput, err := keyWrapper.WrapKeys(&ec, optsData)
+ assert.NoError(t, err)
+
+ configFile, _ = os.OpenFile(testConfigFile, os.O_CREATE|os.O_WRONLY, 0644)
+ _, err = configFile.Write([]byte(configFile2))
+ assert.NoError(t, err)
+ configFile.Close()
+
+ ic, _ = keyprovider_config.GetConfiguration()
+ keyWrapper = NewKeyWrapper("keyprovider-1", ic.KeyProviderConfig["keyprovider-1"])
+ dp := make(map[string][][]byte)
+ dp["keyprovider-1"] = append(dp["keyprovider-1"], []byte("Supported Protocol"))
+
+ dc := config.DecryptConfig{
+ Parameters: dp,
+ }
+ keyUnWrapOutput, err := keyWrapper.UnwrapKey(&dc, keyWrapOutput)
+ assert.NoError(t, err)
+ assert.Equal(t, optsData, keyUnWrapOutput)
+ os.Remove(testConfigFile)
+}
+
+func TestKeyWrapKeyProviderCommandFail(t *testing.T) {
+ testConfigFile := "config.json"
+ os.Setenv("OCICRYPT_KEYPROVIDER_CONFIG", testConfigFile)
+ //Config File with executable for key wrap
+ configFile1 := `{"key-providers": {
+ "keyprovider-1": {
+ "cmd": {
+ "path": "/usr/lib/keyprovider-1-wrapkey",
+ "args": []
+ }
+ },
+ "keyprovider-2": {
+ "cmd": {
+ "path": "/usr/lib/keyprovider-2-wrapkey",
+ "args": []
+ }
+ }
+ }}
+ `
+ //Config File with executable for key unwrap
+ configFile2 := `{"key-providers": {
+ "keyprovider-1": {
+ "cmd": {
+ "path": "/usr/lib/keyprovider-1-unwrapkey",
+ "args": []
+ }
+ },
+ "keyprovider-2": {
+ "cmd": {
+ "path": "/usr/lib/keyprovider-2-unwrapkey",
+ "args": []
+ }
+ }
+ }}
+ `
+ configFile, _ := os.OpenFile(testConfigFile, os.O_CREATE|os.O_WRONLY, 0644)
+ _, err := configFile.Write([]byte(configFile1))
+ assert.NoError(t, err)
+ configFile.Close()
+
+ optsData := []byte("data to be encrypted")
+ ic, _ := keyprovider_config.GetConfiguration()
+ keyWrapper := NewKeyWrapper("keyprovider-1", ic.KeyProviderConfig["keyprovider-1"])
+
+ parameters := make(map[string][][]byte)
+ parameters["keyprovider-1"] = nil
+ ec := config.EncryptConfig{
+ Parameters: parameters,
+ DecryptConfig: config.DecryptConfig{},
+ }
+ encryptingKey = []byte("passphrasewhichneedstobe32bytes!")
+ decryptingKey = []byte("wrongphrasewhichneedstobe32bytes")
+ runner = TestRunner{}
+ keyWrapOutput, err := keyWrapper.WrapKeys(&ec, optsData)
+ assert.NoError(t, err)
+
+ configFile, _ = os.OpenFile(testConfigFile, os.O_CREATE|os.O_WRONLY, 0644)
+ _, err = configFile.Write([]byte(configFile2))
+ assert.NoError(t, err)
+ configFile.Close()
+
+ dp := make(map[string][][]byte)
+ dp["keyprovider-1"] = append(dp["keyprovider-1"], []byte("Supported Protocol"))
+
+ dc := config.DecryptConfig{
+ Parameters: dp,
+ }
+ keyUnWrapOutput, _ := keyWrapper.UnwrapKey(&dc, keyWrapOutput)
+ assert.Nil(t, keyUnWrapOutput)
+ os.Remove(testConfigFile)
+}
+
+func TestKeyWrapKeyProviderGRPCSuccess(t *testing.T) {
+ path := "config.json"
+ os.Setenv("OCICRYPT_KEYPROVIDER_CONFIG", path)
+ filecontent := `{"key-providers": {
+ "keyprovider-1": {
+ "grpc": "localhost:50051"
+ },
+ "keyprovider-2": {
+ "grpc": "localhost:3990"
+ },
+ "keyprovider-3": {
+ "cmd": {
+ "path": "/usr/lib/keyprovider-2-unwrapkey",
+ "args": []
+ }
+ }
+
+ }}
+ `
+ tempFile, _ := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
+ _, err := tempFile.Write([]byte(filecontent))
+ assert.NoError(t, err)
+ tempFile.Close()
+
+ optsData := []byte("data to be encrypted")
+
+ ic, _ := keyprovider_config.GetConfiguration()
+ keyWrapper := NewKeyWrapper("keyprovider-1", ic.KeyProviderConfig["keyprovider-1"])
+
+ parameters := make(map[string][][]byte)
+ parameters["keyprovider-1"] = nil
+ ec := config.EncryptConfig{
+ Parameters: parameters,
+ DecryptConfig: config.DecryptConfig{},
+ }
+
+ runner = TestRunner{}
+ encryptingKey = []byte("passphrasewhichneedstobe32bytes!")
+ decryptingKey = encryptingKey
+ keyWrapOutput, err := keyWrapper.WrapKeys(&ec, optsData)
+ assert.NoError(t, err)
+
+ dp := make(map[string][][]byte)
+ dp["keyprovider-1"] = append(dp["keyprovider-1"], []byte("Supported Protocol"))
+
+ dc := config.DecryptConfig{
+ Parameters: dp,
+ }
+ keyUnWrapOutput, err := keyWrapper.UnwrapKey(&dc, keyWrapOutput)
+ assert.NoError(t, err)
+ assert.Equal(t, optsData, keyUnWrapOutput)
+ os.Remove(path)
+}