12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061 |
- package ws
- import (
- "encoding/binary"
- )
- // Cipher applies XOR cipher to the payload using mask.
- // Offset is used to cipher chunked data (e.g. in io.Reader implementations).
- //
- // To convert masked data into unmasked data, or vice versa, the following
- // algorithm is applied. The same algorithm applies regardless of the
- // direction of the translation, e.g., the same steps are applied to
- // mask the data as to unmask the data.
- func Cipher(payload []byte, mask [4]byte, offset int) {
- n := len(payload)
- if n < 8 {
- for i := 0; i < n; i++ {
- payload[i] ^= mask[(offset+i)%4]
- }
- return
- }
- // Calculate position in mask due to previously processed bytes number.
- mpos := offset % 4
- // Count number of bytes will processed one by one from the beginning of payload.
- ln := remain[mpos]
- // Count number of bytes will processed one by one from the end of payload.
- // This is done to process payload by 8 bytes in each iteration of main loop.
- rn := (n - ln) % 8
- for i := 0; i < ln; i++ {
- payload[i] ^= mask[(mpos+i)%4]
- }
- for i := n - rn; i < n; i++ {
- payload[i] ^= mask[(mpos+i)%4]
- }
- // NOTE: we use here binary.LittleEndian regardless of what is real
- // endianess on machine is. To do so, we have to use binary.LittleEndian in
- // the masking loop below as well.
- var (
- m = binary.LittleEndian.Uint32(mask[:])
- m2 = uint64(m)<<32 | uint64(m)
- )
- // Skip already processed right part.
- // Get number of uint64 parts remaining to process.
- n = (n - ln - rn) >> 3
- for i := 0; i < n; i++ {
- var (
- j = ln + (i << 3)
- chunk = payload[j : j+8]
- )
- p := binary.LittleEndian.Uint64(chunk)
- p = p ^ m2
- binary.LittleEndian.PutUint64(chunk, p)
- }
- }
- // remain maps position in masking key [0,4) to number
- // of bytes that need to be processed manually inside Cipher().
- var remain = [4]int{0, 3, 2, 1}
|