summaryrefslogtreecommitdiffstats
path: root/sys/kgssapi/krb5/kcrypto.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kgssapi/krb5/kcrypto.c')
-rw-r--r--sys/kgssapi/krb5/kcrypto.c266
1 files changed, 266 insertions, 0 deletions
diff --git a/sys/kgssapi/krb5/kcrypto.c b/sys/kgssapi/krb5/kcrypto.c
new file mode 100644
index 0000000..27ee3ec
--- /dev/null
+++ b/sys/kgssapi/krb5/kcrypto.c
@@ -0,0 +1,266 @@
+/*-
+ * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+ * Authors: Doug Rabson <dfr@rabson.org>
+ * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/kobj.h>
+#include <sys/mbuf.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+#include "kcrypto.h"
+
+static struct krb5_encryption_class *krb5_encryption_classes[] = {
+ &krb5_des_encryption_class,
+ &krb5_des3_encryption_class,
+ &krb5_aes128_encryption_class,
+ &krb5_aes256_encryption_class,
+ &krb5_arcfour_encryption_class,
+ &krb5_arcfour_56_encryption_class,
+ NULL
+};
+
+struct krb5_encryption_class *
+krb5_find_encryption_class(int etype)
+{
+ int i;
+
+ for (i = 0; krb5_encryption_classes[i]; i++) {
+ if (krb5_encryption_classes[i]->ec_type == etype)
+ return (krb5_encryption_classes[i]);
+ }
+ return (NULL);
+}
+
+struct krb5_key_state *
+krb5_create_key(const struct krb5_encryption_class *ec)
+{
+ struct krb5_key_state *ks;
+
+ ks = malloc(sizeof(struct krb5_key_state), M_GSSAPI, M_WAITOK);
+ ks->ks_class = ec;
+ refcount_init(&ks->ks_refs, 1);
+ ks->ks_key = malloc(ec->ec_keylen, M_GSSAPI, M_WAITOK);
+ ec->ec_init(ks);
+
+ return (ks);
+}
+
+void
+krb5_free_key(struct krb5_key_state *ks)
+{
+
+ if (refcount_release(&ks->ks_refs)) {
+ ks->ks_class->ec_destroy(ks);
+ bzero(ks->ks_key, ks->ks_class->ec_keylen);
+ free(ks->ks_key, M_GSSAPI);
+ free(ks, M_GSSAPI);
+ }
+}
+
+static size_t
+gcd(size_t a, size_t b)
+{
+
+ if (b == 0)
+ return (a);
+ return gcd(b, a % b);
+}
+
+static size_t
+lcm(size_t a, size_t b)
+{
+ return ((a * b) / gcd(a, b));
+}
+
+/*
+ * Rotate right 13 of a variable precision number in 'in', storing the
+ * result in 'out'. The number is assumed to be big-endian in memory
+ * representation.
+ */
+static void
+krb5_rotate_right_13(uint8_t *out, uint8_t *in, size_t numlen)
+{
+ uint32_t carry;
+ size_t i;
+
+ /*
+ * Special case when numlen == 1. A rotate right 13 of a
+ * single byte number changes to a rotate right 5.
+ */
+ if (numlen == 1) {
+ carry = in[0] >> 5;
+ out[0] = (in[0] << 3) | carry;
+ return;
+ }
+
+ carry = ((in[numlen - 2] & 31) << 8) | in[numlen - 1];
+ for (i = 2; i < numlen; i++) {
+ out[i] = ((in[i - 2] & 31) << 3) | (in[i - 1] >> 5);
+ }
+ out[1] = ((carry & 31) << 3) | (in[0] >> 5);
+ out[0] = carry >> 5;
+}
+
+/*
+ * Add two variable precision numbers in big-endian representation
+ * using ones-complement arithmetic.
+ */
+static void
+krb5_ones_complement_add(uint8_t *out, const uint8_t *in, size_t len)
+{
+ int n, i;
+
+ /*
+ * First calculate the 2s complement sum, remembering the
+ * carry.
+ */
+ n = 0;
+ for (i = len - 1; i >= 0; i--) {
+ n = out[i] + in[i] + n;
+ out[i] = n;
+ n >>= 8;
+ }
+ /*
+ * Then add back the carry.
+ */
+ for (i = len - 1; n && i >= 0; i--) {
+ n = out[i] + n;
+ out[i] = n;
+ n >>= 8;
+ }
+}
+
+static void
+krb5_n_fold(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen)
+{
+ size_t tmplen;
+ uint8_t *tmp;
+ size_t i;
+ uint8_t *p;
+
+ tmplen = lcm(inlen, outlen);
+ tmp = malloc(tmplen, M_GSSAPI, M_WAITOK);
+
+ bcopy(in, tmp, inlen);
+ for (i = inlen, p = tmp; i < tmplen; i += inlen, p += inlen) {
+ krb5_rotate_right_13(p + inlen, p, inlen);
+ }
+ bzero(out, outlen);
+ for (i = 0, p = tmp; i < tmplen; i += outlen, p += outlen) {
+ krb5_ones_complement_add(out, p, outlen);
+ }
+ free(tmp, M_GSSAPI);
+}
+
+struct krb5_key_state *
+krb5_derive_key(struct krb5_key_state *inkey,
+ void *constant, size_t constantlen)
+{
+ struct krb5_key_state *dk;
+ const struct krb5_encryption_class *ec = inkey->ks_class;
+ uint8_t *folded;
+ uint8_t *bytes, *p, *q;
+ struct mbuf *m;
+ int randomlen, i;
+
+ /*
+ * Expand the constant to blocklen bytes.
+ */
+ folded = malloc(ec->ec_blocklen, M_GSSAPI, M_WAITOK);
+ krb5_n_fold(folded, ec->ec_blocklen, constant, constantlen);
+
+ /*
+ * Generate enough bytes for keybits rounded up to a multiple
+ * of blocklen.
+ */
+ randomlen = ((ec->ec_keybits/8 + ec->ec_blocklen - 1) / ec->ec_blocklen)
+ * ec->ec_blocklen;
+ bytes = malloc(randomlen, M_GSSAPI, M_WAITOK);
+ MGET(m, M_WAITOK, MT_DATA);
+ m->m_len = ec->ec_blocklen;
+ for (i = 0, p = bytes, q = folded; i < randomlen;
+ q = p, i += ec->ec_blocklen, p += ec->ec_blocklen) {
+ bcopy(q, m->m_data, ec->ec_blocklen);
+ krb5_encrypt(inkey, m, 0, ec->ec_blocklen, NULL, 0);
+ bcopy(m->m_data, p, ec->ec_blocklen);
+ }
+ m_free(m);
+
+ dk = krb5_create_key(ec);
+ krb5_random_to_key(dk, bytes);
+
+ free(folded, M_GSSAPI);
+ free(bytes, M_GSSAPI);
+
+ return (dk);
+}
+
+static struct krb5_key_state *
+krb5_get_usage_key(struct krb5_key_state *basekey, int usage, int which)
+{
+ const struct krb5_encryption_class *ec = basekey->ks_class;
+
+ if (ec->ec_flags & EC_DERIVED_KEYS) {
+ uint8_t constant[5];
+
+ constant[0] = usage >> 24;
+ constant[1] = usage >> 16;
+ constant[2] = usage >> 8;
+ constant[3] = usage;
+ constant[4] = which;
+ return (krb5_derive_key(basekey, constant, 5));
+ } else {
+ refcount_acquire(&basekey->ks_refs);
+ return (basekey);
+ }
+}
+
+struct krb5_key_state *
+krb5_get_encryption_key(struct krb5_key_state *basekey, int usage)
+{
+
+ return (krb5_get_usage_key(basekey, usage, 0xaa));
+}
+
+struct krb5_key_state *
+krb5_get_integrity_key(struct krb5_key_state *basekey, int usage)
+{
+
+ return (krb5_get_usage_key(basekey, usage, 0x55));
+}
+
+struct krb5_key_state *
+krb5_get_checksum_key(struct krb5_key_state *basekey, int usage)
+{
+
+ return (krb5_get_usage_key(basekey, usage, 0x99));
+}
OpenPOWER on IntegriCloud