ValarDragon 99e582d79a crypto: Refactor to move files out of the top level directory
Currently the top level directory contains basically all of the code
for the crypto package. This PR moves the crypto code into submodules
in a similar manner to what `golang/x/crypto` does. This improves code
organization.

Ref discussion: https://github.com/tendermint/tendermint/pull/1966

Closes #1956
2018-07-18 08:38:44 -07:00

107 lines
3.2 KiB
Go

// Package hkdfchacha20poly1305 creates an AEAD using hkdf, chacha20, and poly1305
// When sealing and opening, the hkdf is used to obtain the nonce and subkey for
// chacha20. Other than the change for the how the subkey and nonce for chacha
// are obtained, this is the same as chacha20poly1305
package hkdfchacha20poly1305
import (
"crypto/cipher"
"crypto/sha256"
"errors"
"io"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/hkdf"
)
// Implements crypto.AEAD
type hkdfchacha20poly1305 struct {
key [KeySize]byte
}
const (
// KeySize is the size of the key used by this AEAD, in bytes.
KeySize = 32
// NonceSize is the size of the nonce used with this AEAD, in bytes.
NonceSize = 24
// TagSize is the size added from poly1305
TagSize = 16
// MaxPlaintextSize is the max size that can be passed into a single call of Seal
MaxPlaintextSize = (1 << 38) - 64
// MaxCiphertextSize is the max size that can be passed into a single call of Open,
// this differs from plaintext size due to the tag
MaxCiphertextSize = (1 << 38) - 48
// HkdfInfo is the parameter used internally for Hkdf's info parameter.
HkdfInfo = "TENDERMINT_SECRET_CONNECTION_FRAME_KEY_DERIVE"
)
//New xChaChapoly1305 AEAD with 24 byte nonces
func New(key []byte) (cipher.AEAD, error) {
if len(key) != KeySize {
return nil, errors.New("chacha20poly1305: bad key length")
}
ret := new(hkdfchacha20poly1305)
copy(ret.key[:], key)
return ret, nil
}
func (c *hkdfchacha20poly1305) NonceSize() int {
return NonceSize
}
func (c *hkdfchacha20poly1305) Overhead() int {
return TagSize
}
func (c *hkdfchacha20poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
if len(nonce) != NonceSize {
panic("hkdfchacha20poly1305: bad nonce length passed to Seal")
}
if uint64(len(plaintext)) > MaxPlaintextSize {
panic("hkdfchacha20poly1305: plaintext too large")
}
subKey, chachaNonce := getSubkeyAndChachaNonceFromHkdf(&c.key, &nonce)
aead, err := chacha20poly1305.New(subKey[:])
if err != nil {
panic("hkdfchacha20poly1305: failed to initialize chacha20poly1305")
}
return aead.Seal(dst, chachaNonce[:], plaintext, additionalData)
}
func (c *hkdfchacha20poly1305) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
if len(nonce) != NonceSize {
return nil, errors.New("hkdfchacha20poly1305: bad nonce length passed to Open")
}
if uint64(len(ciphertext)) > MaxCiphertextSize {
return nil, errors.New("hkdfchacha20poly1305: ciphertext too large")
}
subKey, chachaNonce := getSubkeyAndChachaNonceFromHkdf(&c.key, &nonce)
aead, err := chacha20poly1305.New(subKey[:])
if err != nil {
panic("hkdfchacha20poly1305: failed to initialize chacha20poly1305")
}
return aead.Open(dst, chachaNonce[:], ciphertext, additionalData)
}
func getSubkeyAndChachaNonceFromHkdf(cKey *[32]byte, nonce *[]byte) (
subKey [KeySize]byte, chachaNonce [chacha20poly1305.NonceSize]byte) {
hash := sha256.New
hkdf := hkdf.New(hash, (*cKey)[:], *nonce, []byte(HkdfInfo))
_, err := io.ReadFull(hkdf, subKey[:])
if err != nil {
panic("hkdfchacha20poly1305: failed to read subkey from hkdf")
}
_, err = io.ReadFull(hkdf, chachaNonce[:])
if err != nil {
panic("hkdfchacha20poly1305: failed to read chachaNonce from hkdf")
}
return
}