Ver código fonte

Merge branch 'fix/posix'

Michele Orrù 11 anos atrás
pai
commit
e029bab6a4

+ 20 - 4
configure.ac

@@ -2,20 +2,24 @@
 # Process this file with autoconf to produce a configure script.
 
 AC_PREREQ([2.65])
-AC_INIT([question_authority], [0.1], [maker@tumbolandia.n])
+AC_INIT([question_authority], [0.1], [maker@tumbolandia.net])
 AM_INIT_AUTOMAKE
 AC_CONFIG_SRCDIR([src/qa.c])
 AC_CONFIG_HEADERS([config.h])
 
 # Checks for programs.
-AC_PROG_CC
+AC_PROG_CC([clang gcc cc])
+AC_PROG_CC_C99
 AC_PROG_RANLIB
 
 # Checks for libraries.
 
 # Checks for header files.
 AC_CHECK_HEADERS([limits.h stdint.h stdlib.h string.h unistd.h])
-AC_CHECK_HEADERS([openssl/ssl.h])
+AC_CHECK_HEADER(bsd/sys/queue.h, [:],
+                [AC_MSG_ERROR([Could not find or include bsd queues. Please install libbsd-dev.])])
+AC_CHECK_HEADERS([openssl/ssl.h openssl/bn.h openssl/x509.h openssl/rsa.h], [:],
+                 AC_MSG_ERROR([Could not find or include openssl headers. Please install libssl-dev.]))
 
 # Checks for typedefs, structures, and compiler characteristics.
 AC_TYPE_SIZE_T
@@ -25,4 +29,16 @@ AC_FUNC_ERROR_AT_LINE
 AC_FUNC_MALLOC
 #AC_CHECK_FUNCS([dup2 setlocale strdup])
 
-AC_OUTPUT([Makefile src/Makefile src/questions/Makefile src/apps/Makefile ])
+# Add compiler/linker flags
+CFLAGS="$CFLAGS --std=c99 -I$(pwd)/src/include -Wall"
+
+# Adding package options
+AC_ARG_ENABLE(debug,
+   AS_HELP_STRING([--enable-debug], [enable debugging, default: no]),
+   CFLAGS+=" -DDEBUG -ggdb")
+
+AC_OUTPUT([Makefile
+           src/Makefile src/questions/Makefile
+           src/apps/Makefile
+           src/tests/Makefile src/questions/tests/Makefile
+           ])

+ 1 - 10
reports/report5.org

@@ -6,14 +6,9 @@
 #+EMAIL:  maker@tumbolandia.net
 
 * DOING Cleanup the thesis .template
