comparison 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
comparison
equal deleted inserted replaced
7:976121190b2d 8:870d99055dd1
3 #include <openssl/bio.h> 3 #include <openssl/bio.h>
4 #include <openssl/evp.h> 4 #include <openssl/evp.h>
5 #include <openssl/buffer.h> 5 #include <openssl/buffer.h>
6 #include <openssl/sha.h> 6 #include <openssl/sha.h>
7 #include <openssl/hmac.h> 7 #include <openssl/hmac.h>
8 #include <openssl/dh.h>
8 #include <curl/curl.h> 9 #include <curl/curl.h>
9 #include <expat.h> 10 #include <expat.h>
10 11
11 #include <openid.h> 12 #include <openid.h>
12 13
13 #define BUF_MAX 10240 14 #define BUF_MAX 10240
14 #define BUF_INIT 1024 15 #define BUF_INIT 1024
15 16
16 struct uw_OpenidFfi_discovery { 17 #define PRIME_LEN 64
17 uw_Basis_string endpoint, localId; 18 #define GENERATOR DH_GENERATOR_5
18 };
19 19
20 uw_Basis_string uw_OpenidFfi_endpoint(uw_context ctx, uw_OpenidFfi_discovery d) { 20 uw_Basis_string uw_OpenidFfi_endpoint(uw_context ctx, uw_OpenidFfi_discovery d) {
21 return d->endpoint; 21 return d.endpoint;
22 } 22 }
23 23
24 uw_Basis_string uw_OpenidFfi_localId(uw_context ctx, uw_OpenidFfi_discovery d) { 24 uw_Basis_string uw_OpenidFfi_localId(uw_context ctx, uw_OpenidFfi_discovery d) {
25 return d->localId; 25 return d.localId;
26 } 26 }
27 27
28 uw_unit uw_OpenidFfi_init(uw_context ctx) { 28 uw_unit uw_OpenidFfi_init(uw_context ctx) {
29 curl_global_init(CURL_GLOBAL_ALL); 29 curl_global_init(CURL_GLOBAL_ALL);
30 30
43 return r; 43 return r;
44 } 44 }
45 45
46 typedef struct { 46 typedef struct {
47 uw_context ctx; 47 uw_context ctx;
48 uw_OpenidFfi_discovery d; 48 uw_OpenidFfi_discovery *d;
49 } endpoint; 49 } endpoint;
50 50
51 static void XMLCALL startElement(void *userData, const XML_Char *name, const XML_Char **atts) { 51 static void XMLCALL startElement(void *userData, const XML_Char *name, const XML_Char **atts) {
52 endpoint *ep = userData; 52 endpoint *ep = userData;
53 53
89 89
90 uw_OpenidFfi_discovery *uw_OpenidFfi_discover(uw_context ctx, uw_Basis_string id) { 90 uw_OpenidFfi_discovery *uw_OpenidFfi_discover(uw_context ctx, uw_Basis_string id) {
91 char *s; 91 char *s;
92 CURL *c = curl(ctx); 92 CURL *c = curl(ctx);
93 curl_discovery_data cd = {}; 93 curl_discovery_data cd = {};
94 uw_OpenidFfi_discovery dy = uw_malloc(ctx, sizeof(struct uw_OpenidFfi_discovery)); 94 uw_OpenidFfi_discovery *dy = uw_malloc(ctx, sizeof(uw_OpenidFfi_discovery));
95 endpoint ep = {ctx, dy}; 95 endpoint ep = {ctx, dy};
96 CURLcode code; 96 CURLcode code;
97 97
98 dy->endpoint = dy->localId = NULL; 98 dy->endpoint = dy->localId = NULL;
99 99
118 curl_easy_setopt(c, CURLOPT_WRITEDATA, &cd); 118 curl_easy_setopt(c, CURLOPT_WRITEDATA, &cd);
119 119
120 code = curl_easy_perform(c); 120 code = curl_easy_perform(c);
121 uw_pop_cleanup(ctx); 121 uw_pop_cleanup(ctx);
122 122
123 if (code || !ep.d->endpoint) 123 if (code || !dy->endpoint)
124 return NULL; 124 return NULL;
125 else { 125 else
126 uw_OpenidFfi_discovery *dyp = malloc(sizeof(uw_OpenidFfi_discovery)); 126 return dy;
127 *dyp = ep.d;
128 return dyp;
129 }
130 } 127 }
131 128
132 uw_OpenidFfi_inputs uw_OpenidFfi_createInputs(uw_context ctx) { 129 uw_OpenidFfi_inputs uw_OpenidFfi_createInputs(uw_context ctx) {
133 uw_buffer *r = uw_malloc(ctx, sizeof(uw_buffer)); 130 uw_buffer *r = uw_malloc(ctx, sizeof(uw_buffer));
134 uw_buffer_init(BUF_MAX, r, BUF_INIT); 131 uw_buffer_init(BUF_MAX, r, BUF_INIT);
135 return r; 132 return r;
136 } 133 }
137 134
138 static int okForPost(const char *s) { 135 static void postify(uw_OpenidFfi_inputs buf, uw_Basis_string s) {
139 for (; *s; ++s) 136 for (; *s; ++s) {
140 if (*s == '=' || *s == '&') 137 switch (*s) {
141 return 0; 138 case '=':
142 return 1; 139 uw_buffer_append(buf, "%3D", 3);
140 break;
141 case '&':
142 uw_buffer_append(buf, "%26", 3);
143 break;
144 default:
145 uw_buffer_append(buf, s, 1);
146 }
147 }
143 } 148 }
144 149
145 uw_unit uw_OpenidFfi_addInput(uw_context ctx, uw_OpenidFfi_inputs buf, uw_Basis_string key, uw_Basis_string value) { 150 uw_unit uw_OpenidFfi_addInput(uw_context ctx, uw_OpenidFfi_inputs buf, uw_Basis_string key, uw_Basis_string value) {
146 if (!okForPost(key))
147 uw_error(ctx, FATAL, "Invalid key for OpenID inputs");
148 if (!okForPost(value))
149 uw_error(ctx, FATAL, "Invalid value for OpenID inputs");
150
151 if (uw_buffer_used(buf) > 0) 151 if (uw_buffer_used(buf) > 0)
152 uw_buffer_append(buf, "&", 1); 152 uw_buffer_append(buf, "&", 1);
153 153
154 uw_buffer_append(buf, key, strlen(key)); 154 postify(buf, key);
155 uw_buffer_append(buf, "=", 1); 155 uw_buffer_append(buf, "=", 1);
156 uw_buffer_append(buf, value, strlen(value)); 156 postify(buf, value);
157 157
158 return uw_unit_v; 158 return uw_unit_v;
159 } 159 }
160 160
161 uw_Basis_string uw_OpenidFfi_getOutput(uw_context ctx, uw_OpenidFfi_outputs buf, uw_Basis_string key) { 161 uw_Basis_string uw_OpenidFfi_getOutput(uw_context ctx, uw_OpenidFfi_outputs buf, uw_Basis_string key) {
199 if (code) { 199 if (code) {
200 uw_buffer_reset(buf); 200 uw_buffer_reset(buf);
201 uw_buffer_append(buf, curl_failure, sizeof curl_failure); 201 uw_buffer_append(buf, curl_failure, sizeof curl_failure);
202 } else { 202 } else {
203 char *s; 203 char *s;
204
205 printf("Result: %s\n", buf->start);
204 206
205 s = buf->start; 207 s = buf->start;
206 while (*s) { 208 while (*s) {
207 char *colon = strchr(s, ':'), *newline; 209 char *colon = strchr(s, ':'), *newline;
208 210
295 BIO_free_all(b64); 297 BIO_free_all(b64);
296 298
297 return buff; 299 return buff;
298 } 300 }
299 301
300 static void unbase64(unsigned char *input, int length, unsigned char *buffer, int bufferLength) 302 static int unbase64(unsigned char *input, int length, unsigned char *buffer, int bufferLength)
301 { 303 {
302 BIO *b64, *bmem; 304 BIO *b64, *bmem;
305 int n;
303 306
304 b64 = BIO_new(BIO_f_base64()); 307 b64 = BIO_new(BIO_f_base64());
305 BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); 308 BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
306 bmem = BIO_new_mem_buf(input, length); 309 bmem = BIO_new_mem_buf(input, length);
307 BIO_push(b64, bmem); 310 BIO_push(b64, bmem);
308 BIO_read(b64, buffer, bufferLength); 311 n = BIO_read(b64, buffer, bufferLength);
309 312
310 BIO_free_all(bmem); 313 BIO_free_all(bmem);
314
315 return n;
316 }
317
318 uw_Basis_string uw_OpenidFfi_sha1(uw_context ctx, uw_Basis_string key, uw_Basis_string data) {
319 unsigned char keyBin[SHA_DIGEST_LENGTH], out[EVP_MAX_MD_SIZE];
320 unsigned outLen;
321
322 unbase64((unsigned char *)key, strlen(key), keyBin, sizeof keyBin);
323
324 HMAC(EVP_sha1(), keyBin, sizeof keyBin, (unsigned char *)data, strlen(data), out, &outLen);
325 return base64(ctx, out, outLen);
311 } 326 }
312 327
313 uw_Basis_string uw_OpenidFfi_sha256(uw_context ctx, uw_Basis_string key, uw_Basis_string data) { 328 uw_Basis_string uw_OpenidFfi_sha256(uw_context ctx, uw_Basis_string key, uw_Basis_string data) {
314 unsigned char keyBin[SHA256_DIGEST_LENGTH], out[EVP_MAX_MD_SIZE]; 329 unsigned char keyBin[SHA256_DIGEST_LENGTH], out[EVP_MAX_MD_SIZE];
315 unsigned outLen; 330 unsigned outLen;
316 331
317 unbase64((unsigned char *)key, strlen(key), keyBin, sizeof keyBin); 332 unbase64((unsigned char *)key, strlen(key), keyBin, sizeof keyBin);
318 memset(key, sizeof key, 0);
319 333
320 HMAC(EVP_sha256(), keyBin, sizeof keyBin, (unsigned char *)data, strlen(data), out, &outLen); 334 HMAC(EVP_sha256(), keyBin, sizeof keyBin, (unsigned char *)data, strlen(data), out, &outLen);
321 return base64(ctx, out, outLen); 335 return base64(ctx, out, outLen);
322 } 336 }
337
338 static uw_Basis_string btwoc(uw_context ctx, const BIGNUM *n) {
339 int len = BN_num_bytes(n), i;
340 unsigned char bytes[len+1];
341
342 bytes[0] = 0;
343 BN_bn2bin(n, bytes+1);
344
345 for (i = 1; i <= len; ++i)
346 if (bytes[i]) {
347 if (bytes[i] & 0x80)
348 --i;
349 break;
350 }
351
352 if (i > len)
353 i = len;
354
355 return base64(ctx, bytes+i, len+1-i);
356 }
357
358 static BIGNUM *unbtwoc(uw_context ctx, uw_Basis_string s) {
359 unsigned char bytes[1024];
360 int len;
361
362 len = unbase64((unsigned char *)s, strlen(s), bytes, sizeof bytes);
363 return BN_bin2bn(bytes, len, NULL);
364 }
365
366 uw_Basis_string uw_OpenidFfi_modulus(uw_context ctx, uw_OpenidFfi_dh dh) {
367 return btwoc(ctx, dh->p);
368 }
369
370 uw_Basis_string uw_OpenidFfi_generator(uw_context ctx, uw_OpenidFfi_dh dh) {
371 return btwoc(ctx, dh->g);
372 }
373
374 uw_Basis_string uw_OpenidFfi_public(uw_context ctx, uw_OpenidFfi_dh dh) {
375 return btwoc(ctx, dh->pub_key);
376 }
377
378 static void free_DH(void *data, int will_retry) {
379 DH *dh = data;
380 DH_free(dh);
381 }
382
383 uw_OpenidFfi_dh uw_OpenidFfi_generate(uw_context ctx) {
384 DH *dh = DH_new();
385
386 uw_register_transactional(ctx, dh, NULL, NULL, free_DH);
387
388 DH_generate_parameters_ex(dh, PRIME_LEN, GENERATOR, NULL);
389
390 if (DH_generate_key(dh) != 1)
391 uw_error(ctx, FATAL, "Diffie-Hellman key generation failed");
392
393 return dh;
394 }
395
396 uw_Basis_string uw_OpenidFfi_compute(uw_context ctx, uw_OpenidFfi_dh dh, uw_Basis_string server_pub) {
397 BIGNUM *bn = unbtwoc(ctx, server_pub);
398 unsigned char secret[DH_size(dh)];
399 int size;
400
401 uw_push_cleanup(ctx, (void (*)(void *))BN_free, bn);
402
403 size = DH_compute_key(secret, bn, dh);
404 if (size == -1)
405 uw_error(ctx, FATAL, "Diffie-Hellman key computation failed");
406
407 uw_pop_cleanup(ctx);
408
409 return base64(ctx, secret, size);
410 }