Selaa lähdekoodia

Merge remote-tracking branch 'origin/feature/wiener'

Mergin before the meeting of Thursday, to improve stability and demo features.
Michele Orrù 11 vuotta sitten
vanhempi
commit
ae69ee241d

+ 36 - 0
book/wiener.tex

@@ -0,0 +1,36 @@
+\documentclass[10pt, a4paper]{report}
+\usepackage[a4paper,
+  inner=1.5cm, outer=3cm,
+  top=3cm, bottom=3cm,
+  bindingoffset=1cm]{geometry}
+\usepackage{minted}
+\usepackage{hyperref}
+
+\begin{document}
+
+\section{Bombelli's Algoritm}
+%% cuz python is pseudocode.
+\begin{minted}[fontsize=\small]{python}
+  def intsqrt(a):
+    i = 0
+    while a > 0:
+        g[i] = a % 100
+        a /= 100
+        i += 1
+
+    x = 0
+    r = 0
+    for j in range(L-1, -1, -1):
+        r = r*100 + g[j]
+        y = 0
+        for d in range(1, 10):
+            yn = d*(20*x + d)
+            if yn < r: y = yn
+            else: break
+        r -= y
+        x = 10*x + d-1
+    return (x, r)
+\end{minted}
+Has complexity $O(\log ^2 n)$.
+
+\end{document}

+ 65 - 0
src/apps/gen.c

@@ -0,0 +1,65 @@
+/**
+ * \file gen.c
+ *
+ * Generate a fake RSA certificate file, given as input e, d, p, q.
+ */
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <openssl/bn.h>
+#include <openssl/ssl.h>
+#include <openssl/rsa.h>
+
+
+void usage(void)
+{
+  static const char* help_message = "%s usage: %s"
+    " <pub key> <priv key> <p> <q>"
+    "\n";
+  fprintf(stderr, help_message, program_invocation_short_name,
+          program_invocation_name);
+}
+
+int main(int argc, char **argv)
+{
+  BN_CTX* ctx;
+  BIGNUM* p1, *q1;
+  RSA* rsa;
+
+  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;
+  }
+
+  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());
+  BN_mod(rsa->dmq1, rsa->d, q1, ctx);
+  BN_mod(rsa->dmp1, rsa->d, p1, ctx);
+  BN_mod_inverse(rsa->iqmp, rsa->q, rsa->p, ctx);
+  PEM_write_RSAPrivateKey(stdout, rsa, NULL, NULL, 0, NULL, NULL);
+
+
+  BN_CTX_free(ctx);
+  BN_free(q1);
+  BN_free(p1);
+  RSA_free(rsa);
+
+  return 0;
+}

+ 34 - 54
src/cmdline.c

@@ -16,44 +16,6 @@
 
 #include "qa.h"
 
