aboutsummaryrefslogtreecommitdiff
path: root/src/lib/crypto/krb/dk
diff options
context:
space:
mode:
authorGreg Hudson <ghudson@mit.edu>2010-09-30 21:57:42 +0000
committerGreg Hudson <ghudson@mit.edu>2010-09-30 21:57:42 +0000
commit267657197005c84ec8af9a030b23d14f3be68f32 (patch)
treed927e631ca038c256159e50835bcec5d87135be9 /src/lib/crypto/krb/dk
parenta787dec7564f865343ed511cd1710d3847d8778b (diff)
downloadkrb5-267657197005c84ec8af9a030b23d14f3be68f32.zip
krb5-267657197005c84ec8af9a030b23d14f3be68f32.tar.gz
krb5-267657197005c84ec8af9a030b23d14f3be68f32.tar.bz2
Merge r24242:24397 from trunk to branches/nss. This will break the
nss branch build temporarily due to Camellia changes. git-svn-id: svn://anonsvn.mit.edu/krb5/branches/nss@24398 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/lib/crypto/krb/dk')
-rw-r--r--src/lib/crypto/krb/dk/Makefile.in12
-rw-r--r--src/lib/crypto/krb/dk/checksum_cmac.c66
-rw-r--r--src/lib/crypto/krb/dk/checksum_hmac.c (renamed from src/lib/crypto/krb/dk/checksum.c)2
-rw-r--r--src/lib/crypto/krb/dk/deps48
-rw-r--r--src/lib/crypto/krb/dk/derive.c128
-rw-r--r--src/lib/crypto/krb/dk/dk.h60
-rw-r--r--src/lib/crypto/krb/dk/dk_aead.c8
-rw-r--r--src/lib/crypto/krb/dk/dk_ccm.c614
-rw-r--r--src/lib/crypto/krb/dk/stringtokey.c57
9 files changed, 944 insertions, 51 deletions
diff --git a/src/lib/crypto/krb/dk/Makefile.in b/src/lib/crypto/krb/dk/Makefile.in
index 8d65857..09df6c3 100644
--- a/src/lib/crypto/krb/dk/Makefile.in
+++ b/src/lib/crypto/krb/dk/Makefile.in
@@ -11,20 +11,26 @@ PROG_LIBPATH=-L$(TOPLIBD)
PROG_RPATH=$(KRB5_LIBDIR)
STLIBOBJS=\
- checksum.o \
+ checksum_hmac.o \
+ checksum_cmac.o \
dk_aead.o \
+ dk_ccm.o \
derive.o \
stringtokey.o
OBJS=\
- $(OUTPRE)checksum.$(OBJEXT) \
+ $(OUTPRE)checksum_hmac.$(OBJEXT)\
+ $(OUTPRE)checksum_cmac.$(OBJEXT)\
$(OUTPRE)dk_aead.$(OBJEXT) \
+ $(OUTPRE)dk_ccm.$(OBJEXT) \
$(OUTPRE)derive.$(OBJEXT) \
$(OUTPRE)stringtokey.$(OBJEXT)
SRCS=\
- $(srcdir)/checksum.c \
+ $(srcdir)/checksum_hmac.c \
+ $(srcdir)/checksum_cmac.c \
$(srcdir)/dk_aead.c \
+ $(srcdir)/dk_ccm.c \
$(srcdir)/derive.c \
$(srcdir)/stringtokey.c
diff --git a/src/lib/crypto/krb/dk/checksum_cmac.c b/src/lib/crypto/krb/dk/checksum_cmac.c
new file mode 100644
index 0000000..c2309b7
--- /dev/null
+++ b/src/lib/crypto/krb/dk/checksum_cmac.c
@@ -0,0 +1,66 @@
+/*
+ * lib/crypto/krb/dk/checksum_cmac.c
+ *
+ * Copyright 2010 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include "k5-int.h"
+#include "etypes.h"
+#include "dk.h"
+#include "aead.h"
+#include "cksumtypes.h"
+
+#define K5CLENGTH 5 /* 32 bit net byte order integer + one byte seed */
+
+#ifdef CAMELLIA_CCM
+
+krb5_error_code
+krb5int_dk_cmac_checksum(const struct krb5_cksumtypes *ctp,
+ krb5_key key, krb5_keyusage usage,
+ const krb5_crypto_iov *data, size_t num_data,
+ krb5_data *output)
+{
+ const struct krb5_enc_provider *enc = ctp->enc;
+ krb5_error_code ret;
+ unsigned char constantdata[K5CLENGTH];
+ krb5_data datain;
+ krb5_key kc;
+
+ /* Derive the key. */
+ datain = make_data(constantdata, K5CLENGTH);
+ store_32_be(usage, constantdata);
+ constantdata[4] = (char) 0x99;
+ ret = krb5int_derive_key(enc, key, &kc, &datain, DERIVE_SP800_108_CMAC);
+ if (ret != 0)
+ return ret;
+
+ /* Hash the data. */
+ ret = krb5int_cmac_checksum(enc, kc, data, num_data, output);
+ if (ret != 0)
+ memset(output->data, 0, output->length);
+
+ krb5_k_free_key(NULL, kc);
+ return ret;
+}
+
+#endif /* CAMELLIA_CCM */
diff --git a/src/lib/crypto/krb/dk/checksum.c b/src/lib/crypto/krb/dk/checksum_hmac.c
index 3dbde10..ae51aa3 100644
--- a/src/lib/crypto/krb/dk/checksum.c
+++ b/src/lib/crypto/krb/dk/checksum_hmac.c
@@ -57,7 +57,7 @@ krb5int_dk_checksum(const struct krb5_cksumtypes *ctp,
datain = make_data(constantdata, K5CLENGTH);
store_32_be(usage, constantdata);
constantdata[4] = (char) 0x99;
- ret = krb5int_derive_key(enc, key, &kc, &datain);
+ ret = krb5int_derive_key(enc, key, &kc, &datain, DERIVE_RFC3961);
if (ret)
return ret;
diff --git a/src/lib/crypto/krb/dk/deps b/src/lib/crypto/krb/dk/deps
index 947b73a..a7b610f 100644
--- a/src/lib/crypto/krb/dk/deps
+++ b/src/lib/crypto/krb/dk/deps
@@ -1,7 +1,7 @@
#
# Generated makefile dependencies follow.
#
-checksum.so checksum.po $(OUTPRE)checksum.$(OBJEXT): \
+checksum_hmac.so checksum_hmac.po $(OUTPRE)checksum_hmac.$(OBJEXT): \
$(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
$(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
$(COM_ERR_DEPS) $(srcdir)/../cksumtypes.h $(srcdir)/../etypes.h \
@@ -11,8 +11,23 @@ checksum.so checksum.po $(OUTPRE)checksum.$(OBJEXT): \
$(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
$(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
$(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/locate_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/krb5/preauth_plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ checksum_hmac.c dk.h
+checksum_cmac.so checksum_cmac.po $(OUTPRE)checksum_cmac.$(OBJEXT): \
+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(srcdir)/../aead.h $(srcdir)/../cksumtypes.h \
+ $(srcdir)/../etypes.h $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/locate_plugin.h $(top_srcdir)/include/krb5/plugin.h \
$(top_srcdir)/include/krb5/preauth_plugin.h $(top_srcdir)/include/port-sockets.h \
- $(top_srcdir)/include/socket-utils.h checksum.c dk.h
+ $(top_srcdir)/include/socket-utils.h checksum_cmac.c \
+ dk.h
dk_aead.so dk_aead.po $(OUTPRE)dk_aead.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
$(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../aead.h \
@@ -22,9 +37,21 @@ dk_aead.so dk_aead.po $(OUTPRE)dk_aead.$(OBJEXT): $(BUILDTOP)/include/autoconf.h
$(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
$(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
$(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
- $(top_srcdir)/include/krb5/locate_plugin.h $(top_srcdir)/include/krb5/preauth_plugin.h \
- $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
- dk.h dk_aead.c
+ $(top_srcdir)/include/krb5/locate_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/krb5/preauth_plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h dk.h dk_aead.c
+dk_ccm.so dk_ccm.po $(OUTPRE)dk_ccm.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../aead.h \
+ $(srcdir)/../cksumtypes.h $(srcdir)/../etypes.h $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/locate_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/krb5/preauth_plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h dk.h dk_ccm.c
derive.so derive.po $(OUTPRE)derive.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
$(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../cksumtypes.h \
@@ -34,9 +61,9 @@ derive.so derive.po $(OUTPRE)derive.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
$(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
$(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
- $(top_srcdir)/include/krb5/locate_plugin.h $(top_srcdir)/include/krb5/preauth_plugin.h \
- $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
- derive.c dk.h
+ $(top_srcdir)/include/krb5/locate_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/krb5/preauth_plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h derive.c dk.h
stringtokey.so stringtokey.po $(OUTPRE)stringtokey.$(OBJEXT): \
$(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
$(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
@@ -47,5 +74,6 @@ stringtokey.so stringtokey.po $(OUTPRE)stringtokey.$(OBJEXT): \
$(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
$(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
$(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/locate_plugin.h \
- $(top_srcdir)/include/krb5/preauth_plugin.h $(top_srcdir)/include/port-sockets.h \
- $(top_srcdir)/include/socket-utils.h dk.h stringtokey.c
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/krb5/preauth_plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ dk.h stringtokey.c
diff --git a/src/lib/crypto/krb/dk/derive.c b/src/lib/crypto/krb/dk/derive.c
index 5fd8876..a7ad2e3 100644
--- a/src/lib/crypto/krb/dk/derive.c
+++ b/src/lib/crypto/krb/dk/derive.c
@@ -79,14 +79,14 @@ cleanup:
return ENOMEM;
}
-krb5_error_code
-krb5int_derive_random(const struct krb5_enc_provider *enc,
+static krb5_error_code
+derive_random_rfc3961(const struct krb5_enc_provider *enc,
krb5_key inkey, krb5_data *outrnd,
const krb5_data *in_constant)
{
size_t blocksize, keybytes, n;
- krb5_crypto_iov iov;
krb5_error_code ret;
+ krb5_data block = empty_data();
blocksize = enc->block_size;
keybytes = enc->keybytes;
@@ -95,41 +95,136 @@ krb5int_derive_random(const struct krb5_enc_provider *enc,
return KRB5_CRYPTO_INTERNAL;
/* Allocate encryption data buffer. */
- iov.flags = KRB5_CRYPTO_TYPE_DATA;
- ret = alloc_data(&iov.data, blocksize);
+ ret = alloc_data(&block, blocksize);
if (ret)
return ret;
/* Initialize the input block. */
if (in_constant->length == blocksize) {
- memcpy(iov.data.data, in_constant->data, blocksize);
+ memcpy(block.data, in_constant->data, blocksize);
} else {
krb5int_nfold(in_constant->length * 8,
(unsigned char *) in_constant->data,
- blocksize * 8, (unsigned char *) iov.data.data);
+ blocksize * 8, (unsigned char *) block.data);
}
/* Loop encrypting the blocks until enough key bytes are generated. */
n = 0;
while (n < keybytes) {
- ret = enc->encrypt(inkey, 0, &iov, 1);
+ ret = encrypt_block(enc, inkey, &block);
if (ret)
goto cleanup;
if ((keybytes - n) <= blocksize) {
- memcpy(outrnd->data + n, iov.data.data, (keybytes - n));
+ memcpy(outrnd->data + n, block.data, (keybytes - n));
break;
}
- memcpy(outrnd->data + n, iov.data.data, blocksize);
+ memcpy(outrnd->data + n, block.data, blocksize);
n += blocksize;
}
cleanup:
- zapfree(iov.data.data, blocksize);
+ zapfree(block.data, blocksize);
return ret;
}
+#ifdef CAMELLIA_CCM
+
+/*
+ * NIST SP800-108 KDF in feedback mode (section 5.2).
+ * Parameters:
+ * - CMAC (with enc as the enc provider) is the PRF.
+ * - A block counter of four bytes is used.
+ * - Label is the key derivation constant.
+ * - Context is empty.
+ * - Four bytes are used to encode the output length in the PRF input.
+ */
+static krb5_error_code
+derive_random_sp800_108_cmac(const struct krb5_enc_provider *enc,
+ krb5_key inkey, krb5_data *outrnd,
+ const krb5_data *in_constant)
+{
+ size_t blocksize, keybytes, n;
+ krb5_crypto_iov iov[6];
+ krb5_error_code ret;
+ krb5_data prf;
+ unsigned int i;
+ unsigned char ibuf[4], Lbuf[4];
+
+ blocksize = enc->block_size;
+ keybytes = enc->keybytes;
+
+ if (inkey->keyblock.length != enc->keylength || outrnd->length != keybytes)
+ return KRB5_CRYPTO_INTERNAL;
+
+ /* Allocate encryption data buffer. */
+ ret = alloc_data(&prf, blocksize);
+ if (ret)
+ return ret;
+
+ /* K(i-1): the previous block of PRF output, initially all-zeros. */
+ iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
+ iov[0].data = prf;
+ /* [i]2: four-byte big-endian binary string giving the block counter */
+ iov[1].flags = KRB5_CRYPTO_TYPE_DATA;
+ iov[1].data = make_data(ibuf, sizeof(ibuf));
+ /* Label: the fixed derived-key input */
+ iov[2].flags = KRB5_CRYPTO_TYPE_DATA;
+ iov[2].data = *in_constant;
+ /* 0x00: separator byte */
+ iov[3].flags = KRB5_CRYPTO_TYPE_DATA;
+ iov[3].data = make_data("", 1);
+ /* Context: (unused) */
+ iov[4].flags = KRB5_CRYPTO_TYPE_DATA;
+ iov[4].data = empty_data();
+ /* [L]2: four-byte big-endian binary string giving the output length */
+ iov[5].flags = KRB5_CRYPTO_TYPE_DATA;
+ iov[5].data = make_data(Lbuf, sizeof(Lbuf));
+ store_32_be(outrnd->length, Lbuf);
+
+ for (i = 1, n = 0; n < keybytes; i++) {
+ /* Update the block counter. */
+ store_32_be(i, ibuf);
+
+ /* Compute a CMAC checksum, storing the result into K(i-1). */
+ ret = krb5int_cmac_checksum(enc, inkey, iov, 6, &prf);
+ if (ret)
+ goto cleanup;
+
+ /* Copy the result into the appropriate part of the output buffer. */
+ if (keybytes - n <= blocksize) {
+ memcpy(outrnd->data + n, prf.data, keybytes - n);
+ break;
+ }
+ memcpy(outrnd->data + n, prf.data, blocksize);
+ n += blocksize;
+ }
+
+cleanup:
+ zapfree(prf.data, blocksize);
+ return ret;
+}
+
+#endif /* CAMELLIA_CCM */
+
+krb5_error_code
+krb5int_derive_random(const struct krb5_enc_provider *enc,
+ krb5_key inkey, krb5_data *outrnd,
+ const krb5_data *in_constant, enum deriv_alg alg)
+{
+ switch (alg) {
+ case DERIVE_RFC3961:
+ return derive_random_rfc3961(enc, inkey, outrnd, in_constant);
+#ifdef CAMELLIA_CCM
+ case DERIVE_SP800_108_CMAC:
+ return derive_random_sp800_108_cmac(enc, inkey, outrnd, in_constant);
+#endif
+ default:
+ return EINVAL;
+ }
+}
+
/*
* Compute a derived key into the keyblock outkey. This variation on
* krb5int_derive_key does not cache the result, as it is only used
@@ -139,7 +234,7 @@ cleanup:
krb5_error_code
krb5int_derive_keyblock(const struct krb5_enc_provider *enc,
krb5_key inkey, krb5_keyblock *outkey,
- const krb5_data *in_constant)
+ const krb5_data *in_constant, enum deriv_alg alg)
{
krb5_error_code ret;
krb5_data rawkey = empty_data();
@@ -150,7 +245,7 @@ krb5int_derive_keyblock(const struct krb5_enc_provider *enc,
goto cleanup;
/* Derive pseudo-random data for the key bytes. */
- ret = krb5int_derive_random(enc, inkey, &rawkey, in_constant);
+ ret = krb5int_derive_random(enc, inkey, &rawkey, in_constant, alg);
if (ret)
goto cleanup;
@@ -165,7 +260,7 @@ cleanup:
krb5_error_code
krb5int_derive_key(const struct krb5_enc_provider *enc,
krb5_key inkey, krb5_key *outkey,
- const krb5_data *in_constant)
+ const krb5_data *in_constant, enum deriv_alg alg)
{
krb5_keyblock keyblock;
krb5_error_code ret;
@@ -183,13 +278,10 @@ krb5int_derive_key(const struct krb5_enc_provider *enc,
/* Derive into a temporary keyblock. */
keyblock.length = enc->keylength;
keyblock.contents = malloc(keyblock.length);
- /* Set the enctype as the krb5_k_free_key will iterate over list
- or derived keys and invoke krb5_k_free_key which will lookup
- the enctype for key_cleanup handler */
keyblock.enctype = inkey->keyblock.enctype;
if (keyblock.contents == NULL)
return ENOMEM;
- ret = krb5int_derive_keyblock(enc, inkey, &keyblock, in_constant);
+ ret = krb5int_derive_keyblock(enc, inkey, &keyblock, in_constant, alg);
if (ret)
goto cleanup;
diff --git a/src/lib/crypto/krb/dk/dk.h b/src/lib/crypto/krb/dk/dk.h
index 0fdd984..fb6df88 100644
--- a/src/lib/crypto/krb/dk/dk.h
+++ b/src/lib/crypto/krb/dk/dk.h
@@ -58,16 +58,28 @@ krb5int_aes_string_to_key(const struct krb5_keytypes *enc,
const krb5_data *params, krb5_keyblock *key);
krb5_error_code
+krb5int_camellia_ccm_string_to_key(const struct krb5_keytypes *enc,
+ const krb5_data *string,
+ const krb5_data *salt,
+ const krb5_data *params,
+ krb5_keyblock *key);
+
+enum deriv_alg {
+ DERIVE_RFC3961, /* RFC 3961 section 5.1 */
+#ifdef CAMELLIA_CCM
+ DERIVE_SP800_108_CMAC /* NIST SP 800-108 with CMAC as PRF */
+#endif
+};
+
+krb5_error_code
krb5int_derive_keyblock(const struct krb5_enc_provider *enc,
- krb5_key inkey,
- krb5_keyblock *outkey,
- const krb5_data *in_constant);
+ krb5_key inkey, krb5_keyblock *outkey,
+ const krb5_data *in_constant, enum deriv_alg alg);
krb5_error_code
krb5int_derive_key(const struct krb5_enc_provider *enc,
- krb5_key inkey,
- krb5_key *outkey,
- const krb5_data *in_constant);
+ krb5_key inkey, krb5_key *outkey,
+ const krb5_data *in_constant, enum deriv_alg alg);
krb5_error_code
krb5int_dk_checksum(const struct krb5_cksumtypes *ctp,
@@ -78,4 +90,38 @@ krb5int_dk_checksum(const struct krb5_cksumtypes *ctp,
krb5_error_code
krb5int_derive_random(const struct krb5_enc_provider *enc,
krb5_key inkey, krb5_data *outrnd,
- const krb5_data *in_constant);
+ const krb5_data *in_constant, enum deriv_alg alg);
+
+unsigned int
+krb5int_dk_ccm_crypto_length(const struct krb5_keytypes *ktp,
+ krb5_cryptotype type);
+
+krb5_error_code
+krb5int_dk_ccm_encrypt(const struct krb5_keytypes *ktp,
+ krb5_key key,
+ krb5_keyusage usage,
+ const krb5_data *ivec,
+ krb5_crypto_iov *data,
+ size_t num_data);
+
+krb5_error_code
+krb5int_dk_ccm_decrypt(const struct krb5_keytypes *ktp,
+ krb5_key key,
+ krb5_keyusage usage,
+ const krb5_data *ivec,
+ krb5_crypto_iov *data,
+ size_t num_data);
+
+krb5_error_code
+krb5int_dk_cmac_checksum(const struct krb5_cksumtypes *ctp,
+ krb5_key key, krb5_keyusage usage,
+ const krb5_crypto_iov *data, size_t num_data,
+ krb5_data *output);
+
+krb5_error_code
+krb5int_dk_ccm_init_state(const struct krb5_keytypes *ktp,
+ const krb5_keyblock *key, krb5_keyusage usage,
+ krb5_data *out_state);
+
+void
+krb5int_dk_ccm_free_state(const struct krb5_keytypes *ktp, krb5_data *state);
diff --git a/src/lib/crypto/krb/dk/dk_aead.c b/src/lib/crypto/krb/dk/dk_aead.c
index f44ae84..4e9a7c4 100644
--- a/src/lib/crypto/krb/dk/dk_aead.c
+++ b/src/lib/crypto/krb/dk/dk_aead.c
@@ -134,13 +134,13 @@ krb5int_dk_encrypt(const struct krb5_keytypes *ktp, krb5_key key,
d1.data[4] = 0xAA;
- ret = krb5int_derive_key(enc, key, &ke, &d1);
+ ret = krb5int_derive_key(enc, key, &ke, &d1, DERIVE_RFC3961);
if (ret != 0)
goto cleanup;
d1.data[4] = 0x55;
- ret = krb5int_derive_key(enc, key, &ki, &d1);
+ ret = krb5int_derive_key(enc, key, &ki, &d1, DERIVE_RFC3961);
if (ret != 0)
goto cleanup;
@@ -235,13 +235,13 @@ krb5int_dk_decrypt(const struct krb5_keytypes *ktp, krb5_key key,
d1.data[4] = 0xAA;
- ret = krb5int_derive_key(enc, key, &ke, &d1);
+ ret = krb5int_derive_key(enc, key, &ke, &d1, DERIVE_RFC3961);
if (ret != 0)
goto cleanup;
d1.data[4] = 0x55;
- ret = krb5int_derive_key(enc, key, &ki, &d1);
+ ret = krb5int_derive_key(enc, key, &ki, &d1, DERIVE_RFC3961);
if (ret != 0)
goto cleanup;
diff --git a/src/lib/crypto/krb/dk/dk_ccm.c b/src/lib/crypto/krb/dk/dk_ccm.c
new file mode 100644
index 0000000..284e362
--- /dev/null
+++ b/src/lib/crypto/krb/dk/dk_ccm.c
@@ -0,0 +1,614 @@
+/*
+ * lib/crypto/krb/dk/dk_ccm.c
+ *
+ * Copyright 2008-2010 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include "k5-int.h"
+#include "dk.h"
+#include "aead.h"
+
+#ifdef CAMELLIA_CCM
+
+/*
+ * Implement CCM-mode AEAD as described in section 5.3 and 5.4 of RFC 5116.
+ * This is the CCM mode as described in NIST SP800-38C, with a 12 byte nonce
+ * and 16 byte checksum. Multiple buffers of the same type are logically
+ * concatenated. The underlying enc provider must have a 16-byte block size,
+ * must have a counter-mode encrypt method, and must have a cbc_mac method.
+ *
+ * The IOV should be laid out as follows:
+ *
+ * HEADER | SIGN_DATA | DATA | PADDING | TRAILER
+ *
+ * SIGN_DATA and PADDING may be absent.
+ *
+ * Upon decryption, one can pass in explicit buffers as for encryption, or one
+ * can pass in STREAM, being the concatenation of HEADER | DATA | TRAILER.
+ *
+ * STREAM | SIGN_DATA | DATA
+ *
+ * Upon output, DATA will contain a pointer into the STREAM buffer with the
+ * decrypted payload. SIGN_DATA should be ordered relative to the output DATA
+ * buffer as it was upon encryption.
+ *
+ * For compatibility with RFC 5116, a single key is used both for encryption
+ * and checksumming. The key derivation function is as follows:
+ *
+ * Kc = DK(base-key, usage | 0xCC)
+ *
+ * Again as required by the CCM specification, SIGN_DATA is processed before
+ * DATA for the purpose of checksumming.
+ */
+
+#define K5CLENGTH 5 /* 32 bit net byte order integer + one byte seed */
+
+unsigned int
+krb5int_dk_ccm_crypto_length(const struct krb5_keytypes *ktp,
+ krb5_cryptotype type)
+{
+ unsigned int length;
+
+ switch (type) {
+ case KRB5_CRYPTO_TYPE_HEADER:
+ length = 12; /* RFC 5116 5.3 */
+ break;
+ case KRB5_CRYPTO_TYPE_PADDING:
+ length = 0; /* CTR mode requires no padding */
+ break;
+ case KRB5_CRYPTO_TYPE_TRAILER:
+ case KRB5_CRYPTO_TYPE_CHECKSUM:
+ length = ktp->enc->block_size;
+ break;
+ default:
+ assert(0 && "invalid cryptotype passed to ccm_crypto_length");
+ length = ~0;
+ break;
+ }
+
+ return length;
+}
+
+/*
+ * Encode the length of the additional data according to NIST SP800-38C section
+ * A.2.2. The size of the encoding will be 0, 2, 6, or 10 bytes depending on
+ * the length value.
+ */
+static krb5_error_code
+encode_a_len(krb5_data *a, krb5_ui_8 adata_len)
+{
+ size_t len;
+ unsigned char *p;
+
+ if (adata_len > (1LL << 32))
+ len = 10;
+ else if (adata_len > (1LL << 16) - (1LL << 8))
+ len = 6;
+ else if (adata_len)
+ len = 2;
+ else
+ len = 0;
+
+ if (a->length < len)
+ return KRB5_BAD_MSIZE;
+
+ p = (unsigned char *)a->data;
+
+ switch (len) {
+ case 2:
+ /* Two raw bytes; first byte will not be 0xFF. */
+ p[0] = (adata_len >> 8) & 0xFF;
+ p[1] = (adata_len ) & 0xFF;
+ break;
+ case 6:
+ /* FF FE followed by four bytes. */
+ p[0] = 0xFF;
+ p[1] = 0xFE;
+ p[2] = (adata_len >> 24) & 0xFF;
+ p[3] = (adata_len >> 16) & 0xFF;
+ p[4] = (adata_len >> 8 ) & 0xFF;
+ p[5] = (adata_len ) & 0xFF;
+ break;
+ case 10:
+ /* FF FF followed by eight bytes. */
+ p[0] = 0xFF;
+ p[1] = 0xFF;
+ p[2] = (adata_len >> 56) & 0xFF;
+ p[3] = (adata_len >> 48) & 0xFF;
+ p[4] = (adata_len >> 40) & 0xFF;
+ p[5] = (adata_len >> 32) & 0xFF;
+ p[6] = (adata_len >> 24) & 0xFF;
+ p[7] = (adata_len >> 16) & 0xFF;
+ p[8] = (adata_len >> 8 ) & 0xFF;
+ p[9] = (adata_len ) & 0xFF;
+ break;
+ }
+
+ a->length = len;
+
+ return 0;
+}
+
+/*
+ * Encode the first 16-byte block of CBC-MAC input according to NIST SP800-38C
+ * section A.2.1. n (the nonce length) is given by nonce->length.
+ */
+static krb5_error_code
+format_B0(krb5_data *B0, /* B0 */
+ krb5_data *nonce, /* N */
+ size_t trailer_len, /* t */
+ krb5_ui_8 adata_len, /* a */
+ krb5_ui_8 payload_len) /* Q */
+{
+ unsigned char flags;
+ unsigned char *p;
+ krb5_octet q, i = 0;
+
+ if (B0->length != 16)
+ return KRB5_BAD_MSIZE;
+
+ /* Section A.1: Length Requirements */
+
+ /* t is an element of {4, 6, 8, 10, 12, 14, 16}. */
+ if (trailer_len % 2 ||
+ (trailer_len < 4 || trailer_len > 16))
+ return KRB5_BAD_MSIZE;
+
+ /* n is an element of {7, 8, 9, 10, 11, 12, 13}. */
+ if (nonce->length < 7 || nonce->length > 13)
+ return KRB5_BAD_MSIZE;
+
+ q = 15 - nonce->length;
+
+ /* P consists of fewer than 2^(8q) octets. */
+ if (payload_len >= (1UL << (8 * q)))
+ return KRB5_BAD_MSIZE;
+
+ /* Encode the flags octet. */
+ flags = q - 1;
+ flags |= (((trailer_len - 2) / 2) << 3);
+ if (adata_len != 0)
+ flags |= (1 << 6);
+
+ p = (unsigned char *)B0->data;
+ p[i++] = flags;
+
+ /* Next comes the nonce (n bytes). */
+ memcpy(&p[i], nonce->data, nonce->length);
+ i += nonce->length;
+
+ /* The final q bytes are the payload length. */
+ for (; i < B0->length; i++) {
+ register krb5_octet s;
+
+ s = (q - (i - nonce->length)) * 8;
+
+ p[i] = (payload_len >> s) & 0xFF;
+ }
+
+ return 0;
+}
+
+/*
+ * Encode the initial counter block according to NIST SP800-38C section A.3.
+ * The counter value may be chained across krb5_k_encrypt invocations via the
+ * cipher_state parameter; otherwise it begins at 0.
+ */
+static krb5_error_code
+format_Ctr0(krb5_data *counter, const krb5_data *nonce, const krb5_data *state,
+ unsigned int n)
+{
+ krb5_octet q; /* counter length */
+
+ assert(n >= 7 && n <= 13);
+
+ /* First byte is q-1 in the lowest three bits. */
+ q = 15 - n;
+ counter->data[0] = q - 1;
+ /* Next comes the nonce (n bytes). */
+ memcpy(&counter->data[1], nonce->data, n);
+
+ /* Finally, the counter value. */
+ if (state != NULL)
+ memcpy(&counter->data[1 + n], state->data, q);
+ else
+ memset(&counter->data[1 + n], 0, q);
+
+ return 0;
+}
+
+/* Return true if the payload length is valid given the nonce length n. */
+static krb5_boolean
+valid_payload_length_p(const struct krb5_keytypes *ktp, unsigned int n,
+ unsigned int payload_len)
+{
+ unsigned int block_size = ktp->enc->block_size;
+ unsigned int nblocks, maxblocks;
+ krb5_octet q;
+
+ assert(n >= 7 && n <= 13);
+
+ q = 15 - n;
+
+ maxblocks = (1U << (8 * q)) - 1 /* tag */;
+
+ nblocks = (payload_len + block_size - 1) / block_size;
+
+ return (nblocks <= maxblocks);
+}
+
+/* Encrypt and authenticate data according to NIST SP800-38C section 6.1. */
+static krb5_error_code
+ccm_encrypt(const struct krb5_keytypes *ktp, krb5_key kc,
+ const krb5_data *state, krb5_crypto_iov *data, size_t num_data)
+{
+ krb5_error_code ret;
+ krb5_crypto_iov *header, *trailer, *sign_data = NULL, cksum;
+ size_t i, num_sign_data = 0;
+ unsigned int header_len;
+ unsigned int trailer_len;
+ size_t payload_len = 0;
+ size_t adata_len = 0;
+ char adata_len_buf[6];
+ unsigned char B0[16], Ctr[16];
+ krb5_data counter = make_data(Ctr, sizeof(Ctr));
+
+ header_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_HEADER);
+
+ header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
+ if (header == NULL || header->data.length < header_len) {
+ ret = KRB5_BAD_MSIZE;
+ goto cleanup;
+ }
+
+ trailer_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_TRAILER);
+
+ trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER);
+ if (trailer == NULL || trailer->data.length < trailer_len) {
+ ret = KRB5_BAD_MSIZE;
+ goto cleanup;
+ }
+
+ for (i = 0; i < num_data; i++) {
+ krb5_crypto_iov *iov = &data[i];
+
+ switch (iov->flags) {
+ case KRB5_CRYPTO_TYPE_DATA:
+ payload_len += iov->data.length;
+ break;
+ case KRB5_CRYPTO_TYPE_SIGN_ONLY:
+ adata_len += iov->data.length;
+ break;
+ case KRB5_CRYPTO_TYPE_PADDING:
+ iov->data.length = 0;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!valid_payload_length_p(ktp, header_len, payload_len)) {
+ ret = KRB5_BAD_MSIZE;
+ goto cleanup;
+ }
+
+ header->data.length = header_len;
+ trailer->data.length = trailer_len;
+
+ /* Choose a random nonce. */
+ ret = krb5_c_random_make_octets(NULL, &header->data);
+ if (ret != 0)
+ goto cleanup;
+
+ /* Encode the first counter block. */
+ ret = format_Ctr0(&counter, &header->data, state, header_len);
+ if (ret != 0)
+ goto cleanup;
+
+ /* Create a list of CBC-MAC input blocks. */
+ sign_data = k5alloc((num_data + 1) * sizeof(krb5_crypto_iov), &ret);
+ if (sign_data == NULL)
+ goto cleanup;
+
+ /* Format the initial control/nonce block. */
+ sign_data[0].flags = KRB5_CRYPTO_TYPE_HEADER;
+ sign_data[0].data = make_data(B0, sizeof(B0));
+ ret = format_B0(&sign_data[0].data, &header->data, trailer_len,
+ (krb5_ui_8)adata_len, (krb5_ui_8)payload_len);
+ if (ret != 0)
+ goto cleanup;
+
+ /* Format the length of associated data. */
+ sign_data[1].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
+ sign_data[1].data = make_data(adata_len_buf, sizeof(adata_len_buf));
+ ret = encode_a_len(&sign_data[1].data, (krb5_ui_8)adata_len);
+ if (ret != 0)
+ goto cleanup;
+ num_sign_data = 2;
+
+ /* Reorder input IOV so SIGN_ONLY data is before DATA. */
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags == KRB5_CRYPTO_TYPE_SIGN_ONLY)
+ sign_data[num_sign_data++] = data[i];
+ }
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags == KRB5_CRYPTO_TYPE_DATA)
+ sign_data[num_sign_data++] = data[i];
+ }
+
+ assert(ktp->enc->encrypt != NULL);
+ assert(ktp->enc->cbc_mac != NULL);
+
+ /* Make checksum and place in trailer. */
+ ret = ktp->enc->cbc_mac(kc, sign_data, num_sign_data, NULL,
+ &trailer->data);
+ if (ret != 0)
+ goto cleanup;
+
+ /* Encrypt checksum in trailer using the first counter block. */
+ cksum.flags = KRB5_CRYPTO_TYPE_DATA;
+ cksum.data = trailer->data;
+ ret = ktp->enc->encrypt(kc, &counter, &cksum, 1);
+ if (ret != 0)
+ goto cleanup;
+
+ /* Encrypt everything but B0 (header) in subsequent counter blocks. */
+ ret = ktp->enc->encrypt(kc, &counter, data, num_data);
+ if (ret != 0)
+ goto cleanup;
+
+ /* Store the counter value as cipher state. Subsequent encryptions will
+ * generate a fresh nonce. */
+ if (state != NULL)
+ memcpy(state->data, counter.data + 1 + header_len, 15 - header_len);
+
+cleanup:
+ free(sign_data);
+ return ret;
+}
+
+/* Derive an encryption key based on usage and CCM-encrypt data. */
+krb5_error_code
+krb5int_dk_ccm_encrypt(const struct krb5_keytypes *ktp, krb5_key key,
+ krb5_keyusage usage, const krb5_data *state,
+ krb5_crypto_iov *data, size_t num_data)
+{
+ unsigned char constantdata[K5CLENGTH];
+ krb5_error_code ret;
+ krb5_key kc;
+ krb5_data d1;
+
+ d1.data = (char *)constantdata;
+ d1.length = K5CLENGTH;
+
+ d1.data[0] = (usage >> 24) & 0xFF;
+ d1.data[1] = (usage >> 16) & 0xFF;
+ d1.data[2] = (usage >> 8 ) & 0xFF;
+ d1.data[3] = (usage ) & 0xFF;
+
+ d1.data[4] = 0xCC;
+
+ ret = krb5int_derive_key(ktp->enc, key, &kc, &d1, DERIVE_SP800_108_CMAC);
+ if (ret != 0)
+ return ret;
+
+ ret = ccm_encrypt(ktp, kc, state, data, num_data);
+
+ krb5_k_free_key(NULL, kc);
+
+ return ret;
+}
+
+/* Decrypt and verify data according to NIST SP800-38C section 6.2. */
+static krb5_error_code
+ccm_decrypt(const struct krb5_keytypes *ktp, krb5_key kc,
+ const krb5_data *state, krb5_crypto_iov *data, size_t num_data)
+{
+ krb5_error_code ret;
+ krb5_crypto_iov *header, *trailer, *sign_data = NULL, got_cksum;
+ size_t i, num_sign_data = 0;
+ unsigned int header_len;
+ unsigned int trailer_len;
+ size_t adata_len = 0;
+ size_t payload_len = 0;
+ char adata_len_buf[6];
+ unsigned char B0[16], Ctr[16];
+ krb5_data made_cksum = empty_data();
+ krb5_data counter = make_data(Ctr, sizeof(Ctr));
+
+ header_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_HEADER);
+
+ header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
+ if (header == NULL || header->data.length != header_len) {
+ ret = KRB5_BAD_MSIZE;
+ goto cleanup;
+ }
+
+ trailer_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_TRAILER);
+
+ trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER);
+ if (trailer == NULL || trailer->data.length != trailer_len) {
+ ret = KRB5_BAD_MSIZE;
+ goto cleanup;
+ }
+
+ for (i = 0; i < num_data; i++) {
+ krb5_crypto_iov *iov = &data[i];
+
+ switch (iov->flags) {
+ case KRB5_CRYPTO_TYPE_DATA:
+ payload_len += iov->data.length;
+ break;
+ case KRB5_CRYPTO_TYPE_SIGN_ONLY:
+ adata_len += iov->data.length;
+ break;
+ case KRB5_CRYPTO_TYPE_PADDING:
+ if (iov->data.length != 0) {
+ ret = KRB5_BAD_MSIZE;
+ goto cleanup;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!valid_payload_length_p(ktp, header_len, payload_len)) {
+ ret = KRB5_BAD_MSIZE;
+ goto cleanup;
+ }
+
+ /* Encode the first counter block. */
+ ret = format_Ctr0(&counter, &header->data, state, header_len);
+ if (ret != 0)
+ goto cleanup;
+
+ /* Create a list of CBC-MAC input blocks. */
+ sign_data = k5alloc((num_data + 1) * sizeof(krb5_crypto_iov), &ret);
+ if (sign_data == NULL)
+ goto cleanup;
+
+ /* Format the initial control/nonce block. */
+ sign_data[0].flags = KRB5_CRYPTO_TYPE_HEADER;
+ sign_data[0].data = make_data(B0, sizeof(B0));
+ ret = format_B0(&sign_data[0].data, &header->data, trailer_len,
+ (krb5_ui_8)adata_len, (krb5_ui_8)payload_len);
+ if (ret != 0)
+ goto cleanup;
+
+ /* Format the length of associated data. */
+ sign_data[1].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
+ sign_data[1].data = make_data(adata_len_buf, sizeof(adata_len_buf));
+ ret = encode_a_len(&sign_data[1].data, (krb5_ui_8)adata_len);
+ if (ret != 0)
+ goto cleanup;
+ num_sign_data = 2;
+
+ assert(ktp->enc->decrypt != NULL);
+ assert(ktp->enc->cbc_mac != NULL);
+
+ made_cksum.data = k5alloc(trailer_len, &ret);
+ if (made_cksum.data == NULL)
+ goto cleanup;
+ made_cksum.length = trailer_len;
+
+ /* Decrypt checksum from trailer using the first counter block. */
+ got_cksum.flags = KRB5_CRYPTO_TYPE_DATA;
+ got_cksum.data = trailer->data;
+ ret = ktp->enc->decrypt(kc, &counter, &got_cksum, 1);
+ if (ret != 0)
+ goto cleanup;
+
+ /* Decrypt everything but B0 (header) in subsequent counter blocks. */
+ ret = ktp->enc->decrypt(kc, &counter, data, num_data);
+ if (ret != 0)
+ goto cleanup;
+
+ /* Reorder input IOV so SIGN_ONLY data is before DATA */
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags == KRB5_CRYPTO_TYPE_SIGN_ONLY)
+ sign_data[num_sign_data++] = data[i];
+ }
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags == KRB5_CRYPTO_TYPE_DATA)
+ sign_data[num_sign_data++] = data[i];
+ }
+
+ /* Calculate CBC-MAC for comparison (including B0). */
+ ret = ktp->enc->cbc_mac(kc, sign_data, num_sign_data, NULL, &made_cksum);
+ if (ret != 0)
+ goto cleanup;
+
+ if (made_cksum.length != trailer->data.length ||
+ memcmp(made_cksum.data, trailer->data.data,
+ trailer->data.length) != 0) {
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ goto cleanup;
+ }
+
+ /* Store the counter value as cipher state. Subsequent encryptions will
+ * generate a fresh nonce. */
+ if (state != NULL)
+ memcpy(state->data, counter.data + 1 + header_len, 15 - header_len);
+
+cleanup:
+ free(made_cksum.data);
+ free(sign_data);
+
+ return ret;
+}
+
+/* Derive an encryption key based on usage and CCM-decrypt data. */
+krb5_error_code
+krb5int_dk_ccm_decrypt(const struct krb5_keytypes *ktp, krb5_key key,
+ krb5_keyusage usage, const krb5_data *state,
+ krb5_crypto_iov *data, size_t num_data)
+{
+ unsigned char constantdata[K5CLENGTH];
+ krb5_error_code ret;
+ krb5_key kc;
+ krb5_data d1;
+
+ d1.data = (char *)constantdata;
+ d1.length = K5CLENGTH;
+
+ d1.data[0] = (usage >> 24) & 0xFF;
+ d1.data[1] = (usage >> 16) & 0xFF;
+ d1.data[2] = (usage >> 8 ) & 0xFF;
+ d1.data[3] = (usage ) & 0xFF;
+
+ d1.data[4] = 0xCC;
+
+ ret = krb5int_derive_key(ktp->enc, key, &kc, &d1, DERIVE_SP800_108_CMAC);
+ if (ret != 0)
+ return ret;
+
+ ret = ccm_decrypt(ktp, kc, state, data, num_data);
+
+ krb5_k_free_key(NULL, kc);
+
+ return ret;
+}
+
+krb5_error_code
+krb5int_dk_ccm_init_state(const struct krb5_keytypes *ktp,
+ const krb5_keyblock *key, krb5_keyusage usage,
+ krb5_data *out_state)
+{
+ unsigned int header_len;
+
+ /* The cipher state is the q-byte block counter value. */
+ header_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_HEADER);
+ return alloc_data(out_state, 15 - header_len);
+}
+
+void
+krb5int_dk_ccm_free_state(const struct krb5_keytypes *ktp,
+ krb5_data *state)
+{
+ free(state->data);
+ state->data = NULL;
+ state->length = 0;
+}
+
+#endif /* CAMELLIA_CCM */
diff --git a/src/lib/crypto/krb/dk/stringtokey.c b/src/lib/crypto/krb/dk/stringtokey.c
index 4c7206c..12ef67a 100644
--- a/src/lib/crypto/krb/dk/stringtokey.c
+++ b/src/lib/crypto/krb/dk/stringtokey.c
@@ -87,7 +87,8 @@ krb5int_dk_string_to_key(const struct krb5_keytypes *ktp,
indata.length = kerberos_len;
indata.data = (char *) kerberos;
- ret = krb5int_derive_keyblock(enc, foldkey, keyblock, &indata);
+ ret = krb5int_derive_keyblock(enc, foldkey, keyblock, &indata,
+ DERIVE_RFC3961);
if (ret != 0)
memset(keyblock->contents, 0, keyblock->length);
@@ -103,18 +104,18 @@ cleanup:
#define DEFAULT_ITERATION_COUNT 4096 /* was 0xb000L in earlier drafts */
#define MAX_ITERATION_COUNT 0x1000000L
-krb5_error_code
-krb5int_aes_string_to_key(const struct krb5_keytypes *ktp,
- const krb5_data *string,
- const krb5_data *salt,
- const krb5_data *params,
- krb5_keyblock *key)
+static krb5_error_code
+pbkdf2_string_to_key(const struct krb5_keytypes *ktp, const krb5_data *string,
+ const krb5_data *salt, const krb5_data *pepper,
+ const krb5_data *params, krb5_keyblock *key,
+ enum deriv_alg deriv_alg)
{
unsigned long iter_count;
krb5_data out;
static const krb5_data usage = { KV5M_DATA, 8, "kerberos" };
krb5_key tempkey = NULL;
krb5_error_code err;
+ krb5_data sandp = empty_data();
if (params) {
unsigned char *p = (unsigned char *) params->data;
@@ -142,6 +143,18 @@ krb5int_aes_string_to_key(const struct krb5_keytypes *ktp,
if (out.length != 16 && out.length != 32)
return KRB5_CRYPTO_INTERNAL;
+ if (pepper != NULL) {
+ err = alloc_data(&sandp, pepper->length + 1 + salt->length);
+ if (err)
+ return err;
+
+ memcpy(sandp.data, pepper->data, pepper->length);
+ sandp.data[pepper->length] = '\0';
+ memcpy(&sandp.data[pepper->length + 1], salt->data, salt->length);
+
+ salt = &sandp;
+ }
+
err = krb5int_pbkdf2_hmac_sha1 (&out, iter_count, string, salt);
if (err)
goto cleanup;
@@ -150,11 +163,39 @@ krb5int_aes_string_to_key(const struct krb5_keytypes *ktp,
if (err)
goto cleanup;
- err = krb5int_derive_keyblock(ktp->enc, tempkey, key, &usage);
+ err = krb5int_derive_keyblock(ktp->enc, tempkey, key, &usage, deriv_alg);
cleanup:
+ if (sandp.data)
+ free(sandp.data);
if (err)
memset (out.data, 0, out.length);
krb5_k_free_key (NULL, tempkey);
return err;
}
+
+krb5_error_code
+krb5int_aes_string_to_key(const struct krb5_keytypes *ktp,
+ const krb5_data *string,
+ const krb5_data *salt,
+ const krb5_data *params,
+ krb5_keyblock *key)
+{
+ return pbkdf2_string_to_key(ktp, string, salt, NULL, params, key,
+ DERIVE_RFC3961);
+}
+
+#ifdef CAMELLIA_CCM
+krb5_error_code
+krb5int_camellia_ccm_string_to_key(const struct krb5_keytypes *ktp,
+ const krb5_data *string,
+ const krb5_data *salt,
+ const krb5_data *params,
+ krb5_keyblock *key)
+{
+ krb5_data pepper = string2data(ktp->name);
+
+ return pbkdf2_string_to_key(ktp, string, salt, &pepper, params, key,
+ DERIVE_SP800_108_CMAC);
+}
+#endif