github.com/simonmittag/ws@v1.1.0-rc.5.0.20210419231947-82b846128245/cipher.go (about)

     1  package ws
     2  
     3  import (
     4  	"encoding/binary"
     5  )
     6  
     7  // Cipher applies XOR cipher to the payload using mask.
     8  // Offset is used to cipher chunked data (e.g. in io.Reader implementations).
     9  //
    10  // To convert masked data into unmasked data, or vice versa, the following
    11  // algorithm is applied.  The same algorithm applies regardless of the
    12  // direction of the translation, e.g., the same steps are applied to
    13  // mask the data as to unmask the data.
    14  func Cipher(payload []byte, mask [4]byte, offset int) {
    15  	n := len(payload)
    16  	if n < 8 {
    17  		for i := 0; i < n; i++ {
    18  			payload[i] ^= mask[(offset+i)%4]
    19  		}
    20  		return
    21  	}
    22  
    23  	// Calculate position in mask due to previously processed bytes number.
    24  	mpos := offset % 4
    25  	// Count number of bytes will processed one by one from the beginning of payload.
    26  	ln := remain[mpos]
    27  	// Count number of bytes will processed one by one from the end of payload.
    28  	// This is done to process payload by 8 bytes in each iteration of main loop.
    29  	rn := (n - ln) % 8
    30  
    31  	for i := 0; i < ln; i++ {
    32  		payload[i] ^= mask[(mpos+i)%4]
    33  	}
    34  	for i := n - rn; i < n; i++ {
    35  		payload[i] ^= mask[(mpos+i)%4]
    36  	}
    37  
    38  	// NOTE: we use here binary.LittleEndian regardless of what is real
    39  	// endianness on machine is. To do so, we have to use binary.LittleEndian in
    40  	// the masking loop below as well.
    41  	var (
    42  		m  = binary.LittleEndian.Uint32(mask[:])
    43  		m2 = uint64(m)<<32 | uint64(m)
    44  	)
    45  	// Skip already processed right part.
    46  	// Get number of uint64 parts remaining to process.
    47  	n = (n - ln - rn) >> 3
    48  	for i := 0; i < n; i++ {
    49  		var (
    50  			j     = ln + (i << 3)
    51  			chunk = payload[j : j+8]
    52  		)
    53  		p := binary.LittleEndian.Uint64(chunk)
    54  		p = p ^ m2
    55  		binary.LittleEndian.PutUint64(chunk, p)
    56  	}
    57  }
    58  
    59  // remain maps position in masking key [0,4) to number
    60  // of bytes that need to be processed manually inside Cipher().
    61  var remain = [4]int{0, 3, 2, 1}