-#define QA_DEFAULT_PORT "443"
-
-
-/**
- * \brief Converts a uri into a tuple {host, service}.
- *
- * Parses an input string containing a host and (maybe) a service/port.
- * Valid options are:
- *  - service://hostname
- *  - hostname
- *  - hostname:port
- * The resulting tuple will be stored in params \ref host and \ref service.
- *
- * \param[in]  uri     The input uri.
- * \param[out] host    Place where to store parsed host.
- * \param[out] service Place where to store parsed service.
- *
- * \note \ref uri might be modified during parsing.
- */
-static void host_port(char* uri, char** host, char** service)
-{
-  char* c;
-
-  if (!(c = strchr(uri, ':')))
-    *host = uri;
-  else {
-    *c = '\0';
-    if (c[1] != '/' && c[2] == '/') {
-      *service = uri;
-      *host = c+3;
-    } else {
-      *service = c+1;
-      *host = uri;
-    }
-  }
-}
-
-
 /**
  * \brief Prints the usage message, then exit.
  *
@@ -63,14 +25,19 @@ static void host_port(char* uri, char** host, char** service)
 void usage(void)
 {
   static const char* help_message = "%s usage: %s"
-    " [-p PORT]"
-    " <target>"
+    " [-r HOST:port | -f FILE]"
     " \n";
   fprintf(stderr, help_message,
           program_invocation_short_name,
           program_invocation_name);
 }
 
+void conflicting_args(void)
+{
+  printf("Conflicting fuffa\n");
+  usage();
+  exit(EXIT_FAILURE);
+}
 
 int main(int argc, char** argv)
 {
@@ -79,15 +46,15 @@ int main(int argc, char** argv)
   size_t i;
 
   struct option long_options[] = {
-    {"help", required_argument, NULL, 'h'},
-    {"port", required_argument, NULL, 'p'},
+    {"help", no_argument, NULL, 'h'},
+    {"remote", required_argument, NULL, 'r'},
+    {"file", required_argument, NULL, 'f'},
     {0, 0, 0, 0}
   };
-  static const char* short_options = "h:p:";
+  static const char* short_options = "hr:f:";
 
   struct qa_conf conf = {
-    .host = NULL,
-    .port = NULL,
+    .src_type = NONE,
   };
 
   while ((opt=getopt_long(argc, argv,
@@ -98,8 +65,15 @@ int main(int argc, char** argv)
       usage();
       exit(EXIT_SUCCESS);
       break;
-    case 'p':
-      conf.port = optarg;
+    case 'f':
+      if (conf.src_type != NONE) conflicting_args();
+      conf.src_type = LOCAL;
+      conf.src = optarg;
+      break;
+    case 'r':
+      if (conf.src_type != NONE) conflicting_args();
+      conf.src_type = REMOTE;
+      conf.src = optarg;
       break;
     case '?':
     default:
@@ -107,14 +81,20 @@ int main(int argc, char** argv)
       exit(EXIT_FAILURE);
     }
 
-  if (optind < argc && !strcmp(argv[optind], "--")) optind++;
-  if (optind != argc-1) {
-    usage();
-    exit(EXIT_FAILURE);
+  if (conf.src_type == NONE)  {
+    conf.src_type = REMOTE;
+
+    if (optind == argc)
+      conf.src = "-";
+    else if (optind == argc-1)
+      conf.src = argv[optind];
+    else if (optind == argc-2 && !strcmp(argv[optind], "--"))
+      conf.src = argv[optind+1];
+    else {
+        usage();
+        exit(EXIT_FAILURE);
+      }
   }
 
-  host_port(argv[optind], &conf.host, &conf.port);
-  if (!conf.port) conf.port = QA_DEFAULT_PORT;
-
   return qa_init(&conf);
 }

+ 7 - 3
src/include/qa.h

@@ -4,14 +4,18 @@
 #include <openssl/bio.h>
 
 struct qa_conf {
-  char *host;
-  char *port;
+  enum sources {
+    NONE, LOCAL, REMOTE
+  } src_type;
+  char *src;
 };
 
 
-BIO* bio_out;
+extern BIO* bio_out;
+extern BIO* bio_err;
 
 int qa_init(const struct qa_conf* args);
 
+X509* get_local_cert(const char *src);
 
 #endif   /* _QA_H_ */

+ 5 - 1
src/include/qa_sock.h

@@ -5,4 +5,8 @@
 
 int init_client(const struct qa_conf *options);
 
-#endif
+int host_port(char *uri, char **host, char **service);
+
+X509* get_remote_cert(char *address);
+
+#endif   /* _QA_SOCK_H_ */

+ 34 - 104
src/qa.c

@@ -1,51 +1,24 @@
 #include <assert.h>
+#include <error.h>
 #include <stdio.h>
 #include <stdint.h>
+#include <string.h>
 #include <unistd.h>
-#include <netdb.h>
 
-#include <openssl/ssl.h>
 #include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
 #include <openssl/x509.h>
-#include <openssl/evp.h>
-#include <openssl/bio.h>
 
 #include "qa.h"
 #include "questions.h"
 #include "qa_sock.h"
 
-/**
- * \brief Connection informations.
- */
-struct qa_connection {
-  int socket;   /**< socket file descriptor. */
-  SSL* ssl;     /**< ssl handler for this connection. */
-  SSL_CTX* ctx; /**< ssl context used in this connection. */
-};
-
-
-/**
- * \brief Destructor for a \ref qa_connection.
- *
- * Closes the socket, shuts down the connection, and frees all memory used for
- * holding the connection.
- * \note Input might be partial (ex. a socket exists, but not no ssl session).
- *
- * \param c   The connection to be freed.
- */
-static void qa_connection_free(struct qa_connection* c)
-{
-  if (c->socket)
-    close(c->socket);
-  if (c->ssl) {
-    SSL_shutdown(c->ssl);
-    SSL_free(c->ssl);
-  }
-  if (c->ctx)
-    SSL_CTX_free(c->ctx);
+/** BIO wrapper around stdout */
+BIO* bio_out;
+/** BIO wrapper around srderr */
+BIO* bio_err;
 
-  free(c);
-}
 
 void qa_abort(const char *reason)
 {
@@ -53,99 +26,56 @@ void qa_abort(const char *reason)
   exit(EXIT_FAILURE);
 }
 
