From 78a57dfe83eab9957dce51934ebab871fa8e49a8 Mon Sep 17 00:00:00 2001 From: ilammy Date: Sat, 18 Apr 2015 15:20:41 +0300 Subject: [PATCH 1/7] chibi.crypto: documentation for (chibi crypto sha-2) Clearly state what kind of arguments sha-224 and sha-256 can handle and what they return as a result. --- Makefile | 2 +- lib/chibi/crypto/sha2.sld | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3a9a2855..4aa56ad0 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ INCLUDES = $(BASE_INCLUDES) include/chibi/eval.h MODULE_DOCS := ast config disasm equiv filesystem generic heap-stats io \ loop match mime modules net pathname process repl scribble stty \ system test time trace type-inference uri weak monad/environment \ - show show/base + show show/base crypto/sha2 HTML_LIBS = $(MODULE_DOCS:%=doc/lib/chibi/%.html) diff --git a/lib/chibi/crypto/sha2.sld b/lib/chibi/crypto/sha2.sld index 412f5881..7079d727 100644 --- a/lib/chibi/crypto/sha2.sld +++ b/lib/chibi/crypto/sha2.sld @@ -3,3 +3,15 @@ (import (scheme base) (srfi 33) (chibi bytevector)) (export sha-224 sha-256) (include "sha2.scm")) + +;;> \procedure{(sha-224 src)} +;;> +;;> Computes SHA-224 digest of the \var{src} which can be a string, +;;> a bytevector, or a binary input port. Returns a hexadecimal string +;;> (in lowercase). + +;;> \procedure{(sha-256 src)} +;;> +;;> Computes SHA-256 digest of the \var{src} which can be a string, +;;> a bytevector, or a binary input port. Returns a hexadecimal string +;;> (in lowercase). From baff1af72dce414ba93eb3046c67e4c38ce3552f Mon Sep 17 00:00:00 2001 From: ilammy Date: Mon, 13 Apr 2015 18:22:28 +0300 Subject: [PATCH 2/7] chibi.crypto: native SHA-2 implementation The original Scheme implementation is astonishingly slow. Rewriting SHA-2 in C yields around x10000 speed boost for premade strings and bytevectors. For input ports this is alleviated to x100 boost. The implementation is divided into two parts: native computational backend and thin Scheme interface. It is tedious to properly do IO from C, so the Scheme code handles reading data from an input port and the C code performs actual computations on byte buffers (which is also used to handle strings and bytevectors directly). Scheme wrapper reads data in chunked manner with 'read-bytevector'. Currently, the chunk size has insignificant impact on performance as soon as it is bigger than 64. Also, using simply read-bytevector turned out to be 33% faster than preallocating a buffer and filling it with read-bytevector! One tricky part is how to get exact 32-bit integers in C89. We have no there, so instead we use to see whether we have a standard type with suitable boundaries. The other one is how to return a properly tagged sha_context from C. Chibi FFI currently cannot handle the case when a procedure returns either a C pointer (which needs to be boxed) or an exception (which should be left as is). To workaround this sexp_start_sha() receives a dummy argument of type sha_context; this makes Chibi FFI to put a proper type tag into 'self', which is then extracted in the C code. This commits adds a new shared library 'crypto$(SO)' with intent to keep there all native code of (chibi crypto) libraries. This allows to simply put any future native implementation of SHA-512 or MD5 in some md5.c and just include those files into crypto.stub. --- Makefile | 6 +- lib/chibi/crypto/crypto.stub | 39 +++++ lib/chibi/crypto/integers.h | 28 ++++ lib/chibi/crypto/sha2-native.scm | 24 +++ lib/chibi/crypto/sha2.c | 258 +++++++++++++++++++++++++++++++ 5 files changed, 354 insertions(+), 1 deletion(-) create mode 100644 lib/chibi/crypto/crypto.stub create mode 100644 lib/chibi/crypto/integers.h create mode 100644 lib/chibi/crypto/sha2-native.scm create mode 100644 lib/chibi/crypto/sha2.c diff --git a/Makefile b/Makefile index 4aa56ad0..04d16776 100644 --- a/Makefile +++ b/Makefile @@ -23,12 +23,14 @@ CHIBI_COMPILED_LIBS = lib/chibi/filesystem$(SO) lib/chibi/process$(SO) \ lib/chibi/time$(SO) lib/chibi/system$(SO) lib/chibi/stty$(SO) \ lib/chibi/weak$(SO) lib/chibi/heap-stats$(SO) lib/chibi/disasm$(SO) \ lib/chibi/net$(SO) lib/chibi/ast$(SO) lib/chibi/emscripten$(SO) +CHIBI_CRYPTO_COMPILED_LIBS = lib/chibi/crypto/crypto$(SO) CHIBI_IO_COMPILED_LIBS = lib/chibi/io/io$(SO) CHIBI_OPT_COMPILED_LIBS = lib/chibi/optimize/rest$(SO) \ lib/chibi/optimize/profile$(SO) EXTRA_COMPILED_LIBS ?= COMPILED_LIBS = $(CHIBI_COMPILED_LIBS) $(CHIBI_IO_COMPILED_LIBS) \ - $(CHIBI_OPT_COMPILED_LIBS) $(EXTRA_COMPILED_LIBS) \ + $(CHIBI_OPT_COMPILED_LIBS) $(CHIBI_CRYPTO_COMPILED_LIBS) \ + $(EXTRA_COMPILED_LIBS) \ lib/srfi/18/threads$(SO) lib/srfi/27/rand$(SO) lib/srfi/33/bit$(SO) \ lib/srfi/39/param$(SO) lib/srfi/69/hash$(SO) lib/srfi/95/qsort$(SO) \ lib/srfi/98/env$(SO) lib/scheme/time$(SO) @@ -284,11 +286,13 @@ install: all $(INSTALL) -m0644 lib/srfi/95/*.scm $(DESTDIR)$(MODDIR)/srfi/95/ $(INSTALL) -m0644 lib/srfi/99/*.sld $(DESTDIR)$(MODDIR)/srfi/99/ $(INSTALL) -m0644 lib/srfi/99/records/*.sld lib/srfi/99/records/*.scm $(DESTDIR)$(MODDIR)/srfi/99/records/ + $(MKDIR) $(DESTDIR)$(BINMODDIR)/chibi/crypto/ $(MKDIR) $(DESTDIR)$(BINMODDIR)/chibi/io/ $(MKDIR) $(DESTDIR)$(BINMODDIR)/chibi/optimize/ $(MKDIR) $(DESTDIR)$(BINMODDIR)/scheme/ $(MKDIR) $(DESTDIR)$(BINMODDIR)/srfi/18 $(DESTDIR)$(BINMODDIR)/srfi/27 $(DESTDIR)$(BINMODDIR)/srfi/33 $(DESTDIR)$(BINMODDIR)/srfi/39 $(DESTDIR)$(BINMODDIR)/srfi/69 $(DESTDIR)$(BINMODDIR)/srfi/95 $(DESTDIR)$(BINMODDIR)/srfi/98 $(INSTALL) -m0755 $(CHIBI_COMPILED_LIBS) $(DESTDIR)$(BINMODDIR)/chibi/ + $(INSTALL) -m0755 $(CHIBI_CRYPTO_COMPILED_LIBS) $(DESTDIR)$(BINMODDIR)/chibi/crypto/ $(INSTALL) -m0755 $(CHIBI_IO_COMPILED_LIBS) $(DESTDIR)$(BINMODDIR)/chibi/io/ $(INSTALL) -m0755 $(CHIBI_OPT_COMPILED_LIBS) $(DESTDIR)$(BINMODDIR)/chibi/optimize/ $(INSTALL) -m0755 lib/scheme/time$(SO) $(DESTDIR)$(BINMODDIR)/scheme/ diff --git a/lib/chibi/crypto/crypto.stub b/lib/chibi/crypto/crypto.stub new file mode 100644 index 00000000..6bb9f3bf --- /dev/null +++ b/lib/chibi/crypto/crypto.stub @@ -0,0 +1,39 @@ +(c-include-verbatim "sha2.c") + +;; \procedure{(start-sha type)} +;; +;; Allocates a new opaque computation context for a SHA-\var{type} +;; digest, where \var{type} can be one of the following constants: +;; \scheme{type-sha-224}, \scheme{type-sha-256}. + +(define-c-struct sha_context) + +(define-c sexp (start-sha "sexp_start_sha") + ((value ctx sexp) (value self sexp) unsigned-int (value NULL sha_context))) + +(define-c-const unsigned-int (type-sha-224 "SHA_TYPE_224")) +(define-c-const unsigned-int (type-sha-256 "SHA_TYPE_256")) + +;; \procedure{(add-sha-data! sha-context data)} +;; +;; Adds a new piece of data into the given context. \var{data} can be +;; a bytevector or a string. Bytevectors are added as sequences bytes. +;; Strings are added as sequences of byte representations of their +;; chars (which is either UTF-8 or ASCII code point sequence, depending +;; on whether Chibi was compiled with Unicode support). +;; +;; It is an error to add more data into a context that was finalized +;; by \scheme{get-sha}. This procedure returns an unspecified value. + +(define-c sexp (add-sha-data! "sexp_add_sha_data") + ((value ctx sexp) (value self sexp) sha_context sexp)) + +;; \procedure{(get-sha sha-context)} +;; +;; Finalizes computation and returns resulting SHA-2 digest as a hex +;; string (in lowercase). It is not possible to add more data with +;; \scheme{add-sha-data!} after this call. Though, digest string can +;; be retrieved multiple times from the same computation context. + +(define-c sexp (get-sha "sexp_get_sha") + ((value ctx sexp) (value self sexp) sha_context)) diff --git a/lib/chibi/crypto/integers.h b/lib/chibi/crypto/integers.h new file mode 100644 index 00000000..191bd0f4 --- /dev/null +++ b/lib/chibi/crypto/integers.h @@ -0,0 +1,28 @@ +#ifndef CHIBI_CRYPTO_INTEGERS_H +#define CHIBI_CRYPTO_INTEGERS_H + +#if __STDC_VERSION__ >= 199901L /* C99 */ +# include + typedef uint32_t sexp_uint32_t; + typedef uint8_t sexp_uint8_t; +#else +# include +# +# if UCHAR_MAX == 255 + typedef unsigned char sexp_uint8_t; +# else +# error Could not find 8-bit type +# endif +# +# if UINT_MAX == 4294967295U + typedef unsigned int sexp_uint32_t; +# elif ULONG_MAX == 4294967295UL + typedef unsigned long sexp_uint32_t; +# elif USHRT_MAX == 4294967295U + typedef unsigned short sexp_uint32_t; +# else +# error Could not find 32-bit type +# endif +#endif + +#endif /* CHIBI_CRYPTO_INTEGERS_H */ diff --git a/lib/chibi/crypto/sha2-native.scm b/lib/chibi/crypto/sha2-native.scm new file mode 100644 index 00000000..b3e94032 --- /dev/null +++ b/lib/chibi/crypto/sha2-native.scm @@ -0,0 +1,24 @@ +;; sha2-native.scm -- SHA-2 digest algorithms native interface +;; Copyright (c) 2015 Alexei Lozovsky. All rights reserved. +;; BSD-style license: http://synthcode.com/license.txt + +(define (procees-sha-data! context src) + (cond ((or (bytevector? src) (string? src)) + (add-sha-data! context src)) + ((input-port? src) + (let lp ((chunk (read-bytevector 1024 src))) + (unless (eof-object? chunk) + (add-sha-data! context chunk) + (lp (read-bytevector 1024 src))))) + (else + (error "unknown digest source: " src)))) + +(define (sha-224 src) + (let ((context (start-sha type-sha-224))) + (procees-sha-data! context src) + (get-sha context))) + +(define (sha-256 src) + (let ((context (start-sha type-sha-256))) + (procees-sha-data! context src) + (get-sha context))) diff --git a/lib/chibi/crypto/sha2.c b/lib/chibi/crypto/sha2.c new file mode 100644 index 00000000..f29c7047 --- /dev/null +++ b/lib/chibi/crypto/sha2.c @@ -0,0 +1,258 @@ +/* sha2.c -- SHA-2 digest algorithms native implementations */ +/* Copyright (c) 2015 Alexei Lozovsky. All rights reserved. */ +/* BSD-style license: http://synthcode.com/license.txt */ + +#include "integers.h" + +/* + * SHA-2 algorithms are described in RFC 6234: + * + * http://tools.ietf.org/html/rfc6234 + */ + +/* Initial hash vector for SHA-224 */ +static const sexp_uint32_t h224[8] = { + 0xC1059ED8UL, 0x367CD507UL, 0x3070DD17UL, 0xF70E5939UL, + 0xFFC00B31UL, 0x68581511UL, 0x64F98FA7UL, 0xBEFA4FA4UL, +}; + +/* Initial hash vector for SHA-256 */ +static const sexp_uint32_t h256[8] = { + 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL, + 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL, +}; + +/* Round constants for SHA-224/256 */ +static const sexp_uint32_t k256[64] = { + 0x428A2F98UL, 0x71374491UL, 0xB5C0FBCFUL, 0xE9B5DBA5UL, + 0x3956C25BUL, 0x59F111F1UL, 0x923F82A4UL, 0xAB1C5ED5UL, + 0xD807AA98UL, 0x12835B01UL, 0x243185BEUL, 0x550C7DC3UL, + 0x72BE5D74UL, 0x80DEB1FEUL, 0x9BDC06A7UL, 0xC19BF174UL, + 0xE49B69C1UL, 0xEFBE4786UL, 0x0FC19DC6UL, 0x240CA1CCUL, + 0x2DE92C6FUL, 0x4A7484AAUL, 0x5CB0A9DCUL, 0x76F988DAUL, + 0x983E5152UL, 0xA831C66DUL, 0xB00327C8UL, 0xBF597FC7UL, + 0xC6E00BF3UL, 0xD5A79147UL, 0x06CA6351UL, 0x14292967UL, + 0x27B70A85UL, 0x2E1B2138UL, 0x4D2C6DFCUL, 0x53380D13UL, + 0x650A7354UL, 0x766A0ABBUL, 0x81C2C92EUL, 0x92722C85UL, + 0xA2BFE8A1UL, 0xA81A664BUL, 0xC24B8B70UL, 0xC76C51A3UL, + 0xD192E819UL, 0xD6990624UL, 0xF40E3585UL, 0x106AA070UL, + 0x19A4C116UL, 0x1E376C08UL, 0x2748774CUL, 0x34B0BCB5UL, + 0x391C0CB3UL, 0x4ED8AA4AUL, 0x5B9CCA4FUL, 0x682E6FF3UL, + 0x748F82EEUL, 0x78A5636FUL, 0x84C87814UL, 0x8CC70208UL, + 0x90BEFFFAUL, 0xA4506CEBUL, 0xBEF9A3F7UL, 0xC67178F2UL, +}; + +/* Supported digest types */ +enum sha_type { + SHA_TYPE_224, + SHA_TYPE_256, + SHA_TYPE_MAX +}; + +/* Intermediate digest computation state */ +struct sha_context { + enum sha_type type; + char sealed; + sexp_uint_t len; + union _sha_data { + struct _sha_256 { + sexp_uint8_t buffer[64]; + sexp_uint32_t hash[8]; + } sha_256; + } data; +}; + +#define sha_256_buffer(c) ((c)->data.sha_256.buffer) +#define sha_256_hash(c) ((c)->data.sha_256.hash) + +/* = SHA-224/256 implementation ===================================== */ + +#define ror32(v, a) (((v) >> (a)) | ((v) << (32 - (a)))) + +static void sha_224_256_round (const sexp_uint8_t chunk[64], + sexp_uint32_t hash[8]) { + int i; + sexp_uint32_t w[64]; + sexp_uint32_t tmp1, tmp2; + sexp_uint32_t a, b, c, d, e, f, g, h; + /* Initialize schedule array */ + for (i = 0; i < 16; i++) { + w[i] = (chunk[4*i + 0] << 24) + | (chunk[4*i + 1] << 16) + | (chunk[4*i + 2] << 8) + | (chunk[4*i + 3] << 0); + } + for (i = 16; i < 64; i++) { + w[i] = w[i - 16] + + (ror32(w[i-15], 7) ^ ror32(w[i-15], 18) ^ (w[i-15] >> 3)) + + w[i - 7] + + (ror32(w[i-2], 17) ^ ror32(w[i-2], 19) ^ (w[i-2] >> 10)); + } + /* Initialize working variables */ + a = hash[0]; b = hash[1]; c = hash[2]; d = hash[3]; + e = hash[4]; f = hash[5]; g = hash[6]; h = hash[7]; + /* Main loop */ + for (i = 0; i < 64; i++) { + tmp1 = h + + (ror32(e, 6) ^ ror32(e, 11) ^ ror32(e, 25)) + + ((e & f) ^ ((~e) & g)) + + k256[i] + + w[i]; + tmp2 = (ror32(a, 2) ^ ror32(a, 13) ^ ror32(a, 22)) + + ((a & b) ^ (a & c) ^ (b & c)); + h = g; g = f; f = e; e = d + tmp1; + d = c; c = b; b = a; a = tmp1 + tmp2; + } + /* Update hash values */ + hash[0] += a; hash[1] += b; hash[2] += c; hash[3] += d; + hash[4] += e; hash[5] += f; hash[6] += g; hash[7] += h; +} + +static void sha_224_256_remainder (sexp_uint8_t chunk[64], sexp_uint_t offset, + sexp_uint_t len_bits, sexp_uint32_t hash[8]) { + int i; + /* Pad with '1' bit and zeros */ + chunk[offset] = 0x80; + memset(chunk + offset + 1, 0, 64 - offset - 1); + /* If we can't fit the length, use an additional chunk */ + if (offset >= 56) { + sha_224_256_round(chunk, hash); + memset(chunk, 0, 64); + } + /* Append the message length in bits as big-endian 64-bit integer */ + for (i = 63; i >= 56; i--) { + chunk[i] = len_bits & 0xFF; + len_bits >>= 8; + } + sha_224_256_round(chunk, hash); +} + +/* = Allocating computation context ================================= */ + +sexp sexp_start_sha (sexp ctx, sexp self, unsigned type, struct sha_context* v) { + sexp res; + struct sha_context *sha; + sexp_uint_t sha_context_tag; + if (type >= SHA_TYPE_MAX) + return sexp_xtype_exception(ctx, self, "SHA-2 digest type not supported", + sexp_make_fixnum(type)); + (void)v; /* We receive this phony argument to access the type tag of + sha_context and still be able to return an error with Chibi FFI */ + sha_context_tag = sexp_unbox_fixnum(sexp_vector_ref(sexp_opcode_argn_type(self), SEXP_ZERO)); + res = sexp_alloc_tagged(ctx, sexp_sizeof(cpointer), sha_context_tag); + if (sexp_exceptionp(res)) + return res; + sha = calloc(1, sizeof(*sha)); + sha->type = type; + switch (type) { + case SHA_TYPE_224: + memcpy(sha_256_hash(sha), h224, sizeof(h224)); + break; + case SHA_TYPE_256: + memcpy(sha_256_hash(sha), h256, sizeof(h256)); + break; + default: + break; + } + sexp_cpointer_value(res) = sha; + sexp_freep(res) = 1; + return res; +} + +/* = Processing incoming data ======================================= */ + +static sexp sha_224_256_add_bytes (struct sha_context *sha, + const sexp_uint8_t *src, sexp_uint_t len) { + sexp_uint_t src_offset, buf_offset; + /* Realign (src + src_offset) to 64 bytes */ + src_offset = 0; + buf_offset = sha->len % 64; + sha->len += len; + if (buf_offset) { + while ((buf_offset < 64) && (src_offset < len)) + sha_256_buffer(sha)[buf_offset++] = src[src_offset++]; + if (buf_offset == 64) + sha_224_256_round(sha_256_buffer(sha), sha_256_hash(sha)); + else + return SEXP_VOID; + } + /* Process whole chunks without copying them */ + if (len >= 64) { + for ( ; src_offset <= (len - 64); src_offset += 64) + sha_224_256_round(src + src_offset, sha_256_hash(sha)); + } + /* Copy the remainder into the buffer */ + if (src_offset < len) + memcpy(sha_256_buffer(sha) + buf_offset, src + src_offset, len - src_offset); + return SEXP_VOID; +} + +static sexp sha_add_bytes (sexp ctx, sexp self, struct sha_context *sha, + const char* data, sexp_uint_t len) { + switch (sha->type) { + case SHA_TYPE_224: + case SHA_TYPE_256: + return sha_224_256_add_bytes(sha, (const sexp_uint8_t*) data, len); + default: + return sexp_xtype_exception(ctx, self, "unexpected context type", + sexp_make_fixnum(sha->type)); + } +} + +sexp sexp_add_sha_data (sexp ctx, sexp self, struct sha_context *sha, sexp data) { + if (sha->sealed) + return sexp_xtype_exception(ctx, self, "cannot add to sealed context", data); + if (sexp_bytesp(data)) + return sha_add_bytes(ctx, self, sha, sexp_bytes_data(data), sexp_bytes_length(data)); + if (sexp_stringp(data)) + return sha_add_bytes(ctx, self, sha, sexp_string_data(data), sexp_string_size(data)); + return sexp_xtype_exception(ctx, self, "data type not supported", data); +} + +/* = Extracting computed digest ===================================== */ + +static const char *hex = "0123456789abcdef"; + +static sexp sha_224_256_hash_string (sexp ctx, sexp self, + const sexp_uint32_t hash[8], int count) { + sexp res; + int i, j; + sexp_uint32_t next_word; + /* Allocate a string of target length */ + res = sexp_make_string(ctx, sexp_make_fixnum(count * 8), SEXP_VOID); + if (sexp_exceptionp(res)) + return res; + /* Write 32-bit words as nibbles in big-endian order */ + for (i = 0; i < count; i++) { + next_word = hash[i]; + for (j = 7; j >= 0; j--) { + sexp_string_data(res)[8*i + j] = hex[next_word & 0xF]; + next_word >>= 4; + } + } + return res; +} + +sexp sexp_get_sha (sexp ctx, sexp self, struct sha_context *sha) { + if (!sha->sealed) { + sha->sealed = 1; + switch (sha->type) { + case SHA_TYPE_224: + case SHA_TYPE_256: + sha_224_256_remainder(sha_256_buffer(sha), sha->len % 64, + sha->len * 8, sha_256_hash(sha)); + break; + default: + break; + } + } + switch (sha->type) { + case SHA_TYPE_224: + return sha_224_256_hash_string(ctx, self, sha_256_hash(sha), 7); + case SHA_TYPE_256: + return sha_224_256_hash_string(ctx, self, sha_256_hash(sha), 8); + default: + return sexp_xtype_exception(ctx, self, "unexpected context type", + sexp_make_fixnum(sha->type)); + } +} From 37d808e4705d2e3d2d13f3513ddddaaaf0097232 Mon Sep 17 00:00:00 2001 From: ilammy Date: Sat, 18 Apr 2015 13:40:36 +0300 Subject: [PATCH 3/7] chibi.crypto: more tests for SHA-2 * Boundary cases Both SHA-224 and SHA-256 use 512-bit data chunks and have a special behavior when chunk size is near the 448-bit boundary. * Source type support Basic smoke tests for accepting bytevectors and binary input ports as valid arguments. --- lib/chibi/crypto/sha2-test.sld | 62 +++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/lib/chibi/crypto/sha2-test.sld b/lib/chibi/crypto/sha2-test.sld index 3fc853ac..f9cf255a 100644 --- a/lib/chibi/crypto/sha2-test.sld +++ b/lib/chibi/crypto/sha2-test.sld @@ -1,6 +1,6 @@ (define-library (chibi crypto sha2-test) (export run-tests) - (import (chibi) (chibi crypto sha2) (chibi test)) + (import (chibi) (chibi io) (chibi crypto sha2) (chibi test)) (begin (define (run-tests) (test-begin "sha2") @@ -10,6 +10,36 @@ (sha-224 "abc")) (test "730e109bd7a8a32b1cb9d9a09aa2325d2430587ddbc0c38bad911525" (sha-224 "The quick brown fox jumps over the lazy dog")) + (test "7c9da3bf97ccdeee630639aacdce35d3c136e514332a28e67097a4a4" + (sha-224 "Boundary test for 448 bits (-1) - 012345678901234567890")) + (test "35aebce593c857a2c817428340ff465922ffe43ed076d24553db1a24" + (sha-224 "Boundary test for 448 bits (0) - 0123456789012345678901")) + (test "3f8dbeb9c33981d7007e20641d506d048e89e98a9546ecccc3224d3b" + (sha-224 "Boundary test for 448 bits (+1) - 01234567890123456789012")) + (test "8b311209d5880800911d3e72ffe7e75ec33a6e83932d5cdd00c96327" + (sha-224 "Boundary test for 512 bits (-1) - 01234567890123456789012345678")) + (test "9b68fdc122e1cb38575ba97f54699d71eaf0e58ee88f9e653b31d6ce" + (sha-224 "Boundary test for 512 bits (0) - 012345678901234567890123456789")) + (test "52b28e31226ee5e6ada43e33194e11d8015abf8b5511c1631ad11aea" + (sha-224 "Boundary test for 512 bits (+1) - 0123456789012345678901234567890")) + (test "aa85fe2924d9c259f92e154fa88d0c845654fe69aa7dc1e3f7e4c789" + (sha-224 "Boundary test for 960 bits (-1) - 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234")) + (test "dd8af6abfe24e78065afd1ae06220e8d46401db13f202109770ca2d2" + (sha-224 "Boundary test for 960 bits (0) - 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345")) + (test "5299a41ce9c6e8b405f42b193922fb4af3da16a1519610057baca20f" + (sha-224 "Boundary test for 960 bits (+1) - 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456")) + (test "cb88e45dc662233ef4e7171e9e1c4903bd6502dd25923105778ea82e" + (sha-224 "Boundary test for 1024 bits (-1) - 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901")) + (test "f41c907a7fd2fa3aec70815669fe467760f4fd15763a75192d2c9f45" + (sha-224 "Boundary test for 1024 bits (0) - 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012")) + (test "cc1501345f86b1ef60eaf3637f7a37c38c63252b5674d343a3cc4aea" + (sha-224 "Boundary test for 1024 bits (+1) - 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123")) + (test "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f" + (sha-224 #u8())) + (test "ae40be26ae2072dd84f37c13a5f6af48e3c33ea1c08a5ef4a54b22e3" + (sha-224 #u8(1 2 3 4 5 6 7 8 9))) + (test "54e5eb52479c241cc4759318619f548994ae46979124cb9b1435db14" + (sha-224 (open-input-bytevector #u8(1 2 3 9)))) (test "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" (sha-256 "")) (test "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" @@ -20,4 +50,34 @@ (sha-256 "abcdbcdecdefdefgefghfghighijhijkijkljklmklm")) (test "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1" (sha-256 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")) + (test "f904e41d6488bc982a929e1f9307d9b47f12e6cc01ab42d109b083a780dbb70a" + (sha-256 "Boundary test for 448 bits (-1) - 012345678901234567890")) + (test "4621c7c067a12951ed5b0339a6c6811aec2dea4adcb2dcbb1383868765dbbc21" + (sha-256 "Boundary test for 448 bits (0) - 0123456789012345678901")) + (test "a62bd24e12494c5a213dc366fec9d79e2bd77789febf6b1437191f264ad0a7fe" + (sha-256 "Boundary test for 448 bits (+1) - 01234567890123456789012")) + (test "2c47adeb018cd5634aa3c121bf0e6d122789448568814e7243b19b6c26ac4860" + (sha-256 "Boundary test for 512 bits (-1) - 01234567890123456789012345678")) + (test "eb1018cf7e5f40ba45a711c4154584234e2194f10cc6fa7559a438bed9e4a388" + (sha-256 "Boundary test for 512 bits (0) - 012345678901234567890123456789")) + (test "714f030e4971ade8976564693a8fe202ca357e87cb1cb7391a9af3c45590f7c0" + (sha-256 "Boundary test for 512 bits (+1) - 0123456789012345678901234567890")) + (test "a745d68a9999da92558757735428346439e2af5668b188e9e4da7935e318335b" + (sha-256 "Boundary test for 960 bits (-1) - 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234")) + (test "f2d7ad79e0360fbad145dd551db33548dc7cd253e6c56c975f2820e4c99dee51" + (sha-256 "Boundary test for 960 bits (0) - 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345")) + (test "9f0378e0ba55965bd17232f994710b786e9d72a88a806c0b10cd9d36a06e41ed" + (sha-256 "Boundary test for 960 bits (+1) - 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456")) + (test "483a36ca7824cc0d9bff2d63901301ba8ca7deb675628c71d8a08d52a0396cfe" + (sha-256 "Boundary test for 1024 bits (-1) - 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901")) + (test "8bd16f15e5f1b753650753497d09e1956137fba0cb2162a61dc6a2b49c7fcda3" + (sha-256 "Boundary test for 1024 bits (0) - 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012")) + (test "c6c960e1c106d214e82d58c12c44adb000903d2022ea2ce239f273294d3055e5" + (sha-256 "Boundary test for 1024 bits (+1) - 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123")) + (test "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + (sha-256 #u8())) + (test "47e4ee7f211f73265dd17658f6e21c1318bd6c81f37598e20a2756299542efcf" + (sha-256 #u8(1 2 3 4 5 6 7 8 9))) + (test "a745f3ca4f474d583c050eaf476ce76439d171ebe2b49d4af8b44f13ba71fb56" + (sha-256 (open-input-bytevector #u8(1 2 3 9)))) (test-end)))) From 1f8c0088a70a76167b0a5a8a8818293b3ab2692b Mon Sep 17 00:00:00 2001 From: ilammy Date: Sat, 18 Apr 2015 14:42:33 +0300 Subject: [PATCH 4/7] chibi.crypto: fix formatting bug in portable SHA-2 We can't use 'integer->hex-string' alone to print out SHA-224/256 digest because it rightly converts #x00001234 into "1234", while we need to keep the padding zero nibbles and get "00001234". 'hex' got renamed into 'hex32' because SHA-512 will need some different 'hex64' which returns 16-character-long strings. --- lib/chibi/crypto/sha2.scm | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/chibi/crypto/sha2.scm b/lib/chibi/crypto/sha2.scm index 2378b0e6..fa5af55f 100644 --- a/lib/chibi/crypto/sha2.scm +++ b/lib/chibi/crypto/sha2.scm @@ -40,7 +40,11 @@ (u32 (arithmetic-shift n (- 32 k))) (arithmetic-shift n (- k)))) -(define hex integer->hex-string) +(define (hex32 num) + (let ((res (make-string 8 #\0)) + (num (integer->hex-string num))) + (string-copy! res (- 8 (string-length num)) num) + res)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -152,8 +156,8 @@ (if (>= n 56) (chunk (+ i n) 0 a b c d e f g h) (string-append - (hex a) (hex b) (hex c) (hex d) - (hex e) (hex f) (hex g) (if full? (hex h) "")))) + (hex32 a) (hex32 b) (hex32 c) (hex32 d) + (hex32 e) (hex32 f) (hex32 g) (if full? (hex32 h) "")))) (else (chunk (+ i 64) pad a b c d e f g h))))) (else From 9088b1954c483ecc53528965869c3a52b2173018 Mon Sep 17 00:00:00 2001 From: ilammy Date: Sat, 18 Apr 2015 15:41:49 +0300 Subject: [PATCH 5/7] chibi.crypto: make Chibi use native SHA-2 by default This change concerns only Chibi. The portable implementation is still kept around because it is... well... portable and can be used by other Scheme implementations. --- lib/chibi/crypto/sha2.sld | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/chibi/crypto/sha2.sld b/lib/chibi/crypto/sha2.sld index 7079d727..6aa17072 100644 --- a/lib/chibi/crypto/sha2.sld +++ b/lib/chibi/crypto/sha2.sld @@ -1,8 +1,14 @@ (define-library (chibi crypto sha2) - (import (scheme base) (srfi 33) (chibi bytevector)) (export sha-224 sha-256) - (include "sha2.scm")) + (cond-expand + (chibi + (import (scheme base)) + (include "sha2-native.scm") + (include-shared "crypto")) + (else + (import (scheme base) (srfi 33) (chibi bytevector)) + (include "sha2.scm")))) ;;> \procedure{(sha-224 src)} ;;> From db2b598cdeeec1197106e47ced4aae759c1a2455 Mon Sep 17 00:00:00 2001 From: ilammy Date: Sun, 19 Apr 2015 13:16:43 +0300 Subject: [PATCH 6/7] chibi.crypto: code style fixes - Fixed some typos in sha-native.scm - Removed unnecessary structs and unions from sha_context - Used more efficient implementation of hex32 - Made (scheme base) a common import in (chibi crypto sha2) --- lib/chibi/crypto/sha2-native.scm | 6 +++--- lib/chibi/crypto/sha2.c | 31 ++++++++++++------------------- lib/chibi/crypto/sha2.scm | 9 +++++---- lib/chibi/crypto/sha2.sld | 4 ++-- 4 files changed, 22 insertions(+), 28 deletions(-) diff --git a/lib/chibi/crypto/sha2-native.scm b/lib/chibi/crypto/sha2-native.scm index b3e94032..f6dafc99 100644 --- a/lib/chibi/crypto/sha2-native.scm +++ b/lib/chibi/crypto/sha2-native.scm @@ -2,7 +2,7 @@ ;; Copyright (c) 2015 Alexei Lozovsky. All rights reserved. ;; BSD-style license: http://synthcode.com/license.txt -(define (procees-sha-data! context src) +(define (process-sha-data! context src) (cond ((or (bytevector? src) (string? src)) (add-sha-data! context src)) ((input-port? src) @@ -15,10 +15,10 @@ (define (sha-224 src) (let ((context (start-sha type-sha-224))) - (procees-sha-data! context src) + (process-sha-data! context src) (get-sha context))) (define (sha-256 src) (let ((context (start-sha type-sha-256))) - (procees-sha-data! context src) + (process-sha-data! context src) (get-sha context))) diff --git a/lib/chibi/crypto/sha2.c b/lib/chibi/crypto/sha2.c index f29c7047..c0a9c8a3 100644 --- a/lib/chibi/crypto/sha2.c +++ b/lib/chibi/crypto/sha2.c @@ -54,17 +54,10 @@ struct sha_context { enum sha_type type; char sealed; sexp_uint_t len; - union _sha_data { - struct _sha_256 { - sexp_uint8_t buffer[64]; - sexp_uint32_t hash[8]; - } sha_256; - } data; + sexp_uint32_t hash256[8]; + sexp_uint8_t buffer[128]; /* enough for all SHA-2 */ }; -#define sha_256_buffer(c) ((c)->data.sha_256.buffer) -#define sha_256_hash(c) ((c)->data.sha_256.hash) - /* = SHA-224/256 implementation ===================================== */ #define ror32(v, a) (((v) >> (a)) | ((v) << (32 - (a)))) @@ -146,10 +139,10 @@ sexp sexp_start_sha (sexp ctx, sexp self, unsigned type, struct sha_context* v) sha->type = type; switch (type) { case SHA_TYPE_224: - memcpy(sha_256_hash(sha), h224, sizeof(h224)); + memcpy(sha->hash256, h224, sizeof(h224)); break; case SHA_TYPE_256: - memcpy(sha_256_hash(sha), h256, sizeof(h256)); + memcpy(sha->hash256, h256, sizeof(h256)); break; default: break; @@ -170,20 +163,20 @@ static sexp sha_224_256_add_bytes (struct sha_context *sha, sha->len += len; if (buf_offset) { while ((buf_offset < 64) && (src_offset < len)) - sha_256_buffer(sha)[buf_offset++] = src[src_offset++]; + sha->buffer[buf_offset++] = src[src_offset++]; if (buf_offset == 64) - sha_224_256_round(sha_256_buffer(sha), sha_256_hash(sha)); + sha_224_256_round(sha->buffer, sha->hash256); else return SEXP_VOID; } /* Process whole chunks without copying them */ if (len >= 64) { for ( ; src_offset <= (len - 64); src_offset += 64) - sha_224_256_round(src + src_offset, sha_256_hash(sha)); + sha_224_256_round(src + src_offset, sha->hash256); } /* Copy the remainder into the buffer */ if (src_offset < len) - memcpy(sha_256_buffer(sha) + buf_offset, src + src_offset, len - src_offset); + memcpy(sha->buffer + buf_offset, src + src_offset, len - src_offset); return SEXP_VOID; } @@ -239,8 +232,8 @@ sexp sexp_get_sha (sexp ctx, sexp self, struct sha_context *sha) { switch (sha->type) { case SHA_TYPE_224: case SHA_TYPE_256: - sha_224_256_remainder(sha_256_buffer(sha), sha->len % 64, - sha->len * 8, sha_256_hash(sha)); + sha_224_256_remainder(sha->buffer, sha->len % 64, + sha->len * 8, sha->hash256); break; default: break; @@ -248,9 +241,9 @@ sexp sexp_get_sha (sexp ctx, sexp self, struct sha_context *sha) { } switch (sha->type) { case SHA_TYPE_224: - return sha_224_256_hash_string(ctx, self, sha_256_hash(sha), 7); + return sha_224_256_hash_string(ctx, self, sha->hash256, 7); case SHA_TYPE_256: - return sha_224_256_hash_string(ctx, self, sha_256_hash(sha), 8); + return sha_224_256_hash_string(ctx, self, sha->hash256, 8); default: return sexp_xtype_exception(ctx, self, "unexpected context type", sexp_make_fixnum(sha->type)); diff --git a/lib/chibi/crypto/sha2.scm b/lib/chibi/crypto/sha2.scm index fa5af55f..1aacfacc 100644 --- a/lib/chibi/crypto/sha2.scm +++ b/lib/chibi/crypto/sha2.scm @@ -41,10 +41,11 @@ (arithmetic-shift n (- k)))) (define (hex32 num) - (let ((res (make-string 8 #\0)) - (num (integer->hex-string num))) - (string-copy! res (- 8 (string-length num)) num) - res)) + (let* ((res (number->string num 16)) + (len (string-length res))) + (if (>= len 8) + res + (string-append (make-string (- 8 len) #\0) res)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/lib/chibi/crypto/sha2.sld b/lib/chibi/crypto/sha2.sld index 6aa17072..f1d4d797 100644 --- a/lib/chibi/crypto/sha2.sld +++ b/lib/chibi/crypto/sha2.sld @@ -1,13 +1,13 @@ (define-library (chibi crypto sha2) + (import (scheme base)) (export sha-224 sha-256) (cond-expand (chibi - (import (scheme base)) (include "sha2-native.scm") (include-shared "crypto")) (else - (import (scheme base) (srfi 33) (chibi bytevector)) + (import (srfi 33) (chibi bytevector)) (include "sha2.scm")))) ;;> \procedure{(sha-224 src)} From a6ca2e39dc58764b2eee673fa14d6afbb1fc5203 Mon Sep 17 00:00:00 2001 From: ilammy Date: Sun, 19 Apr 2015 15:37:47 +0300 Subject: [PATCH 7/7] chibi.crypto: move sexp_uintN_t typedefs to First we check for C99 support in Makefile.detect, looking for the header we need and verifying whether it is the right one by using a definition required by C99 standard to be present in that header. uintN_t types are optional, but implementations are required to provide corresponding limit #defines for the types they support, so we can check for this with preprocessor only. Finally, we define SEXP_UINTN_DEFINED for any sexp_uintN_t we have so that the code can use #ifs to check for exact integer support. --- Makefile.detect | 10 +++++++++- include/chibi/sexp.h | 28 ++++++++++++++++++++++++++++ lib/chibi/crypto/integers.h | 28 ---------------------------- lib/chibi/crypto/sha2.c | 4 +++- 4 files changed, 40 insertions(+), 30 deletions(-) delete mode 100644 lib/chibi/crypto/integers.h diff --git a/Makefile.detect b/Makefile.detect index 7b786442..83994c72 100644 --- a/Makefile.detect +++ b/Makefile.detect @@ -108,7 +108,7 @@ RLDFLAGS=-Wl,-R$(DESTDIR)$(LIBDIR) endif ######################################################################## -# Check for NTP (who needs autoconf?) +# Check for headers (who needs autoconf?) ifndef $(SEXP_USE_NTP_GETTIME) SEXP_USE_NTP_GETTIME := $(shell echo "main(){struct ntptimeval n; ntp_gettime(&n);}" | gcc -fsyntax-only -include sys/timex.h -xc - >/dev/null 2>/dev/null && echo 1 || echo 0) @@ -117,3 +117,11 @@ endif ifeq ($(SEXP_USE_NTP_GETTIME),1) CPPFLAGS += -DSEXP_USE_NTPGETTIME endif + +ifndef $(SEXP_USE_INTTYPES) +SEXP_USE_INTTYPES := $(shell echo "main(){int_least8_t x;}" | gcc -fsyntax-only -include inttypes.h -xc - >/dev/null 2>/dev/null && echo 1 || echo 0) +endif + +ifeq ($(SEXP_USE_INTTYPES),1) +CPPFLAGS += -DSEXP_USE_INTTYPES +endif diff --git a/include/chibi/sexp.h b/include/chibi/sexp.h index f21ac4f9..af618d0e 100755 --- a/include/chibi/sexp.h +++ b/include/chibi/sexp.h @@ -195,6 +195,34 @@ typedef int sexp_sint_t; #define sexp_heap_align(n) sexp_align(n, 4) #endif +#ifdef SEXP_USE_INTTYPES +# include +# ifdef UINT8_MAX +# define SEXP_UINT8_DEFINED 1 +typedef uint8_t sexp_uint8_t; +# endif +# ifdef UINT32_MAX +# define SEXP_UINT32_DEFINED 1 +typedef uint32_t sexp_uint32_t; +# endif +#else +# include +# if UCHAR_MAX == 255 +# define SEXP_UINT8_DEFINED 1 +typedef unsigned char sexp_uint8_t; +# endif +# if UINT_MAX == 4294967295U +# define SEXP_UINT32_DEFINED 1 +typedef unsigned int sexp_uint32_t; +# elif ULONG_MAX == 4294967295UL +# define SEXP_UINT32_DEFINED 1 +typedef unsigned long sexp_uint32_t; +# elif USHRT_MAX == 4294967295U +# define SEXP_UINT32_DEFINED 1 +typedef unsigned short sexp_uint32_t; +# endif +#endif + #if SEXP_USE_LONG_PROCEDURE_ARGS typedef int sexp_proc_num_args_t; #else diff --git a/lib/chibi/crypto/integers.h b/lib/chibi/crypto/integers.h deleted file mode 100644 index 191bd0f4..00000000 --- a/lib/chibi/crypto/integers.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef CHIBI_CRYPTO_INTEGERS_H -#define CHIBI_CRYPTO_INTEGERS_H - -#if __STDC_VERSION__ >= 199901L /* C99 */ -# include - typedef uint32_t sexp_uint32_t; - typedef uint8_t sexp_uint8_t; -#else -# include -# -# if UCHAR_MAX == 255 - typedef unsigned char sexp_uint8_t; -# else -# error Could not find 8-bit type -# endif -# -# if UINT_MAX == 4294967295U - typedef unsigned int sexp_uint32_t; -# elif ULONG_MAX == 4294967295UL - typedef unsigned long sexp_uint32_t; -# elif USHRT_MAX == 4294967295U - typedef unsigned short sexp_uint32_t; -# else -# error Could not find 32-bit type -# endif -#endif - -#endif /* CHIBI_CRYPTO_INTEGERS_H */ diff --git a/lib/chibi/crypto/sha2.c b/lib/chibi/crypto/sha2.c index c0a9c8a3..fe0d14f3 100644 --- a/lib/chibi/crypto/sha2.c +++ b/lib/chibi/crypto/sha2.c @@ -2,7 +2,9 @@ /* Copyright (c) 2015 Alexei Lozovsky. All rights reserved. */ /* BSD-style license: http://synthcode.com/license.txt */ -#include "integers.h" +#if !(SEXP_UINT8_DEFINED && SEXP_UINT32_DEFINED) +# error SHA-2 requires exact 8-bit and 32-bit integers to be available +#endif /* * SHA-2 algorithms are described in RFC 6234: