195 lines
5.4 KiB
Go
195 lines
5.4 KiB
Go
/*
|
|
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/rand"
|
|
"fmt"
|
|
"io"
|
|
|
|
miscreant "github.com/miscreant/miscreant-go"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// AESSIVLayerBlockCipher implements the AES SIV block cipher
|
|
type AESSIVLayerBlockCipher struct {
|
|
keylen int // in bytes
|
|
reader io.Reader
|
|
encryptor *miscreant.StreamEncryptor
|
|
decryptor *miscreant.StreamDecryptor
|
|
err error // error that occurred during operation
|
|
eof bool // hit EOF in the input data
|
|
toread int // how many bytes to read in one chunk
|
|
inbuffer []byte // input buffer with data from reader
|
|
inoffset int64 // offset where to read from next
|
|
outbuffer []byte // output buffer to return to user
|
|
outoffset int // offset in output buffer
|
|
outsize int64 // output size
|
|
}
|
|
|
|
type aessivcryptor struct {
|
|
bc *AESSIVLayerBlockCipher
|
|
outputReader io.Reader
|
|
}
|
|
|
|
// NewAESSIVLayerBlockCipher returns a new AES SIV block cipher of 256 or 512 bits
|
|
func NewAESSIVLayerBlockCipher(bits int) (LayerBlockCipher, error) {
|
|
if bits != 256 && bits != 512 {
|
|
return nil, errors.New("AES SIV bit count not supported")
|
|
}
|
|
return &AESSIVLayerBlockCipher{keylen: bits / 8}, nil
|
|
}
|
|
|
|
func (r *aessivcryptor) Read(p []byte) (int, error) {
|
|
if r.bc.err != nil {
|
|
return 0, r.bc.err
|
|
}
|
|
|
|
for {
|
|
// return data if we have any
|
|
if r.bc.outbuffer != nil && r.bc.outoffset < len(r.bc.outbuffer) {
|
|
n := copy(p, r.bc.outbuffer[r.bc.outoffset:])
|
|
r.bc.outoffset += n
|
|
|
|
return n, nil
|
|
}
|
|
// no data and hit eof before?
|
|
if r.bc.eof {
|
|
return 0, io.EOF
|
|
}
|
|
// read new data; we expect to get r.bc.toread number of bytes
|
|
// for anything less we assume it's EOF
|
|
numbytes := 0
|
|
for numbytes < r.bc.toread {
|
|
var n int
|
|
n, r.bc.err = r.bc.reader.Read(r.bc.inbuffer[numbytes:r.bc.toread])
|
|
numbytes += n
|
|
if r.bc.err != nil {
|
|
if r.bc.err == io.EOF {
|
|
r.bc.eof = true
|
|
r.bc.err = nil
|
|
break
|
|
} else {
|
|
return 0, r.bc.err
|
|
}
|
|
}
|
|
if n == 0 {
|
|
break
|
|
}
|
|
}
|
|
if numbytes < r.bc.toread {
|
|
r.bc.eof = true
|
|
}
|
|
|
|
r.bc.inoffset += int64(numbytes)
|
|
|
|
// transform the data
|
|
if r.bc.encryptor != nil {
|
|
r.bc.outbuffer = r.bc.encryptor.Seal(nil, r.bc.inbuffer[:numbytes], []byte(""), r.bc.eof)
|
|
} else {
|
|
r.bc.outbuffer, r.bc.err = r.bc.decryptor.Open(nil, r.bc.inbuffer[:numbytes], []byte(""), r.bc.eof)
|
|
if r.bc.err != nil {
|
|
return 0, r.bc.err
|
|
}
|
|
}
|
|
// let reader start from beginning of buffer
|
|
r.bc.outoffset = 0
|
|
r.bc.outsize += int64(len(r.bc.outbuffer))
|
|
}
|
|
}
|
|
|
|
// init initializes an instance
|
|
func (bc *AESSIVLayerBlockCipher) init(encrypt bool, reader io.Reader, opt LayerBlockCipherOptions) (LayerBlockCipherOptions, error) {
|
|
var (
|
|
err error
|
|
se miscreant.StreamEncryptor
|
|
)
|
|
|
|
bc.reader = reader
|
|
|
|
key := opt.SymmetricKey
|
|
if len(key) != bc.keylen {
|
|
return LayerBlockCipherOptions{}, fmt.Errorf("invalid key length of %d bytes; need %d bytes", len(key), bc.keylen)
|
|
}
|
|
|
|
nonce := opt.CipherOptions["nonce"]
|
|
if len(nonce) == 0 {
|
|
nonce = make([]byte, se.NonceSize())
|
|
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
|
return LayerBlockCipherOptions{}, errors.Wrap(err, "unable to generate random nonce")
|
|
}
|
|
}
|
|
|
|
bc.inbuffer = make([]byte, 1024*1024)
|
|
bc.toread = len(bc.inbuffer)
|
|
bc.inoffset = 0
|
|
bc.outbuffer = nil
|
|
bc.outoffset = 0
|
|
bc.eof = false
|
|
bc.err = nil
|
|
bc.outsize = 0
|
|
|
|
if encrypt {
|
|
bc.encryptor, err = miscreant.NewStreamEncryptor("AES-SIV", key, nonce)
|
|
if err != nil {
|
|
return LayerBlockCipherOptions{}, errors.Wrap(err, "unable to create AES-SIV stream encryptor")
|
|
}
|
|
bc.toread -= bc.encryptor.Overhead()
|
|
bc.decryptor = nil
|
|
} else {
|
|
bc.decryptor, err = miscreant.NewStreamDecryptor("AES-SIV", key, nonce)
|
|
if err != nil {
|
|
return LayerBlockCipherOptions{}, errors.Wrap(err, "unable to create AES-SIV stream decryptor")
|
|
}
|
|
bc.encryptor = nil
|
|
}
|
|
|
|
lbco := LayerBlockCipherOptions{
|
|
SymmetricKey: key,
|
|
CipherOptions: map[string][]byte{
|
|
"nonce": nonce,
|
|
},
|
|
}
|
|
|
|
return lbco, nil
|
|
}
|
|
|
|
// GenerateKey creates a synmmetric key
|
|
func (bc *AESSIVLayerBlockCipher) GenerateKey() []byte {
|
|
return miscreant.GenerateKey(bc.keylen)
|
|
}
|
|
|
|
// Encrypt takes in layer data and returns the ciphertext and relevant LayerBlockCipherOptions
|
|
func (bc *AESSIVLayerBlockCipher) Encrypt(plainDataReader io.Reader, opt LayerBlockCipherOptions) (io.Reader, LayerBlockCipherOptions, error) {
|
|
lbco, err := bc.init(true, plainDataReader, opt)
|
|
if err != nil {
|
|
return nil, LayerBlockCipherOptions{}, err
|
|
}
|
|
|
|
return &aessivcryptor{bc, nil}, lbco, nil
|
|
}
|
|
|
|
// Decrypt takes in layer ciphertext data and returns the plaintext and relevant LayerBlockCipherOptions
|
|
func (bc *AESSIVLayerBlockCipher) Decrypt(encDataReader io.Reader, opt LayerBlockCipherOptions) (io.Reader, LayerBlockCipherOptions, error) {
|
|
lbco, err := bc.init(false, encDataReader, opt)
|
|
if err != nil {
|
|
return nil, LayerBlockCipherOptions{}, err
|
|
}
|
|
|
|
return &aessivcryptor{bc, nil}, lbco, nil
|
|
}
|