Skip to content

Commit 472c54f

Browse files
author
pfeatherstone
committed
use custom sha1 implementation. Added unit tests
1 parent 5be383f commit 472c54f

File tree

7 files changed

+7365
-13
lines changed

7 files changed

+7365
-13
lines changed

src/http.cpp

Lines changed: 156 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include <cassert>
12
#include <cstring>
23
#include <algorithm>
34
#include <filesystem>
@@ -13,7 +14,7 @@ namespace fs = std::filesystem;
1314
namespace http
1415
{
1516

16-
//---------------------------------------------------push_back-------------------------------------------------------------
17+
//----------------------------------------------------------------------------------------------------------------
1718

1819
const auto BOOST_ASIO_VERSION_STRING = []() -> std::string
1920
{
@@ -585,6 +586,160 @@ namespace http
585586
return "application/text";
586587
}
587588

589+
//----------------------------------------------------------------------------------------------------------------
590+
591+
592+
static constexpr uint32_t rotl(uint32_t x, size_t s)
593+
{
594+
return (x << s) | (x >> (32 - s));
595+
}
596+
597+
static constexpr uint32_t f1(uint32_t b, uint32_t c, uint32_t d)
598+
{
599+
return d ^ (b & (c ^ d)); // original: f = (b & c) | ((~b) & d);
600+
}
601+
602+
static constexpr uint32_t f2(uint32_t b, uint32_t c, uint32_t d)
603+
{
604+
return b ^ c ^ d;
605+
}
606+
607+
static constexpr uint32_t f3(uint32_t b, uint32_t c, uint32_t d)
608+
{
609+
return (b & c) | (b & d) | (c & d);
610+
}
611+
612+
const auto process_sha1_block = [](auto& hash, const auto& block, auto& words)
613+
{
614+
// Initialise buffer
615+
for (size_t i = 0 ; i < 16 ; ++i)
616+
{
617+
std::memcpy(&words[i], &block[i*4], 4);
618+
words[i] = htobe32(words[i]);
619+
}
620+
621+
for (size_t i = 16; i < 80; ++i)
622+
words[i] = rotl(words[i-3] ^ words[i-8] ^ words[i-14] ^ words[i-16], 1);
623+
624+
// Initialize
625+
uint32_t a = hash[0];
626+
uint32_t b = hash[1];
627+
uint32_t c = hash[2];
628+
uint32_t d = hash[3];
629+
uint32_t e = hash[4];
630+
631+
// first round
632+
for (size_t i = 0; i < 4; ++i)
633+
{
634+
const size_t offset = 5*i;
635+
e += rotl(a,5) + f1(b,c,d) + words[offset ] + 0x5a827999; b = rotl(b,30);
636+
d += rotl(e,5) + f1(a,b,c) + words[offset+1] + 0x5a827999; a = rotl(a,30);
637+
c += rotl(d,5) + f1(e,a,b) + words[offset+2] + 0x5a827999; e = rotl(e,30);
638+
b += rotl(c,5) + f1(d,e,a) + words[offset+3] + 0x5a827999; d = rotl(d,30);
639+
a += rotl(b,5) + f1(c,d,e) + words[offset+4] + 0x5a827999; c = rotl(c,30);
640+
}
641+
642+
// second round
643+
for (size_t i = 4; i < 8; ++i)
644+
{
645+
const size_t offset = 5*i;
646+
e += rotl(a,5) + f2(b,c,d) + words[offset ] + 0x6ed9eba1; b = rotl(b,30);
647+
d += rotl(e,5) + f2(a,b,c) + words[offset+1] + 0x6ed9eba1; a = rotl(a,30);
648+
c += rotl(d,5) + f2(e,a,b) + words[offset+2] + 0x6ed9eba1; e = rotl(e,30);
649+
b += rotl(c,5) + f2(d,e,a) + words[offset+3] + 0x6ed9eba1; d = rotl(d,30);
650+
a += rotl(b,5) + f2(c,d,e) + words[offset+4] + 0x6ed9eba1; c = rotl(c,30);
651+
}
652+
653+
// third round
654+
for (size_t i = 8; i < 12; ++i)
655+
{
656+
const size_t offset = 5*i;
657+
e += rotl(a,5) + f3(b,c,d) + words[offset ] + 0x8f1bbcdc; b = rotl(b,30);
658+
d += rotl(e,5) + f3(a,b,c) + words[offset+1] + 0x8f1bbcdc; a = rotl(a,30);
659+
c += rotl(d,5) + f3(e,a,b) + words[offset+2] + 0x8f1bbcdc; e = rotl(e,30);
660+
b += rotl(c,5) + f3(d,e,a) + words[offset+3] + 0x8f1bbcdc; d = rotl(d,30);
661+
a += rotl(b,5) + f3(c,d,e) + words[offset+4] + 0x8f1bbcdc; c = rotl(c,30);
662+
}
663+
664+
// fourth round
665+
for (size_t i = 12; i < 16; ++i)
666+
{
667+
const size_t offset = 5*i;
668+
e += rotl(a,5) + f2(b,c,d) + words[offset ] + 0xca62c1d6; b = rotl(b,30);
669+
d += rotl(e,5) + f2(a,b,c) + words[offset+1] + 0xca62c1d6; a = rotl(a,30);
670+
c += rotl(d,5) + f2(e,a,b) + words[offset+2] + 0xca62c1d6; e = rotl(e,30);
671+
b += rotl(c,5) + f2(d,e,a) + words[offset+3] + 0xca62c1d6; d = rotl(d,30);
672+
a += rotl(b,5) + f2(c,d,e) + words[offset+4] + 0xca62c1d6; c = rotl(c,30);
673+
}
674+
675+
// update hash
676+
hash[0] += a;
677+
hash[1] += b;
678+
hash[2] += c;
679+
hash[3] += d;
680+
hash[4] += e;
681+
};
682+
683+
sha1& sha1::push(size_t ndata, const uint8_t* data)
684+
{
685+
for (size_t i = 0 ; i < ndata ; ++i)
686+
{
687+
block[off] = data[i];
688+
++off;
689+
++total;
690+
691+
if (off == std::size(block))
692+
{
693+
off = 0;
694+
process_sha1_block(hash, block, words);
695+
}
696+
}
697+
698+
return *this;
699+
}
700+
701+
sha1::digest sha1::finish()
702+
{
703+
// number of bits
704+
const uint64_t ml = htobe64(total*8);
705+
706+
// Add 0x80
707+
block[off++] = 0x80;
708+
if (off == std::size(block))
709+
{
710+
off = 0;
711+
process_sha1_block(hash, block, words);
712+
}
713+
714+
// Add remaining 0 bits
715+
const size_t end = off <= 56 ? 56 : 56 + 64;
716+
for (size_t i = off ; i < end ; ++i)
717+
{
718+
block[off++] = 0;
719+
if (off == std::size(block))
720+
{
721+
off = 0;
722+
process_sha1_block(hash, block, words);
723+
}
724+
}
725+
assert(off == 56);
726+
727+
// Add message length
728+
uint8_t tmp[8];
729+
memcpy(tmp, &ml, 8);
730+
for (size_t i = 0 ; i < 8 ; ++i)
731+
block[off++] = tmp[i];
732+
assert(off == std::size(block));
733+
process_sha1_block(hash, block, words);
734+
735+
// Get final hash
736+
sha1::digest h;
737+
for (size_t i = 0 ; i < std::size(hash) ; ++i)
738+
hash[i] = htobe32(hash[i]);
739+
std::memcpy(&h[0], &hash[0], h.size());
740+
return h;
741+
}
742+
588743
//----------------------------------------------------------------------------------------------------------------
589744

590745
std::string base64_encode(std::string_view data)

src/http.h

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
#pragma once
22

3-
#include <cstdio>
43
#include <string>
54
#include <string_view>
6-
#include <optional>
75
#include <vector>
6+
#include <array>
7+
#include <memory>
8+
#include <system_error>
89

910
namespace http
1011
{
@@ -489,6 +490,22 @@ namespace http
489490
struct file_deleter {void operator()(FILE* ptr) {fclose(ptr);}};
490491
using file_ptr = std::unique_ptr<FILE, file_deleter>;
491492

493+
//----------------------------------------------------------------------------------------------------------------
494+
495+
class sha1
496+
{
497+
private:
498+
uint32_t hash[5] = {0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0};
499+
uint32_t words[80] = {0};
500+
uint8_t block[64] = {0};
501+
size_t off{0};
502+
size_t total{0};
503+
public:
504+
using digest = std::array<uint8_t, 20>;
505+
sha1& push(size_t ndata, const uint8_t* data);
506+
digest finish();
507+
};
508+
492509
//----------------------------------------------------------------------------------------------------------------
493510

494511
std::string base64_encode(std::string_view data);

src/http_async.h

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
#include <boost/asio/read.hpp>
66
#include <boost/asio/read_until.hpp>
77
#include <boost/asio/write.hpp>
8-
#include <openssl/evp.h>
98
#include "http.h"
109

1110
namespace http
@@ -462,15 +461,10 @@ namespace http
462461
inline std::string compute_sec_ws_accept(std::string_view sec_ws_key)
463462
{
464463
constexpr std::string_view magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
465-
char hash[EVP_MAX_MD_SIZE];
466-
unsigned int hash_len{0};
467-
EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
468-
EVP_DigestInit_ex(mdctx, EVP_sha1(), NULL);
469-
EVP_DigestUpdate(mdctx, sec_ws_key.data(), sec_ws_key.size());
470-
EVP_DigestUpdate(mdctx, magic.data(), magic.size());
471-
EVP_DigestFinal_ex(mdctx, (unsigned char*)hash, &hash_len);
472-
EVP_MD_CTX_free(mdctx);
473-
return base64_encode(std::string_view{hash, hash_len});
464+
const auto hash = sha1{}.push(sec_ws_key.size(), (const uint8_t*)sec_ws_key.data())
465+
.push(magic.size(), (const uint8_t*)magic.data())
466+
.finish();
467+
return base64_encode(std::string_view{(const char*)&hash[0], hash.size()});
474468
}
475469

476470
template<class AsyncStream>

test/CMakeLists.txt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
cmake_minimum_required(VERSION 3.13)
2+
project(Http)
3+
4+
# Deps
5+
include(FetchContent)
6+
find_package(Threads REQUIRED)
7+
find_package(OpenSSL REQUIRED)
8+
9+
if(POLICY CMP0135)
10+
cmake_policy(SET CMP0135 OLD)
11+
endif()
12+
set(BOOST_INCLUDE_LIBRARIES asio)
13+
set(BOOST_ENABLE_CMAKE ON)
14+
FetchContent_Declare(
15+
Boost
16+
URL "https://github.com/boostorg/boost/releases/download/boost-1.88.0/boost-1.88.0-cmake.tar.xz"
17+
URL_HASH MD5=3edffaacd2cfe63c240ef1b99497c74f)
18+
FetchContent_MakeAvailable(Boost)
19+
20+
# Lib
21+
add_library(http ${CMAKE_CURRENT_SOURCE_DIR}/../src/http.cpp)
22+
target_compile_features(http PUBLIC cxx_std_17)
23+
target_include_directories(http PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../src)
24+
target_link_libraries(http
25+
PUBLIC Boost::asio
26+
PUBLIC OpenSSL::SSL OpenSSL::Crypto)
27+
28+
# Unit tests
29+
add_executable(tests
30+
main.cpp
31+
sha1.cpp)
32+
target_link_options(tests PUBLIC $<$<CONFIG:Release>:-s>)
33+
target_link_libraries(tests PUBLIC http)

0 commit comments

Comments
 (0)