-
-static int verify_callback(int ok, X509_STORE_CTX* ctx)
-{
-  return ok;
-}
-
-
-/**
- * \brief Set up a new ssl connection.
- *
- * Create a new \ref qa_connection, turning on OpenSSL (if not yet started), and
- * opening a socket with the target server over ssl.
- *
- * \param[in] conf  Configuration holding informations about the target.
- * \return          The new connection.
- */
-struct qa_connection* qa_connection_new(const struct qa_conf* conf)
+X509* get_local_cert(const char *src)
 {
-  struct qa_connection* c;
-  int err;
+  X509* crt;
+  FILE* fp;
 
-  c = malloc(sizeof(struct qa_connection));
-  if (!c) qa_abort("No Memory.");
+  if (!strcmp(src, "-")) fp = stdin;
+  else if (!(fp = fopen(src, "r")))
+    return NULL;
 
-  /* Initialize SSL Library by registering algorithms. */
-  SSL_library_init();
-
-
-  c->ctx = SSL_CTX_new(SSLv23_client_method());
-  if (!c->ctx) {
-    qa_connection_free(c);
-    qa_abort("Cannot create context");
-  }
-
-  /* is also the default. lol. */
-  SSL_CTX_set_verify(c->ctx, SSL_VERIFY_NONE, verify_callback);
-  c->ssl = SSL_new(c->ctx);
-  if (!c->ssl) {
-    qa_connection_free(c);
-    qa_abort("Cannot create ssl handle");
-  }
-
-  if (!(c->socket = init_client(conf))) {
-    qa_connection_free(c);
-    qa_abort("Cannot create socket.");
-  }
-
-  if (!SSL_set_fd(c->ssl, c->socket)) {
-    qa_connection_free(c);
-    qa_abort("Cannot bind socket to ssl session");
-  }
-
-  /* XXX. Handle errors appropriately using error codes from OpenSSL */
-  err = SSL_connect(c->ssl);
-  if (err != 1) {
-    qa_connection_free(c);
-    qa_abort("Cannot Connect");
-  }
-
-  SSL_set_connect_state(c->ssl);
-
-  return c;
+  crt = PEM_read_X509(fp, NULL, 0, NULL);
+  return crt;
 }
 
-
 /**
  * \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* args)
+int qa_init(const struct qa_conf* conf)
 {
   X509 *crt;
-  struct qa_connection *c;
   struct qa_question *q;
 
-  /* bind stdout to a BIO shit to be used externally */
+  /* bind stdout/stderr to a BIO shit to be used externally */
   bio_out = BIO_new_fp(stdout, BIO_NOCLOSE);
+  bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
+
+  /* Initialize SSL Library by registering algorithms. */
+  SSL_library_init();
+
+
+  if (conf->src_type == REMOTE)
+    crt = get_remote_cert(conf->src);
+  else if (conf->src_type == LOCAL)
+    crt = get_local_cert(conf->src);
+  else
+    error(EXIT_FAILURE, 0, "iternal error: unable to determine source type.");
 
-  /* create a new connection, and download the certificate */
-  c = qa_connection_new(args);
-  crt = SSL_get_peer_certificate(c->ssl);
-  if (!crt) qa_abort("Cannot obtain certificate");
+  if (!crt)
+    error(EXIT_FAILURE, errno, "oops");
 
   register_all_questions();
-for (q=questions.lh_first; q; q = q->qs.le_next) {
+  for (q=questions.lh_first; q; q = q->qs.le_next) {
     q->setup();
     q->test(crt);
     q->ask(crt);
     q->teardown();
   }
 
-  qa_connection_free(c);
+  X509_free(crt);
 
   return 0;
 }

+ 157 - 5
src/qa_sock.c

@@ -1,3 +1,5 @@
+#define _POSIX_SOURCE
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <strings.h>
@@ -8,26 +10,69 @@
 #include <sys/socket.h>
 #include <netdb.h>
 
+#include <openssl/x509.h>
+#include <openssl/ssl.h>
+
 #include "qa.h"
 
 #define SOCKET_PROTOCOL 0
 #define INVALID_SOCKET  (-1)
 
