<RFC.6455-5,3>: The masking does not affect the length of the “Payload data”. 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.
Octet i of the transformed <Hu-slation[fbno]: in order to produce the masked | version> data (“transformed-octet-i”) is the XOR of octet i of the original data (“original-octet-i”) with octet at index i modulo 4 of the masking key (“masking-key-octet-j”):
j = i MOD 4
transformed-octet-i = original-octet-i XOR masking-key-octet-j, Hu: Where XOR = either true, but not both, modulo = the modulo operation returns the remainder or signed remainder of a division, after one number is divided by another<r: Wikipedia a-r>
The payload length, indicated in the framing as frame-payload-length, does NOT include the length of the masking key. It is the length of the “Payload data”, e.g., the number of bytes following the masking key.
H3S1: Unmasking the payload:
var unmask = function(mask_bytes, buffer) {
var payload = new Buffer(buffer.length);
for (var i=0; i<buffer.length; i++) {
payload[i] = mask_bytes[i%4] ^ buffer[i];
}
return payload;
}
Wang<pg-46>[D]: The first bit of the second byte of the frame header indicates whether the frame is
masked. Every payload received by a WebSocket server is first unmasked before processing. After unmasking, the server has the original message contents: binary messages can be delivered directly, and text messages will be UTF-8 decoded and exposed through the server API as strings. Hu: Unmasking is done in only a single point, in the bidirectional-comms; on the server-side, after the message is received, from the client.
H4S1: Mozilla:
dev-Mozilla<a-r>: Let’s call the data ENCODED
, and the key MASK
. To get DECODED
, loop through the octets (bytes a.k.a. characters for text data) of ENCODED
and XOR the octet with the (i modulo 4)th octet of MASK
. In pseudocode (that happens to be valid JavaScript):
const MASK = [1, 2, 3, 4]; // 4-byte mask
const ENCODED = [105, 103, 111, 104, 110]; // encoded string "hello"
// Create the byte Array of decoded payload
const DECODED = Uint8Array.from(ENCODED, (elt, i) => elt ^ mask[i % 4]); // Perform an XOR on the mask
References:
https://www.rfc-editor.org/rfc/rfc6455#section-5.3
https://en.wikipedia.org/wiki/Modulo_operation
Wang, Vanessa, et al. HTML5 WebSocket: Build Real-Time Applications w/ HTML5. Apress.
https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers