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) 2009 - 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#include "scram.h"
37
38#ifdef ENABLE_SCRAM
39
40static void
41scram_data_zero(heim_scram_data *data)
42{
43    if (data) {
44	data->data = NULL;
45	data->length = 0;
46    }
47}
48
49void
50heim_scram_data_free(heim_scram_data *data)
51{
52    free(data->data);
53    scram_data_zero(data);
54}
55
56static void
57scram_data_alloc(heim_scram_data *to, size_t length)
58{
59    to->length = length;
60    to->data = malloc(to->length);
61    heim_assert(to->data != NULL, "out of memory");
62}
63
64static void
65scram_data_copy(heim_scram_data *to, void *data, size_t length)
66{
67    scram_data_alloc(to, length);
68    memcpy(to->data, data, length);
69}
70
71
72static heim_scram_pairs *
73scram_pairs_new(void)
74{
75    heim_scram_pairs *d;
76    d = calloc(1, sizeof(*d));
77    if (d == NULL)
78	return NULL;
79    d->flags = SCRAM_ARRAY_ALLOCATED|SCRAM_PAIR_ALLOCATED;
80    return d;
81}
82
83void
84_heim_scram_pairs_free(heim_scram_pairs *d)
85{
86    if (d == NULL)
87	return;
88
89    if (d->flags & SCRAM_PAIR_ALLOCATED) {
90	size_t i;
91	for (i = 0; i < d->len; i++)
92	    free(d->val[i].data.data);
93    }
94    if (d->flags & SCRAM_ARRAY_ALLOCATED)
95	free(d->val);
96    free(d);
97}
98
99
100static heim_scram_data *
101scram_find_type(heim_scram_pairs *d, char type)
102{
103    size_t i;
104    for (i = 0; i < d->len; i++)
105	if (d->val[i].type == type)
106	    return &d->val[i].data;
107    return NULL;
108}
109
110static int
111scram_add_type(heim_scram_pairs *d, char type, heim_scram_data *data)
112{
113    void *ptr;
114
115    if ((d->flags & (SCRAM_ARRAY_ALLOCATED|SCRAM_PAIR_ALLOCATED)) != (SCRAM_ARRAY_ALLOCATED|SCRAM_PAIR_ALLOCATED))
116	return EINVAL;
117
118    ptr = realloc(d->val, (d->len + 1) * sizeof(d->val[0]));
119    if (ptr == NULL)
120	return EINVAL;
121    d->val = ptr;
122
123    d->val[d->len].type = type;
124    scram_data_copy(&d->val[d->len].data, data->data, data->length);
125
126    d->len++;
127
128    return 0;
129}
130
131static int
132scram_add_string(heim_scram_pairs *d, char type, const char *str)
133{
134    heim_scram_data data;
135    data.data = rk_UNCONST(str);
136    data.length = strlen(str);
137    return scram_add_type(d, type, &data);
138}
139
140static int
141scram_add_base64(heim_scram_pairs *d, char type, heim_scram_data *data)
142{
143    char *str;
144    int ret;
145
146    if (base64_encode(data->data, (int)data->length, &str) < 0)
147	return ENOMEM;
148
149    ret = scram_add_string(d, type, str);
150    free(str);
151
152    return ret;
153}
154
155struct heim_scram_method_desc {
156    CCDigestAlg dalg;
157	CCHmacAlgorithm halg;
158	size_t halglength;
159    CCPseudoRandomAlgorithm alg;
160    size_t length;
161};
162
163struct heim_scram_method_desc heim_scram_digest_sha1_s = {
164    kCCDigestSHA1,
165	kCCHmacAlgSHA1,
166	CC_SHA1_DIGEST_LENGTH,
167    kCCPRFHmacAlgSHA1,
168    CC_SHA1_DIGEST_LENGTH
169};
170
171struct heim_scram_method_desc heim_scram_digest_sha256_s = {
172    kCCDigestSHA256,
173	kCCHmacAlgSHA256,
174	CC_SHA256_DIGEST_LENGTH,
175    kCCPRFHmacAlgSHA256,
176    CC_SHA256_DIGEST_LENGTH
177};
178
179int
180_heim_scram_parse(heim_scram_data *data, heim_scram_pairs **pd)
181{
182    size_t i, n, start;
183    unsigned char *p;
184    heim_scram_pairs *d;
185
186    *pd = NULL;
187
188    d = scram_pairs_new();
189
190    d->flags &= ~SCRAM_PAIR_ALLOCATED;
191
192    if (data->length < 2)
193	return EINVAL;
194
195    p = data->data;
196
197    if (memcmp(p, "n,", 2) == 0) {
198	d->flags |= SCRAM_BINDINGS_NO;
199	start = 2;
200    } else if (memcmp(p, "y,", 2) == 0) {
201	d->flags |= SCRAM_BINDINGS_YES;
202	start = 2;
203    } else
204	start = 0;
205
206    /* count the , */
207    for (n = 1, i = start; i < data->length; i++)
208	if (p[i] == ',')
209	    n++;
210
211    d->val = calloc(n, sizeof(d->val[0]));
212    if (d->val == NULL)
213	return ENOMEM;
214    d->len = n;
215
216    /* now parse up the arguments */
217    i = start;
218    n = 0;
219    while (n < d->len && i < data->length) {
220	size_t m;
221
222	if (i > data->length - 2)
223	    goto bad;
224	d->val[n].type = p[i];
225	if (p[i + 1] != '=')
226	    goto bad;
227	i += 2;
228	d->val[n].data.data = &p[i];
229	m = i;
230	while (i < data->length && p[i] != ',')
231	    i++;
232	d->val[n].data.length = i - m;
233	n++;
234	i++; /* skip over , */
235    }
236
237    *pd = d;
238
239    return 0;
240 bad:
241    _heim_scram_pairs_free(d);
242    return EINVAL;
243}
244
245static int
246remove_proof(heim_scram_data *in, heim_scram_data *out)
247{
248    unsigned char *p;
249    size_t i;
250
251    if (in->length < 3)
252	return HSCRAM_ERR_INVALID_PROOF;
253
254    p = in->data;
255    for (i = in->length - 1; i > 0; i--)
256	if (p[i] == ',')
257	    break;
258    if (i == 0)
259	return HSCRAM_ERR_INVALID_PROOF;
260    if (i + 3 > in->length)
261	return HSCRAM_ERR_INVALID_PROOF;
262    if (p[i + 1] != 'p')
263	return HSCRAM_ERR_INVALID_PROOF;
264    if (p[i + 2] != '=')
265	return HSCRAM_ERR_INVALID_PROOF;
266
267    out->length = i;
268    out->data = p;
269
270    return 0;
271}
272
273int
274_heim_scram_unparse(heim_scram_pairs *d, heim_scram_data *out)
275{
276    size_t i, len;
277    unsigned char *p;
278
279    heim_assert(d->len != 0, "no key pairs");
280
281    len = d->len * 3 - 1; /* t=, */
282
283    if (d->flags & (SCRAM_BINDINGS_YES|SCRAM_BINDINGS_NO))
284	len += 2;
285
286    for (i = 0; i < d->len; i++)
287	len += d->val[i].data.length;
288
289    scram_data_alloc(out, len);
290    p = out->data;
291
292    if (d->flags & SCRAM_BINDINGS_YES) {
293	memcpy(p, "y,", 2);
294	p += 2;
295    } else if (d->flags & SCRAM_BINDINGS_NO) {
296	memcpy(p, "n,", 2);
297	p += 2;
298    }
299
300    for (i = 0; i < d->len; i++) {
301	*p++ = d->val[i].type;
302	*p++ = '=';
303	memcpy(p, d->val[i].data.data, d->val[i].data.length);
304	p += d->val[i].data.length;
305	if (i + 1 < d->len)
306	    *p++ = ',';
307    }
308    heim_assert((size_t)((p - (unsigned char *)out->data)) == out->length, "generated packet wrong length");
309    return 0;
310}
311
312#define TOPTIONAL 0x100
313
314static const int client_first[] =
315    { 'p' | TOPTIONAL, 'm' | TOPTIONAL, 'n', 'r', 0 };
316static const int server_first[] =
317    { 'm' | TOPTIONAL, 'r', 's', 'i', 0 };
318static const int client_final[] =
319    { 'c', 'r', 'p', 0 };
320static const int server_final[] =
321    { 'v', 0 };
322
323static int
324_scram_validate(heim_scram_pairs *d, const int *template)
325{
326    size_t i = 0;
327    int same;
328    while(*template) {
329	same = (*template & 0xff) == d->val[i].type;
330	if (!same && (*template & TOPTIONAL) == 0)
331	    return HSCRAM_ERR_INVALID_MESSAGE;
332	else if (same)
333	    i++;
334	template++;
335    }
336    return 0;
337}
338
339static int
340scram_authmessage_signature(heim_scram_method method,
341			    const heim_scram_data *key,
342			    const heim_scram_data *c1,
343			    const heim_scram_data *s1,
344			    const heim_scram_data *c2noproof,
345			    const heim_scram_data *clientKey,
346			    heim_scram_data *sig)
347{
348    CCHmacContext hmac;
349
350    CCHmacInit(&hmac, method->halg, key->data, key->length);
351
352    /* only for session key generation */
353    if (clientKey) {
354	CCHmacUpdate(&hmac, "GSS-API session key", 19);
355	CCHmacUpdate(&hmac, clientKey->data, clientKey->length);
356    }
357
358    /* Build AuthMessage */
359    CCHmacUpdate(&hmac, c1->data, c1->length);
360    CCHmacUpdate(&hmac, (const void *)",", 1);
361    CCHmacUpdate(&hmac, s1->data, s1->length);
362    CCHmacUpdate(&hmac, (const void *)",", 1);
363    CCHmacUpdate(&hmac, c2noproof->data, c2noproof->length);
364
365    scram_data_alloc(sig, method->halglength);
366
367    CCHmacFinal(&hmac, sig->data);
368    memset(&hmac, 0, sizeof(hmac));
369
370    return 0;
371}
372
373/* generate "printable" nonce */
374
375static void
376generate_nonce(size_t len, heim_scram_data *result)
377{
378    unsigned char *p;
379    char *str;
380
381    p = malloc(len);
382    heim_assert(p != NULL, "out of memory");
383    if (CCRandomCopyBytes(kCCRandomDefault, p, len) != 0)
384	heim_abort("CCRandomCopyBytes failes");
385
386    if (base64_encode(p, (int)len, &str) < 0)
387	heim_abort("base64 encode failed");
388
389    free(p);
390
391    result->data = str;
392    result->length = strlen(str);
393}
394
395int
396heim_scram_client1(const char *username,
397		   heim_scram_data *ch,
398		   heim_scram_method method,
399		   heim_scram **scram,
400		   heim_scram_data *out)
401{
402    heim_scram_pairs *msg;
403    heim_scram *s;
404    int ret;
405
406    scram_data_zero(out);
407    *scram = NULL;
408
409    s = calloc(1, sizeof(*s));
410    if (s == NULL)
411	return ENOMEM;
412
413    s->type = CLIENT;
414    s->method = method;
415
416    generate_nonce(12, &s->nonce);
417
418    msg = scram_pairs_new();
419
420    if (ch == NULL)
421	msg->flags |= SCRAM_BINDINGS_NO;
422
423    ret = scram_add_string(msg, 'n', username);
424    if (ret) {
425	_heim_scram_pairs_free(msg);
426	heim_scram_free(s);
427	return ret;
428    }
429
430    ret = scram_add_type(msg, 'r', &s->nonce);
431    if (ret) {
432	_heim_scram_pairs_free(msg);
433	heim_scram_free(s);
434	return ret;
435    }
436
437    ret = _heim_scram_unparse(msg, &s->client1);
438    _heim_scram_pairs_free(msg);
439    if (ret) {
440	heim_scram_free(s);
441	return ret;
442    }
443
444    *out = s->client1;
445    *scram = s;
446
447    return 0;
448}
449
450int
451heim_scram_server1(heim_scram_data *in,
452		   heim_scram_data *ch,
453		   heim_scram_method method,
454		   struct heim_scram_server *server,
455		   void *ctx,
456		   heim_scram **scram,
457		   heim_scram_data *out)
458{
459    heim_scram_data *user, *clientnonce;
460    heim_scram *s;
461    heim_scram_pairs *p = NULL, *q = NULL;
462    heim_scram_data salt, servernonce;
463    unsigned int iteration;
464    char iter[12];
465    int ret;
466
467    memset(&p, 0, sizeof(p));
468
469    scram_data_zero(out);
470    scram_data_zero(&salt);
471    scram_data_zero(&servernonce);
472
473    *scram = NULL;
474
475    ret = _heim_scram_parse(in, &p);
476    if (ret)
477	return ret;
478
479    ret = _scram_validate(p, client_first);
480    if (ret) {
481	_heim_scram_pairs_free(p);
482	return ret;
483    }
484
485    s = calloc(1, sizeof(*s));
486    if (s == NULL)
487	goto out;
488
489    s->type = SERVER;
490    s->server = server;
491    s->ctx = ctx;
492    s->method = method;
493
494    scram_data_copy(&s->client1, in->data, in->length);
495
496    user = scram_find_type(p, 'n');
497    clientnonce = scram_find_type(p, 'r');
498
499    heim_assert(clientnonce != NULL && user != NULL, "validate doesn't work");
500
501    scram_data_copy(&s->user, user->data, user->length);
502
503    ret = (s->server->param)(s->ctx, &s->user, &salt,
504			     &iteration, &servernonce);
505    if (ret)
506	goto out;
507
508    /*
509     * If ->param didn't generate nonce, let do it ourself
510     */
511
512    if (servernonce.length == 0)
513	generate_nonce(12, &servernonce);
514
515    s->nonce.length = clientnonce->length + servernonce.length;
516    s->nonce.data = malloc(s->nonce.length);
517
518    memcpy(s->nonce.data, clientnonce->data, clientnonce->length);
519    memcpy(((unsigned char *)s->nonce.data) + clientnonce->length,
520	   servernonce.data, servernonce.length);
521
522    q = scram_pairs_new();
523
524    ret = scram_add_type(q, 'r', &s->nonce);
525    if (ret)
526	goto out;
527
528    ret = scram_add_type(q, 's', &salt);
529    if (ret)
530	goto out;
531
532    snprintf(iter, sizeof(iter), "%lu", (unsigned long)iteration);
533    ret = scram_add_string(q, 'i', iter);
534    if (ret)
535	goto out;
536
537    ret = _heim_scram_unparse(q, &s->server1);
538    if (ret)
539	goto out;
540
541    *out = s->server1;
542    *scram = s;
543
544 out:
545    if (ret)
546	heim_scram_free(s);
547    _heim_scram_pairs_free(p);
548    _heim_scram_pairs_free(q);
549    heim_scram_data_free(&salt);
550    heim_scram_data_free(&servernonce);
551
552    return ret;
553}
554
555int
556heim_scram_generate(heim_scram_method method,
557		    const heim_scram_data *stored_key,
558		    const heim_scram_data *server_key,
559		    const heim_scram_data *c1,
560		    const heim_scram_data *s1,
561		    const heim_scram_data *c2noproof,
562		    heim_scram_data *clientSig,
563		    heim_scram_data *serverSig)
564{
565    int ret;
566
567    scram_data_zero(clientSig);
568    scram_data_zero(serverSig);
569
570    ret = scram_authmessage_signature(method, stored_key,
571				      c1, s1, c2noproof, NULL, clientSig);
572    if (ret)
573	return ret;
574
575    ret = scram_authmessage_signature(method, server_key,
576				      c1, s1, c2noproof, NULL, serverSig);
577    if (ret)
578	heim_scram_data_free(clientSig);
579
580    return ret;
581}
582
583int
584heim_scram_session_key(heim_scram_method method,
585		       const heim_scram_data *stored_key,
586		       const heim_scram_data *client_key,
587		       const heim_scram_data *c1,
588		       const heim_scram_data *s1,
589		       const heim_scram_data *c2noproof,
590		       heim_scram_data *sessionKey)
591{
592    return scram_authmessage_signature(method,
593				       stored_key,
594				       c1, s1, c2noproof,
595				       client_key,
596				       sessionKey);
597}
598
599int
600heim_scram_validate_client_signature(heim_scram_method method,
601				     const heim_scram_data *stored_key,
602				     const heim_scram_data *client_signature,
603				     const heim_scram_data *proof,
604				     heim_scram_data *clientKey)
605{
606    unsigned char *p, *q, *u = NULL;
607    size_t length, n;
608    int ret;
609
610    scram_data_zero(clientKey);
611
612    if (stored_key->length != method->length || client_signature->length != method->length || proof->length != method->length)
613	return EINVAL;
614
615    q = client_signature->data;
616    p = proof->data;
617    u = malloc(method->length);
618    if (u == NULL)
619	return ENOMEM;
620
621    for (n = 0 ; n < proof->length; n++)
622	u[n] = p[n] ^ q[n];
623
624    scram_data_copy(clientKey, u, proof->length);
625
626    length = method->length;
627    ret = CCDigest(method->dalg, u, length, u);
628    if (ret != 0) {
629	ret = EINVAL;
630	goto out;
631    }
632
633    ret = memcmp(u, stored_key->data, stored_key->length);
634    if (ret != 0)
635	ret = EINVAL;
636
637 out:
638    free(u);
639    if (ret)
640	heim_scram_data_free(clientKey);
641
642    return ret;
643}
644
645
646static int
647client_calculate(void *ctx,
648		 heim_scram_method method,
649		 unsigned int iterations,
650		 heim_scram_data *salt,
651		 const heim_scram_data *c1,
652		 const heim_scram_data *s1,
653		 const heim_scram_data *c2noproof,
654		 heim_scram_data *proof,
655		 heim_scram_data *server,
656		 heim_scram_data *sessionKey)
657{
658    heim_scram_data client, stored, server_key;
659    unsigned char *p, *q;
660    size_t n;
661    int ret;
662
663    scram_data_zero(proof);
664    scram_data_zero(server);
665
666    ret = heim_scram_stored_key(method, ctx, iterations, salt,
667				&client, &stored, &server_key);
668    if (ret)
669	goto out;
670
671    ret = heim_scram_generate(method, &stored, &server_key,
672			      c1, s1, c2noproof, proof, server);
673    if (ret)
674	goto out;
675
676
677    ret = heim_scram_session_key(method, &stored, &client,
678				 c1, s1, c2noproof,
679				 sessionKey);
680    if (ret)
681	goto out;
682
683    /*
684     * Now client_key XOR proof
685     */
686    p = proof->data;
687    q = client.data;
688
689    heim_assert(proof->length == client.length, "proof.length == client.length");
690
691    for (n = 0 ; n < client.length; n++)
692	p[n] = p[n] ^ q[n];
693
694 out:
695    heim_scram_data_free(&server_key);
696    heim_scram_data_free(&stored);
697    heim_scram_data_free(&client);
698
699    return ret;
700}
701
702
703struct heim_scram_client heim_scram_client_password_procs_s = {
704    .version = SCRAM_CLIENT_VERSION_1,
705    .calculate = client_calculate
706};
707
708int
709heim_scram_client2(heim_scram_data *in,
710		   struct heim_scram_client *client,
711		   void *ctx,
712		   struct heim_scram *scram,
713		   heim_scram_data *out)
714{
715    heim_scram_pairs *p, *q = NULL;
716    heim_scram_data *servernonce, *salt, *iterations;
717    unsigned int iter;
718    char *str;
719    int ret;
720
721    scram_data_zero(out);
722
723    if (scram->type != CLIENT)
724	return HSCRAM_ERR_INVALID_ROLE;
725
726    ret = _heim_scram_parse(in, &p);
727    if (ret)
728	return ret;
729
730    ret = _scram_validate(p, server_first);
731    if (ret) {
732	_heim_scram_pairs_free(p);
733	return ret;
734    }
735
736    scram_data_copy(&scram->server1, in->data, in->length);
737
738    servernonce = scram_find_type(p, 'r');
739
740    /* Validate that the server reflects back our nonce to us */
741    if (servernonce->length < scram->nonce.length || memcmp(scram->nonce.data, servernonce->data, scram->nonce.length) != 0) {
742	_heim_scram_pairs_free(p);
743	return EINVAL;
744    }
745
746    salt = scram_find_type(p, 's');
747    iterations = scram_find_type(p, 'i');
748
749    heim_assert(servernonce != NULL && salt != NULL && iterations != NULL,
750		"validate doesn't work");
751
752    str = malloc(iterations->length + 1);
753    memcpy(str, iterations->data, iterations->length);
754    str[iterations->length] = '\0';
755    iter = atoi(str);
756    free(str);
757    if (iter == 0) {
758	_heim_scram_pairs_free(p);
759	return EINVAL;
760    }
761
762    q = scram_pairs_new();
763
764    scram_add_string(q, 'c', "biws");
765    scram_add_type(q, 'r', servernonce);
766
767    ret = _heim_scram_unparse(q, out);
768    if (ret)
769	goto out;
770
771    ret = client->calculate(ctx, scram->method,
772			    iter, salt, &scram->client1, &scram->server1, out,
773			    &scram->ClientProof, &scram->ServerSignature,
774			    &scram->SessionKey);
775    heim_scram_data_free(out);
776    if (ret)
777	goto out;
778
779    ret = scram_add_base64(q, 'p', &scram->ClientProof);
780    if (ret)
781	goto out;
782
783    ret = _heim_scram_unparse(q, out);
784    if (ret)
785	goto out;
786
787 out:
788    _heim_scram_pairs_free(p);
789    _heim_scram_pairs_free(q);
790
791    return ret;
792}
793
794
795int
796heim_scram_server2(heim_scram_data *in,
797		   struct heim_scram *scram,
798		   heim_scram_data *out)
799{
800    heim_scram_pairs *p = NULL, *q = NULL;
801    heim_scram_data *nonce, *proof, binaryproof, noproof, server;
802    int ret;
803
804    scram_data_zero(out);
805    scram_data_zero(&binaryproof);
806
807    if (scram->type != SERVER)
808	return HSCRAM_ERR_INVALID_ROLE;
809
810    ret = _heim_scram_parse(in, &p);
811    if (ret)
812	return ret;
813
814    ret = _scram_validate(p, client_final);
815    if (ret)
816	goto out;
817
818    /* chbinding = scram_find_type(p, 'c'); */
819    nonce = scram_find_type(p, 'r');
820
821    /* Validate that the client reflects back our nonce to us */
822    if (nonce->length != scram->nonce.length || memcmp(scram->nonce.data, nonce->data, scram->nonce.length) != 0) {
823	ret = EINVAL;
824	goto out;
825    }
826
827    proof = scram_find_type(p, 'p');
828
829    scram_data_alloc(&binaryproof, proof->length + 1);
830    memcpy(binaryproof.data, proof->data, proof->length);
831    ((char *)binaryproof.data)[proof->length] = '\0';
832    ret = base64_decode(binaryproof.data, binaryproof.data);
833    if (ret < 0) {
834	ret = EINVAL;
835	goto out;
836    }
837    binaryproof.length = ret;
838
839
840    ret = remove_proof(in, &noproof);
841    if (ret)
842	goto out;
843
844    ret = scram->server->calculate(scram->ctx,
845				   scram->method,
846				   &scram->user,
847				   &scram->client1,
848				   &scram->server1,
849				   &noproof,
850				   &binaryproof,
851				   &server,
852				   &scram->SessionKey);
853    if (ret)
854	goto out;
855
856
857    q = scram_pairs_new();
858
859    ret = scram_add_base64(q, 'v', &server);
860    heim_scram_data_free(&server);
861    if (ret)
862	goto out;
863
864    ret = _heim_scram_unparse(q, out);
865
866 out:
867    heim_scram_data_free(&binaryproof);
868    _heim_scram_pairs_free(p);
869    _heim_scram_pairs_free(q);
870
871    return ret;
872}
873
874int
875heim_scram_client3(heim_scram_data *in,
876		   heim_scram *scram)
877{
878    heim_scram_pairs *p;
879    heim_scram_data *data;
880    char *str;
881    int ret;
882
883    if (scram->type != CLIENT)
884	return HSCRAM_ERR_INVALID_ROLE;
885
886    ret = _heim_scram_parse(in, &p);
887    if (ret)
888	return ret;
889
890    ret = _scram_validate(p, server_final);
891    if (ret) {
892	_heim_scram_pairs_free(p);
893	return ret;
894    }
895
896    data = scram_find_type(p, 'v');
897
898    if (base64_encode(scram->ServerSignature.data,
899		      (int)scram->ServerSignature.length,
900		      &str) < 0) {
901	ret = EINVAL;
902	goto out;
903    }
904
905    if (strlen(str) != data->length ||
906	memcmp(str, data->data, data->length) != 0)
907	ret = EINVAL;
908    else
909	ret = 0;
910
911    free(str);
912
913 out:
914    _heim_scram_pairs_free(p);
915
916    return ret;
917}
918
919int
920heim_scram_get_channel_binding(heim_scram *scram,
921			       heim_scram_data *ch)
922{
923    scram_data_zero(ch);
924
925    return 0;
926}
927
928int
929heim_scram_get_session_key(heim_scram *scram,
930		       heim_scram_data *sessionKey)
931{
932    scram_data_copy(sessionKey, scram->SessionKey.data, scram->SessionKey.length);
933    return 0;
934}
935
936
937void
938heim_scram_free(heim_scram *scram)
939{
940    if (scram == NULL)
941	return;
942
943    heim_scram_data_free(&scram->client1);
944    heim_scram_data_free(&scram->server1);
945    heim_scram_data_free(&scram->nonce);
946    heim_scram_data_free(&scram->ClientProof);
947    heim_scram_data_free(&scram->ServerSignature);
948    heim_scram_data_free(&scram->SessionKey);
949
950
951    memset(scram, 0, sizeof(*scram));
952    free(scram);
953}
954
955
956int
957heim_scram_salted_key(heim_scram_method method,
958		      const char *password,
959		      unsigned int iterations,
960		      heim_scram_data *salt,
961		      heim_scram_data *data)
962{
963    heim_scram_data key;
964    size_t in32_len, out32_len, pwlen;
965    uint32_t *in32, *out32;
966    char *pw = NULL;
967    int ret;
968
969    scram_data_zero(data);
970
971    key.length = method->length;
972    key.data = malloc(key.length);
973    if (key.data == NULL)
974	return ENOMEM;
975
976    ret = wind_utf8ucs4_copy(password, &in32, &in32_len);
977    if (ret) {
978	heim_scram_data_free(&key);
979	return ret;
980    }
981
982    if (in32_len > UINT_MAX/(sizeof(out32[0]) * 4)) {
983	heim_scram_data_free(&key);
984	return ERANGE;
985    }
986
987    out32_len = in32_len * 4;
988    out32 = malloc(out32_len * sizeof(out32[0]));
989    if (out32 == NULL) {
990	heim_scram_data_free(&key);
991	return ENOMEM;
992    }
993
994    ret = wind_stringprep(in32, in32_len, out32, &out32_len, WIND_PROFILE_SASL);
995    free(in32);
996    if (ret) {
997	free(out32);
998	heim_scram_data_free(&key);
999	return ret;
1000    }
1001
1002    ret = wind_ucs4utf8_copy(out32, out32_len, &pw, &pwlen);
1003    free(out32);
1004    if (ret) {
1005	heim_scram_data_free(&key);
1006	return ret;
1007    }
1008
1009    ret = CCKeyDerivationPBKDF(kCCPBKDF2, pw, pwlen,
1010			       salt->data, salt->length,
1011			       method->alg, iterations,
1012			       key.data, key.length);
1013    if (ret) {
1014	heim_scram_data_free(&key);
1015	return ret;
1016    }
1017
1018    *data = key;
1019
1020    return 0;
1021}
1022
1023int
1024heim_scram_stored_key(heim_scram_method method,
1025		      const char *password,
1026		      unsigned int iterations,
1027		      heim_scram_data *salt,
1028		      heim_scram_data *client_key,
1029		      heim_scram_data *stored_key,
1030		      heim_scram_data *server_key)
1031{
1032    size_t length;
1033    heim_scram_data sk;
1034    int ret;
1035
1036    scram_data_zero(client_key);
1037    scram_data_zero(stored_key);
1038    scram_data_zero(server_key);
1039
1040    ret = heim_scram_salted_key(method, password,
1041				iterations, salt, &sk);
1042    if (ret)
1043	return ret;
1044
1045    length = method->halglength;
1046    scram_data_alloc(stored_key, length);
1047    scram_data_alloc(client_key, length);
1048
1049    CCHmac(method->halg, sk.data, sk.length, "Client Key", 10, client_key->data);
1050
1051    ret = CCDigest(method->dalg, client_key->data, length,
1052		     stored_key->data);
1053    if (ret) {
1054	heim_scram_data_free(&sk);
1055	return EINVAL;
1056    }
1057
1058    if (server_key) {
1059	scram_data_alloc(server_key, length);
1060
1061	CCHmac(method->halg, sk.data, sk.length, "Server Key", 10,
1062	     server_key->data);
1063    }
1064
1065    heim_scram_data_free(&sk);
1066
1067    return 0;
1068}
1069
1070#endif
1071