commit 322536d2f9d30f42218cc9f2ab40574557da8a9a Author: Gaurav Shah Date: Thu Jan 28 15:01:23 2010 -0800 RSA signature verification and SHA-1/256/512 reference implementation for verified boot. Also contains some preliminary tests for these primitives. Review URL: http://codereview.chromium.org/553023 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..55f480367b --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +export CC=gcc +export CFLAGS=-Wall -ansi +export TOP=$(shell pwd) +export INCLUDEDIR=$(TOP)/include +export INCLUDES=-I$(INCLUDEDIR) + +SUBDIRS=common crypto utils tests + +all: + for i in $(SUBDIRS); do \ + ( cd $$i ; $(MAKE)) ; \ + done + +clean: + for i in $(SUBDIRS); do \ + ( cd $$i ; make clean) ; \ + done diff --git a/common/Makefile b/common/Makefile new file mode 100644 index 0000000000..51d482e206 --- /dev/null +++ b/common/Makefile @@ -0,0 +1,17 @@ +# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +SRCS=utility_stub.c +OBJS=$(SRCS:.c=.o) + +all: libcommon.a + +libcommon.a: $(OBJS) + ar rs $@ $< + +.c.o: $(OBJS) + $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ + +clean: + rm -f $(OBJS) libcommon.a diff --git a/common/utility_stub.c b/common/utility_stub.c new file mode 100644 index 0000000000..14a5910fba --- /dev/null +++ b/common/utility_stub.c @@ -0,0 +1,44 @@ +/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Stub implementations of utility functions which call their linux-specific + * equivalents. + */ + +#include "utility.h" + +#include +#include +#include + +void* Malloc(size_t size) { + void* p = malloc(size); + if (!p) { + /* Fatal Error. We must abort. */ + abort(); + } + return p; +} + +void Free(void* ptr) { + free(ptr); +} + +void* Memcpy(void* dest, const void* src, size_t n) { + return memcpy(dest, src, n); +} + +int SafeMemcmp(const void* s1, const void* s2, size_t n) { + int match = 1; + const unsigned char* us1 = s1; + const unsigned char* us2 = s2; + while (n--) { + if (*us1++ != *us2++) + match = 0; + else + match = 1; + } + + return match; +} diff --git a/crypto/Makefile b/crypto/Makefile new file mode 100644 index 0000000000..201085e96b --- /dev/null +++ b/crypto/Makefile @@ -0,0 +1,20 @@ +# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +SRCS=rsa.c sha1.c sha2.c padding.c +OBJS=$(SRCS:.c=.o) + +all: libcrypto.a + +libcrypto.a: $(OBJS) + ar rs libcrypto.a $(OBJS) + +padding.c: genpadding.sh + ./genpadding.sh >$@ + +.c.o: $(OBJS) + $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ + +clean: + rm -f $(OBJS) libcrypto.a diff --git a/crypto/genpadding.sh b/crypto/genpadding.sh new file mode 100755 index 0000000000..e51e457c23 --- /dev/null +++ b/crypto/genpadding.sh @@ -0,0 +1,169 @@ +#!/bin/bash + +# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Script to generate padding.c containing PKCS 1.5 padding byte arrays for +# various combinations of RSA key lengths and message digest algorithms. + +Pad_Preamble="0x00,0x01" + +SHA1_Suffix="0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05"\ +",0x00,0x04,0x14" +SHA256_Suffix="0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03"\ +",0x04,0x02,0x01,0x05,0x00,0x04,0x20" +SHA512_Suffix="0x30,0x51,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03"\ +",0x04,0x02,0x03,0x05,0x00,0x04,0x40" + +RSA1024_Len=128 +RSA2048_Len=256 +RSA4096_Len=512 +RSA8192_Len=1024 + +SHA1_T_Len=35 +SHA256_T_Len=51 +SHA512_T_Len=83 + +HashAlgos=( SHA1 SHA256 SHA512 ) +RSAAlgos=( RSA1024 RSA2048 RSA4096 RSA8192 ) + +function genFFOctets { + count=$1 + while [ $count -gt 0 ]; do + echo -n "0xff," + let count=count-1 + done +} + + +cat < + +#include "padding.h" +#include "rsa.h" +#include "utility.h" + +/* a[] -= mod */ +static void subM(const RSAPublicKey *key, uint32_t *a) { + int64_t A = 0; + int i; + for (i = 0; i < key->len; ++i) { + A += (uint64_t)a[i] - key->n[i]; + a[i] = (uint32_t)A; + A >>= 32; + } +} + +/* return a[] >= mod */ +static int geM(const RSAPublicKey *key, uint32_t *a) { + int i; + for (i = key->len; i;) { + --i; + if (a[i] < key->n[i]) return 0; + if (a[i] > key->n[i]) return 1; + } + return 1; /* equal */ + } + +/* montgomery c[] += a * b[] / R % mod */ +static void montMulAdd(const RSAPublicKey *key, + uint32_t* c, + const uint32_t a, + const uint32_t* b) { + uint64_t A = (uint64_t)a * b[0] + c[0]; + uint32_t d0 = (uint32_t)A * key->n0inv; + uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A; + int i; + + for (i = 1; i < key->len; ++i) { + A = (A >> 32) + (uint64_t)a * b[i] + c[i]; + B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A; + c[i - 1] = (uint32_t)B; + } + + A = (A >> 32) + (B >> 32); + + c[i - 1] = (uint32_t)A; + + if (A >> 32) { + subM(key, c); + } +} + +/* montgomery c[] = a[] * b[] / R % mod */ +static void montMul(const RSAPublicKey *key, + uint32_t* c, + uint32_t* a, + uint32_t* b) { + int i; + for (i = 0; i < key->len; ++i) { + c[i] = 0; + } + for (i = 0; i < key->len; ++i) { + montMulAdd(key, c, a[i], b); + } +} + +/* In-place public exponentiation. (65537} + * Input and output big-endian byte array in inout. + */ +static void modpowF4(const RSAPublicKey *key, + uint8_t* inout) { + uint32_t* a = (uint32_t*) Malloc(key->len * sizeof(uint32_t)); + uint32_t* aR = (uint32_t*) Malloc(key->len * sizeof(uint32_t)); + uint32_t* aaR = (uint32_t*) Malloc(key->len * sizeof(uint32_t)); + + uint32_t* aaa = aaR; /* Re-use location. */ + int i; + + /* Convert from big endian byte array to little endian word array. */ + for (i = 0; i < key->len; ++i) { + uint32_t tmp = + (inout[((key->len - 1 - i) * 4) + 0] << 24) | + (inout[((key->len - 1 - i) * 4) + 1] << 16) | + (inout[((key->len - 1 - i) * 4) + 2] << 8) | + (inout[((key->len - 1 - i) * 4) + 3] << 0); + a[i] = tmp; + } + + montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M */ + for (i = 0; i < 16; i+=2) { + montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */ + montMul(key, aR, aaR, aaR); /* aR = aaR * aaR / R mod M */ + } + montMul(key, aaa, aR, a); /* aaa = aR * a / R mod M */ + + + /* Make sure aaa < mod; aaa is at most 1x mod too large. */ + if (geM(key, aaa)) { + subM(key, aaa); + } + + /* Convert to bigendian byte array */ + for (i = key->len - 1; i >= 0; --i) { + uint32_t tmp = aaa[i]; + *inout++ = tmp >> 24; + *inout++ = tmp >> 16; + *inout++ = tmp >> 8; + *inout++ = tmp >> 0; + } + + Free(a); + Free(aR); + Free(aaR); +} + +/* Verify a RSA PKCS1.5 signature against an expected hash. + * Returns 0 on failure, 1 on success. + */ +int RSA_verify(const RSAPublicKey *key, + const uint8_t *sig, + const int sig_len, + const uint8_t sig_type, + const uint8_t *hash) { + int i; + uint8_t* buf; + const uint8_t* padding; + int success = 1; + + if (sig_len != (key->len * sizeof(uint32_t))) { + fprintf(stderr, "Signature is of incorrect length!\n"); + return 0; + } + + if (sig_type >= kNumAlgorithms) { + fprintf(stderr, "Invalid signature type!\n"); + return 0; + } + + if (key->len != siglen_map[sig_type]) { + fprintf(stderr, "Wrong key passed in!\n"); + return 0; + } + + buf = (uint8_t*) Malloc(sig_len); + Memcpy(buf, sig, sig_len); + + modpowF4(key, buf); + + /* Determine padding to use depending on the signature type. */ + padding = padding_map[sig_type]; + + /* Check pkcs1.5 padding bytes. */ + for (i = 0; i < padding_size_map[sig_type]; ++i) { + if (buf[i] != padding[i]) { +#ifndef NDEBUG +/* TODO(gauravsh): Replace with a macro call for logging. */ + fprintf(stderr, "Padding: Expecting = %02x Got = %02x\n", padding[i], + buf[i]); +#endif + success = 0; + } + } + + /* Check if digest matches. */ + for (; i < sig_len; ++i) { + if (buf[i] != *hash++) { +#ifndef NDEBUG +/* TODO(gauravsh): Replace with a macro call for logging. */ + fprintf(stderr, "Digest: Expecting = %02x Got = %02x\n", padding[i], + buf[i]); +#endif + success = 0; + } + } + + Free(buf); + + return success; +} diff --git a/crypto/sha1.c b/crypto/sha1.c new file mode 100644 index 0000000000..d19a5a2d8b --- /dev/null +++ b/crypto/sha1.c @@ -0,0 +1,289 @@ +/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* SHA-1 implementation largely based on libmincrypt in the the Android + * Open Source Project (platorm/system/core.git/libmincrypt/sha.c + */ + +#include "sha.h" + +/* Some machines lack byteswap.h and endian.h. These have to use the + * slower code, even if they're little-endian. + */ + +#if defined(HAVE_ENDIAN_H) && defined(HAVE_LITTLE_ENDIAN) + +#include +#include + +/* This version is about 28% faster than the generic version below, + * but assumes little-endianness. + */ +static inline uint32_t ror27(uint32_t val) { + return (val >> 27) | (val << 5); +} +static inline uint32_t ror2(uint32_t val) { + return (val >> 2) | (val << 30); +} +static inline uint32_t ror31(uint32_t val) { + return (val >> 31) | (val << 1); +} + +static void SHA1_Transform(SHA_CTX* ctx) { + uint32_t W[80]; + register uint32_t A, B, C, D, E; + int t; + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + +#define SHA_F1(A,B,C,D,E,t) \ + E += ror27(A) + \ + (W[t] = bswap_32(ctx->buf.w[t])) + \ + (D^(B&(C^D))) + 0x5A827999; \ + B = ror2(B); + + for (t = 0; t < 15; t += 5) { + SHA_F1(A,B,C,D,E,t + 0); + SHA_F1(E,A,B,C,D,t + 1); + SHA_F1(D,E,A,B,C,t + 2); + SHA_F1(C,D,E,A,B,t + 3); + SHA_F1(B,C,D,E,A,t + 4); + } + SHA_F1(A,B,C,D,E,t + 0); /* 16th one, t == 15 */ + +#undef SHA_F1 + +#define SHA_F1(A,B,C,D,E,t) \ + E += ror27(A) + \ + (W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \ + (D^(B&(C^D))) + 0x5A827999; \ + B = ror2(B); + + SHA_F1(E,A,B,C,D,t + 1); + SHA_F1(D,E,A,B,C,t + 2); + SHA_F1(C,D,E,A,B,t + 3); + SHA_F1(B,C,D,E,A,t + 4); + +#undef SHA_F1 + +#define SHA_F2(A,B,C,D,E,t) \ + E += ror27(A) + \ + (W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \ + (B^C^D) + 0x6ED9EBA1; \ + B = ror2(B); + + for (t = 20; t < 40; t += 5) { + SHA_F2(A,B,C,D,E,t + 0); + SHA_F2(E,A,B,C,D,t + 1); + SHA_F2(D,E,A,B,C,t + 2); + SHA_F2(C,D,E,A,B,t + 3); + SHA_F2(B,C,D,E,A,t + 4); + } + +#undef SHA_F2 + +#define SHA_F3(A,B,C,D,E,t) \ + E += ror27(A) + \ + (W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \ + ((B&C)|(D&(B|C))) + 0x8F1BBCDC; \ + B = ror2(B); + + for (; t < 60; t += 5) { + SHA_F3(A,B,C,D,E,t + 0); + SHA_F3(E,A,B,C,D,t + 1); + SHA_F3(D,E,A,B,C,t + 2); + SHA_F3(C,D,E,A,B,t + 3); + SHA_F3(B,C,D,E,A,t + 4); + } + +#undef SHA_F3 + +#define SHA_F4(A,B,C,D,E,t) \ + E += ror27(A) + \ + (W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \ + (B^C^D) + 0xCA62C1D6; \ + B = ror2(B); + + for (; t < 80; t += 5) { + SHA_F4(A,B,C,D,E,t + 0); + SHA_F4(E,A,B,C,D,t + 1); + SHA_F4(D,E,A,B,C,t + 2); + SHA_F4(C,D,E,A,B,t + 3); + SHA_F4(B,C,D,E,A,t + 4); + } + +#undef SHA_F4 + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; +} + +void SHA1_update(SHA1_CTX* ctx, const uint8_t* data, size_t len) { + int i = ctx->count % sizeof(ctx->buf); + const uint8_t* p = (const uint8_t*)data; + + ctx->count += len; + + while (len > sizeof(ctx->buf) - i) { + memcpy(&ctx->buf.b[i], p, sizeof(ctx->buf) - i); + len -= sizeof(ctx->buf) - i; + p += sizeof(ctx->buf) - i; + SHA1_Transform(ctx); + i = 0; + } + + while (len--) { + ctx->buf.b[i++] = *p++; + if (i == sizeof(ctx->buf)) { + SHA1_Transform(ctx); + i = 0; + } + } +} + + +uint8_t* SHA1_final(SHA_CTX* ctx) { + uint64_t cnt = ctx->count * 8; + int i; + + SHA1_update(ctx, (uint8_t*)"\x80", 1); + while ((ctx->count % sizeof(ctx->buf)) != (sizeof(ctx->buf) - 8)) { + SHA1_update(ctx, (uint8_t*)"\0", 1); + } + for (i = 0; i < 8; ++i) { + uint8_t tmp = cnt >> ((7 - i) * 8); + SHA1_update(ctx, &tmp, 1); + } + + for (i = 0; i < 5; i++) { + ctx->buf.w[i] = bswap_32(ctx->state[i]); + } + + return ctx->buf.b; +} + +#else /* #if defined(HAVE_ENDIAN_H) && defined(HAVE_LITTLE_ENDIAN) */ + +#define rol(bits, value) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +static void SHA1_transform(SHA1_CTX *ctx) { + uint32_t W[80]; + uint32_t A, B, C, D, E; + uint8_t *p = ctx->buf; + int t; + + for(t = 0; t < 16; ++t) { + uint32_t tmp = *p++ << 24; + tmp |= *p++ << 16; + tmp |= *p++ << 8; + tmp |= *p++; + W[t] = tmp; + } + + for(; t < 80; t++) { + W[t] = rol(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + + for(t = 0; t < 80; t++) { + uint32_t tmp = rol(5,A) + E + W[t]; + + if (t < 20) + tmp += (D^(B&(C^D))) + 0x5A827999; + else if ( t < 40) + tmp += (B^C^D) + 0x6ED9EBA1; + else if ( t < 60) + tmp += ((B&C)|(D&(B|C))) + 0x8F1BBCDC; + else + tmp += (B^C^D) + 0xCA62C1D6; + + E = D; + D = C; + C = rol(30,B); + B = A; + A = tmp; + } + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; +} + +void SHA1_update(SHA1_CTX *ctx, const uint8_t *data, int len) { + int i = ctx->count % sizeof(ctx->buf); + const uint8_t* p = (const uint8_t*) data; + + ctx->count += len; + + while (len--) { + ctx->buf[i++] = *p++; + if (i == sizeof(ctx->buf)) { + SHA1_transform(ctx); + i = 0; + } + } +} +uint8_t* SHA1_final(SHA1_CTX *ctx) { + uint8_t *p = ctx->buf; + uint64_t cnt = ctx->count * 8; + int i; + + SHA1_update(ctx, (uint8_t*)"\x80", 1); + while ((ctx->count % sizeof(ctx->buf)) != (sizeof(ctx->buf) - 8)) { + SHA1_update(ctx, (uint8_t*)"\0", 1); + } + for (i = 0; i < 8; ++i) { + uint8_t tmp = cnt >> ((7 - i) * 8); + SHA1_update(ctx, &tmp, 1); + } + + for (i = 0; i < 5; i++) { + uint32_t tmp = ctx->state[i]; + *p++ = tmp >> 24; + *p++ = tmp >> 16; + *p++ = tmp >> 8; + *p++ = tmp >> 0; + } + + return ctx->buf; +} + +#endif /* endianness */ + +void SHA1_init(SHA1_CTX* ctx) { + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; + ctx->count = 0; +} + +uint8_t* SHA1(const void *data, int len, uint8_t *digest) { + const uint8_t *p; + int i; + SHA1_CTX ctx; + SHA1_init(&ctx); + SHA1_update(&ctx, data, len); + p = SHA1_final(&ctx); + for (i = 0; i < SHA1_DIGEST_SIZE; ++i) { + digest[i] = *p++; + } + return digest; +} diff --git a/crypto/sha2.c b/crypto/sha2.c new file mode 100644 index 0000000000..47eaaef5c3 --- /dev/null +++ b/crypto/sha2.c @@ -0,0 +1,623 @@ +/* SHA-256 and SHA-512 implementation based on code by Oliver Gay + * under a BSD-style license. See below. + */ + +/* + * FIPS 180-2 SHA-224/256/384/512 implementation + * Last update: 02/02/2007 + * Issue date: 04/30/2005 + * + * Copyright (C) 2005, 2007 Olivier Gay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "sha.h" +#include + +#define SHFR(x, n) (x >> n) +#define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) +#define ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n))) +#define CH(x, y, z) ((x & y) ^ (~x & z)) +#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) + +#define SHA256_F1(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define SHA256_F2(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) +#define SHA256_F3(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3)) +#define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10)) + +#define SHA512_F1(x) (ROTR(x, 28) ^ ROTR(x, 34) ^ ROTR(x, 39)) +#define SHA512_F2(x) (ROTR(x, 14) ^ ROTR(x, 18) ^ ROTR(x, 41)) +#define SHA512_F3(x) (ROTR(x, 1) ^ ROTR(x, 8) ^ SHFR(x, 7)) +#define SHA512_F4(x) (ROTR(x, 19) ^ ROTR(x, 61) ^ SHFR(x, 6)) + +#define UNPACK32(x, str) \ + { \ + *((str) + 3) = (uint8_t) ((x) ); \ + *((str) + 2) = (uint8_t) ((x) >> 8); \ + *((str) + 1) = (uint8_t) ((x) >> 16); \ + *((str) + 0) = (uint8_t) ((x) >> 24); \ + } + +#define PACK32(str, x) \ + { \ + *(x) = ((uint32_t) *((str) + 3) ) \ + | ((uint32_t) *((str) + 2) << 8) \ + | ((uint32_t) *((str) + 1) << 16) \ + | ((uint32_t) *((str) + 0) << 24); \ + } + +#define UNPACK64(x, str) \ + { \ + *((str) + 7) = (uint8_t) ((x) ); \ + *((str) + 6) = (uint8_t) ((x) >> 8); \ + *((str) + 5) = (uint8_t) ((x) >> 16); \ + *((str) + 4) = (uint8_t) ((x) >> 24); \ + *((str) + 3) = (uint8_t) ((x) >> 32); \ + *((str) + 2) = (uint8_t) ((x) >> 40); \ + *((str) + 1) = (uint8_t) ((x) >> 48); \ + *((str) + 0) = (uint8_t) ((x) >> 56); \ + } + +#define PACK64(str, x) \ + { \ + *(x) = ((uint64_t) *((str) + 7) ) \ + | ((uint64_t) *((str) + 6) << 8) \ + | ((uint64_t) *((str) + 5) << 16) \ + | ((uint64_t) *((str) + 4) << 24) \ + | ((uint64_t) *((str) + 3) << 32) \ + | ((uint64_t) *((str) + 2) << 40) \ + | ((uint64_t) *((str) + 1) << 48) \ + | ((uint64_t) *((str) + 0) << 56); \ + } + +/* Macros used for loops unrolling */ + +#define SHA256_SCR(i) \ + { \ + w[i] = SHA256_F4(w[i - 2]) + w[i - 7] \ + + SHA256_F3(w[i - 15]) + w[i - 16]; \ + } + +#define SHA512_SCR(i) \ + { \ + w[i] = SHA512_F4(w[i - 2]) + w[i - 7] \ + + SHA512_F3(w[i - 15]) + w[i - 16]; \ + } + +#define SHA256_EXP(a, b, c, d, e, f, g, h, j) \ + { \ + t1 = wv[h] + SHA256_F2(wv[e]) + CH(wv[e], wv[f], wv[g]) \ + + sha256_k[j] + w[j]; \ + t2 = SHA256_F1(wv[a]) + MAJ(wv[a], wv[b], wv[c]); \ + wv[d] += t1; \ + wv[h] = t1 + t2; \ + } + +#define SHA512_EXP(a, b, c, d, e, f, g ,h, j) \ + { \ + t1 = wv[h] + SHA512_F2(wv[e]) + CH(wv[e], wv[f], wv[g]) \ + + sha512_k[j] + w[j]; \ + t2 = SHA512_F1(wv[a]) + MAJ(wv[a], wv[b], wv[c]); \ + wv[d] += t1; \ + wv[h] = t1 + t2; \ + } + +uint32_t sha256_h0[8] = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; + +uint64_t sha512_h0[8] = { + 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL}; + +uint32_t sha256_k[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; + +uint64_t sha512_k[80] = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, + 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, + 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, + 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, + 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, + 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, + 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, + 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, + 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, + 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, + 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, + 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, + 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, + 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, + 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, + 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, + 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, + 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, + 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, + 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, + 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, + 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, + 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, + 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, + 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL}; + + +/* SHA-256 implementation */ +void SHA256_init(SHA256_CTX *ctx) { +#ifndef UNROLL_LOOPS + int i; + for (i = 0; i < 8; i++) { + ctx->h[i] = sha256_h0[i]; + } +#else + ctx->h[0] = sha256_h0[0]; ctx->h[1] = sha256_h0[1]; + ctx->h[2] = sha256_h0[2]; ctx->h[3] = sha256_h0[3]; + ctx->h[4] = sha256_h0[4]; ctx->h[5] = sha256_h0[5]; + ctx->h[6] = sha256_h0[6]; ctx->h[7] = sha256_h0[7]; +#endif /* !UNROLL_LOOPS */ + + ctx->len = 0; + ctx->tot_len = 0; +} + + +static void SHA256_transform(SHA256_CTX* ctx, const uint8_t* message, + unsigned int block_nb) { + uint32_t w[64]; + uint32_t wv[8]; + uint32_t t1, t2; + const unsigned char *sub_block; + int i; + +#ifndef UNROLL_LOOPS + int j; +#endif + + for (i = 0; i < (int) block_nb; i++) { + sub_block = message + (i << 6); + +#ifndef UNROLL_LOOPS + for (j = 0; j < 16; j++) { + PACK32(&sub_block[j << 2], &w[j]); + } + + for (j = 16; j < 64; j++) { + SHA256_SCR(j); + } + + for (j = 0; j < 8; j++) { + wv[j] = ctx->h[j]; + } + + for (j = 0; j < 64; j++) { + t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + + sha256_k[j] + w[j]; + t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]); + wv[7] = wv[6]; + wv[6] = wv[5]; + wv[5] = wv[4]; + wv[4] = wv[3] + t1; + wv[3] = wv[2]; + wv[2] = wv[1]; + wv[1] = wv[0]; + wv[0] = t1 + t2; + } + + for (j = 0; j < 8; j++) { + ctx->h[j] += wv[j]; + } +#else + PACK32(&sub_block[ 0], &w[ 0]); PACK32(&sub_block[ 4], &w[ 1]); + PACK32(&sub_block[ 8], &w[ 2]); PACK32(&sub_block[12], &w[ 3]); + PACK32(&sub_block[16], &w[ 4]); PACK32(&sub_block[20], &w[ 5]); + PACK32(&sub_block[24], &w[ 6]); PACK32(&sub_block[28], &w[ 7]); + PACK32(&sub_block[32], &w[ 8]); PACK32(&sub_block[36], &w[ 9]); + PACK32(&sub_block[40], &w[10]); PACK32(&sub_block[44], &w[11]); + PACK32(&sub_block[48], &w[12]); PACK32(&sub_block[52], &w[13]); + PACK32(&sub_block[56], &w[14]); PACK32(&sub_block[60], &w[15]); + + SHA256_SCR(16); SHA256_SCR(17); SHA256_SCR(18); SHA256_SCR(19); + SHA256_SCR(20); SHA256_SCR(21); SHA256_SCR(22); SHA256_SCR(23); + SHA256_SCR(24); SHA256_SCR(25); SHA256_SCR(26); SHA256_SCR(27); + SHA256_SCR(28); SHA256_SCR(29); SHA256_SCR(30); SHA256_SCR(31); + SHA256_SCR(32); SHA256_SCR(33); SHA256_SCR(34); SHA256_SCR(35); + SHA256_SCR(36); SHA256_SCR(37); SHA256_SCR(38); SHA256_SCR(39); + SHA256_SCR(40); SHA256_SCR(41); SHA256_SCR(42); SHA256_SCR(43); + SHA256_SCR(44); SHA256_SCR(45); SHA256_SCR(46); SHA256_SCR(47); + SHA256_SCR(48); SHA256_SCR(49); SHA256_SCR(50); SHA256_SCR(51); + SHA256_SCR(52); SHA256_SCR(53); SHA256_SCR(54); SHA256_SCR(55); + SHA256_SCR(56); SHA256_SCR(57); SHA256_SCR(58); SHA256_SCR(59); + SHA256_SCR(60); SHA256_SCR(61); SHA256_SCR(62); SHA256_SCR(63); + + wv[0] = ctx->h[0]; wv[1] = ctx->h[1]; + wv[2] = ctx->h[2]; wv[3] = ctx->h[3]; + wv[4] = ctx->h[4]; wv[5] = ctx->h[5]; + wv[6] = ctx->h[6]; wv[7] = ctx->h[7]; + + SHA256_EXP(0,1,2,3,4,5,6,7, 0); SHA256_EXP(7,0,1,2,3,4,5,6, 1); + SHA256_EXP(6,7,0,1,2,3,4,5, 2); SHA256_EXP(5,6,7,0,1,2,3,4, 3); + SHA256_EXP(4,5,6,7,0,1,2,3, 4); SHA256_EXP(3,4,5,6,7,0,1,2, 5); + SHA256_EXP(2,3,4,5,6,7,0,1, 6); SHA256_EXP(1,2,3,4,5,6,7,0, 7); + SHA256_EXP(0,1,2,3,4,5,6,7, 8); SHA256_EXP(7,0,1,2,3,4,5,6, 9); + SHA256_EXP(6,7,0,1,2,3,4,5,10); SHA256_EXP(5,6,7,0,1,2,3,4,11); + SHA256_EXP(4,5,6,7,0,1,2,3,12); SHA256_EXP(3,4,5,6,7,0,1,2,13); + SHA256_EXP(2,3,4,5,6,7,0,1,14); SHA256_EXP(1,2,3,4,5,6,7,0,15); + SHA256_EXP(0,1,2,3,4,5,6,7,16); SHA256_EXP(7,0,1,2,3,4,5,6,17); + SHA256_EXP(6,7,0,1,2,3,4,5,18); SHA256_EXP(5,6,7,0,1,2,3,4,19); + SHA256_EXP(4,5,6,7,0,1,2,3,20); SHA256_EXP(3,4,5,6,7,0,1,2,21); + SHA256_EXP(2,3,4,5,6,7,0,1,22); SHA256_EXP(1,2,3,4,5,6,7,0,23); + SHA256_EXP(0,1,2,3,4,5,6,7,24); SHA256_EXP(7,0,1,2,3,4,5,6,25); + SHA256_EXP(6,7,0,1,2,3,4,5,26); SHA256_EXP(5,6,7,0,1,2,3,4,27); + SHA256_EXP(4,5,6,7,0,1,2,3,28); SHA256_EXP(3,4,5,6,7,0,1,2,29); + SHA256_EXP(2,3,4,5,6,7,0,1,30); SHA256_EXP(1,2,3,4,5,6,7,0,31); + SHA256_EXP(0,1,2,3,4,5,6,7,32); SHA256_EXP(7,0,1,2,3,4,5,6,33); + SHA256_EXP(6,7,0,1,2,3,4,5,34); SHA256_EXP(5,6,7,0,1,2,3,4,35); + SHA256_EXP(4,5,6,7,0,1,2,3,36); SHA256_EXP(3,4,5,6,7,0,1,2,37); + SHA256_EXP(2,3,4,5,6,7,0,1,38); SHA256_EXP(1,2,3,4,5,6,7,0,39); + SHA256_EXP(0,1,2,3,4,5,6,7,40); SHA256_EXP(7,0,1,2,3,4,5,6,41); + SHA256_EXP(6,7,0,1,2,3,4,5,42); SHA256_EXP(5,6,7,0,1,2,3,4,43); + SHA256_EXP(4,5,6,7,0,1,2,3,44); SHA256_EXP(3,4,5,6,7,0,1,2,45); + SHA256_EXP(2,3,4,5,6,7,0,1,46); SHA256_EXP(1,2,3,4,5,6,7,0,47); + SHA256_EXP(0,1,2,3,4,5,6,7,48); SHA256_EXP(7,0,1,2,3,4,5,6,49); + SHA256_EXP(6,7,0,1,2,3,4,5,50); SHA256_EXP(5,6,7,0,1,2,3,4,51); + SHA256_EXP(4,5,6,7,0,1,2,3,52); SHA256_EXP(3,4,5,6,7,0,1,2,53); + SHA256_EXP(2,3,4,5,6,7,0,1,54); SHA256_EXP(1,2,3,4,5,6,7,0,55); + SHA256_EXP(0,1,2,3,4,5,6,7,56); SHA256_EXP(7,0,1,2,3,4,5,6,57); + SHA256_EXP(6,7,0,1,2,3,4,5,58); SHA256_EXP(5,6,7,0,1,2,3,4,59); + SHA256_EXP(4,5,6,7,0,1,2,3,60); SHA256_EXP(3,4,5,6,7,0,1,2,61); + SHA256_EXP(2,3,4,5,6,7,0,1,62); SHA256_EXP(1,2,3,4,5,6,7,0,63); + + ctx->h[0] += wv[0]; ctx->h[1] += wv[1]; + ctx->h[2] += wv[2]; ctx->h[3] += wv[3]; + ctx->h[4] += wv[4]; ctx->h[5] += wv[5]; + ctx->h[6] += wv[6]; ctx->h[7] += wv[7]; +#endif /* !UNROLL_LOOPS */ + } +} + + + +void SHA256_update(SHA256_CTX* ctx, const uint8_t* data, int len) { + unsigned int block_nb; + unsigned int new_len, rem_len, tmp_len; + const uint8_t *shifted_data; + + tmp_len = SHA256_BLOCK_SIZE - ctx->len; + rem_len = len < tmp_len ? len : tmp_len; + + memcpy(&ctx->block[ctx->len], data, rem_len); + + if (ctx->len + len < SHA256_BLOCK_SIZE) { + ctx->len += len; + return; + } + + new_len = len - rem_len; + block_nb = new_len / SHA256_BLOCK_SIZE; + + shifted_data = data + rem_len; + + SHA256_transform(ctx, ctx->block, 1); + SHA256_transform(ctx, shifted_data, block_nb); + + rem_len = new_len % SHA256_BLOCK_SIZE; + + memcpy(ctx->block, &shifted_data[block_nb << 6], + rem_len); + + ctx->len = rem_len; + ctx->tot_len += (block_nb + 1) << 6; +} + +uint8_t* SHA256_final(SHA256_CTX* ctx) { + unsigned int block_nb; + unsigned int pm_len; + unsigned int len_b; +#ifndef UNROLL_LOOPS + int i; +#endif + + block_nb = (1 + ((SHA256_BLOCK_SIZE - 9) + < (ctx->len % SHA256_BLOCK_SIZE))); + + len_b = (ctx->tot_len + ctx->len) << 3; + pm_len = block_nb << 6; + + memset(ctx->block + ctx->len, 0, pm_len - ctx->len); + ctx->block[ctx->len] = 0x80; + UNPACK32(len_b, ctx->block + pm_len - 4); + + SHA256_transform(ctx, ctx->block, block_nb); + +#ifndef UNROLL_LOOPS + for (i = 0 ; i < 8; i++) { + UNPACK32(ctx->h[i], &ctx->buf[i << 2]); + } +#else + UNPACK32(ctx->h[0], &ctx->buf[ 0]); + UNPACK32(ctx->h[1], &ctx->buf[ 4]); + UNPACK32(ctx->h[2], &ctx->buf[ 8]); + UNPACK32(ctx->h[3], &ctx->buf[12]); + UNPACK32(ctx->h[4], &ctx->buf[16]); + UNPACK32(ctx->h[5], &ctx->buf[20]); + UNPACK32(ctx->h[6], &ctx->buf[24]); + UNPACK32(ctx->h[7], &ctx->buf[28]); +#endif /* !UNROLL_LOOPS */ + + return ctx->buf; +} + + +/* SHA-512 implementation */ + +void SHA512_init(SHA512_CTX *ctx) { +#ifndef UNROLL_LOOPS + int i; + for (i = 0; i < 8; i++) { + ctx->h[i] = sha512_h0[i]; + } +#else + ctx->h[0] = sha512_h0[0]; ctx->h[1] = sha512_h0[1]; + ctx->h[2] = sha512_h0[2]; ctx->h[3] = sha512_h0[3]; + ctx->h[4] = sha512_h0[4]; ctx->h[5] = sha512_h0[5]; + ctx->h[6] = sha512_h0[6]; ctx->h[7] = sha512_h0[7]; +#endif /* !UNROLL_LOOPS */ + + ctx->len = 0; + ctx->tot_len = 0; +} + + +static void SHA512_transform(SHA512_CTX* ctx, const uint8_t* message, + unsigned int block_nb) +{ + uint64_t w[80]; + uint64_t wv[8]; + uint64_t t1, t2; + const uint8_t *sub_block; + int i, j; + + for (i = 0; i < (int) block_nb; i++) { + sub_block = message + (i << 7); + +#ifndef UNROLL_LOOPS + for (j = 0; j < 16; j++) { + PACK64(&sub_block[j << 3], &w[j]); + } + + for (j = 16; j < 80; j++) { + SHA512_SCR(j); + } + + for (j = 0; j < 8; j++) { + wv[j] = ctx->h[j]; + } + + for (j = 0; j < 80; j++) { + t1 = wv[7] + SHA512_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + + sha512_k[j] + w[j]; + t2 = SHA512_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]); + wv[7] = wv[6]; + wv[6] = wv[5]; + wv[5] = wv[4]; + wv[4] = wv[3] + t1; + wv[3] = wv[2]; + wv[2] = wv[1]; + wv[1] = wv[0]; + wv[0] = t1 + t2; + } + + for (j = 0; j < 8; j++) { + ctx->h[j] += wv[j]; + } +#else + PACK64(&sub_block[ 0], &w[ 0]); PACK64(&sub_block[ 8], &w[ 1]); + PACK64(&sub_block[ 16], &w[ 2]); PACK64(&sub_block[ 24], &w[ 3]); + PACK64(&sub_block[ 32], &w[ 4]); PACK64(&sub_block[ 40], &w[ 5]); + PACK64(&sub_block[ 48], &w[ 6]); PACK64(&sub_block[ 56], &w[ 7]); + PACK64(&sub_block[ 64], &w[ 8]); PACK64(&sub_block[ 72], &w[ 9]); + PACK64(&sub_block[ 80], &w[10]); PACK64(&sub_block[ 88], &w[11]); + PACK64(&sub_block[ 96], &w[12]); PACK64(&sub_block[104], &w[13]); + PACK64(&sub_block[112], &w[14]); PACK64(&sub_block[120], &w[15]); + + SHA512_SCR(16); SHA512_SCR(17); SHA512_SCR(18); SHA512_SCR(19); + SHA512_SCR(20); SHA512_SCR(21); SHA512_SCR(22); SHA512_SCR(23); + SHA512_SCR(24); SHA512_SCR(25); SHA512_SCR(26); SHA512_SCR(27); + SHA512_SCR(28); SHA512_SCR(29); SHA512_SCR(30); SHA512_SCR(31); + SHA512_SCR(32); SHA512_SCR(33); SHA512_SCR(34); SHA512_SCR(35); + SHA512_SCR(36); SHA512_SCR(37); SHA512_SCR(38); SHA512_SCR(39); + SHA512_SCR(40); SHA512_SCR(41); SHA512_SCR(42); SHA512_SCR(43); + SHA512_SCR(44); SHA512_SCR(45); SHA512_SCR(46); SHA512_SCR(47); + SHA512_SCR(48); SHA512_SCR(49); SHA512_SCR(50); SHA512_SCR(51); + SHA512_SCR(52); SHA512_SCR(53); SHA512_SCR(54); SHA512_SCR(55); + SHA512_SCR(56); SHA512_SCR(57); SHA512_SCR(58); SHA512_SCR(59); + SHA512_SCR(60); SHA512_SCR(61); SHA512_SCR(62); SHA512_SCR(63); + SHA512_SCR(64); SHA512_SCR(65); SHA512_SCR(66); SHA512_SCR(67); + SHA512_SCR(68); SHA512_SCR(69); SHA512_SCR(70); SHA512_SCR(71); + SHA512_SCR(72); SHA512_SCR(73); SHA512_SCR(74); SHA512_SCR(75); + SHA512_SCR(76); SHA512_SCR(77); SHA512_SCR(78); SHA512_SCR(79); + + wv[0] = ctx->h[0]; wv[1] = ctx->h[1]; + wv[2] = ctx->h[2]; wv[3] = ctx->h[3]; + wv[4] = ctx->h[4]; wv[5] = ctx->h[5]; + wv[6] = ctx->h[6]; wv[7] = ctx->h[7]; + + j = 0; + + do { + SHA512_EXP(0,1,2,3,4,5,6,7,j); j++; + SHA512_EXP(7,0,1,2,3,4,5,6,j); j++; + SHA512_EXP(6,7,0,1,2,3,4,5,j); j++; + SHA512_EXP(5,6,7,0,1,2,3,4,j); j++; + SHA512_EXP(4,5,6,7,0,1,2,3,j); j++; + SHA512_EXP(3,4,5,6,7,0,1,2,j); j++; + SHA512_EXP(2,3,4,5,6,7,0,1,j); j++; + SHA512_EXP(1,2,3,4,5,6,7,0,j); j++; + } while (j < 80); + + ctx->h[0] += wv[0]; ctx->h[1] += wv[1]; + ctx->h[2] += wv[2]; ctx->h[3] += wv[3]; + ctx->h[4] += wv[4]; ctx->h[5] += wv[5]; + ctx->h[6] += wv[6]; ctx->h[7] += wv[7]; +#endif /* !UNROLL_LOOPS */ + } +} + + +void SHA512_update(SHA512_CTX* ctx, const uint8_t* data, + int len) { + unsigned int block_nb; + unsigned int new_len, rem_len, tmp_len; + const uint8_t* shifted_data; + + tmp_len = SHA512_BLOCK_SIZE - ctx->len; + rem_len = len < tmp_len ? len : tmp_len; + + memcpy(&ctx->block[ctx->len], data, rem_len); + + if (ctx->len + len < SHA512_BLOCK_SIZE) { + ctx->len += len; + return; + } + + new_len = len - rem_len; + block_nb = new_len / SHA512_BLOCK_SIZE; + + shifted_data = data + rem_len; + + SHA512_transform(ctx, ctx->block, 1); + SHA512_transform(ctx, shifted_data, block_nb); + + rem_len = new_len % SHA512_BLOCK_SIZE; + + memcpy(ctx->block, &shifted_data[block_nb << 7], + rem_len); + + ctx->len = rem_len; + ctx->tot_len += (block_nb + 1) << 7; +} + +uint8_t* SHA512_final(SHA512_CTX* ctx) +{ + unsigned int block_nb; + unsigned int pm_len; + unsigned int len_b; + +#ifndef UNROLL_LOOPS + int i; +#endif + + block_nb = 1 + ((SHA512_BLOCK_SIZE - 17) + < (ctx->len % SHA512_BLOCK_SIZE)); + + len_b = (ctx->tot_len + ctx->len) << 3; + pm_len = block_nb << 7; + + memset(ctx->block + ctx->len, 0, pm_len - ctx->len); + ctx->block[ctx->len] = 0x80; + UNPACK32(len_b, ctx->block + pm_len - 4); + + SHA512_transform(ctx, ctx->block, block_nb); + +#ifndef UNROLL_LOOPS + for (i = 0 ; i < 8; i++) { + UNPACK64(ctx->h[i], &ctx->buf[i << 3]); + } +#else + UNPACK64(ctx->h[0], &ctx->buf[ 0]); + UNPACK64(ctx->h[1], &ctx->buf[ 8]); + UNPACK64(ctx->h[2], &ctx->buf[16]); + UNPACK64(ctx->h[3], &ctx->buf[24]); + UNPACK64(ctx->h[4], &ctx->buf[32]); + UNPACK64(ctx->h[5], &ctx->buf[40]); + UNPACK64(ctx->h[6], &ctx->buf[48]); + UNPACK64(ctx->h[7], &ctx->buf[56]); +#endif /* !UNROLL_LOOPS */ + + return ctx->buf; +} + + + +/* Convenient functions. */ +uint8_t* SHA256(const uint8_t* data, int len, uint8_t* digest) { + const uint8_t* p; + int i; + SHA256_CTX ctx; + SHA256_init(&ctx); + SHA256_update(&ctx, data, len); + p = SHA256_final(&ctx); + for (i = 0; i < SHA256_DIGEST_SIZE; ++i) { + digest[i] = *p++; + } + return digest; +} + + +uint8_t* SHA512(const uint8_t* data, int len, uint8_t* digest) { + const uint8_t* p; + int i; + SHA512_CTX ctx; + SHA512_init(&ctx); + SHA512_update(&ctx, data, len); + p = SHA512_final(&ctx); + for (i = 0; i < SHA512_DIGEST_SIZE; ++i) { + digest[i] = *p++; + } + return digest; +} diff --git a/include/padding.h b/include/padding.h new file mode 100644 index 0000000000..2cc74bfe0d --- /dev/null +++ b/include/padding.h @@ -0,0 +1,27 @@ +#ifndef VBOOT_REFERENCE_PADDING_H_ +#define VBOOT_REFERENCE_PADDING_H_ + +#include + +extern const uint8_t paddingRSA1024_SHA1[]; +extern const uint8_t paddingRSA1024_SHA256[]; +extern const uint8_t paddingRSA1024_SHA512[]; +extern const uint8_t paddingRSA2048_SHA1[]; +extern const uint8_t paddingRSA2048_SHA256[]; +extern const uint8_t paddingRSA2048_SHA512[]; +extern const uint8_t paddingRSA4096_SHA1[]; +extern const uint8_t paddingRSA4096_SHA256[]; +extern const uint8_t paddingRSA4096_SHA512[]; +extern const uint8_t paddingRSA8192_SHA1[]; +extern const uint8_t paddingRSA8192_SHA256[]; +extern const uint8_t paddingRSA8192_SHA512[]; + +extern const int kNumAlgorithms; + +extern const int siglen_map[]; +extern const uint8_t* padding_map[]; +extern const int padding_size_map[]; +extern const int hash_blocksize_map[]; +extern const char* algo_strings[]; + +#endif /* VBOOT_REFERENCE_PADDING_H_ */ diff --git a/include/rsa.h b/include/rsa.h new file mode 100644 index 0000000000..1969ab651d --- /dev/null +++ b/include/rsa.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef VBOOT_REFERENCE_RSA_H_ +#define VBOOT_REFERENCE_RSA_H_ + +#include + +#define RSA1024NUMBYTES 128 /* 1024 bit key length */ +#define RSA2048NUMBYTES 256 /* 2048 bit key length */ +#define RSA4096NUMBYTES 512 /* 4096 bit key length */ +#define RSA8192NUMBYTES 1024 /* 8192 bit key length */ + +#define RSA1024NUMWORDS (RSA1024NUMBYTES / sizeof(uint32_t)) +#define RSA2048NUMWORDS (RSA2048NUMBYTES / sizeof(uint32_t)) +#define RSA4096NUMWORDS (RSA4096NUMBYTES / sizeof(uint32_t)) +#define RSA8192NUMWORDS (RSA8192NUMBYTES / sizeof(uint32_t)) + +typedef struct RSAPublicKey { + int len; /* Length of n[] in number of uint32_t */ + uint32_t n0inv; /* -1 / n[0] mod 2^32 */ + uint32_t* n; /* modulus as little endian array */ + uint32_t* rr; /* R^2 as little endian array */ +} RSAPublicKey; + +/* Verify a RSA PKCS1.5 signature [sig] of [sig_type] and length [sig_len] + * against an expected [hash] using [key]. Returns 0 on failure, 1 on success. + */ +int RSA_verify(const RSAPublicKey *key, + const uint8_t* sig, + const int sig_len, + const uint8_t sig_type, + const uint8_t* hash); + +#endif /* VBOOT_REFERENCE_RSA_H_ */ diff --git a/include/sha.h b/include/sha.h new file mode 100644 index 0000000000..b15cfd1d09 --- /dev/null +++ b/include/sha.h @@ -0,0 +1,73 @@ +/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* SHA-1, 256 and 512 functions. */ + +#ifndef VBOOT_REFERENCE_SHA_H_ +#define VBOOT_REFERENCE_SHA_H_ + +#include +#include + +#define SHA1_DIGEST_SIZE 20 +#define SHA1_BLOCK_SIZE 64 + +#define SHA256_DIGEST_SIZE 32 +#define SHA256_BLOCK_SIZE 64 + +#define SHA512_DIGEST_SIZE 64 +#define SHA512_BLOCK_SIZE 128 + +typedef struct SHA1_CTX { + uint64_t count; + uint32_t state[5]; +#if defined(HAVE_ENDIAN_H) && defined(HAVE_LITTLE_ENDIAN) + union { + uint8_t b[64]; + uint32_t w[16]; + } buf; +#else + uint8_t buf[64]; +#endif +} SHA1_CTX; + +typedef struct { + uint32_t h[8]; + uint32_t tot_len; + uint32_t len; + uint8_t block[2 * SHA256_BLOCK_SIZE]; + uint8_t buf[SHA256_DIGEST_SIZE]; /* Used for storing the final digest. */ +} SHA256_CTX; + +typedef struct { + uint64_t h[8]; + uint32_t tot_len; + uint32_t len; + uint8_t block[2 * SHA512_BLOCK_SIZE]; + uint8_t buf[SHA512_DIGEST_SIZE]; /* Used for storing the final digest. */ +} SHA512_CTX; + + +void SHA1_init(SHA1_CTX* ctx); +void SHA1_update(SHA1_CTX* ctx, const uint8_t* data, int len); +uint8_t* SHA1_final(SHA1_CTX* ctx); +/* Convenience function for SHA-1. Computes hash on [data] of length [len]. + * and stores it into [digest]. [digest] should be pre-allocated to + * SHA1_DIGEST_SIZE bytes. + */ +uint8_t* SHA1(const void* data, int len, uint8_t* digest); + +void SHA256_init(SHA256_CTX* ctx); +void SHA256_update(SHA256_CTX* ctx, const uint8_t* data, int len); +uint8_t* SHA256_final(SHA256_CTX* ctx); +uint8_t* SHA256(const uint8_t* data, int len, uint8_t* digest); + +void SHA512_init(SHA512_CTX* ctx); +void SHA512_update(SHA512_CTX* ctx, const uint8_t* data, int len); +uint8_t* SHA512_final(SHA512_CTX* ctx); +uint8_t* SHA512(const uint8_t* data, int len, uint8_t* digest); + + +#endif /* VBOOT_REFERENCE_SHA_H_ */ diff --git a/include/utility.h b/include/utility.h new file mode 100644 index 0000000000..ddb1dc055f --- /dev/null +++ b/include/utility.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Helper functions/wrappers for memory allocations, manipulation and + * comparison. + */ + +#ifndef VBOOT_REFERENCE_UTILITY_H_ +#define VBOOT_REFERENCE_UTILITY_H_ + +#include + +/* Allocate [size] bytes and return a pointer to the allocated memory. Abort + * on error. + */ +void* Malloc(size_t size); + +/* Free memory pointed by [ptr] previously allocated by Malloc(). */ +void Free(void* ptr); + +/* Copy [n] bytes from [src] to [dest]. */ +void* Memcpy(void* dest, const void* src, size_t n); + +/* Compare [n] bytes starting at [s1] with [s2] and return 1 if they match, + * 0 if they don't. Time taken to perform the comparison is only dependent on + * [n] and not on the relationship of the match between [s1] and [s2]. + */ +int SafeMemcmp(const void* s1, const void* s2, size_t n); + +#endif /* VBOOT_REFERENCE_UTILITY_H_ */ diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000000..e3694cf56d --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,18 @@ +# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +SRCS=sha_tests.c verify_data.c +OBJS=$(SRCS:.c=.o) +LIBS=$(TOP)/crypto/libcrypto.a $(TOP)/common/libcommon.a + +tests: sha_tests verify_data + +sha_tests: sha_tests.c + $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) + +verify_data: verify_data.c + $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) + +clean: + rm -f $(OBJS) sha_tests verify_data diff --git a/tests/run_tests.sh b/tests/run_tests.sh new file mode 100755 index 0000000000..bfde0a20fd --- /dev/null +++ b/tests/run_tests.sh @@ -0,0 +1,83 @@ +#!/bin/bash + +# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Run tests for cryptographic routine implementations - Message digests +# and RSA Signature verification. + +hash_algos=( sha1 sha256 sha512 ) +key_lengths=( 1024 2048 4096 8192 ) +TEST_FILE=test_file +TEST_FILE_SIZE=1000000 +UTILDIR=../utils/ + +# Generate RSA test keys of various lengths. +function generate_keys { + for i in ${key_lengths[@]} + do + openssl genrsa -F4 -out key_rsa$i.pem $i + # Generate self-signed certificate from key. + openssl req -batch -new -x509 -key key_rsa$i.pem -out key_rsa$i.crt + # Generate pre-processed key for use by RSA signature verification code. + ${UTILDIR}/dumpRSAPublicKey key_rsa$i.crt > key_rsa$i.keyb + done +} + +# Generate public key signatures on an input file for various combinations +# of message digest algorithms and RSA key sizes. +function generate_signatures { + for i in ${hash_algos[@]} + do + for j in ${key_lengths[@]} + do + openssl dgst -binary -$i $1 >$1.digest.$i + openssl pkeyutl -in $1.digest.$i -inkey key_rsa$j.pem \ + -pkeyopt digest:$i > $1.rsa$j\_$i.sig + done + done +} + +function test_signatures { + algorithmcounter=0 + for rsaalgo in ${key_lengths[@]} + do + for hashalgo in ${hash_algos[@]} + do + echo "For RSA-$rsaalgo and $hashalgo:" + ./verify_data $algorithmcounter key_rsa${rsaalgo}.keyb \ + ${TEST_FILE}.rsa${rsaalgo}_${hashalgo}.sig ${TEST_FILE} + let algorithmcounter=algorithmcounter+1 + done + done +} + + +function pre_work { + # Generate a file with random bytes for signature tests. + echo "Generating test file..." + dd if=/dev/urandom of=${TEST_FILE} bs=${TEST_FILE_SIZE} count=1 + echo "Generating test keys..." + generate_keys + echo "Generating signatures..." + generate_signatures $TEST_FILE +} + +function cleanup { + rm ${TEST_FILE} ${TEST_FILE}.digest.* ${TEST_FILE}.*.sig key_rsa*.* +} + +echo "Testing message digests..." +./sha_tests + +echo +echo "Testing signature verification..." +pre_work +test_signatures + +echo +echo "Cleaning up..." +cleanup + + diff --git a/tests/sha_test_vectors.h b/tests/sha_test_vectors.h new file mode 100644 index 0000000000..640c06b969 --- /dev/null +++ b/tests/sha_test_vectors.h @@ -0,0 +1,93 @@ +/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* FIPS 180-2 test vectors for SHA-1, SHA-256 and SHA-512 */ + +#ifndef VBOOT_REFERENCE_SHA_TEST_VECTORS_H_ +#define VBOOT_REFERENCE_SHA_TEST_VECTORS_H_ + +#include "sha.h" + +char *oneblock_msg = "abc"; +char *multiblock_msg1 = "abcdbcdecdefdefgefghfghighijhijkijkl" + "jklmklmnlmnomnopnopq"; +char *multiblock_msg2= "abcdefghbcdefghicdefghijdefghijkefghi" + "jklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnop" + "qrsmnopqrstnopqrstu"; +char *long_msg; + +uint8_t sha1_results[][SHA1_DIGEST_SIZE] = { + { + 0xa9,0x99,0x3e,0x36,0x47,0x06,0x81,0x6a, + 0xba,0x3e,0x25,0x71,0x78,0x50,0xc2,0x6c, + 0x9c,0xd0,0xd8,0x9d + }, + { + 0x84,0x98,0x3e,0x44,0x1c,0x3b,0xd2,0x6e, + 0xba,0xae,0x4a,0xa1,0xf9,0x51,0x29,0xe5, + 0xe5,0x46,0x70,0xf1 + }, + { + 0x34,0xaa,0x97,0x3c,0xd4,0xc4,0xda,0xa4, + 0xf6,0x1e,0xeb,0x2b,0xdb,0xad,0x27,0x31, + 0x65,0x34,0x01,0x6f + } +}; + +uint8_t sha256_results[][SHA256_DIGEST_SIZE] = { + { + 0xba,0x78,0x16,0xbf,0x8f,0x01,0xcf,0xea, + 0x41,0x41,0x40,0xde,0x5d,0xae,0x22,0x23, + 0xb0,0x03,0x61,0xa3,0x96,0x17,0x7a,0x9c, + 0xb4,0x10,0xff,0x61,0xf2,0x00,0x15,0xad + }, + { + 0x24,0x8d,0x6a,0x61,0xd2,0x06,0x38,0xb8, + 0xe5,0xc0,0x26,0x93,0x0c,0x3e,0x60,0x39, + 0xa3,0x3c,0xe4,0x59,0x64,0xff,0x21,0x67, + 0xf6,0xec,0xed,0xd4,0x19,0xdb,0x06,0xc1 + }, + { + 0xcd,0xc7,0x6e,0x5c,0x99,0x14,0xfb,0x92, + 0x81,0xa1,0xc7,0xe2,0x84,0xd7,0x3e,0x67, + 0xf1,0x80,0x9a,0x48,0xa4,0x97,0x20,0x0e, + 0x04,0x6d,0x39,0xcc,0xc7,0x11,0x2c,0xd0 + } +}; + +uint8_t sha512_results[][SHA512_DIGEST_SIZE] = { + { + 0xdd,0xaf,0x35,0xa1,0x93,0x61,0x7a,0xba, + 0xcc,0x41,0x73,0x49,0xae,0x20,0x41,0x31, + 0x12,0xe6,0xfa,0x4e,0x89,0xa9,0x7e,0xa2, + 0x0a,0x9e,0xee,0xe6,0x4b,0x55,0xd3,0x9a, + 0x21,0x92,0x99,0x2a,0x27,0x4f,0xc1,0xa8, + 0x36,0xba,0x3c,0x23,0xa3,0xfe,0xeb,0xbd, + 0x45,0x4d,0x44,0x23,0x64,0x3c,0xe8,0x0e, + 0x2a,0x9a,0xc9,0x4f,0xa5,0x4c,0xa4,0x9f + }, + { + 0x8e,0x95,0x9b,0x75,0xda,0xe3,0x13,0xda, + 0x8c,0xf4,0xf7,0x28,0x14,0xfc,0x14,0x3f, + 0x8f,0x77,0x79,0xc6,0xeb,0x9f,0x7f,0xa1, + 0x72,0x99,0xae,0xad,0xb6,0x88,0x90,0x18, + 0x50,0x1d,0x28,0x9e,0x49,0x00,0xf7,0xe4, + 0x33,0x1b,0x99,0xde,0xc4,0xb5,0x43,0x3a, + 0xc7,0xd3,0x29,0xee,0xb6,0xdd,0x26,0x54, + 0x5e,0x96,0xe5,0x5b,0x87,0x4b,0xe9,0x09 + }, + { + 0xe7,0x18,0x48,0x3d,0x0c,0xe7,0x69,0x64, + 0x4e,0x2e,0x42,0xc7,0xbc,0x15,0xb4,0x63, + 0x8e,0x1f,0x98,0xb1,0x3b,0x20,0x44,0x28, + 0x56,0x32,0xa8,0x03,0xaf,0xa9,0x73,0xeb, + 0xde,0x0f,0xf2,0x44,0x87,0x7e,0xa6,0x0a, + 0x4c,0xb0,0x43,0x2c,0xe5,0x77,0xc3,0x1b, + 0xeb,0x00,0x9c,0x5c,0x2c,0x49,0xaa,0x2e, + 0x4e,0xad,0xb2,0x17,0xad,0x8c,0xc0,0x9b + } +}; + +#endif /* VBOOT_REFERENCE_SHA_TEST_VECTORS_H_ */ diff --git a/tests/sha_tests.c b/tests/sha_tests.c new file mode 100644 index 0000000000..2c07b3fcfa --- /dev/null +++ b/tests/sha_tests.c @@ -0,0 +1,99 @@ +/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* FIPS 180-2 Tests for message digest functions. */ + +#include +#include +#include + +#include "sha.h" + +#include "sha_test_vectors.h" + +int SHA1_tests(void) { + int i, success = 1; + uint8_t sha1_digest[SHA1_DIGEST_SIZE]; + uint8_t* test_inputs[3]; + test_inputs[0] = (uint8_t *) oneblock_msg; + test_inputs[1] = (uint8_t *) multiblock_msg1; + test_inputs[2] = (uint8_t *) long_msg; + + for (i = 0; i < 3; i++) { + SHA1(test_inputs[i], strlen((char *)test_inputs[i]), + sha1_digest); + if (!memcmp(sha1_digest, sha1_results[i], SHA1_DIGEST_SIZE)) { + fprintf(stderr, "Test vector %d PASSED for SHA-1\n", i+1); + } + else { + fprintf(stderr, "Test vector %d FAILED for SHA-1\n", i+1); + success = 0; + } + } + return success; +} + +int SHA256_tests(void) { + int i, success = 1; + uint8_t sha256_digest[SHA256_DIGEST_SIZE]; + uint8_t* test_inputs[3]; + test_inputs[0] = (uint8_t *) oneblock_msg; + test_inputs[1] = (uint8_t *) multiblock_msg1; + test_inputs[2] = (uint8_t *) long_msg; + + for (i = 0; i < 3; i++) { + SHA256(test_inputs[i], strlen((char *)test_inputs[i]), + sha256_digest); + if (!memcmp(sha256_digest, sha256_results[i], SHA256_DIGEST_SIZE)) { + fprintf(stderr, "Test vector %d PASSED for SHA-256\n", i+1); + } + else { + fprintf(stderr, "Test vector %d FAILED for SHA-256\n", i+1); + success = 0; + } + } + return success; +} + +int SHA512_tests(void) { + int i, success = 1; + uint8_t sha512_digest[SHA512_DIGEST_SIZE]; + uint8_t* test_inputs[3]; + test_inputs[0] = (uint8_t *) oneblock_msg; + test_inputs[1] = (uint8_t *) multiblock_msg2; + test_inputs[2] = (uint8_t *) long_msg; + + for (i = 0; i < 3; i++) { + SHA512(test_inputs[i], strlen((char *)test_inputs[i]), + sha512_digest); + if (!memcmp(sha512_digest, sha512_results[i], SHA512_DIGEST_SIZE)) { + fprintf(stderr, "Test vector %d PASSED for SHA-512\n", i+1); + } + else { + fprintf(stderr, "Test vector %d FAILED for SHA-512\n", i+1); + success = 0; + } + } + return success; +} + +int main(int argc, char* argv[]) { + int success = 1; + /* Initialize long_msg with 'a' x 1,000,000 */ + long_msg = (char *) malloc(1000001); + memset(long_msg, 'a', 1000000); + long_msg[1000000]=0; + + if (!SHA1_tests()) + success = 0; + if (!SHA256_tests()) + success = 0; + if (!SHA512_tests()) + success = 0; + + free(long_msg); + + return !success; +} diff --git a/tests/verify_data.c b/tests/verify_data.c new file mode 100644 index 0000000000..d5b1c99d24 --- /dev/null +++ b/tests/verify_data.c @@ -0,0 +1,243 @@ +/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Routines for verifying a file's signature. Useful in testing the core + * RSA verification implementation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "padding.h" +#include "rsa.h" +#include "sha.h" +#include "verify_data.h" + + +RSAPublicKey* read_RSAkey(char *input_file, int len) { + int key_fd; + RSAPublicKey *key = NULL; + + if ((key_fd = open(input_file, O_RDONLY)) == -1) { + fprintf(stderr, "Couldn't open pre-processed key file\n"); + return NULL; + } + + key = (RSAPublicKey *) malloc(sizeof(RSAPublicKey)); + if (!key) + return NULL; + + /* Read the pre-processed RSA key into a RSAPublicKey structure */ + /* TODO(gauravsh): Add error checking here? */ + + read(key_fd, &key->len, sizeof(key->len)); + read(key_fd, &key->n0inv, sizeof(key->n0inv)); + +#ifndef NDEBUG + fprintf(stderr, "%d\n", key->len); + fprintf(stderr, "%d\n", key->n0inv); +#endif + + key->n = (uint32_t *) malloc(len); + read(key_fd, key->n, len); + + key->rr = (uint32_t *) malloc(len); + read(key_fd, key->rr, len); + +#ifndef NDEBUG + { + int i; + for(i=0; ilen; i++) { + fprintf(stderr, "%d,", key->n[i]); + } + fprintf(stderr, "\n"); + + for(i=0; ilen; i++) { + fprintf(stderr, "%d,", key->rr[i]); + } + fprintf(stderr, "\n"); + } +#endif + + close(key_fd); + return key; +} + +uint8_t* SHA1_file(char *input_file) { + int i, input_fd, len; + uint8_t data[SHA1_BLOCK_SIZE], *digest = NULL, *p = NULL; + SHA1_CTX ctx; + + if( (input_fd = open(input_file, O_RDONLY)) == -1 ) { + fprintf(stderr, "Couldn't open input file.\n"); + return NULL; + } + + /* Calculate SHA1 hash of input blocks, reading one block at a time. */ + SHA1_init(&ctx); + while ( (len = read(input_fd, data, SHA1_BLOCK_SIZE)) == SHA1_BLOCK_SIZE) + SHA1_update(&ctx, data, len); + if (len != -1) + SHA1_update(&ctx, data, len); + p = SHA1_final(&ctx); + close(input_fd); + + digest = (uint8_t*) malloc(SHA1_DIGEST_SIZE); + if (!digest) + return NULL; + for (i=0; i < SHA1_DIGEST_SIZE; i++) + digest[i] = *p++; + + return digest; +} + +uint8_t* SHA256_file(char *input_file) { + int i, input_fd, len; + uint8_t data[SHA256_BLOCK_SIZE], *digest = NULL, *p = NULL; + SHA256_CTX ctx; + + if( (input_fd = open(input_file, O_RDONLY)) == -1 ) { + fprintf(stderr, "Couldn't open input file.\n"); + return NULL; + } + + /* Calculate SHA256 hash of file, reading one block at a time. */ + SHA256_init(&ctx); + while ( (len = read(input_fd, data, SHA256_BLOCK_SIZE)) == SHA256_BLOCK_SIZE) + SHA256_update(&ctx, data, len); + if (len != -1) + SHA256_update(&ctx, data, len); + p = SHA256_final(&ctx); + close(input_fd); + + digest = (uint8_t*) malloc(SHA256_DIGEST_SIZE); + if (!digest) + return NULL; + for (i=0; i < SHA256_DIGEST_SIZE; i++) + digest[i] = *p++; + + return digest; +} + +uint8_t* SHA512_file(char* input_file) { + int input_fd; + uint8_t data[SHA512_BLOCK_SIZE], *digest = NULL, *p = NULL; + int i, len; + SHA512_CTX ctx; + + if( (input_fd = open(input_file, O_RDONLY)) == -1 ) { + fprintf(stderr, "Couldn't open input file.\n"); + return NULL; + } + + /* Calculate SHA512 hash of file, reading one block at a time. */ + SHA512_init(&ctx); + while ( (len = read(input_fd, data, SHA512_BLOCK_SIZE)) == SHA512_BLOCK_SIZE) + SHA512_update(&ctx, data, len); + if (len != -1) + SHA512_update(&ctx, data, len); + p = SHA512_final(&ctx); + close(input_fd); + + digest = (uint8_t*) malloc(SHA512_DIGEST_SIZE); + if (!digest) + return NULL; + for (i=0; i < SHA512_DIGEST_SIZE; i++) + digest[i] = *p++; + + return digest; +} + + +uint8_t* calculate_digest(char *input_file, int algorithm) { + typedef uint8_t* (*Hash_file_ptr) (char*); + Hash_file_ptr hash_file[] = { + SHA1_file, /* RSA 1024 */ + SHA256_file, + SHA512_file, + SHA1_file, /* RSA 2048 */ + SHA256_file, + SHA512_file, + SHA1_file, /* RSA 4096 */ + SHA256_file, + SHA512_file, + SHA1_file, /* RSA 8192 */ + SHA256_file, + SHA512_file, + }; + return hash_file[algorithm](input_file); +} + +uint8_t* read_signature(char *input_file, int len) { + int i, sigfd; + uint8_t *signature = NULL; + if ((sigfd = open(input_file, O_RDONLY)) == -1) { + fprintf(stderr, "Couldn't open signature file\n"); + return NULL; + } + + /* Read the signature into a buffer*/ + signature = (uint8_t*) malloc(len); + if (!signature) + return NULL; + + if( (i = read(sigfd, signature, len)) != len ) { + fprintf(stderr, "Wrong signature length - Expected = %d, Received = %d\n", + len, i); + close(sigfd); + return NULL; + } + + close(sigfd); + return signature; +} + + +int main(int argc, char* argv[]) { + int i, algorithm, sig_len; + uint8_t *digest = NULL, *signature = NULL; + RSAPublicKey* key = NULL; + + if (argc!=5) { + fprintf(stderr, "Usage: %s " + " \n\n", argv[0]); + fprintf(stderr, "where depends on the signature algorithm" + " used:\n"); + for(i = 0; i= kNumAlgorithms) { + fprintf(stderr, "Invalid Algorithm!\n"); + return 0; + } + /* Length of the RSA Signature/RSA Key */ + sig_len = siglen_map[algorithm] * sizeof(uint32_t); + + if (!(key = read_RSAkey(argv[2], sig_len))) + goto failure; + if (!(signature = read_signature(argv[3], sig_len))) + goto failure; + if (!(digest = calculate_digest(argv[4], algorithm))) + goto failure; + if(RSA_verify(key, signature, sig_len, algorithm, digest)) + fprintf(stderr, "Signature Verification SUCCEEDED.\n"); + else + fprintf(stderr, "Signature Verification FAILED!\n"); + +failure: + free(key); + free(signature); + free(digest); + + return 0; +} diff --git a/tests/verify_data.h b/tests/verify_data.h new file mode 100644 index 0000000000..e377415aef --- /dev/null +++ b/tests/verify_data.h @@ -0,0 +1,41 @@ +/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef VBOOT_REFERENCE_VERIFY_DATA_H_ +#define VBOOT_REFERENCE_VERIFY_DATA_H_ + +/* Reads a pre-processed key of a [len] bytes from [input_file] and + * returns it in a RSAPublicKey structure. + * Caller owns the returned key and must free it. + */ +RSAPublicKey* read_RSAkey(char *input_file, int len); + +/* Returns the SHA-1 digest of [input_file]. + * Caller owns the returned digest and must free it. + */ +uint8_t* SHA1_file(char *input_file); + +/* Returns the SHA-256 digest of [input_file]. + * Caller owns the returned digest and must free it. + */ +uint8_t* SHA256_file(char *input_file); + +/* Returns the SHA-512 digest of [input_file]. + * Caller owns the returned digest and must free it. + */ +uint8_t* SHA512_file(char *input_file); + +/* Returns the appropriate digest for the [input_file] based on the + * signature [algorithm]. + * Caller owns the returned digest and must free it. + */ +uint8_t* calculate_digest(char *input_file, int algorithm); + +/* Return a signature of [len] bytes read from [input_file]. + * Caller owns the returned signature and must free it. + */ +uint8_t* read_signature(char *input_file, int len); + +#endif /* VBOOT_REFERENCE_VERIFY_DATA_H_ */ diff --git a/utils/Makefile b/utils/Makefile new file mode 100644 index 0000000000..e7ebc7e6f0 --- /dev/null +++ b/utils/Makefile @@ -0,0 +1,13 @@ +# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +LIBS=-lcrypto + +all: dumpRSAPublicKey + +dumpRSAPublicKey: dumpRSAPublicKey.c + $(CC) $(CFLAGS) $(LIBS) $< -o $@ + +clean: + rm -f dumpRSAPublicKey diff --git a/utils/dumpRSAPublicKey.c b/utils/dumpRSAPublicKey.c new file mode 100644 index 0000000000..232fe9969a --- /dev/null +++ b/utils/dumpRSAPublicKey.c @@ -0,0 +1,175 @@ +/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* C port of DumpPublicKey.java from the Android Open source project with + * support for additional RSA key sizes. (platform/system/core,git/libmincrypt + * /tools/DumpPublicKey.java). Uses the OpenSSL X509 and BIGNUM library. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Command line tool to extract RSA public keys from X.509 certificates + * and output a pre-processed version of keys for use by RSA verification + * routines. + */ + +int check(RSA* key) { + int public_exponent = BN_get_word(key->e); + int modulus = BN_num_bits(key->n); + + if (public_exponent != 65537) { + fprintf(stderr, "WARNING: Public exponent should be 65537 (but is %d).\n", + public_exponent); + } + + if (modulus != 1024 && modulus != 2048 && modulus != 4096 + && modulus != 8192) { + fprintf(stderr, "ERROR: Unknown modulus length = %d.\n", modulus); + return 0; + } + return 1; +} + +/* Pre-processes and outputs RSA public key to standard out. + */ +void output(RSA* key) { + int i, nwords; + BIGNUM *N = key->n; + BIGNUM *Big1, *Big2, *Big32, *BigMinus1; + BIGNUM *B; + BIGNUM *N0inv, *R, *RR, *RRTemp, *NnumBits; + BIGNUM *n, *rr; + BN_CTX *bn_ctx = BN_CTX_new(); + uint32_t n0invout; + + N = key->n; + /* Output size of RSA key in 32-bit words */ + nwords = BN_num_bits(N) / 32; + write(1, &nwords, sizeof(nwords)); + + /* Initialize BIGNUMs */ + Big1 = BN_new(); + Big2 = BN_new(); + Big32 = BN_new(); + BigMinus1 = BN_new(); + N0inv= BN_new(); + R = BN_new(); + RR = BN_new(); + RRTemp = BN_new(); + NnumBits = BN_new(); + n = BN_new(); + rr = BN_new(); + + + BN_set_word(Big1, 1L); + BN_set_word(Big2, 2L); + BN_set_word(Big32, 32L); + BN_sub(BigMinus1, Big1, Big2); + + B = BN_new(); + BN_exp(B, Big2, Big32, bn_ctx); /* B = 2^32 */ + + /* Calculate and output N0inv = -1 / N[0] mod 2^32 */ + BN_mod_inverse(N0inv, N, B, bn_ctx); + BN_sub(N0inv, B, N0inv); + n0invout = BN_get_word(N0inv); + write(1, &n0invout, sizeof(n0invout)); + + /* Calculate R = 2^(# of key bits) */ + BN_set_word(NnumBits, BN_num_bits(N)); + BN_exp(R, Big2, NnumBits, bn_ctx); + + /* Calculate RR = R^2 mod N */ + BN_copy(RR, R); + BN_mul(RRTemp, RR, R, bn_ctx); + BN_mod(RR, RRTemp, N, bn_ctx); + + + /* Write out modulus as little endian array of integers. */ + for (i = 0; i < nwords; ++i) { + uint32_t nout; + + BN_mod(n, N, B, bn_ctx); /* n = N mod B */ + nout = BN_get_word(n); + write(1, &nout, sizeof(nout)); + + BN_rshift(N, N, 32); /* N = N/B */ + } + + /* Write R^2 as little endian array of integers. */ + for (i = 0; i < nwords; ++i) { + uint32_t rrout; + + BN_mod(rr, RR, B, bn_ctx); /* rr = RR mod B */ + rrout = BN_get_word(rr); + write(1, &rrout, sizeof(rrout)); + + BN_rshift(RR, RR, 32); /* RR = RR/B */ + } + + /* Free BIGNUMs. */ + BN_free(Big1); + BN_free(Big2); + BN_free(Big32); + BN_free(BigMinus1); + BN_free(N0inv); + BN_free(R); + BN_free(RRTemp); + BN_free(NnumBits); + BN_free(n); + BN_free(rr); + +} + +int main(int argc, char* argv[]) { + FILE* fp; + X509* cert = NULL; + RSA* pubkey = NULL; + EVP_PKEY* key; + + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return -1; + } + + fp = fopen(argv[1], "r"); + + if (!fp) { + fprintf(stderr, "Couldn't open certificate file!\n"); + return -1; + } + + /* Read the certificate */ + if (!PEM_read_X509(fp, &cert, NULL, NULL)) { + fprintf(stderr, "Couldn't read certificate.\n"); + goto fail; + } + + /* Get the public key from the certificate. */ + key = X509_get_pubkey(cert); + + /* Convert to a RSA_style key. */ + if (!(pubkey = EVP_PKEY_get1_RSA(key))) { + fprintf(stderr, "Couldn't convert to a RSA style key.\n"); + goto fail; + } + + if (check(pubkey)) { + output (pubkey); + } + +fail: + X509_free(cert); + RSA_free(pubkey); + fclose(fp); + + return 0; +}