[ioquake3] 96/136: Initial add of rsa_tools.

Simon McVittie smcv at debian.org
Thu Jun 15 09:09:14 UTC 2017


This is an automated email from the git hooks/post-receive script.

smcv pushed a commit to branch debian/master
in repository ioquake3.

commit ece37f13905ca39e028c13be372fc79eae87639a
Author: Ryan C. Gordon <icculus at icculus.org>
Date:   Sat Jun 3 20:26:07 2017 -0400

    Initial add of rsa_tools.
    
    This is just a simple RSA public key digital signature thing built on
    libtomcrypt. The gist:
    
    Some admin will generate a public/private key with rsa_make_keys, keeping the
    private key secret. Using the private key and rsa_sign, the admin will sign
    the autoupdater manifests, generating manifest.txt.sig.
    
    The public key ships with the game (adding 270 bytes to the download), the
    .sig is downloaded with the manifest by the autoupdater (256 bytes extra
    download), then the autoupdater checks the manifest against the signature
    with the public key. if the signature isn't valid (the manifest was tampered
    with or corrupt), the autoupdater refuses to continue.
    
    If the manifest is to be trusted, it lists sha256 checksums for every file to
    download, so there's no need to sign every file; if they can't tamper with the
    manifest, they can't tamper with any other file to be updated since the file's
    listed sha256 won't match.
    
    If the private key is compromised, we generate new keys and ship new
    installers, so new installations will be able to update but existing ones
    will need to do a new install to keep getting updates. Don't let the private
    key get compromised. The private key doesn't go on a public server. Maybe it
    doesn't even live on the admin's laptop hard drive.
    
    If the download server is compromised and serving malware, the autoupdater
    will reject it outright if they haven't compromised the private key, generated
    a new manifest, and signed it with the private key.
    
    libtomcrypt is sort of a big pile of source code, so instead of putting it
    in revision control, we have a script to download it. Most things don't need
    it. It lives on GitHub, so we _could_ do a git submodule, but most people
    don't need it, so why waste their disk and bandwidth? That said, when compiled
    you end up with a few hundred kilobytes of binary code to verify a signature
    and no external dependencies, so it seems like a win.
---
 code/autoupdater/rsa_tools/build-libtom-unix.sh | 68 ++++++++++++++++++++++
 code/autoupdater/rsa_tools/build-rsa-tools.sh   | 23 ++++++++
 code/autoupdater/rsa_tools/rsa_common.c         | 61 ++++++++++++++++++++
 code/autoupdater/rsa_tools/rsa_common.h         | 30 ++++++++++
 code/autoupdater/rsa_tools/rsa_make_keys.c      | 45 +++++++++++++++
 code/autoupdater/rsa_tools/rsa_sign.c           | 75 +++++++++++++++++++++++++
 code/autoupdater/rsa_tools/rsa_verify.c         | 60 ++++++++++++++++++++
 code/autoupdater/rsa_tools/test-rsa-tools.sh    | 17 ++++++
 8 files changed, 379 insertions(+)

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 0000000..8700e86
--- /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 0000000..212d6e7
--- /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 0000000..d0a10a0
--- /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 0000000..4869552
--- /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 <stdarg.h>
+#include <stdio.h>
+#include <errno.h>
+
+#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 0000000..a7f801c
--- /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 0000000..5eec24d
--- /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 0000000..09abfb2
--- /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 0000000..f4b4bdb
--- /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
+

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/ioquake3.git



More information about the Pkg-games-commits mailing list