-int init_client(const struct qa_conf *options)
+/**
+ * \brief Converts a uri into a tuple {host, service}.
+ *
+ * Parses an input string containing a host and (maybe) a service/port.
+ * Valid options are:
+ *  - service://hostname
+ *  - hostname
+ *  - hostname:port
+ * The resulting tuple will be stored in params \ref host and \ref service.
+ * \note \ref uri might be modified during parsing.
+ *
+ * \param[in]  uri     The input uri.
+ * \param[out] host    Place where to store parsed host.
+ * \param[out] service Place where to store parsed service.
+ *
+ * \return 0 if the string was not parsable, 1 otherwise.
+
+ */
+int host_port(char *uri, char **host, char **service)
+{
+  char* c;
+
+  if (!(c = strchr(uri, ':'))) {
+    *host = uri;
+    *service = NULL;
+  } else {
+    *c = '\0';
+    if (c[1] == '/' && c[2] == '/') {
+      *service = uri;
+      *host = c+3;
+    } else {
+      *service = c+1;
+      *host = uri;
+    }
+  }
+
+  return 1;
+}
+
+int init_client(const char *host, const char *port)
 {
   struct addrinfo hints;
   struct addrinfo *result, *rp;
   int s;
   int i;
 
+
   hints.ai_family = AF_UNSPEC;
   hints.ai_socktype = SOCK_STREAM;
   hints.ai_flags = 0;
   hints.ai_protocol = 0;
 
-  if ((i=getaddrinfo(options->host, options->port, NULL, &result))) {
-    fprintf(stderr, "Error: %s\n", gai_strerror(i));
-    exit(EXIT_FAILURE);
+  if ((i=getaddrinfo(host, port, NULL, &result))) {
+    BIO_printf(bio_err, "Error: %s\n", gai_strerror(i));
+    return -1;
   }
 
   for (rp=result; rp; rp = rp->ai_next)  {
@@ -44,7 +89,114 @@ int init_client(const struct qa_conf *options)
 
     if (connect(s, rp->ai_addr, rp->ai_addrlen) != -1) break;
   }
-  if (!rp) exit(EXIT_FAILURE);
+  if (!rp) return -1;
 
   return s;
 }
+
+/**
+ * \brief Connection informations.
+ */
+typedef struct qa_connection {
+  int socket;   /**< socket file descriptor. */
+  SSL* ssl;     /**< ssl handler for this connection. */
+  SSL_CTX* ctx; /**< ssl context used in this connection. */
+} qa_connection_t;
+
+
+/**
+ * \brief Destructor for a \ref qa_connection.
+ *
+ * Closes the socket, shuts down the connection, and frees all memory used for
+ * holding the connection.
+ * \note Input might be partial (ex. a socket exists, but not no ssl session).
+ *
+ * \param c   The connection to be freed.
+ */
+static void qa_connection_free(struct qa_connection* c)
+{
+  if (!c) return;
+
+  if (c->socket != -1)
+    close(c->socket);
+  if (c->ssl) {
+    SSL_shutdown(c->ssl);
+    SSL_free(c->ssl);
+  }
+  if (c->ctx)
+    SSL_CTX_free(c->ctx);
+
+  free(c);
+}
+
+
+static int verify_callback(int ok, X509_STORE_CTX* ctx)
+{
+  return ok;
+}
+
+
+/**
+ * \brief Set up a new ssl connection.
+ *
+ * Create a new \ref qa_connection, turning on OpenSSL (if not yet started), and
+ * opening a socket with the target server over ssl.
+ *
+ * \param[in] conf  Configuration holding informations about the target.
+ * \return          The new connection.
+ */
+struct qa_connection* qa_connection_new(char* address)
+{
+  struct qa_connection* c;
+  char *host, *port;
+  int err;
+
+  /* parse input address */
+  if (!host_port(address, &host, &port)) goto error;
+
+  c = malloc(sizeof(struct qa_connection));
+  if (!c) goto error;
+  /* set up context, and protocol versions */
+  c->ctx = SSL_CTX_new(SSLv23_client_method());
+  if (!c->ctx) goto error;
+  /* create the ssl session, disabling certificate verification */
+  SSL_CTX_set_verify(c->ctx, SSL_VERIFY_NONE, verify_callback);
+  c->ssl = SSL_new(c->ctx);
+  if (!c->ssl) goto error;
+  /* open the socket over ssl */
+  c->socket = init_client(host, port);
+  if (c->socket == -1)                goto error;
+  if (!SSL_set_fd(c->ssl, c->socket)) goto error;
+  if (SSL_connect(c->ssl) != 1)       goto error;
+
+  SSL_set_connect_state(c->ssl);
+  return c;
+
+ error:
+  /* XXX. add checks for errno, and the ssl error stack (ssl_get_error) */
+  qa_connection_free(c);
+  return NULL;
+}
+
+/**
+ * \brief Fetches the certificate opening a tcp connection to the given address.
+ *
+ * Attempts to open a new tcp connection to the address `address`, and return
+ * the X509 certificate presented by the server.
+ *
+ * \param address[in] the uri to which handshake a ssl connection.
+ * \return a valid pointer to the X509 certificate if the handshake succeeded,
+ *         NULL otherwise.
+ */
+X509* get_remote_cert(char *address)
+{
+  X509 *crt;
+  qa_connection_t *c;
+
+  c = qa_connection_new(address);
+  if (!c) return NULL;
+
+  crt = SSL_get_peer_certificate(c->ssl);
+  qa_connection_free(c);
+  return crt;
+}

+ 2 - 2
src/questions/allquestions.c

@@ -6,7 +6,7 @@
 #define REGISTER_QUESTION(q)                      \
   {                                               \
       extern struct qa_question q;                \
-      LIST_INSERT_HEAD(&questions, &q, qs);        \
+      LIST_INSERT_HEAD(&questions, &q, qs);       \
   }
 
 /**
@@ -20,5 +20,5 @@ void register_all_questions(void)
   LIST_INIT(&questions);
 
   REGISTER_QUESTION(ExampleQuestion);
-
+  REGISTER_QUESTION(WienerQuestion);
 }

+ 14 - 4
src/questions/example.c

@@ -1,7 +1,16 @@
+#include <openssl/bio.h>
+#include <openssl/x509.h>
+
+
 #include "questions.h"
-#include "qa.h"
 
-int example_question_setup(void) { return 0; }
+static BIO* out;
+
+int example_question_setup(void) {
+  out = BIO_new_fp(stdout, BIO_NOCLOSE);
+  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; }
@@ -10,12 +19,13 @@ int example_question_ask(X509* cert)
   EVP_PKEY* pkey;
 
   pkey = X509_get_pubkey(cert);
-  EVP_PKEY_print_public(bio_out, pkey, 3, NULL);
+  EVP_PKEY_print_public(out, pkey, 3, NULL);
+  return 1;
 }
 
 
 
-struct qa_question ExampleQuestion = {
+qa_question_t ExampleQuestion = {
   .name = "Example Question",
   .setup = example_question_setup,
   .teardown = example_question_teardown,

+ 2 - 2
src/questions/questions.h

@@ -6,7 +6,7 @@
 #include <openssl/x509.h>
 
 
-struct qa_question {
+typedef struct qa_question {
   const char* name;
   int (* setup) (void);
   int (* teardown) ();
@@ -14,7 +14,7 @@ struct qa_question {
   int (* ask) (X509* cert);
 
   LIST_ENTRY(qa_question) qs;
-};
+} qa_question_t;
 
 
 LIST_HEAD(listhead, qa_question) questions;

+ 27 - 12
src/questions/qwiener.h

@@ -2,19 +2,34 @@
 #define _QA_WIENER_H
 
 #include <stdlib.h>
+#include <openssl/bn.h>
 
-struct cf {
-  struct fraction {
-    long h;
-    long k;
-  } fs[3];
-  short int i;
-  double x;
-  long a;
-};
+/**
+ * Fractions made of bignums.
+ */
+typedef struct bigfraction {
+  BIGNUM* h;
+  BIGNUM* k;
+} bigfraction_t;
 
-void cfrac_init(struct cf* f, double x);
-struct fraction* cfrac_next(struct cf* f);
 
+typedef struct cf {
+  bigfraction_t fs[3];
+  short i;
+  bigfraction_t x;
+  BIGNUM* a;
+  BN_CTX* ctx;
+} cf_t;
+
+/* continued fractions utilities. */
+cf_t* cf_new(void);
+cf_t* cf_init(cf_t *f, BIGNUM *num, BIGNUM *b);
+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
+
+#endif /* _QA_WIENER_H_ */

+ 132 - 20
src/questions/test/test_wiener.c

@@ -1,18 +1,28 @@
 #include <assert.h>
 #include <math.h>
 
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+
 #include "questions.h"
 #include "qwiener.h"
 
+
 /**
  * \brief Testing the continued fractions generator.
  */
 void test_cf(void)
 {
-  double x;
-  struct cf f;
-  struct fraction *it;
+  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ᵢ
@@ -24,17 +34,18 @@ void test_cf(void)
    *                 2 + …
    *
    */
-  x = sqrt(2);
-  cfrac_init(&f, x);
-
-  it = cfrac_next(&f);
-  assert(it && f.a == 1);
-  it = cfrac_next(&f);
-  for (i=0; i!=10 && it; i++) {
-    assert(f.a == 2);
-    it = cfrac_next(&f);
+  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==10);
+  assert(i==5);
 
   /*
    * Testing hᵢ/kᵢ
@@ -45,21 +56,122 @@ void test_cf(void)
    *                      1 + ⎽⎽⎽⎽⎽
    *                          1 + …
    */
-  int fib[] = {1, 1, 2, 3, 5, 8, 13};
-  x = (1 + sqrt(5))/2;
-  cfrac_init(&f, x);
-  it = cfrac_next(&f);
+  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++) {
-    assert(it->h == fib[i] &&
-           it->k == fib[i-1]);
-    it=cfrac_next(&f);
+    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;
+
+  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);
+}
+
+
+void test_wiener(void)
+{
+  X509 *crt;
+  FILE *fp = fopen("test/wiener_test.crt", "r");
+
+  crt = PEM_read_X509(fp, NULL, 0, NULL);
+  if (!crt) {
+    ERR_print_errors();
+    exit(EXIT_FAILURE);
+  }
+
+  WienerQuestion.setup();
+  assert(WienerQuestion.test(crt));
+  assert(WienerQuestion.ask(crt));
+  WienerQuestion.teardown();
+}
+
 int main(int argc, char ** argv)
 {
   test_cf();
+  test_BN_sqrtmod();
+  test_wiener();
+
   return 0;
 }

