1/*
2   Copyright The ocicrypt Authors.
3
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7
8       http://www.apache.org/licenses/LICENSE-2.0
9
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15*/
16
17package blockcipher
18
19import (
20	"io"
21
22	"github.com/opencontainers/go-digest"
23	"github.com/pkg/errors"
24)
25
26// LayerCipherType is the ciphertype as specified in the layer metadata
27type LayerCipherType string
28
29// TODO: Should be obtained from OCI spec once included
30const (
31	AES256CTR LayerCipherType = "AES_256_CTR_HMAC_SHA256"
32)
33
34// PrivateLayerBlockCipherOptions includes the information required to encrypt/decrypt
35// an image which are sensitive and should not be in plaintext
36type PrivateLayerBlockCipherOptions struct {
37	// SymmetricKey represents the symmetric key used for encryption/decryption
38	// This field should be populated by Encrypt/Decrypt calls
39	SymmetricKey []byte `json:"symkey"`
40
41	// Digest is the digest of the original data for verification.
42	// This is NOT populated by Encrypt/Decrypt calls
43	Digest digest.Digest `json:"digest"`
44
45	// CipherOptions contains the cipher metadata used for encryption/decryption
46	// This field should be populated by Encrypt/Decrypt calls
47	CipherOptions map[string][]byte `json:"cipheroptions"`
48}
49
50// PublicLayerBlockCipherOptions includes the information required to encrypt/decrypt
51// an image which are public and can be deduplicated in plaintext across multiple
52// recipients
53type PublicLayerBlockCipherOptions struct {
54	// CipherType denotes the cipher type according to the list of OCI suppported
55	// cipher types.
56	CipherType LayerCipherType `json:"cipher"`
57
58	// Hmac contains the hmac string to help verify encryption
59	Hmac []byte `json:"hmac"`
60
61	// CipherOptions contains the cipher metadata used for encryption/decryption
62	// This field should be populated by Encrypt/Decrypt calls
63	CipherOptions map[string][]byte `json:"cipheroptions"`
64}
65
66// LayerBlockCipherOptions contains the public and private LayerBlockCipherOptions
67// required to encrypt/decrypt an image
68type LayerBlockCipherOptions struct {
69	Public  PublicLayerBlockCipherOptions
70	Private PrivateLayerBlockCipherOptions
71}
72
73// LayerBlockCipher returns a provider for encrypt/decrypt functionality
74// for handling the layer data for a specific algorithm
75type LayerBlockCipher interface {
76	// GenerateKey creates a symmetric key
77	GenerateKey() ([]byte, error)
78	// Encrypt takes in layer data and returns the ciphertext and relevant LayerBlockCipherOptions
79	Encrypt(layerDataReader io.Reader, opt LayerBlockCipherOptions) (io.Reader, Finalizer, error)
80	// Decrypt takes in layer ciphertext data and returns the plaintext and relevant LayerBlockCipherOptions
81	Decrypt(layerDataReader io.Reader, opt LayerBlockCipherOptions) (io.Reader, LayerBlockCipherOptions, error)
82}
83
84// LayerBlockCipherHandler is the handler for encrypt/decrypt for layers
85type LayerBlockCipherHandler struct {
86	cipherMap map[LayerCipherType]LayerBlockCipher
87}
88
89// Finalizer is called after data blobs are written, and returns the LayerBlockCipherOptions for the encrypted blob
90type Finalizer func() (LayerBlockCipherOptions, error)
91
92// GetOpt returns the value of the cipher option and if the option exists
93func (lbco LayerBlockCipherOptions) GetOpt(key string) (value []byte, ok bool) {
94	if v, ok := lbco.Public.CipherOptions[key]; ok {
95		return v, ok
96	} else if v, ok := lbco.Private.CipherOptions[key]; ok {
97		return v, ok
98	} else {
99		return nil, false
100	}
101}
102
103func wrapFinalizerWithType(fin Finalizer, typ LayerCipherType) Finalizer {
104	return func() (LayerBlockCipherOptions, error) {
105		lbco, err := fin()
106		if err != nil {
107			return LayerBlockCipherOptions{}, err
108		}
109		lbco.Public.CipherType = typ
110		return lbco, err
111	}
112}
113
114// Encrypt is the handler for the layer decryption routine
115func (h *LayerBlockCipherHandler) Encrypt(plainDataReader io.Reader, typ LayerCipherType) (io.Reader, Finalizer, error) {
116	if c, ok := h.cipherMap[typ]; ok {
117		sk, err := c.GenerateKey()
118		if err != nil {
119			return nil, nil, err
120		}
121		opt := LayerBlockCipherOptions{
122			Private: PrivateLayerBlockCipherOptions{
123				SymmetricKey: sk,
124			},
125		}
126		encDataReader, fin, err := c.Encrypt(plainDataReader, opt)
127		if err == nil {
128			fin = wrapFinalizerWithType(fin, typ)
129		}
130		return encDataReader, fin, err
131	}
132	return nil, nil, errors.Errorf("unsupported cipher type: %s", typ)
133}
134
135// Decrypt is the handler for the layer decryption routine
136func (h *LayerBlockCipherHandler) Decrypt(encDataReader io.Reader, opt LayerBlockCipherOptions) (io.Reader, LayerBlockCipherOptions, error) {
137	typ := opt.Public.CipherType
138	if typ == "" {
139		return nil, LayerBlockCipherOptions{}, errors.New("no cipher type provided")
140	}
141	if c, ok := h.cipherMap[LayerCipherType(typ)]; ok {
142		return c.Decrypt(encDataReader, opt)
143	}
144	return nil, LayerBlockCipherOptions{}, errors.Errorf("unsupported cipher type: %s", typ)
145}
146
147// NewLayerBlockCipherHandler returns a new default handler
148func NewLayerBlockCipherHandler() (*LayerBlockCipherHandler, error) {
149	h := LayerBlockCipherHandler{
150		cipherMap: map[LayerCipherType]LayerBlockCipher{},
151	}
152
153	var err error
154	h.cipherMap[AES256CTR], err = NewAESCTRLayerBlockCipher(256)
155	if err != nil {
156		return nil, errors.Wrap(err, "unable to set up Cipher AES-256-CTR")
157	}
158
159	return &h, nil
160}
161