Skip to content

Commit

Permalink
Merge ocicrypt contents into this project
Browse files Browse the repository at this point in the history
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
  • Loading branch information
stefanberger committed Aug 8, 2019
1 parent d8a6859 commit 0212339
Show file tree
Hide file tree
Showing 35 changed files with 1,559 additions and 1,256 deletions.
2 changes: 1 addition & 1 deletion Makefile
Expand Up @@ -17,7 +17,7 @@
all: build

build:
go build images/encryption/*.go
go build -v ./...

check:
golangci-lint run
2 changes: 1 addition & 1 deletion images/encryption/client.go
Expand Up @@ -23,7 +23,7 @@ import (
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/diff"
"github.com/containerd/containerd/errdefs"
encconfig "github.com/containerd/ocicrypt/pkg/encryption/config"
encconfig "github.com/containerd/imgcrypt/pkg/encryption/config"
"github.com/containerd/typeurl"
"github.com/gogo/protobuf/types"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
Expand Down
4 changes: 2 additions & 2 deletions images/encryption/encryption.go
Expand Up @@ -25,8 +25,8 @@ import (
"math/rand"

"github.com/containerd/containerd/images"
"github.com/containerd/ocicrypt/pkg/encryption"
encconfig "github.com/containerd/ocicrypt/pkg/encryption/config"
"github.com/containerd/imgcrypt/pkg/encryption"
encconfig "github.com/containerd/imgcrypt/pkg/encryption/config"

"github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
Expand Down
Expand Up @@ -28,8 +28,7 @@ type LayerCipherType string

// TODO: Should be obtained from OCI spec once included
const (
AESSIVCMAC256 LayerCipherType = "AEAD_AES_SIV_CMAC_STREAM_256"
AESSIVCMAC512 LayerCipherType = "AEAD_AES_SIV_CMAC_STREAM_512"
AES256CTR LayerCipherType = "AES_256_CTR"
CipherTypeOpt string = "type"
)

Expand All @@ -49,15 +48,19 @@ type LayerBlockCipherOptions struct {
CipherOptions map[string][]byte `json:"cipheroptions"`
}

// SetHmac allows a client to pass in a function that the block cipher
// passes the calculated HMAC to after encrypting the stream.
type SetHmac func([]byte)

// LayerBlockCipher returns a provider for encrypt/decrypt functionality
// for handling the layer data for a specific algorithm
type LayerBlockCipher interface {
// GenerateKey creates a symmetric key
GenerateKey() []byte
GenerateKey() ([]byte, error)
// Encrypt takes in layer data and returns the ciphertext and relevant LayerBlockCipherOptions
Encrypt(layerDataReader io.Reader, opt LayerBlockCipherOptions) (io.Reader, LayerBlockCipherOptions, error)
Encrypt(layerDataReader io.Reader, opt LayerBlockCipherOptions, setHmac SetHmac) (io.Reader, LayerBlockCipherOptions, error)
// Decrypt takes in layer ciphertext data and returns the plaintext and relevant LayerBlockCipherOptions
Decrypt(layerDataReader io.Reader, opt LayerBlockCipherOptions) (io.Reader, LayerBlockCipherOptions, error)
Decrypt(layerDataReader io.Reader, opt LayerBlockCipherOptions, hmac []byte) (io.Reader, LayerBlockCipherOptions, error)
}

// LayerBlockCipherHandler is the handler for encrypt/decrypt for layers
Expand All @@ -66,13 +69,17 @@ type LayerBlockCipherHandler struct {
}

// Encrypt is the handler for the layer decryption routine
func (h *LayerBlockCipherHandler) Encrypt(plainDataReader io.Reader, typ LayerCipherType) (io.Reader, LayerBlockCipherOptions, error) {
func (h *LayerBlockCipherHandler) Encrypt(plainDataReader io.Reader, typ LayerCipherType, setHmac SetHmac) (io.Reader, LayerBlockCipherOptions, error) {

if c, ok := h.cipherMap[typ]; ok {
sk, err := c.GenerateKey()
if err != nil {
return nil, LayerBlockCipherOptions{}, err
}
opt := LayerBlockCipherOptions{
SymmetricKey: c.GenerateKey(),
SymmetricKey: sk,
}
encDataReader, newopt, err := c.Encrypt(plainDataReader, opt)
encDataReader, newopt, err := c.Encrypt(plainDataReader, opt, setHmac)
if err == nil {
newopt.CipherOptions[CipherTypeOpt] = []byte(typ)
}
Expand All @@ -82,13 +89,13 @@ func (h *LayerBlockCipherHandler) Encrypt(plainDataReader io.Reader, typ LayerCi
}

// Decrypt is the handler for the layer decryption routine
func (h *LayerBlockCipherHandler) Decrypt(encDataReader io.Reader, opt LayerBlockCipherOptions) (io.Reader, LayerBlockCipherOptions, error) {
func (h *LayerBlockCipherHandler) Decrypt(encDataReader io.Reader, opt LayerBlockCipherOptions, hmac []byte) (io.Reader, LayerBlockCipherOptions, error) {
typ, ok := opt.CipherOptions[CipherTypeOpt]
if !ok {
return nil, LayerBlockCipherOptions{}, errors.New("no cipher type provided")
}
if c, ok := h.cipherMap[LayerCipherType(typ)]; ok {
return c.Decrypt(encDataReader, opt)
return c.Decrypt(encDataReader, opt, hmac)
}
return nil, LayerBlockCipherOptions{}, errors.Errorf("unsupported cipher type: %s", typ)
}
Expand All @@ -100,14 +107,9 @@ func NewLayerBlockCipherHandler() (*LayerBlockCipherHandler, error) {
}

var err error
h.cipherMap[AESSIVCMAC256], err = NewAESSIVLayerBlockCipher(256)
if err != nil {
return nil, errors.Wrap(err, "unable to set up Cipher AES-SIV-CMAC-256")
}

h.cipherMap[AESSIVCMAC512], err = NewAESSIVLayerBlockCipher(512)
h.cipherMap[AES256CTR], err = NewAESCTRLayerBlockCipher(256)
if err != nil {
return nil, errors.Wrap(err, "unable to set up Cipher AES-SIV-CMAC-512")
return nil, errors.Wrap(err, "unable to set up Cipher AES-256-CTR")
}

return &h, nil
Expand Down
176 changes: 176 additions & 0 deletions pkg/encryption/blockcipher/blockcipher_aes_ctr.go
@@ -0,0 +1,176 @@
/*
Copyright The containerd 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 blockcipher

import (
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"fmt"
"hash"
"io"

"github.com/pkg/errors"
)

// AESCTRLayerBlockCipher implements the AES CTR stream cipher
type AESCTRLayerBlockCipher struct {
keylen int // in bytes
reader io.Reader
encrypt bool
stream cipher.Stream
err error
hmac hash.Hash
setHmac SetHmac
expHmac []byte
}

type aesctrcryptor struct {
bc *AESCTRLayerBlockCipher
outputReader io.Reader
}

// NewAESCTRLayerBlockCipher returns a new AES SIV block cipher of 256 or 512 bits
func NewAESCTRLayerBlockCipher(bits int) (LayerBlockCipher, error) {
if bits != 256 {
return nil, errors.New("AES CTR bit count not supported")
}
return &AESCTRLayerBlockCipher{keylen: bits / 8}, nil
}

func (r *aesctrcryptor) Read(p []byte) (int, error) {
var (
o int
)

if r.bc.err != nil {
return 0, r.bc.err
}

for o = 0; o < len(p); {
n, err := r.bc.reader.Read(p[o:])
o += n
if err != nil {
if err == io.EOF {
r.bc.err = err
break
} else {
return 0, err
}
}
}

if !r.bc.encrypt {
if _, err := r.bc.hmac.Write(p[:o]); err != nil {
return 0, errors.Wrapf(err, "could not write to hmac")
}
if r.bc.err == io.EOF {
// Before we return EOF we let the HMAC comparison
// provide a verdict
if !hmac.Equal(r.bc.hmac.Sum(nil), r.bc.expHmac) {
return 0, fmt.Errorf("could not properly decrypt byte stream; exp hmac: '%x', actual hmac: '%s'", r.bc.expHmac, r.bc.hmac.Sum(nil))
}
}
}

r.bc.stream.XORKeyStream(p[:o], p[:o])

if r.bc.encrypt {
if _, err := r.bc.hmac.Write(p[:o]); err != nil {
return 0, errors.Wrapf(err, "could not write to hmac")
}
if r.bc.err == io.EOF {
// Final data encrypted; Do the 'then-MAC' part
r.bc.setHmac(r.bc.hmac.Sum(nil))
}
}

return o, r.bc.err
}

// init initializes an instance
func (bc *AESCTRLayerBlockCipher) init(encrypt bool, reader io.Reader, opts LayerBlockCipherOptions, setHmac SetHmac, expHmac []byte) (LayerBlockCipherOptions, error) {
var (
err error
)

key := opts.SymmetricKey
if len(key) != bc.keylen {
return LayerBlockCipherOptions{}, fmt.Errorf("invalid key length of %d bytes; need %d bytes", len(key), bc.keylen)
}

nonce := opts.CipherOptions["nonce"]
if len(nonce) == 0 {
nonce = make([]byte, aes.BlockSize)
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return LayerBlockCipherOptions{}, errors.Wrap(err, "unable to generate random nonce")
}
}

block, err := aes.NewCipher(key)
if err != nil {
return LayerBlockCipherOptions{}, errors.Wrap(err, "aes.NewCipher failed")
}

bc.reader = reader
bc.encrypt = encrypt
bc.stream = cipher.NewCTR(block, nonce)
bc.err = nil
bc.hmac = hmac.New(sha256.New, key)
bc.setHmac = setHmac
bc.expHmac = expHmac

lbco := LayerBlockCipherOptions{
SymmetricKey: key,
CipherOptions: map[string][]byte{
"nonce": nonce,
},
}

return lbco, nil
}

// GenerateKey creates a synmmetric key
func (bc *AESCTRLayerBlockCipher) GenerateKey() ([]byte, error) {
key := make([]byte, bc.keylen)
if _, err := io.ReadFull(rand.Reader, key); err != nil {
return nil, err
}
return key, nil
}

// Encrypt takes in layer data and returns the ciphertext and relevant LayerBlockCipherOptions
func (bc *AESCTRLayerBlockCipher) Encrypt(plainDataReader io.Reader, opt LayerBlockCipherOptions, setHmac SetHmac) (io.Reader, LayerBlockCipherOptions, error) {
lbco, err := bc.init(true, plainDataReader, opt, setHmac, nil)
if err != nil {
return nil, LayerBlockCipherOptions{}, err
}

return &aesctrcryptor{bc, nil}, lbco, nil
}

// Decrypt takes in layer ciphertext data and returns the plaintext and relevant LayerBlockCipherOptions
func (bc *AESCTRLayerBlockCipher) Decrypt(encDataReader io.Reader, opt LayerBlockCipherOptions, expHmac []byte) (io.Reader, LayerBlockCipherOptions, error) {
lbco, err := bc.init(false, encDataReader, opt, nil, expHmac)
if err != nil {
return nil, LayerBlockCipherOptions{}, err
}

return &aesctrcryptor{bc, nil}, lbco, nil
}

0 comments on commit 0212339

Please sign in to comment.