/* * Carla sha1 utils * Copyright (C) 2023 Filipe Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * For a full copy of the GNU General Public License see the doc/GPL.txt file. */ #ifndef CARLA_SHA1_UTILS_HPP_INCLUDED #define CARLA_SHA1_UTILS_HPP_INCLUDED #include "CarlaUtils.hpp" #ifdef __BIG_ENDIAN__ # define CARLA_SHA1_BIG_ENDIAN #elif defined __LITTLE_ENDIAN__ /* override */ #elif defined __BYTE_ORDER # if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ # define CARLA_SHA1_BIG_ENDIAN # endif #else // ! defined __LITTLE_ENDIAN__ # include // machine/endian.h # if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ # define CARLA_SHA1_BIG_ENDIAN # endif #endif /*! * Simple, single-use SHA1 class. * Must be discarded after use. * * Based on libcrypt by Wei Dai and other contributors (originally in the public domain) */ class CarlaSha1 { static constexpr const size_t BLOCK_LENGTH = 64; union { uint8_t u8[BLOCK_LENGTH]; uint32_t u32[BLOCK_LENGTH/sizeof(uint32_t)]; } buffer; uint32_t state[5]; uint32_t byteCount; uint8_t bufferOffset; char resultstr[41]; static_assert(sizeof(buffer.u8) == sizeof(buffer.u32), "valid size"); public: /* * Constructor. */ CarlaSha1() noexcept : byteCount(0), bufferOffset(0) { state[0] = 0x67452301; state[1] = 0xefcdab89; state[2] = 0x98badcfe; state[3] = 0x10325476; state[4] = 0xc3d2e1f0; } /* * Write a single byte of data. */ void writeByte(const uint8_t data) noexcept { ++byteCount; _addUncounted(data); } /* * Write a custom blob of data. */ void write(const void* const data, size_t len) noexcept { const uint8_t* u8data = static_cast(data); while (len--) writeByte(*u8data++); } /* * Return hash result as byte array (20 characters). * @note must be called only once! */ const uint8_t* resultAsHash() noexcept { // Pad to complete the last block _pad(); #ifndef CARLA_SHA1_BIG_ENDIAN // Swap byte order back for (int i=0; i<5; ++i) { state[i] = ((state[i] << 24) & 0xff000000) | ((state[i] << 8) & 0x00ff0000) | ((state[i] >> 8) & 0x0000ff00) | ((state[i] >> 24) & 0x000000ff); } #endif return static_cast(static_cast(state)); } /* * Return hash result as null-terminated string. * @note must be called only once! */ const char* resultAsString() noexcept { const uint8_t* const hash = resultAsHash(); for (int i=0; i<20; ++i) std::snprintf(resultstr + (i * 2), 3, "%02x", hash[i]); resultstr[40] = '\0'; return resultstr; } private: void _addUncounted(const uint8_t data) noexcept { #ifdef CARLA_SHA1_BIG_ENDIAN buffer.u8[bufferOffset] = data; #else buffer.u8[bufferOffset ^ 3] = data; #endif if (++bufferOffset == BLOCK_LENGTH) { bufferOffset = 0; _hashBlock(); } } void _hashBlock() noexcept { 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 t; for (uint8_t i=0; i<80; ++i) { if (i >= 16) { t = buffer.u32[(i + 13) & 15] ^ buffer.u32[(i + 8) & 15] ^ buffer.u32[(i + 2) & 15] ^ buffer.u32[i & 15]; buffer.u32[i & 15] = _rol32(t, 1); } if (i < 20) { t = (d ^ (b & (c ^ d))) + 0x5a827999; } else if (i < 40) { t = (b ^ c ^ d) + 0x6ed9eba1; } else if (i < 60) { t = ((b & c) | (d & (b | c))) + 0x8f1bbcdc; } else { t = (b ^ c ^ d) + 0xca62c1d6; } t += _rol32(a, 5) + e + buffer.u32[i & 15]; e = d; d = c; c = _rol32(b, 30); b = a; a = t; } state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; } // Implement SHA-1 padding (fips180-2 ยง5.1.1) void _pad() noexcept { // Pad with 0x80 followed by 0x00 until the end of the block _addUncounted(0x80); while (bufferOffset != 56) _addUncounted(0x00); // Append length in the last 8 bytes _addUncounted(0); // We're only using 32 bit lengths _addUncounted(0); // But SHA-1 supports 64 bit lengths _addUncounted(0); // So zero pad the top bits _addUncounted(byteCount >> 29); // Shifting to multiply by 8 _addUncounted(byteCount >> 21); // as SHA-1 supports bitstreams as well as _addUncounted(byteCount >> 13); // byte. _addUncounted(byteCount >> 5); _addUncounted(byteCount << 3); } static uint32_t _rol32(const uint32_t number, const uint8_t bits) noexcept { return (number << bits) | (number >> (32 - bits)); } }; #endif // CARLA_SHA1_UTILS_HPP_INCLUDED