forked from quic-go/quic-go
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.
134 lines
3.4 KiB
Go
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)
|
|
}
|