Skip to content

Commit

Permalink
Merge pull request #3 from cmazakas/feature/sha2
Browse files Browse the repository at this point in the history
sha-256 impl
  • Loading branch information
pdimov authored Sep 26, 2024
2 parents 961133f + b594a3e commit 2ffd620
Show file tree
Hide file tree
Showing 4 changed files with 352 additions and 0 deletions.
11 changes: 11 additions & 0 deletions include/boost/hash2/detail/rot.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ BOOST_FORCEINLINE std::uint64_t rotl( std::uint64_t v, int k )
return _rotl64( v, k );
}

BOOST_FORCEINLINE std::uint32_t rotr( std::uint32_t v, int k )
{
return _rotr( v, k );
}

#else

// k must not be 0
Expand All @@ -43,6 +48,12 @@ BOOST_FORCEINLINE std::uint64_t rotl( std::uint64_t v, int k )
return ( v << k ) + ( v >> ( 64 - k ) );
}

// k must not be 0
BOOST_FORCEINLINE std::uint32_t rotr( std::uint32_t v, int k )
{
return ( v >> k ) | ( v << ( 32 - k ) );
}

#endif

} // namespace detail
Expand Down
229 changes: 229 additions & 0 deletions include/boost/hash2/sha2.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
#ifndef BOOST_HASH2_SHA2_HPP_INCLUDED
#define BOOST_HASH2_SHA2_HPP_INCLUDED

// Copyright 2024 Peter Dimov.
// Copyright 2024 Christian Mazakas.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
// SHA2 message digest algorithm, https://csrc.nist.gov/pubs/fips/180-4/upd1/final, https://www.rfc-editor.org/rfc/rfc6234

#include <boost/hash2/detail/read.hpp>
#include <boost/hash2/detail/rot.hpp>
#include <boost/hash2/detail/write.hpp>
#include <boost/assert.hpp>
#include <boost/cstdint.hpp>
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring>

namespace boost
{
namespace hash2
{

class sha2_256
{
private:

std::uint32_t state_[ 8 ];

static const int N = 64;

unsigned char buffer_[ N ];
std::size_t m_; // == n_ % N

std::uint64_t n_;

private:

void init()
{
state_[ 0 ] = 0x6a09e667;
state_[ 1 ] = 0xbb67ae85;
state_[ 2 ] = 0x3c6ef372;
state_[ 3 ] = 0xa54ff53a;
state_[ 4 ] = 0x510e527f;
state_[ 5 ] = 0x9b05688c;
state_[ 6 ] = 0x1f83d9ab;
state_[ 7 ] = 0x5be0cd19;
}

static std::uint32_t Sigma0( std::uint32_t x ) noexcept
{
return detail::rotr( x, 2 ) ^ detail::rotr( x, 13 ) ^ detail::rotr( x, 22 );
}

static std::uint32_t Sigma1( std::uint32_t x ) noexcept
{
return detail::rotr( x, 6 ) ^ detail::rotr( x, 11 ) ^ detail::rotr( x, 25 );
}

static std::uint32_t sigma0( std::uint32_t x ) noexcept
{
return detail::rotr( x, 7 ) ^ detail::rotr( x, 18 ) ^ ( x >> 3 );
}

static std::uint32_t sigma1( std::uint32_t x ) noexcept
{
return detail::rotr( x, 17 ) ^ detail::rotr( x, 19 ) ^ ( x >> 10 );
}

static std::uint32_t Ch( std::uint32_t x, std::uint32_t y, std::uint32_t z ) noexcept
{
return ( x & y ) ^ ( ~x & z );
}

static std::uint32_t Maj( std::uint32_t x, std::uint32_t y, std::uint32_t z ) noexcept
{
return ( x & y ) ^ ( x & z ) ^ ( y & z );
}

void transform( unsigned char const block[ 64 ] )
{
constexpr static std::uint32_t const K[ 64 ] =
{
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
};

std::uint32_t W[ 64 ];

for( int t = 0; t < 16; ++t )
{
W[ t ] = detail::read32be( block + t * 4 );
}

for( int t = 16; t < 64; ++t )
{
W[ t ] = ( sigma1( W[ t - 2 ] ) + W[ t - 7] + sigma0( W[ t - 15 ] ) + W[ t - 16 ] );
}

std::uint32_t a = state_[ 0 ];
std::uint32_t b = state_[ 1 ];
std::uint32_t c = state_[ 2 ];
std::uint32_t d = state_[ 3 ];
std::uint32_t e = state_[ 4 ];
std::uint32_t f = state_[ 5 ];
std::uint32_t g = state_[ 6 ];
std::uint32_t h = state_[ 7 ];

for( int t = 0; t < 64; ++t )
{
std::uint32_t T1 = h + Sigma1( e ) + Ch( e, f, g ) + K[ t ] + W[ t ];
std::uint32_t T2 = Sigma0( a ) + Maj( a, b, c );

h = g;
g = f;
f = e;
e = (d + T1);
d = c;
c = b;
b = a;
a = (T1 + T2);
}

state_[0] += a;
state_[1] += b;
state_[2] += c;
state_[3] += d;
state_[4] += e;
state_[5] += f;
state_[6] += g;
state_[7] += h;
}

public:
using result_type = std::array<unsigned char, 32>;

sha2_256(): m_( 0 ), n_( 0 )
{
init();
}

void update( void const * pv, std::size_t n )
{
unsigned char const* p = static_cast<unsigned char const*>( pv );

BOOST_ASSERT( m_ == n_ % N );

n_ += n;

if( m_ > 0 )
{
std::size_t k = N - m_;

if( n < k )
{
k = n;
}

std::memcpy( buffer_ + m_, p, k );

p += k;
n -= k;
m_ += k;

if( m_ < N ) return;

BOOST_ASSERT( m_ == N );

transform( buffer_ );
m_ = 0;

std::memset( buffer_, 0, N );
}

BOOST_ASSERT( m_ == 0 );

while( n >= N )
{
transform( p );

p += N;
n -= N;
}

BOOST_ASSERT( n < N );

if( n > 0 )
{
std::memcpy( buffer_, p, n );
m_ = n;
}

BOOST_ASSERT( m_ == n_ % N );
}

result_type result()
{
unsigned char bits[ 8 ];
detail::write64be( bits, n_ * 8 );

std::size_t k = m_ < 56 ? 56 - m_ : 64 + 56 - m_;
unsigned char padding[ 64 ] = { 0x80 };

update( padding, k );
update( bits, 8 );
BOOST_ASSERT( m_ == 0 );

result_type digest;
for( int i = 0; i < 8; ++i ) {
detail::write32be( &digest[ i * 4 ], state_[ i ] );
}

return digest;
}
};

} // namespace hash2
} // namespace boost