-  + Some small improvements to the thesis documentclass. For what is worth, that
-    could be adopted also for future thesis.
-  + Copying template.tex and starting the creation of the master file for my
-    thesis.
+  + Laurea deve essere scirtta
   + [[http://tesi.clusit.it/regolamento.php][Premio tesi]] - Non riesco a parteciparci vero?
 * DOING Reviewing MPI library for running c code on a cluster.
-  + creating some small, random programs designed to work in parallel
-  + running those programs in cluster.science.unitn.it
 * DOING Implementing Dixon's attack
   + Finished implementation. Starting testing, but there are a few things that
     shall be discussed about, the tuning of some constants.
@@ -21,7 +16,3 @@
     * e_bits
     * REPOP_EPOCHS
   + Discuss the algorithm for populating the R_pool
-* DOING Hacking with autoconf/automake
-  + Providing a test suite that can be easily run with `make check`
-  + Uniforming compiler options, and giving `--enable-debug` option to the
-    ./configure script

+ 2 - 7
src/Makefile.am

@@ -1,10 +1,5 @@
-SUBDIRS = questions/ apps/
-
-CC = clang
-
-AM_LDFLAGS = -lcrypto -lm -lssl
-AM_CFLAGS = --std=c99 -ggdb -I include/ -I questions/
+SUBDIRS = questions/ apps/ tests/
 
 bin_PROGRAMS = qa
 qa_SOURCES = qa.c qa_sock.c cmdline.c
-qa_LDADD = questions/libquestions.a
+qa_LDADD = questions/libquestions.a -lssl -lcrypto

+ 3 - 5
src/apps/Makefile.am

@@ -1,10 +1,8 @@
 bin_PROGRAMS = gen
 
-AM_CFLAGS = --std=c99
-
 gen_SOURCES = gen.c
-gen_LDFLAGS = -lcrypto -lssl
+gen_LDADD = -lcrypto -lssl
 
 
-gen.o:
-	$(CC) $(CFLAGS) $(LIBS) gen.c -c -o gen.o
+TESTS = tests/test_gen_pub.test \
+	tests/test_gen_priv.test

+ 93 - 22
src/apps/gen.c

@@ -8,45 +8,69 @@
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <unistd.h>
 
 #include <openssl/bn.h>
 #include <openssl/ssl.h>
 #include <openssl/rsa.h>
 
-
-void usage(void)
+static void
+usage(int ret)
 {
-  static const char* help_message = "%s usage: %s"
-    " <pub key> <priv key> <p> <q>"
-    "\n";
+  static const char* help_message = "%s usage: \n"
+    "%s pub [-n MODULUS | -p PRIME -q PRIME] -e PUBLIC_EXPONENT\n"
+    "%s priv -p PRIME -q PRIME -e PUBLIC_EXPONENT -d PRIVATE_EXPONENT\n";
   fprintf(stderr, help_message, program_invocation_short_name,
-          program_invocation_name);
+          program_invocation_name, program_invocation_name);
+
+  exit(ret);
 }
 
-int main(int argc, char **argv)
+
+static int
+pubkey_generation(RSA* rsa)
+{
+  BN_CTX *ctx = BN_CTX_new();
+
+  /* we need <N, e> to get a valid public key. */
+  if (!(rsa->e &&
+        (rsa->n ||(rsa->p && rsa->q)))) {
+    fprintf(stderr, "Not enough parameter for the public key generation!\n");
+    exit(EXIT_FAILURE);
+    }
+
+  if (!rsa->n) {
+    rsa->n = BN_new();
+    BN_mul(rsa->n, rsa->p, rsa->q, ctx);
+  }
+
+  PEM_write_RSAPublicKey(stdout, rsa);
+
+  BN_CTX_free(ctx);
+  RSA_free(rsa);
+
+  return EXIT_SUCCESS;
+}
+
+static int
+privkey_generation(RSA *rsa)
 {
-  BN_CTX* ctx;
-  BIGNUM* p1, *q1;
-  RSA* rsa;
+  BIGNUM *p1, *q1;
+  BN_CTX *ctx = BN_CTX_new();
+
+  if (!(rsa->p && rsa->q && rsa->e && rsa->d)) {
+    fprintf(stderr, "Not enough parameter for the private key generation!\n");
+    return EXIT_FAILURE;
+  }
 
-  rsa = RSA_new();
   p1 = BN_new();
   q1 = BN_new();
-  ctx = BN_CTX_new();
   rsa->n = BN_new();
   rsa->iqmp = BN_new();
   rsa->dmp1 = BN_new();
   rsa->dmq1 = BN_new();
 
-  if (argc < 4+1) {
-    usage();
-    return EXIT_FAILURE;
-  }
   /* generating RSA key */
-  BN_dec2bn(&rsa->e, argv[1]);
-  BN_dec2bn(&rsa->d, argv[2]);
-  BN_dec2bn(&rsa->p, argv[3]);
-  BN_dec2bn(&rsa->q, argv[4]);
   BN_mul(rsa->n, rsa->p, rsa->q, ctx);
   BN_sub(p1, rsa->p, BN_value_one());
   BN_sub(q1, rsa->q, BN_value_one());
@@ -55,22 +79,69 @@ int main(int argc, char **argv)
   BN_mod_inverse(rsa->iqmp, rsa->q, rsa->p, ctx);
   PEM_write_RSAPrivateKey(stdout, rsa, NULL, NULL, 0, NULL, NULL);
 
+  BN_CTX_free(ctx);
+  return EXIT_SUCCESS;
+}
+
+int main(int argc, char **argv)
+{
+  int opt;
+  RSA *rsa = RSA_new();
+
+  rsa->n = rsa->e = rsa->p = rsa->q = NULL;
+
+  if (argc < 3) usage(EXIT_FAILURE);
+
+  while ((opt = getopt(argc-1, argv+1, "d:e:N:n:p:q:")) != -1)  {
+    switch (opt) {
+    case 'N':
+    case 'n':
+      if (!BN_dec2bn(&rsa->n, optarg)) usage(EXIT_FAILURE);
+      break;
+    case 'd':
+      if (!BN_dec2bn(&rsa->d, optarg)) usage(EXIT_FAILURE);
+      break;
+    case 'e':
+      if (!BN_dec2bn(&rsa->e, optarg)) usage(EXIT_FAILURE);
+      break;
+    case 'p':
+      if (!BN_dec2bn(&rsa->p, optarg)) usage(EXIT_FAILURE);
+      break;
+    case 'q':
+      if (!BN_dec2bn(&rsa->q, optarg)) usage(EXIT_FAILURE);
+      break;
+    default:
+      usage(EXIT_FAILURE);
+    }
+  }
+
+  if (!strcmp(argv[1], "pub"))
+    return pubkey_generation(rsa);
+  else if (!strcmp(argv[1], "priv"))
+    return privkey_generation(rsa);
+  else
+    usage(EXIT_FAILURE);
+
   /* creating public key */
+  /*
   EVP_PKEY *pk;
   pk = EVP_PKEY_new();
   EVP_PKEY_set1_RSA(pk, rsa);
-
+  */
   /* creating dummy certificate */
+  /*
   X509* crt;
   crt = X509_new();
   if (!X509_set_pubkey(crt, pk)) exit(EXIT_FAILURE);
+  */
   /* PEM_write_X509(stdout, crt); */
+  /*
   X509_free(crt);
   EVP_PKEY_free(pk);
-  BN_CTX_free(ctx);
   BN_free(q1);
   BN_free(p1);
   RSA_free(rsa);
 
+  */
   return EXIT_SUCCESS;
 }

+ 6 - 0
src/apps/tests/test_gen_priv.test

@@ -0,0 +1,6 @@
+#!/bin/bash
+
+# N = 11 * 13
+# e = 103
+# d = 7
+./gen priv -p 11 -q 13 -e 103 -d 7 | openssl rsa -noout -modulus | grep -q '8F'

+ 24 - 0
src/apps/tests/test_gen_pub.test

@@ -0,0 +1,24 @@
+#!/bin/sh
+
+# N = 11 * 13
+# e = 103
+# d = 7
+./gen pub -N 143 -e 103 | grep -q PUBLIC
+if [ $? -ne 0 ]
+then
+    echo "should accept public exponent and public modulus."
+    exit 1
+fi
+
+
+# <http://cryptowars2011.forumfree.it/?t=58954862>
+p='5596931086554284281185296406554840964858720713572245165038844132225787390922\
+226340619345603534845283511495476913891096668345376424228245705523358529690689'
+q='5596931086554284281185296406554840964858720713572245165038844132225787390922\
+226340619345603534845283511495476913891096668345376424228245705523358529692809'
+./gen pub -p $p -q $q -e 29
+if [ $? -ne 0 ]
+then
+    echo "should accept the pair of primes <p, q> and the public exponent."
+    exit 1
+fi

+ 17 - 7
src/cmdline.c

@@ -14,7 +14,7 @@
 #include <string.h>
 #include <errno.h>
 
-#include "qa.h"
+#include "qa/qa.h"
 
 /**
  * \brief Prints the usage message, then exit.
@@ -25,7 +25,8 @@
 void usage(void)
 {
   static const char* help_message = "%s usage: %s"
-    " [-r HOST:port | -f FILE]"
+    " [-r HOST:port | -f X509 | -R RSA]"
+    " [-a ATTACK]"
     " \n";
   fprintf(stderr, help_message,
           program_invocation_short_name,
@@ -34,7 +35,7 @@ void usage(void)
 
 void conflicting_args(void)
 {
-  printf("Conflicting fuffa\n");
+  fprintf(stderr, "Conflicting arguments.\n");
   usage();
   exit(EXIT_FAILURE);
 }
@@ -43,18 +44,19 @@ int main(int argc, char** argv)
 {
   char opt;
   int option_index;
-  size_t i;
 
   struct option long_options[] = {
     {"help", no_argument, NULL, 'h'},
     {"remote", required_argument, NULL, 'r'},
     {"file", required_argument, NULL, 'f'},
+    {"rsa", required_argument, NULL, 'R'},
     {0, 0, 0, 0}
   };
-  static const char* short_options = "hr:f:";
+  static const char* short_options = "hr:f:a:R:";
 
   struct qa_conf conf = {
     .src_type = NONE,
+    .attacks = NULL,
   };
 
   while ((opt=getopt_long(argc, argv,
@@ -67,7 +69,7 @@ int main(int argc, char** argv)
       break;
     case 'f':
       if (conf.src_type != NONE) conflicting_args();
-      conf.src_type = LOCAL;
+      conf.src_type = LOCAL_X509;
       conf.src = optarg;
       break;
     case 'r':
@@ -75,6 +77,14 @@ int main(int argc, char** argv)
       conf.src_type = REMOTE;
       conf.src = optarg;
       break;
+    case 'R':
+      if (conf.src_type != NONE) conflicting_args();
+      conf.src_type = LOCAL_RSA;
+      conf.src = optarg;
+      break;
+    case 'a':
+      conf.attacks = optarg;
+      break;
     case '?':
     default:
       usage();
@@ -82,7 +92,7 @@ int main(int argc, char** argv)
     }
 
   if (conf.src_type == NONE)  {
-    conf.src_type = REMOTE;
+    conf.src_type = LOCAL_RSA;
 
     if (optind == argc)
       conf.src = "-";

+ 2 - 4
src/include/qa.h

@@ -5,15 +5,13 @@
 
 struct qa_conf {
   enum sources {
-    NONE, LOCAL, REMOTE
+    NONE, LOCAL_X509, LOCAL_RSA, REMOTE
   } src_type;
   char *src;
+  char *attacks;
 };
 
 
-extern BIO* bio_out;
-extern BIO* bio_err;
-
 int qa_init(const struct qa_conf* args);
 
 X509* get_local_cert(const char *src);

+ 5 - 2
src/include/qa_sock.h

@@ -1,9 +1,12 @@
 #ifndef _QA_SOCK_H_
 #define _QA_SOCK_H_
 
-#include "qa.h"
+#include "qa/qa.h"
 
-int init_client(const struct qa_conf *options);
+extern BIO* bio_out;
+extern BIO* bio_err;
+
+int init_client(const char *host, const char *port);
 
 int host_port(char *uri, char **host, char **service);
 

+ 1 - 0
src/include/qa/questions

@@ -0,0 +1 @@
+../../questions/include

+ 156 - 25
src/qa.c

@@ -1,32 +1,50 @@
+/**
+ * \file qa.c
+ * \brief QA controller and engine.
+ *
+ * After retrieving a valid configuration from the frontend, this file takes
+ * care of running the actual mainloop.
+ */
+
 #include <assert.h>
 #include <error.h>
 #include <stdio.h>
 #include <stdint.h>
 #include <string.h>
 #include <unistd.h>
+#include <bsd/sys/queue.h>
 
 #include <openssl/err.h>
 #include <openssl/pem.h>
 #include <openssl/ssl.h>
 #include <openssl/x509.h>
 
-#include "qa.h"
-#include "questions.h"
-#include "qa_sock.h"
-
-/** BIO wrapper around stdout */
-BIO* bio_out;
-/** BIO wrapper around srderr */
-BIO* bio_err;
+#include "qa/qa.h"
+#include "qa/questions/questions.h"
+#include "qa/qa_sock.h"
 
+static int qa_dispose(X509 *crt, RSA *rsa);
 
-void qa_abort(const char *reason)
+/**
+ * \Handle unexpected error.
+ *
+ * Function handling fatal errors: exit immediately, reporting eventual errors
+ * coming from openssl/bio or the standard errno.
+ */
+void
+qa_abort(const char *reason)
 {
   //ERR_print_errors_fp(stderr);
   exit(EXIT_FAILURE);
 }
 
-X509* get_local_cert(const char *src)
+/**
+ * \brief Loads a valid ssl certificate from file.
+ *
+ * \return NULL in case of error, a X509* structure otherwise.
+ */
+X509*
+get_local_cert(const char *src)
 {
   X509 *crt;
   FILE *fp;
@@ -39,15 +57,68 @@ X509* get_local_cert(const char *src)
   return crt;
 }
 
+/**
+ * \brief Loads a valid rsa public key from file.
+ *
+ * /return NULL in case of error, a X509* structure otherwise.
+ */
+RSA*
+get_local_rsa(const char *src)
+{
+  RSA *rsa = NULL;
+  FILE *fp;
+
+  if (!strcmp(src, "-")) fp = stdin;
+  else if (!(fp = fopen(src, "r")))
+    return NULL;
+
+  rsa = PEM_read_RSAPublicKey(fp, &rsa, NULL, NULL);
+  return rsa;
+}
+
+/**
+ * \brief Print out a valid RSA Private Key.
+ *
+ */
+static void
+print_rsa_private(RSA *rsa)
+{
+  size_t i;
+  char *dec, *hex;
+  const struct {
+    const char *desc;
+    BIGNUM *n;
+  } items[5] = {
+    {"Public Modulus", rsa->n},
+    {"Prime Factor p", rsa->p},
+    {"Prime Factor q", rsa->q},
+    {"Public Exponent", rsa->e},
+    {"Private Exponent", rsa->d},
+  };
+
+
+  assert(rsa); /* && rsa->p && rsa->q && rsa->e); */
+  for (i=0; i!=5; i++) {
+    if (!items[i].n) continue;
+    dec = BN_bn2dec(items[i].n);
+    hex = BN_bn2hex(items[i].n);
+    fprintf(stdout, "\t%-22s : %-15s (0x%s)\n", items[i].desc, dec, hex);
+    OPENSSL_free(dec);
+    OPENSSL_free(hex);
+  }
+}
+
 /**
  * \brief Given an initial configuration, stuctures the program flow.
  *
  * \param[in] args   Initial configuration given from a frontend.
  */
-int qa_init(const struct qa_conf* conf)
+int
+qa_init(const struct qa_conf* conf)
 {
-  X509 *crt;
-  struct qa_question *q;
+  int exitcode;
+  X509 *crt = NULL;
+  RSA *rsa = NULL;
 
   /* bind stdout/stderr to a BIO shit to be used externally */
   bio_out = BIO_new_fp(stdout, BIO_NOCLOSE);
@@ -56,26 +127,86 @@ int qa_init(const struct qa_conf* conf)
   /* Initialize SSL Library by registering algorithms. */
   SSL_library_init();
 
+  if (!conf->attacks) select_all_questions();
+  else select_question(conf->attacks);
+  if (!questions.lh_first) error(EXIT_FAILURE, 0, "No valid question selected.");
 
   if (conf->src_type == REMOTE)
     crt = get_remote_cert(conf->src);
-  else if (conf->src_type == LOCAL)
+  else if (conf->src_type == LOCAL_X509)
     crt = get_local_cert(conf->src);
+  else if (conf->src_type == LOCAL_RSA)
+    rsa = get_local_rsa(conf->src);
   else
     error(EXIT_FAILURE, 0, "iternal error: unable to determine source type.");
+  if (!crt && !rsa)
+    error(EXIT_FAILURE, errno, "Unable to open source.");
 
-  if (!crt)
-    error(EXIT_FAILURE, errno, "oops");
+  exitcode = qa_dispose(crt, rsa);
+  X509_free(crt);
+  return exitcode;
+}
 
-  register_all_questions();
-  for (q=questions.lh_first; q; q = q->qs.le_next) {
-    if (q->setup)    q->setup();
-    if (q->test)     q->test(crt);
-    q->ask(crt);
-    if (q->teardown) q->teardown();
+static int
+qa_dispose(X509 *crt, RSA *rsa)
+{
+  RSA *pub;
+  RSA *priv;
+  qa_question_t *q;
+
+  if (!rsa && crt)  pub = X509_get_pubkey(crt)->pkey.rsa;
+  else pub = rsa;
+
+  printf("[+] Certificate acquired\n");
+  LIST_FOREACH(q, &questions, qs) {
+    printf( "[-] Running: %s\n", q->pretty_name);
+
+    /*
+     * Run setup. If it fails, then print an error message and go to the next
+     * question.
+     */
+    if (q->setup && q->setup() <= 0)  {
+      fprintf(stderr, "[x] Unexpected error loading question %s\n", q->pretty_name);
+      continue;
+    }
+
+    /*
+     * Run test. If the test is undecidible or either ok, go on. Otherwise,
+     * print an error message and go to the next question.
+     */
+    if (q->test && q->test(crt) < 0) {
+      fprintf(stderr, "[|] Question %s cannot attack the given certificate.\n", q->pretty_name);
+      continue;
+    }
+
+    /*
+     * Attempt to attack RSA. If the attack went ok, there's no need to go
+     * on. Print out a nice message and then quit.
+     */
+    if (q->ask_rsa &&
+        (priv = q->ask_rsa(pub))) {
+      fprintf(stderr, "[\\] Key Broken using %s.\n", q->pretty_name);
+      print_rsa_private(priv);
+      return EXIT_SUCCESS;
+    }
+
+    /*
+     * Attempt to attack the X509 certificate.
+     */
+    if (crt && q->ask_crt)  q->ask_crt(crt);
+
+    /*
+     * Shut down the given question. If it fails, print an error messae and go
+     * on.
+     */
+    if (q->teardown && q->teardown() <= 0) {
+      fprintf(stderr, "[x] Unexpected error shutting down question %s.\n", q->pretty_name);
+      continue;
+    }
   }
 
-  X509_free(crt);
-
-  return 0;
+  /*
+   *  Key seems resistent: exit with status -1
+   */
+  return -1;
 }

+ 9 - 3
src/qa_sock.c

@@ -13,11 +13,18 @@
 #include <openssl/x509.h>
 #include <openssl/ssl.h>
 
-#include "qa.h"
+#include "qa/qa.h"
+#include "qa/qa_sock.h"
 
 #define SOCKET_PROTOCOL 0
 #define INVALID_SOCKET  (-1)
 
+/** BIO wrapper around stdout */
+BIO* bio_out;
+/** BIO wrapper around srderr */
+BIO* bio_err;
+
+
 /**
  * \brief Converts a uri into a tuple {host, service}.
  *
@@ -147,9 +154,8 @@ static int verify_callback(int ok, X509_STORE_CTX* ctx)
  */
 struct qa_connection* qa_connection_new(char* address)
 {
-  struct qa_connection* c;
+  struct qa_connection* c = NULL;
   char *host, *port;
-  int err;
 
   /* parse input address */
   if (!host_port(address, &host, &port)) goto error;

+ 16 - 2
src/questions/Makefile.am

@@ -1,4 +1,18 @@
+SUBDIRS = tests/
+
+# following the fucking manual, I am putting headers into the _SOURCES variable
+#  <https://www.gnu.org/software/automake/manual/html_node/Headers.html>
+EXAMPLE_QUESTION =   example.c
+WIENER_QUESTION =     wiener.c         include/qwiener.h
+POLLARD_QUESTION =   pollard.c         include/qpollard.h
+DIXON_QUESTION =       dixon.c
+
+QUESTIONS = $(WIENER_QUESTION) $(POLLARD_QUESTION) $(DIXON_QUESTION) \
+	    $(FERMAT_QUESTION) $(EXAMPLE_QUESTION)
+
+QLIBSOURCES =         qarith.c         include/qarith.h \
+	            qstrings.c         include/qstrings.h \
+                allquestions.c         include/questions.h
 
 lib_LIBRARIES = libquestions.a
-libquestions_a_SOURCES = wiener.c pollard.c example.c allquestions.c
-# da fuck liquestions_a_HEADERS = qwiener.h questions.h
+libquestions_a_SOURCES = $(QUESTIONS) $(QLIBSOURCES)

+ 29 - 9
src/questions/allquestions.c

@@ -1,21 +1,41 @@
-#include <sys/queue.h>
+/**
+ * \file allquestions.c
+ *
+ * \brief Quetions controller.
+ *
+ * Implements procedures for addign and removing questions from the global \ref
+ * questions variable.
+ */
 
-#include "questions.h"
+#include <assert.h>
+#include <string.h>
+#include <bsd/sys/queue.h>
 
+#include "qa/questions/questions.h"
 
-#define REGISTER_QUESTION(q)                      \
-  {                                               \
-      extern struct qa_question q;                \
-      LIST_INSERT_HEAD(&questions, &q, qs);       \
-  }
+/**
+ * \brief Select a single question to be used.
+ *
+ */
+void select_question(const char *sq)
+{
+  qa_question_t *q, *tmpq;
+
+  select_all_questions();
+  assert(questions.lh_first);
+
+  LIST_FOREACH_SAFE(q, &questions, qs, tmpq)
+    if (strcmp(q->name, sq))
+      LIST_REMOVE(q, qs);
+}
 
 /**
- * /brief Puts registered questions into \ref questions.
+ * \brief Puts registered questions into \ref questions.
  *
  * Disposes all registered questions into a global linked list, so that future
  * procedures can iterate over all possible tests.
  */
-void register_all_questions(void)
+void select_all_questions(void)
 {
   LIST_INIT(&questions);
 

+ 283 - 0
src/questions/dixon.c

@@ -0,0 +1,283 @@
+/**
+ * \file dixon.c
+ * \brief An implementation of Dixon's Attack using bignums.
+ *
+ * Given a non-empty set B of primes, called factor-base, and
+ * given a non-empty set of random numbers R, s.t. ∀ r ∈ R,  s ≡ r² (mod N) is B-smooth.
+ *
+ * Try to find
+ *   U ⊂ R | (Πᵤ rᵢ)² ≡ Π s (mod N) and by defining x ≝ Πᵤ rᵢ, y ≝ √(Π s)
+ *                 x² ≡ y² (mod N)
+ * Asserting that x ≢ y (mod N) we claim to have found the two non-trivial
+ * factors of N by computing gcd(x+y, N) and gcd(x-y, N).
+ *
+ * \note N = pq is assumed to be the public modulus,
+ * while e, d . ed ≡ 1 (mod φ(N))  are respectively the public and the private
+ * exponent.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <openssl/bn.h>
+
+#include "qa/questions/qarith.h"
+#include "qa/questions/qstrings.h"
+#include "qa/questions/questions.h"
+
+#define EPOCHS             100
+#define REPOP_EPOCHS        50
+#define BPOOL_EXTEND_STEP   42
+#define BPOOL_STARTING_BITS  7
+#define RPOOL_EXTEND_STEP   42
+#define U_SIZE              10
+
+#define qa_rand rand
+
+static BIGNUM* zero;
+
+/**
+ * \struct dixon_number_t
+ * \brief Auxiliary structure holding informations for R_pool.
+ */
+typedef struct dixon_number {
+  BIGNUM *r;   /**< the random number which have been chosen */
+  BIGNUM *s;   /**< s ≡ r² (mod N) */
+  BIGNUM **v;   /**< a cached vectors holding the exponents for the prime
+                 * factorization of s. */
+} dixon_number_t;
+
+/** Pool of random numbers, i.e. the set R. */
+dixon_number_t *R_pool = NULL;
+
+static size_t R_size = 0;
+
+/** Pool of prime numbers, i.e. B, the factor base. */
+static BIGNUM** B_pool = NULL;
+static size_t B_size = 0;
+
+
+/**
+ * \brief Extends the factor base, and then adjusts R_pool
+ *
+ */
+static void extend_B_pool(int max_bits)
+{
+  size_t i, j, old_B_size;
+  int bits;
+
+  old_B_size = B_size;
+  B_size += BPOOL_EXTEND_STEP;
+  /* check for size_t overflow */
+  assert(old_B_size < B_size);
+
+  B_pool = realloc(B_pool, B_size * sizeof(BIGNUM*));
+
+  for (i=old_B_size; i!=B_size; i++) {
+    bits = 1 + qa_rand() % max_bits;
+    B_pool[i] = BN_generate_prime(NULL, bits, 0, NULL, NULL, NULL, NULL);
+  }
+  /* reallocate space for vectors in R_pool */
+  for (i=0; i!=R_size; i++) {
+    R_pool[i].v = realloc(R_pool[i].v, sizeof(BIGNUM*) * B_size);
+    for (j=old_B_size; j!=B_size; j++) R_pool[i].v[j] = NULL;
+  }
+}
+
+#define B_pool_free() free(B_pool)
+
+/**
+ * We have two possible choices here, for generating a valid random rumber
+ * satisfying Dixon's theorem requirements.
+ *
+ * Alg. 1 - 1. Start by generating a random r such that r > √N,
+ *          2. Calculate s ≡ r² (mod N)
+ *          3. Factorize s using B and see if that's B-smooth
+ * This algorithm shall have complexity O(k + N² + |B|lg N)
+ *
+ * Alg. 2 - 1. Generate the random exponents for s, {e₀, e₁, …, eₘ} where m = |B|
+ *          2. From the generated exponents, calculate s = p₀^e₀·p₁^e₁·…·pₘ^eₘ
+ *             knowing that s < N
+ *          3. Find an r = √(s + tN) , t ∈ {1..N-1}
+ * This algorithm shall have complexity O(k|B| + (N-1)lg N)
+ */
+static void extend_R_pool(BIGNUM* N)
+{
+  const size_t old_R_size = R_size;
+  size_t i, j;
+  int e_bits;
+  BN_CTX *ctx = BN_CTX_new();
+  BIGNUM
+    *e,
+    *tmp = BN_new(),
+    *rem = BN_new(),
+    *t = BN_new();
+  dixon_number_t *d;
+
+  R_size += RPOOL_EXTEND_STEP;
+  /* size_t overflow */
+  assert(R_size > old_R_size);
+  R_pool = realloc(R_pool, sizeof(dixon_number_t));
+  /*
+   * XXX. There is much more to think about this.
+   * We are trying to generate some random exponents e₀…eₖ such that s < N .
+   * Hence, log(N) = ae₀ + be₁ + … + leₖ
+   */
+  e_bits = BN_num_bits(N) / 5;
+
+  for (i=old_R_size; i!= R_size; i++) {
+    d = &R_pool[i];
+    d->s = BN_new();
+    d->r = BN_new();
+
+    /* generate exponents and calculate s */
+    for (j=0; j != B_size && BN_cmp(N, d->s) == 1; j++) {
+      e = d->v[j] = BN_new();
+      /* XXX. better check for error here. */
+      BN_pseudo_rand(e, e_bits, -1, 0);
+      BN_exp(tmp, B_pool[j], e, ctx);
+      BN_mul(d->s, tmp, d->s, ctx);
+    }
+
+    /*  Find an r = √(s + tN) , t ∈ {1..N-1} */
+    BN_sqr(tmp, N, ctx);
+    BN_one(t);
+    for (BN_add(t, t, N); BN_cmp(tmp, t) == 1; BN_add(t, t, N))
+      if (BN_sqrtmod(d->r, rem, t, ctx)) break;
+  }
+
+
+  BN_CTX_free(ctx);
+  BN_free(rem);
+  BN_free(tmp);
+  BN_free(t);
+
+}
+
+
+#define R_pool_free() free(R_pool)
+
+int dixon_question_setup(void)
+{
+  extern BIGNUM* zero;
+  zero = BN_new();
+  BN_zero(zero);
+
+  extend_B_pool(BPOOL_STARTING_BITS);
+  return 1;
+}
+
+int dixon_question_teardown(void) {
+  BN_free(zero);
+
+  B_pool_free();
+  R_pool_free();
+  return 0;
+}
+
+
+RSA* dixon_question_ask_rsa(const RSA *rsa) {
+  /* key data */
+  RSA *ret = NULL;
+  BIGNUM
+    *n,
+    *p, *q;
+  /* x, y */
+  BIGNUM
+    *x, *x2,
+    *y, *y2;
+  BN_CTX *ctx;
+  /* U ⊆ R */
+  ssize_t *U_bucket;
+  /* internal data */
+  int epoch;
+  BIGNUM *tmp;
+  char *even_powers;
+  size_t i, j, k;
+
+  n = rsa->n;
+  U_bucket = malloc(sizeof(ssize_t) * U_SIZE);
+  even_powers = malloc(sizeof(char) * B_size);
+  ctx = BN_CTX_new();
+  x = BN_new();
+  y = BN_new();
+  x2 = BN_new();
+  y2 = BN_new();
+  tmp = BN_new();
+
+  /* mainloop: iterate until a key is found, or convergence. */
+  for (epoch=0; epoch < EPOCHS; epoch++) {
+    /* depending on the epoch, populate R_pool and B_pool */
+    if (epoch % REPOP_EPOCHS) extend_R_pool(n);
+
+    /* reset variables */
+    for (i=0; i!=U_SIZE; i++) U_bucket[i] =  -1;
+    bzero(even_powers, B_size * sizeof(char));
+    j = 0;
+
+    /* choose a subset of R such that the product of primes can be squared */
+    do {
+      for (i=0; i!=B_size && j < U_SIZE; i++) {
+        /* choose whether to take or not R_pool[i] */
+        if (qa_rand() % 2) continue;
+
+        /* add the number */
+        U_bucket[j++] = i;
+        for (k=0; k!=B_size; k++)
+          even_powers[k] ^= BN_is_odd(R_pool[i].v[j]);
+      }
+    } while (!is_vzero(even_powers, B_size * sizeof(char)));
+
+    /* let x = Πᵢ rᵢ , y² = Πᵢ sᵢ */
+    BN_one(x);
+    BN_one(y2);
+    for (i=0; i != U_SIZE; i++) {
+      if (U_bucket[i] == -1) continue;
+
+      j = U_bucket[i];
+      BN_mul(x, x, R_pool[j].r, ctx);
+      BN_mul(y2, y2, R_pool[j].s, ctx);
+    }
+    /* retrieve x² from x */
+    BN_sqr(x2, x, ctx);
+    /* retrieve y from y² */
+    /* test: shall *always* be a perfect square */
+    if (!BN_sqrtmod(y, tmp, y2, ctx)) continue;
+    /* test: assert that x ≡ y (mod N) */
+    if (!BN_cmp(x, y)) continue;
+
+    /* p, q found :) */
+    ret = RSA_new();
+    ret->e = rsa->e;
+    ret->n = rsa->n;
+    ret->p = p = BN_new();
+    ret->q = q = BN_new();
+
+    BN_uadd(tmp, x, y);
+    BN_gcd(p, tmp, n, ctx);
+    assert(!BN_is_one(p) && BN_cmp(p, n));
+    BN_usub(tmp, x, y);
+    BN_gcd(q, tmp, n, ctx);
+    assert(!BN_is_one(q) && BN_cmp(q, n));
+  }
+
+  BN_free(x);
+  BN_free(x2);
+  BN_free(y);
+  BN_free(y2);
+  free(U_bucket);
+  free(even_powers);
+
+  return ret;
+}
+
+qa_question_t DixonQuestion = {
+  .name = "dixon",
+  .pretty_name = "Dixon's Factorization",
+  .setup = dixon_question_setup,
+  .teardown = dixon_question_teardown,
+  .test =  NULL,
+  .ask_rsa = dixon_question_ask_rsa,
+  .ask_crt = NULL
+};

+ 58 - 8
src/questions/example.c

@@ -1,20 +1,59 @@
+/**
+ * \file example.c
+ * \brief Template for future Questions.
+ *
+ * This file has the purpose of showing and documenting how a
+ * \ref{qa_question_t} is supposed to be used.
+ *
+ */
 #include <openssl/bio.h>
+#include <openssl/rsa.h>
 #include <openssl/x509.h>
 
+#include "qa/questions/questions.h"
 
-#include "questions.h"
 
 static BIO* out;
 
-int example_question_setup(void) {
+/**
+ * \brief Example Setup.
+ *
+ * This functions returns false if `out` could not be opened.
+ */
+static int
+example_question_setup(void) {
   out = BIO_new_fp(stdout, BIO_NOCLOSE);
+
+  return (out != NULL);
+}
+
+/**
+ * \brief Example Teardown.
+ *
+ * This function returns an error if `out` could not be closed.
+ */
+static int
+example_question_teardown(void)
+{
+  return BIO_free(out);
+}
+
+/**
+ * \brief Example Test.
+ *
+ * This function always returns zero, as its attack is undecidible.
+ */
+static int
+example_question_test(X509* cert) {
   return 0;
 }
 
-int example_question_teardown(void) { return 0; }
-/* XXX. apparently openssl does not allow const X509* in get_pkey() func */
-int example_question_test(X509* cert) { return 1; }
-int example_question_ask(X509* cert)
+
+/**
+ * \brief Example Attack to X509 certificate
+ */
+static int
+example_question_ask_crt(X509* cert)
 {
   EVP_PKEY* pkey;
 
@@ -23,12 +62,23 @@ int example_question_ask(X509* cert)
   return 1;
 }
 
+/**
+ * \brief Example Attack on a RSA key.
+ */
+static RSA*
+example_question_ask_rsa(const RSA *rsa)
+{
+  return NULL;
+}
+
 
 
 qa_question_t ExampleQuestion = {
-  .name = "Example Question",
+  .name = "example",
+  .pretty_name = "Example Question",
   .setup = example_question_setup,
   .teardown = example_question_teardown,
   .test = example_question_test,
-  .ask = example_question_ask
+  .ask_crt = example_question_ask_crt,
+  .ask_rsa = example_question_ask_rsa
 };

+ 14 - 10
src/questions/qwiener.h

@@ -1,15 +1,16 @@
-#ifndef _QA_WIENER_H_
-#define _QA_WIENER_H
+#ifndef _QA_ARITH_H_
+#define _QA_ARITH_H
 
-#include <stdlib.h>
-#include <openssl/bn.h>
+
+/* shortcut macros. */
+#define BN_uiadd1(a) BN_uadd(a, a, BN_value_one())
 
 /**
  * Fractions made of bignums.
  */
 typedef struct bigfraction {
-  BIGNUM* h;
-  BIGNUM* k;
+  BIGNUM* h;   /**< numerator */
+  BIGNUM* k;   /**< denominator */
 } bigfraction_t;
 
 
@@ -21,15 +22,18 @@ typedef struct cf {
   BN_CTX* ctx;
 } cf_t;
 
+
 /* continued fractions utilities. */
 cf_t* cf_new(void);
+
 cf_t* cf_init(cf_t *f, BIGNUM *num, BIGNUM *b);
+
+void cf_free(cf_t* f);
+
 bigfraction_t* cf_next(cf_t *f);
 
+
 /* square root calculation */
 int BN_sqrtmod(BIGNUM* dv, BIGNUM* rem, BIGNUM* a, BN_CTX* ctx);
 
-/* the actual attack */
-extern struct qa_question WienerQuestion;
-
-#endif /* _QA_WIENER_H_ */
+#endif /* _QA_ARITH_H_ */

src/questions/qpollard.h → src/questions/include/qpollard.h


+ 6 - 0
src/questions/include/qstrings.h

@@ -0,0 +1,6 @@
+#ifndef _QA_QSTRINGS_H_
+#define _QA_QSTRINGS_H_
+
+int is_vzero(const void *v, size_t len);
+
+#endif /* _QA_QSTRINGS_H_ */

+ 51 - 0
src/questions/include/questions.h

@@ -0,0 +1,51 @@
+#ifndef _QA_QUESTIONS_H_
+#define _QA_QUESTIONS_H_
+
+#include <sys/queue.h>
+
+#include <openssl/x509.h>
+
+/**
+ * A question: name, command-line name, callbacks.
+ */
+typedef struct qa_question {
+  const char* name;    /**< short name - name given as command-line argument */
+  const char* pretty_name;    /**< full name - name used for identifying the question */
+
+  int (* setup) (void);    /**< setup callback - initializes static glabal
+                              variables.
+                              Return <= 0 in case of error */
+  int (* teardown) ();    /**< teardown callback - frees static global
+                             variables
+                             Return <= 0 in case of error*/
+  int (* test) (X509 *cert);   /**< test callback - assert the attack can be
+                                  performed over the certificate cert.
+                                  Return 1 if it is possible to attack the
+                                  certificate, -1 if not, 0 if undecidible. */
+  RSA* (* ask_rsa) (const RSA *rsa);    /**< ask_rsa callback - attack the RSA
+                                           key rsa. Return NULL if the key was
+                                           not broken, a valid private key
+                                           structure otherwise.*/
+  int (* ask_crt) (X509 *crt);    /**< ask_crt callback - attack the certificate
+                                     crt.
+                                     XXX. Return type has still to be
+                                     established here.
+                                     XXX. apparently openssl does not allow const
+                                     X509* in get_pkey() func. */
+
+  LIST_ENTRY(qa_question) qs;
+} qa_question_t;
+
+LIST_HEAD(listhead, qa_question) questions;
+
+void select_question(const char *);
+void select_all_questions(void);
+
+#define REGISTER_QUESTION(q)                      \
+  {                                               \
+      extern struct qa_question q;                \
+      LIST_INSERT_HEAD(&questions, &q, qs);       \
+  }
+
+
+#endif /* _QA_QUESTIONS_H_ */

+ 6 - 0
src/questions/include/qwiener.h

@@ -0,0 +1,6 @@
+#ifndef _QA_WIENER_H_
+#define _QA_WIENER_H
+
+extern struct qa_question WienerQuestion;
+
+#endif /* _QA_WIENER_H_ */

+ 25 - 26
src/questions/pollard.c

@@ -25,34 +25,31 @@
 #include <openssl/x509.h>
 #include <openssl/err.h>
 
-#include "questions.h"
+#include "qa/questions/questions.h"
+#include "qa/questions/qarith.h"
+#include "qa/questions/qpollard.h"
+
 
 static BIGNUM *two;
 
-int pollard1_question_setup(void)
+static int
+pollard1_question_setup(void)
 {
   /* create 2 */
   two = BN_new();
   BN_one(two);
   BN_uadd(two, two, BN_value_one());
-  return 0;
+  return 1;
 }
 
-int pollard1_question_teardown(void)
+static int
+pollard1_question_teardown(void)
 {
   BN_free(two);
-  return 0;
-}
-
-
-int pollard1_question_test(X509 *cert)
-{
-  return 0;
+  return 1;
 }
 
 
-int BN_sqrtmod(BIGNUM*, BIGNUM*, BIGNUM*, BN_CTX*);
-
 /**
  * \brief Pollard (p-1) factorization.
  *
@@ -65,17 +62,16 @@ int BN_sqrtmod(BIGNUM*, BIGNUM*, BIGNUM*, BN_CTX*);
  * about 3^(−3) = 1/27 that a B value of n^(1/6) will yield a factorisation.»
  *
  */
-int pollard1_question_ask(X509 *cert)
+static RSA*
+pollard1_question_ask_rsa(const RSA *rsa)
 {
-  int ret = 1;
-  RSA *rsa;
+  RSA *ret = NULL;
   BIGNUM *a, *B, *a1;
   BIGNUM *gcd, *rem;
   BIGNUM *n;
   BIGNUM *p, *q;
   BN_CTX *ctx;
 
-  rsa = X509_get_pubkey(cert)->pkey.rsa;
   n = rsa->n;
   a = BN_new();
   B = BN_new();
@@ -99,12 +95,13 @@ int pollard1_question_ask(X509 *cert)
   }
 
   /* Either p or q found :) */
-  ret = BN_is_zero(B);
-  if (!ret) {
-    p = BN_dup(gcd);
-    q = BN_new();
+  if (!BN_is_zero(B)) {
+    ret = RSA_new();
+    ret->n = rsa->n;
+    ret->e = rsa->e;
+    ret->q = q = BN_new();
+    ret->p = p = BN_dup(gcd);
     BN_div(q, NULL, n, gcd, ctx);
-    printf("p:%s, q=%s \n", BN_bn2dec(p), BN_bn2dec(q));
   }
 
   BN_free(a);
@@ -118,10 +115,12 @@ int pollard1_question_ask(X509 *cert)
 }
 
 
-struct qa_question PollardQuestion = {
-  .name = "Pollard's (p-1) factorization",
+qa_question_t PollardQuestion = {
+  .name = "pollard1",
+  .pretty_name = "Pollard's (p-1) factorization",
   .setup = pollard1_question_setup,
   .teardown = pollard1_question_teardown,
-  .test = pollard1_question_test,
-  .ask = pollard1_question_ask,
+  .test = NULL,
+  .ask_rsa = pollard1_question_ask_rsa,
+  .ask_crt = NULL
 };

+ 174 - 0
src/questions/qarith.c

@@ -0,0 +1,174 @@
+/**
+ * \file qarith.c
+ * \brief Random Algebraic utilities with BIGNUMs.
+ *
+ */
+#include <openssl/bn.h>
+
+#include "qa/questions/qarith.h"
+
+
+cf_t* cf_new(void)
+{
+  cf_t *f;
+
+  f = (cf_t *) malloc(sizeof(cf_t));
+
+  size_t i;
+
+  for (i=0; i!=3; i++) {
+    f->fs[i].h = BN_new();
+    f->fs[i].k = BN_new();
+  }
+  f->a = BN_new();
+  f->x.h = BN_new();
+  f->x.k = BN_new();
+
+  f->ctx = BN_CTX_new();
+
+  return f;
+}
+
+void cf_free(cf_t* f)
+{
+  size_t i;
+
+  for (i=0; i!=3; i++) {
+    BN_free(f->fs[i].h);
+    BN_free(f->fs[i].k);
+  }
+  BN_free(f->a);
+  BN_free(f->x.h);
+  BN_free(f->x.k);
+
+  free(f);
+}
+
+
+/**
+ * \brief Initialized a continued fraction.
+ *
+ * A continued fraction for a floating number x can be expressed as a series
+ *  <a₀; a₁, a₂…, aₙ>
+ * such that
+ * <pre>
+ *
+ *                1
+ *  x = a₀ + ⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽
+ *                    1
+ *           a₁ + ⎽⎽⎽⎽⎽⎽⎽⎽⎽
+ *                 a₂ + …
+ *
+ * </pre>
+ * , where for each i < n, there exists an approximation hᵢ / kᵢ.
+ * By definition,
+ *   a₋₁ = 0
+ *   h₋₁ = 1    h₋₂ = 0
+ *   k₋₁ = 0    k₋₂ = 1
+ *
+ * \param f     A continued fraction structure. If f is NULL, a new one is
+ *              allocated.
+ * \param num   Numerator to be used as initial numerator for the fraction to be
+ *              approximated.
+ * \param den   Denominator to be used as denominator for the fraction to be
+ *              approximated.
+ *
+ * \return the continued fraction fiven as input.
+ */
+cf_t* cf_init(cf_t* f, BIGNUM* num, BIGNUM* den)
+{
+  if (!f) f = cf_new();
+
+  BN_zero(f->fs[0].h);
+  BN_one(f->fs[0].k);
+
+  BN_one(f->fs[1].h);
+  BN_zero(f->fs[1].k);
+
+  f->i = 2;
+  if (!BN_copy(f->x.h, num)) return NULL;
+  if (!BN_copy(f->x.k, den)) return NULL;
+
+  return f;
+}
+
+
+/**
+ * \brief Produces the next fraction.
+ *
+ * Each new approximation hᵢ/kᵢ is defined rec ursively as:
+ *   hᵢ = aᵢhᵢ₋₁ + hᵢ₋₂
+ *   kᵢ = aᵢkᵢ₋₁ + kᵢ₋₂
+ * Meanwhile each new aᵢ is simply the integer part of x.
+ *
+ *
+ * \param f   The continued fraction.
+ * \return NULL if the previous fraction approximates at its best the number,
+ *         a pointer to the next fraction in the series othw.
+ */
+bigfraction_t* cf_next(cf_t *f)
+{
+  bigfraction_t *ith_fs = &f->fs[f->i];
+  BIGNUM* rem = BN_new();
+
+  if (BN_is_zero(f->x.h)) return NULL;
+  BN_div(f->a, rem, f->x.h, f->x.k, f->ctx);
+
+  /* computing hᵢ */
+  BN_mul(f->fs[f->i].h , f->a, f->fs[(f->i-1+3) % 3].h, f->ctx);
+  BN_uadd(f->fs[f->i].h, f->fs[f->i].h, f->fs[(f->i-2+3) % 3].h);
+  /* computing kᵢ */
+  BN_mul(f->fs[f->i].k , f->a, f->fs[(f->i-1+3) % 3].k, f->ctx);
+  BN_uadd(f->fs[f->i].k, f->fs[f->i].k, f->fs[(f->i-2+3) % 3].k);
+
+  f->i = (f->i + 1) % 3;
+  /* update x. */
+  BN_copy(f->x.h, f->x.k);
+  BN_copy(f->x.k, rem);
+
+  return ith_fs;
+}
+
+/**
+ * \brief Square Root for bignums.
+ *
+ * An implementation of Dijkstra's Square Root Algorithm.
+ * A Discipline of Programming, page 61 - Fifth Exercise.
+ *
+ * \return true if rem is equal to zero, false otherwise.
+ */
+int BN_sqrtmod(BIGNUM* dv, BIGNUM* rem, BIGNUM* a, BN_CTX* ctx)
+{
+  BIGNUM *shift;
+  BIGNUM *adj;
+
+  shift = BN_new();
+  adj = BN_new();
+  BN_zero(dv);
+  BN_copy(rem, a);
+
+  /* hacking into internal sequence to skip some cycles. */
+  /* for  (BN_one(shift);     original */
+  for (bn_wexpand(shift, a->top+1), shift->top=a->top, shift->d[shift->top-1] = 1;
+       BN_ucmp(shift, rem) != 1;
+       /* BN_rshift(shift, shift, 2); */
+       BN_lshift1(shift, shift), BN_lshift1(shift, shift));
+
+
+  while (!BN_is_one(shift)) {
+    /* BN_rshift(shift, shift, 2); */
+    BN_rshift1(shift, shift);
+    BN_rshift1(shift, shift);
+
+    BN_uadd(adj, dv, shift);
+    BN_rshift1(dv, dv);
+    if (BN_ucmp(rem, adj) != -1) {
+      BN_uadd(dv, dv, shift);
+      BN_usub(rem, rem, adj);
+    }
+  }
+
+  BN_free(shift);
+  BN_free(adj);
+  return BN_is_zero(rem);
+}

+ 23 - 0
src/questions/qstrings.c

@@ -0,0 +1,23 @@
+/**
+ * \file qstrings.c
+ *
+ * \brief Extend stdlib support with some common functions used in questions.
+ *
+ */
+#include <stddef.h>
+
+#include "qa/questions/qstrings.h"
+
+
+/**
+ * \brief Check v the first len bits of v are filled with zeroes
+ *
+ * \return true if the first len bits of v are zero, false otherwise.
+ */
+int is_vzero(const void *v, size_t len)
+{
+  char unsigned *s = (char unsigned *) v;
+  while (len--)
+    if (*(s++)) return 0;
+  return 1;
+}

+ 0 - 25
src/questions/questions.h

@@ -1,25 +0,0 @@
-#ifndef _QA_QUESTIONS_H_
-#define _QA_QUESTIONS_H_
-
-#include <sys/queue.h>
-
-#include <openssl/x509.h>
-
-
-typedef struct qa_question {
-  const char* name;
-  int (* setup) (void);
-  int (* teardown) ();
-  int (* test) (X509* cert);
-  int (* ask) (X509* cert);
-
-  LIST_ENTRY(qa_question) qs;
-} qa_question_t;
-
-
-LIST_HEAD(listhead, qa_question) questions;
-
-void register_all_questions(void);
-
-
-#endif /* _QA_QUESTIONS_H_ */

+ 11 - 0
src/questions/tests/Makefile.am

@@ -0,0 +1,11 @@
+# unittesting my ass
+LDADD=../libquestions.a -lssl -lcrypto
+
+TESTS = test_qarith test_qstrings test_wiener test_pollard
+check_PROGRAMS = $(TESTS)
+# check_LIBRARIES = libquestions.a
+
+test_qstrings_SOURCES = test_qstrings.c
+test_qarith_SOURCES = test_qarith.c
+test_wiener_SOURCES = test_wiener.c
+test_pollard_SOURCES = test_pollard.c

src/questions/test/pollard.pem → src/questions/tests/pollard.pem


+ 9 - 7
src/questions/test/test_pollard.c

@@ -1,22 +1,24 @@
 #include <assert.h>
 
+#include <openssl/rsa.h>
 #include <openssl/x509.h>
 #include <openssl/pem.h>
 
-#include  "questions.h"
-#include "qpollard.h"
+#include "qa/questions/questions.h"
+#include "qa/questions/qpollard.h"
 
 void test_pollard(void)
 {
   X509 *crt;
-  FILE *fp = fopen("test/pollard.pem", "r");
+  RSA *rsa;
+  FILE *fp = fopen("pollard.pem", "r");
+
   if (!fp) exit(EXIT_FAILURE);
   crt = PEM_read_X509(fp, NULL, 0, NULL);
-  if (!crt) {
-    exit(EXIT_FAILURE);
-  }
+  if (!crt) exit(EXIT_FAILURE);
 
-  PollardQuestion.ask(crt);
+  rsa = X509_get_pubkey(crt)->pkey.rsa;
+  PollardQuestion.ask_rsa(rsa);
 }
 
 int main(int argc, char **argv)

+ 158 - 0
src/questions/tests/test_qarith.c

@@ -0,0 +1,158 @@
+#include <assert.h>
+
+#include <openssl/bn.h>
+#include "qa/questions/qarith.h"
+
+
+/**
+ * \brief Testing the continued fractions generator.
+ *
+ *
+ */
+void test_cf(void)
+{
+  bigfraction_t x = {NULL, NULL};
+  cf_t* f;
+  size_t i;
+  bigfraction_t *it;
+  BIGNUM* expected;
+
+  f = cf_new();
+  x.h = BN_new();
+  x.k = BN_new();
+  expected = BN_new();
+
+   /*
+   *  Testing aᵢ
+   *
+   *              1
+   * √2 = 1 + ⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽
+   *                  1
+   *           2 +  ⎽⎽⎽⎽⎽⎽
+   *                 2 + …
+   *
+   */
+  BN_dec2bn(&x.h, "14142135623730951");
+  BN_dec2bn(&x.k, "10000000000000000");
+  BN_dec2bn(&expected, "2");
+  cf_init(f, x.h, x.k);
+
+  it = cf_next(f);
+  assert(BN_is_one(f->a));
+  for (i=0; i!=5 && it; i++) {
+    it = cf_next(f);
+    assert(!BN_cmp(f->a, expected));
+  }
+  assert(i==5);
+
+  /*
+   * Testing hᵢ/kᵢ
+   *
+   *                        1
+   * φ = (1+√5)/2  = 1 + ⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽
+   *                            1
+   *                      1 + ⎽⎽⎽⎽⎽
+   *                          1 + …
+   */
+  const char* fib[] = {"1", "1", "2", "3", "5", "8", "13"};
+  BN_dec2bn(&x.h, "323606797749979");
+  BN_dec2bn(&x.k, "200000000000000");
+  cf_init(f, x.h, x.k);
+  it = cf_next(f);
+  for (i=1; i!=7; i++) {
+    BN_dec2bn(&expected, fib[i]);
+    assert(!BN_cmp(it->h, expected));
+
+    BN_dec2bn(&expected, fib[i-1]);
+    assert(!BN_cmp(it->k, expected));
+
+    it=cf_next(f);
+  }
+
+  BN_dec2bn(&x.h, "60728973");
+  BN_dec2bn(&x.k, "160523347");
+  cf_init(f, x.h, x.k);
+  /* 0 */
+  it = cf_next(f);
+  /* 1 / 2 */
+  it = cf_next(f);
+  BN_dec2bn(&expected, "2");
+  assert(BN_is_one(it->h) && !BN_cmp(it->k, expected));
+  /* 1 / 3 */
+  it = cf_next(f);
+  BN_dec2bn(&expected, "3");
+  assert(BN_is_one(it->h) && !BN_cmp(it->k, expected));
+  /* 2 / 5 */
+  it = cf_next(f);
+  BN_dec2bn(&expected, "2");
+  assert(!BN_cmp(expected, it->h));
+  BN_dec2bn(&expected, "5");
+  assert(!BN_cmp(expected, it->k));
+  /* 3 / 8 */
+  it = cf_next(f);
+  BN_dec2bn(&expected, "3");
+  assert(!BN_cmp(expected, it->h));
+  BN_dec2bn(&expected, "8");
+  assert(!BN_cmp(expected, it->k));
+  /* 14/ 37 */
+  it = cf_next(f);
+  BN_dec2bn(&expected, "14");
+  assert(!BN_cmp(expected, it->h));
+  BN_dec2bn(&expected, "37");
+  assert(!BN_cmp(expected, it->k));
+}
+
+
+static void test_BN_sqrtmod(void)
+{
+  BIGNUM *a, *b, *expected;
+  BIGNUM *root, *rem;
+  BIGNUM *mayzero;
+  BN_CTX *ctx;
+
+  a = b = expected = NULL;
+  root = BN_new();
+  rem = BN_new();
+  mayzero = BN_new();
+  ctx = BN_CTX_new();
+
+  BN_dec2bn(&a, "144");
+  BN_dec2bn(&expected, "12");
+  BN_sqrtmod(root, rem, a, ctx);
+  assert(!BN_cmp(expected, root));
+  assert(BN_is_zero(rem));
+
+  BN_dec2bn(&a, "15245419238964964");
+  BN_dec2bn(&expected, "123472342");
+  BN_sqrtmod(root, rem, a, ctx);
+  assert(!BN_cmp(root, expected));
+  assert(BN_is_zero(rem));
+
+  BN_dec2bn(&a, "5");
+  BN_dec2bn(&expected, "2");
+  BN_sqrtmod(root, rem, a, ctx);
+  assert(!BN_cmp(root, expected));
+  assert(BN_is_one(rem));
+
+  BN_dec2bn(&a, "106929");
+  BN_dec2bn(&expected, "327");
+  BN_sqrtmod(root, rem, a, ctx);
+  assert(BN_is_zero(rem));
+  assert(!BN_cmp(root, expected));
+
+  BN_free(root);
+  BN_free(rem);
+  BN_free(mayzero);
+  BN_CTX_free(ctx);
+  BN_free(a);
+  BN_free(expected);
+}
+
+
+int main(int argc, char **argv)
+{
+  test_cf();
+  test_BN_sqrtmod();
+  return 0;
+
+}

+ 19 - 0
src/questions/tests/test_qstrings.c

@@ -0,0 +1,19 @@
+#include <stddef.h>
+#include <assert.h>
+
+#include "qa/questions/qstrings.h"
+
+void test_is_vzero(void)
+{
+  const char *v = "\x0\x0\x0\x1\x0\x1";
+
+  assert(is_vzero(v, 3));
+  assert(!is_vzero(v, 4));
+  assert(!is_vzero(v, 6));
+}
+
+int main(int argc, char **argv)
+{
+  test_is_vzero();
+  return 0;
+}

+ 9 - 154
src/questions/tests/test_wiener.c

@@ -2,7 +2,6 @@
 #include <error.h>
 #include <errno.h>
 #include <libgen.h>
-#include <math.h>
 #include <string.h>
 
 
@@ -11,158 +10,15 @@
 #include <openssl/x509.h>
 #include <openssl/err.h>
 
-#include "questions.h"
-#include "qwiener.h"
-
-/**
- * \brief Testing the continued fractions generator.
- *
- *
- */
-void test_cf(void)
-{
-  bigfraction_t x = {NULL, NULL};
-  cf_t* f;
-  size_t i;
-  bigfraction_t *it;
-  BIGNUM* expected;
-
-  f = cf_new();
-  x.h = BN_new();
-  x.k = BN_new();
-  expected = BN_new();
-
-   /*
-   *  Testing aᵢ
-   *
-   *              1
-   * √2 = 1 + ⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽
-   *                  1
-   *           2 +  ⎽⎽⎽⎽⎽⎽
-   *                 2 + …
-   *
-   */
-  BN_dec2bn(&x.h, "14142135623730951");
-  BN_dec2bn(&x.k, "10000000000000000");
-  BN_dec2bn(&expected, "2");
-  cf_init(f, x.h, x.k);
-
-  it = cf_next(f);
-  assert(BN_is_one(f->a));
-  for (i=0; i!=5 && it; i++) {
-    it = cf_next(f);
-    assert(!BN_cmp(f->a, expected));
-  }
-  assert(i==5);
-
-  /*
-   * Testing hᵢ/kᵢ
-   *
-   *                        1
-   * φ = (1+√5)/2  = 1 + ⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽
-   *                            1
-   *                      1 + ⎽⎽⎽⎽⎽
-   *                          1 + …
-   */
-  const char* fib[] = {"1", "1", "2", "3", "5", "8", "13"};
-  BN_dec2bn(&x.h, "323606797749979");
-  BN_dec2bn(&x.k, "200000000000000");
-  cf_init(f, x.h, x.k);
-  it = cf_next(f);
-  for (i=1; i!=7; i++) {
-    BN_dec2bn(&expected, fib[i]);
-    assert(!BN_cmp(it->h, expected));
-
-    BN_dec2bn(&expected, fib[i-1]);
-    assert(!BN_cmp(it->k, expected));
-
-    it=cf_next(f);
-  }
-
-  BN_dec2bn(&x.h, "60728973");
-  BN_dec2bn(&x.k, "160523347");
-  cf_init(f, x.h, x.k);
-  /* 0 */
-  it = cf_next(f);
-  /* 1 / 2 */
-  it = cf_next(f);
-  BN_dec2bn(&expected, "2");
-  assert(BN_is_one(it->h) && !BN_cmp(it->k, expected));
-  /* 1 / 3 */
-  it = cf_next(f);
-  BN_dec2bn(&expected, "3");
-  assert(BN_is_one(it->h) && !BN_cmp(it->k, expected));
-  /* 2 / 5 */
-  it = cf_next(f);
-  BN_dec2bn(&expected, "2");
-  assert(!BN_cmp(expected, it->h));
-  BN_dec2bn(&expected, "5");
-  assert(!BN_cmp(expected, it->k));
-  /* 3 / 8 */
-  it = cf_next(f);
-  BN_dec2bn(&expected, "3");
-  assert(!BN_cmp(expected, it->h));
-  BN_dec2bn(&expected, "8");
-  assert(!BN_cmp(expected, it->k));
-  /* 14/ 37 */
-  it = cf_next(f);
-  BN_dec2bn(&expected, "14");
-  assert(!BN_cmp(expected, it->h));
-  BN_dec2bn(&expected, "37");
-  assert(!BN_cmp(expected, it->k));
-}
-
-
-void test_BN_sqrtmod(void)
-{
-  BIGNUM *a, *b, *expected;
-  BIGNUM *root, *rem;
-  BIGNUM *mayzero;
-  BN_CTX *ctx;
-
-  a = b = expected = NULL;
-  root = BN_new();
-  rem = BN_new();
-  mayzero = BN_new();
-  ctx = BN_CTX_new();
-
-  BN_dec2bn(&a, "144");
-  BN_dec2bn(&expected, "12");
-  BN_sqrtmod(root, rem, a, ctx);
-  assert(!BN_cmp(expected, root));
-  assert(BN_is_zero(rem));
-
-  BN_dec2bn(&a, "15245419238964964");
-  BN_dec2bn(&expected, "123472342");
-  BN_sqrtmod(root, rem, a, ctx);
-  assert(!BN_cmp(root, expected));
-  assert(BN_is_zero(rem));
-
-  BN_dec2bn(&a, "5");
-  BN_dec2bn(&expected, "2");
-  BN_sqrtmod(root, rem, a, ctx);
-  assert(!BN_cmp(root, expected));
-  assert(BN_is_one(rem));
-
-  BN_dec2bn(&a, "106929");
-  BN_dec2bn(&expected, "327");
-  BN_sqrtmod(root, rem, a, ctx);
-  assert(BN_is_zero(rem));
-  assert(!BN_cmp(root, expected));
-
-  BN_free(root);
-  BN_free(rem);
-  BN_free(mayzero);
-  BN_CTX_free(ctx);
-  BN_free(a);
-  BN_free(expected);
-}
+#include "qa/questions/questions.h"
+#include "qa/questions/qwiener.h"
 
 
 void test_wiener(void)
 {
   X509 *crt;
-  FILE *fp = fopen("tests/wiener_test.crt", "r");
+  RSA *rsa;
+  FILE *fp = fopen("wiener_test.crt", "r");
 
   if (!fp) exit(EXIT_FAILURE);
   crt = PEM_read_X509(fp, NULL, 0, NULL);
@@ -170,18 +26,17 @@ void test_wiener(void)
     exit(EXIT_FAILURE);
   }
 
-  assert(WienerQuestion.test(crt));
-  assert(WienerQuestion.ask(crt));
+  rsa = X509_get_pubkey(crt)->pkey.rsa;
+  /* assert(WienerQuestion.test(crt)); */
+  assert(WienerQuestion.ask_rsa(rsa));
 }
 
 int main(int argc, char ** argv)
 {
-  WienerQuestion.setup();
+  if (WienerQuestion.setup) WienerQuestion.setup();
 
-  test_cf();
-  test_BN_sqrtmod();
   test_wiener();
 
-  WienerQuestion.teardown();
+  if (WienerQuestion.teardown) WienerQuestion.teardown();
   return 0;
 }

+ 28 - 191
src/questions/wiener.c

@@ -1,5 +1,12 @@
 /**
  * \file wiener.c
+ * \brief An implementation of Wiener's Attack using bignums.
+ *
+ * Wiener's atttack states that:
+ * given N = pq the public modulus, the couple e, d . ed ≡ 1 (mod φ(N))
+ * respectively the private and public exponent,
+ * given p < q < 2p and d < ⅓ ⁴√N,
+ * one can efficently recover d knowing only <N, e>.
  *
  */
 #include <math.h>
@@ -9,193 +16,20 @@
 #include <openssl/rsa.h>
 #include <openssl/bn.h>
 
-#include "questions.h"
-#include "qwiener.h"
-
-
-cf_t* cf_new(void)
-{
-  cf_t *f;
-
-  f = (cf_t *) malloc(sizeof(cf_t));
-
-  size_t i;
-
-  for (i=0; i!=3; i++) {
-    f->fs[i].h = BN_new();
-    f->fs[i].k = BN_new();
-  }
-  f->a = BN_new();
-  f->x.h = BN_new();
-  f->x.k = BN_new();
-
-  f->ctx = BN_CTX_new();
-
-  return f;
-}
-
-void cf_free(cf_t* f)
-{
-  size_t i;
-
-  for (i=0; i!=3; i++) {
-    BN_free(f->fs[i].h);
-    BN_free(f->fs[i].k);
-  }
-  BN_free(f->a);
-  BN_free(f->x.h);
-  BN_free(f->x.k);
-
-  free(f);
-}
-
-
-/**
- * \brief Initialized a continued fraction.
- *
- * A continued fraction for a floating number x can be expressed as a series
- *  <a₀; a₁, a₂…, aₙ>
- * such that
- * <pre>
- *
- *                1
- *  x = a₀ + ⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽
- *                    1
- *           a₁ + ⎽⎽⎽⎽⎽⎽⎽⎽⎽
- *                 a₂ + …
- *
- * </pre>
- * , where for each i < n, there exists an approximation hᵢ / kᵢ.
- * By definition,
- *   a₋₁ = 0
- *   h₋₁ = 1    h₋₂ = 0
- *   k₋₁ = 0    k₋₂ = 1
- *
- * \param f     A continued fraction structure. If f is NULL, a new one is
- *              allocated.
- * \param num   Numerator to be used as initial numerator for the fraction to be
- *              approximated.
- * \param den   Denominator to be used as denominator for the fraction to be
- *              approximated.
- *
- * \return the continued fraction fiven as input.
- */
-cf_t* cf_init(cf_t* f, BIGNUM* num, BIGNUM* den)
-{
-  if (!f) f = cf_new();
-
-  BN_zero(f->fs[0].h);
-  BN_one(f->fs[0].k);
-
-  BN_one(f->fs[1].h);
-  BN_zero(f->fs[1].k);
-
-  f->i = 2;
-  if (!BN_copy(f->x.h, num)) return NULL;
-  if (!BN_copy(f->x.k, den)) return NULL;
-
-  return f;
-}
-
-
-/**
- * \brief Produces the next fraction.
- *
- * Each new approximation hᵢ/kᵢ is defined rec ursively as:
- *   hᵢ = aᵢhᵢ₋₁ + hᵢ₋₂
- *   kᵢ = aᵢkᵢ₋₁ + kᵢ₋₂
- * Meanwhile each new aᵢ is simply the integer part of x.
- *
- *
- * \param f   The continued fraction.
- * \return NULL if the previous fraction approximates at its best the number,
- *         a pointer to the next fraction in the series othw.
- */
-bigfraction_t* cf_next(cf_t *f)
-{
-  bigfraction_t *ith_fs = &f->fs[f->i];
-  BIGNUM* rem = BN_new();
-
-  if (BN_is_zero(f->x.h)) return NULL;
-  BN_div(f->a, rem, f->x.h, f->x.k, f->ctx);
-
-  /* computing hᵢ */
-  BN_mul(f->fs[f->i].h , f->a, f->fs[(f->i-1+3) % 3].h, f->ctx);
-  BN_uadd(f->fs[f->i].h, f->fs[f->i].h, f->fs[(f->i-2+3) % 3].h);
-  /* computing kᵢ */
-  BN_mul(f->fs[f->i].k , f->a, f->fs[(f->i-1+3) % 3].k, f->ctx);
-  BN_uadd(f->fs[f->i].k, f->fs[f->i].k, f->fs[(f->i-2+3) % 3].k);
-
-  f->i = (f->i + 1) % 3;
-  /* update x. */
-  BN_copy(f->x.h, f->x.k);
-  BN_copy(f->x.k, rem);
-
-  return ith_fs;
-}
-
-
-/**
- * \brief Square Root for bignums.
- *
- * An implementation of Dijkstra's Square Root Algorithm.
- * A Discipline of Programming, page 61 - Fifth Exercise.
- *
- */
-int BN_sqrtmod(BIGNUM* dv, BIGNUM* rem, BIGNUM* a, BN_CTX* ctx)
-{
-  BIGNUM *shift;
-  BIGNUM *adj;
-
-  shift = BN_new();
-  adj = BN_new();
-  BN_zero(dv);
-  BN_copy(rem, a);
-
-  /* hacking into internal sequence to skip some cycles. */
-  /* for  (BN_one(shift);     original */
-  for (bn_wexpand(shift, a->top+1), shift->top=a->top, shift->d[shift->top-1] = 1;
-       BN_ucmp(shift, rem) != 1;
-       /* BN_rshift(shift, shift, 2); */
-       BN_lshift1(shift, shift), BN_lshift1(shift, shift));
-
-
-  while (!BN_is_one(shift)) {
-    /* BN_rshift(shift, shift, 2); */
-    BN_rshift1(shift, shift);
-    BN_rshift1(shift, shift);
-
-    BN_uadd(adj, dv, shift);
-    BN_rshift1(dv, dv);
-    if (BN_ucmp(rem, adj) != -1) {
-      BN_uadd(dv, dv, shift);
-      BN_usub(rem, rem, adj);
-    }
-  }
-
-  BN_free(shift);
-  BN_free(adj);
-  return BN_is_zero(rem);
-}
+#include "qa/questions/questions.h"
+#include "qa/questions/qarith.h"
+#include "qa/questions/qwiener.h"
 
 
 /*
  *  Weiner Attack Implementation
  */
-
-int wiener_question_setup(void) { return 0; }
-
-int wiener_question_teardown(void) { return 0; }
-
-int wiener_question_test(X509* cert) { return 1; }
-
-
-int wiener_question_ask(X509* cert)
+static RSA*
+wiener_question_ask_rsa(const RSA *rsa)
 {
-  RSA *rsa;
   /* key data */
+  RSA *ret = NULL;
   BIGNUM *n, *e, *d, *phi;
-  BIGNUM *p, *q;
   /* continued fractions coefficient, and mod */
   cf_t* cf;
   bigfraction_t *it;
@@ -206,7 +40,6 @@ int wiener_question_ask(X509* cert)
   BN_CTX *ctx;
   int bits;
 
-  rsa = X509_get_pubkey(cert)->pkey.rsa;
   phi = BN_new();
   tmp = BN_new();
   rem = BN_new();
@@ -255,11 +88,13 @@ int wiener_question_ask(X509* cert)
     BN_usub(delta, tmp, n);
     if (!BN_sqrtmod(tmp, rem, delta, ctx)) continue;
     /* key found :) */
-    p = BN_new();
-    q = BN_new();
-    BN_usub(p, b2, tmp);
-    BN_uadd(q, b2, tmp);
-    //printf("Primes: %s %s", BN_bn2dec(p), BN_bn2dec(q));
+    ret = RSA_new();
+    ret->n = rsa->n;
+    ret->e = rsa->e;
+    ret->p = BN_new();
+    ret->q = BN_new();
+    BN_usub(ret->p, b2, tmp);
+    BN_uadd(ret->q, b2, tmp);
     break;
   }
 
@@ -270,15 +105,17 @@ int wiener_question_ask(X509* cert)
   BN_free(delta);
   BN_free(phi);
 
-  return i;
+  return ret;
 }
 
 
 
 qa_question_t WienerQuestion = {
-  .name = "Wiener",
-  .setup = wiener_question_setup,
-  .teardown = wiener_question_teardown,
-  .test = wiener_question_test,
-  .ask = wiener_question_ask
+  .name = "wiener",
+  .pretty_name = "Wiener's Attack",
+  .setup = NULL,
+  .teardown = NULL,
+  .test = NULL,
+  .ask_rsa = wiener_question_ask_rsa,
+  .ask_crt = NULL,
 };

+ 12 - 0
src/tests/Makefile.am

@@ -0,0 +1,12 @@
+# unittesting my ass
+AM_CFLAGS = -I ../include/
+AM_LDFLAGS = -lcrypto -lssl
+
+check_PROGRAMS = test_qa_sock test_qa
+TESTS = $(check_PROGRAMS) test_cmdline.test
+
+test_qa_sock_SOURCES = test_qa_sock.c
+test_qa_sock_LDADD = ../qa_sock.o
+
+test_qa_SOURCES = test_qa.c
+test_qa_LDADD = ../qa_sock.o ../qa.o ../questions/allquestions.o ../questions/libquestions.a

+ 30 - 0
src/tests/dummy.crt

@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIE3zCCA8egAwIBAgIQaLrqpO+0m3j1KxmYyr+CGzANBgkqhkiG9w0BAQUFADBB
+MQswCQYDVQQGEwJGUjESMBAGA1UEChMJR0FOREkgU0FTMR4wHAYDVQQDExVHYW5k
+aSBTdGFuZGFyZCBTU0wgQ0EwHhcNMTMwNTE1MDAwMDAwWhcNMTQwNTE5MjM1OTU5
+WjBgMSEwHwYDVQQLExhEb21haW4gQ29udHJvbCBWYWxpZGF0ZWQxJDAiBgNVBAsT
+G0dhbmRpIFN0YW5kYXJkIFdpbGRjYXJkIFNTTDEVMBMGA1UEAxQMKi5yaXNldXAu
+bmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0728UY+67ogXceEz
+2wTXgYix9EXowEk8Eb5LcW7mitYzhM0rsJfDp1V+jsJy5h1nJJQYcW2CAmKm1OkX
+Ld1vYZHEToHOmarkDIsB6Bx44ayfHtSMTTJrRRFekgOOABaGefR/sSRD7PkSfnxj
+LvfFXiyIM+FeQ2X3Ol/cGPIq30fqWyKd8cs74Geku92bopmdRVYZ2SthOsgtagus
+KvhvLv/maI3DCr+sspvL2eW9IrfSb3M6eyCQef8tL12kepsJHrzv0RpZxQkiRfbU
+a34SauVQs35/zyqvvNp75pOPOAMlmf7nBZK6Py0SFVbDx0Q5y4VHNgVQXpUPc0kj
+lq92LQIDAQABo4IBsjCCAa4wHwYDVR0jBBgwFoAUtqj/oqgv0KbNS7Fo8+dQEDGn
+eSEwHQYDVR0OBBYEFGUdEnjQeczO8TeIGaRF8gjVAT62MA4GA1UdDwEB/wQEAwIF
+oDAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBg
+BgNVHSAEWTBXMEsGCysGAQQBsjEBAgIaMDwwOgYIKwYBBQUHAgEWLmh0dHA6Ly93
+d3cuZ2FuZGkubmV0L2NvbnRyYWN0cy9mci9zc2wvY3BzL3BkZi8wCAYGZ4EMAQIB
+MDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuZ2FuZGkubmV0L0dhbmRpU3Rh
+bmRhcmRTU0xDQS5jcmwwagYIKwYBBQUHAQEEXjBcMDcGCCsGAQUFBzAChitodHRw
+Oi8vY3J0LmdhbmRpLm5ldC9HYW5kaVN0YW5kYXJkU1NMQ0EuY3J0MCEGCCsGAQUF
+BzABhhVodHRwOi8vb2NzcC5nYW5kaS5uZXQwIwYDVR0RBBwwGoIMKi5yaXNldXAu
+bmV0ggpyaXNldXAubmV0MA0GCSqGSIb3DQEBBQUAA4IBAQBCPaLL3IrQk4hBKNtZ
+uwsHzrhSL++nRn7v8iKGwy6wYoVqBMArvPaCs0eQKBUWpN8c//S1P4LuKXrQ93O2
+2iMccTmZxWXQb0v5bb9jRYVfsSROonJvnUT+Z1D98Wo+I0+db0oiEQszATlsge9u
+LljxMNF8INKL4+Fu7wgTHFgVL0nB6YfnkJTP0yo8HYWFdfZfOFyOPde7YZ+n4Dkh
+zfXP3KXrHdgWv5F+H66HWjEWz5HPjBdy9hOpFM5KGJyxfu8yRGH19Ujh3etMqaPD
+5fGMo/K82hpv6NX+BK2CitqTiUM5uYNX0qt2HiHjNmKyATvLnfX7EICQjWStdwB8
+8qMQ
+-----END CERTIFICATE-----
+

+ 8 - 0
src/tests/test_cmdline.test

@@ -0,0 +1,8 @@
+#!/bin/sh
+
+../qa -a non_existing_test < /dev/null
+if [ $? -ne 1 ]
+then
+    echo "Invalid questions shall raise an error."
+    exit 1
+fi

+ 2 - 2
src/tests/test_qa.c

@@ -3,7 +3,7 @@
 
 #include <openssl/x509.h>
 
-#include "qa.h"
+#include "qa/qa.h"
 
 
 void test_get_local_cert(void)
@@ -16,7 +16,7 @@ void test_get_local_cert(void)
   strcpy(path, "/lifting/me/higher/keeps/me/lifting.crt");
   assert(!get_local_cert(path));
 
-  strcpy(path, "/home/maker/dev/uni/thesis/src/dummy.crt");
+  strcpy(path, "dummy.crt");
   crt = get_local_cert(path);
   assert(crt);
   pkey = X509_get_pubkey(crt);

+ 81 - 0
src/tests/test_qa_sock.c

@@ -0,0 +1,81 @@
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <openssl/bio.h>
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+
+#include "qa/qa.h"
+#include "qa/qa_sock.h"
+
+FILE* ferr;
+
+void set_up(void)
+{
+  ferr = fopen("/dev/null", "w");
+
+  /* Initialize SSL Library by registering algorithms. */
+  SSL_library_init();
+  /* trash directly network informative messages */
+  if (ferr) bio_err = BIO_new_fp(ferr, BIO_NOCLOSE);
+  else errno = 0;
+}
+
+void tear_down(void)
+{
+  fclose(ferr);
+}
+void test_host_port(void)
+{
+  char uri[100];
+  char *host, *port;
+
+  /* uris of the form host:port shall be recognized */
+  strcpy(uri, "host:port");
+  host_port(uri, &host, &port);
+  assert(!strcmp(host, "host") &&
+         !strcmp(port, "port"));
+  /* uris given as urls shall be recognized */
+  strcpy(uri, "https://cheese");
+  host_port(uri, &host, &port);
+  assert(!strcmp(host, "cheese") &&
+         !strcmp(port, "https"));
+  /* uris containing just a hostname shall be recognized */
+  strcpy(uri, "queer");
+  host_port(uri, &host, &port);
+  assert(!strcmp(host, "queer") &&
+         !port);
+}
+
+void test_get_remote_cert(void)
+{
+  X509 *crt;
+  char url[100];
+
+  /* NULL shall be returned if the host does not exists. */
+  strcpy(url, "space_oddity");
+  crt = get_remote_cert(url);
+  assert(!crt);
+
+#ifdef NETWORK_TESTS
+  /* Google™ shall support https, and accept tcp connections on https default port. */
+  strcpy(url, "google.com:443");
+  crt = get_remote_cert(url);
+  assert(crt);
+
+#else
+  printf("Skipping %s..\n", __func__);
+
+#endif /* NETWORK_TESTS */
+}
+int main(int argc, char **argv)
+{
+  set_up();
+
+  test_host_port();
+  test_get_remote_cert();
+
+  return 0;
+}