From 34778459b364cd940f9e2bbf7c1d43a6985e60a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20St=C3=B6ggl?= Date: Sat, 29 Apr 2017 06:57:53 +0200 Subject: [PATCH] Change end-of-line encoding of two files to Unix (LF) The line encoding of the following two files is CR+LF. In order to harmonize eol encoding with the other files, it is changed using: dos2unix Release/src/http/oauth/oauth1.cpp dos2unix Release/libs/websocketpp/websocketpp/sha1/sha1.hpp --- .../websocketpp/websocketpp/sha1/sha1.hpp | 378 ++++---- Release/src/http/oauth/oauth1.cpp | 844 +++++++++--------- 2 files changed, 611 insertions(+), 611 deletions(-) diff --git a/Release/libs/websocketpp/websocketpp/sha1/sha1.hpp b/Release/libs/websocketpp/websocketpp/sha1/sha1.hpp index 43a843382d..6b48d9578c 100644 --- a/Release/libs/websocketpp/websocketpp/sha1/sha1.hpp +++ b/Release/libs/websocketpp/websocketpp/sha1/sha1.hpp @@ -1,189 +1,189 @@ -/* -***** -sha1.hpp is a repackaging of the sha1.cpp and sha1.h files from the smallsha1 -library (http://code.google.com/p/smallsha1/) into a single header suitable for -use as a header only library. This conversion was done by Peter Thorson -(webmaster@zaphoyd.com) in 2013. All modifications to the code are redistributed -under the same license as the original, which is listed below. -***** - - Copyright (c) 2011, Micael Hildenborg - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Micael Hildenborg nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef SHA1_DEFINED -#define SHA1_DEFINED - -namespace websocketpp { -namespace sha1 { - -namespace { // local - -// Rotate an integer value to left. -inline unsigned int rol(unsigned int value, unsigned int steps) { - return ((value << steps) | (value >> (32 - steps))); -} - -// Sets the first 16 integers in the buffert to zero. -// Used for clearing the W buffert. -inline void clearWBuffert(unsigned int * buffert) -{ - for (int pos = 16; --pos >= 0;) - { - buffert[pos] = 0; - } -} - -inline void innerHash(unsigned int * result, unsigned int * w) -{ - unsigned int a = result[0]; - unsigned int b = result[1]; - unsigned int c = result[2]; - unsigned int d = result[3]; - unsigned int e = result[4]; - - int round = 0; - - #define sha1macro(func,val) \ - { \ - const unsigned int t = rol(a, 5) + (func) + e + val + w[round]; \ - e = d; \ - d = c; \ - c = rol(b, 30); \ - b = a; \ - a = t; \ - } - - while (round < 16) - { - sha1macro((b & c) | (~b & d), 0x5a827999) - ++round; - } - while (round < 20) - { - w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); - sha1macro((b & c) | (~b & d), 0x5a827999) - ++round; - } - while (round < 40) - { - w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); - sha1macro(b ^ c ^ d, 0x6ed9eba1) - ++round; - } - while (round < 60) - { - w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); - sha1macro((b & c) | (b & d) | (c & d), 0x8f1bbcdc) - ++round; - } - while (round < 80) - { - w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); - sha1macro(b ^ c ^ d, 0xca62c1d6) - ++round; - } - - #undef sha1macro - - result[0] += a; - result[1] += b; - result[2] += c; - result[3] += d; - result[4] += e; -} - -} // namespace - -/// Calculate a SHA1 hash -/** - * @param src points to any kind of data to be hashed. - * @param bytelength the number of bytes to hash from the src pointer. - * @param hash should point to a buffer of at least 20 bytes of size for storing - * the sha1 result in. - */ -inline void calc(void const * src, size_t bytelength, unsigned char * hash) { - // Init the result array. - unsigned int result[5] = { 0x67452301, 0xefcdab89, 0x98badcfe, - 0x10325476, 0xc3d2e1f0 }; - - // Cast the void src pointer to be the byte array we can work with. - unsigned char const * sarray = (unsigned char const *) src; - - // The reusable round buffer - unsigned int w[80]; - - // Loop through all complete 64byte blocks. - - size_t endCurrentBlock; - size_t currentBlock = 0; - - if (bytelength >= 64) { - size_t const endOfFullBlocks = bytelength - 64; - - while (currentBlock <= endOfFullBlocks) { - endCurrentBlock = currentBlock + 64; - - // Init the round buffer with the 64 byte block data. - for (int roundPos = 0; currentBlock < endCurrentBlock; currentBlock += 4) - { - // This line will swap endian on big endian and keep endian on - // little endian. - w[roundPos++] = (unsigned int) sarray[currentBlock + 3] - | (((unsigned int) sarray[currentBlock + 2]) << 8) - | (((unsigned int) sarray[currentBlock + 1]) << 16) - | (((unsigned int) sarray[currentBlock]) << 24); - } - innerHash(result, w); - } - } - - // Handle the last and not full 64 byte block if existing. - endCurrentBlock = bytelength - currentBlock; - clearWBuffert(w); - size_t lastBlockBytes = 0; - for (;lastBlockBytes < endCurrentBlock; ++lastBlockBytes) { - w[lastBlockBytes >> 2] |= (unsigned int) sarray[lastBlockBytes + currentBlock] << ((3 - (lastBlockBytes & 3)) << 3); - } - - w[lastBlockBytes >> 2] |= 0x80 << ((3 - (lastBlockBytes & 3)) << 3); - if (endCurrentBlock >= 56) { - innerHash(result, w); - clearWBuffert(w); - } - w[15] = bytelength << 3; - innerHash(result, w); - - // Store hash in result pointer, and make sure we get in in the correct - // order on both endian models. - for (int hashByte = 20; --hashByte >= 0;) { - hash[hashByte] = (result[hashByte >> 2] >> (((3 - hashByte) & 0x3) << 3)) & 0xff; - } -} - -} // namespace sha1 -} // namespace websocketpp - -#endif // SHA1_DEFINED +/* +***** +sha1.hpp is a repackaging of the sha1.cpp and sha1.h files from the smallsha1 +library (http://code.google.com/p/smallsha1/) into a single header suitable for +use as a header only library. This conversion was done by Peter Thorson +(webmaster@zaphoyd.com) in 2013. All modifications to the code are redistributed +under the same license as the original, which is listed below. +***** + + Copyright (c) 2011, Micael Hildenborg + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Micael Hildenborg nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SHA1_DEFINED +#define SHA1_DEFINED + +namespace websocketpp { +namespace sha1 { + +namespace { // local + +// Rotate an integer value to left. +inline unsigned int rol(unsigned int value, unsigned int steps) { + return ((value << steps) | (value >> (32 - steps))); +} + +// Sets the first 16 integers in the buffert to zero. +// Used for clearing the W buffert. +inline void clearWBuffert(unsigned int * buffert) +{ + for (int pos = 16; --pos >= 0;) + { + buffert[pos] = 0; + } +} + +inline void innerHash(unsigned int * result, unsigned int * w) +{ + unsigned int a = result[0]; + unsigned int b = result[1]; + unsigned int c = result[2]; + unsigned int d = result[3]; + unsigned int e = result[4]; + + int round = 0; + + #define sha1macro(func,val) \ + { \ + const unsigned int t = rol(a, 5) + (func) + e + val + w[round]; \ + e = d; \ + d = c; \ + c = rol(b, 30); \ + b = a; \ + a = t; \ + } + + while (round < 16) + { + sha1macro((b & c) | (~b & d), 0x5a827999) + ++round; + } + while (round < 20) + { + w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro((b & c) | (~b & d), 0x5a827999) + ++round; + } + while (round < 40) + { + w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro(b ^ c ^ d, 0x6ed9eba1) + ++round; + } + while (round < 60) + { + w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro((b & c) | (b & d) | (c & d), 0x8f1bbcdc) + ++round; + } + while (round < 80) + { + w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro(b ^ c ^ d, 0xca62c1d6) + ++round; + } + + #undef sha1macro + + result[0] += a; + result[1] += b; + result[2] += c; + result[3] += d; + result[4] += e; +} + +} // namespace + +/// Calculate a SHA1 hash +/** + * @param src points to any kind of data to be hashed. + * @param bytelength the number of bytes to hash from the src pointer. + * @param hash should point to a buffer of at least 20 bytes of size for storing + * the sha1 result in. + */ +inline void calc(void const * src, size_t bytelength, unsigned char * hash) { + // Init the result array. + unsigned int result[5] = { 0x67452301, 0xefcdab89, 0x98badcfe, + 0x10325476, 0xc3d2e1f0 }; + + // Cast the void src pointer to be the byte array we can work with. + unsigned char const * sarray = (unsigned char const *) src; + + // The reusable round buffer + unsigned int w[80]; + + // Loop through all complete 64byte blocks. + + size_t endCurrentBlock; + size_t currentBlock = 0; + + if (bytelength >= 64) { + size_t const endOfFullBlocks = bytelength - 64; + + while (currentBlock <= endOfFullBlocks) { + endCurrentBlock = currentBlock + 64; + + // Init the round buffer with the 64 byte block data. + for (int roundPos = 0; currentBlock < endCurrentBlock; currentBlock += 4) + { + // This line will swap endian on big endian and keep endian on + // little endian. + w[roundPos++] = (unsigned int) sarray[currentBlock + 3] + | (((unsigned int) sarray[currentBlock + 2]) << 8) + | (((unsigned int) sarray[currentBlock + 1]) << 16) + | (((unsigned int) sarray[currentBlock]) << 24); + } + innerHash(result, w); + } + } + + // Handle the last and not full 64 byte block if existing. + endCurrentBlock = bytelength - currentBlock; + clearWBuffert(w); + size_t lastBlockBytes = 0; + for (;lastBlockBytes < endCurrentBlock; ++lastBlockBytes) { + w[lastBlockBytes >> 2] |= (unsigned int) sarray[lastBlockBytes + currentBlock] << ((3 - (lastBlockBytes & 3)) << 3); + } + + w[lastBlockBytes >> 2] |= 0x80 << ((3 - (lastBlockBytes & 3)) << 3); + if (endCurrentBlock >= 56) { + innerHash(result, w); + clearWBuffert(w); + } + w[15] = bytelength << 3; + innerHash(result, w); + + // Store hash in result pointer, and make sure we get in in the correct + // order on both endian models. + for (int hashByte = 20; --hashByte >= 0;) { + hash[hashByte] = (result[hashByte >> 2] >> (((3 - hashByte) & 0x3) << 3)) & 0xff; + } +} + +} // namespace sha1 +} // namespace websocketpp + +#endif // SHA1_DEFINED diff --git a/Release/src/http/oauth/oauth1.cpp b/Release/src/http/oauth/oauth1.cpp index 6f77462043..37c71deb22 100644 --- a/Release/src/http/oauth/oauth1.cpp +++ b/Release/src/http/oauth/oauth1.cpp @@ -1,422 +1,422 @@ -/*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* HTTP Library: Oauth 1.0 -* -* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ - -#include "stdafx.h" - -#if !defined(CPPREST_TARGET_XP) - -using namespace utility; -using web::http::client::http_client; -using web::http::client::http_client_config; -using web::http::oauth1::details::oauth1_state; -using web::http::oauth1::details::oauth1_strings; - -namespace web { namespace http { namespace oauth1 -{ - -namespace details -{ - -#define _OAUTH1_STRINGS -#define DAT(a_, b_) const oauth1_string oauth1_strings::a_(_XPLATSTR(b_)); -#include "cpprest/details/http_constants.dat" -#undef _OAUTH1_STRINGS -#undef DAT - -} // namespace web::http::oauth1::details - -namespace experimental -{ - -// -// Start of platform-dependent _hmac_sha1() block... -// -#if defined(_WIN32) && !defined(__cplusplus_winrt) // Windows desktop - -#include -#include - -// Code analysis complains even though there is no bug. -#pragma warning(push) -#pragma warning(disable : 6102) -std::vector oauth1_config::_hmac_sha1(const utility::string_t& key, const utility::string_t& data) -{ - NTSTATUS status; - BCRYPT_ALG_HANDLE alg_handle = nullptr; - BCRYPT_HASH_HANDLE hash_handle = nullptr; - - std::vector hash; - DWORD hash_len = 0; - ULONG result_len = 0; - - const auto &key_c = conversions::utf16_to_utf8(key); - const auto &data_c = conversions::utf16_to_utf8(data); - - status = BCryptOpenAlgorithmProvider(&alg_handle, BCRYPT_SHA1_ALGORITHM, nullptr, BCRYPT_ALG_HANDLE_HMAC_FLAG); - if (!NT_SUCCESS(status)) - { - goto cleanup; - } - status = BCryptGetProperty(alg_handle, BCRYPT_HASH_LENGTH, (PBYTE) &hash_len, sizeof(hash_len), &result_len, 0); - if (!NT_SUCCESS(status)) - { - goto cleanup; - } - hash.resize(hash_len); - - status = BCryptCreateHash(alg_handle, &hash_handle, nullptr, 0, (PBYTE) key_c.c_str(), (ULONG) key_c.length(), 0); - if (!NT_SUCCESS(status)) - { - goto cleanup; - } - status = BCryptHashData(hash_handle, (PBYTE) data_c.c_str(), (ULONG) data_c.length(), 0); - if (!NT_SUCCESS(status)) - { - goto cleanup; - } - status = BCryptFinishHash(hash_handle, hash.data(), hash_len, 0); - if (!NT_SUCCESS(status)) - { - goto cleanup; - } - -cleanup: - if (hash_handle) - { - BCryptDestroyHash(hash_handle); - } - if (alg_handle) - { - BCryptCloseAlgorithmProvider(alg_handle, 0); - } - - return hash; -} -#pragma warning(pop) - -#elif defined(_WIN32) && defined(__cplusplus_winrt) // Windows RT - -using namespace Windows::Security::Cryptography; -using namespace Windows::Security::Cryptography::Core; -using namespace Windows::Storage::Streams; - -std::vector oauth1_config::_hmac_sha1(const utility::string_t& key, const utility::string_t& data) -{ - Platform::String^ data_str = ref new Platform::String(data.c_str()); - Platform::String^ key_str = ref new Platform::String(key.c_str()); - - MacAlgorithmProvider^ HMACSha1Provider = MacAlgorithmProvider::OpenAlgorithm(MacAlgorithmNames::HmacSha1); - IBuffer^ content_buffer = CryptographicBuffer::ConvertStringToBinary(data_str, BinaryStringEncoding::Utf8); - IBuffer^ key_buffer = CryptographicBuffer::ConvertStringToBinary(key_str, BinaryStringEncoding::Utf8); - - auto signature_key = HMACSha1Provider->CreateKey(key_buffer); - auto signed_buffer = CryptographicEngine::Sign(signature_key, content_buffer); - - Platform::Array^ arr; - CryptographicBuffer::CopyToByteArray(signed_buffer, &arr); - return std::vector(arr->Data, arr->Data + arr->Length); -} - -#else // Linux, Mac OS X - -#include - -std::vector oauth1_config::_hmac_sha1(const utility::string_t& key, const utility::string_t& data) -{ - unsigned char digest[HMAC_MAX_MD_CBLOCK]; - unsigned int digest_len = 0; - - HMAC(EVP_sha1(), key.c_str(), static_cast(key.length()), - (const unsigned char*) data.c_str(), data.length(), - digest, &digest_len); - - return std::vector(digest, digest + digest_len); -} - -#endif -// -// ...End of platform-dependent _hmac_sha1() block. -// - -// Notes: -// - Doesn't support URIs without scheme or host. -// - If URI port is unspecified. -utility::string_t oauth1_config::_build_base_string_uri(const uri& u) -{ - utility::ostringstream_t os; - os.imbue(std::locale::classic()); - os << u.scheme() << "://" << u.host(); - if (!u.is_port_default() && u.port() != 80 && u.port() != 443) - { - os << ":" << u.port(); - } - os << u.path(); - return uri::encode_data_string(os.str()); -} - -utility::string_t oauth1_config::_build_normalized_parameters(web::http::uri u, const oauth1_state& state) const -{ - // While map sorts items by keys it doesn't take value into account. - // We need to sort the query parameters separately. - std::map queries_map = http::uri::split_query(std::move(u).query()); - std::vector queries; - for (const auto& query : queries_map) - { - utility::ostringstream_t os; - os.imbue(std::locale::classic()); - os << query.first << "=" << query.second; - queries.push_back(os.str()); - } - - for (const auto& query : parameters()) - { - utility::ostringstream_t os; - os.imbue(std::locale::classic()); - os << query.first << "=" << query.second; - queries.push_back(os.str()); - } - - // Push oauth1 parameters. - queries.push_back(oauth1_strings::version + U("=1.0")); - queries.push_back(oauth1_strings::consumer_key + U("=") + web::uri::encode_data_string(consumer_key())); - if (!m_token.access_token().empty()) - { - queries.push_back(oauth1_strings::token + U("=") + web::uri::encode_data_string(m_token.access_token())); - } - queries.push_back(oauth1_strings::signature_method + U("=") + method()); - queries.push_back(oauth1_strings::timestamp + U("=") + state.timestamp()); - queries.push_back(oauth1_strings::nonce + U("=") + state.nonce()); - if (!state.extra_key().empty()) - { - queries.push_back(state.extra_key() + U("=") + web::uri::encode_data_string(state.extra_value())); - } - - // Sort parameters and build the string. - sort(queries.begin(), queries.end()); - utility::ostringstream_t os; - os.imbue(std::locale::classic()); - for (auto i = queries.begin(); i != queries.end() - 1; ++i) - { - os << *i << U("&"); - } - os << queries.back(); - return uri::encode_data_string(os.str()); -} - -static bool is_application_x_www_form_urlencoded (http_request &request) -{ - const auto content_type(request.headers()[header_names::content_type]); - return 0 == content_type.find(web::http::details::mime_types::application_x_www_form_urlencoded); -} - -utility::string_t oauth1_config::_build_signature_base_string(http_request request, oauth1_state state) const -{ - uri u(request.absolute_uri()); - utility::ostringstream_t os; - os.imbue(std::locale::classic()); - os << request.method(); - os << "&" << _build_base_string_uri(u); - - // http://oauth.net/core/1.0a/#signing_process - // 9.1.1. Normalize Request Parameters - // The request parameters are collected, sorted and concatenated into a normalized string: - // - Parameters in the OAuth HTTP Authorization header excluding the realm parameter. - // - Parameters in the HTTP POST request body (with a content-type of application/x-www-form-urlencoded). - // - HTTP GET parameters added to the URLs in the query part (as defined by [RFC3986] section 3). - if (is_application_x_www_form_urlencoded(request)) - { - // Note: this should be improved to not block and handle any potential exceptions. - utility::string_t str = request.extract_string(true).get(); - request.set_body(str, web::http::details::mime_types::application_x_www_form_urlencoded); - uri v = http::uri_builder(request.absolute_uri()).append_query(std::move(str), false).to_uri(); - os << "&" << _build_normalized_parameters(std::move(v), std::move(state)); - } - else - { - os << "&" << _build_normalized_parameters(std::move(u), std::move(state)); - } - return os.str(); -} - -utility::string_t oauth1_config::_build_signature(http_request request, oauth1_state state) const -{ - if (oauth1_methods::hmac_sha1 == method()) - { - return _build_hmac_sha1_signature(std::move(request), std::move(state)); - } - else if (oauth1_methods::plaintext == method()) - { - return _build_plaintext_signature(); - } - throw oauth1_exception(U("invalid signature method.")); // Should never happen. -} - -pplx::task oauth1_config::_request_token(oauth1_state state, bool is_temp_token_request) -{ - utility::string_t endpoint = is_temp_token_request ? temp_endpoint() : token_endpoint(); - http_request req; - req.set_method(methods::POST); - req.set_request_uri(utility::string_t()); - req._set_base_uri(endpoint); - - _authenticate_request(req, std::move(state)); - - // configure proxy - http_client_config config; - config.set_proxy(m_proxy); - - http_client client(endpoint, config); - - return client.request(req) - .then([](http_response resp) - { - return resp.extract_string(); - }) - .then([this, is_temp_token_request](utility::string_t body) -> void - { - auto query(uri::split_query(body)); - - if (is_temp_token_request) - { - auto callback_confirmed_param = query.find(oauth1_strings::callback_confirmed); - if (callback_confirmed_param == query.end()) - { - throw oauth1_exception(U("parameter 'oauth_callback_confirmed' is missing from response: ") + body - + U(". the service may be using obsoleted and insecure OAuth Core 1.0 protocol.")); - } - } - - auto token_param = query.find(oauth1_strings::token); - if (token_param == query.end()) - { - throw oauth1_exception(U("parameter 'oauth_token' missing from response: ") + body); - } - - auto token_secret_param = query.find(oauth1_strings::token_secret); - if (token_secret_param == query.end()) - { - throw oauth1_exception(U("parameter 'oauth_token_secret' missing from response: ") + body); - } - - // Here the token can be either temporary or access token. - // The authorization is complete if it is access token. - m_is_authorization_completed = !is_temp_token_request; - m_token = oauth1_token(web::uri::decode(token_param->second), web::uri::decode(token_secret_param->second)); - - for (const auto& qa : query) - { - if (qa.first == oauth1_strings::token || qa.first == oauth1_strings::token_secret) continue ; - m_token.set_additional_parameter(web::uri::decode(qa.first), web::uri::decode(qa.second)); - } - }); -} - -void oauth1_config::_authenticate_request(http_request &request, oauth1_state state) -{ - utility::ostringstream_t os; - os.imbue(std::locale::classic()); - os << "OAuth "; - if (!realm().empty()) - { - os << oauth1_strings::realm << "=\"" << web::uri::encode_data_string (realm()) << "\", "; - } - os << oauth1_strings::version << "=\"1.0"; - os << "\", " << oauth1_strings::consumer_key << "=\"" << web::uri::encode_data_string (consumer_key()); - if (!m_token.access_token().empty()) - { - os << "\", " << oauth1_strings::token << "=\"" << web::uri::encode_data_string(m_token.access_token()); - } - os << "\", " << oauth1_strings::signature_method << "=\"" << method(); - os << "\", " << oauth1_strings::timestamp << "=\"" << state.timestamp(); - os << "\", " << oauth1_strings::nonce << "=\"" << state.nonce(); - os << "\", " << oauth1_strings::signature << "=\"" << uri::encode_data_string(_build_signature(request, state)); - os << "\""; - - if (!state.extra_key().empty()) - { - os << ", " << state.extra_key() << "=\"" << web::uri::encode_data_string(state.extra_value()) << "\""; - } - - request.headers().add(header_names::authorization, os.str()); -} - -pplx::task oauth1_config::build_authorization_uri() -{ - pplx::task temp_token_req = _request_token(_generate_auth_state(oauth1_strings::callback, callback_uri()), true); - - return temp_token_req.then([this] - { - uri_builder ub(auth_endpoint()); - ub.append_query(oauth1_strings::token, m_token.access_token()); - return ub.to_string(); - }); -} - -pplx::task oauth1_config::token_from_redirected_uri(const web::http::uri& redirected_uri) -{ - auto query = uri::split_query(redirected_uri.query()); - - auto token_param = query.find(oauth1_strings::token); - if (token_param == query.end()) - { - return pplx::task_from_exception(oauth1_exception(U("parameter 'oauth_token' missing from redirected URI."))); - } - if (m_token.access_token() != token_param->second) - { - utility::ostringstream_t err; - err.imbue(std::locale::classic()); - err << U("redirected URI parameter 'oauth_token'='") << token_param->second - << U("' does not match temporary token='") << m_token.access_token() << U("'."); - return pplx::task_from_exception(oauth1_exception(err.str().c_str())); - } - - auto verifier_param = query.find(oauth1_strings::verifier); - if (verifier_param == query.end()) - { - return pplx::task_from_exception(oauth1_exception(U("parameter 'oauth_verifier' missing from redirected URI."))); - } - - return token_from_verifier(verifier_param->second); -} - -// Remove once VS 2013 is no longer supported. -#if defined(_WIN32) && _MSC_VER < 1900 -static const oauth1_token empty_token; -#endif -const oauth1_token& oauth1_config::token() const -{ - if (m_is_authorization_completed) - { - // Return the token object only if authorization has been completed. - // Otherwise the token object holds a temporary token which should not be - // returned to the user. - return m_token; - } - else - { -#if !defined(_WIN32) || _MSC_VER >= 1900 - static const oauth1_token empty_token; -#endif - return empty_token; - } -} - -#define _OAUTH1_METHODS -#define DAT(a,b) const oauth1_method oauth1_methods::a = b; -#include "cpprest/details/http_constants.dat" -#undef _OAUTH1_METHODS -#undef DAT - -}}}} - -#endif +/*** +* Copyright (C) Microsoft. All rights reserved. +* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +* +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* HTTP Library: Oauth 1.0 +* +* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#include "stdafx.h" + +#if !defined(CPPREST_TARGET_XP) + +using namespace utility; +using web::http::client::http_client; +using web::http::client::http_client_config; +using web::http::oauth1::details::oauth1_state; +using web::http::oauth1::details::oauth1_strings; + +namespace web { namespace http { namespace oauth1 +{ + +namespace details +{ + +#define _OAUTH1_STRINGS +#define DAT(a_, b_) const oauth1_string oauth1_strings::a_(_XPLATSTR(b_)); +#include "cpprest/details/http_constants.dat" +#undef _OAUTH1_STRINGS +#undef DAT + +} // namespace web::http::oauth1::details + +namespace experimental +{ + +// +// Start of platform-dependent _hmac_sha1() block... +// +#if defined(_WIN32) && !defined(__cplusplus_winrt) // Windows desktop + +#include +#include + +// Code analysis complains even though there is no bug. +#pragma warning(push) +#pragma warning(disable : 6102) +std::vector oauth1_config::_hmac_sha1(const utility::string_t& key, const utility::string_t& data) +{ + NTSTATUS status; + BCRYPT_ALG_HANDLE alg_handle = nullptr; + BCRYPT_HASH_HANDLE hash_handle = nullptr; + + std::vector hash; + DWORD hash_len = 0; + ULONG result_len = 0; + + const auto &key_c = conversions::utf16_to_utf8(key); + const auto &data_c = conversions::utf16_to_utf8(data); + + status = BCryptOpenAlgorithmProvider(&alg_handle, BCRYPT_SHA1_ALGORITHM, nullptr, BCRYPT_ALG_HANDLE_HMAC_FLAG); + if (!NT_SUCCESS(status)) + { + goto cleanup; + } + status = BCryptGetProperty(alg_handle, BCRYPT_HASH_LENGTH, (PBYTE) &hash_len, sizeof(hash_len), &result_len, 0); + if (!NT_SUCCESS(status)) + { + goto cleanup; + } + hash.resize(hash_len); + + status = BCryptCreateHash(alg_handle, &hash_handle, nullptr, 0, (PBYTE) key_c.c_str(), (ULONG) key_c.length(), 0); + if (!NT_SUCCESS(status)) + { + goto cleanup; + } + status = BCryptHashData(hash_handle, (PBYTE) data_c.c_str(), (ULONG) data_c.length(), 0); + if (!NT_SUCCESS(status)) + { + goto cleanup; + } + status = BCryptFinishHash(hash_handle, hash.data(), hash_len, 0); + if (!NT_SUCCESS(status)) + { + goto cleanup; + } + +cleanup: + if (hash_handle) + { + BCryptDestroyHash(hash_handle); + } + if (alg_handle) + { + BCryptCloseAlgorithmProvider(alg_handle, 0); + } + + return hash; +} +#pragma warning(pop) + +#elif defined(_WIN32) && defined(__cplusplus_winrt) // Windows RT + +using namespace Windows::Security::Cryptography; +using namespace Windows::Security::Cryptography::Core; +using namespace Windows::Storage::Streams; + +std::vector oauth1_config::_hmac_sha1(const utility::string_t& key, const utility::string_t& data) +{ + Platform::String^ data_str = ref new Platform::String(data.c_str()); + Platform::String^ key_str = ref new Platform::String(key.c_str()); + + MacAlgorithmProvider^ HMACSha1Provider = MacAlgorithmProvider::OpenAlgorithm(MacAlgorithmNames::HmacSha1); + IBuffer^ content_buffer = CryptographicBuffer::ConvertStringToBinary(data_str, BinaryStringEncoding::Utf8); + IBuffer^ key_buffer = CryptographicBuffer::ConvertStringToBinary(key_str, BinaryStringEncoding::Utf8); + + auto signature_key = HMACSha1Provider->CreateKey(key_buffer); + auto signed_buffer = CryptographicEngine::Sign(signature_key, content_buffer); + + Platform::Array^ arr; + CryptographicBuffer::CopyToByteArray(signed_buffer, &arr); + return std::vector(arr->Data, arr->Data + arr->Length); +} + +#else // Linux, Mac OS X + +#include + +std::vector oauth1_config::_hmac_sha1(const utility::string_t& key, const utility::string_t& data) +{ + unsigned char digest[HMAC_MAX_MD_CBLOCK]; + unsigned int digest_len = 0; + + HMAC(EVP_sha1(), key.c_str(), static_cast(key.length()), + (const unsigned char*) data.c_str(), data.length(), + digest, &digest_len); + + return std::vector(digest, digest + digest_len); +} + +#endif +// +// ...End of platform-dependent _hmac_sha1() block. +// + +// Notes: +// - Doesn't support URIs without scheme or host. +// - If URI port is unspecified. +utility::string_t oauth1_config::_build_base_string_uri(const uri& u) +{ + utility::ostringstream_t os; + os.imbue(std::locale::classic()); + os << u.scheme() << "://" << u.host(); + if (!u.is_port_default() && u.port() != 80 && u.port() != 443) + { + os << ":" << u.port(); + } + os << u.path(); + return uri::encode_data_string(os.str()); +} + +utility::string_t oauth1_config::_build_normalized_parameters(web::http::uri u, const oauth1_state& state) const +{ + // While map sorts items by keys it doesn't take value into account. + // We need to sort the query parameters separately. + std::map queries_map = http::uri::split_query(std::move(u).query()); + std::vector queries; + for (const auto& query : queries_map) + { + utility::ostringstream_t os; + os.imbue(std::locale::classic()); + os << query.first << "=" << query.second; + queries.push_back(os.str()); + } + + for (const auto& query : parameters()) + { + utility::ostringstream_t os; + os.imbue(std::locale::classic()); + os << query.first << "=" << query.second; + queries.push_back(os.str()); + } + + // Push oauth1 parameters. + queries.push_back(oauth1_strings::version + U("=1.0")); + queries.push_back(oauth1_strings::consumer_key + U("=") + web::uri::encode_data_string(consumer_key())); + if (!m_token.access_token().empty()) + { + queries.push_back(oauth1_strings::token + U("=") + web::uri::encode_data_string(m_token.access_token())); + } + queries.push_back(oauth1_strings::signature_method + U("=") + method()); + queries.push_back(oauth1_strings::timestamp + U("=") + state.timestamp()); + queries.push_back(oauth1_strings::nonce + U("=") + state.nonce()); + if (!state.extra_key().empty()) + { + queries.push_back(state.extra_key() + U("=") + web::uri::encode_data_string(state.extra_value())); + } + + // Sort parameters and build the string. + sort(queries.begin(), queries.end()); + utility::ostringstream_t os; + os.imbue(std::locale::classic()); + for (auto i = queries.begin(); i != queries.end() - 1; ++i) + { + os << *i << U("&"); + } + os << queries.back(); + return uri::encode_data_string(os.str()); +} + +static bool is_application_x_www_form_urlencoded (http_request &request) +{ + const auto content_type(request.headers()[header_names::content_type]); + return 0 == content_type.find(web::http::details::mime_types::application_x_www_form_urlencoded); +} + +utility::string_t oauth1_config::_build_signature_base_string(http_request request, oauth1_state state) const +{ + uri u(request.absolute_uri()); + utility::ostringstream_t os; + os.imbue(std::locale::classic()); + os << request.method(); + os << "&" << _build_base_string_uri(u); + + // http://oauth.net/core/1.0a/#signing_process + // 9.1.1. Normalize Request Parameters + // The request parameters are collected, sorted and concatenated into a normalized string: + // - Parameters in the OAuth HTTP Authorization header excluding the realm parameter. + // - Parameters in the HTTP POST request body (with a content-type of application/x-www-form-urlencoded). + // - HTTP GET parameters added to the URLs in the query part (as defined by [RFC3986] section 3). + if (is_application_x_www_form_urlencoded(request)) + { + // Note: this should be improved to not block and handle any potential exceptions. + utility::string_t str = request.extract_string(true).get(); + request.set_body(str, web::http::details::mime_types::application_x_www_form_urlencoded); + uri v = http::uri_builder(request.absolute_uri()).append_query(std::move(str), false).to_uri(); + os << "&" << _build_normalized_parameters(std::move(v), std::move(state)); + } + else + { + os << "&" << _build_normalized_parameters(std::move(u), std::move(state)); + } + return os.str(); +} + +utility::string_t oauth1_config::_build_signature(http_request request, oauth1_state state) const +{ + if (oauth1_methods::hmac_sha1 == method()) + { + return _build_hmac_sha1_signature(std::move(request), std::move(state)); + } + else if (oauth1_methods::plaintext == method()) + { + return _build_plaintext_signature(); + } + throw oauth1_exception(U("invalid signature method.")); // Should never happen. +} + +pplx::task oauth1_config::_request_token(oauth1_state state, bool is_temp_token_request) +{ + utility::string_t endpoint = is_temp_token_request ? temp_endpoint() : token_endpoint(); + http_request req; + req.set_method(methods::POST); + req.set_request_uri(utility::string_t()); + req._set_base_uri(endpoint); + + _authenticate_request(req, std::move(state)); + + // configure proxy + http_client_config config; + config.set_proxy(m_proxy); + + http_client client(endpoint, config); + + return client.request(req) + .then([](http_response resp) + { + return resp.extract_string(); + }) + .then([this, is_temp_token_request](utility::string_t body) -> void + { + auto query(uri::split_query(body)); + + if (is_temp_token_request) + { + auto callback_confirmed_param = query.find(oauth1_strings::callback_confirmed); + if (callback_confirmed_param == query.end()) + { + throw oauth1_exception(U("parameter 'oauth_callback_confirmed' is missing from response: ") + body + + U(". the service may be using obsoleted and insecure OAuth Core 1.0 protocol.")); + } + } + + auto token_param = query.find(oauth1_strings::token); + if (token_param == query.end()) + { + throw oauth1_exception(U("parameter 'oauth_token' missing from response: ") + body); + } + + auto token_secret_param = query.find(oauth1_strings::token_secret); + if (token_secret_param == query.end()) + { + throw oauth1_exception(U("parameter 'oauth_token_secret' missing from response: ") + body); + } + + // Here the token can be either temporary or access token. + // The authorization is complete if it is access token. + m_is_authorization_completed = !is_temp_token_request; + m_token = oauth1_token(web::uri::decode(token_param->second), web::uri::decode(token_secret_param->second)); + + for (const auto& qa : query) + { + if (qa.first == oauth1_strings::token || qa.first == oauth1_strings::token_secret) continue ; + m_token.set_additional_parameter(web::uri::decode(qa.first), web::uri::decode(qa.second)); + } + }); +} + +void oauth1_config::_authenticate_request(http_request &request, oauth1_state state) +{ + utility::ostringstream_t os; + os.imbue(std::locale::classic()); + os << "OAuth "; + if (!realm().empty()) + { + os << oauth1_strings::realm << "=\"" << web::uri::encode_data_string (realm()) << "\", "; + } + os << oauth1_strings::version << "=\"1.0"; + os << "\", " << oauth1_strings::consumer_key << "=\"" << web::uri::encode_data_string (consumer_key()); + if (!m_token.access_token().empty()) + { + os << "\", " << oauth1_strings::token << "=\"" << web::uri::encode_data_string(m_token.access_token()); + } + os << "\", " << oauth1_strings::signature_method << "=\"" << method(); + os << "\", " << oauth1_strings::timestamp << "=\"" << state.timestamp(); + os << "\", " << oauth1_strings::nonce << "=\"" << state.nonce(); + os << "\", " << oauth1_strings::signature << "=\"" << uri::encode_data_string(_build_signature(request, state)); + os << "\""; + + if (!state.extra_key().empty()) + { + os << ", " << state.extra_key() << "=\"" << web::uri::encode_data_string(state.extra_value()) << "\""; + } + + request.headers().add(header_names::authorization, os.str()); +} + +pplx::task oauth1_config::build_authorization_uri() +{ + pplx::task temp_token_req = _request_token(_generate_auth_state(oauth1_strings::callback, callback_uri()), true); + + return temp_token_req.then([this] + { + uri_builder ub(auth_endpoint()); + ub.append_query(oauth1_strings::token, m_token.access_token()); + return ub.to_string(); + }); +} + +pplx::task oauth1_config::token_from_redirected_uri(const web::http::uri& redirected_uri) +{ + auto query = uri::split_query(redirected_uri.query()); + + auto token_param = query.find(oauth1_strings::token); + if (token_param == query.end()) + { + return pplx::task_from_exception(oauth1_exception(U("parameter 'oauth_token' missing from redirected URI."))); + } + if (m_token.access_token() != token_param->second) + { + utility::ostringstream_t err; + err.imbue(std::locale::classic()); + err << U("redirected URI parameter 'oauth_token'='") << token_param->second + << U("' does not match temporary token='") << m_token.access_token() << U("'."); + return pplx::task_from_exception(oauth1_exception(err.str().c_str())); + } + + auto verifier_param = query.find(oauth1_strings::verifier); + if (verifier_param == query.end()) + { + return pplx::task_from_exception(oauth1_exception(U("parameter 'oauth_verifier' missing from redirected URI."))); + } + + return token_from_verifier(verifier_param->second); +} + +// Remove once VS 2013 is no longer supported. +#if defined(_WIN32) && _MSC_VER < 1900 +static const oauth1_token empty_token; +#endif +const oauth1_token& oauth1_config::token() const +{ + if (m_is_authorization_completed) + { + // Return the token object only if authorization has been completed. + // Otherwise the token object holds a temporary token which should not be + // returned to the user. + return m_token; + } + else + { +#if !defined(_WIN32) || _MSC_VER >= 1900 + static const oauth1_token empty_token; +#endif + return empty_token; + } +} + +#define _OAUTH1_METHODS +#define DAT(a,b) const oauth1_method oauth1_methods::a = b; +#include "cpprest/details/http_constants.dat" +#undef _OAUTH1_METHODS +#undef DAT + +}}}} + +#endif