]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/kgssapi/krb5/kcrypto.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / kgssapi / krb5 / kcrypto.c
1 /*-
2  * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
3  * Authors: Doug Rabson <dfr@rabson.org>
4  * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
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  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/param.h>
32 #include <sys/malloc.h>
33 #include <sys/kobj.h>
34 #include <sys/mbuf.h>
35
36 #include <kgssapi/gssapi.h>
37 #include <kgssapi/gssapi_impl.h>
38
39 #include "kcrypto.h"
40
41 static struct krb5_encryption_class *krb5_encryption_classes[] = {
42         &krb5_des_encryption_class,
43         &krb5_des3_encryption_class,
44         &krb5_aes128_encryption_class,
45         &krb5_aes256_encryption_class,
46         &krb5_arcfour_encryption_class,
47         &krb5_arcfour_56_encryption_class,
48         NULL
49 };
50
51 struct krb5_encryption_class *
52 krb5_find_encryption_class(int etype)
53 {
54         int i;
55
56         for (i = 0; krb5_encryption_classes[i]; i++) {
57                 if (krb5_encryption_classes[i]->ec_type == etype)
58                         return (krb5_encryption_classes[i]);
59         }
60         return (NULL);
61 }
62
63 struct krb5_key_state *
64 krb5_create_key(const struct krb5_encryption_class *ec)
65 {
66         struct krb5_key_state *ks;
67
68         ks = malloc(sizeof(struct krb5_key_state), M_GSSAPI, M_WAITOK);
69         ks->ks_class = ec;
70         refcount_init(&ks->ks_refs, 1);
71         ks->ks_key = malloc(ec->ec_keylen, M_GSSAPI, M_WAITOK);
72         ec->ec_init(ks);
73
74         return (ks);
75 }
76
77 void
78 krb5_free_key(struct krb5_key_state *ks)
79 {
80
81         if (refcount_release(&ks->ks_refs)) {
82                 ks->ks_class->ec_destroy(ks);
83                 bzero(ks->ks_key, ks->ks_class->ec_keylen);
84                 free(ks->ks_key, M_GSSAPI);
85                 free(ks, M_GSSAPI);
86         }
87 }
88
89 static size_t
90 gcd(size_t a, size_t b)
91 {
92
93         if (b == 0)
94                 return (a);
95         return gcd(b, a % b);
96 }
97
98 static size_t
99 lcm(size_t a, size_t b)
100 {
101         return ((a * b) / gcd(a, b));
102 }
103
104 /*
105  * Rotate right 13 of a variable precision number in 'in', storing the
106  * result in 'out'. The number is assumed to be big-endian in memory
107  * representation.
108  */
109 static void
110 krb5_rotate_right_13(uint8_t *out, uint8_t *in, size_t numlen)
111 {
112         uint32_t carry;
113         size_t i;
114
115         /*
116          * Special case when numlen == 1. A rotate right 13 of a
117          * single byte number changes to a rotate right 5.
118          */
119         if (numlen == 1) {
120                 carry = in[0] >> 5;
121                 out[0] = (in[0] << 3) | carry;
122                 return;
123         }
124
125         carry = ((in[numlen - 2] & 31) << 8) | in[numlen - 1];
126         for (i = 2; i < numlen; i++) {
127                 out[i] = ((in[i - 2] & 31) << 3) | (in[i - 1] >> 5);
128         }
129         out[1] = ((carry & 31) << 3) | (in[0] >> 5);
130         out[0] = carry >> 5;
131 }
132
133 /*
134  * Add two variable precision numbers in big-endian representation
135  * using ones-complement arithmetic.
136  */
137 static void
138 krb5_ones_complement_add(uint8_t *out, const uint8_t *in, size_t len)
139 {
140         int n, i;
141
142         /*
143          * First calculate the 2s complement sum, remembering the
144          * carry.
145          */
146         n = 0;
147         for (i = len - 1; i >= 0; i--) {
148                 n = out[i] + in[i] + n;
149                 out[i] = n;
150                 n >>= 8;
151         }
152         /*
153          * Then add back the carry.
154          */
155         for (i = len - 1; n && i >= 0; i--) {
156                 n = out[i] + n;
157                 out[i] = n;
158                 n >>= 8;
159         }
160 }
161
162 static void
163 krb5_n_fold(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen)
164 {
165         size_t tmplen;
166         uint8_t *tmp;
167         size_t i;
168         uint8_t *p;
169
170         tmplen = lcm(inlen, outlen);
171         tmp = malloc(tmplen, M_GSSAPI, M_WAITOK);
172
173         bcopy(in, tmp, inlen);
174         for (i = inlen, p = tmp; i < tmplen; i += inlen, p += inlen) {
175                 krb5_rotate_right_13(p + inlen, p, inlen);
176         }
177         bzero(out, outlen);
178         for (i = 0, p = tmp; i < tmplen; i += outlen, p += outlen) {
179                 krb5_ones_complement_add(out, p, outlen);
180         }
181         free(tmp, M_GSSAPI);
182 }
183
184 struct krb5_key_state *
185 krb5_derive_key(struct krb5_key_state *inkey,
186     void *constant, size_t constantlen)
187 {
188         struct krb5_key_state *dk;
189         const struct krb5_encryption_class *ec = inkey->ks_class;
190         uint8_t *folded;
191         uint8_t *bytes, *p, *q;
192         struct mbuf *m;
193         int randomlen, i;
194
195         /*
196          * Expand the constant to blocklen bytes.
197          */
198         folded = malloc(ec->ec_blocklen, M_GSSAPI, M_WAITOK);
199         krb5_n_fold(folded, ec->ec_blocklen, constant, constantlen);
200
201         /*
202          * Generate enough bytes for keybits rounded up to a multiple
203          * of blocklen.
204          */
205         randomlen = ((ec->ec_keybits/8 + ec->ec_blocklen - 1) / ec->ec_blocklen)
206                 * ec->ec_blocklen;
207         bytes = malloc(randomlen, M_GSSAPI, M_WAITOK);
208         MGET(m, M_WAITOK, MT_DATA);
209         m->m_len = ec->ec_blocklen;
210         for (i = 0, p = bytes, q = folded; i < randomlen;
211              q = p, i += ec->ec_blocklen, p += ec->ec_blocklen) {
212                 bcopy(q, m->m_data, ec->ec_blocklen);
213                 krb5_encrypt(inkey, m, 0, ec->ec_blocklen, NULL, 0);
214                 bcopy(m->m_data, p, ec->ec_blocklen);
215         }
216         m_free(m);
217
218         dk = krb5_create_key(ec);
219         krb5_random_to_key(dk, bytes);
220
221         free(folded, M_GSSAPI);
222         free(bytes, M_GSSAPI);
223
224         return (dk);
225 }
226
227 static struct krb5_key_state *
228 krb5_get_usage_key(struct krb5_key_state *basekey, int usage, int which)
229 {
230         const struct krb5_encryption_class *ec = basekey->ks_class;
231
232         if (ec->ec_flags & EC_DERIVED_KEYS) {
233                 uint8_t constant[5];
234
235                 constant[0] = usage >> 24;
236                 constant[1] = usage >> 16;
237                 constant[2] = usage >> 8;
238                 constant[3] = usage;
239                 constant[4] = which;
240                 return (krb5_derive_key(basekey, constant, 5));
241         } else {
242                 refcount_acquire(&basekey->ks_refs);
243                 return (basekey);
244         }
245 }
246
247 struct krb5_key_state *
248 krb5_get_encryption_key(struct krb5_key_state *basekey, int usage)
249 {
250
251         return (krb5_get_usage_key(basekey, usage, 0xaa));
252 }
253
254 struct krb5_key_state *
255 krb5_get_integrity_key(struct krb5_key_state *basekey, int usage)
256 {
257
258         return (krb5_get_usage_key(basekey, usage, 0x55));
259 }
260
261 struct krb5_key_state *
262 krb5_get_checksum_key(struct krb5_key_state *basekey, int usage)
263 {
264
265         return (krb5_get_usage_key(basekey, usage, 0x99));
266 }