// 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 }