Skip to content

Commit

Permalink
Adds SHA-256 hashing function
Browse files Browse the repository at this point in the history
  • Loading branch information
crsib committed Jan 23, 2024
1 parent cf6a2ae commit 6aac7d6
Show file tree
Hide file tree
Showing 6 changed files with 325 additions and 0 deletions.
1 change: 1 addition & 0 deletions libraries/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ set( LIBRARIES
lib-note-track
lib-viewport
lib-music-information-retrieval
lib-crypto
)

if ( ${_OPT}use_lv2 )
Expand Down
16 changes: 16 additions & 0 deletions libraries/lib-crypto/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# SPDX-FileName: CMakeLists.txt
# SPDX-FileContributor: Dmitry Vedenko
#[[
A set of helpers for working with cryptographic functions.
]]

set( SOURCES
crypto/SHA256.cpp
crypto/SHA256.h
)
set( LIBRARIES
PUBLIC
)
audacity_library( lib-crypto "${SOURCES}" "${LIBRARIES}"
"" "" )
207 changes: 207 additions & 0 deletions libraries/lib-crypto/crypto/SHA256.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
/*
* SPDX-License-Identifier: GPL-2.0-or-later
* SPDX-FileName: SHA256.h
* SPDX-FileContributor: Dmitry Vedenko
*
* Based on a public domain code by Brad Conte.
*/

#include "SHA256.h"

#include <cassert>
#include <cstring>

namespace crypto
{

namespace
{
constexpr uint32_t 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
};

#define ROTLEFT(a, b) (((a) << (b)) | ((a) >> (32 - (b))))
#define ROTRIGHT(a, b) (((a) >> (b)) | ((a) << (32 - (b))))

#define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z)))
#define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define EP0(x) (ROTRIGHT(x, 2) ^ ROTRIGHT(x, 13) ^ ROTRIGHT(x, 22))
#define EP1(x) (ROTRIGHT(x, 6) ^ ROTRIGHT(x, 11) ^ ROTRIGHT(x, 25))
#define SIG0(x) (ROTRIGHT(x, 7) ^ ROTRIGHT(x, 18) ^ ((x) >> 3))
#define SIG1(x) (ROTRIGHT(x, 17) ^ ROTRIGHT(x, 19) ^ ((x) >> 10))

void sha256_transform(uint32_t state[8], const uint8_t data[64])
{
uint32_t m[SHA256::BLOCK_SIZE];

int i = 0;
int j = 0;

for (; i < 16; ++i, j += 4)
m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) |
(data[j + 3]);

for (; i < 64; ++i)
m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];

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

for (i = 0; i < SHA256::BLOCK_SIZE; ++i)
{
const uint32_t t1 = h + EP1(e) + CH(e, f, g) + K[i] + m[i];
const uint32_t t2 = EP0(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;
}

} // namespace

SHA256::SHA256()
{
Reset();
}

void SHA256::Update(const void* data, std::size_t size)
{
const uint8_t* dataPtr = static_cast<const uint8_t*>(data);

while (size > 0)
{
std::size_t blockSize =
std::min<size_t>(size, SHA256::BLOCK_SIZE - mBufferLength);

std::memcpy(mBuffer + mBufferLength, dataPtr, blockSize);

mBufferLength += blockSize;
dataPtr += blockSize;
size -= blockSize;

if (mBufferLength == SHA256::BLOCK_SIZE)
{
sha256_transform(mState, mBuffer);
mBitLength += 512;
mBufferLength = 0;
}
}
}

void SHA256::Update(const char* zString)
{
Update(zString, std::strlen(zString));
}

