heimdal Extra files required for merge up to current heimdal
[sfrench/samba-autobuild/.git] / source4 / heimdal / lib / krb5 / crypto-pk.c
1 /*
2  * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #include "krb5_locl.h"
35
36 #include <pkinit_asn1.h>
37
38 krb5_error_code
39 _krb5_pk_octetstring2key(krb5_context context,
40                          krb5_enctype type,
41                          const void *dhdata,
42                          size_t dhsize,
43                          const heim_octet_string *c_n,
44                          const heim_octet_string *k_n,
45                          krb5_keyblock *key)
46 {
47     struct encryption_type *et = _krb5_find_enctype(type);
48     krb5_error_code ret;
49     size_t keylen, offset;
50     void *keydata;
51     unsigned char counter;
52     unsigned char shaoutput[SHA_DIGEST_LENGTH];
53     EVP_MD_CTX *m;
54
55     if(et == NULL) {
56         krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
57                                N_("encryption type %d not supported", ""),
58                                type);
59         return KRB5_PROG_ETYPE_NOSUPP;
60     }
61     keylen = (et->keytype->bits + 7) / 8;
62
63     keydata = malloc(keylen);
64     if (keydata == NULL) {
65         krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
66         return ENOMEM;
67     }
68
69     m = EVP_MD_CTX_create();
70     if (m == NULL) {
71         free(keydata);
72         krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
73         return ENOMEM;
74     }
75
76     counter = 0;
77     offset = 0;
78     do {
79
80         EVP_DigestInit_ex(m, EVP_sha1(), NULL);
81         EVP_DigestUpdate(m, &counter, 1);
82         EVP_DigestUpdate(m, dhdata, dhsize);
83
84         if (c_n)
85             EVP_DigestUpdate(m, c_n->data, c_n->length);
86         if (k_n)
87             EVP_DigestUpdate(m, k_n->data, k_n->length);
88
89         EVP_DigestFinal_ex(m, shaoutput, NULL);
90
91         memcpy((unsigned char *)keydata + offset,
92                shaoutput,
93                min(keylen - offset, sizeof(shaoutput)));
94
95         offset += sizeof(shaoutput);
96         counter++;
97     } while(offset < keylen);
98     memset(shaoutput, 0, sizeof(shaoutput));
99
100     EVP_MD_CTX_destroy(m);
101
102     ret = krb5_random_to_key(context, type, keydata, keylen, key);
103     memset(keydata, 0, sizeof(keylen));
104     free(keydata);
105     return ret;
106 }
107
108 static krb5_error_code
109 encode_uvinfo(krb5_context context, krb5_const_principal p, krb5_data *data)
110 {
111     KRB5PrincipalName pn;
112     krb5_error_code ret;
113     size_t size;
114
115     pn.principalName = p->name;
116     pn.realm = p->realm;
117
118     ASN1_MALLOC_ENCODE(KRB5PrincipalName, data->data, data->length,
119                        &pn, &size, ret);
120     if (ret) {
121         krb5_data_zero(data);
122         krb5_set_error_message(context, ret,
123                                N_("Failed to encode KRB5PrincipalName", ""));
124         return ret;
125     }
126     if (data->length != size)
127         krb5_abortx(context, "asn1 compiler internal error");
128     return 0;
129 }
130
131 static krb5_error_code
132 encode_otherinfo(krb5_context context,
133                  const AlgorithmIdentifier *ai,
134                  krb5_const_principal client,
135                  krb5_const_principal server,
136                  krb5_enctype enctype,
137                  const krb5_data *as_req,
138                  const krb5_data *pk_as_rep,
139                  const Ticket *ticket,
140                  krb5_data *other)
141 {
142     PkinitSP80056AOtherInfo otherinfo;
143     PkinitSuppPubInfo pubinfo;
144     krb5_error_code ret;
145     krb5_data pub;
146     size_t size;
147
148     krb5_data_zero(other);
149     memset(&otherinfo, 0, sizeof(otherinfo));
150     memset(&pubinfo, 0, sizeof(pubinfo));
151
152     pubinfo.enctype = enctype;
153     pubinfo.as_REQ = *as_req;
154     pubinfo.pk_as_rep = *pk_as_rep;
155     pubinfo.ticket = *ticket;
156     ASN1_MALLOC_ENCODE(PkinitSuppPubInfo, pub.data, pub.length,
157                        &pubinfo, &size, ret);
158     if (ret) {
159         krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
160         return ret;
161     }
162     if (pub.length != size)
163         krb5_abortx(context, "asn1 compiler internal error");
164
165     ret = encode_uvinfo(context, client, &otherinfo.partyUInfo);
166     if (ret) {
167         free(pub.data);
168         return ret;
169     }
170     ret = encode_uvinfo(context, server, &otherinfo.partyVInfo);
171     if (ret) {
172         free(otherinfo.partyUInfo.data);
173         free(pub.data);
174         return ret;
175     }
176
177     otherinfo.algorithmID = *ai;
178     otherinfo.suppPubInfo = &pub;
179
180     ASN1_MALLOC_ENCODE(PkinitSP80056AOtherInfo, other->data, other->length,
181                        &otherinfo, &size, ret);
182     free(otherinfo.partyUInfo.data);
183     free(otherinfo.partyVInfo.data);
184     free(pub.data);
185     if (ret) {
186         krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
187         return ret;
188     }
189     if (other->length != size)
190         krb5_abortx(context, "asn1 compiler internal error");
191
192     return 0;
193 }
194
195 krb5_error_code
196 _krb5_pk_kdf(krb5_context context,
197              const struct AlgorithmIdentifier *ai,
198              const void *dhdata,
199              size_t dhsize,
200              krb5_const_principal client,
201              krb5_const_principal server,
202              krb5_enctype enctype,
203              const krb5_data *as_req,
204              const krb5_data *pk_as_rep,
205              const Ticket *ticket,
206              krb5_keyblock *key)
207 {
208     struct encryption_type *et;
209     krb5_error_code ret;
210     krb5_data other;
211     size_t keylen, offset;
212     uint32_t counter;
213     unsigned char *keydata;
214     unsigned char shaoutput[SHA_DIGEST_LENGTH];
215     EVP_MD_CTX *m;
216
217     if (der_heim_oid_cmp(&asn1_oid_id_pkinit_kdf_ah_sha1, &ai->algorithm) != 0) {
218         krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
219                                N_("KDF not supported", ""));
220         return KRB5_PROG_ETYPE_NOSUPP;
221     }
222     if (ai->parameters != NULL &&
223         (ai->parameters->length != 2 ||
224          memcmp(ai->parameters->data, "\x05\x00", 2) != 0))
225         {
226             krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
227                                    N_("kdf params not NULL or the NULL-type",
228                                       ""));
229             return KRB5_PROG_ETYPE_NOSUPP;
230         }
231
232     et = _krb5_find_enctype(enctype);
233     if(et == NULL) {
234         krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
235                                N_("encryption type %d not supported", ""),
236                                enctype);
237         return KRB5_PROG_ETYPE_NOSUPP;
238     }
239     keylen = (et->keytype->bits + 7) / 8;
240
241     keydata = malloc(keylen);
242     if (keydata == NULL) {
243         krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
244         return ENOMEM;
245     }
246
247     ret = encode_otherinfo(context, ai, client, server,
248                            enctype, as_req, pk_as_rep, ticket, &other);
249     if (ret) {
250         free(keydata);
251         return ret;
252     }
253
254     m = EVP_MD_CTX_create();
255     if (m == NULL) {
256         free(keydata);
257         free(other.data);
258         krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
259         return ENOMEM;
260     }
261
262     offset = 0;
263     counter = 1;
264     do {
265         unsigned char cdata[4];
266
267         EVP_DigestInit_ex(m, EVP_sha1(), NULL);
268         _krb5_put_int(cdata, counter, 4);
269         EVP_DigestUpdate(m, cdata, 4);
270         EVP_DigestUpdate(m, dhdata, dhsize);
271         EVP_DigestUpdate(m, other.data, other.length);
272
273         EVP_DigestFinal_ex(m, shaoutput, NULL);
274
275         memcpy((unsigned char *)keydata + offset,
276                shaoutput,
277                min(keylen - offset, sizeof(shaoutput)));
278
279         offset += sizeof(shaoutput);
280         counter++;
281     } while(offset < keylen);
282     memset(shaoutput, 0, sizeof(shaoutput));
283
284     EVP_MD_CTX_destroy(m);
285     free(other.data);
286
287     ret = krb5_random_to_key(context, enctype, keydata, keylen, key);
288     memset(keydata, 0, sizeof(keylen));
289     free(keydata);
290
291     return ret;
292 }