github.com/xraypb/Xray-core@v1.8.1/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  }