std::string SHA256::Finalize()
{
uint8_t pad[SHA256::BLOCK_SIZE];
std::size_t padLength;

// `mBufferLength` is always less than SHA256::BLOCK_SIZE. See `Update`
// method.
assert(mBufferLength < SHA256::BLOCK_SIZE);

mBitLength += mBufferLength * 8;

if (mBufferLength < 56)
{
mBuffer[mBufferLength++] = 0x80;
std::memset(mBuffer + mBufferLength, 0, 56 - mBufferLength);
}
else
{
mBuffer[mBufferLength++] = 0x80;
std::memset(
mBuffer + mBufferLength, 0, SHA256::BLOCK_SIZE - mBufferLength);
sha256_transform(mState, mBuffer);
std::memset(mBuffer, 0, 56);
}


mBuffer[56] = (mBitLength >> 56) & 0xff;
mBuffer[57] = (mBitLength >> 48) & 0xff;
mBuffer[58] = (mBitLength >> 40) & 0xff;
mBuffer[59] = (mBitLength >> 32) & 0xff;
mBuffer[60] = (mBitLength >> 24) & 0xff;
mBuffer[61] = (mBitLength >> 16) & 0xff;
mBuffer[62] = (mBitLength >> 8) & 0xff;
mBuffer[63] = (mBitLength >> 0) & 0xff;

sha256_transform(mState, mBuffer);

uint8_t result[SHA256::HASH_SIZE];

for (int i = 0; i < 8; ++i)
{
result[i * 4 + 0] = (mState[i] >> 24) & 0xff;
result[i * 4 + 1] = (mState[i] >> 16) & 0xff;
result[i * 4 + 2] = (mState[i] >> 8) & 0xff;
result[i * 4 + 3] = (mState[i] >> 0) & 0xff;
}

Reset();

// Convert to hex string
constexpr char hexChars[] = "0123456789ABCDEF";
std::string resultStr;
resultStr.resize(HASH_SIZE * 2);

for (int i = 0; i < SHA256::HASH_SIZE; ++i)
{
resultStr[i * 2 + 0] = hexChars[(result[i] >> 4) & 0xf];
resultStr[i * 2 + 1] = hexChars[result[i] & 0xf];
}

return resultStr;
}

void SHA256::Reset()
{
mBitLength = 0;

mState[0] = 0x6a09e667;
mState[1] = 0xbb67ae85;
mState[2] = 0x3c6ef372;
mState[3] = 0xa54ff53a;
mState[4] = 0x510e527f;
mState[5] = 0x9b05688c;
mState[6] = 0x1f83d9ab;
mState[7] = 0x5be0cd19;

std::memset(mBuffer, 0, sizeof(mBuffer));
mBufferLength = 0;
}

} // namespace crypto
56 changes: 56 additions & 0 deletions libraries/lib-crypto/crypto/SHA256.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* SPDX-License-Identifier: GPL-2.0-or-later
* SPDX-FileName: SHA256.h
* SPDX-FileContributor: Dmitry Vedenko
*/

#pragma once

#include <cstdint>
#include <cstddef>

#include <string>

namespace crypto
{
class CRYPTO_API SHA256 final
{
public:
static constexpr std::size_t HASH_SIZE = 32;
static constexpr std::size_t BLOCK_SIZE = 64;

SHA256();

SHA256(const SHA256&) = delete;
SHA256(SHA256&&) = delete;
SHA256& operator=(const SHA256&) = delete;
SHA256& operator=(SHA256&&) = delete;

void Update(const void* data, std::size_t size);
void Update(const char* zString);

template<typename T>
void Update(const T& data)
{
Update(data.data(), data.size());
}

std::string Finalize();

void Reset();

private:
uint64_t mBitLength;
uint32_t mState[8];
uint8_t mBuffer[BLOCK_SIZE];
uint32_t mBufferLength;
}; // class SHA256

template<typename T>
std::string sha256(const T& data)
{
SHA256 hasher;
hasher.Update(data);
return hasher.Finalize();
}
} // namespace crypto
8 changes: 8 additions & 0 deletions libraries/lib-crypto/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
add_unit_test(
NAME
lib-crypto
SOURCES
CryptoTests.cpp
LIBRARIES
lib-crypto-interface
)
37 changes: 37 additions & 0 deletions libraries/lib-crypto/tests/CryptoTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* SPDX-License-Identifier: GPL-2.0-or-later
* SPDX-FileName: CryptoTests.cpp
* SPDX-FileContributor: Dmitry Vedenko
*/

#include <catch2/catch.hpp>

#include "crypto/SHA256.h"

TEST_CASE("SHA256", "")
{
crypto::SHA256 sha256;

REQUIRE(sha256.Finalize() == "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855");

sha256.Update("a");

REQUIRE(
sha256.Finalize() ==
"CA978112CA1BBDCAFAC231B39A23DC4DA786EFF8147C4E72B9807785AFEE48BB");

sha256.Update("bc", 2);

REQUIRE(
sha256.Finalize() ==
"1E0BBD6C686BA050B8EB03FFEEDC64FDC9D80947FCE821ABBE5D6DC8D252C5AC");

REQUIRE(
crypto::sha256("Audacity") ==
"CF21DBCF13FC6B66CCBF713B88AD02EC1EF0C6196ACAF28B0FB48D04858A5D04");

REQUIRE(
crypto::sha256(
" is a free, open source, cross-platform audio software for multi-track recording and editing.") ==
"00E7C81A5357B1734035CE4CAE5DC0B3F886D22C8AF2E3952E2F5569A994B8A8");
}

0 comments on commit 6aac7d6

Please sign in to comment.