mask.go raw

   1  package websocket
   2  
   3  import (
   4  	"encoding/binary"
   5  	"math/bits"
   6  )
   7  
   8  // maskGo applies the WebSocket masking algorithm to p
   9  // with the given key.
  10  // See https://tools.ietf.org/html/rfc6455#section-5.3
  11  //
  12  // The returned value is the correctly rotated key to
  13  // to continue to mask/unmask the message.
  14  //
  15  // It is optimized for LittleEndian and expects the key
  16  // to be in little endian.
  17  //
  18  // See https://github.com/golang/go/issues/31586
  19  func maskGo(b []byte, key uint32) uint32 {
  20  	if len(b) >= 8 {
  21  		key64 := uint64(key)<<32 | uint64(key)
  22  
  23  		// At some point in the future we can clean these unrolled loops up.
  24  		// See https://github.com/golang/go/issues/31586#issuecomment-487436401
  25  
  26  		// Then we xor until b is less than 128 bytes.
  27  		for len(b) >= 128 {
  28  			v := binary.LittleEndian.Uint64(b)
  29  			binary.LittleEndian.PutUint64(b, v^key64)
  30  			v = binary.LittleEndian.Uint64(b[8:16])
  31  			binary.LittleEndian.PutUint64(b[8:16], v^key64)
  32  			v = binary.LittleEndian.Uint64(b[16:24])
  33  			binary.LittleEndian.PutUint64(b[16:24], v^key64)
  34  			v = binary.LittleEndian.Uint64(b[24:32])
  35  			binary.LittleEndian.PutUint64(b[24:32], v^key64)
  36  			v = binary.LittleEndian.Uint64(b[32:40])
  37  			binary.LittleEndian.PutUint64(b[32:40], v^key64)
  38  			v = binary.LittleEndian.Uint64(b[40:48])
  39  			binary.LittleEndian.PutUint64(b[40:48], v^key64)
  40  			v = binary.LittleEndian.Uint64(b[48:56])
  41  			binary.LittleEndian.PutUint64(b[48:56], v^key64)
  42  			v = binary.LittleEndian.Uint64(b[56:64])
  43  			binary.LittleEndian.PutUint64(b[56:64], v^key64)
  44  			v = binary.LittleEndian.Uint64(b[64:72])
  45  			binary.LittleEndian.PutUint64(b[64:72], v^key64)
  46  			v = binary.LittleEndian.Uint64(b[72:80])
  47  			binary.LittleEndian.PutUint64(b[72:80], v^key64)
  48  			v = binary.LittleEndian.Uint64(b[80:88])
  49  			binary.LittleEndian.PutUint64(b[80:88], v^key64)
  50  			v = binary.LittleEndian.Uint64(b[88:96])
  51  			binary.LittleEndian.PutUint64(b[88:96], v^key64)
  52  			v = binary.LittleEndian.Uint64(b[96:104])
  53  			binary.LittleEndian.PutUint64(b[96:104], v^key64)
  54  			v = binary.LittleEndian.Uint64(b[104:112])
  55  			binary.LittleEndian.PutUint64(b[104:112], v^key64)
  56  			v = binary.LittleEndian.Uint64(b[112:120])
  57  			binary.LittleEndian.PutUint64(b[112:120], v^key64)
  58  			v = binary.LittleEndian.Uint64(b[120:128])
  59  			binary.LittleEndian.PutUint64(b[120:128], v^key64)
  60  			b = b[128:]
  61  		}
  62  
  63  		// Then we xor until b is less than 64 bytes.
  64  		for len(b) >= 64 {
  65  			v := binary.LittleEndian.Uint64(b)
  66  			binary.LittleEndian.PutUint64(b, v^key64)
  67  			v = binary.LittleEndian.Uint64(b[8:16])
  68  			binary.LittleEndian.PutUint64(b[8:16], v^key64)
  69  			v = binary.LittleEndian.Uint64(b[16:24])
  70  			binary.LittleEndian.PutUint64(b[16:24], v^key64)
  71  			v = binary.LittleEndian.Uint64(b[24:32])
  72  			binary.LittleEndian.PutUint64(b[24:32], v^key64)
  73  			v = binary.LittleEndian.Uint64(b[32:40])
  74  			binary.LittleEndian.PutUint64(b[32:40], v^key64)
  75  			v = binary.LittleEndian.Uint64(b[40:48])
  76  			binary.LittleEndian.PutUint64(b[40:48], v^key64)
  77  			v = binary.LittleEndian.Uint64(b[48:56])
  78  			binary.LittleEndian.PutUint64(b[48:56], v^key64)
  79  			v = binary.LittleEndian.Uint64(b[56:64])
  80  			binary.LittleEndian.PutUint64(b[56:64], v^key64)
  81  			b = b[64:]
  82  		}
  83  
  84  		// Then we xor until b is less than 32 bytes.
  85  		for len(b) >= 32 {
  86  			v := binary.LittleEndian.Uint64(b)
  87  			binary.LittleEndian.PutUint64(b, v^key64)
  88  			v = binary.LittleEndian.Uint64(b[8:16])
  89  			binary.LittleEndian.PutUint64(b[8:16], v^key64)
  90  			v = binary.LittleEndian.Uint64(b[16:24])
  91  			binary.LittleEndian.PutUint64(b[16:24], v^key64)
  92  			v = binary.LittleEndian.Uint64(b[24:32])
  93  			binary.LittleEndian.PutUint64(b[24:32], v^key64)
  94  			b = b[32:]
  95  		}
  96  
  97  		// Then we xor until b is less than 16 bytes.
  98  		for len(b) >= 16 {
  99  			v := binary.LittleEndian.Uint64(b)
 100  			binary.LittleEndian.PutUint64(b, v^key64)
 101  			v = binary.LittleEndian.Uint64(b[8:16])
 102  			binary.LittleEndian.PutUint64(b[8:16], v^key64)
 103  			b = b[16:]
 104  		}
 105  
 106  		// Then we xor until b is less than 8 bytes.
 107  		for len(b) >= 8 {
 108  			v := binary.LittleEndian.Uint64(b)
 109  			binary.LittleEndian.PutUint64(b, v^key64)
 110  			b = b[8:]
 111  		}
 112  	}
 113  
 114  	// Then we xor until b is less than 4 bytes.
 115  	for len(b) >= 4 {
 116  		v := binary.LittleEndian.Uint32(b)
 117  		binary.LittleEndian.PutUint32(b, v^key)
 118  		b = b[4:]
 119  	}
 120  
 121  	// xor remaining bytes.
 122  	for i := range b {
 123  		b[i] ^= byte(key)
 124  		key = bits.RotateLeft32(key, -8)
 125  	}
 126  
 127  	return key
 128  }
 129