+ 10 - 0
src/questions/test/wiener_test.crt

@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBWzCCAU+gAwIBAgIJAO0CRfiHioXRMAMGAQAwUTELMAkGA1UEBhMCSVQxDjAM
+BgNVBAgMBUl0YWx5MQ8wDQYDVQQHDAZUcmVudG8xITAfBgNVBAoMGEludGVybmV0
+IFdpZGdpdHMgUHR5IEx0ZDAeFw0xMzEyMDEwOTIyMjVaFw0xMzEyMzEwOTIyMjVa
+MFExCzAJBgNVBAYTAklUMQ4wDAYDVQQIDAVJdGFseTEPMA0GA1UEBwwGVHJlbnRv
+MSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwIDANBgkqhkiG9w0B
+AQEFAAMPADAMAgQJkWRTAgQDnqaNo1AwTjAdBgNVHQ4EFgQUfCWIgzopf97JPQrs
+h2MlJhPvQy0wHwYDVR0jBBgwFoAUfCWIgzopf97JPQrsh2MlJhPvQy0wDAYDVR0T
+BAUwAwEB/zADBgEAAwEA
+-----END CERTIFICATE-----

+ 41 - 5
src/questions/weiner.c

@@ -1,16 +1,52 @@
-int wiener_setup(void)
+#include <openssl/x509.h>
+#include <math.h>
+#include <stdlib.h>
+
+#include "questions.h"
+#include "weiner.h"
+
+int wiener_question_setup(void) { return 0; }
+int wiener_question_teardown(void) { return 0; }
+
+int wiener_question_test(X509* cert) { return 1; }
+
+
+void cfrac_init(struct cf* f, double x)
 {
-  return 0;
+  f->fs[0].h = 0;
+  f->fs[0].k = 1;
+
+  f->fs[1].h = 1;
+  f->fs[1].k = 0;
+
+  f->i = 2;
+  f->x = x;
+}
+
+struct fraction cfrac_next(struct cf* f)
+{
+  long a = lrint(floor(f->x));
+  struct fraction ith_cf, *fs = f->fs;
+
+  ith_cf.h = fs[f->i%3].h = a*fs[(f->i-1)%3].h + fs[(f->i-2)%3].h;
+  ith_cf.k = fs[f->i%3].k = a*fs[(f->i-1)%3].k + fs[(f->i-2)%3].k;
+  f->x = 1./(f->x-a);
+  f->i = (f->i+1) % 3;
+
+  return ith_cf;
 }
 
