1/*
2 * Copyright (c) 2006 - 2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include <stdio.h>
37#include <stdlib.h>
38#include <unistd.h>
39#include <roken.h>
40#include <err.h>
41#include "heim-auth.h"
42
43static int
44test_sasl_digest_md5(void)
45{
46    heim_digest_t ctx;
47    const char *user, *challenge, *resp;
48    char *r;
49
50    if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_AUTO)) == NULL)
51	abort();
52
53    if (heim_digest_parse_challenge(ctx, "realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",qop=\"auth\",algorithm=md5-sess,charset=utf-8"))
54	abort();
55
56    /* check that server detects changing QOP */
57    if (!heim_digest_parse_response(ctx, "charset=utf-8,username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",nc=00000001,cnonce=\"OA6MHXh6VqTrRk\",digest-uri=\"imap/elwood.innosoft.com\",response=d388dad90d4bbd760a152321f2143af7,qop=auth-int"))
58	errx(1, "don't detect changing qop");
59
60    /* should pass */
61    if (heim_digest_parse_response(ctx, "charset=utf-8,username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",nc=00000001,cnonce=\"OA6MHXh6VqTrRk\",digest-uri=\"imap/elwood.innosoft.com\",response=d388dad90d4bbd760a152321f2143af7,qop=auth"))
62	abort();
63
64    if ((user = heim_digest_get_key(ctx, "username")) == NULL)
65	abort();
66    if (strcmp(user, "chris") != 0)
67	abort();
68
69    /*
70     * check password
71     */
72
73    heim_digest_set_key(ctx, "password", "secret");
74
75    if (heim_digest_verify(ctx, &r))
76	abort();
77
78    if (strcmp(r, "rspauth=ea40f60335c427b5527b84dbabcdfffd") != 0)
79	abort();
80
81    free(r);
82
83    /*
84     * Also check userhash
85     */
86
87    r = heim_digest_userhash("chris", "elwood.innosoft.com", "secret");
88    if (strcmp(r, "eb5a750053e4d2c34aa84bbc9b0b6ee7") != 0)
89	abort();
90
91    heim_digest_set_key(ctx, "userhash", r);
92    free(r);
93
94    if (heim_digest_verify(ctx, &r))
95	abort();
96
97    if (strcmp(r, "rspauth=ea40f60335c427b5527b84dbabcdfffd") != 0)
98	abort();
99
100    free(r);
101
102    /* check that it failes */
103
104    heim_digest_set_key(ctx, "username", "notright");
105    heim_digest_set_key(ctx, "password", "secret");
106
107    if (heim_digest_verify(ctx, &r) == 0)
108	abort();
109
110    if ((user = heim_digest_get_key(ctx, "username")) == NULL)
111	abort();
112    if (strcmp(user, "notright") != 0)
113	abort();
114
115
116    /* Done */
117
118    heim_digest_release(ctx);
119
120
121    /*
122     * Check heim_digest_generate_challenge()
123     */
124
125    if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_RFC2831)) == NULL)
126	abort();
127
128    heim_digest_set_key(ctx, "serverRealm", "elwood.innosoft.com");
129    heim_digest_set_key(ctx, "serverNonce", "OA6MG9tEQGm2hh");
130    heim_digest_set_key(ctx, "serverQOP", "auth,auth-int");
131
132    challenge = heim_digest_generate_challenge(ctx);
133    if (challenge == NULL)
134	abort();
135
136    if (heim_digest_parse_challenge(ctx, challenge))
137	abort();
138
139    /* check that server detects changing QOP */
140    if (!heim_digest_parse_response(ctx, "charset=utf-8,username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",nc=00000001,cnonce=\"OA6MHXh6VqTrRk\",digest-uri=\"imap/elwood.innosoft.com\",response=d388dad90d4bbd760a152321f2143af7,qop=auth-conf"))
141	abort();
142
143    if (heim_digest_parse_response(ctx, "charset=utf-8,username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",nc=00000001,cnonce=\"OA6MHXh6VqTrRk\",digest-uri=\"imap/elwood.innosoft.com\",response=d388dad90d4bbd760a152321f2143af7,qop=auth"))
144	abort();
145
146    heim_digest_set_key(ctx, "password", "secret");
147
148    if (heim_digest_verify(ctx, &r))
149	abort();
150
151    if (strcmp(r, "rspauth=ea40f60335c427b5527b84dbabcdfffd") != 0)
152	abort();
153
154    free(r);
155
156    heim_digest_release(ctx);
157
158    /*
159     * Validate heim_digest_service_response()
160     */
161
162    if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_RFC2831)) == NULL)
163	abort();
164
165    heim_digest_set_key(ctx, "clientNonce", "OA6MHXh6VqTrRk");
166    heim_digest_set_key(ctx, "clientQOP", "auth");
167    heim_digest_set_key(ctx, "clientNC", "00000001");
168    heim_digest_set_key(ctx, "clientURI", "imap/elwood.innosoft.com");
169    heim_digest_set_key(ctx, "serverRealm", "elwood.innosoft.com");
170    heim_digest_set_key(ctx, "serverNonce", "OA6MG9tEQGm2hh");
171    heim_digest_set_key(ctx, "userhash", "eb5a750053e4d2c34aa84bbc9b0b6ee7");
172
173    resp = heim_digest_server_response(ctx);
174
175    if (resp == NULL || strcmp(resp, "rspauth=ea40f60335c427b5527b84dbabcdfffd") != 0)
176	abort();
177
178    heim_digest_release(ctx);
179
180    if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_RFC2831)) == NULL)
181	abort();
182
183    heim_digest_set_key(ctx, "clientNonce", "OA6MHXh6VqTrRk");
184    heim_digest_set_key(ctx, "clientQOP", "auth");
185    heim_digest_set_key(ctx, "clientNC", "00000001");
186    heim_digest_set_key(ctx, "clientURI", "imap/elwood.innosoft.com");
187    heim_digest_set_key(ctx, "serverRealm", "elwood.innosoft.com");
188    heim_digest_set_key(ctx, "serverNonce", "OA6MG9tEQGm2hh");
189    heim_digest_set_key(ctx, "password", "secret");
190    heim_digest_set_key(ctx, "username", "chris");
191
192    resp = heim_digest_server_response(ctx);
193
194    if (resp == NULL || strcmp(resp, "rspauth=ea40f60335c427b5527b84dbabcdfffd") != 0)
195	abort();
196
197    heim_digest_release(ctx);
198
199    return 0;
200}
201
202static int
203test_http_digest_md5(void)
204{
205    heim_digest_t ctx, ctx2;
206    const char *user, *chal, *resp;
207    char *serverresp, *serverresp2;
208
209    if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_AUTO)) == NULL)
210	abort();
211
212    if (heim_digest_parse_challenge(ctx, "realm=\"testrealm@host.com\","
213				    "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\","
214				    "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""))
215	abort();
216
217    if (heim_digest_parse_response(ctx, "username=\"Mufasa\","
218				   "realm=\"testrealm@host.com\","
219				   "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\","
220				   "uri=\"/dir/index.html\","
221				   "response=\"1949323746fe6a43ef61f9606e7febea\","
222				   "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""))
223	abort();
224
225    if ((user = heim_digest_get_key(ctx, "username")) == NULL)
226	abort();
227    if (strcmp(user, "Mufasa") != 0)
228	abort();
229
230    if ((user = heim_digest_get_key(ctx, "clientUsername")) == NULL)
231	abort();
232    if (strcmp(user, "Mufasa") != 0)
233	abort();
234
235    heim_digest_set_key(ctx, "password", "CircleOfLife");
236
237    if (heim_digest_verify(ctx, NULL))
238	abort();
239
240    /* Verify failure */
241
242    heim_digest_set_key(ctx, "username", "Oskar");
243
244    if (heim_digest_verify(ctx, NULL) == 0)
245	abort();
246
247    heim_digest_release(ctx);
248
249    /*
250     * Check myself
251     */
252
253    /* server */
254    if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_RFC2831)) == NULL)
255	abort();
256
257    heim_digest_set_key(ctx, "serverRealm", "myrealmhahaha");
258    heim_digest_set_key(ctx, "serverQOP", "auth,auth-int");
259
260    chal = heim_digest_generate_challenge(ctx);
261    if (chal == NULL)
262	abort();
263
264    /* client */
265    if ((ctx2 = heim_digest_create(1, HEIM_DIGEST_TYPE_RFC2831)) == NULL)
266	abort();
267
268    if (heim_digest_parse_challenge(ctx2, chal))
269	abort();
270
271    heim_digest_set_key(ctx2, "username", "lha");
272    heim_digest_set_key(ctx2, "password", "passw0rd");
273    heim_digest_set_key(ctx2, "uri", "/uri");
274
275    resp = heim_digest_create_response(ctx2, &serverresp);
276    if (resp == NULL)
277	abort();
278
279    /* server */
280    if (heim_digest_parse_response(ctx, resp))
281	abort();
282
283    heim_digest_set_key(ctx,  "password", "passw0rd");
284    heim_digest_verify(ctx, &serverresp2);
285
286
287    /* client */
288    if (strcmp(serverresp, serverresp2) != 0)
289	abort();
290
291    heim_digest_release(ctx);
292    heim_digest_release(ctx2);
293
294    /*
295     * check prefix
296     */
297
298    if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_AUTO)) == NULL)
299	abort();
300
301    if (heim_digest_parse_challenge(ctx, "Digest realm=\"testrealm@host.com\","
302				    "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\","
303				    "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""))
304	abort();
305
306    heim_digest_release(ctx);
307
308    /*
309     * check prefix
310     */
311
312    if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_AUTO)) == NULL)
313	abort();
314
315    if (heim_digest_parse_challenge(ctx, "Digest  realm=\"testrealm@host.com\","
316				    "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\","
317				    "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""))
318	abort();
319
320    heim_digest_release(ctx);
321
322    /*
323     * Check md5-sess
324     */
325
326    /* server */
327    if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_MD5_SESS)) == NULL)
328	abort();
329
330    heim_digest_set_key(ctx, "serverRealm", "myrealmhahaha");
331
332    chal = heim_digest_generate_challenge(ctx);
333    if (chal == NULL)
334	abort();
335
336    /* client */
337    if ((ctx2 = heim_digest_create(1, HEIM_DIGEST_TYPE_RFC2617_MD5_SESS)) == NULL)
338	abort();
339
340    if (heim_digest_parse_challenge(ctx2, chal))
341	abort();
342
343    heim_digest_set_key(ctx2, "username", "lha");
344    heim_digest_set_key(ctx2, "password", "passw0rd");
345    heim_digest_set_key(ctx2, "uri", "/uri");
346
347    resp = heim_digest_create_response(ctx2, &serverresp);
348    if (resp == NULL)
349	abort();
350
351    /* server */
352    if (heim_digest_parse_response(ctx, resp))
353	abort();
354
355    heim_digest_set_key(ctx,  "password", "passw0rd");
356    heim_digest_verify(ctx, &serverresp2);
357
358    /* client */
359    if (strcmp(serverresp, serverresp2) != 0)
360	abort();
361
362    heim_digest_release(ctx);
363    heim_digest_release(ctx2);
364
365
366    return 0;
367}
368
369static int
370test_cram_md5(void)
371{
372    const char *chal = "<1896.697170952@postoffice.reston.mci.net>";
373    const char *secret = "tanstaaftanstaaf";
374    const char *resp = "b913a602c7eda7a495b4e6e7334d3890";
375    heim_CRAM_MD5_STATE state;
376    heim_cram_md5 ctx;
377    char *t;
378
379    const uint8_t *prestate = (uint8_t *)
380	"\x87\x1E\x24\x10\xB4\x0C\x72\x5D\xA3\x95\x2D\x5B\x8B\xFC\xDD\xE1"
381	"\x29\x90\xCB\xA7\x66\xF6\xB3\x40\xE8\xAC\x48\x2C\xE4\xE3\xA4\x40";
382
383    /*
384     * Test prebuild blobs
385     */
386
387    assert(sizeof(state) == 32);
388
389    heim_cram_md5_export("foo", &state);
390
391    if (memcmp(prestate, &state, 32) != 0)
392	abort();
393
394    /*
395     * Check example
396     */
397
398
399    if (heim_cram_md5_verify(chal, secret, resp) != 0)
400	abort();
401
402
403    /*
404     * Do it ourself
405     */
406
407    t = heim_cram_md5_create(chal, secret);
408    if (t == NULL)
409	abort();
410
411    if (strcmp(resp, t) != 0)
412	abort();
413
414    heim_cram_md5_export(secret, &state);
415    /* here you can store the memcpy-ed version of state somewhere else */
416
417    ctx = heim_cram_md5_import(&state, sizeof(state));
418
419    memset(&state, 0, sizeof(state));
420
421    if (heim_cram_md5_verify_ctx(ctx, chal, resp) != 0)
422	abort();
423
424    heim_cram_md5_free(ctx);
425
426    free(t);
427
428    return 0;
429}
430
431static int
432test_apop(void)
433{
434    const char *chal = "<1896.697170952@dbc.mtview.ca.us>";
435    const char *secret = "tanstaaf";
436    const char *resp = "c4c9334bac560ecc979e58001b3e22fb";
437    char *t;
438
439    t = heim_apop_create(chal, secret);
440    if (t == NULL)
441	abort();
442
443    if (strcmp(resp, t) != 0)
444	abort();
445
446    if (heim_apop_verify(chal, secret, resp) != 0)
447	abort();
448
449    free(t);
450
451    return 0;
452}
453
454
455int
456main(int argc, char **argv)
457{
458    int ret = 0;
459
460    ret |= test_sasl_digest_md5();
461    ret |= test_http_digest_md5();
462    ret |= test_cram_md5();
463    ret |= test_apop();
464
465    return ret;
466}
467