Files
quic-go/crypto/chacha20poly1305trunc12/chacha20poly1305.go
Lucas Clemente 86b4ffdaaf use chacha20poly1305 instead of AES and include cert in key derivation
Unfortunately QUIC uses non-standard tag sizes with both AES-GCM and
Poly1305. Adopting AES-GCM seems much harder, so I changed it to
Chacha20Poly1305 and only made some slight changes to an existing algo.
This should probably be double-checked at some point.
2016-04-14 23:44:31 +02:00

134 lines
3.4 KiB
Go

// Taken and modified to have tag size = 12
// from https://github.com/EncEve/crypto/blob/master/chacha/chachaPoly1305.go
// TODO: Check, make independent
package chacha20poly1305trunc12
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
import (
"crypto/cipher"
"crypto/subtle"
"github.com/EncEve/crypto"
"github.com/EncEve/crypto/chacha"
"github.com/EncEve/crypto/poly1305"
)
// The AEAD cipher ChaCha20-Poly1305
type aeadCipher struct {
key [32]byte
}
// NewAEAD returns a cipher.AEAD implementing the
// ChaCha20-Poly1305 construction specified in
// RFC 7539. The key argument must be 256 bit
// (32 byte).
func NewAEAD(key []byte) (cipher.AEAD, error) {
if k := len(key); k != 32 {
return nil, crypto.KeySizeError(k)
}
c := new(aeadCipher)
for i, v := range key {
c.key[i] = v
}
return c, nil
}
func (c *aeadCipher) Overhead() int { return 12 }
func (c *aeadCipher) NonceSize() int { return chacha.NonceSize }
func (c *aeadCipher) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
if n := len(nonce); n != chacha.NonceSize {
panic(crypto.NonceSizeError(n))
}
if len(dst) < len(plaintext) {
panic("dst buffer to small")
}
// create the ploy1305 key
var polyKey [32]byte
var tmp [64]byte
chacha.XORKeyStream(tmp[:], c.key[:], nonce, 0, tmp[:])
copy(polyKey[:], tmp[:32])
// encrypt the plaintext
n := len(plaintext)
chacha.XORKeyStream(dst, c.key[:], nonce, 1, plaintext)
// authenticate the ciphertext
tag := authenticate(&polyKey, dst[:n], additionalData)
return append(dst[:n], tag[0:12]...)
}
func (c *aeadCipher) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
if n := len(nonce); n != chacha.NonceSize {
return nil, crypto.NonceSizeError(n)
}
if len(ciphertext) < 12 {
return nil, crypto.AuthenticationError{}
}
if len(dst) < len(ciphertext)-12 {
panic("dst buffer to small")
}
hash := ciphertext[len(ciphertext)-12:]
ciphertext = ciphertext[:len(ciphertext)-12]
// create the ploy1305 key
var polyKey [32]byte
var tmp [64]byte
chacha.XORKeyStream(tmp[:], c.key[:], nonce, 0, tmp[:])
copy(polyKey[:], tmp[:32])
// authenticate the ciphertext
tag := authenticate(&polyKey, ciphertext, additionalData)
if subtle.ConstantTimeCompare(tag[0:12], hash[0:12]) != 1 {
return nil, crypto.AuthenticationError{}
}
// decrypt ciphertext
chacha.XORKeyStream(dst, c.key[:], nonce, 1, ciphertext)
return dst[:len(ciphertext)], nil
}
// authenticate calculates the poly1305 tag from
// the given ciphertext and additional data.
func authenticate(key *[32]byte, ciphertext, additionalData []byte) []byte {
ctLen := uint64(len(ciphertext))
adLen := uint64(len(additionalData))
padAD, padCT := adLen%16, ctLen%16
var buf [16]byte
buf[0] = byte(adLen)
buf[1] = byte(adLen >> 8)
buf[2] = byte(adLen >> 16)
buf[3] = byte(adLen >> 24)
buf[4] = byte(adLen >> 32)
buf[5] = byte(adLen >> 40)
buf[6] = byte(adLen >> 48)
buf[7] = byte(adLen >> 56)
buf[8] = byte(ctLen)
buf[9] = byte(ctLen >> 8)
buf[10] = byte(ctLen >> 16)
buf[11] = byte(ctLen >> 24)
buf[12] = byte(ctLen >> 32)
buf[13] = byte(ctLen >> 40)
buf[14] = byte(ctLen >> 48)
buf[15] = byte(ctLen >> 56)
poly, _ := poly1305.New(key[:])
poly.Write(additionalData)
if padAD > 0 {
poly.Write(make([]byte, 16-padAD))
}
poly.Write(ciphertext)
if padCT > 0 {
poly.Write(make([]byte, 16-padCT))
}
poly.Write(buf[:])
return poly.Sum(nil)
}