aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/supplemental/sha1/CMakeLists.txt12
-rw-r--r--src/supplemental/sha1/sha1.c247
-rw-r--r--src/supplemental/sha1/sha1.h26
-rw-r--r--tests/CMakeLists.txt1
-rw-r--r--tests/sha1.c63
6 files changed, 350 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 2ab202d9..93fa390d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -121,6 +121,7 @@ endif()
add_subdirectory(supplemental/base64)
add_subdirectory(supplemental/mbedtls)
+add_subdirectory(supplemental/sha1)
add_subdirectory(protocol/bus0)
add_subdirectory(protocol/pair0)
diff --git a/src/supplemental/sha1/CMakeLists.txt b/src/supplemental/sha1/CMakeLists.txt
new file mode 100644
index 00000000..8496777b
--- /dev/null
+++ b/src/supplemental/sha1/CMakeLists.txt
@@ -0,0 +1,12 @@
+#
+# Copyright 2017 Capitar IT Group BV <info@capitar.com>
+# Copyright 2017 Staysail Systems, Inc. <info@staysail.tech>
+#
+# This software is supplied under the terms of the MIT License, a
+# copy of which should be located in the distribution where this
+# file was obtained (LICENSE.txt). A copy of the license may also be
+# found online at https://opensource.org/licenses/MIT.
+#
+
+set(SHA1_SOURCES supplemental/sha1/sha1.c supplemental/sha1/sha1.h)
+set(NNG_SOURCES ${NNG_SOURCES} ${SHA1_SOURCES} PARENT_SCOPE)
diff --git a/src/supplemental/sha1/sha1.c b/src/supplemental/sha1/sha1.c
new file mode 100644
index 00000000..39a98e07
--- /dev/null
+++ b/src/supplemental/sha1/sha1.c
@@ -0,0 +1,247 @@
+//
+// Copyright 2017 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2017 Capitar IT Group BV <info@capitar.com>
+//
+// This software is supplied under the terms of the MIT License, a
+// copy of which should be located in the distribution where this
+// file was obtained (LICENSE.txt). A copy of the license may also be
+// found online at https://opensource.org/licenses/MIT.
+//
+
+// This file represents a modification of Paul E. Jones' implementation.
+// We have adapted this code for C99, removed the error checks on input size,
+// and adjusted names to fit within NNG. We also updated the code to emit
+// the digest as a byte array, following convention. The original code was
+// distributed with the following notice:
+
+// Copyright (C) 1998, 2009
+// Paul E. Jones <paulej@packetizer.com>
+//
+// Freeware Public License (FPL)
+//
+// This software is licensed as "freeware." Permission to distribute
+// this software in source and binary forms, including incorporation
+// into other products, is hereby granted without a fee. THIS SOFTWARE
+// IS PROVIDED 'AS IS' AND WITHOUT ANY EXPRESSED OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHOR SHALL NOT BE HELD
+// LIABLE FOR ANY DAMAGES RESULTING FROM THE USE OF THIS SOFTWARE, EITHER
+// DIRECTLY OR INDIRECTLY, INCLUDING, BUT NOT LIMITED TO, LOSS OF DATA
+// OR DATA BEING RENDERED INACCURATE.
+
+// This file implements the Secure Hashing Standard, defined in FIPS PUB 180-1
+// and RFC 3174. This particular implementation has not undergone any NIST
+// validation. Furthermore, SHA-1 has been found to be insufficiently strong
+// against cryptanalysis, and it's use is specifically discouraged in new
+// security-sensitive applications. Nonetheless, it is useful for non-secure
+// applications such as basic message validation. In the websocket protocol
+// (RFC 6455), SHA-1's use is limited to a non-security sensitive context.
+
+// This implementation assumes an 8-bit byte, and a C99 compilation
+// environment including support for 64-bit integers. It does not
+// detect overflows caused by issuing too large messages (2^56 bytes is the
+// maximum message size) or caused by incorrect usage. The results in either
+// of those circumstances are undefined.
+
+#include <stdint.h>
+#include <string.h>
+
+#include "sha1.h"
+
+// Define the circular shift macro
+#define nni_sha1_circular_shift(bits, word) \
+ ((((word) << (bits)) & 0xFFFFFFFF) | ((word) >> (32 - (bits))))
+
+static void nni_sha1_process(nni_sha1_ctx *);
+static void nni_sha1_pad(nni_sha1_ctx *);
+
+// nni_sha1_init initializes the context to an initial value.
+void
+nni_sha1_init(nni_sha1_ctx *ctx)
+{
+ ctx->len = 0;
+ ctx->idx = 0;
+
+ ctx->digest[0] = 0x67452301;
+ ctx->digest[1] = 0xEFCDAB89;
+ ctx->digest[2] = 0x98BADCFE;
+ ctx->digest[3] = 0x10325476;
+ ctx->digest[4] = 0xC3D2E1F0;
+}
+
+// nni_sha1_final runs the final padding for the digest, and stores
+// the resulting digest in the supplied output buffer.
+void
+nni_sha1_final(nni_sha1_ctx *ctx, uint8_t digest[20])
+{
+ nni_sha1_pad(ctx);
+ for (int i = 0; i < 5; i++) {
+ digest[i * 4] = (ctx->digest[i] >> 24) & 0xff;
+ digest[i * 4 + 1] = (ctx->digest[i] >> 16) & 0xff;
+ digest[i * 4 + 2] = (ctx->digest[i] >> 8) & 0xff;
+ digest[i * 4 + 3] = (ctx->digest[i] >> 0) & 0xff;
+ }
+}
+
+// nni_sha1 is a convenience that does the entire init, update, and final
+// sequence in a single operation.
+void
+nni_sha1(const uint8_t *msg, size_t length, uint8_t digest[20])
+{
+ nni_sha1_ctx ctx;
+
+ nni_sha1_init(&ctx);
+ nni_sha1_update(&ctx, msg, length);
+ nni_sha1_final(&ctx, digest);
+}
+
+// nni_sha1_update updates the SHA1 context, reading from the message supplied.
+void
+nni_sha1_update(nni_sha1_ctx *ctx, const uint8_t *msg, size_t length)
+{
+ if (!length) {
+ return;
+ }
+
+ while (length--) {
+ // memcpy might be faster...
+ ctx->blk[ctx->idx++] = (*msg & 0xFF);
+ ctx->len += 8;
+
+ if (ctx->idx == 64) {
+ // This will reset the index back to zero.
+ nni_sha1_process(ctx);
+ }
+
+ msg++;
+ }
+}
+
+// nni_sha1_process processes the next 512 bites of the message stored
+// in the blk array.
+void
+nni_sha1_process(nni_sha1_ctx *ctx)
+{
+ const unsigned K[] = // Constants defined in SHA-1
+ { 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 };
+ unsigned temp; // Temporary word value
+ unsigned W[80]; // Word sequence
+ unsigned A, B, C, D, E; // Word buffers
+
+ // Initialize the first 16 words in the array W
+ for (int t = 0; t < 16; t++) {
+ W[t] = ((unsigned) ctx->blk[t * 4]) << 24;
+ W[t] |= ((unsigned) ctx->blk[t * 4 + 1]) << 16;
+ W[t] |= ((unsigned) ctx->blk[t * 4 + 2]) << 8;
+ W[t] |= ((unsigned) ctx->blk[t * 4 + 3]);
+ }
+
+ for (int t = 16; t < 80; t++) {
+ W[t] = nni_sha1_circular_shift(
+ 1, W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]);
+ }
+
+ A = ctx->digest[0];
+ B = ctx->digest[1];
+ C = ctx->digest[2];
+ D = ctx->digest[3];
+ E = ctx->digest[4];
+
+ for (int t = 0; t < 20; t++) {
+ temp = nni_sha1_circular_shift(5, A) + ((B & C) | ((~B) & D)) +
+ E + W[t] + K[0];
+ temp &= 0xFFFFFFFF;
+ E = D;
+ D = C;
+ C = nni_sha1_circular_shift(30, B);
+ B = A;
+ A = temp;
+ }
+
+ for (int t = 20; t < 40; t++) {
+ temp = nni_sha1_circular_shift(5, A) + (B ^ C ^ D) + E + W[t] +
+ K[1];
+ temp &= 0xFFFFFFFF;
+ E = D;
+ D = C;
+ C = nni_sha1_circular_shift(30, B);
+ B = A;
+ A = temp;
+ }
+
+ for (int t = 40; t < 60; t++) {
+ temp = nni_sha1_circular_shift(5, A) +
+ ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
+ temp &= 0xFFFFFFFF;
+ E = D;
+ D = C;
+ C = nni_sha1_circular_shift(30, B);
+ B = A;
+ A = temp;
+ }
+
+ for (int t = 60; t < 80; t++) {
+ temp = nni_sha1_circular_shift(5, A) + (B ^ C ^ D) + E + W[t] +
+ K[3];
+ temp &= 0xFFFFFFFF;
+ E = D;
+ D = C;
+ C = nni_sha1_circular_shift(30, B);
+ B = A;
+ A = temp;
+ }
+
+ ctx->digest[0] = (ctx->digest[0] + A) & 0xFFFFFFFF;
+ ctx->digest[1] = (ctx->digest[1] + B) & 0xFFFFFFFF;
+ ctx->digest[2] = (ctx->digest[2] + C) & 0xFFFFFFFF;
+ ctx->digest[3] = (ctx->digest[3] + D) & 0xFFFFFFFF;
+ ctx->digest[4] = (ctx->digest[4] + E) & 0xFFFFFFFF;
+
+ ctx->idx = 0;
+}
+
+// nni_sha1_pad pads the message, adding the length. This is done
+// when finishing the message.
+//
+// According to the standard, the message must be padded to an even 512 bits.
+// The first padding bit must be a '1'. The last 64 bits represent the length
+// of the original message. All bits in between should be 0. This function
+// will pad the message according to those rules by filling the blk array
+// accordingly. It will also call nni_sha1_process() appropriately. When it
+// returns, it can be assumed that the message digest has been computed.
+void
+nni_sha1_pad(nni_sha1_ctx *ctx)
+{
+ // Check to see if the current message block is too small to hold
+ // the initial padding bits and length. If so, we will pad the
+ // block, process it, and then continue padding into a second block.
+ if (ctx->idx > 55) {
+ ctx->blk[ctx->idx++] = 0x80;
+ while (ctx->idx < 64) {
+ ctx->blk[ctx->idx++] = 0;
+ }
+
+ nni_sha1_process(ctx);
+
+ while (ctx->idx < 56) {
+ ctx->blk[ctx->idx++] = 0;
+ }
+ } else {
+ ctx->blk[ctx->idx++] = 0x80;
+ while (ctx->idx < 56) {
+ ctx->blk[ctx->idx++] = 0;
+ }
+ }
+
+ // Store the message length as the last 8 octets (big endian)
+ ctx->blk[56] = (ctx->len >> 56) & 0xff;
+ ctx->blk[57] = (ctx->len >> 48) & 0xff;
+ ctx->blk[58] = (ctx->len >> 40) & 0xff;
+ ctx->blk[59] = (ctx->len >> 32) & 0xff;
+ ctx->blk[60] = (ctx->len >> 24) & 0xff;
+ ctx->blk[61] = (ctx->len >> 16) & 0xff;
+ ctx->blk[62] = (ctx->len >> 8) & 0xff;
+ ctx->blk[63] = (ctx->len) & 0xff;
+
+ nni_sha1_process(ctx);
+}
diff --git a/src/supplemental/sha1/sha1.h b/src/supplemental/sha1/sha1.h
new file mode 100644
index 00000000..91554dd7
--- /dev/null
+++ b/src/supplemental/sha1/sha1.h
@@ -0,0 +1,26 @@
+//
+// Copyright 2017 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2017 Capitar IT Group BV <info@capitar.com>
+//
+// This software is supplied under the terms of the MIT License, a
+// copy of which should be located in the distribution where this
+// file was obtained (LICENSE.txt). A copy of the license may also be
+// found online at https://opensource.org/licenses/MIT.
+//
+
+#ifndef NNG_SUPPLEMENTAL_SHA1_SHA1_H
+#define NNG_SUPPLEMENTAL_SHA1_SHA1_H
+
+typedef struct {
+ uint32_t digest[5]; // resulting digest
+ uint64_t len; // length in bits
+ uint8_t blk[64]; // message block
+ int idx; // index of next byte in block
+} nni_sha1_ctx;
+
+extern void nni_sha1_init(nni_sha1_ctx *);
+extern void nni_sha1_update(nni_sha1_ctx *, const uint8_t *, size_t);
+extern void nni_sha1_final(nni_sha1_ctx *, uint8_t[20]);
+extern void nn_sha1(const uint8_t *, size_t, uint8_t[20]);
+
+#endif // NNG_SUPPLEMENTAL_SHA1_SHA1_H
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index aa6905f2..77eb0bc5 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -136,6 +136,7 @@ add_nng_test(pollfd 5)
add_nng_test(pubsub 5)
add_nng_test(reconnect 5)
add_nng_test(resolv 10)
+add_nng_test(sha1 5)
add_nng_test(sock 5)
add_nng_test(survey 5)
add_nng_test(synch 5)
diff --git a/tests/sha1.c b/tests/sha1.c
new file mode 100644
index 00000000..f26a5b9c
--- /dev/null
+++ b/tests/sha1.c
@@ -0,0 +1,63 @@
+//
+// Copyright 2017 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2017 Capitar IT Group BV <info@capitar.com>
+//
+// This software is supplied under the terms of the MIT License, a
+// copy of which should be located in the distribution where this
+// file was obtained (LICENSE.txt). A copy of the license may also be
+// found online at https://opensource.org/licenses/MIT.
+//
+
+#include <stdint.h>
+#include <strings.h>
+
+#include "convey.h"
+
+#include "nng.h"
+
+#include "supplemental/sha1/sha1.h"
+
+// The following test vectors are from RFC 3174.
+#define TEST1 "abc"
+#define TEST2a "abcdbcdecdefdefgefghfghighijhi"
+#define TEST2b "jkijkljklmklmnlmnomnopnopq"
+#define TEST2 TEST2a TEST2b
+#define TEST3 "a"
+#define TEST4a "01234567012345670123456701234567"
+#define TEST4b "01234567012345670123456701234567"
+/* an exact multiple of 512 bits */
+#define TEST4 TEST4a TEST4b
+
+char *testarray[4] = { TEST1, TEST2, TEST3, TEST4 };
+int repeatcount[4] = { 1, 1, 1000000, 10 };
+char *resultarray[4] = {
+ "A9 99 3E 36 47 06 81 6A BA 3E 25 71 78 50 C2 6C 9C D0 D8 9D",
+ "84 98 3E 44 1C 3B D2 6E BA AE 4A A1 F9 51 29 E5 E5 46 70 F1",
+ "34 AA 97 3C D4 C4 DA A4 F6 1E EB 2B DB AD 27 31 65 34 01 6F",
+ "DE A3 56 A2 CD DD 90 C7 A7 EC ED C5 EB B5 63 93 4F 46 04 52"
+};
+
+TestMain("SHA1 Verification", {
+
+ Convey("SHA1 Works", {
+ for (int i = 0; i < 4; i++) {
+ nni_sha1_ctx ctx;
+ size_t slen = strlen(testarray[i]);
+ uint8_t digest[20];
+ char strout[20 * 3 + 1];
+ memset(digest, 0, sizeof(digest));
+ nni_sha1_init(&ctx);
+ for (int j = 0; j < repeatcount[i]; j++) {
+ nni_sha1_update(
+ &ctx, (uint8_t *) testarray[i], slen);
+ }
+ nni_sha1_final(&ctx, digest);
+ for (int j = 0; j < 20; j++) {
+ snprintf(
+ strout + j * 3, 4, "%02X ", digest[j]);
+ }
+ strout[20 * 3 - 1] = '\0';
+ So(strcmp(strout, resultarray[i]) == 0);
+ }
+ });
+});