File: kmsfs.go

package info (click to toggle)
golang-github-smallstep-crypto 0.63.0-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 3,800 kB
  • sloc: sh: 66; makefile: 50
file content (128 lines) | stat: -rw-r--r-- 2,645 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package kms

import (
	"context"
	"fmt"
	"io/fs"

	"go.step.sm/crypto/kms/apiv1"
)

// FS adds a close method to the fs.FS interface. This new method allows to
// properly close the underlying KMS.
type FS interface {
	fs.FS
	Close() error
}

type kmsfs struct {
	apiv1.KeyManager
}

func newFS(ctx context.Context, kmsuri string) (*kmsfs, error) {
	if kmsuri == "" {
		return &kmsfs{}, nil
	}
	km, err := loadKMS(ctx, kmsuri)
	if err != nil {
		return nil, err
	}
	return &kmsfs{KeyManager: km}, nil
}

func (f *kmsfs) Close() error {
	if f != nil && f.KeyManager != nil {
		return f.KeyManager.Close()
	}
	return nil
}

func (f *kmsfs) getKMS(kmsuri string) (apiv1.KeyManager, error) {
	if f.KeyManager == nil {
		return loadKMS(context.TODO(), kmsuri)
	}
	return f.KeyManager, nil
}

func loadKMS(ctx context.Context, kmsuri string) (apiv1.KeyManager, error) {
	return New(ctx, apiv1.Options{
		URI: kmsuri,
	})
}

func openError(name string, err error) *fs.PathError {
	return &fs.PathError{
		Path: name,
		Op:   "open",
		Err:  err,
	}
}

// certFS implements an io/fs to load certificates from a KMS.
type certFS struct {
	*kmsfs
}

// CertFS creates a new io/fs with the given KMS URI.
func CertFS(ctx context.Context, kmsuri string) (FS, error) {
	km, err := newFS(ctx, kmsuri)
	if err != nil {
		return nil, err
	}
	_, ok := km.KeyManager.(apiv1.CertificateManager)
	if !ok {
		return nil, fmt.Errorf("%s does not implement a CertificateManager", kmsuri)
	}
	return &certFS{kmsfs: km}, nil
}

// Open returns a file representing a certificate in an KMS.
func (f *certFS) Open(name string) (fs.File, error) {
	km, err := f.getKMS(name)
	if err != nil {
		return nil, openError(name, err)
	}
	cert, err := km.(apiv1.CertificateManager).LoadCertificate(&apiv1.LoadCertificateRequest{
		Name: name,
	})
	if err != nil {
		return nil, openError(name, err)
	}
	return &object{
		Path:   name,
		Object: cert,
	}, nil
}

// keyFS implements an io/fs to load public keys from a KMS.
type keyFS struct {
	*kmsfs
}

// KeyFS creates a new KeyFS with the given KMS URI.
func KeyFS(ctx context.Context, kmsuri string) (FS, error) {
	km, err := newFS(ctx, kmsuri)
	if err != nil {
		return nil, err
	}
	return &keyFS{kmsfs: km}, nil
}

// Open returns a file representing a public key in a KMS.
func (f *keyFS) Open(name string) (fs.File, error) {
	km, err := f.getKMS(name)
	if err != nil {
		return nil, openError(name, err)
	}
	// Attempt with a public key
	pub, err := km.GetPublicKey(&apiv1.GetPublicKeyRequest{
		Name: name,
	})
	if err != nil {
		return nil, openError(name, err)
	}
	return &object{
		Path:   name,
		Object: pub,
	}, nil
}