-int wiener_teardown(void)
+int wiener_question_ask(X509* cert)
 {
   return 0;
 }
 
 
 
-struct qa_question q_wiener = {
+struct qa_question WienerQuestion = {
   .name = "Wiener",
-  -question_setup = wiener_setup,
+  .setup = wiener_question_setup,
+  .teardown = wiener_question_teardown,
+  .test = wiener_question_test,
+  .ask = wiener_question_ask
 };

+ 225 - 23
src/questions/wiener.c

@@ -1,16 +1,54 @@
-#include <openssl/x509.h>
+/**
+ * \file wiener.c
+ *
+ */
 #include <math.h>
 #include <stdlib.h>
 
+#include <openssl/x509.h>
+#include <openssl/rsa.h>
+#include <openssl/bn.h>
+
 #include "questions.h"
 #include "qwiener.h"
 
-#define EPS 1e-10
 
-int wiener_question_setup(void) { return 0; }
-int wiener_question_teardown(void) { return 0; }
+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);
+}
 
-int wiener_question_test(X509* cert) { return 1; }
 
 /**
  * \brief Initialized a continued fraction.
@@ -32,25 +70,38 @@ int wiener_question_test(X509* cert) { return 1; }
  *   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.
  */
-void cfrac_init(struct cf* f, double x)
+cf_t* cf_init(cf_t* f, BIGNUM* num, BIGNUM* den)
 {
-  f->fs[0].h = 0;
-  f->fs[0].k = 1;
+  if (!f) f = cf_new();
 
-  f->fs[1].h = 1;
-  f->fs[1].k = 0;
+  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;
-  f->x = x;
-  f->a = 0;
+  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 recursively as:
+ * 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.
@@ -60,31 +111,182 @@ void cfrac_init(struct cf* f, double x)
  * \return NULL if the previous fraction approximates at its best the number,
  *         a pointer to the next fraction in the series othw.
  */
-struct fraction* cfrac_next(struct cf* f)
+bigfraction_t* cf_next(cf_t *f)
 {
-  struct fraction *fs = f->fs;
-  struct fraction *ith_fs = &fs[f->i];
+  bigfraction_t *ith_fs = &f->fs[f->i];
+  BIGNUM* rem = BN_new();
 
-  f->a = lrint(floor(f->x));
-  if (f->x - f->a < EPS) return NULL;
+  if (BN_is_zero(f->x.h)) return NULL;
+  BN_div(f->a, rem, f->x.h, f->x.k, f->ctx);
 
-  fs[f->i].h = f->a * fs[(f->i-1+3) % 3].h + fs[(f->i-2+3) % 3].h;
-  fs[f->i].k = f->a * fs[(f->i-1+3) % 3].k + fs[(f->i-2+3) % 3].k;
+  /* computing hᵢ */
+  if (!BN_mul(f->fs[f->i].h , f->a, f->fs[(f->i-1+3) % 3].h, f->ctx)) goto oh_fuck;
+  if (!BN_add(f->fs[f->i].h, f->fs[f->i].h, f->fs[(f->i-2+3) % 3].h)) goto oh_fuck;
+  /* computing kᵢ */
+  if (!BN_mul(f->fs[f->i].k , f->a, f->fs[(f->i-1+3) % 3].k, f->ctx)) goto oh_fuck;
+  if (!BN_add(f->fs[f->i].k, f->fs[f->i].k, f->fs[(f->i-2+3) % 3].k)) goto oh_fuck;
 
   f->i = (f->i + 1) % 3;
-  f->x = 1. / (f->x - f->a);
+  /* update x. */
+  if (!BN_copy(f->x.h, f->x.k)) goto oh_fuck;
+  if (!BN_copy(f->x.k, rem))    goto oh_fuck;
 
   return ith_fs;
+
+ oh_fuck:
+  printf("of fuck!\n");
+  exit(EXIT_FAILURE);
 }
 
+static void BN_int2bn(BIGNUM** a, short int i)
+{
+  if (!*a) *a = BN_new();
+  /* trolololololol. */
+  BN_one(*a);
+  (*a)->d[0] = i;
+}
+
+
+/**
+ * \brief Square Root for bignums.
+ *
+ */
+int BN_sqrtmod(BIGNUM* dv, BIGNUM* rem, BIGNUM* a, BN_CTX* ctx)
+{
+  char *abn2dec;
+  int g[100];
+  long al;
+  long x = 0, r = 0;
+  int i, j;
+  int d;
+  long y, yn;
+
+  abn2dec = BN_bn2dec(a);
+  sscanf(abn2dec, "%ld", &al);
+
+  r = 0;
+  x = 0;
+  for (i=0; al > 0; i++) {
+    g[i] = al%100;
+    al /= 100;
+  }
+
+  for (j=i-1; j>=0; j--) {
+    r = r*100 + g[j];
+    y = 0;
+    for (d=1; d!=10; d++) {
+      yn = d*(20*x + d);
+      if (yn <= r) y = yn; else break;
+    }
+    r -= y;
+    x = 10*x + d -1;
+  }
+
+  sprintf(abn2dec, "%ld", r);
+  BN_dec2bn(&rem, abn2dec);
+  sprintf(abn2dec, "%ld", x);
+  BN_dec2bn(&dv, abn2dec);
+  OPENSSL_free(abn2dec);
+
+  return BN_is_zero(rem);
+}
+
+
+/*
+ *  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)
 {
-  return 0;
+  RSA *rsa;
+  /* key data */
+  BIGNUM *n, *e, *d, *phi;
+  BIGNUM *p, *q;
+  /* continued fractions coefficient, and mod */
+  cf_t* cf;
+  bigfraction_t *it;
+  size_t  i;
+  BIGNUM *t, *tmp, *rem;
+  /* equation coefficients */
+  BIGNUM *b2, *delta;
+  BN_CTX *ctx;
+
+  rsa = X509_get_pubkey(cert)->pkey.rsa;
+  phi = BN_new();
+  tmp = BN_new();
+  rem = BN_new();
+  n = rsa->n;
+  e = rsa->e;
+  b2 = BN_new();
+  delta = BN_new();
+
+  /*
+   * generate the continued fractions approximating e/N
+   */
+  cf = cf_init(NULL, e, n);
+  ctx = cf->ctx;
+  for (i=0, it = cf_next(cf);
+       // XXX. how many keys shall I test?
+       i!=100 && it;
+       i++, it = cf_next(cf)) {
+    t = it->h;
+    d = it->k;
+    /*
+     * Recovering φ(N) = (ed - 1) / t
+     * TEST1: obviously the couple {t, d} is correct → (ed-1) | t
+     */
+    BN_mul(phi, e, d, cf->ctx);
+    BN_usub(tmp, phi, BN_value_one());
+    BN_div(phi, rem, tmp, t, cf->ctx);
+    if (!BN_is_zero(rem)) continue;
+    // XXX. check, is it possible to fall here, assuming N, e are valid?
+    if (BN_is_odd(phi) && BN_cmp(n, phi) > 0)   continue;
+    /*
+     * Recovering p, q
+     * Solving the equation
+     *  x² + [N-φ(N)+1]x + N = 0
+     * which, after a few passages, boils down to:
+     *  x² + (p+q)x + (pq) = 0
+     *
+     * TEST2: φ(N) is correct → the two roots of x are integers
+     */
+    BN_usub(b2, n, phi);
+    BN_uadd(b2, b2, BN_value_one());
+    BN_rshift(b2, b2, 1);
+    if (BN_is_zero(b2)) continue;
+    /* delta */
+    BN_sqr(tmp, b2, ctx);
+    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));
+    break;
+  }
+
+  cf_free(cf);
+  BN_free(rem);
+  BN_free(tmp);
+  BN_free(b2);
+  BN_free(delta);
+  BN_free(phi);
+
+  return i;
 }
 
 
 