#endif // #ifndef BOOST_HASH2_SHA2_HPP_INCLUDED
1 change: 1 addition & 0 deletions test/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ run xxhash.cpp ;
run md5.cpp ;
run hmac_md5.cpp ;
run sha1.cpp ;
run sha2.cpp ;
run hmac_sha1.cpp ;
run ripemd.cpp ;

Expand Down
111 changes: 111 additions & 0 deletions test/sha2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright 2024 Christian Mazakas.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#define _CRT_SECURE_NO_WARNINGS

#include <boost/hash2/sha2.hpp>
#include <boost/core/lightweight_test.hpp>
#include <cstdio>
#include <string>
#include <vector>

template<std::size_t N> std::string to_string( std::array<unsigned char, N> const & v )
{
std::string r;

for( std::size_t i = 0; i < N; ++i )
{
char buffer[ 4 ];
std::snprintf( buffer, sizeof( buffer ), "%02x", v[ i ] );
r += buffer;
}

return r;
}

template<class H> std::string digest( std::string const & s )
{
H h;

h.update( s.data(), s.size() );

return to_string( h.result() );
}

template<class H, std::size_t N> std::string digest( char (&buf)[N])
{
H h;

h.update( buf, N );

return to_string( h.result() );
}

template<class H> std::string digest( std::vector<char> const & v )
{
H h;

h.update( v.data(), v.size() );

return to_string( h.result() );
}

using boost::hash2::sha2_256;

int main()
{
// https://en.wikipedia.org/wiki/SHA-2#Test_vectors

BOOST_TEST_EQ( digest<sha2_256>( "" ), std::string( "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" ) );

// https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA256.pdf

BOOST_TEST_EQ( digest<sha2_256>( "abc" ), std::string( "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" ) );
BOOST_TEST_EQ( digest<sha2_256>( "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" ), std::string( "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1" ) );

// use test vectors from here
// https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA2_Additional.pdf

BOOST_TEST_EQ( digest<sha2_256>( "\xbd" ), std::string( "68325720aabd7c82f30f554b313d0570c95accbb7dc4b5aae11204c08ffe732b" ) );
BOOST_TEST_EQ( digest<sha2_256>( "\xc9\x8c\x8e\x55" ), std::string( "7abc22c0ae5af26ce93dbb94433a0e0b2e119d014f8e7f65bd56c61ccccd9504" ) );

{
char buf[ 55 ] = { 0 };
BOOST_TEST_EQ( digest<sha2_256>( buf ), std::string( "02779466cdec163811d078815c633f21901413081449002f24aa3e80f0b88ef7" ) );
}

{
char buf[ 56 ] = { 0 };
BOOST_TEST_EQ( digest<sha2_256>( buf ), std::string( "d4817aa5497628e7c77e6b606107042bbba3130888c5f47a375e6179be789fbb" ) );
}

{
char buf[ 64 ] = { 0 };
BOOST_TEST_EQ( digest<sha2_256>( buf ), std::string( "f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b" ) );
}

{
char buf[ 1000 ] = { 0 };
BOOST_TEST_EQ( digest<sha2_256>( buf ), std::string( "541b3e9daa09b20bf85fa273e5cbd3e80185aa4ec298e765db87742b70138a53" ) );
}

{
char buf[ 1000 ] = {};
for( auto& c : buf ) c = 'A';
BOOST_TEST_EQ( digest<sha2_256>( buf ), std::string( "c2e686823489ced2017f6059b8b239318b6364f6dcd835d0a519105a1eadd6e4" ) );
}

{
char buf[ 1005 ] = {};
for( auto& c : buf ) c = 'U';
BOOST_TEST_EQ( digest<sha2_256>( buf ), std::string( "f4d62ddec0f3dd90ea1380fa16a5ff8dc4c54b21740650f24afc4120903552b0" ) );
}

{
std::vector<char> buf(1000000, 0x00);
BOOST_TEST_EQ( digest<sha2_256>( buf ), std::string( "d29751f2649b32ff572b5e0a9f541ea660a50f94ff0beedfb0b692b924cc8025" ) );
}

return boost::report_errors();
}

0 comments on commit 2ffd620

Please sign in to comment.