Files
WireGold/internal/algo/crypto.go
2025-05-13 00:59:05 +09:00

107 lines
2.7 KiB
Go

package algo
import (
"crypto/cipher"
"crypto/rand"
"encoding/binary"
"errors"
)
var (
ErrCipherTextTooShort = errors.New("ciphertext too short")
)
func EncodeAEAD(aead cipher.AEAD, additional uint16, b []byte) []byte {
nsz := aead.NonceSize()
// Accocate capacity for all the stuffs.
buf := make([]byte, 2+nsz+len(b)+aead.Overhead())
n := 0
binary.LittleEndian.PutUint16(buf[:2], additional)
nonce := buf[2 : 2+nsz]
// Select a random nonce
_, err := rand.Read(nonce)
if err != nil {
panic(err)
}
// Encrypt the message and append the ciphertext to the nonce.
eb := aead.Seal(nonce[nsz:nsz], nonce, b, buf[:2])
n = len(eb)
return buf[2 : 2+nsz+n]
}
func DecodeAEAD(aead cipher.AEAD, additional uint16, b []byte) (data []byte, err error) {
nsz := aead.NonceSize()
if len(b) < nsz {
return nil, ErrCipherTextTooShort
}
// Split nonce and ciphertext.
nonce, ciphertext := b[:nsz], b[nsz:]
if len(ciphertext) == 0 {
return nil, nil
}
// Decrypt the message and check it wasn't tampered with.
var buf [2]byte
binary.LittleEndian.PutUint16(buf[:], additional)
data = make([]byte, len(ciphertext))
n := 0
d, err := aead.Open(data[:0], nonce, ciphertext, buf[:])
n = len(d)
if err != nil {
return
}
return data[:n], nil
}
func EncodeXORLen(datalen int) int {
batchsz := datalen / 8
return 8 + batchsz*8 + 8 // seqrand dat tail
}
// EncodeXOR 按 8 字节, 以初始 mask 循环异或编码 data
func EncodeXOR(data []byte, mask uint64, seq uint32) []byte {
batchsz := len(data) / 8
remain := len(data) % 8
sum := mask
buf := make([]byte, EncodeXORLen(len(data)))
binary.LittleEndian.PutUint32(buf[:4], seq)
_, _ = rand.Read(buf[4:8]) // seqrand
sum ^= binary.LittleEndian.Uint64(buf[:8]) // init from seqrand
binary.LittleEndian.PutUint64(buf[:8], sum)
for i := 0; i < batchsz; i++ { // range on batch data
a := i * 8
b := (i + 1) * 8
sum ^= binary.LittleEndian.Uint64(data[a:b])
binary.LittleEndian.PutUint64(buf[a+8:b+8], sum)
}
p := batchsz * 8
copy(buf[8+p:], data[p:])
buf[len(buf)-1] = byte(remain)
sum ^= binary.LittleEndian.Uint64(buf[8+p:])
binary.LittleEndian.PutUint64(buf[8+p:], sum)
return buf
}
// DecodeXOR 按 8 字节, 以初始 mask 循环异或解码 data,
// 解码结果完全覆盖 data.
func DecodeXOR(data []byte, mask uint64) (uint32, []byte) {
if len(data) < 16 || len(data)%8 != 0 {
return 0, nil
}
batchsz := len(data) / 8
sum := mask
for i := 0; i < batchsz; i++ {
a := i * 8
b := (i + 1) * 8
x := binary.LittleEndian.Uint64(data[a:b])
sum ^= x
binary.LittleEndian.PutUint64(data[a:b], sum)
sum = x
}
remain := data[len(data)-1]
if remain >= 8 {
return 0, nil
}
return binary.LittleEndian.Uint32(data[:4]),
data[8 : len(data)-8+int(remain)]
}