croc/vendor/github.com/mars9/crypt/crypt.go

166 lines
3.5 KiB
Go

// Package crypt provides password-based encryption and decryption of
// data streams.
package crypt
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/rand"
"errors"
"hash"
"io"
)
const (
blockSize = aes.BlockSize // AES block size
version = 1
)
// Crypter encrypt/decrypts with AES (Rijndael) in cipher block counter
// mode (CTR) and authenticate with HMAC-SHA.
type Crypter struct {
HashFunc func() hash.Hash
HashSize int
Key Key
BufSize int
}
func (c *Crypter) encHeader(salt, iv, hmacKey []byte) []byte {
keySize := c.Key.Size()
headerSize := 1 + keySize + blockSize + c.HashSize
b := make([]byte, headerSize)
b[0] = version
copy(b[1:1+keySize], salt)
copy(b[1+keySize:1+keySize+blockSize], iv)
mac := hmac.New(c.HashFunc, hmacKey)
mac.Write(b[:1+keySize+blockSize])
copy(b[1+keySize+blockSize:], mac.Sum(nil))
return b
}
func (c *Crypter) bufSize() int {
if c.BufSize == 0 {
return 2 * 1024 * 1024
}
return c.BufSize
}
func (c *Crypter) decHeader(b []byte) ([]byte, []byte, error) {
if b[0] != version {
return nil, nil, errors.New("malformed encrypted packet")
}
keySize := c.Key.Size()
salt := b[1 : 1+keySize]
iv := b[1+keySize : 1+keySize+blockSize]
return salt, iv, nil
}
// Encrypt encrypts from src until either EOF is reached on src or an
// error occurs. A successful Encrypt returns err == nil, not err == EOF.
func (c *Crypter) Encrypt(dst io.Writer, src io.Reader) (err error) {
salt := make([]byte, c.Key.Size())
if _, err := rand.Read(salt); err != nil {
return err
}
iv := make([]byte, blockSize)
if _, err := rand.Read(iv); err != nil {
return err
}
aesKey, hmacKey := c.Key.Derive(salt)
header := c.encHeader(salt, iv, hmacKey)
if _, err := dst.Write(header); err != nil {
return err
}
mac := hmac.New(c.HashFunc, hmacKey)
mac.Write(header)
block, err := aes.NewCipher(aesKey)
if err != nil {
return err
}
stream := cipher.NewCTR(block, iv)
buf := make([]byte, c.bufSize())
n := 0
for {
n, err = src.Read(buf)
if err != nil {
if err == io.EOF {
break
}
return err
}
mac.Write(buf[:n])
stream.XORKeyStream(buf[:n], buf[:n])
if _, err = dst.Write(buf[:n]); err != nil {
return err
}
if _, err = dst.Write(mac.Sum(nil)); err != nil {
return err
}
}
return nil
}
// Decrypt decrypts from src until either EOF is reached on src or an
// error occurs. A successful Decrypt returns err == nil, not err == EOF.
func (c *Crypter) Decrypt(dst io.Writer, src io.Reader) (err error) {
keySize := c.Key.Size()
headerSize := 1 + keySize + blockSize + c.HashSize
header := make([]byte, headerSize)
if _, err = src.Read(header); err != nil {
return err
}
salt, iv, err := c.decHeader(header)
if err != nil {
return err
}
aesKey, hmacKey := c.Key.Derive(salt)
mac := hmac.New(c.HashFunc, hmacKey)
mac.Write(header[:1+keySize+blockSize])
if !bytes.Equal(header[1+keySize+blockSize:], mac.Sum(nil)) {
return errors.New("cannot authenticate header")
}
mac.Write(header[1+keySize+blockSize:])
block, err := aes.NewCipher(aesKey)
if err != nil {
return err
}
stream := cipher.NewCTR(block, iv)
buf := make([]byte, c.bufSize()+c.HashSize)
n := 0
for {
n, err = src.Read(buf)
if err != nil {
if err == io.EOF {
break
}
return err
}
stream.XORKeyStream(buf[:n-c.HashSize], buf[:n-c.HashSize])
mac.Write(buf[:n-c.HashSize])
if !bytes.Equal(buf[n-c.HashSize:n], mac.Sum(nil)) {
return errors.New("cannot authenticate packet")
}
if _, err = dst.Write(buf[:n-c.HashSize]); err != nil {
return err
}
}
return nil
}