From eecdc408182bbcfd7c5b27eea0e1de3a789cc3a3 Mon Sep 17 00:00:00 2001 From: ZitongLi Date: Thu, 17 Jul 2025 09:35:56 +0800 Subject: [PATCH] Random generate sec-websocket-key Random generate sec-websocket-key Issue: https://gitee.com/openharmony/arkcompiler_toolchain/issues/ICMQ18 Signed-off-by: zitongli --- websocket/client/websocket_client.cpp | 12 ++++++------ websocket/client/websocket_client.h | 24 +++++++++++++----------- websocket/define.h | 1 + websocket/handshake_helper.cpp | 19 +++++++++++++++++++ websocket/handshake_helper.h | 4 +++- 5 files changed, 42 insertions(+), 18 deletions(-) diff --git a/websocket/client/websocket_client.cpp b/websocket/client/websocket_client.cpp index 3a151955..c8a629a8 100644 --- a/websocket/client/websocket_client.cpp +++ b/websocket/client/websocket_client.cpp @@ -22,13 +22,11 @@ #include "client/websocket_client.h" namespace OHOS::ArkCompiler::Toolchain { -static bool ValidateServerHandShake(HttpResponse& response) +bool WebSocketClient::ValidateServerHandShake(HttpResponse& response) { static constexpr std::string_view HTTP_SWITCHING_PROTOCOLS_STATUS_CODE = "101"; static constexpr std::string_view HTTP_RESPONSE_REQUIRED_UPGRADE = "websocket"; static constexpr std::string_view HTTP_RESPONSE_REQUIRED_CONNECTION = "upgrade"; - // NB! `defaultWebSocketKey` must match "Sec-WebSocket-Key" used in `WebSocketClient`. - static constexpr unsigned char defaultWebSocketKey[] = "64b4B+s5JDlgkdg7NekJ+g=="; // in accordance to https://www.rfc-editor.org/rfc/rfc6455#section-4.1 if (response.status != HTTP_SWITCHING_PROTOCOLS_STATUS_CODE) { @@ -46,7 +44,7 @@ static bool ValidateServerHandShake(HttpResponse& response) // The same WebSocket-Key is used for all connections // - must either use a randomly-selected, as required by spec or do this calculation statically. unsigned char expectedSecWebSocketAccept[WebSocketKeyEncoder::ENCODED_KEY_LEN + 1]; - if (!WebSocketKeyEncoder::EncodeKey(defaultWebSocketKey, expectedSecWebSocketAccept)) { + if (!WebSocketKeyEncoder::EncodeKey(secWebSocketKey_, expectedSecWebSocketAccept)) { LOGE("ValidateServerHandShake failed to generate expected Sec-WebSocket-Accept token"); return false; } @@ -180,8 +178,10 @@ bool WebSocketClient::ClientSendWSUpgradeReq() return true; } - // length without null-terminator - if (!Send(GetConnectionSocket(), CLIENT_WEBSOCKET_UPGRADE_REQ, sizeof(CLIENT_WEBSOCKET_UPGRADE_REQ) - 1, 0)) { + secWebSocketKey_ = WebSocketKeyEncoder::GenerateRandomSecWSKey(); + std::string upgradeReq = std::string(CLIENT_WS_UPGRADE_REQ_BEFORE_KEY) + secWebSocketKey_ + + std::string(CLIENT_WS_UPGRADE_REQ_AFTER_KEY); + if (!Send(GetConnectionSocket(), upgradeReq.data(), upgradeReq.size(), 0)) { LOGE("ClientSendWSUpgradeReq::client send wsupgrade req failed, error = %{public}d, desc = %{public}sn", errno, strerror(errno)); CloseOnInitFailure(); diff --git a/websocket/client/websocket_client.h b/websocket/client/websocket_client.h index ded6a969..08f50033 100644 --- a/websocket/client/websocket_client.h +++ b/websocket/client/websocket_client.h @@ -47,6 +47,7 @@ private: std::string CreateFrame(bool isLast, FrameType frameType) const override; std::string CreateFrame(bool isLast, FrameType frameType, const std::string& payload) const override; std::string CreateFrame(bool isLast, FrameType frameType, std::string&& payload) const override; + bool ValidateServerHandShake(HttpResponse& response); private: static constexpr std::array SOCKET_STATE_NAMES = { @@ -56,20 +57,21 @@ private: "closed" }; - // May replace default websocket-key with randomly generated, as required by spec. - static constexpr char CLIENT_WEBSOCKET_UPGRADE_REQ[] = "GET / HTTP/1.1\r\n" - "Connection: Upgrade\r\n" - "Pragma: no-cache\r\n" - "Cache-Control: no-cache\r\n" - "Upgrade: websocket\r\n" - "Sec-WebSocket-Version: 13\r\n" - "Accept-Encoding: gzip, deflate, br\r\n" - "Sec-WebSocket-Key: 64b4B+s5JDlgkdg7NekJ+g==\r\n" - "Sec-WebSocket-Extensions: permessage-deflate\r\n" - "\r\n"; + static constexpr char CLIENT_WS_UPGRADE_REQ_BEFORE_KEY[] = "GET / HTTP/1.1\r\n" + "Connection: Upgrade\r\n" + "Pragma: no-cache\r\n" + "Cache-Control: no-cache\r\n" + "Upgrade: websocket\r\n" + "Sec-WebSocket-Version: 13\r\n" + "Accept-Encoding: gzip, deflate, br\r\n" + "Sec-WebSocket-Key: "; + + static constexpr char CLIENT_WS_UPGRADE_REQ_AFTER_KEY[] = "\r\nSec-WebSocket-Extensions: permessage-deflate\r\n" + "\r\n"; static constexpr int NET_SUCCESS = 1; static constexpr uint8_t MASK_KEY[] = {0xa, 0xb, 0xc, 0xd}; + std::string secWebSocketKey_; }; } // namespace OHOS::ArkCompiler::Toolchain diff --git a/websocket/define.h b/websocket/define.h index ba40e3c0..cecadb78 100644 --- a/websocket/define.h +++ b/websocket/define.h @@ -18,6 +18,7 @@ #include #include +#include #include #if defined(WINDOWS_PLATFORM) #include diff --git a/websocket/handshake_helper.cpp b/websocket/handshake_helper.cpp index 8bb1a23f..b8c843a1 100644 --- a/websocket/handshake_helper.cpp +++ b/websocket/handshake_helper.cpp @@ -17,6 +17,25 @@ #include "handshake_helper.h" namespace OHOS::ArkCompiler::Toolchain { +/* static */ +std::string WebSocketKeyEncoder::GenerateRandomSecWSKey() +{ + std::array bytes {}; + if (RAND_bytes(bytes.data(), bytes.size()) != 1) { + LOGE("RAND_bytes failed to generate secure random bytes"); + return ""; + } + + std::array encoded {}; + // base64-encoding is done via EVP_EncodeBlock, which writes a null-terminated string. + int encodedBytes = EVP_EncodeBlock(encoded.data(), bytes.data(), SEC_WEBSOCKET_KEY_BYTES_LEN); + if (encodedBytes != KEY_LENGTH) { + LOGE("EVP_EncodeBlock failed to encode Sec-WebSocket-Key bytes, encodedBytes = %{public}d", encodedBytes); + return ""; + } + return std::string(reinterpret_cast(encoded.data()), KEY_LENGTH); +} + /* static */ bool WebSocketKeyEncoder::EncodeKey(std::string_view key, unsigned char (&destination)[ENCODED_KEY_LEN + 1]) { diff --git a/websocket/handshake_helper.h b/websocket/handshake_helper.h index 89b9a12b..4c56ac4d 100644 --- a/websocket/handshake_helper.h +++ b/websocket/handshake_helper.h @@ -29,10 +29,12 @@ public: // WebSocket Globally Unique Identifier static constexpr std::string_view WEB_SOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; // The value of |Sec-WebSocket-Key| header field MUST be a nonce consisting of a randomly selected 16-byte value - static constexpr size_t KEY_LENGTH = GetBase64EncodingLength(16); + static constexpr size_t SEC_WEBSOCKET_KEY_BYTES_LEN = 16; + static constexpr size_t KEY_LENGTH = GetBase64EncodingLength(SEC_WEBSOCKET_KEY_BYTES_LEN); // SHA1 will write SHA_DIGEST_LENGTH == 20 bytes of output static constexpr size_t ENCODED_KEY_LEN = GetBase64EncodingLength(SHA_DIGEST_LENGTH); + static std::string GenerateRandomSecWSKey(); static bool EncodeKey(std::string_view key, unsigned char (&destination)[ENCODED_KEY_LEN + 1]); static bool EncodeKey(const unsigned char(&key)[KEY_LENGTH + 1], unsigned char (&destination)[ENCODED_KEY_LEN + 1]); -- Gitee