74 lines
1.7 KiB
Go
74 lines
1.7 KiB
Go
package lafsession
|
|
|
|
import (
|
|
"crypto/cipher"
|
|
"encoding/binary"
|
|
|
|
"golang.org/x/crypto/chacha20poly1305"
|
|
)
|
|
|
|
const (
|
|
recordHeaderLen = 1 + 8
|
|
|
|
recordTypeData byte = 0x10
|
|
recordTypeClose byte = 0x11
|
|
)
|
|
|
|
func newAEAD(key []byte) (cipher.AEAD, error) {
|
|
return chacha20poly1305.New(key)
|
|
}
|
|
|
|
func sealRecord(aead cipher.AEAD, seq uint64, recType byte, plaintext, dst []byte, nonce *[nonceLen]byte, aad *[recordHeaderLen]byte) []byte {
|
|
aad[0] = recType
|
|
binary.BigEndian.PutUint64(aad[1:], seq)
|
|
|
|
for i := 0; i < 4; i++ {
|
|
nonce[i] = 0
|
|
}
|
|
binary.BigEndian.PutUint64(nonce[4:], seq)
|
|
|
|
need := recordHeaderLen + len(plaintext) + aead.Overhead()
|
|
if cap(dst) < need {
|
|
dst = make([]byte, need)
|
|
} else {
|
|
dst = dst[:need]
|
|
}
|
|
dst[0] = recType
|
|
binary.BigEndian.PutUint64(dst[1:9], seq)
|
|
_ = aead.Seal(dst[recordHeaderLen:recordHeaderLen], nonce[:], plaintext, aad[:])
|
|
return dst
|
|
}
|
|
|
|
// openRecord decrypts a full frame. Returns record type and plaintext (reusing dst when possible).
|
|
func openRecord(aead cipher.AEAD, frame, dst []byte, nonce *[nonceLen]byte, aad *[recordHeaderLen]byte) (recType byte, plaintext []byte, err error) {
|
|
if len(frame) < recordHeaderLen+aead.Overhead() {
|
|
return 0, nil, ErrShortMessage
|
|
}
|
|
recType = frame[0]
|
|
seq := binary.BigEndian.Uint64(frame[1:9])
|
|
|
|
aad[0] = recType
|
|
binary.BigEndian.PutUint64(aad[1:], seq)
|
|
|
|
for i := 0; i < 4; i++ {
|
|
nonce[i] = 0
|
|
}
|
|
binary.BigEndian.PutUint64(nonce[4:], seq)
|
|
|
|
ct := frame[recordHeaderLen:]
|
|
ptLen := len(ct) - aead.Overhead()
|
|
if ptLen < 0 {
|
|
return 0, nil, ErrShortMessage
|
|
}
|
|
if cap(dst) < ptLen {
|
|
dst = make([]byte, ptLen)
|
|
} else {
|
|
dst = dst[:ptLen]
|
|
}
|
|
out, err := aead.Open(dst[:0], nonce[:], ct, aad[:])
|
|
if err != nil {
|
|
return 0, nil, ErrBadTag
|
|
}
|
|
return recType, out, nil
|
|
}
|