mirror of
https://github.com/fumiama/WireGold.git
synced 2026-06-09 02:02:41 +08:00
168 lines
4.0 KiB
Go
168 lines
4.0 KiB
Go
package link
|
|
|
|
import (
|
|
"crypto/cipher"
|
|
"crypto/rand"
|
|
"encoding/binary"
|
|
"errors"
|
|
"math/bits"
|
|
mrand "math/rand"
|
|
|
|
"github.com/fumiama/WireGold/helper"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
var (
|
|
ErrCipherTextTooShort = errors.New("ciphertext too short")
|
|
)
|
|
|
|
func (l *Link) randkeyidx() uint8 {
|
|
if l.keys[1] == nil {
|
|
return 0
|
|
}
|
|
return uint8(mrand.Intn(32))
|
|
}
|
|
|
|
func mixkeys(k1, k2 []byte) []byte {
|
|
if len(k1) != 32 || len(k2) != 32 {
|
|
panic("unexpected key len")
|
|
}
|
|
k := make([]byte, 64)
|
|
for i := range k1 {
|
|
k1i, k2i := i, 31-i
|
|
k1v, k2v := k1[k1i], k2[k2i]
|
|
binary.LittleEndian.PutUint16(
|
|
k[i*2:(i+1)*2],
|
|
expandkeyunit(k1v, k2v),
|
|
)
|
|
}
|
|
return k
|
|
}
|
|
|
|
func expandkeyunit(v1, v2 byte) (v uint16) {
|
|
v1s, v2s := uint16(v1), uint16(bits.Reverse8(v2))
|
|
for i := 0; i < 8; i++ {
|
|
v |= v1s & (1 << (i * 2))
|
|
v1s <<= 1
|
|
}
|
|
for i := 0; i < 8; i++ {
|
|
v2s <<= 1
|
|
v |= v2s & (2 << (i * 2))
|
|
}
|
|
return
|
|
}
|
|
|
|
// Encode by aead and put b into pool
|
|
func (l *Link) Encode(teatype uint8, additional uint16, b []byte) (eb []byte) {
|
|
if len(b) == 0 || teatype >= 32 {
|
|
return
|
|
}
|
|
if l.keys[0] == nil {
|
|
eb = helper.MakeBytes(len(b))
|
|
copy(eb, b)
|
|
return
|
|
}
|
|
aead := l.keys[teatype]
|
|
if aead == nil {
|
|
logrus.Warnln("[crypto] cipher key at index", teatype, "is empty")
|
|
return
|
|
}
|
|
eb = encode(aead, additional, b)
|
|
return
|
|
}
|
|
|
|
// Decode by aead and put b into pool
|
|
func (l *Link) Decode(teatype uint8, additional uint16, b []byte) (db []byte, err error) {
|
|
if len(b) == 0 || teatype >= 32 {
|
|
return
|
|
}
|
|
if l.keys[0] == nil {
|
|
db = helper.MakeBytes(len(b))
|
|
copy(db, b)
|
|
return
|
|
}
|
|
aead := l.keys[teatype]
|
|
if aead == nil {
|
|
return
|
|
}
|
|
return decode(aead, additional, b)
|
|
}
|
|
|
|
func encode(aead cipher.AEAD, additional uint16, b []byte) []byte {
|
|
nsz := aead.NonceSize()
|
|
// Accocate capacity for all the stuffs.
|
|
buf := helper.MakeBytes(2 + nsz + len(b) + aead.Overhead())
|
|
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])
|
|
return nonce[:nsz+len(eb)]
|
|
}
|
|
|
|
func decode(aead cipher.AEAD, additional uint16, b []byte) ([]byte, 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)
|
|
return aead.Open(helper.SelectWriter().Bytes(), nonce, ciphertext, buf[:])
|
|
}
|
|
|
|
// xorenc 按 8 字节, 以初始 m.mask 循环异或编码 data
|
|
func (m *Me) xorenc(data []byte, seq uint32) []byte {
|
|
batchsz := len(data) / 8
|
|
remain := len(data) % 8
|
|
sum := m.mask
|
|
newdat := helper.MakeBytes(8 + batchsz*8 + 8) // seqrand dat tail
|
|
binary.LittleEndian.PutUint32(newdat[:4], seq)
|
|
_, _ = rand.Read(newdat[4:8]) // seqrand
|
|
sum ^= binary.LittleEndian.Uint64(newdat[:8]) // init from seqrand
|
|
binary.LittleEndian.PutUint64(newdat[: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(newdat[a+8:b+8], sum)
|
|
}
|
|
p := batchsz * 8
|
|
copy(newdat[8+p:], data[p:])
|
|
newdat[len(newdat)-1] = byte(remain)
|
|
sum ^= binary.LittleEndian.Uint64(newdat[8+p:])
|
|
binary.LittleEndian.PutUint64(newdat[8+p:], sum)
|
|
return newdat
|
|
}
|
|
|
|
// xordec 按 8 字节, 以初始 m.mask 循环异或解码 data
|
|
func (m *Me) xordec(data []byte) (uint32, []byte) {
|
|
if len(data) < 16 || len(data)%8 != 0 {
|
|
return 0, nil
|
|
}
|
|
batchsz := len(data) / 8
|
|
sum := m.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)]
|
|
}
|