From c54c6a8f44c70fb0567f39a87c9391f3cac57fb4 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Mon, 14 Sep 2015 09:12:20 +0200 Subject: implement sha2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/libsha2/digest.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/libsha2/digest.h | 4 +- src/libsha2/state.c | 4 +- src/libsha2/state.h | 28 ++++----- 4 files changed, 189 insertions(+), 22 deletions(-) diff --git a/src/libsha2/digest.c b/src/libsha2/digest.c index 5cf0f03..07f5c4a 100644 --- a/src/libsha2/digest.c +++ b/src/libsha2/digest.c @@ -17,18 +17,136 @@ * along with this library. If not, see . */ #include "digest.h" +#include +#include +/** + * Unified implementation (what can unified without performance impact) + * of the chunk processing for all SHA-2 functions + * + * @param A Wordsize-dependent constant, take a look at the code + * @param B Wordsize-dependent constant, take a look at the code + * @param C Wordsize-dependent constant, take a look at the code + * @param D Wordsize-dependent constant, take a look at the code + * @param E Wordsize-dependent constant, take a look at the code + * @param F Wordsize-dependent constant, take a look at the code + * @param G Wordsize-dependent constant, take a look at the code + * @param H Wordsize-dependent constant, take a look at the code + * @param I Wordsize-dependent constant, take a look at the code + * @param J Wordsize-dependent constant, take a look at the code + * @param K Wordsize-dependent constant, take a look at the code + * @param L Wordsize-dependent constant, take a look at the code + * @param WORD_T `__typeof()` on any wordsize-dependent variable, with exact size + * @param k Round constants + * @param w Words + * @param h Hash values + * @param work_h Space for temporary hash values + */ +#define SHA2_IMPLEMENTATION(A, B, C, D, E, F, G, H, I, J, K, L, WORD_T, k, w, h, work_h) \ + memcpy(work_h, h, sizeof(work_h)); \ + \ + memset(w, 0, 16 * sizeof(*(w))); \ + for (i = 0; i < 16; i++) \ + for (j = 0; j < sizeof(WORD_T); j++) \ + w[i] |= ((WORD_T)(state->chunk[(i + 1) * sizeof(WORD_T) - j - 1])) << (j << 3); \ + \ + for (i = 16; i < sizeof(k) / sizeof(*(k)); i++) \ + { \ + w[i] = w[i - 16] + w[i - 7]; \ + w[i] += ROTR(w[i - 15], A) ^ ROTR(w[i - 15], B) ^ (w[i - 15] >> (C)); \ + w[i] += ROTR(w[i - 2], D) ^ ROTR(w[i - 2], E) ^ (w[i - 2] >> (F)); \ + } \ + \ + for (i = 0; i < sizeof(k) / sizeof(*(k)); i++) \ + { \ + s1 = (work_h[4] & work_h[5]) ^ (work_h[6] & ~(work_h[4])); \ + s1 += work_h[7] + k[i] + w[i]; \ + s0 = (work_h[0] & work_h[1]) ^ (work_h[0] & work_h[2]) ^ (work_h[1] & work_h[2]); \ + s1 += ROTR(work_h[4], G) ^ ROTR(work_h[4], H) ^ ROTR(work_h[4], I); \ + s0 += ROTR(work_h[0], J) ^ ROTR(work_h[0], K) ^ ROTR(work_h[0], L); \ + \ + memmove(work_h + 1, work_h, 7 * sizeof(*(work_h))); \ + work_h[4] += s1; \ + work_h[0] = s1 + s0; \ + } \ + \ + for (i = 0; i < 8; i++) \ + h[i] += work_h[i] + + + +/** + * Process a chunk using SHA-256 + * + * @param state The hashing state + */ +__attribute__((nonnull, nothrow)) +static void process256(libsha2_state_t* restrict state) +{ + uint32_t s0, s1; + size_t i, j; +#define ROTR(X, N) (((X) >> (N)) | ((X) << ((sizeof(uint32_t) * 8) - (N)))) + SHA2_IMPLEMENTATION(7, 18, 3, 17, 19, 10, 6, 11, 25, 2, 13, 22, uint32_t, + state->k.b32, state->w.b32, state->h.b32, state->work_h.b32); +#undef ROTR +} + + +/** + * Process a chunk using SHA-512 + * + * @param state The hashing state + */ +__attribute__((nonnull, nothrow)) +static void process512(libsha2_state_t* restrict state) +{ + uint64_t s0, s1; + size_t i, j; +#define ROTR(X, N) (((X) >> (N)) | ((X) << ((sizeof(uint64_t) * 8) - (N)))) + SHA2_IMPLEMENTATION(1, 8, 7, 19, 61, 6, 14, 18, 41, 28, 34, 39, uint64_t, + state->k.b64, state->w.b64, state->h.b64, state->work_h.b64); +#undef ROTR +} + + /** * Absorb more of the message * * @param state The hashing state - * @param message The message + * @param message The message, in bits, must be equivalent to 0 modulus 8 * @param msglen The length of the message */ void libsha2_update(libsha2_state_t* restrict state, const char* restrict message, size_t msglen) { + size_t n, off, mlen; + + msglen /= 8; + mlen = state->message_size / 8; + + while (msglen) + { + off = mlen % state->chunk_size; + n = state->chunk_size - off; + n = n < msglen ? n : msglen; + memcpy(state->chunk + off, message, n); + if (off + n == state->chunk_size) + switch (state->algorithm) + { + case LIBSHA2_224: + case LIBSHA2_256: + process256(state); + break; + + default: + process512(state); + break; + } + message += n, mlen += n, msglen -= n; + } + + state->message_size = mlen * 8; } @@ -36,11 +154,64 @@ void libsha2_update(libsha2_state_t* restrict state, const char* restrict messag * Absorb the last part of the message and output a hash * * @param state The hashing state - * @param message The message + * @param message The message, in bits * @param msglen The length of the message, zero if there is nothing more to absorb * @param output The output buffer for the hash */ void libsha2_digest(libsha2_state_t* restrict state, const char* restrict message, size_t msglen, char* output) { + char* appendix; + size_t i, j, k, n; + + if (msglen & ~7) + { + libsha2_update(state, message, msglen & ~7); + message += msglen & ~7; + msglen &= 7; + } + + k = 8 * state->chunk_size; + n = state->chunk_size + 8; + n = (k + (n % k)) % k; + n = n / 8 - 1; + + appendix = state->appendix; + if (msglen) + { + j = 7 - msglen; + *appendix = *message; + *appendix |= 1 << j; + *appendix &= ~((1 << j) - 1); + } + else + *appendix = (unsigned char)128; + + k = state->message_size + msglen; + i = state->chunk_size / 8; + appendix += n + i - 1; + for (i = i < sizeof(size_t) ? i : sizeof(size_t); i--;) + *(appendix - i) = (unsigned char)((k >> (i * 8)) & 255); + + n += state->chunk_size; + libsha2_update(state, state->appendix, n); + + n = libsha2_algorithm_output_size(state->algorithm); + switch (state->algorithm) + { + case LIBSHA2_224: + case LIBSHA2_256: + for (i = 0; i < 8; i++) + for (j = 0; j < (state->chunk_size / 16); j++) + if (k = (i + 1) * (state->chunk_size / 16) - j - 1, k < n) + output[k] = (char)((state->h.b32[i] >> (8 * j)) & 255); + break; + + default: + for (i = 0; i < 8; i++) + for (j = 0; j < (state->chunk_size / 16); j++) + if (k = (i + 1) * (state->chunk_size / 16) - j - 1, k < n) + output[k] = (char)((state->h.b64[i] >> (8 * j)) & 255); + break; + } } diff --git a/src/libsha2/digest.h b/src/libsha2/digest.h index 47b3414..be81f90 100644 --- a/src/libsha2/digest.h +++ b/src/libsha2/digest.h @@ -27,7 +27,7 @@ * Absorb more of the message * * @param state The hashing state - * @param message The message + * @param message The message, in bits, must be equivalent to 0 modulus 8 * @param msglen The length of the message */ __attribute__((nonnull, nothrow)) @@ -37,7 +37,7 @@ void libsha2_update(libsha2_state_t* restrict state, const char* restrict messag * Absorb the last part of the message and output a hash * * @param state The hashing state - * @param message The message + * @param message The message, in bits * @param msglen The length of the message, zero if there is nothing more to absorb * @param output The output buffer for the hash */ diff --git a/src/libsha2/state.c b/src/libsha2/state.c index 0a7a5b1..e907cec 100644 --- a/src/libsha2/state.c +++ b/src/libsha2/state.c @@ -95,17 +95,19 @@ int libsha2_state_initialise(libsha2_state_t* restrict state, libsha2_algorithm_ return errno = EINVAL, -1; } - /* Set round constants. */ + /* Set round constants, and chunk size. */ switch (algorithm) { case LIBSHA2_224: case LIBSHA2_256: for (i = 0; i < 64; i++) state->k.b32[i] = (uint32_t)(ROUND_CONSTANTS[i] >> 32); + state->chunk_size = 64; break; default: memcpy(state->k.b64, ROUND_CONSTANTS, sizeof(ROUND_CONSTANTS)); + state->chunk_size = 128; break; } diff --git a/src/libsha2/state.h b/src/libsha2/state.h index c290186..1dfd4be 100644 --- a/src/libsha2/state.h +++ b/src/libsha2/state.h @@ -148,27 +148,21 @@ typedef struct libsha2_state } work_h; /** - * Space for chunks to process + * Space for chunks to process, limited + * to 64 bytes on 32-bit algorithms */ - union - { - /** - * For 32-bit algorithms - */ - unsigned char b32[64]; - - /** - * For 64-bit algorithms - */ - unsigned char b64[128]; - - } chunk; + unsigned char chunk[128]; + + /** + * Space for storing the last bits and + * the padding + */ + char appendix[256]; /** - * Output buffer, required because - * some algorithms truncate the output + * The size of the chunks, in bytes */ - char output[64]; + size_t chunk_size; /** * The algorithm that is used -- cgit v1.2.3-70-g09d2