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/tests/Makefile b/tests/Makefile
new file mode 100644
index 0000000..e3694cf
--- /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 0000000..bfde0a2
--- /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 0000000..640c06b
--- /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 0000000..2c07b3f
--- /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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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 0000000..d5b1c99
--- /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 <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#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; i<key->len; i++) {
+ fprintf(stderr, "%d,", key->n[i]);
+ }
+ fprintf(stderr, "\n");
+
+ for(i=0; i<key->len; 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 <algorithm> <key file> <signature file>"
+ " <input file>\n\n", argv[0]);
+ fprintf(stderr, "where <algorithm> depends on the signature algorithm"
+ " used:\n");
+ for(i = 0; i<kNumAlgorithms; i++)
+ fprintf(stderr, "\t%d for %s\n", i, algo_strings[i]);
+ return -1;
+ }
+
+ algorithm = atoi(argv[1]);
+ if (algorithm >= 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 0000000..e377415
--- /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_ */