diff src/c/openid.c @ 8:870d99055dd1

Diffie-Hellman started but not fully tested; successfully checked signature from AOL
author Adam Chlipala <adam@chlipala.net>
date Wed, 29 Dec 2010 12:16:32 -0500
parents 976121190b2d
children e637249abfd2
line wrap: on
line diff
--- a/src/c/openid.c	Tue Dec 28 19:57:25 2010 -0500
+++ b/src/c/openid.c	Wed Dec 29 12:16:32 2010 -0500
@@ -5,6 +5,7 @@
 #include <openssl/buffer.h>
 #include <openssl/sha.h>
 #include <openssl/hmac.h>
+#include <openssl/dh.h>
 #include <curl/curl.h>
 #include <expat.h>
 
@@ -13,16 +14,15 @@
 #define BUF_MAX 10240
 #define BUF_INIT 1024
 
-struct uw_OpenidFfi_discovery {
-  uw_Basis_string endpoint, localId;
-};
+#define PRIME_LEN 64
+#define GENERATOR DH_GENERATOR_5
 
 uw_Basis_string uw_OpenidFfi_endpoint(uw_context ctx, uw_OpenidFfi_discovery d) {
-  return d->endpoint;
+  return d.endpoint;
 }
 
 uw_Basis_string uw_OpenidFfi_localId(uw_context ctx, uw_OpenidFfi_discovery d) {
-  return d->localId;
+  return d.localId;
 }
 
 uw_unit uw_OpenidFfi_init(uw_context ctx) {
@@ -45,7 +45,7 @@
 
 typedef struct {
   uw_context ctx;
-  uw_OpenidFfi_discovery d;
+  uw_OpenidFfi_discovery *d;
 } endpoint;
 
 static void XMLCALL startElement(void *userData, const XML_Char *name, const XML_Char **atts) {
@@ -91,7 +91,7 @@
   char *s;
   CURL *c = curl(ctx);
   curl_discovery_data cd = {};
-  uw_OpenidFfi_discovery dy = uw_malloc(ctx, sizeof(struct uw_OpenidFfi_discovery));
+  uw_OpenidFfi_discovery *dy = uw_malloc(ctx, sizeof(uw_OpenidFfi_discovery));
   endpoint ep = {ctx, dy};
   CURLcode code;
 
@@ -120,13 +120,10 @@
   code = curl_easy_perform(c);
   uw_pop_cleanup(ctx);
 
-  if (code || !ep.d->endpoint)
+  if (code || !dy->endpoint)
     return NULL;
-  else {
-    uw_OpenidFfi_discovery *dyp = malloc(sizeof(uw_OpenidFfi_discovery));
-    *dyp = ep.d;
-    return dyp;
-  }
+  else
+    return dy;
 }
 
 uw_OpenidFfi_inputs uw_OpenidFfi_createInputs(uw_context ctx) {
@@ -135,25 +132,28 @@
   return r;
 }
 
-static int okForPost(const char *s) {
-  for (; *s; ++s)
-    if (*s == '=' || *s == '&')
-      return 0;
-  return 1;
+static void postify(uw_OpenidFfi_inputs buf, uw_Basis_string s) {
+  for (; *s; ++s) {
+    switch (*s) {
+    case '=':
+      uw_buffer_append(buf, "%3D", 3);
+      break;
+    case '&':
+      uw_buffer_append(buf, "%26", 3);
+      break;
+    default:
+      uw_buffer_append(buf, s, 1);
+    }
+  }
 }
 
 uw_unit uw_OpenidFfi_addInput(uw_context ctx, uw_OpenidFfi_inputs buf, uw_Basis_string key, uw_Basis_string value) {
-  if (!okForPost(key))
-    uw_error(ctx, FATAL, "Invalid key for OpenID inputs");
-  if (!okForPost(value))
-    uw_error(ctx, FATAL, "Invalid value for OpenID inputs");
-
   if (uw_buffer_used(buf) > 0)
     uw_buffer_append(buf, "&", 1);
 
-  uw_buffer_append(buf, key, strlen(key));
+  postify(buf, key);
   uw_buffer_append(buf, "=", 1);
-  uw_buffer_append(buf, value, strlen(value));
+  postify(buf, value);
 
   return uw_unit_v;
 }
@@ -202,6 +202,8 @@
   } else {
     char *s;
 
+    printf("Result: %s\n", buf->start);
+
     s = buf->start;
     while (*s) {
       char *colon = strchr(s, ':'), *newline;
@@ -297,17 +299,30 @@
   return buff;
 }
 
-static void unbase64(unsigned char *input, int length, unsigned char *buffer, int bufferLength)
+static int unbase64(unsigned char *input, int length, unsigned char *buffer, int bufferLength)
 {
   BIO *b64, *bmem;
+  int n;
 
   b64 = BIO_new(BIO_f_base64());
   BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
   bmem = BIO_new_mem_buf(input, length);
   BIO_push(b64, bmem);
-  BIO_read(b64, buffer, bufferLength);
+  n = BIO_read(b64, buffer, bufferLength);
 
   BIO_free_all(bmem);
+
+  return n;
+}
+
+uw_Basis_string uw_OpenidFfi_sha1(uw_context ctx, uw_Basis_string key, uw_Basis_string data) {
+  unsigned char keyBin[SHA_DIGEST_LENGTH], out[EVP_MAX_MD_SIZE];
+  unsigned outLen;
+
+  unbase64((unsigned char *)key, strlen(key), keyBin, sizeof keyBin);
+
+  HMAC(EVP_sha1(), keyBin, sizeof keyBin, (unsigned char *)data, strlen(data), out, &outLen);
+  return base64(ctx, out, outLen);
 }
 
 uw_Basis_string uw_OpenidFfi_sha256(uw_context ctx, uw_Basis_string key, uw_Basis_string data) {
@@ -315,8 +330,81 @@
   unsigned outLen;
 
   unbase64((unsigned char *)key, strlen(key), keyBin, sizeof keyBin);
-  memset(key, sizeof key, 0);
 
   HMAC(EVP_sha256(), keyBin, sizeof keyBin, (unsigned char *)data, strlen(data), out, &outLen);
   return base64(ctx, out, outLen);
 }
+
+static uw_Basis_string btwoc(uw_context ctx, const BIGNUM *n) {
+  int len = BN_num_bytes(n), i;
+  unsigned char bytes[len+1];
+
+  bytes[0] = 0;
+  BN_bn2bin(n, bytes+1);
+
+  for (i = 1; i <= len; ++i)
+    if (bytes[i]) {
+      if (bytes[i] & 0x80)
+        --i;
+      break;
+    }
+
+  if (i > len)
+    i = len;
+
+  return base64(ctx, bytes+i, len+1-i);
+}
+
+static BIGNUM *unbtwoc(uw_context ctx, uw_Basis_string s) {
+  unsigned char bytes[1024];
+  int len;
+
+  len = unbase64((unsigned char *)s, strlen(s), bytes, sizeof bytes);
+  return BN_bin2bn(bytes, len, NULL);
+}
+
+uw_Basis_string uw_OpenidFfi_modulus(uw_context ctx, uw_OpenidFfi_dh dh) {
+  return btwoc(ctx, dh->p);
+}
+
+uw_Basis_string uw_OpenidFfi_generator(uw_context ctx, uw_OpenidFfi_dh dh) {
+  return btwoc(ctx, dh->g);
+}
+
+uw_Basis_string uw_OpenidFfi_public(uw_context ctx, uw_OpenidFfi_dh dh) {
+  return btwoc(ctx, dh->pub_key);
+}
+
+static void free_DH(void *data, int will_retry) {
+  DH *dh = data;
+  DH_free(dh);
+}
+
+uw_OpenidFfi_dh uw_OpenidFfi_generate(uw_context ctx) {
+  DH *dh = DH_new();
+
+  uw_register_transactional(ctx, dh, NULL, NULL, free_DH);
+
+  DH_generate_parameters_ex(dh, PRIME_LEN, GENERATOR, NULL);
+
+  if (DH_generate_key(dh) != 1)
+    uw_error(ctx, FATAL, "Diffie-Hellman key generation failed");
+
+  return dh;
+}
+
+uw_Basis_string uw_OpenidFfi_compute(uw_context ctx, uw_OpenidFfi_dh dh, uw_Basis_string server_pub) {
+  BIGNUM *bn = unbtwoc(ctx, server_pub);
+  unsigned char secret[DH_size(dh)];
+  int size;
+
+  uw_push_cleanup(ctx, (void (*)(void *))BN_free, bn);
+
+  size = DH_compute_key(secret, bn, dh);
+  if (size == -1)
+    uw_error(ctx, FATAL, "Diffie-Hellman key computation failed");
+
+  uw_pop_cleanup(ctx);
+
+  return base64(ctx, secret, size);
+}