1/*
2 * Copyright (c) 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 KTH nor the names of its contributors may be
20 *    used to endorse or promote products derived from this software without
21 *    specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
24 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
30 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
32 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
33 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36
37#include "config.h"
38
39#include <stdio.h>
40#include <err.h>
41#include <roken.h>
42#include <getarg.h>
43#include <base64.h>
44
45#include <heimbase.h>
46
47static int verbose_flag = 0;
48static int version_flag = 0;
49static int help_flag	= 0;
50
51static struct getargs args[] = {
52    {"verbose",	0,	arg_flag,	&verbose_flag, "verbose", NULL },
53    {"version",	0,	arg_flag,	&version_flag, "print version", NULL },
54    {"help",	0,	arg_flag,	&help_flag,  NULL, NULL }
55};
56
57static void
58usage (int ret)
59{
60    arg_printusage (args, sizeof(args)/sizeof(*args),
61		    NULL, "");
62    exit (ret);
63}
64
65#ifdef ENABLE_SCRAM
66
67#include "scram.h"
68
69static int
70test_parser(void)
71{
72#if 0
73    heim_scram_pairs *d;
74    size_t i;
75    int ret;
76    struct {
77	char *str;
78	int ret;
79    } strings[] = {
80	{ "", EINVAL },
81	{ "a", EINVAL },
82	{ "a=bar", 0 },
83	{ "a=", 0 },
84	{ "a=,", EINVAL },
85	{ "a", EINVAL },
86	{ "aa=", EINVAL },
87	{ "a=,b", EINVAL },
88	{ "a=,b=", 0 },
89	{ "a=,b=,", EINVAL },
90	{ "a=", 0 },
91	{ "a=aaa,b=b  bb b  b", 0 },
92	{ "a=aaa,b=b  bb b  b,c=    c  =", 0 },
93	{ "a=a,b=AF,c====", 0 },
94	{ "n,a=a,b=AF,c====", 0 },
95	{ "y,a=a,b=AF,c====", 0 },
96	{ "p=foo,a=a,b=AF,c====", 0 }
97    };
98
99    for (i = 0; i < sizeof(strings)/sizeof(strings[0]); i++) {
100	heim_scram_data data, data2;
101
102	data.data = strings[i].str;
103	data.length = strlen(strings[i].str);
104
105	d = NULL;
106
107	ret = _heim_scram_parse(&data, &d);
108	if (verbose_flag)
109	    printf("%s -> %d\n", strings[i].str, ret);
110	if (ret != strings[i].ret)
111	    return 1;
112	if (ret)
113	    continue;
114
115	ret = _heim_scram_unparse(d, &data2);
116	if (ret)
117	    return ret;
118	if (verbose_flag)
119	    printf("unparse %d %s = %.*s\n", ret,
120		   strings[i].str,
121		   (int)data2.length, (char *)data2.data);
122	if (data.length != data2.length ||
123	    memcmp(data.data, data2.data, data.length) != 0) {
124	    heim_scram_data_free(&data2);
125	    return 1;
126	}
127	heim_scram_data_free(&data2);
128	_heim_scram_pairs_free(d);
129    }
130
131    printf("parse success\n");
132#endif
133    return 0;
134}
135
136static int
137test_storekey(void)
138{
139    const char *pw = "password";
140    const char salt[] = "c2FsdA==";
141    char clientkey[20] = "\xdc\x58\xe3\x8a\xf4\xb5\x54\xc6\x95\x2c\xfe\xc6\xff\xe3\xea\x17\x5f\x44\xb6\x0e";
142    char storekey[20] = "\xbd\x59\xe9\xd0\x58\x50\x66\x64\x11\x48\xcb\xf0\xf6\x8a\xb5\x2c\x53\x02\x87\xc1";
143    char saltinfo[sizeof(salt)];
144    heim_scram_data saltdata, key, client;
145    int ret, len;
146
147    len = base64_decode(salt, saltinfo);
148    if (len < 0)
149	return 1;
150
151    saltdata.data = saltinfo;
152    saltdata.length = len;
153
154    /*
155     * store key
156     */
157
158    ret = heim_scram_stored_key(HEIM_SCRAM_DIGEST_SHA1, pw, 1, &saltdata,
159				&client, &key, NULL);
160    if (ret)
161	return 1;
162
163    if (key.length != sizeof(storekey) ||
164	memcmp(key.data, storekey, sizeof(storekey)) != 0)
165	return 1;
166
167    if (client.length != sizeof(clientkey) ||
168	memcmp(client.data, clientkey, sizeof(clientkey)) != 0)
169	return 1;
170
171    printf("store key success\n");
172
173    heim_scram_data_free(&key);
174
175    heim_scram_data_free(&key);
176
177    return 0;
178}
179
180static unsigned int giterations = 1000;
181static heim_scram_data gsalt = {
182    .data = rk_UNCONST("salt"),
183    .length = 4
184};
185
186static int
187param(void *ctx,
188      const heim_scram_data *user,
189      heim_scram_data *salt,
190      unsigned int *iteration,
191      heim_scram_data *servernonce)
192{
193    if (user->length != 3 && memcmp(user->data, "lha", 3) != 0)
194	return ENOENT;
195
196    *iteration = giterations;
197
198    salt->data = malloc(gsalt.length);
199    memcpy(salt->data, gsalt.data, gsalt.length);
200    salt->length = gsalt.length;
201
202    servernonce->data = NULL;
203    servernonce->length = 0;
204
205    return 0;
206}
207
208static int
209calculate(void *ctx,
210	  heim_scram_method method,
211	  const heim_scram_data *user,
212	  const heim_scram_data *c1,
213	  const heim_scram_data *s1,
214	  const heim_scram_data *c2noproof,
215	  const heim_scram_data *proof,
216	  heim_scram_data *server,
217	  heim_scram_data *sessionKey)
218{
219    heim_scram_data client_key, client_key2, stored_key, server_key, clientSig;
220    int ret;
221
222    memset(&client_key2, 0, sizeof(client_key2));
223
224    ret = heim_scram_stored_key(method,
225				ctx, giterations, &gsalt,
226				&client_key, &stored_key, &server_key);
227    if (ret)
228	return ret;
229
230
231    ret = heim_scram_generate(method, &stored_key, &server_key,
232			      c1, s1, c2noproof, &clientSig, server);
233    heim_scram_data_free(&server_key);
234    if (ret)
235	goto out;
236
237    ret = heim_scram_validate_client_signature(method,
238					       &stored_key,
239					       &clientSig,
240					       proof,
241					       &client_key2);
242    if (ret)
243	goto out;
244
245
246    /* extra check since we know the client key */
247    if (client_key2.length != client_key.length ||
248	memcmp(client_key.data, client_key2.data, client_key.length) != 0) {
249	ret = EINVAL;
250	goto out;
251    }
252
253    ret = heim_scram_session_key(method,
254				 &stored_key,
255				 &client_key,
256				 c1, s1, c2noproof, sessionKey);
257    if (ret)
258	goto out;
259
260 out:
261    heim_scram_data_free(&stored_key);
262    heim_scram_data_free(&client_key);
263    heim_scram_data_free(&client_key2);
264
265    return ret;
266}
267
268static struct heim_scram_server server_procs = {
269    .version = SCRAM_SERVER_VERSION_1,
270    .param = param,
271    .calculate = calculate
272};
273
274static int
275test_exchange(void)
276{
277    int ret;
278    heim_scram *cs = NULL, *ss = NULL;
279    heim_scram_data cp, sp;
280
281    ret = heim_scram_client1("lha", NULL, HEIM_SCRAM_DIGEST_SHA1, &cs, &cp);
282    if (ret)
283	goto out;
284
285    printf("c1: %.*s\n", (int)cp.length, (char *)cp.data);
286
287    ret = heim_scram_server1(&cp, NULL, HEIM_SCRAM_DIGEST_SHA1,
288			     &server_procs, "password", &ss, &sp);
289    if (ret)
290	goto out;
291
292    printf("s1: %.*s\n", (int)sp.length, (char *)sp.data);
293
294    ret = heim_scram_client2(&sp, HEIM_SCRAM_CLIENT_PASSWORD_PROCS, "password", cs, &cp);
295    if (ret)
296	goto out;
297
298    printf("c2: %.*s\n", (int)cp.length, (char *)cp.data);
299
300    ret = heim_scram_server2(&cp, ss, &sp);
301    if (ret)
302	goto out;
303
304    printf("s2: %.*s\n", (int)sp.length, (char *)sp.data);
305
306    ret = heim_scram_client3(&sp, cs);
307    if (ret)
308	goto out;
309
310    printf("exchange success\n");
311
312 out:
313    heim_scram_free(cs);
314    heim_scram_free(ss);
315
316    return ret;
317}
318#endif /* ENABLE_SCRAM */
319
320int
321main(int argc, char **argv)
322{
323    int ret, optidx = 0;
324
325    setprogname(argv[0]);
326
327    if (getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
328	usage(1);
329
330    if (help_flag)
331	usage (0);
332
333    if (version_flag){
334	print_version(NULL);
335	exit(0);
336    }
337
338    ret = 0;
339#ifdef ENABLE_SCRAM
340    ret |= test_parser();
341    ret |= test_storekey();
342    ret |= test_exchange();
343#endif
344
345    return ret;
346}
347