package rabaead
import (
"crypto/cipher"
"errors"
"snix.ir/poly1305"
"snix.ir/rabbitio"
"snix.ir/rabbitio/subtle"
)
const polykeylen = 0x20 // poly1305 key len: 32byte
var ErrAuthMsg = errors.New("rabaead: message authentication failed")
type rabbitPoly1305 struct {
key []byte // rabbit cipher key
noncesize int // rabbit iv size
}
// NewAEAD returns a rabbit aead data-type
// key must be 16 byte len
func NewAEAD(key []byte) (cipher.AEAD, error) { return newRabbitAead(key) }
func newRabbitAead(key []byte) (cipher.AEAD, error) {
if len(key) != rabbitio.KeyLen {
return nil, rabbitio.ErrInvalidKey
}
rabbitAead := &rabbitPoly1305{
noncesize: rabbitio.IVXLen,
key: make([]byte, rabbitio.KeyLen),
}
copy(rabbitAead.key, key)
return rabbitAead, nil
}
// Overhead returns poly1305 tag size: 16byte
func (c *rabbitPoly1305) Overhead() int { return poly1305.TagSize }
// NonceSize returns rabbit iv len: 8byte
func (c *rabbitPoly1305) NonceSize() int { return c.noncesize }
func (c *rabbitPoly1305) sealRabbit(dst, nonce, plaintext, ad []byte) []byte {
ret, out := headtail(dst, len(plaintext)+poly1305.TagSize)
ciphertext, tag := out[:len(plaintext)], out[len(plaintext):]
if subtle.InexactOverlap(out, plaintext) {
panic("rabaead: invalid buffer memory overlap")
}
var polyKey [polykeylen]byte
s, err := rabbitio.NewCipher(c.key, nonce)
if err != nil {
panic(err)
}
s.XORKeyStream(polyKey[:], polyKey[:])
p := poly1305.New(&polyKey)
writePadding(p, ad)
s, err = rabbitio.NewCipher(c.key, nonce)
if err != nil {
panic(err)
}
s.XORKeyStream(ciphertext, plaintext)
writePadding(p, ciphertext)
writeUint64(p, len(ad))
writeUint64(p, len(plaintext))
p.Sum(tag[:0x00])
return ret
}
func (c *rabbitPoly1305) openRabbit(dst, nonce, ciphertext, ad []byte) ([]byte, error) {
tag := ciphertext[len(ciphertext)-poly1305.TagSize:]
ciphertext = ciphertext[:len(ciphertext)-poly1305.TagSize]
var polyKey [polykeylen]byte
s, err := rabbitio.NewCipher(c.key, nonce)
if err != nil {
panic(err)
}
s.XORKeyStream(polyKey[:], polyKey[:])
p := poly1305.New(&polyKey)
writePadding(p, ad)
writePadding(p, ciphertext)
writeUint64(p, len(ad))
writeUint64(p, len(ciphertext))
ret, out := headtail(dst, len(ciphertext))
if subtle.InexactOverlap(out, ciphertext) {
panic("rabaead: invalid buffer memory overlap")
}
// check data integrity
if !p.Verify(tag) {
return nil, ErrAuthMsg
}
s, err = rabbitio.NewCipher(c.key, nonce)
if err != nil {
panic(err)
}
s.XORKeyStream(out, ciphertext)
return ret, nil
}
// Open opens a rabbit aead ciphertext.
// panic occurs if nonce len is not equal to IVXLen (8byte) or zero
// if data is not verified, ErrAuthMsg will be returned
func (c *rabbitPoly1305) Open(dst, nonce, ciphertext, ad []byte) ([]byte, error) {
if len(ciphertext) < poly1305.TagSize {
return nil, ErrAuthMsg
}
if uint64(len(ciphertext)) > (1<<38)-48 {
panic("rabaead: ciphertext too large")
}
return c.openRabbit(dst, nonce, ciphertext, ad)
}
// Seal seals a plaintext into the rabbit aead ciphertext.
// panic occurs if nonce len is not equal to IVXLen (8byte) or zero
func (c *rabbitPoly1305) Seal(dst, nonce, plaintext, ad []byte) []byte {
if uint64(len(plaintext)) > (1<<38)-64 {
panic("rabaead: plaintext too large")
}
return c.sealRabbit(dst, nonce, plaintext, ad)
}