xref: /freebsd/sys/kgssapi/krb5/kcrypto.c (revision 39beb93c)
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 }
267