storj/pkg/eestream/secretbox.go
Maximillian von Briesen 821d0b6f1d
Stream encryption (#302)
* begin adding encryption for remote pieces

* begin adding decryption

* add encryption key as arg to Put and Get

* move encryption/decryption to object store

* Add encryption key to object store constructor

* Add the erasure scheme to object store constructor

* Ensure decrypter is initialized with the stripe size used by encrypter

* Revert "Ensure decrypter is initialized with the stripe size used by encrypter"

This reverts commit 07272333f461606edfb43ad106cc152f37a3bd46.

* Revert "Add the erasure scheme to object store constructor"

This reverts commit ea5e793b536159d993b96e3db69a37c1656a193c.

* move encryption to stream store

* move decryption stuff to stream store

* revert changes in object store

* add encryptedBlockSize and close rangers on error during Get

* calculate padding sizes correctly

* encryptedBlockSize -> encryptionBlockSize

* pass encryption key and block size into stream store

* remove encryption key and block size from object store constructor

* move encrypter/decrypter initialization

* remove unnecessary cast

* Fix padding issue

* Fix linter

* add todos

* use random encryption key for data encryption. Store an encrypted copy of this key in segment metadata

* use different encryption key for each segment

* encrypt data in one step if it is small enough

* refactor and move encryption stuff

* fix errors related to nil slices passed to copy

* fix encrypter vs. decrypter bug

* put encryption stuff in eestream

* get captplanet test to pass

* fix linting errors

* add types for encryption keys/nonces and clean up

* fix tests

* more review changes

* add Cipher type for encryption stuff

* fix rs_test

* Simplify type casting of key and nonce

* Init starting nonce to the segment index

* don't copy derived key

* remove default encryption key; force user to explicitly set it

* move getSegmentPath to streams package

* dont require user to specify encryption key for captplanet

* rename GenericKey and GenericNonce to Key and Nonce

* review changes

* fix linting error

* Download uses the encryption type from metadata

* Store enc block size in metadata and use it for download
2018-09-26 09:32:23 -04:00

119 lines
3.6 KiB
Go

// Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information.
package eestream
import (
"github.com/zeebo/errs"
"golang.org/x/crypto/nacl/secretbox"
)
type secretboxEncrypter struct {
blockSize int
key Key
startingNonce Nonce
}
// NewSecretboxEncrypter returns a Transformer that encrypts the data passing
// through with key.
//
// startingNonce is treated as a big-endian encoded unsigned
// integer, and as blocks pass through, their block number and the starting
// nonce is added together to come up with that block's nonce. Encrypting
// different data with the same key and the same nonce is a huge security
// issue. It's safe to always encode new data with a random key and random
// startingNonce. The monotonically-increasing nonce (that rolls over) is to
// protect against data reordering.
//
// When in doubt, generate a new key from crypto/rand and a startingNonce
// from crypto/rand as often as possible.
func NewSecretboxEncrypter(key *Key, startingNonce *Nonce, encryptedBlockSize int) (Transformer, error) {
if encryptedBlockSize <= secretbox.Overhead {
return nil, Error.New("block size too small")
}
return &secretboxEncrypter{
blockSize: encryptedBlockSize - secretbox.Overhead,
key: *key,
startingNonce: *startingNonce,
}, nil
}
func (s *secretboxEncrypter) InBlockSize() int {
return s.blockSize
}
func (s *secretboxEncrypter) OutBlockSize() int {
return s.blockSize + secretbox.Overhead
}
func calcNonce(startingNonce *Nonce, blockNum int64) (rv *Nonce, err error) {
rv = new(Nonce)
if copy(rv[:], (*startingNonce)[:]) != len(rv) {
return rv, Error.New("didn't copy memory?!")
}
_, err = incrementBytes(rv[:], blockNum)
return rv, err
}
func (s *secretboxEncrypter) Transform(out, in []byte, blockNum int64) ([]byte, error) {
n, err := calcNonce(&s.startingNonce, blockNum)
if err != nil {
return nil, err
}
return secretbox.Seal(out, in, n.Bytes(), s.key.Bytes()), nil
}
type secretboxDecrypter struct {
blockSize int
key Key
startingNonce Nonce
}
// NewSecretboxDecrypter returns a Transformer that decrypts the data passing
// through with key. See the comments for NewSecretboxEncrypter about
// startingNonce.
func NewSecretboxDecrypter(key *Key, startingNonce *Nonce, encryptedBlockSize int) (Transformer, error) {
if encryptedBlockSize <= secretbox.Overhead {
return nil, Error.New("block size too small")
}
return &secretboxDecrypter{
blockSize: encryptedBlockSize - secretbox.Overhead,
key: *key,
startingNonce: *startingNonce,
}, nil
}
func (s *secretboxDecrypter) InBlockSize() int {
return s.blockSize + secretbox.Overhead
}
func (s *secretboxDecrypter) OutBlockSize() int {
return s.blockSize
}
func (s *secretboxDecrypter) Transform(out, in []byte, blockNum int64) ([]byte, error) {
n, err := calcNonce(&s.startingNonce, blockNum)
if err != nil {
return nil, err
}
rv, success := secretbox.Open(out, in, n.Bytes(), s.key.Bytes())
if !success {
return nil, Error.New("failed decrypting")
}
return rv, nil
}
// EncryptSecretBox encrypts byte data with a key and nonce. The cipher data is returned
func EncryptSecretBox(data []byte, key *Key, nonce *Nonce) (cipherData []byte, err error) {
return secretbox.Seal(nil, data, nonce.Bytes(), key.Bytes()), nil
}
// DecryptSecretBox decrypts byte data with a key and nonce. The plain data is returned
func DecryptSecretBox(cipherData []byte, key *Key, nonce *Nonce) (data []byte, err error) {
data, success := secretbox.Open(nil, cipherData, nonce.Bytes(), key.Bytes())
if !success {
return nil, errs.New("Failed decrypting")
}
return data, nil
}