restic/crypto/crypto.go

317 lines
7.9 KiB
Go

package crypto
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/json"
"errors"
"fmt"
"github.com/restic/restic/chunker"
"golang.org/x/crypto/poly1305"
"golang.org/x/crypto/scrypt"
)
const (
aesKeySize = 32 // for AES256
macKeySizeK = 16 // for AES-128
macKeySizeR = 16 // for Poly1305
macKeySize = macKeySizeK + macKeySizeR // for Poly1305-AES128
ivSize = aes.BlockSize
macSize = poly1305.TagSize // Poly1305 size is 16 byte
Extension = ivSize + macSize
)
var (
// ErrUnauthenticated is returned when ciphertext verification has failed.
ErrUnauthenticated = errors.New("ciphertext verification failed")
// ErrBufferTooSmall is returned when the destination slice is too small
// for the ciphertext.
ErrBufferTooSmall = errors.New("destination buffer too small")
)
// Key holds signing and encryption keys for a repository. It is stored
// encrypted and signed as a JSON data structure in the Data field of the Key
// structure. For the master key, the secret random polynomial used for content
// defined chunking is included.
type Key struct {
Sign SigningKey `json:"sign"`
Encrypt EncryptionKey `json:"encrypt"`
ChunkerPolynomial chunker.Pol `json:"chunker_polynomial,omitempty"`
}
type EncryptionKey [32]byte
type SigningKey struct {
K [16]byte `json:"k"` // for AES128
R [16]byte `json:"r"` // for Poly1305
}
// mask for key, (cf. http://cr.yp.to/mac/poly1305-20050329.pdf)
var poly1305KeyMask = [16]byte{
0xff,
0xff,
0xff,
0x0f, // 3: top four bits zero
0xfc, // 4: bottom two bits zero
0xff,
0xff,
0x0f, // 7: top four bits zero
0xfc, // 8: bottom two bits zero
0xff,
0xff,
0x0f, // 11: top four bits zero
0xfc, // 12: bottom two bits zero
0xff,
0xff,
0x0f, // 15: top four bits zero
}
// key is a [32]byte, in the form k||r
func poly1305Sign(msg []byte, nonce []byte, key *SigningKey) []byte {
// prepare key for low-level poly1305.Sum(): r||n
var k [32]byte
// make sure key is masked
maskKey(key)
// fill in nonce, encrypted with AES and key[:16]
cipher, err := aes.NewCipher(key.K[:])
if err != nil {
panic(err)
}
cipher.Encrypt(k[16:], nonce[:])
// copy r
copy(k[:16], key.R[:])
// save mac in out
var out [16]byte
poly1305.Sum(&out, msg, &k)
return out[:]
}
// mask poly1305 key
func maskKey(k *SigningKey) {
if k == nil {
return
}
for i := 0; i < poly1305.TagSize; i++ {
k.R[i] = k.R[i] & poly1305KeyMask[i]
}
}
// construct mac key from slice (k||r), with masking
func macKeyFromSlice(mk *SigningKey, data []byte) {
copy(mk.K[:], data[:16])
copy(mk.R[:], data[16:32])
maskKey(mk)
}
// key: k||r
func poly1305Verify(msg []byte, nonce []byte, key *SigningKey, mac []byte) bool {
// prepare key for low-level poly1305.Sum(): r||n
var k [32]byte
// make sure key is masked
maskKey(key)
// fill in nonce, encrypted with AES and key[:16]
cipher, err := aes.NewCipher(key.K[:])
if err != nil {
panic(err)
}
cipher.Encrypt(k[16:], nonce[:])
// copy r
copy(k[:16], key.R[:])
// copy mac to array
var m [16]byte
copy(m[:], mac)
return poly1305.Verify(&m, msg, &k)
}
// NewKey returns new encryption and signing keys.
func NewKey() (k *Key) {
k = &Key{}
n, err := rand.Read(k.Encrypt[:])
if n != aesKeySize || err != nil {
panic("unable to read enough random bytes for encryption key")
}
n, err = rand.Read(k.Sign.K[:])
if n != macKeySizeK || err != nil {
panic("unable to read enough random bytes for mac encryption key")
}
n, err = rand.Read(k.Sign.R[:])
if n != macKeySizeR || err != nil {
panic("unable to read enough random bytes for mac signing key")
}
// mask r
maskKey(&k.Sign)
return k
}
func newIV() []byte {
iv := make([]byte, ivSize)
n, err := rand.Read(iv)
if n != ivSize || err != nil {
panic("unable to read enough random bytes for iv")
}
return iv
}
type jsonMACKey struct {
K []byte `json:"k"`
R []byte `json:"r"`
}
func (m *SigningKey) MarshalJSON() ([]byte, error) {
return json.Marshal(jsonMACKey{K: m.K[:], R: m.R[:]})
}
func (m *SigningKey) UnmarshalJSON(data []byte) error {
j := jsonMACKey{}
err := json.Unmarshal(data, &j)
if err != nil {
return err
}
copy(m.K[:], j.K)
copy(m.R[:], j.R)
return nil
}
func (k *EncryptionKey) MarshalJSON() ([]byte, error) {
return json.Marshal(k[:])
}
func (k *EncryptionKey) UnmarshalJSON(data []byte) error {
d := make([]byte, aesKeySize)
err := json.Unmarshal(data, &d)
if err != nil {
return err
}
copy(k[:], d)
return nil
}
// ErrInvalidCiphertext is returned when trying to encrypt into the slice that
// holds the plaintext.
var ErrInvalidCiphertext = errors.New("invalid ciphertext, same slice used for plaintext")
// Encrypt encrypts and signs data. Stored in ciphertext is IV || Ciphertext ||
// MAC. Encrypt returns the new ciphertext slice, which is extended when
// necessary. ciphertext and plaintext may not point to (exactly) the same
// slice or non-intersecting slices.
func Encrypt(ks *Key, ciphertext, plaintext []byte) ([]byte, error) {
ciphertext = ciphertext[:cap(ciphertext)]
// test for same slice, if possible
if len(plaintext) > 0 && len(ciphertext) > 0 && &plaintext[0] == &ciphertext[0] {
return nil, ErrInvalidCiphertext
}
// extend ciphertext slice if necessary
if len(ciphertext) < len(plaintext)+Extension {
ext := len(plaintext) + Extension - cap(ciphertext)
ciphertext = append(ciphertext, make([]byte, ext)...)
ciphertext = ciphertext[:cap(ciphertext)]
}
iv := newIV()
c, err := aes.NewCipher(ks.Encrypt[:])
if err != nil {
panic(fmt.Sprintf("unable to create cipher: %v", err))
}
e := cipher.NewCTR(c, iv[:])
e.XORKeyStream(ciphertext[ivSize:], plaintext)
copy(ciphertext, iv[:])
// truncate to only conver iv and actual ciphertext
ciphertext = ciphertext[:ivSize+len(plaintext)]
mac := poly1305Sign(ciphertext[ivSize:], ciphertext[:ivSize], &ks.Sign)
// append the mac tag
ciphertext = append(ciphertext, mac...)
return ciphertext, nil
}
// Decrypt verifies and decrypts the ciphertext. Ciphertext must be in the form
// IV || Ciphertext || MAC. plaintext and ciphertext may point to (exactly) the
// same slice.
func Decrypt(ks *Key, plaintext, ciphertext []byte) ([]byte, error) {
// check for plausible length
if len(ciphertext) < ivSize+macSize {
panic("trying to decrypt invalid data: ciphertext too small")
}
if cap(plaintext) < len(ciphertext) {
// extend plaintext
plaintext = append(plaintext, make([]byte, len(ciphertext)-cap(plaintext))...)
}
// extract mac
l := len(ciphertext) - macSize
ciphertext, mac := ciphertext[:l], ciphertext[l:]
// verify mac
if !poly1305Verify(ciphertext[ivSize:], ciphertext[:ivSize], &ks.Sign, mac) {
return nil, ErrUnauthenticated
}
// extract iv
iv, ciphertext := ciphertext[:ivSize], ciphertext[ivSize:]
// decrypt data
c, err := aes.NewCipher(ks.Encrypt[:])
if err != nil {
panic(fmt.Sprintf("unable to create cipher: %v", err))
}
// decrypt
e := cipher.NewCTR(c, iv)
plaintext = plaintext[:len(ciphertext)]
e.XORKeyStream(plaintext, ciphertext)
return plaintext, nil
}
// KDF derives encryption and signing keys from the password using the supplied
// parameters N, R and P and the Salt.
func KDF(N, R, P int, salt []byte, password string) (*Key, error) {
if len(salt) == 0 {
return nil, fmt.Errorf("scrypt() called with empty salt")
}
derKeys := &Key{}
keybytes := macKeySize + aesKeySize
scryptKeys, err := scrypt.Key([]byte(password), salt, N, R, P, keybytes)
if err != nil {
return nil, fmt.Errorf("error deriving keys from password: %v", err)
}
if len(scryptKeys) != keybytes {
return nil, fmt.Errorf("invalid numbers of bytes expanded from scrypt(): %d", len(scryptKeys))
}
// first 32 byte of scrypt output is the encryption key
copy(derKeys.Encrypt[:], scryptKeys[:aesKeySize])
// next 32 byte of scrypt output is the mac key, in the form k||r
macKeyFromSlice(&derKeys.Sign, scryptKeys[aesKeySize:])
return derKeys, nil
}