diff --git a/code/autoupdater/rsa_tools/build-libtom-unix.sh b/code/autoupdater/rsa_tools/build-libtom-unix.sh new file mode 100755 index 00000000..8700e868 --- /dev/null +++ b/code/autoupdater/rsa_tools/build-libtom-unix.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +TFMVER=0.13.1 +LTCVER=1.17 +set -e + +OSTYPE=`uname -s` +if [ "$OSTYPE" = "Linux" ]; then + NCPU=`cat /proc/cpuinfo |grep vendor_id |wc -l` + let NCPU=$NCPU+1 +elif [ "$OSTYPE" = "Darwin" ]; then + NCPU=`sysctl -n hw.ncpu` +elif [ "$OSTYPE" = "SunOS" ]; then + NCPU=`/usr/sbin/psrinfo |wc -l |sed -e 's/^ *//g;s/ *$//g'` +else + NCPU=1 +fi + +if [ -z "$NCPU" ]; then + NCPU=1 +elif [ "$NCPU" = "0" ]; then + NCPU=1 +fi + +if [ ! -f ./crypt-$LTCVER.tar.bz2 ]; then + echo "Downloading LibTomCrypt $LTCVER sources..." + curl -L -o crypt-$LTCVER.tar.bz2 https://github.com/libtom/libtomcrypt/releases/download/$LTCVER/crypt-$LTCVER.tar.bz2 || exit 1 +fi + +if [ ! -f tfm-$TFMVER.tar.xz ]; then + echo "Downloading TomsFastMath $TFMVER sources..." + curl -L -o tfm-$TFMVER.tar.xz https://github.com/libtom/tomsfastmath/releases/download/v$TFMVER/tfm-$TFMVER.tar.xz || exit 1 +fi + +if [ ! -d tomsfastmath-$TFMVER ]; then + echo "Unpacking TomsFastMath $TFMVER sources..." + tar -xJvvf ./tfm-$TFMVER.tar.xz +fi + +if [ ! -d libtomcrypt-$LTCVER ]; then + echo "Unpacking LibTomCrypt $LTCVER sources..." + tar -xjvvf ./crypt-$LTCVER.tar.bz2 +fi + +echo +echo +echo "Will use make -j$NCPU. If this is wrong, check NCPU at top of script." +echo +echo + +set -e +set -x + +# Some compilers can't handle the ROLC inline asm; just turn it off. +cd tomsfastmath-$TFMVER +make -j$NCPU +cd .. + +export CFLAGS="$CFLAGS -DTFM_DESC -DLTC_NO_ROLC -I ../tomsfastmath-$TFMVER/src/headers" +cd libtomcrypt-$LTCVER +make -j$NCPU +cd .. + +set +x +echo "All done." + +# end of build-libtom-unix.sh ... + diff --git a/code/autoupdater/rsa_tools/build-rsa-tools.sh b/code/autoupdater/rsa_tools/build-rsa-tools.sh new file mode 100755 index 00000000..212d6e70 --- /dev/null +++ b/code/autoupdater/rsa_tools/build-rsa-tools.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# You don't need these to be built with the autoupdater, so here's a simple +# shell file to make them on a Mac. + +export TFMDIR="tomsfastmath-0.13.1" +export LTCDIR="libtomcrypt-1.17" + +function build { + clang -I $TFMDIR/src/headers -I $LTCDIR/src/headers -o "$1" -Wall -O3 "$1.c" rsa_common.c $LTCDIR/libtomcrypt.a $TFMDIR/libtfm.a +} + +set -e +set -x + +./build-libtom-unix.sh +build rsa_make_keys +build rsa_sign +build rsa_verify + +set +x +echo "rsa_tools are compiled!" + diff --git a/code/autoupdater/rsa_tools/rsa_common.c b/code/autoupdater/rsa_tools/rsa_common.c new file mode 100644 index 00000000..d0a10a0a --- /dev/null +++ b/code/autoupdater/rsa_tools/rsa_common.c @@ -0,0 +1,61 @@ +#include "rsa_common.h" + +void fail(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputs("\n", stderr); + fflush(stderr); + exit(1); +} + +void write_file(const char *fname, const void *buf, const unsigned long len) +{ + FILE *io = fopen(fname, "wb"); + if (!io) { + fail("Can't open '%s' for writing: %s", fname, strerror(errno)); + } + + if (fwrite(buf, len, 1, io) != 1) { + fail("Couldn't write '%s': %s", fname, strerror(errno)); + } + + if (fclose(io) != 0) { + fail("Couldn't flush '%s' to disk: %s", fname, strerror(errno)); + } +} + +void read_file(const char *fname, void *buf, unsigned long *len) +{ + ssize_t br; + FILE *io = fopen(fname, "rb"); + if (!io) { + fail("Can't open '%s' for reading: %s", fname, strerror(errno)); + } + + br = fread(buf, 1, *len, io); + if (ferror(io)) { + fail("Couldn't read '%s': %s", fname, strerror(errno)); + } else if (!feof(io)) { + fail("Buffer too small to read '%s'", fname); + } + fclose(io); + + *len = (unsigned long) br; +} + +void read_rsakey(rsa_key *key, const char *fname) +{ + unsigned char buf[4096]; + unsigned long len = sizeof (buf); + int rc; + + read_file(fname, buf, &len); + + if ((rc = rsa_import(buf, len, key)) != CRYPT_OK) { + fail("rsa_import for '%s' failed: %s", fname, error_to_string(rc)); + } +} + diff --git a/code/autoupdater/rsa_tools/rsa_common.h b/code/autoupdater/rsa_tools/rsa_common.h new file mode 100644 index 00000000..48695522 --- /dev/null +++ b/code/autoupdater/rsa_tools/rsa_common.h @@ -0,0 +1,30 @@ +#ifndef _INCL_RSA_COMMON_H_ +#define _INCL_RSA_COMMON_H_ 1 + +#include +#include +#include + +#define TFM_DESC +#define LTC_NO_ROLC +#include "tomcrypt.h" + +#define SALT_LEN 8 + +#if defined(__GNUC__) || defined(__clang__) +#define NEVER_RETURNS __attribute__((noreturn)) +#define PRINTF_FUNC(fmtargnum, dotargnum) __attribute__ (( format( __printf__, fmtargnum, dotargnum ))) +#else +#define NEVER_RETURNS +#define PRINTF_FUNC(fmtargnum, dotargnum) +#endif + +void fail(const char *fmt, ...) NEVER_RETURNS PRINTF_FUNC(1, 2); +void write_file(const char *fname, const void *buf, const unsigned long len); +void read_file(const char *fname, void *buf, unsigned long *len); +void read_rsakey(rsa_key *key, const char *fname); + +#endif + +/* end of rsa_common.h ... */ + diff --git a/code/autoupdater/rsa_tools/rsa_make_keys.c b/code/autoupdater/rsa_tools/rsa_make_keys.c new file mode 100644 index 00000000..a7f801c0 --- /dev/null +++ b/code/autoupdater/rsa_tools/rsa_make_keys.c @@ -0,0 +1,45 @@ +#include "rsa_common.h" + +static void write_rsakey(rsa_key *key, const int type, const char *fname) +{ + unsigned char buf[4096]; + unsigned long len = sizeof (buf); + int rc; + + if ((rc = rsa_export(buf, &len, type, key)) != CRYPT_OK) { + fail("rsa_export for '%s' failed: %s", fname, error_to_string(rc)); + } + write_file(fname, buf, len); +} + +int main(int argc, char **argv) +{ + int rc = 0; + prng_state prng; + int prng_index; + rsa_key key; + + ltc_mp = tfm_desc; + prng_index = register_prng(&sprng_desc); /* (fortuna_desc is a good choice if your platform's PRNG sucks.) */ + + if (prng_index == -1) { + fail("Failed to register a RNG"); + } + + if ((rc = rng_make_prng(128, prng_index, &prng, NULL)) != CRYPT_OK) { + fail("rng_make_prng failed: %s", error_to_string(rc)); + } + + if ((rc = rsa_make_key(&prng, prng_index, 256, 65537, &key)) != CRYPT_OK) { + fail("rng_make_key failed: %s", error_to_string(rc)); + } + + write_rsakey(&key, PK_PRIVATE, "privatekey.bin"); + write_rsakey(&key, PK_PUBLIC, "publickey.bin"); + + rsa_free(&key); + + return 0; +} + +/* end of rsa_make_keys.c ... */ diff --git a/code/autoupdater/rsa_tools/rsa_sign.c b/code/autoupdater/rsa_tools/rsa_sign.c new file mode 100644 index 00000000..5eec24dd --- /dev/null +++ b/code/autoupdater/rsa_tools/rsa_sign.c @@ -0,0 +1,75 @@ +#include "rsa_common.h" + +static void sign_file(const char *fname, rsa_key *key, prng_state *prng, const int prng_index, const int hash_index) +{ + const size_t sigfnamelen = strlen(fname) + 5; + char *sigfname = (char *) malloc(sigfnamelen); + unsigned char hash[256]; + unsigned long hashlen = sizeof (hash); + unsigned char sig[1024]; + unsigned long siglen = sizeof (sig); + int rc = 0; + int status = 0; + + if (!sigfname) { + fail("out of memory"); + } + + if ((rc = hash_file(hash_index, fname, hash, &hashlen)) != CRYPT_OK) { + fail("hash_file for '%s' failed: %s", fname, error_to_string(rc)); + } + + if ((rc = rsa_sign_hash(hash, hashlen, sig, &siglen, prng, prng_index, hash_index, SALT_LEN, key)) != CRYPT_OK) { + fail("rsa_sign_hash for '%s' failed: %s", fname, error_to_string(rc)); + } + + if ((rc = rsa_verify_hash(sig, siglen, hash, hashlen, hash_index, SALT_LEN, &status, key)) != CRYPT_OK) { + fail("rsa_verify_hash for '%s' failed: %s", fname, error_to_string(rc)); + } + + if (!status) { + fail("Generated signature isn't valid! Bug in the program!"); + } + + snprintf(sigfname, sigfnamelen, "%s.sig", fname); + write_file(sigfname, sig, siglen); + free(sigfname); +} + +int main(int argc, char **argv) +{ + int rc = 0; + prng_state prng; + int prng_index, hash_index; + rsa_key key; + int i; + + ltc_mp = tfm_desc; + + prng_index = register_prng(&sprng_desc); /* (fortuna_desc is a good choice if your platform's PRNG sucks.) */ + if (prng_index == -1) { + fail("Failed to register a RNG"); + } + + hash_index = register_hash(&sha256_desc); + if (hash_index == -1) { + fail("Failed to register sha256 hasher"); + } + + if ((rc = rng_make_prng(128, prng_index, &prng, NULL)) != CRYPT_OK) { + fail("rng_make_prng failed: %s", error_to_string(rc)); + } + + read_rsakey(&key, "privatekey.bin"); + + for (i = 1; i < argc; i++) { + sign_file(argv[i], &key, &prng, prng_index, hash_index); + } + + rsa_free(&key); + + return 0; +} + +/* end of rsa_sign.c ... */ + diff --git a/code/autoupdater/rsa_tools/rsa_verify.c b/code/autoupdater/rsa_tools/rsa_verify.c new file mode 100644 index 00000000..09abfb24 --- /dev/null +++ b/code/autoupdater/rsa_tools/rsa_verify.c @@ -0,0 +1,60 @@ +#include "rsa_common.h" + +static void verify_file(const char *fname, rsa_key *key, const int hash_index) +{ + const size_t sigfnamelen = strlen(fname) + 5; + char *sigfname = (char *) malloc(sigfnamelen); + unsigned char hash[256]; + unsigned long hashlen = sizeof (hash); + unsigned char sig[1024]; + unsigned long siglen = sizeof (sig); + int status = 0; + int rc = 0; + + if (!sigfname) { + fail("out of memory"); + } + + snprintf(sigfname, sigfnamelen, "%s.sig", fname); + read_file(sigfname, sig, &siglen); + free(sigfname); + + if ((rc = hash_file(hash_index, fname, hash, &hashlen)) != CRYPT_OK) { + fail("hash_file for '%s' failed: %s", fname, error_to_string(rc)); + } + + if ((rc = rsa_verify_hash(sig, siglen, hash, hashlen, hash_index, SALT_LEN, &status, key)) != CRYPT_OK) { + fail("rsa_verify_hash for '%s' failed: %s", fname, error_to_string(rc)); + } + + if (!status) { + fail("Invalid signature for '%s'! Don't trust this file!", fname); + } +} + +int main(int argc, char **argv) +{ + int hash_index; + rsa_key key; + int i; + + ltc_mp = tfm_desc; + + hash_index = register_hash(&sha256_desc); + if (hash_index == -1) { + fail("Failed to register sha256 hasher"); + } + + read_rsakey(&key, "publickey.bin"); + + for (i = 1; i < argc; i++) { + verify_file(argv[i], &key, hash_index); + } + + rsa_free(&key); + + return 0; +} + +/* end of rsa_verify.c ... */ + diff --git a/code/autoupdater/rsa_tools/test-rsa-tools.sh b/code/autoupdater/rsa_tools/test-rsa-tools.sh new file mode 100755 index 00000000..f4b4bdb0 --- /dev/null +++ b/code/autoupdater/rsa_tools/test-rsa-tools.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +if [ -f privatekey.bin ]; then + echo "move your existing keys out of the way." + exit 1 +fi + +( ./rsa_make_keys && echo "key making okay") || echo "key making NOT okay" +echo "The quick brown fox jumped over the lazy dog." >testmsg.txt +( ./rsa_sign testmsg.txt && echo "signing okay" ) || echo "signing NOT okay" +( ./rsa_verify testmsg.txt && echo "basic verifying okay" ) || echo "basic verifying NOT okay" +echo "The quick brown fox jumped over the lazy dog!" >testmsg.txt +( ./rsa_verify testmsg.txt 2>/dev/null && echo "tamper test NOT okay" ) || echo "tamper test okay" +echo "The quick brown fox jumped over the lazy dog." >testmsg.txt +( ./rsa_verify testmsg.txt && echo "reverify okay" ) || echo "reverify NOT okay" +rm -f testmsg.txt testmsg.txt.sig publickey.bin privatekey.bin +