1package aead
2
3import (
4	"context"
5	"crypto/aes"
6	"crypto/cipher"
7	"errors"
8
9	wrapping "github.com/hashicorp/go-kms-wrapping"
10	"github.com/hashicorp/go-uuid"
11)
12
13// Wrapper implements the wrapping.Wrapper interface for Shamir
14type Wrapper struct {
15	keyBytes []byte
16	aead     cipher.AEAD
17}
18
19// Ensure that we are implementing AutoSealAccess
20var _ wrapping.Wrapper = (*Wrapper)(nil)
21
22// NewWrapper creates a new Wrapper with the provided logger
23func NewWrapper(opts *wrapping.WrapperOptions) *Wrapper {
24	if opts == nil {
25		opts = new(wrapping.WrapperOptions)
26	}
27	seal := new(Wrapper)
28	return seal
29}
30
31func (s *Wrapper) GetKeyBytes() []byte {
32	return s.keyBytes
33}
34
35func (s *Wrapper) SetAEAD(aead cipher.AEAD) {
36	s.aead = aead
37}
38
39// SetAESGCMKeyBytes takes in a byte slice and constucts an AES-GCM AEAD from it
40func (s *Wrapper) SetAESGCMKeyBytes(key []byte) error {
41	aesCipher, err := aes.NewCipher(key)
42	if err != nil {
43		return err
44	}
45
46	aead, err := cipher.NewGCM(aesCipher)
47	if err != nil {
48		return err
49	}
50
51	s.keyBytes = key
52	s.aead = aead
53	return nil
54}
55
56// Init is a no-op at the moment
57func (s *Wrapper) Init(_ context.Context) error {
58	return nil
59}
60
61// Finalize is called during shutdown. This is a no-op since
62// Wrapper doesn't require any cleanup.
63func (s *Wrapper) Finalize(_ context.Context) error {
64	return nil
65}
66
67// Type returns the seal type for this particular Wrapper implementation
68func (s *Wrapper) Type() string {
69	return wrapping.Shamir
70}
71
72// KeyID returns the last known key id
73func (s *Wrapper) KeyID() string {
74	return ""
75}
76
77// HMACKeyID returns the last known HMAC key id
78func (s *Wrapper) HMACKeyID() string {
79	return ""
80}
81
82// Encrypt is used to encrypt the plaintext using the aead held by the seal.
83func (s *Wrapper) Encrypt(_ context.Context, plaintext, aad []byte) (*wrapping.EncryptedBlobInfo, error) {
84	if plaintext == nil {
85		return nil, errors.New("given plaintext for encryption is nil")
86	}
87
88	if s.aead == nil {
89		return nil, errors.New("aead is not configured in the seal")
90	}
91
92	iv, err := uuid.GenerateRandomBytes(12)
93	if err != nil {
94		return nil, err
95	}
96
97	ciphertext := s.aead.Seal(nil, iv, plaintext, aad)
98
99	return &wrapping.EncryptedBlobInfo{
100		Ciphertext: append(iv, ciphertext...),
101	}, nil
102}
103
104func (s *Wrapper) Decrypt(_ context.Context, in *wrapping.EncryptedBlobInfo, aad []byte) ([]byte, error) {
105	if in == nil {
106		return nil, errors.New("given plaintext for encryption is nil")
107	}
108
109	if s.aead == nil {
110		return nil, errors.New("aead is not configured in the seal")
111	}
112
113	iv, ciphertext := in.Ciphertext[:12], in.Ciphertext[12:]
114
115	plaintext, err := s.aead.Open(nil, iv, ciphertext, aad)
116	if err != nil {
117		return nil, err
118	}
119
120	return plaintext, nil
121}
122