summaryrefslogtreecommitdiffstats
path: root/utils
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 18:11:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 18:11:55 +0000
commit5edd1635e6e67adc18d05795cf10dee10a99811b (patch)
tree28f214bdb516789ceefe88dba26471e2cce84b7c /utils
parentInitial commit. (diff)
downloadgolang-github-containers-ocicrypt-5edd1635e6e67adc18d05795cf10dee10a99811b.tar.xz
golang-github-containers-ocicrypt-5edd1635e6e67adc18d05795cf10dee10a99811b.zip
Adding upstream version 1.1.9.upstream/1.1.9upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'utils')
-rw-r--r--utils/delayedreader.go109
-rw-r--r--utils/delayedreader_test.go72
-rw-r--r--utils/ioutils.go58
-rw-r--r--utils/keyprovider/keyprovider.pb.go243
-rw-r--r--utils/keyprovider/keyprovider.proto17
-rw-r--r--utils/softhsm/softhsm.go91
-rw-r--r--utils/testing.go165
-rw-r--r--utils/utils.go249
8 files changed, 1004 insertions, 0 deletions
diff --git a/utils/delayedreader.go b/utils/delayedreader.go
new file mode 100644
index 0000000..3b939bd
--- /dev/null
+++ b/utils/delayedreader.go
@@ -0,0 +1,109 @@
+/*
+ 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 utils
+
+import (
+ "io"
+)
+
+func min(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
+
+// DelayedReader wraps a io.Reader and allows a client to use the Reader
+// interface. The DelayedReader holds back some buffer to the client
+// so that it can report any error that occurred on the Reader it wraps
+// early to the client while it may still have held some data back.
+type DelayedReader struct {
+ reader io.Reader // Reader to Read() bytes from and delay them
+ err error // error that occurred on the reader
+ buffer []byte // delay buffer
+ bufbytes int // number of bytes in the delay buffer to give to Read(); on '0' we return 'EOF' to caller
+ bufoff int // offset in the delay buffer to give to Read()
+}
+
+// NewDelayedReader wraps a io.Reader and allocates a delay buffer of bufsize bytes
+func NewDelayedReader(reader io.Reader, bufsize uint) io.Reader {
+ return &DelayedReader{
+ reader: reader,
+ buffer: make([]byte, bufsize),
+ }
+}
+
+// Read implements the io.Reader interface
+func (dr *DelayedReader) Read(p []byte) (int, error) {
+ if dr.err != nil && dr.err != io.EOF {
+ return 0, dr.err
+ }
+
+ // if we are completely drained, return io.EOF
+ if dr.err == io.EOF && dr.bufbytes == 0 {
+ return 0, io.EOF
+ }
+
+ // only at the beginning we fill our delay buffer in an extra step
+ if dr.bufbytes < len(dr.buffer) && dr.err == nil {
+ dr.bufbytes, dr.err = FillBuffer(dr.reader, dr.buffer)
+ if dr.err != nil && dr.err != io.EOF {
+ return 0, dr.err
+ }
+ }
+ // dr.err != nil means we have EOF and can drain the delay buffer
+ // otherwise we need to still read from the reader
+
+ var tmpbuf []byte
+ tmpbufbytes := 0
+ if dr.err == nil {
+ tmpbuf = make([]byte, len(p))
+ tmpbufbytes, dr.err = FillBuffer(dr.reader, tmpbuf)
+ if dr.err != nil && dr.err != io.EOF {
+ return 0, dr.err
+ }
+ }
+
+ // copy out of the delay buffer into 'p'
+ tocopy1 := min(len(p), dr.bufbytes)
+ c1 := copy(p[:tocopy1], dr.buffer[dr.bufoff:])
+ dr.bufoff += c1
+ dr.bufbytes -= c1
+
+ c2 := 0
+ // can p still hold more data?
+ if c1 < len(p) {
+ // copy out of the tmpbuf into 'p'
+ c2 = copy(p[tocopy1:], tmpbuf[:tmpbufbytes])
+ }
+
+ // if tmpbuf holds data we need to hold onto, copy them
+ // into the delay buffer
+ if tmpbufbytes-c2 > 0 {
+ // left-shift the delay buffer and append the tmpbuf's remaining data
+ dr.buffer = dr.buffer[dr.bufoff : dr.bufoff+dr.bufbytes]
+ dr.buffer = append(dr.buffer, tmpbuf[c2:tmpbufbytes]...)
+ dr.bufoff = 0
+ dr.bufbytes = len(dr.buffer)
+ }
+
+ var err error
+ if dr.bufbytes == 0 {
+ err = io.EOF
+ }
+ return c1 + c2, err
+}
diff --git a/utils/delayedreader_test.go b/utils/delayedreader_test.go
new file mode 100644
index 0000000..0554574
--- /dev/null
+++ b/utils/delayedreader_test.go
@@ -0,0 +1,72 @@
+/*
+ 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 utils
+
+import (
+ "bytes"
+ "io"
+ "reflect"
+ "testing"
+)
+
+func makeRangeExp(n int) []int {
+ var res []int
+ for i := 0; i < n; i++ {
+ res = append(res, 1<<uint(i))
+ }
+ return res
+}
+
+func makeRange(lo, hi int) []int {
+ var res []int
+ for i := lo; i < hi; i++ {
+ res = append(res, i)
+ }
+ return res
+}
+
+func TestDelayedReader(t *testing.T) {
+ buf := make([]byte, 10)
+
+ for _, buflen := range makeRangeExp(20) {
+ obuf := make([]byte, buflen)
+
+ for _, bufsize := range makeRange(2, 32) {
+ r := bytes.NewReader(obuf)
+
+ dr := NewDelayedReader(r, uint(bufsize))
+
+ var ibuf []byte
+ for {
+ n, err := dr.Read(buf)
+ if n == 0 {
+ t.Fatal("Did not expect n == 0")
+ }
+ if err != nil && err != io.EOF {
+ t.Fatal(err)
+ }
+ ibuf = append(ibuf, buf[:n]...)
+ if err == io.EOF {
+ break
+ }
+ }
+ if !reflect.DeepEqual(ibuf, obuf) {
+ t.Fatalf("original buffer (len=%d) != received buffer (len=%d)", len(obuf), len(ibuf))
+ }
+ }
+ }
+}
diff --git a/utils/ioutils.go b/utils/ioutils.go
new file mode 100644
index 0000000..c626516
--- /dev/null
+++ b/utils/ioutils.go
@@ -0,0 +1,58 @@
+/*
+ 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 utils
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "os/exec"
+)
+
+// FillBuffer fills the given buffer with as many bytes from the reader as possible. It returns
+// EOF if an EOF was encountered or any other error.
+func FillBuffer(reader io.Reader, buffer []byte) (int, error) {
+ n, err := io.ReadFull(reader, buffer)
+ if err == io.ErrUnexpectedEOF {
+ return n, io.EOF
+ }
+ return n, err
+}
+
+// first argument is the command, like cat or echo,
+// the second is the list of args to pass to it
+type CommandExecuter interface {
+ Exec(string, []string, []byte) ([]byte, error)
+}
+
+type Runner struct{}
+
+// ExecuteCommand is used to execute a linux command line command and return the output of the command with an error if it exists.
+func (r Runner) Exec(cmdName string, args []string, input []byte) ([]byte, error) {
+ var out bytes.Buffer
+ var stderr bytes.Buffer
+ stdInputBuffer := bytes.NewBuffer(input)
+ cmd := exec.Command(cmdName, args...)
+ cmd.Stdin = stdInputBuffer
+ cmd.Stdout = &out
+ cmd.Stderr = &stderr
+ err := cmd.Run()
+ if err != nil {
+ return nil, fmt.Errorf("Error while running command: %s. stderr: %s: %w", cmdName, stderr.String(), err)
+ }
+ return out.Bytes(), nil
+}
diff --git a/utils/keyprovider/keyprovider.pb.go b/utils/keyprovider/keyprovider.pb.go
new file mode 100644
index 0000000..dc477d3
--- /dev/null
+++ b/utils/keyprovider/keyprovider.pb.go
@@ -0,0 +1,243 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: keyprovider.proto
+
+package keyprovider
+
+import (
+ context "context"
+ fmt "fmt"
+ proto "github.com/golang/protobuf/proto"
+ grpc "google.golang.org/grpc"
+ codes "google.golang.org/grpc/codes"
+ status "google.golang.org/grpc/status"
+ math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type KeyProviderKeyWrapProtocolInput struct {
+ KeyProviderKeyWrapProtocolInput []byte `protobuf:"bytes,1,opt,name=KeyProviderKeyWrapProtocolInput,proto3" json:"KeyProviderKeyWrapProtocolInput,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *KeyProviderKeyWrapProtocolInput) Reset() { *m = KeyProviderKeyWrapProtocolInput{} }
+func (m *KeyProviderKeyWrapProtocolInput) String() string { return proto.CompactTextString(m) }
+func (*KeyProviderKeyWrapProtocolInput) ProtoMessage() {}
+func (*KeyProviderKeyWrapProtocolInput) Descriptor() ([]byte, []int) {
+ return fileDescriptor_da74c8e785ad390c, []int{0}
+}
+
+func (m *KeyProviderKeyWrapProtocolInput) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_KeyProviderKeyWrapProtocolInput.Unmarshal(m, b)
+}
+func (m *KeyProviderKeyWrapProtocolInput) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_KeyProviderKeyWrapProtocolInput.Marshal(b, m, deterministic)
+}
+func (m *KeyProviderKeyWrapProtocolInput) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_KeyProviderKeyWrapProtocolInput.Merge(m, src)
+}
+func (m *KeyProviderKeyWrapProtocolInput) XXX_Size() int {
+ return xxx_messageInfo_KeyProviderKeyWrapProtocolInput.Size(m)
+}
+func (m *KeyProviderKeyWrapProtocolInput) XXX_DiscardUnknown() {
+ xxx_messageInfo_KeyProviderKeyWrapProtocolInput.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_KeyProviderKeyWrapProtocolInput proto.InternalMessageInfo
+
+func (m *KeyProviderKeyWrapProtocolInput) GetKeyProviderKeyWrapProtocolInput() []byte {
+ if m != nil {
+ return m.KeyProviderKeyWrapProtocolInput
+ }
+ return nil
+}
+
+type KeyProviderKeyWrapProtocolOutput struct {
+ KeyProviderKeyWrapProtocolOutput []byte `protobuf:"bytes,1,opt,name=KeyProviderKeyWrapProtocolOutput,proto3" json:"KeyProviderKeyWrapProtocolOutput,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *KeyProviderKeyWrapProtocolOutput) Reset() { *m = KeyProviderKeyWrapProtocolOutput{} }
+func (m *KeyProviderKeyWrapProtocolOutput) String() string { return proto.CompactTextString(m) }
+func (*KeyProviderKeyWrapProtocolOutput) ProtoMessage() {}
+func (*KeyProviderKeyWrapProtocolOutput) Descriptor() ([]byte, []int) {
+ return fileDescriptor_da74c8e785ad390c, []int{1}
+}
+
+func (m *KeyProviderKeyWrapProtocolOutput) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_KeyProviderKeyWrapProtocolOutput.Unmarshal(m, b)
+}
+func (m *KeyProviderKeyWrapProtocolOutput) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_KeyProviderKeyWrapProtocolOutput.Marshal(b, m, deterministic)
+}
+func (m *KeyProviderKeyWrapProtocolOutput) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_KeyProviderKeyWrapProtocolOutput.Merge(m, src)
+}
+func (m *KeyProviderKeyWrapProtocolOutput) XXX_Size() int {
+ return xxx_messageInfo_KeyProviderKeyWrapProtocolOutput.Size(m)
+}
+func (m *KeyProviderKeyWrapProtocolOutput) XXX_DiscardUnknown() {
+ xxx_messageInfo_KeyProviderKeyWrapProtocolOutput.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_KeyProviderKeyWrapProtocolOutput proto.InternalMessageInfo
+
+func (m *KeyProviderKeyWrapProtocolOutput) GetKeyProviderKeyWrapProtocolOutput() []byte {
+ if m != nil {
+ return m.KeyProviderKeyWrapProtocolOutput
+ }
+ return nil
+}
+
+func init() {
+ proto.RegisterType((*KeyProviderKeyWrapProtocolInput)(nil), "keyprovider.keyProviderKeyWrapProtocolInput")
+ proto.RegisterType((*KeyProviderKeyWrapProtocolOutput)(nil), "keyprovider.keyProviderKeyWrapProtocolOutput")
+}
+
+func init() {
+ proto.RegisterFile("keyprovider.proto", fileDescriptor_da74c8e785ad390c)
+}
+
+var fileDescriptor_da74c8e785ad390c = []byte{
+ // 169 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x12, 0xcc, 0x4e, 0xad, 0x2c,
+ 0x28, 0xca, 0x2f, 0xcb, 0x4c, 0x49, 0x2d, 0xd2, 0x03, 0x32, 0x4a, 0xf2, 0x85, 0xb8, 0x91, 0x84,
+ 0x94, 0xb2, 0xb9, 0xe4, 0x81, 0xdc, 0x00, 0x28, 0xd7, 0x3b, 0xb5, 0x32, 0xbc, 0x28, 0xb1, 0x20,
+ 0x00, 0xa4, 0x2e, 0x39, 0x3f, 0xc7, 0x33, 0xaf, 0xa0, 0xb4, 0x44, 0xc8, 0x83, 0x4b, 0xde, 0x1b,
+ 0xbf, 0x12, 0x09, 0x46, 0x05, 0x46, 0x0d, 0x9e, 0x20, 0x42, 0xca, 0x94, 0xf2, 0xb8, 0x14, 0x70,
+ 0x5b, 0xe6, 0x5f, 0x5a, 0x02, 0xb2, 0xcd, 0x8b, 0x4b, 0xc1, 0x9b, 0x80, 0x1a, 0xa8, 0x75, 0x04,
+ 0xd5, 0x19, 0xbd, 0x62, 0xe4, 0x12, 0x42, 0x52, 0x14, 0x9c, 0x5a, 0x54, 0x96, 0x99, 0x9c, 0x2a,
+ 0x94, 0xc1, 0xc5, 0x0e, 0x52, 0x0c, 0x94, 0x11, 0xd2, 0xd1, 0x43, 0x0e, 0x1f, 0x02, 0x21, 0x21,
+ 0xa5, 0x4b, 0xa4, 0x6a, 0x88, 0xf5, 0x4a, 0x0c, 0x42, 0x59, 0x5c, 0x9c, 0xa1, 0x79, 0xf4, 0xb1,
+ 0xcb, 0x89, 0x37, 0x0a, 0x39, 0x62, 0x93, 0xd8, 0xc0, 0x91, 0x6d, 0x0c, 0x08, 0x00, 0x00, 0xff,
+ 0xff, 0x9a, 0x10, 0xcb, 0xf9, 0x01, 0x02, 0x00, 0x00,
+}
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConnInterface
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+const _ = grpc.SupportPackageIsVersion6
+
+// KeyProviderServiceClient is the client API for KeyProviderService service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
+type KeyProviderServiceClient interface {
+ WrapKey(ctx context.Context, in *KeyProviderKeyWrapProtocolInput, opts ...grpc.CallOption) (*KeyProviderKeyWrapProtocolOutput, error)
+ UnWrapKey(ctx context.Context, in *KeyProviderKeyWrapProtocolInput, opts ...grpc.CallOption) (*KeyProviderKeyWrapProtocolOutput, error)
+}
+
+type keyProviderServiceClient struct {
+ cc grpc.ClientConnInterface
+}
+
+func NewKeyProviderServiceClient(cc grpc.ClientConnInterface) KeyProviderServiceClient {
+ return &keyProviderServiceClient{cc}
+}
+
+func (c *keyProviderServiceClient) WrapKey(ctx context.Context, in *KeyProviderKeyWrapProtocolInput, opts ...grpc.CallOption) (*KeyProviderKeyWrapProtocolOutput, error) {
+ out := new(KeyProviderKeyWrapProtocolOutput)
+ err := c.cc.Invoke(ctx, "/keyprovider.KeyProviderService/WrapKey", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *keyProviderServiceClient) UnWrapKey(ctx context.Context, in *KeyProviderKeyWrapProtocolInput, opts ...grpc.CallOption) (*KeyProviderKeyWrapProtocolOutput, error) {
+ out := new(KeyProviderKeyWrapProtocolOutput)
+ err := c.cc.Invoke(ctx, "/keyprovider.KeyProviderService/UnWrapKey", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+// KeyProviderServiceServer is the server API for KeyProviderService service.
+type KeyProviderServiceServer interface {
+ WrapKey(context.Context, *KeyProviderKeyWrapProtocolInput) (*KeyProviderKeyWrapProtocolOutput, error)
+ UnWrapKey(context.Context, *KeyProviderKeyWrapProtocolInput) (*KeyProviderKeyWrapProtocolOutput, error)
+}
+
+// UnimplementedKeyProviderServiceServer can be embedded to have forward compatible implementations.
+type UnimplementedKeyProviderServiceServer struct {
+}
+
+func (*UnimplementedKeyProviderServiceServer) WrapKey(ctx context.Context, req *KeyProviderKeyWrapProtocolInput) (*KeyProviderKeyWrapProtocolOutput, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method WrapKey not implemented")
+}
+func (*UnimplementedKeyProviderServiceServer) UnWrapKey(ctx context.Context, req *KeyProviderKeyWrapProtocolInput) (*KeyProviderKeyWrapProtocolOutput, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method UnWrapKey not implemented")
+}
+
+func RegisterKeyProviderServiceServer(s *grpc.Server, srv KeyProviderServiceServer) {
+ s.RegisterService(&_KeyProviderService_serviceDesc, srv)
+}
+
+func _KeyProviderService_WrapKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(KeyProviderKeyWrapProtocolInput)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(KeyProviderServiceServer).WrapKey(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/keyprovider.KeyProviderService/WrapKey",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(KeyProviderServiceServer).WrapKey(ctx, req.(*KeyProviderKeyWrapProtocolInput))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _KeyProviderService_UnWrapKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(KeyProviderKeyWrapProtocolInput)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(KeyProviderServiceServer).UnWrapKey(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/keyprovider.KeyProviderService/UnWrapKey",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(KeyProviderServiceServer).UnWrapKey(ctx, req.(*KeyProviderKeyWrapProtocolInput))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+var _KeyProviderService_serviceDesc = grpc.ServiceDesc{
+ ServiceName: "keyprovider.KeyProviderService",
+ HandlerType: (*KeyProviderServiceServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "WrapKey",
+ Handler: _KeyProviderService_WrapKey_Handler,
+ },
+ {
+ MethodName: "UnWrapKey",
+ Handler: _KeyProviderService_UnWrapKey_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{},
+ Metadata: "keyprovider.proto",
+}
diff --git a/utils/keyprovider/keyprovider.proto b/utils/keyprovider/keyprovider.proto
new file mode 100644
index 0000000..a71f0a5
--- /dev/null
+++ b/utils/keyprovider/keyprovider.proto
@@ -0,0 +1,17 @@
+syntax = "proto3";
+
+package keyprovider;
+option go_package = "keyprovider";
+
+message keyProviderKeyWrapProtocolInput {
+ bytes KeyProviderKeyWrapProtocolInput = 1;
+}
+
+message keyProviderKeyWrapProtocolOutput {
+ bytes KeyProviderKeyWrapProtocolOutput = 1;
+}
+
+service KeyProviderService {
+ rpc WrapKey(keyProviderKeyWrapProtocolInput) returns (keyProviderKeyWrapProtocolOutput) {};
+ rpc UnWrapKey(keyProviderKeyWrapProtocolInput) returns (keyProviderKeyWrapProtocolOutput) {};
+} \ No newline at end of file
diff --git a/utils/softhsm/softhsm.go b/utils/softhsm/softhsm.go
new file mode 100644
index 0000000..cc97a1e
--- /dev/null
+++ b/utils/softhsm/softhsm.go
@@ -0,0 +1,91 @@
+/*
+ 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 softhsm
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "os"
+ "os/exec"
+ "strings"
+)
+
+type SoftHSMSetup struct {
+ statedir string
+}
+
+func NewSoftHSMSetup() *SoftHSMSetup {
+ return &SoftHSMSetup{}
+}
+
+// GetConfigFilename returns the path to the softhsm configuration file; this function
+// may only be called after RunSoftHSMSetup
+func (s *SoftHSMSetup) GetConfigFilename() string {
+ return s.statedir + "/softhsm2.conf"
+}
+
+// RunSoftHSMSetup runs 'softhsm_setup setup' and returns the public key that was displayed
+func (s *SoftHSMSetup) RunSoftHSMSetup(softhsmSetup string) (string, error) {
+ statedir, err := os.MkdirTemp("", "ocicrypt")
+ if err != nil {
+ return "", fmt.Errorf("Could not create temporary directory fot softhsm state: %w", err)
+ }
+ s.statedir = statedir
+
+ cmd := exec.Command(softhsmSetup, "setup")
+ var out bytes.Buffer
+ cmd.Stdout = &out
+ cmd.Env = append(cmd.Env, "SOFTHSM_SETUP_CONFIGDIR="+s.statedir)
+ err = cmd.Run()
+ if err != nil {
+ os.RemoveAll(s.statedir)
+ return "", fmt.Errorf("%s setup failed: %s: %w", softhsmSetup, out.String(), err)
+ }
+
+ o := out.String()
+ idx := strings.Index(o, "pkcs11:")
+ if idx < 0 {
+ os.RemoveAll(s.statedir)
+ return "", errors.New("Could not find pkcs11 URI in output")
+ }
+
+ return strings.TrimRight(o[idx:], "\n "), nil
+}
+
+// RunSoftHSMGetPubkey runs 'softhsm_setup getpubkey' and returns the public key
+func (s *SoftHSMSetup) RunSoftHSMGetPubkey(softhsmSetup string) (string, error) {
+ cmd := exec.Command(softhsmSetup, "getpubkey")
+ var out bytes.Buffer
+ cmd.Stdout = &out
+ cmd.Env = append(cmd.Env, "SOFTHSM_SETUP_CONFIGDIR="+s.statedir)
+ err := cmd.Run()
+ if err != nil {
+ return "", fmt.Errorf("%s getpubkey failed: %s: %w", softhsmSetup, out.String(), err)
+ }
+
+ return out.String(), nil
+}
+
+// RunSoftHSMTeardown runs 'softhsm_setup teardown
+func (s *SoftHSMSetup) RunSoftHSMTeardown(softhsmSetup string) {
+ cmd := exec.Command(softhsmSetup, "teardown")
+ cmd.Env = append(cmd.Env, "SOFTHSM_SETUP_CONFIGDIR="+s.statedir)
+ _ = cmd.Run()
+
+ os.RemoveAll(s.statedir)
+}
diff --git a/utils/testing.go b/utils/testing.go
new file mode 100644
index 0000000..69bb9d1
--- /dev/null
+++ b/utils/testing.go
@@ -0,0 +1,165 @@
+/*
+ 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 utils
+
+import (
+ "crypto/ecdsa"
+ "crypto/elliptic"
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/x509"
+ "crypto/x509/pkix"
+ "encoding/pem"
+ "fmt"
+ "math/big"
+ "time"
+)
+
+// CreateRSAKey creates an RSA key
+func CreateRSAKey(bits int) (*rsa.PrivateKey, error) {
+ key, err := rsa.GenerateKey(rand.Reader, bits)
+ if err != nil {
+ return nil, fmt.Errorf("rsa.GenerateKey failed: %w", err)
+ }
+ return key, nil
+}
+
+// CreateRSATestKey creates an RSA key of the given size and returns
+// the public and private key in PEM or DER format
+func CreateRSATestKey(bits int, password []byte, pemencode bool) ([]byte, []byte, error) {
+ key, err := CreateRSAKey(bits)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ pubData, err := x509.MarshalPKIXPublicKey(&key.PublicKey)
+ if err != nil {
+ return nil, nil, fmt.Errorf("x509.MarshalPKIXPublicKey failed: %w", err)
+ }
+ privData := x509.MarshalPKCS1PrivateKey(key)
+
+ // no more encoding needed for DER
+ if !pemencode {
+ return pubData, privData, nil
+ }
+
+ publicKey := pem.EncodeToMemory(&pem.Block{
+ Type: "PUBLIC KEY",
+ Bytes: pubData,
+ })
+
+ var block *pem.Block
+
+ typ := "RSA PRIVATE KEY"
+ if len(password) > 0 {
+ block, err = x509.EncryptPEMBlock(rand.Reader, typ, privData, password, x509.PEMCipherAES256) //nolint:staticcheck // ignore SA1019, which is kept for backward compatibility
+ if err != nil {
+ return nil, nil, fmt.Errorf("x509.EncryptPEMBlock failed: %w", err)
+ }
+ } else {
+ block = &pem.Block{
+ Type: typ,
+ Bytes: privData,
+ }
+ }
+
+ privateKey := pem.EncodeToMemory(block)
+
+ return publicKey, privateKey, nil
+}
+
+// CreateECDSATestKey creates and elliptic curve key for the given curve and returns
+// the public and private key in DER format
+func CreateECDSATestKey(curve elliptic.Curve) ([]byte, []byte, error) {
+ key, err := ecdsa.GenerateKey(curve, rand.Reader)
+ if err != nil {
+ return nil, nil, fmt.Errorf("ecdsa.GenerateKey failed: %w", err)
+ }
+
+ pubData, err := x509.MarshalPKIXPublicKey(&key.PublicKey)
+ if err != nil {
+ return nil, nil, fmt.Errorf("x509.MarshalPKIXPublicKey failed: %w", err)
+ }
+
+ privData, err := x509.MarshalECPrivateKey(key)
+ if err != nil {
+ return nil, nil, fmt.Errorf("x509.MarshalECPrivateKey failed: %w", err)
+ }
+
+ return pubData, privData, nil
+}
+
+// CreateTestCA creates a root CA for testing
+func CreateTestCA() (*rsa.PrivateKey, *x509.Certificate, error) {
+ key, err := rsa.GenerateKey(rand.Reader, 2048)
+ if err != nil {
+ return nil, nil, fmt.Errorf("rsa.GenerateKey failed: %w", err)
+ }
+
+ ca := &x509.Certificate{
+ SerialNumber: big.NewInt(1),
+ Subject: pkix.Name{
+ CommonName: "test-ca",
+ },
+ NotBefore: time.Now(),
+ NotAfter: time.Now().AddDate(1, 0, 0),
+ IsCA: true,
+ KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
+ BasicConstraintsValid: true,
+ }
+ caCert, err := certifyKey(&key.PublicKey, ca, key, ca)
+
+ return key, caCert, err
+}
+
+// CertifyKey certifies a public key using the given CA's private key and cert;
+// The certificate template for the public key is optional
+func CertifyKey(pubbytes []byte, template *x509.Certificate, caKey *rsa.PrivateKey, caCert *x509.Certificate) (*x509.Certificate, error) {
+ pubKey, err := ParsePublicKey(pubbytes, "CertifyKey")
+ if err != nil {
+ return nil, err
+ }
+ return certifyKey(pubKey, template, caKey, caCert)
+}
+
+func certifyKey(pub interface{}, template *x509.Certificate, caKey *rsa.PrivateKey, caCert *x509.Certificate) (*x509.Certificate, error) {
+ if template == nil {
+ template = &x509.Certificate{
+ SerialNumber: big.NewInt(1),
+ Subject: pkix.Name{
+ CommonName: "testkey",
+ },
+ NotBefore: time.Now(),
+ NotAfter: time.Now().Add(time.Hour),
+ IsCA: false,
+ KeyUsage: x509.KeyUsageDigitalSignature,
+ BasicConstraintsValid: true,
+ }
+ }
+
+ certDER, err := x509.CreateCertificate(rand.Reader, template, caCert, pub, caKey)
+ if err != nil {
+ return nil, fmt.Errorf("x509.CreateCertificate failed: %w", err)
+ }
+
+ cert, err := x509.ParseCertificate(certDER)
+ if err != nil {
+ return nil, fmt.Errorf("x509.ParseCertificate failed: %w", err)
+ }
+
+ return cert, nil
+}
diff --git a/utils/utils.go b/utils/utils.go
new file mode 100644
index 0000000..160f747
--- /dev/null
+++ b/utils/utils.go
@@ -0,0 +1,249 @@
+/*
+ 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 utils
+
+import (
+ "bytes"
+ "crypto/x509"
+ "encoding/base64"
+ "encoding/pem"
+ "errors"
+ "fmt"
+ "strings"
+
+ "github.com/containers/ocicrypt/crypto/pkcs11"
+ "github.com/go-jose/go-jose/v3"
+ "golang.org/x/crypto/openpgp"
+)
+
+// parseJWKPrivateKey parses the input byte array as a JWK and makes sure it's a private key
+func parseJWKPrivateKey(privKey []byte, prefix string) (interface{}, error) {
+ jwk := jose.JSONWebKey{}
+ err := jwk.UnmarshalJSON(privKey)
+ if err != nil {
+ return nil, fmt.Errorf("%s: Could not parse input as JWK: %w", prefix, err)
+ }
+ if jwk.IsPublic() {
+ return nil, fmt.Errorf("%s: JWK is not a private key", prefix)
+ }
+ return &jwk, nil
+}
+
+// parseJWKPublicKey parses the input byte array as a JWK
+func parseJWKPublicKey(privKey []byte, prefix string) (interface{}, error) {
+ jwk := jose.JSONWebKey{}
+ err := jwk.UnmarshalJSON(privKey)
+ if err != nil {
+ return nil, fmt.Errorf("%s: Could not parse input as JWK: %w", prefix, err)
+ }
+ if !jwk.IsPublic() {
+ return nil, fmt.Errorf("%s: JWK is not a public key", prefix)
+ }
+ return &jwk, nil
+}
+
+// parsePkcs11PrivateKeyYaml parses the input byte array as pkcs11 key file yaml format)
+func parsePkcs11PrivateKeyYaml(yaml []byte, prefix string) (*pkcs11.Pkcs11KeyFileObject, error) {
+ // if the URI does not have enough attributes, we will throw an error when decrypting
+ return pkcs11.ParsePkcs11KeyFile(yaml)
+}
+
+// parsePkcs11URIPublicKey parses the input byte array as a pkcs11 key file yaml
+func parsePkcs11PublicKeyYaml(yaml []byte, prefix string) (*pkcs11.Pkcs11KeyFileObject, error) {
+ // if the URI does not have enough attributes, we will throw an error when decrypting
+ return pkcs11.ParsePkcs11KeyFile(yaml)
+}
+
+// IsPasswordError checks whether an error is related to a missing or wrong
+// password
+func IsPasswordError(err error) bool {
+ if err == nil {
+ return false
+ }
+ msg := strings.ToLower(err.Error())
+
+ return strings.Contains(msg, "password") &&
+ (strings.Contains(msg, "missing") || strings.Contains(msg, "wrong"))
+}
+
+// ParsePrivateKey tries to parse a private key in DER format first and
+// PEM format after, returning an error if the parsing failed
+func ParsePrivateKey(privKey, privKeyPassword []byte, prefix string) (interface{}, error) {
+ key, err := x509.ParsePKCS8PrivateKey(privKey)
+ if err != nil {
+ key, err = x509.ParsePKCS1PrivateKey(privKey)
+ if err != nil {
+ key, err = x509.ParseECPrivateKey(privKey)
+ }
+ }
+ if err != nil {
+ block, _ := pem.Decode(privKey)
+ if block != nil {
+ var der []byte
+ if x509.IsEncryptedPEMBlock(block) { //nolint:staticcheck // ignore SA1019, which is kept for backward compatibility
+ if privKeyPassword == nil {
+ return nil, fmt.Errorf("%s: Missing password for encrypted private key", prefix)
+ }
+ der, err = x509.DecryptPEMBlock(block, privKeyPassword) //nolint:staticcheck // ignore SA1019, which is kept for backward compatibility
+ if err != nil {
+ return nil, fmt.Errorf("%s: Wrong password: could not decrypt private key", prefix)
+ }
+ } else {
+ der = block.Bytes
+ }
+
+ key, err = x509.ParsePKCS8PrivateKey(der)
+ if err != nil {
+ key, err = x509.ParsePKCS1PrivateKey(der)
+ if err != nil {
+ return nil, fmt.Errorf("%s: Could not parse private key: %w", prefix, err)
+ }
+ }
+ } else {
+ key, err = parseJWKPrivateKey(privKey, prefix)
+ if err != nil {
+ key, err = parsePkcs11PrivateKeyYaml(privKey, prefix)
+ }
+ }
+ }
+ return key, err
+}
+
+// IsPrivateKey returns true in case the given byte array represents a private key
+// It returns an error if for example the password is wrong
+func IsPrivateKey(data []byte, password []byte) (bool, error) {
+ _, err := ParsePrivateKey(data, password, "")
+ return err == nil, err
+}
+
+// IsPkcs11PrivateKey returns true in case the given byte array represents a pkcs11 private key
+func IsPkcs11PrivateKey(data []byte) bool {
+ return pkcs11.IsPkcs11PrivateKey(data)
+}
+
+// ParsePublicKey tries to parse a public key in DER format first and
+// PEM format after, returning an error if the parsing failed
+func ParsePublicKey(pubKey []byte, prefix string) (interface{}, error) {
+ key, err := x509.ParsePKIXPublicKey(pubKey)
+ if err != nil {
+ block, _ := pem.Decode(pubKey)
+ if block != nil {
+ key, err = x509.ParsePKIXPublicKey(block.Bytes)
+ if err != nil {
+ return nil, fmt.Errorf("%s: Could not parse public key: %w", prefix, err)
+ }
+ } else {
+ key, err = parseJWKPublicKey(pubKey, prefix)
+ if err != nil {
+ key, err = parsePkcs11PublicKeyYaml(pubKey, prefix)
+ }
+ }
+ }
+ return key, err
+}
+
+// IsPublicKey returns true in case the given byte array represents a public key
+func IsPublicKey(data []byte) bool {
+ _, err := ParsePublicKey(data, "")
+ return err == nil
+}
+
+// IsPkcs11PublicKey returns true in case the given byte array represents a pkcs11 public key
+func IsPkcs11PublicKey(data []byte) bool {
+ return pkcs11.IsPkcs11PublicKey(data)
+}
+
+// ParseCertificate tries to parse a public key in DER format first and
+// PEM format after, returning an error if the parsing failed
+func ParseCertificate(certBytes []byte, prefix string) (*x509.Certificate, error) {
+ x509Cert, err := x509.ParseCertificate(certBytes)
+ if err != nil {
+ block, _ := pem.Decode(certBytes)
+ if block == nil {
+ return nil, fmt.Errorf("%s: Could not PEM decode x509 certificate", prefix)
+ }
+ x509Cert, err = x509.ParseCertificate(block.Bytes)
+ if err != nil {
+ return nil, fmt.Errorf("%s: Could not parse x509 certificate: %w", prefix, err)
+ }
+ }
+ return x509Cert, err
+}
+
+// IsCertificate returns true in case the given byte array represents an x.509 certificate
+func IsCertificate(data []byte) bool {
+ _, err := ParseCertificate(data, "")
+ return err == nil
+}
+
+// IsGPGPrivateKeyRing returns true in case the given byte array represents a GPG private key ring file
+func IsGPGPrivateKeyRing(data []byte) bool {
+ r := bytes.NewBuffer(data)
+ _, err := openpgp.ReadKeyRing(r)
+ return err == nil
+}
+
+// SortDecryptionKeys parses a list of comma separated base64 entries and sorts the data into
+// a map. Each entry in the list may be either a GPG private key ring, private key, or x.509
+// certificate
+func SortDecryptionKeys(b64ItemList string) (map[string][][]byte, error) {
+ dcparameters := make(map[string][][]byte)
+
+ for _, b64Item := range strings.Split(b64ItemList, ",") {
+ var password []byte
+ b64Data := strings.Split(b64Item, ":")
+ keyData, err := base64.StdEncoding.DecodeString(b64Data[0])
+ if err != nil {
+ return nil, errors.New("Could not base64 decode a passed decryption key")
+ }
+ if len(b64Data) == 2 {
+ password, err = base64.StdEncoding.DecodeString(b64Data[1])
+ if err != nil {
+ return nil, errors.New("Could not base64 decode a passed decryption key password")
+ }
+ }
+ var key string
+ isPrivKey, err := IsPrivateKey(keyData, password)
+ if IsPasswordError(err) {
+ return nil, err
+ }
+ if isPrivKey {
+ key = "privkeys"
+ if _, ok := dcparameters["privkeys-passwords"]; !ok {
+ dcparameters["privkeys-passwords"] = [][]byte{password}
+ } else {
+ dcparameters["privkeys-passwords"] = append(dcparameters["privkeys-passwords"], password)
+ }
+ } else if IsCertificate(keyData) {
+ key = "x509s"
+ } else if IsGPGPrivateKeyRing(keyData) {
+ key = "gpg-privatekeys"
+ }
+ if key != "" {
+ values := dcparameters[key]
+ if values == nil {
+ dcparameters[key] = [][]byte{keyData}
+ } else {
+ dcparameters[key] = append(dcparameters[key], keyData)
+ }
+ } else {
+ return nil, errors.New("Unknown decryption key type")
+ }
+ }
+
+ return dcparameters, nil
+}