1184588Sdfr/*-
2184588Sdfr * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
3184588Sdfr * Authors: Doug Rabson <dfr@rabson.org>
4184588Sdfr * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
5184588Sdfr *
6184588Sdfr * Redistribution and use in source and binary forms, with or without
7184588Sdfr * modification, are permitted provided that the following conditions
8184588Sdfr * are met:
9184588Sdfr * 1. Redistributions of source code must retain the above copyright
10184588Sdfr *    notice, this list of conditions and the following disclaimer.
11184588Sdfr * 2. Redistributions in binary form must reproduce the above copyright
12184588Sdfr *    notice, this list of conditions and the following disclaimer in the
13184588Sdfr *    documentation and/or other materials provided with the distribution.
14184588Sdfr *
15184588Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16184588Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17184588Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18184588Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19184588Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20184588Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21184588Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22184588Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23184588Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24184588Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25184588Sdfr * SUCH DAMAGE.
26184588Sdfr */
27184588Sdfr
28184588Sdfr#include <sys/cdefs.h>
29184588Sdfr__FBSDID("$FreeBSD$");
30184588Sdfr
31184588Sdfr#include <sys/param.h>
32184588Sdfr#include <sys/malloc.h>
33184588Sdfr#include <sys/kobj.h>
34184588Sdfr#include <sys/mbuf.h>
35184588Sdfr
36184588Sdfr#include <kgssapi/gssapi.h>
37184588Sdfr#include <kgssapi/gssapi_impl.h>
38184588Sdfr
39184588Sdfr#include "kcrypto.h"
40184588Sdfr
41184588Sdfrstatic struct krb5_encryption_class *krb5_encryption_classes[] = {
42184588Sdfr	&krb5_des_encryption_class,
43184588Sdfr	&krb5_des3_encryption_class,
44184588Sdfr	&krb5_aes128_encryption_class,
45184588Sdfr	&krb5_aes256_encryption_class,
46184588Sdfr	&krb5_arcfour_encryption_class,
47184588Sdfr	&krb5_arcfour_56_encryption_class,
48184588Sdfr	NULL
49184588Sdfr};
50184588Sdfr
51184588Sdfrstruct krb5_encryption_class *
52184588Sdfrkrb5_find_encryption_class(int etype)
53184588Sdfr{
54184588Sdfr	int i;
55184588Sdfr
56184588Sdfr	for (i = 0; krb5_encryption_classes[i]; i++) {
57184588Sdfr		if (krb5_encryption_classes[i]->ec_type == etype)
58184588Sdfr			return (krb5_encryption_classes[i]);
59184588Sdfr	}
60184588Sdfr	return (NULL);
61184588Sdfr}
62184588Sdfr
63184588Sdfrstruct krb5_key_state *
64184588Sdfrkrb5_create_key(const struct krb5_encryption_class *ec)
65184588Sdfr{
66184588Sdfr	struct krb5_key_state *ks;
67184588Sdfr
68184588Sdfr	ks = malloc(sizeof(struct krb5_key_state), M_GSSAPI, M_WAITOK);
69184588Sdfr	ks->ks_class = ec;
70184588Sdfr	refcount_init(&ks->ks_refs, 1);
71184588Sdfr	ks->ks_key = malloc(ec->ec_keylen, M_GSSAPI, M_WAITOK);
72184588Sdfr	ec->ec_init(ks);
73184588Sdfr
74184588Sdfr	return (ks);
75184588Sdfr}
76184588Sdfr
77184588Sdfrvoid
78184588Sdfrkrb5_free_key(struct krb5_key_state *ks)
79184588Sdfr{
80184588Sdfr
81184588Sdfr	if (refcount_release(&ks->ks_refs)) {
82184588Sdfr		ks->ks_class->ec_destroy(ks);
83184588Sdfr		bzero(ks->ks_key, ks->ks_class->ec_keylen);
84184588Sdfr		free(ks->ks_key, M_GSSAPI);
85184588Sdfr		free(ks, M_GSSAPI);
86184588Sdfr	}
87184588Sdfr}
88184588Sdfr
89184588Sdfrstatic size_t
90184588Sdfrgcd(size_t a, size_t b)
91184588Sdfr{
92184588Sdfr
93184588Sdfr	if (b == 0)
94184588Sdfr		return (a);
95184588Sdfr	return gcd(b, a % b);
96184588Sdfr}
97184588Sdfr
98184588Sdfrstatic size_t
99184588Sdfrlcm(size_t a, size_t b)
100184588Sdfr{
101184588Sdfr	return ((a * b) / gcd(a, b));
102184588Sdfr}
103184588Sdfr
104184588Sdfr/*
105184588Sdfr * Rotate right 13 of a variable precision number in 'in', storing the
106184588Sdfr * result in 'out'. The number is assumed to be big-endian in memory
107184588Sdfr * representation.
108184588Sdfr */
109184588Sdfrstatic void
110184588Sdfrkrb5_rotate_right_13(uint8_t *out, uint8_t *in, size_t numlen)
111184588Sdfr{
112184588Sdfr	uint32_t carry;
113184588Sdfr	size_t i;
114184588Sdfr
115184588Sdfr	/*
116184588Sdfr	 * Special case when numlen == 1. A rotate right 13 of a
117184588Sdfr	 * single byte number changes to a rotate right 5.
118184588Sdfr	 */
119184588Sdfr	if (numlen == 1) {
120184588Sdfr		carry = in[0] >> 5;
121184588Sdfr		out[0] = (in[0] << 3) | carry;
122184588Sdfr		return;
123184588Sdfr	}
124184588Sdfr
125184588Sdfr	carry = ((in[numlen - 2] & 31) << 8) | in[numlen - 1];
126184588Sdfr	for (i = 2; i < numlen; i++) {
127184588Sdfr		out[i] = ((in[i - 2] & 31) << 3) | (in[i - 1] >> 5);
128184588Sdfr	}
129184588Sdfr	out[1] = ((carry & 31) << 3) | (in[0] >> 5);
130184588Sdfr	out[0] = carry >> 5;
131184588Sdfr}
132184588Sdfr
133184588Sdfr/*
134184588Sdfr * Add two variable precision numbers in big-endian representation
135184588Sdfr * using ones-complement arithmetic.
136184588Sdfr */
137184588Sdfrstatic void
138184588Sdfrkrb5_ones_complement_add(uint8_t *out, const uint8_t *in, size_t len)
139184588Sdfr{
140184588Sdfr	int n, i;
141184588Sdfr
142184588Sdfr	/*
143184588Sdfr	 * First calculate the 2s complement sum, remembering the
144184588Sdfr	 * carry.
145184588Sdfr	 */
146184588Sdfr	n = 0;
147184588Sdfr	for (i = len - 1; i >= 0; i--) {
148184588Sdfr		n = out[i] + in[i] + n;
149184588Sdfr		out[i] = n;
150184588Sdfr		n >>= 8;
151184588Sdfr	}
152184588Sdfr	/*
153184588Sdfr	 * Then add back the carry.
154184588Sdfr	 */
155184588Sdfr	for (i = len - 1; n && i >= 0; i--) {
156184588Sdfr		n = out[i] + n;
157184588Sdfr		out[i] = n;
158184588Sdfr		n >>= 8;
159184588Sdfr	}
160184588Sdfr}
161184588Sdfr
162184588Sdfrstatic void
163184588Sdfrkrb5_n_fold(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen)
164184588Sdfr{
165184588Sdfr	size_t tmplen;
166184588Sdfr	uint8_t *tmp;
167184588Sdfr	size_t i;
168184588Sdfr	uint8_t *p;
169184588Sdfr
170184588Sdfr	tmplen = lcm(inlen, outlen);
171184588Sdfr	tmp = malloc(tmplen, M_GSSAPI, M_WAITOK);
172184588Sdfr
173184588Sdfr	bcopy(in, tmp, inlen);
174184588Sdfr	for (i = inlen, p = tmp; i < tmplen; i += inlen, p += inlen) {
175184588Sdfr		krb5_rotate_right_13(p + inlen, p, inlen);
176184588Sdfr	}
177184588Sdfr	bzero(out, outlen);
178184588Sdfr	for (i = 0, p = tmp; i < tmplen; i += outlen, p += outlen) {
179184588Sdfr		krb5_ones_complement_add(out, p, outlen);
180184588Sdfr	}
181184588Sdfr	free(tmp, M_GSSAPI);
182184588Sdfr}
183184588Sdfr
184184588Sdfrstruct krb5_key_state *
185184588Sdfrkrb5_derive_key(struct krb5_key_state *inkey,
186184588Sdfr    void *constant, size_t constantlen)
187184588Sdfr{
188184588Sdfr	struct krb5_key_state *dk;
189184588Sdfr	const struct krb5_encryption_class *ec = inkey->ks_class;
190184588Sdfr	uint8_t *folded;
191184588Sdfr	uint8_t *bytes, *p, *q;
192184588Sdfr	struct mbuf *m;
193184588Sdfr	int randomlen, i;
194184588Sdfr
195184588Sdfr	/*
196184588Sdfr	 * Expand the constant to blocklen bytes.
197184588Sdfr	 */
198184588Sdfr	folded = malloc(ec->ec_blocklen, M_GSSAPI, M_WAITOK);
199184588Sdfr	krb5_n_fold(folded, ec->ec_blocklen, constant, constantlen);
200184588Sdfr
201184588Sdfr	/*
202184588Sdfr	 * Generate enough bytes for keybits rounded up to a multiple
203184588Sdfr	 * of blocklen.
204184588Sdfr	 */
205184588Sdfr	randomlen = ((ec->ec_keybits/8 + ec->ec_blocklen - 1) / ec->ec_blocklen)
206184588Sdfr		* ec->ec_blocklen;
207184588Sdfr	bytes = malloc(randomlen, M_GSSAPI, M_WAITOK);
208184588Sdfr	MGET(m, M_WAITOK, MT_DATA);
209184588Sdfr	m->m_len = ec->ec_blocklen;
210184588Sdfr	for (i = 0, p = bytes, q = folded; i < randomlen;
211184588Sdfr	     q = p, i += ec->ec_blocklen, p += ec->ec_blocklen) {
212184588Sdfr		bcopy(q, m->m_data, ec->ec_blocklen);
213184588Sdfr		krb5_encrypt(inkey, m, 0, ec->ec_blocklen, NULL, 0);
214184588Sdfr		bcopy(m->m_data, p, ec->ec_blocklen);
215184588Sdfr	}
216184588Sdfr	m_free(m);
217184588Sdfr
218184588Sdfr	dk = krb5_create_key(ec);
219184588Sdfr	krb5_random_to_key(dk, bytes);
220184588Sdfr
221184588Sdfr	free(folded, M_GSSAPI);
222184588Sdfr	free(bytes, M_GSSAPI);
223184588Sdfr
224184588Sdfr	return (dk);
225184588Sdfr}
226184588Sdfr
227184588Sdfrstatic struct krb5_key_state *
228184588Sdfrkrb5_get_usage_key(struct krb5_key_state *basekey, int usage, int which)
229184588Sdfr{
230184588Sdfr	const struct krb5_encryption_class *ec = basekey->ks_class;
231184588Sdfr
232184588Sdfr	if (ec->ec_flags & EC_DERIVED_KEYS) {
233184588Sdfr		uint8_t constant[5];
234184588Sdfr
235184588Sdfr		constant[0] = usage >> 24;
236184588Sdfr		constant[1] = usage >> 16;
237184588Sdfr		constant[2] = usage >> 8;
238184588Sdfr		constant[3] = usage;
239184588Sdfr		constant[4] = which;
240184588Sdfr		return (krb5_derive_key(basekey, constant, 5));
241184588Sdfr	} else {
242184588Sdfr		refcount_acquire(&basekey->ks_refs);
243184588Sdfr		return (basekey);
244184588Sdfr	}
245184588Sdfr}
246184588Sdfr
247184588Sdfrstruct krb5_key_state *
248184588Sdfrkrb5_get_encryption_key(struct krb5_key_state *basekey, int usage)
249184588Sdfr{
250184588Sdfr
251184588Sdfr	return (krb5_get_usage_key(basekey, usage, 0xaa));
252184588Sdfr}
253184588Sdfr
254184588Sdfrstruct krb5_key_state *
255184588Sdfrkrb5_get_integrity_key(struct krb5_key_state *basekey, int usage)
256184588Sdfr{
257184588Sdfr
258184588Sdfr	return (krb5_get_usage_key(basekey, usage, 0x55));
259184588Sdfr}
260184588Sdfr
261184588Sdfrstruct krb5_key_state *
262184588Sdfrkrb5_get_checksum_key(struct krb5_key_state *basekey, int usage)
263184588Sdfr{
264184588Sdfr
265184588Sdfr	return (krb5_get_usage_key(basekey, usage, 0x99));
266184588Sdfr}
267