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