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 }