curl-fuzzer C++ reference
Loading...
Searching...
No Matches
ws_accept_key.h
Go to the documentation of this file.
1/*
2 * Copyright (C) Max Dymond, <cmeister2@gmail.com>, et al.
3 *
4 * SPDX-License-Identifier: curl
5 */
6
12
13#ifndef PROTO_FUZZER_WS_ACCEPT_KEY_H_
14#define PROTO_FUZZER_WS_ACCEPT_KEY_H_
15
16#include <array>
17#include <cstddef>
18#include <cstdint>
19#include <cstring>
20#include <string>
21
22namespace proto_fuzzer {
23namespace detail {
24
29inline std::array<uint8_t, 20> Sha1(const uint8_t* data, std::size_t len) {
30 // Initial hash values.
31 uint32_t h0 = 0x67452301, h1 = 0xEFCDAB89, h2 = 0x98BADCFE, h3 = 0x10325476, h4 = 0xC3D2E1F0;
32
33 // Pre-processing: build the padded message.
34 uint64_t bit_len = static_cast<uint64_t>(len) * 8;
35 // Number of bytes after original message: 1 (0x80) + padding + 8 (length).
36 std::size_t padded_len = ((len + 8) / 64 + 1) * 64;
37 // Use a small stack buffer for typical WebSocket key sizes (< 128 bytes).
38 // Fall back to heap for anything larger.
39 uint8_t stack_buf[128];
40 uint8_t* msg;
41 bool heap = padded_len > sizeof(stack_buf);
42 if (heap) {
43 msg = new uint8_t[padded_len]();
44 } else {
45 msg = stack_buf;
46 std::memset(msg, 0, padded_len);
47 }
48 std::memcpy(msg, data, len);
49 msg[len] = 0x80;
50 // Append original length in bits as big-endian 64-bit.
51 for (int i = 0; i < 8; ++i) {
52 msg[padded_len - 1 - i] = static_cast<uint8_t>(bit_len >> (i * 8));
53 }
54
55 auto left_rotate = [](uint32_t v, unsigned n) -> uint32_t { return (v << n) | (v >> (32 - n)); };
56
57 for (std::size_t offset = 0; offset < padded_len; offset += 64) {
58 uint32_t w[80];
59 for (int i = 0; i < 16; ++i) {
60 w[i] = static_cast<uint32_t>(msg[offset + 4 * i]) << 24 | static_cast<uint32_t>(msg[offset + 4 * i + 1]) << 16 |
61 static_cast<uint32_t>(msg[offset + 4 * i + 2]) << 8 | static_cast<uint32_t>(msg[offset + 4 * i + 3]);
62 }
63 for (int i = 16; i < 80; ++i) {
64 w[i] = left_rotate(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
65 }
66 uint32_t a = h0, b = h1, c = h2, d = h3, e = h4;
67 for (int i = 0; i < 80; ++i) {
68 uint32_t f, k;
69 if (i < 20) {
70 f = (b & c) | ((~b) & d);
71 k = 0x5A827999;
72 } else if (i < 40) {
73 f = b ^ c ^ d;
74 k = 0x6ED9EBA1;
75 } else if (i < 60) {
76 f = (b & c) | (b & d) | (c & d);
77 k = 0x8F1BBCDC;
78 } else {
79 f = b ^ c ^ d;
80 k = 0xCA62C1D6;
81 }
82 uint32_t temp = left_rotate(a, 5) + f + e + k + w[i];
83 e = d;
84 d = c;
85 c = left_rotate(b, 30);
86 b = a;
87 a = temp;
88 }
89 h0 += a;
90 h1 += b;
91 h2 += c;
92 h3 += d;
93 h4 += e;
94 }
95
96 if (heap) {
97 delete[] msg;
98 }
99
100 std::array<uint8_t, 20> digest;
101 auto store = [&](int idx, uint32_t val) {
102 digest[idx * 4 + 0] = static_cast<uint8_t>(val >> 24);
103 digest[idx * 4 + 1] = static_cast<uint8_t>(val >> 16);
104 digest[idx * 4 + 2] = static_cast<uint8_t>(val >> 8);
105 digest[idx * 4 + 3] = static_cast<uint8_t>(val);
106 };
107 store(0, h0);
108 store(1, h1);
109 store(2, h2);
110 store(3, h3);
111 store(4, h4);
112 return digest;
113}
114
119inline std::string Base64Encode(const uint8_t* data, std::size_t len) {
120 static const char kTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
121 std::string out;
122 out.reserve(4 * ((len + 2) / 3));
123 for (std::size_t i = 0; i < len; i += 3) {
124 uint32_t n = static_cast<uint32_t>(data[i]) << 16;
125 if (i + 1 < len) n |= static_cast<uint32_t>(data[i + 1]) << 8;
126 if (i + 2 < len) n |= static_cast<uint32_t>(data[i + 2]);
127 out.push_back(kTable[(n >> 18) & 0x3F]);
128 out.push_back(kTable[(n >> 12) & 0x3F]);
129 out.push_back((i + 1 < len) ? kTable[(n >> 6) & 0x3F] : '=');
130 out.push_back((i + 2 < len) ? kTable[n & 0x3F] : '=');
131 }
132 return out;
133}
134
135} // namespace detail
136
140inline std::string ComputeWebSocketAcceptKey(const std::string& key) {
141 static const char kGuid[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
142 std::string combined = key + kGuid;
143 auto digest = detail::Sha1(reinterpret_cast<const uint8_t*>(combined.data()), combined.size());
144 return detail::Base64Encode(digest.data(), digest.size());
145}
146
147} // namespace proto_fuzzer
148
149#endif // PROTO_FUZZER_WS_ACCEPT_KEY_H_
std::string Base64Encode(const uint8_t *data, std::size_t len)
Minimal base64 encoder.
std::array< uint8_t, 20 > Sha1(const uint8_t *data, std::size_t len)
Minimal SHA-1 (FIPS 180-4) — not for security use; only for the WS accept key.
std::string ComputeWebSocketAcceptKey(const std::string &key)
Compute the RFC 6455 Sec-WebSocket-Accept value for a given client key.