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