1226031Sstas/*
2226031Sstas * Copyright (c) 1997 - 2008 Kungliga Tekniska H��gskolan
3226031Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4226031Sstas * All rights reserved.
5226031Sstas *
6226031Sstas * Redistribution and use in source and binary forms, with or without
7226031Sstas * modification, are permitted provided that the following conditions
8226031Sstas * are met:
9226031Sstas *
10226031Sstas * 1. Redistributions of source code must retain the above copyright
11226031Sstas *    notice, this list of conditions and the following disclaimer.
12226031Sstas *
13226031Sstas * 2. Redistributions in binary form must reproduce the above copyright
14226031Sstas *    notice, this list of conditions and the following disclaimer in the
15226031Sstas *    documentation and/or other materials provided with the distribution.
16226031Sstas *
17226031Sstas * 3. Neither the name of the Institute nor the names of its contributors
18226031Sstas *    may be used to endorse or promote products derived from this software
19226031Sstas *    without specific prior written permission.
20226031Sstas *
21226031Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22226031Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23226031Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24226031Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25226031Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26226031Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27226031Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28226031Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29226031Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30226031Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31226031Sstas * SUCH DAMAGE.
32226031Sstas */
33226031Sstas
34226031Sstas#include "krb5_locl.h"
35226031Sstas
36226031Sstas#include <pkinit_asn1.h>
37226031Sstas
38226031Sstaskrb5_error_code
39226031Sstas_krb5_pk_octetstring2key(krb5_context context,
40226031Sstas			 krb5_enctype type,
41226031Sstas			 const void *dhdata,
42226031Sstas			 size_t dhsize,
43226031Sstas			 const heim_octet_string *c_n,
44226031Sstas			 const heim_octet_string *k_n,
45226031Sstas			 krb5_keyblock *key)
46226031Sstas{
47226031Sstas    struct _krb5_encryption_type *et = _krb5_find_enctype(type);
48226031Sstas    krb5_error_code ret;
49226031Sstas    size_t keylen, offset;
50226031Sstas    void *keydata;
51226031Sstas    unsigned char counter;
52226031Sstas    unsigned char shaoutput[SHA_DIGEST_LENGTH];
53226031Sstas    EVP_MD_CTX *m;
54226031Sstas
55226031Sstas    if(et == NULL) {
56226031Sstas	krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
57226031Sstas			       N_("encryption type %d not supported", ""),
58226031Sstas			       type);
59226031Sstas	return KRB5_PROG_ETYPE_NOSUPP;
60226031Sstas    }
61226031Sstas    keylen = (et->keytype->bits + 7) / 8;
62226031Sstas
63226031Sstas    keydata = malloc(keylen);
64226031Sstas    if (keydata == NULL) {
65226031Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
66226031Sstas	return ENOMEM;
67226031Sstas    }
68226031Sstas
69226031Sstas    m = EVP_MD_CTX_create();
70226031Sstas    if (m == NULL) {
71226031Sstas	free(keydata);
72226031Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
73226031Sstas	return ENOMEM;
74226031Sstas    }
75226031Sstas
76226031Sstas    counter = 0;
77226031Sstas    offset = 0;
78226031Sstas    do {
79226031Sstas
80226031Sstas	EVP_DigestInit_ex(m, EVP_sha1(), NULL);
81226031Sstas	EVP_DigestUpdate(m, &counter, 1);
82226031Sstas	EVP_DigestUpdate(m, dhdata, dhsize);
83226031Sstas
84226031Sstas	if (c_n)
85226031Sstas	    EVP_DigestUpdate(m, c_n->data, c_n->length);
86226031Sstas	if (k_n)
87226031Sstas	    EVP_DigestUpdate(m, k_n->data, k_n->length);
88226031Sstas
89226031Sstas	EVP_DigestFinal_ex(m, shaoutput, NULL);
90226031Sstas
91226031Sstas	memcpy((unsigned char *)keydata + offset,
92226031Sstas	       shaoutput,
93226031Sstas	       min(keylen - offset, sizeof(shaoutput)));
94226031Sstas
95226031Sstas	offset += sizeof(shaoutput);
96226031Sstas	counter++;
97226031Sstas    } while(offset < keylen);
98226031Sstas    memset(shaoutput, 0, sizeof(shaoutput));
99226031Sstas
100226031Sstas    EVP_MD_CTX_destroy(m);
101226031Sstas
102226031Sstas    ret = krb5_random_to_key(context, type, keydata, keylen, key);
103226031Sstas    memset(keydata, 0, sizeof(keylen));
104226031Sstas    free(keydata);
105226031Sstas    return ret;
106226031Sstas}
107226031Sstas
108226031Sstasstatic krb5_error_code
109226031Sstasencode_uvinfo(krb5_context context, krb5_const_principal p, krb5_data *data)
110226031Sstas{
111226031Sstas    KRB5PrincipalName pn;
112226031Sstas    krb5_error_code ret;
113226031Sstas    size_t size = 0;
114226031Sstas
115226031Sstas    pn.principalName = p->name;
116226031Sstas    pn.realm = p->realm;
117226031Sstas
118226031Sstas    ASN1_MALLOC_ENCODE(KRB5PrincipalName, data->data, data->length,
119226031Sstas		       &pn, &size, ret);
120226031Sstas    if (ret) {
121226031Sstas	krb5_data_zero(data);
122226031Sstas	krb5_set_error_message(context, ret,
123226031Sstas			       N_("Failed to encode KRB5PrincipalName", ""));
124226031Sstas	return ret;
125226031Sstas    }
126226031Sstas    if (data->length != size)
127226031Sstas	krb5_abortx(context, "asn1 compiler internal error");
128226031Sstas    return 0;
129226031Sstas}
130226031Sstas
131226031Sstasstatic krb5_error_code
132226031Sstasencode_otherinfo(krb5_context context,
133226031Sstas		 const AlgorithmIdentifier *ai,
134226031Sstas		 krb5_const_principal client,
135226031Sstas		 krb5_const_principal server,
136226031Sstas		 krb5_enctype enctype,
137226031Sstas		 const krb5_data *as_req,
138226031Sstas		 const krb5_data *pk_as_rep,
139226031Sstas		 const Ticket *ticket,
140226031Sstas		 krb5_data *other)
141226031Sstas{
142226031Sstas    PkinitSP80056AOtherInfo otherinfo;
143226031Sstas    PkinitSuppPubInfo pubinfo;
144226031Sstas    krb5_error_code ret;
145226031Sstas    krb5_data pub;
146226031Sstas    size_t size = 0;
147226031Sstas
148226031Sstas    krb5_data_zero(other);
149226031Sstas    memset(&otherinfo, 0, sizeof(otherinfo));
150226031Sstas    memset(&pubinfo, 0, sizeof(pubinfo));
151226031Sstas
152226031Sstas    pubinfo.enctype = enctype;
153226031Sstas    pubinfo.as_REQ = *as_req;
154226031Sstas    pubinfo.pk_as_rep = *pk_as_rep;
155226031Sstas    pubinfo.ticket = *ticket;
156226031Sstas    ASN1_MALLOC_ENCODE(PkinitSuppPubInfo, pub.data, pub.length,
157226031Sstas		       &pubinfo, &size, ret);
158226031Sstas    if (ret) {
159226031Sstas	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
160226031Sstas	return ret;
161226031Sstas    }
162226031Sstas    if (pub.length != size)
163226031Sstas	krb5_abortx(context, "asn1 compiler internal error");
164226031Sstas
165226031Sstas    ret = encode_uvinfo(context, client, &otherinfo.partyUInfo);
166226031Sstas    if (ret) {
167226031Sstas	free(pub.data);
168226031Sstas	return ret;
169226031Sstas    }
170226031Sstas    ret = encode_uvinfo(context, server, &otherinfo.partyVInfo);
171226031Sstas    if (ret) {
172226031Sstas	free(otherinfo.partyUInfo.data);
173226031Sstas	free(pub.data);
174226031Sstas	return ret;
175226031Sstas    }
176226031Sstas
177226031Sstas    otherinfo.algorithmID = *ai;
178226031Sstas    otherinfo.suppPubInfo = &pub;
179226031Sstas
180226031Sstas    ASN1_MALLOC_ENCODE(PkinitSP80056AOtherInfo, other->data, other->length,
181226031Sstas		       &otherinfo, &size, ret);
182226031Sstas    free(otherinfo.partyUInfo.data);
183226031Sstas    free(otherinfo.partyVInfo.data);
184226031Sstas    free(pub.data);
185226031Sstas    if (ret) {
186226031Sstas	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
187226031Sstas	return ret;
188226031Sstas    }
189226031Sstas    if (other->length != size)
190226031Sstas	krb5_abortx(context, "asn1 compiler internal error");
191226031Sstas
192226031Sstas    return 0;
193226031Sstas}
194226031Sstas
195226031Sstas
196226031Sstas
197226031Sstaskrb5_error_code
198226031Sstas_krb5_pk_kdf(krb5_context context,
199226031Sstas	     const struct AlgorithmIdentifier *ai,
200226031Sstas	     const void *dhdata,
201226031Sstas	     size_t dhsize,
202226031Sstas	     krb5_const_principal client,
203226031Sstas	     krb5_const_principal server,
204226031Sstas	     krb5_enctype enctype,
205226031Sstas	     const krb5_data *as_req,
206226031Sstas	     const krb5_data *pk_as_rep,
207226031Sstas	     const Ticket *ticket,
208226031Sstas	     krb5_keyblock *key)
209226031Sstas{
210226031Sstas    struct _krb5_encryption_type *et;
211226031Sstas    krb5_error_code ret;
212226031Sstas    krb5_data other;
213226031Sstas    size_t keylen, offset;
214226031Sstas    uint32_t counter;
215226031Sstas    unsigned char *keydata;
216226031Sstas    unsigned char shaoutput[SHA512_DIGEST_LENGTH];
217226031Sstas    const EVP_MD *md;
218226031Sstas    EVP_MD_CTX *m;
219226031Sstas
220226031Sstas    if (der_heim_oid_cmp(&asn1_oid_id_pkinit_kdf_ah_sha1, &ai->algorithm) == 0) {
221226031Sstas        md = EVP_sha1();
222226031Sstas    } else if (der_heim_oid_cmp(&asn1_oid_id_pkinit_kdf_ah_sha256, &ai->algorithm) == 0) {
223226031Sstas        md = EVP_sha256();
224226031Sstas    } else if (der_heim_oid_cmp(&asn1_oid_id_pkinit_kdf_ah_sha512, &ai->algorithm) == 0) {
225226031Sstas        md = EVP_sha512();
226226031Sstas    } else {
227226031Sstas	krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
228226031Sstas			       N_("KDF not supported", ""));
229226031Sstas	return KRB5_PROG_ETYPE_NOSUPP;
230226031Sstas    }
231226031Sstas    if (ai->parameters != NULL &&
232226031Sstas	(ai->parameters->length != 2 ||
233226031Sstas	 memcmp(ai->parameters->data, "\x05\x00", 2) != 0))
234226031Sstas	{
235226031Sstas	    krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
236226031Sstas				   N_("kdf params not NULL or the NULL-type",
237226031Sstas				      ""));
238226031Sstas	    return KRB5_PROG_ETYPE_NOSUPP;
239226031Sstas	}
240226031Sstas
241226031Sstas    et = _krb5_find_enctype(enctype);
242226031Sstas    if(et == NULL) {
243226031Sstas	krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
244226031Sstas			       N_("encryption type %d not supported", ""),
245226031Sstas			       enctype);
246226031Sstas	return KRB5_PROG_ETYPE_NOSUPP;
247226031Sstas    }
248226031Sstas    keylen = (et->keytype->bits + 7) / 8;
249226031Sstas
250226031Sstas    keydata = malloc(keylen);
251226031Sstas    if (keydata == NULL) {
252226031Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
253226031Sstas	return ENOMEM;
254226031Sstas    }
255226031Sstas
256226031Sstas    ret = encode_otherinfo(context, ai, client, server,
257226031Sstas			   enctype, as_req, pk_as_rep, ticket, &other);
258226031Sstas    if (ret) {
259226031Sstas	free(keydata);
260226031Sstas	return ret;
261226031Sstas    }
262226031Sstas
263226031Sstas    m = EVP_MD_CTX_create();
264226031Sstas    if (m == NULL) {
265226031Sstas	free(keydata);
266226031Sstas	free(other.data);
267226031Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
268226031Sstas	return ENOMEM;
269226031Sstas    }
270226031Sstas
271226031Sstas    offset = 0;
272226031Sstas    counter = 1;
273226031Sstas    do {
274226031Sstas	unsigned char cdata[4];
275226031Sstas
276226031Sstas	EVP_DigestInit_ex(m, md, NULL);
277226031Sstas	_krb5_put_int(cdata, counter, 4);
278226031Sstas	EVP_DigestUpdate(m, cdata, 4);
279226031Sstas	EVP_DigestUpdate(m, dhdata, dhsize);
280226031Sstas	EVP_DigestUpdate(m, other.data, other.length);
281226031Sstas
282226031Sstas	EVP_DigestFinal_ex(m, shaoutput, NULL);
283226031Sstas
284226031Sstas	memcpy((unsigned char *)keydata + offset,
285226031Sstas	       shaoutput,
286226031Sstas	       min(keylen - offset, EVP_MD_CTX_size(m)));
287226031Sstas
288226031Sstas	offset += EVP_MD_CTX_size(m);
289226031Sstas	counter++;
290226031Sstas    } while(offset < keylen);
291226031Sstas    memset(shaoutput, 0, sizeof(shaoutput));
292226031Sstas
293226031Sstas    EVP_MD_CTX_destroy(m);
294226031Sstas    free(other.data);
295226031Sstas
296226031Sstas    ret = krb5_random_to_key(context, enctype, keydata, keylen, key);
297226031Sstas    memset(keydata, 0, sizeof(keylen));
298226031Sstas    free(keydata);
299226031Sstas
300226031Sstas    return ret;
301226031Sstas}
302