github.com/EagleQL/Xray-core@v1.4.3/common/crypto/internal/chacha.go (about) 1 package internal 2 3 //go:generate go run chacha_core_gen.go 4 5 import ( 6 "encoding/binary" 7 ) 8 9 const ( 10 wordSize = 4 // the size of ChaCha20's words 11 stateSize = 16 // the size of ChaCha20's state, in words 12 blockSize = stateSize * wordSize // the size of ChaCha20's block, in bytes 13 ) 14 15 type ChaCha20Stream struct { 16 state [stateSize]uint32 // the state as an array of 16 32-bit words 17 block [blockSize]byte // the keystream as an array of 64 bytes 18 offset int // the offset of used bytes in block 19 rounds int 20 } 21 22 func NewChaCha20Stream(key []byte, nonce []byte, rounds int) *ChaCha20Stream { 23 s := new(ChaCha20Stream) 24 // the magic constants for 256-bit keys 25 s.state[0] = 0x61707865 26 s.state[1] = 0x3320646e 27 s.state[2] = 0x79622d32 28 s.state[3] = 0x6b206574 29 30 for i := 0; i < 8; i++ { 31 s.state[i+4] = binary.LittleEndian.Uint32(key[i*4 : i*4+4]) 32 } 33 34 switch len(nonce) { 35 case 8: 36 s.state[14] = binary.LittleEndian.Uint32(nonce[0:]) 37 s.state[15] = binary.LittleEndian.Uint32(nonce[4:]) 38 case 12: 39 s.state[13] = binary.LittleEndian.Uint32(nonce[0:4]) 40 s.state[14] = binary.LittleEndian.Uint32(nonce[4:8]) 41 s.state[15] = binary.LittleEndian.Uint32(nonce[8:12]) 42 default: 43 panic("bad nonce length") 44 } 45 46 s.rounds = rounds 47 ChaCha20Block(&s.state, s.block[:], s.rounds) 48 return s 49 } 50 51 func (s *ChaCha20Stream) XORKeyStream(dst, src []byte) { 52 // Stride over the input in 64-byte blocks, minus the amount of keystream 53 // previously used. This will produce best results when processing blocks 54 // of a size evenly divisible by 64. 55 i := 0 56 max := len(src) 57 for i < max { 58 gap := blockSize - s.offset 59 60 limit := i + gap 61 if limit > max { 62 limit = max 63 } 64 65 o := s.offset 66 for j := i; j < limit; j++ { 67 dst[j] = src[j] ^ s.block[o] 68 o++ 69 } 70 71 i += gap 72 s.offset = o 73 74 if o == blockSize { 75 s.offset = 0 76 s.state[12]++ 77 ChaCha20Block(&s.state, s.block[:], s.rounds) 78 } 79 } 80 }