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_ */