github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/crypto/encrypt.go (about)

     1  package crypto
     2  
     3  // encrypt.go contains functions for encrypting and decrypting data byte slices
     4  // and readers.
     5  
     6  import (
     7  	"crypto/cipher"
     8  	"crypto/rand"
     9  	"encoding/json"
    10  	"errors"
    11  	"io"
    12  
    13  	"golang.org/x/crypto/twofish"
    14  )
    15  
    16  const (
    17  	TwofishOverhead = 28 // number of bytes added by EncryptBytes
    18  )
    19  
    20  var (
    21  	ErrInsufficientLen = errors.New("supplied ciphertext is not long enough to contain a nonce")
    22  )
    23  
    24  type (
    25  	Ciphertext []byte
    26  	TwofishKey [EntropySize]byte
    27  )
    28  
    29  // GenerateEncryptionKey produces a key that can be used for encrypting and
    30  // decrypting files.
    31  func GenerateTwofishKey() (key TwofishKey, err error) {
    32  	_, err = rand.Read(key[:])
    33  	return key, err
    34  }
    35  
    36  // NewCipher creates a new Twofish cipher from the key.
    37  func (key TwofishKey) NewCipher() cipher.Block {
    38  	// NOTE: NewCipher only returns an error if len(key) != 16, 24, or 32.
    39  	cipher, _ := twofish.NewCipher(key[:])
    40  	return cipher
    41  }
    42  
    43  // EncryptBytes encrypts a []byte using the key. EncryptBytes uses GCM and
    44  // prepends the nonce (12 bytes) to the ciphertext.
    45  func (key TwofishKey) EncryptBytes(plaintext []byte) (Ciphertext, error) {
    46  	// Create the cipher.
    47  	// NOTE: NewGCM only returns an error if twofishCipher.BlockSize != 16.
    48  	aead, _ := cipher.NewGCM(key.NewCipher())
    49  
    50  	// Create the nonce.
    51  	nonce, err := RandBytes(aead.NonceSize())
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  
    56  	// Encrypt the data. No authenticated data is provided, as EncryptBytes is
    57  	// meant for file encryption.
    58  	return aead.Seal(nonce, nonce, plaintext, nil), nil
    59  }
    60  
    61  // DecryptBytes decrypts the ciphertext created by EncryptBytes. The nonce is
    62  // expected to be the first 12 bytes of the ciphertext.
    63  func (key TwofishKey) DecryptBytes(ct Ciphertext) ([]byte, error) {
    64  	// Create the cipher.
    65  	// NOTE: NewGCM only returns an error if twofishCipher.BlockSize != 16.
    66  	aead, _ := cipher.NewGCM(key.NewCipher())
    67  
    68  	// Check for a nonce.
    69  	if len(ct) < aead.NonceSize() {
    70  		return nil, ErrInsufficientLen
    71  	}
    72  
    73  	// Decrypt the data.
    74  	return aead.Open(nil, ct[:aead.NonceSize()], ct[aead.NonceSize():], nil)
    75  }
    76  
    77  // NewWriter returns a writer that encrypts or decrypts its input stream.
    78  func (key TwofishKey) NewWriter(w io.Writer) io.Writer {
    79  	// OK to use a zero IV if the key is unique for each ciphertext.
    80  	iv := make([]byte, twofish.BlockSize)
    81  	stream := cipher.NewOFB(key.NewCipher(), iv)
    82  
    83  	return &cipher.StreamWriter{S: stream, W: w}
    84  }
    85  
    86  // NewReader returns a reader that encrypts or decrypts its input stream.
    87  func (key TwofishKey) NewReader(r io.Reader) io.Reader {
    88  	// OK to use a zero IV if the key is unique for each ciphertext.
    89  	iv := make([]byte, twofish.BlockSize)
    90  	stream := cipher.NewOFB(key.NewCipher(), iv)
    91  
    92  	return &cipher.StreamReader{S: stream, R: r}
    93  }
    94  
    95  func (c Ciphertext) MarshalJSON() ([]byte, error) {
    96  	return json.Marshal([]byte(c))
    97  }
    98  
    99  func (c *Ciphertext) UnmarshalJSON(b []byte) error {
   100  	var umarB []byte
   101  	err := json.Unmarshal(b, &umarB)
   102  	if err != nil {
   103  		return err
   104  	}
   105  	*c = Ciphertext(umarB)
   106  	return nil
   107  }