-struct qa_question WienerQuestion = {
+qa_question_t WienerQuestion = {
   .name = "Wiener",
   .setup = wiener_question_setup,
   .teardown = wiener_question_teardown,

+ 38 - 0
src/tests/test_qa.c

@@ -0,0 +1,38 @@
+#include <assert.h>
+#include <string.h>
+
+#include <openssl/x509.h>
+
+#include "qa.h"
+
+
+void test_get_local_cert(void)
+{
+  X509 *crt;
+  EVP_PKEY *pkey;
+  char path[64];
+
+  /* get_local_cert() shall return NULL if the file does not exist. */
+  strcpy(path, "/lifting/me/higher/keeps/me/lifting.crt");
+  assert(!get_local_cert(path));
+
+  strcpy(path, "/home/maker/dev/uni/thesis/src/dummy.crt");
+  crt = get_local_cert(path);
+  assert(crt);
+  pkey = X509_get_pubkey(crt);
+  assert(pkey);
+  /*
+   *  The certificate shall make accessible both the modulus and the public
+   *  exponent.
+   */
+  assert(pkey->pkey.rsa->n);
+  assert(pkey->pkey.rsa->e);
+
+}
+
+
+int main(int argc, char **argv)
+{
+  test_get_local_cert();
+  return 0;
+}