HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

sha1sum implementation in C++

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
sha1sumimplementationstackoverflow

Problem

I rarely program C++ in anger, and considered myself somewhat rusty, and certainly not up to speed on C++11. For a bit of exercise, I thought I'd try implementing sha1sum, as wasn't really up to speed with topics such as cryptographic hash functions either.

(Yes, yes, you shouldn't use SHA-1 in new code, understood!).

Would this pass muster as modern idiomatic C++?

```
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

namespace {
const auto blockBits = 512;

const auto int32Size = 4;
const auto int32Bits = int32Size * 8;
const auto intsPerBlock = blockBits / int32Bits;

const auto resultBits = 160;
const auto intsInResult = resultBits / int32Bits;

typedef std::vector BlockVector;
typedef std::vector HashVector;

uint32_t rotl(const uint32_t x, const unsigned int& n) {
const unsigned int rshift = int32Bits - n;
return (x > rshift);
}

uint32_t accumulateUint(const uint32_t& left, unsigned char& right) {
return (left RoundFunction;

RoundFunction& getFunctionForRound(const unsigned int round) {
static RoundFunction roundFunctions[] = {
// Ch: rounds 0 - 19
[] (const uint32_t& x, const uint32_t& y, const uint32_t& z) -> uint32_t {
return (x & y) ^ (~x & z);
},

// Parity: 20-39, 60 - 79
[] (const uint32_t& x, const uint32_t& y, const uint32_t& z) -> uint32_t {
return x ^ y ^ z;
},

// Maj: 40 - 59
[] (const uint32_t& x, const uint32_t& y, const uint32_t& z) -> uint32_t {
return (x & y) ^ (x & z) ^ (y & z);
}
};

return roundFunctions[(round > 59 ? round - 40 : round) / 20];
}

const uint32_t& getConstantForRound(const unsigned int round) {
static uint32_t constants[] = {
0x5a827999, // 0 - 29
0x6ed9eba1, // 20 - 39
0x8f1bbcdc, // 40 - 59
0xca62c1d6, // 60 - 79
};

return constants[round / 20];
}

struct RoundVariables {
HashVector workingVars;
BlockVector W;

RoundVariables(HashVector& hash, BlockVector&& w) : w

Solution

Without doing a full comprehensive review, here are some things you could do to improve your code:

-
While it's generally a good practice to always explicitly include the header that you need from the standard library, ` is guaranteed to include , and ; moreover it's easy to remember. Therefore, you could get rid of the explicit #includes of , and .

-
Instead of putting everything in a single file and putting everything but
main in an anonymous namespace, you could break your code in reusable pieces of code (you already have reusable functions, which is good) and create a header to put their prototypes.

-
const auto int32Size = 4; and friends could be safer. If you know that you can only work when a 32-bits integer is present, then I would write it like that:

constexpr std::int32_t in32Size = sizeof(std::int32_t);


While using
auto is generally a good practice, especially when writing generic code, you are actually writing code for a very specific type, namely std::int32_t (it's even in the name, using anything else could be misleading). Therefore, I would document that by using std::int32_t and letting sizeof deduce its size. This way, you can never be wrong.

-
const auto int32Bits = int32Size * 8; can also be trivially replaced with this:

constexpr std::int32_t int32Bits = std::numeric_limits::digits;


std::numeric_limits is the tool you want to use if you want to get type-safe and implementation-safe information about the built-in integer and floating point types (and even more if the types are overloaded).

-
Note that I used
constexpr in both of the previous points. It's the C++11 standard way to require the compiler to compute the value at compile time so that it can be used in more contexts requiring a compile-time constant expression.

-
The two following lines could benefit from a little bit of C++11:

typedef std::vector BlockVector;
typedef std::vector HashVector;


Using the new alias template, they could be rewritten as:

using BlockVector = std::vector;
using HashVector = std::vector;


While the old and new version are semantically the same, I find the new alias template syntax closer to the variable assignment and namespace assignment syntaxes which helps not having to remember the
typedef syntax which becomes awkward with functions pointers. Morevoer, this using declaration can be templated, unlike typedef.

-
It is often considered good practice to pass built-in types and small types (~smaller than the size of two pointers) by value to functions instead of passing them by
const&`. The rationale being that types that can fit in registers are easier to optimize if the compiler doesn't have to take into account possible aliasing problems introduced by references.

-
This line does not feel safe:

HashVector&& getHash() { return std::move(workingVars); }


Generally speaking, one should return by rvalue-reference only if the current instance is guaranteed to be a temporary. In order to do this, you could rvlue-qualify the function itself:

HashVector&& getHash() && { return std::move(workingVars); }


But then it would also probably need another overload for lvalue-references. Actually, rvalue-references were also introduced so that simple code could be simple to write. Which is to say, your function should simply be:

HashVector getHash() { return workingVars; }


The compiler will do the job of finding what is a temporary and move when appropriate.

Code Snippets

constexpr std::int32_t in32Size = sizeof(std::int32_t);
constexpr std::int32_t int32Bits = std::numeric_limits<std::int32_t>::digits;
typedef std::vector<uint32_t> BlockVector;
typedef std::vector<uint32_t> HashVector;
using BlockVector = std::vector<uint32_t>;
using HashVector = std::vector<uint32_t>;
HashVector&& getHash() { return std::move(workingVars); }

Context

StackExchange Code Review Q#78690, answer score: 4

Revisions (0)

No revisions yet.