115 lines
2.2 KiB
Go
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)
|
|
}
|