This repository has been archived on 2026-10-31. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Ananta-ps/pkg/crypto/chacha8/chacha8.go
T

115 lines
2.2 KiB
Go

package chacha8
import (
"encoding/binary"
"errors"
"math/bits"
)
// NetEase 4229938 UXChaCha8 stream cipher.
// Uses custom Sigma constants differing from standard ChaCha.
const (
Sigma0 uint32 = 0x60707865
Sigma1 uint32 = 0x3321646e
Sigma2 uint32 = 0x79622d32
Sigma3 uint32 = 0x6b206e74
)
type UxChaCha8 struct {
state [16]uint32
keystream [64]byte
keystreamPos int
}
func New(key []byte, nonce []byte) (*UxChaCha8, error) {
if len(key) != 32 {
return nil, errors.New("UXChaCha8 key must be 32 bytes")
}
if len(nonce) != 12 {
return nil, errors.New("UXChaCha8 nonce must be 12 bytes")
}
c := &UxChaCha8{
keystreamPos: 64,
}
c.state[0] = Sigma0
c.state[1] = Sigma1
c.state[2] = Sigma2
c.state[3] = Sigma3
for i := 0; i < 8; i++ {
c.state[4+i] = binary.LittleEndian.Uint32(key[i*4 : (i+1)*4])
}
c.state[12] = 0
c.state[13] = binary.LittleEndian.Uint32(nonce[0:4])
c.state[14] = binary.LittleEndian.Uint32(nonce[4:8])
c.state[15] = binary.LittleEndian.Uint32(nonce[8:12])
return c, nil
}
func (c *UxChaCha8) XorInPlace(data []byte) {
offset := 0
for offset < len(data) {
if c.keystreamPos == 64 {
c.generateBlock()
c.keystreamPos = 0
}
take := 64 - c.keystreamPos
if len(data)-offset < take {
take = len(data) - offset
}
for i := 0; i < take; i++ {
data[offset+i] ^= c.keystream[c.keystreamPos+i]
}
offset += take
c.keystreamPos += take
}
}
func (c *UxChaCha8) generateBlock() {
var x [16]uint32
copy(x[:], c.state[:])
for i := 0; i < 4; i++ {
quarterRound(&x[0], &x[4], &x[8], &x[12])
quarterRound(&x[1], &x[5], &x[9], &x[13])
quarterRound(&x[2], &x[6], &x[10], &x[14])
quarterRound(&x[3], &x[7], &x[11], &x[15])
quarterRound(&x[0], &x[5], &x[10], &x[15])
quarterRound(&x[1], &x[6], &x[11], &x[12])
quarterRound(&x[2], &x[7], &x[8], &x[13])
quarterRound(&x[3], &x[4], &x[9], &x[14])
}
for i := 0; i < 16; i++ {
binary.LittleEndian.PutUint32(c.keystream[i*4:(i+1)*4], x[i]+c.state[i])
}
c.state[12]++
}
func quarterRound(a, b, c, d *uint32) {
*a += *b
*d ^= *a
*d = bits.RotateLeft32(*d, 16)
*c += *d
*b ^= *c
*b = bits.RotateLeft32(*b, 12)
*a += *b
*d ^= *a
*d = bits.RotateLeft32(*d, 8)
*c += *d
*b ^= *c
*b = bits.RotateLeft32(*b, 7)
}