Add KRB5KDF from RFC 3961
authorSimo Sorce <simo@redhat.com>
Tue, 17 Sep 2019 20:35:23 +0000 (16:35 -0400)
committerTomas Mraz <tmraz@fedoraproject.org>
Wed, 23 Oct 2019 06:32:48 +0000 (08:32 +0200)
Signed-off-by: Simo Sorce <simo@redhat.com>
Reviewed-by: Tomas Mraz <tmraz@fedoraproject.org>
Reviewed-by: Richard Levitte <levitte@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/9949)

13 files changed:
crypto/err/openssl.txt
doc/man3/EVP_KDF.pod
doc/man7/EVP_KDF-KRB5KDF.pod [new file with mode: 0644]
include/openssl/core_names.h
providers/common/include/prov/providercommonerr.h
providers/common/provider_err.c
providers/defltprov.c
providers/implementations/include/prov/implementations.h
providers/implementations/kdfs/build.info
providers/implementations/kdfs/krb5kdf.c [new file with mode: 0644]
test/evp_kdf_test.c
test/evp_test.c
test/recipes/30-test_evp_data/evpkdf.txt

index 46d2eaa..9f40501 100644 (file)
@@ -2681,6 +2681,7 @@ PROV_R_FAILED_TO_GET_PARAMETER:103:failed to get parameter
 PROV_R_FAILED_TO_SET_PARAMETER:104:failed to set parameter
 PROV_R_INAVLID_UKM_LENGTH:146:inavlid ukm length
 PROV_R_INVALID_AAD:108:invalid aad
+PROV_R_INVALID_CONSTANT_LENGTH:157:invalid constant length
 PROV_R_INVALID_CUSTOM_LENGTH:111:invalid custom length
 PROV_R_INVALID_DATA:115:invalid data
 PROV_R_INVALID_DIGEST:122:invalid digest
@@ -2698,6 +2699,8 @@ PROV_R_INVALID_SEED_LENGTH:154:invalid seed length
 PROV_R_INVALID_TAG:110:invalid tag
 PROV_R_INVALID_TAGLEN:118:invalid taglen
 PROV_R_MISSING_CEK_ALG:144:missing cek alg
+PROV_R_MISSING_CIPHER:155:missing cipher
+PROV_R_MISSING_CONSTANT:156:missing constant
 PROV_R_MISSING_KEY:128:missing key
 PROV_R_MISSING_MAC:150:missing mac
 PROV_R_MISSING_MESSAGE_DIGEST:129:missing message digest
index 339129a..9bb360d 100644 (file)
@@ -183,8 +183,10 @@ The default value, if any, is implementation dependent.
 
 =item "digest" (B<OSSL_KDF_PARAM_DIGEST>) <UTF8 string>
 
-For KDF implementations that use an underlying computation MAC or
-digest, these parameters set what the algorithm should be.
+=item "cipher" (B<OSSL_KDF_PARAM_CIPHER>) <UTF8 string>
+
+For KDF implementations that use an underlying computation MAC, digest or
+cipher, these parameters set what the algorithm should be.
 
 The value is always the name of the intended algorithm,
 or the properties.
diff --git a/doc/man7/EVP_KDF-KRB5KDF.pod b/doc/man7/EVP_KDF-KRB5KDF.pod
new file mode 100644 (file)
index 0000000..08f50d1
--- /dev/null
@@ -0,0 +1,119 @@
+=pod
+
+=head1 NAME
+
+EVP_KDF-KRB5KDF - The RFC3961 Krb5 KDF EVP_KDF implementation
+
+=head1 DESCRIPTION
+
+Support for computing the B<KRB5KDF> KDF through the B<EVP_KDF> API.
+
+The EVP_KDF-KRB5KDF algorithm implements the key derivation function defined
+in RFC 3961, section 5.1 and is used by Krb5 to derive session keys.
+Three inputs are required to perform key derivation: a cipher, (for example
+AES-128-CBC), the initial key, and a constant.
+
+=head2 Identity
+
+"KRB5KDF" is the name for this implementation;
+it can be used with the EVP_KDF_fetch() function.
+
+=head2 Supported parameters
+
+The supported parameters are:
+
+=over 4
+
+=item "properies" (B<OSSL_KDF_PARAM_PROPERTIES>) <UTF8 string>
+
+=item "cipher" (B<OSSL_KDF_PARAM_CIPHER>) <UTF8 string>
+
+=item "key" (B<OSSL_KDF_PARAM_KEY>) <octet string>
+
+These parameters work as described in L<EVP_KDF(3)/PARAMETERS>.
+
+=item "constant" (B<OSSL_KDF_PARAM_CONSTANT>) <octet string>
+
+This parameter sets the constant value for the KDF.
+If a value is already set, the contents are replaced.
+
+=back
+
+=head1 NOTES
+
+A context for KRB5KDF can be obtained by calling:
+
+ EVP_KDF *kdf = EVP_KDF_fetch(NULL, "KRB5KDF", NULL);
+ EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf);
+
+The output length of the KRB5KDF derivation is specified via the I<keylen>
+parameter to the L<EVP_KDF_derive(3)> function, and MUST match the key
+length for the chosen cipher or an error is returned. Moreover the
+constant's length must not exceed the block size of the cipher.
+Since the KRB5KDF output length depends on the chosen cipher, calling
+L<EVP_KDF_size()> to obtain the requisite length returns the correct length
+only after the cipher is set. Prior to that B<EVP_MAX_KEY_LENGTH> is returned.
+The caller must allocate a buffer of the correct length for the chosen
+cipher, and pass that buffer to the L<EVP_KDF_derive(3)> function along
+with that length.
+
+=head1 EXAMPLES
+
+This example derives a key using the AES-128-CBC cipher:
+
+ EVP_KDF *kdf;
+ EVP_KDF_CTX *kctx;
+ unsigned char key[16] = "01234...";
+ unsigned char constant[] = "I'm a constant";
+ unsigned char out[16];
+ size_t outlen = sizeof(out);
+ OSSL_PARAM params[4], *p = params;
+
+ kdf = EVP_KDF_fetch(NULL, "KRB5KDF", NULL);
+ kctx = EVP_KDF_CTX_new(kdf);
+ EVP_KDF_free(kdf);
+
+ *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_CIPHER,
+                                         SN_aes_128_cbc,
+                                         strlen(SN_aes_128_cbc));
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
+                                          key, (size_t)16);
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_CONSTANT,
+                                          constant, strlen(constant));
+ *p = OSSL_PARAM_construct_end();
+ if (EVP_KDF_set_params(kctx, params) <= 0)
+     /* Error */
+
+ if (EVP_KDF_derive(kctx, out, &outlen) <= 0)
+     /* Error */
+
+
+=head1 CONFORMING TO
+
+RFC 3961
+
+=head1 SEE ALSO
+
+L<EVP_KDF>,
+L<EVP_KDF_CTX_new_id(3)>,
+L<EVP_KDF_CTX_free(3)>,
+L<EVP_KDF_ctrl(3)>,
+L<EVP_KDF_size(3)>,
+L<EVP_KDF_derive(3)>,
+L<EVP_KDF(3)/PARAMETERS>
+
+=head1 HISTORY
+
+This functionality was added to OpenSSL 3.0.
+
+=head1 COPYRIGHT
+
+Copyright 2016-2019 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the OpenSSL license (the "License").  You may not use
+this file except in compliance with the License.  You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
+
index e7e5ea0..4cbcf0c 100644 (file)
@@ -133,6 +133,8 @@ extern "C" {
 #define OSSL_KDF_PARAM_SSHKDF_SESSION_ID "session_id" /* octet string */
 #define OSSL_KDF_PARAM_SSHKDF_TYPE  "type"      /* int */
 #define OSSL_KDF_PARAM_SIZE         "size"      /* size_t */
+#define OSSL_KDF_PARAM_CIPHER       OSSL_ALG_PARAM_CIPHER     /* utf8 string */
+#define OSSL_KDF_PARAM_CONSTANT     "constant"  /* octet string */
 
 /* Known KDF names */
 #define OSSL_KDF_NAME_HKDF          "HKDF"
@@ -144,6 +146,7 @@ extern "C" {
 #define OSSL_KDF_NAME_X942KDF       "X942KDF"
 #define OSSL_KDF_NAME_X963KDF       "X963KDF"
 #define OSSL_KDF_NAME_KBKDF         "KBKDF"
+#define OSSL_KDF_NAME_KRB5KDF       "KRB5KDF"
 
 /* PKEY parameters */
 /* Diffie-Hellman/DSA Parameters */
index d35789e..c726a1e 100644 (file)
@@ -60,6 +60,7 @@ int ERR_load_PROV_strings(void);
 # define PROV_R_FAILED_TO_SET_PARAMETER                   104
 # define PROV_R_INAVLID_UKM_LENGTH                        146
 # define PROV_R_INVALID_AAD                               108
+# define PROV_R_INVALID_CONSTANT_LENGTH                   157
 # define PROV_R_INVALID_CUSTOM_LENGTH                     111
 # define PROV_R_INVALID_DATA                              115
 # define PROV_R_INVALID_DIGEST                            122
@@ -77,6 +78,8 @@ int ERR_load_PROV_strings(void);
 # define PROV_R_INVALID_TAG                               110
 # define PROV_R_INVALID_TAGLEN                            118
 # define PROV_R_MISSING_CEK_ALG                           144
+# define PROV_R_MISSING_CIPHER                            155
+# define PROV_R_MISSING_CONSTANT                          156
 # define PROV_R_MISSING_KEY                               128
 # define PROV_R_MISSING_MAC                               150
 # define PROV_R_MISSING_MESSAGE_DIGEST                    129
index 20060b0..589f37b 100644 (file)
@@ -32,6 +32,8 @@ static const ERR_STRING_DATA PROV_str_reasons[] = {
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_INAVLID_UKM_LENGTH),
     "inavlid ukm length"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_INVALID_AAD), "invalid aad"},
+    {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_INVALID_CONSTANT_LENGTH),
+    "invalid constant length"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_INVALID_CUSTOM_LENGTH),
     "invalid custom length"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_INVALID_DATA), "invalid data"},
@@ -54,6 +56,8 @@ static const ERR_STRING_DATA PROV_str_reasons[] = {
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_INVALID_TAG), "invalid tag"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_INVALID_TAGLEN), "invalid taglen"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_MISSING_CEK_ALG), "missing cek alg"},
+    {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_MISSING_CIPHER), "missing cipher"},
+    {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_MISSING_CONSTANT), "missing constant"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_MISSING_KEY), "missing key"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_MISSING_MAC), "missing mac"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_MISSING_MESSAGE_DIGEST),
index 8104227..cf0a175 100644 (file)
@@ -339,6 +339,7 @@ static const OSSL_ALGORITHM deflt_kdfs[] = {
 #ifndef OPENSSL_NO_SCRYPT
     { "SCRYPT:id-scrypt", "default=yes", kdf_scrypt_functions },
 #endif
+    { OSSL_KDF_NAME_KRB5KDF, "default=yes", kdf_krb5kdf_functions },
    { NULL, NULL, NULL }
 };
 
index 16f2129..ded3ef9 100644 (file)
@@ -236,6 +236,7 @@ extern const OSSL_DISPATCH kdf_kbkdf_functions[];
 #ifndef OPENSSL_NO_CMS
 extern const OSSL_DISPATCH kdf_x942_kdf_functions[];
 #endif
+extern const OSSL_DISPATCH kdf_krb5kdf_functions[];
 
 
 /* Key management */
index dee8f53..2c6b2ce 100644 (file)
@@ -4,6 +4,7 @@
 $TLS1_PRF_GOAL=../../libimplementations.a
 $HKDF_GOAL=../../libimplementations.a
 $KBKDF_GOAL=../../libimplementations.a
+$KRB5KDF_GOAL=../../libimplementations.a
 $PBKDF2_GOAL=../../libimplementations.a
 $SSKDF_GOAL=../../libimplementations.a
 $SCRYPT_GOAL=../../libimplementations.a
@@ -16,6 +17,8 @@ SOURCE[$HKDF_GOAL]=hkdf.c
 
 SOURCE[$KBKDF_GOAL]=kbkdf.c
 
+SOURCE[$KRB5KDF_GOAL]=krb5kdf.c
+
 SOURCE[$PBKDF2_GOAL]=pbkdf2.c
 # Extra code to satisfy the FIPS and non-FIPS separation.
 # When the PBKDF2 moves to legacy, this can be removed.
diff --git a/providers/implementations/kdfs/krb5kdf.c b/providers/implementations/kdfs/krb5kdf.c
new file mode 100644 (file)
index 0000000..83dfa9c
--- /dev/null
@@ -0,0 +1,432 @@
+/*
+ * Copyright 2018-2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <openssl/core_names.h>
+#include <openssl/des.h>
+#include <openssl/evp.h>
+#include <openssl/kdf.h>
+
+#include "internal/cryptlib.h"
+#include "crypto/evp.h"
+#include "internal/numbers.h"
+#include "prov/implementations.h"
+#include "prov/provider_ctx.h"
+#include "prov/provider_util.h"
+#include "prov/providercommonerr.h"
+
+/* KRB5 KDF defined in RFC 3961, Section 5.1 */
+
+static OSSL_OP_kdf_newctx_fn krb5kdf_new;
+static OSSL_OP_kdf_freectx_fn krb5kdf_free;
+static OSSL_OP_kdf_reset_fn krb5kdf_reset;
+static OSSL_OP_kdf_derive_fn krb5kdf_derive;
+static OSSL_OP_kdf_settable_ctx_params_fn krb5kdf_settable_ctx_params;
+static OSSL_OP_kdf_set_ctx_params_fn krb5kdf_set_ctx_params;
+static OSSL_OP_kdf_gettable_ctx_params_fn krb5kdf_gettable_ctx_params;
+static OSSL_OP_kdf_get_ctx_params_fn krb5kdf_get_ctx_params;
+
+static int KRB5KDF(const EVP_CIPHER *cipher, ENGINE *engine,
+                   const unsigned char *key, size_t key_len,
+                   const unsigned char *constant, size_t constant_len,
+                   unsigned char *okey, size_t okey_len);
+
+typedef struct {
+    void *provctx;
+    PROV_CIPHER cipher;
+    unsigned char *key;
+    size_t key_len;
+    unsigned char *constant;
+    size_t constant_len;
+} KRB5KDF_CTX;
+
+static void *krb5kdf_new(void *provctx)
+{
+    KRB5KDF_CTX *ctx;
+
+    if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL)
+        ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
+    ctx->provctx = provctx;
+    return ctx;
+}
+
+static void krb5kdf_free(void *vctx)
+{
+    KRB5KDF_CTX *ctx = (KRB5KDF_CTX *)vctx;
+
+    krb5kdf_reset(ctx);
+    OPENSSL_free(ctx);
+}
+
+static void krb5kdf_reset(void *vctx)
+{
+    KRB5KDF_CTX *ctx = (KRB5KDF_CTX *)vctx;
+
+    ossl_prov_cipher_reset(&ctx->cipher);
+    OPENSSL_clear_free(ctx->key, ctx->key_len);
+    OPENSSL_clear_free(ctx->constant, ctx->constant_len);
+    memset(ctx, 0, sizeof(*ctx));
+}
+
+static int krb5kdf_set_membuf(unsigned char **dst, size_t *dst_len,
+                              const OSSL_PARAM *p)
+{
+    OPENSSL_clear_free(*dst, *dst_len);
+    *dst = NULL;
+    return OSSL_PARAM_get_octet_string(p, (void **)dst, 0, dst_len);
+}
+
+static int krb5kdf_derive(void *vctx, unsigned char *key,
+                              size_t keylen)
+{
+    KRB5KDF_CTX *ctx = (KRB5KDF_CTX *)vctx;
+    const EVP_CIPHER *cipher = ossl_prov_cipher_cipher(&ctx->cipher);
+    ENGINE *engine = ossl_prov_cipher_engine(&ctx->cipher);
+
+    if (cipher == NULL) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_CIPHER);
+        return 0;
+    }
+    if (ctx->key == NULL) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
+        return 0;
+    }
+    if (ctx->constant == NULL) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_CONSTANT);
+        return 0;
+    }
+    return KRB5KDF(cipher, engine, ctx->key, ctx->key_len,
+                   ctx->constant, ctx->constant_len,
+                   key, keylen);
+}
+
+static int krb5kdf_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+    const OSSL_PARAM *p;
+    KRB5KDF_CTX *ctx = vctx;
+    OPENSSL_CTX *provctx = PROV_LIBRARY_CONTEXT_OF(ctx->provctx);
+
+    if (!ossl_prov_cipher_load_from_params(&ctx->cipher, params, provctx))
+        return 0;
+
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY)) != NULL)
+        if (!krb5kdf_set_membuf(&ctx->key, &ctx->key_len, p))
+            return 0;
+
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_CONSTANT))
+        != NULL)
+        if (!krb5kdf_set_membuf(&ctx->constant, &ctx->constant_len, p))
+            return 0;
+
+    return 1;
+}
+
+static const OSSL_PARAM *krb5kdf_settable_ctx_params(void)
+{
+    static const OSSL_PARAM known_settable_ctx_params[] = {
+        OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0),
+        OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_CIPHER, NULL, 0),
+        OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY, NULL, 0),
+        OSSL_PARAM_octet_string(OSSL_KDF_PARAM_CONSTANT, NULL, 0),
+        OSSL_PARAM_END
+    };
+    return known_settable_ctx_params;
+}
+
+static int krb5kdf_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+    KRB5KDF_CTX *ctx = (KRB5KDF_CTX *)vctx;
+    const EVP_CIPHER *cipher;
+    size_t len;
+    OSSL_PARAM *p;
+
+    cipher = ossl_prov_cipher_cipher(&ctx->cipher);
+    if (cipher)
+        len = EVP_CIPHER_key_length(cipher);
+    else
+        len = EVP_MAX_KEY_LENGTH;
+
+    if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL)
+        return OSSL_PARAM_set_size_t(p, len);
+    return -2;
+}
+
+static const OSSL_PARAM *krb5kdf_gettable_ctx_params(void)
+{
+    static const OSSL_PARAM known_gettable_ctx_params[] = {
+        OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL),
+        OSSL_PARAM_END
+    };
+    return known_gettable_ctx_params;
+}
+
+const OSSL_DISPATCH kdf_krb5kdf_functions[] = {
+    { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))krb5kdf_new },
+    { OSSL_FUNC_KDF_FREECTX, (void(*)(void))krb5kdf_free },
+    { OSSL_FUNC_KDF_RESET, (void(*)(void))krb5kdf_reset },
+    { OSSL_FUNC_KDF_DERIVE, (void(*)(void))krb5kdf_derive },
+    { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
+      (void(*)(void))krb5kdf_settable_ctx_params },
+    { OSSL_FUNC_KDF_SET_CTX_PARAMS,
+      (void(*)(void))krb5kdf_set_ctx_params },
+    { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
+      (void(*)(void))krb5kdf_gettable_ctx_params },
+    { OSSL_FUNC_KDF_GET_CTX_PARAMS,
+      (void(*)(void))krb5kdf_get_ctx_params },
+    { 0, NULL }
+};
+
+#ifndef OPENSSL_NO_DES
+/*
+ * DES3 is a special case, it requires a random-to-key function and its
+ * input truncated to 21 bytes of the 24 produced by the cipher.
+ * See RFC3961 6.3.1
+ */
+static int fixup_des3_key(unsigned char *key)
+{
+    unsigned char *cblock;
+    int i, j;
+
+    for (i = 2; i >= 0; i--) {
+        cblock = &key[i * 8];
+        memmove(cblock, &key[i * 7], 7);
+        cblock[7] = 0;
+        for (j = 0; j < 7; j++)
+            cblock[7] |= (cblock[j] & 1) << (j + 1);
+        DES_set_odd_parity((DES_cblock *)cblock);
+    }
+
+    /* fail if keys are such that triple des degrades to single des */
+    if (CRYPTO_memcmp(&key[0], &key[8], 8) == 0 ||
+        CRYPTO_memcmp(&key[8], &key[16], 8) == 0) {
+        return 0;
+    }
+
+    return 1;
+}
+#endif
+
+/*
+ * N-fold(K) where blocksize is N, and constant_len is K
+ * Note: Here |= denotes concatenation
+ *
+ * L = lcm(N,K)
+ * R = L/K
+ *
+ * for r: 1 -> R
+ *   s |= constant rot 13*(r-1))
+ *
+ * block = 0
+ * for k: 1 -> K
+ *   block += s[N(k-1)..(N-1)k] (one's complement addition)
+ *
+ * Optimizing for space we compute:
+ * for each l in L-1 -> 0:
+ *   s[l] = (constant rot 13*(l/K))[l%k]
+ *   block[l % N] += s[l] (with carry)
+ * finally add carry if any
+ */
+static void n_fold(unsigned char *block, unsigned int blocksize,
+                   const unsigned char *constant, size_t constant_len)
+{
+    unsigned int tmp, gcd, remainder, lcm, carry;
+    int b, l;
+
+    if (constant_len == blocksize) {
+        memcpy(block, constant, constant_len);
+        return;
+    }
+
+    /* Least Common Multiple of lengths: LCM(a,b)*/
+    gcd = blocksize;
+    remainder = constant_len;
+    /* Calculate Great Common Divisor first GCD(a,b) */
+    while (remainder != 0) {
+        tmp = gcd % remainder;
+        gcd = remainder;
+        remainder = tmp;
+    }
+    /* resulting a is the GCD, LCM(a,b) = |a*b|/GCD(a,b) */
+    lcm = blocksize * constant_len / gcd;
+
+    /* now spread out the bits */
+    memset(block, 0, blocksize);
+
+    /* last to first to be able to bring carry forward */
+    carry = 0;
+    for (l = lcm - 1; l >= 0; l--) {
+        unsigned int rotbits, rshift, rbyte;
+
+        /* destination byte in block is l % N */
+        b = l % blocksize;
+        /* Our virtual s buffer is R = L/K long (K = constant_len) */
+        /* So we rotate backwards from R-1 to 0 (none) rotations */
+        rotbits = 13 * (l / constant_len);
+        /* find the byte on s where rotbits falls onto */
+        rbyte = l - (rotbits / 8);
+        /* calculate how much shift on that byte */
+        rshift = rotbits & 0x07;
+        /* rbyte % constant_len gives us the unrotated byte in the
+         * constant buffer, get also the previous byte then
+         * appropriately shift them to get the rotated byte we need */
+        tmp = (constant[(rbyte-1) % constant_len] << (8 - rshift)
+               | constant[rbyte % constant_len] >> rshift)
+              & 0xff;
+        /* add with carry to any value placed by previous passes */
+        tmp += carry + block[b];
+        block[b] = tmp & 0xff;
+        /* save any carry that may be left */
+        carry = tmp >> 8;
+    }
+
+    /* if any carry is left at the end, add it through the number */
+    for (b = blocksize - 1; b >= 0 && carry != 0; b--) {
+        carry += block[b];
+        block[b] = carry & 0xff;
+        carry >>= 8;
+    }
+}
+
+static int cipher_init(EVP_CIPHER_CTX *ctx,
+                       const EVP_CIPHER *cipher, ENGINE *engine,
+                       const unsigned char *key, size_t key_len)
+{
+    int klen, ret;
+
+    ret = EVP_EncryptInit_ex(ctx, cipher, engine, key, NULL);
+    if (!ret)
+        goto out;
+    /* set the key len for the odd variable key len cipher */
+    klen = EVP_CIPHER_CTX_key_length(ctx);
+    if (key_len != (size_t)klen) {
+        ret = EVP_CIPHER_CTX_set_key_length(ctx, key_len);
+        if (!ret)
+            goto out;
+    }
+    /* we never want padding, either the length requested is a multiple of
+     * the cipher block size or we are passed a cipher that can cope with
+     * partial blocks via techniques like cipher text stealing */
+    ret = EVP_CIPHER_CTX_set_padding(ctx, 0);
+    if (!ret)
+        goto out;
+
+out:
+    return ret;
+}
+
+static int KRB5KDF(const EVP_CIPHER *cipher, ENGINE *engine,
+                   const unsigned char *key, size_t key_len,
+                   const unsigned char *constant, size_t constant_len,
+                   unsigned char *okey, size_t okey_len)
+{
+    EVP_CIPHER_CTX *ctx = NULL;
+    unsigned char block[EVP_MAX_BLOCK_LENGTH * 2];
+    unsigned char *plainblock, *cipherblock;
+    size_t blocksize;
+    size_t cipherlen;
+    size_t osize;
+    int des3_no_fixup = 0;
+    int ret;
+
+    if (key_len != okey_len) {
+        /* special case for 3des, where the caller may be requesting
+         * the random raw key, instead of the fixed up key  */
+        if (EVP_CIPHER_nid(cipher) == NID_des_ede3_cbc &&
+            key_len == 24 && okey_len == 21) {
+                des3_no_fixup = 1;
+        } else {
+            ERR_raise(ERR_LIB_PROV, PROV_R_WRONG_OUTPUT_BUFFER_SIZE);
+            return 0;
+        }
+    }
+
+    ctx = EVP_CIPHER_CTX_new();
+    if (ctx == NULL)
+        return 0;
+
+    ret = cipher_init(ctx, cipher, engine, key, key_len);
+    if (!ret)
+        goto out;
+
+    /* Initialize input block */
+    blocksize = EVP_CIPHER_CTX_block_size(ctx);
+
+    if (constant_len > blocksize) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CONSTANT_LENGTH);
+        ret = 0;
+        goto out;
+    }
+
+    n_fold(block, blocksize, constant, constant_len);
+    plainblock = block;
+    cipherblock = block + EVP_MAX_BLOCK_LENGTH;
+
+    for (osize = 0; osize < okey_len; osize += cipherlen) {
+        int olen;
+
+        ret = EVP_EncryptUpdate(ctx, cipherblock, &olen,
+                                plainblock, blocksize);
+        if (!ret)
+            goto out;
+        cipherlen = olen;
+        ret = EVP_EncryptFinal_ex(ctx, cipherblock, &olen);
+        if (!ret)
+            goto out;
+        if (olen != 0) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_WRONG_FINAL_BLOCK_LENGTH);
+            ret = 0;
+            goto out;
+        }
+
+        /* write cipherblock out */
+        if (cipherlen > okey_len - osize)
+            cipherlen = okey_len - osize;
+        memcpy(okey + osize, cipherblock, cipherlen);
+
+        if (okey_len > osize + cipherlen) {
+            /* we need to reinitialize cipher context per spec */
+            ret = EVP_CIPHER_CTX_reset(ctx);
+            if (!ret)
+                goto out;
+            ret = cipher_init(ctx, cipher, engine, key, key_len);
+            if (!ret)
+                goto out;
+
+            /* also swap block offsets so last ciphertext becomes new
+             * plaintext */
+            plainblock = cipherblock;
+            if (cipherblock == block) {
+                cipherblock += EVP_MAX_BLOCK_LENGTH;
+            } else {
+                cipherblock = block;
+            }
+        }
+    }
+
+#ifndef OPENSSL_NO_DES
+    if (EVP_CIPHER_nid(cipher) == NID_des_ede3_cbc && !des3_no_fixup) {
+        ret = fixup_des3_key(okey);
+        if (!ret) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY);
+            goto out;
+        }
+    }
+#endif
+
+    ret = 1;
+
+out:
+    EVP_CIPHER_CTX_free(ctx);
+    OPENSSL_cleanse(block, EVP_MAX_BLOCK_LENGTH * 2);
+    return ret;
+}
+
index 21a0c27..ddf935b 100644 (file)
@@ -732,6 +732,43 @@ static int test_kdf_x942_asn1(void)
 }
 #endif /* OPENSSL_NO_CMS */
 
+static int test_kdf_krb5kdf(void)
+{
+    int ret;
+    EVP_KDF_CTX *kctx;
+    OSSL_PARAM params[4], *p = params;
+    unsigned char out[16];
+    static unsigned char key[] = {
+        0x42, 0x26, 0x3C, 0x6E, 0x89, 0xF4, 0xFC, 0x28,
+        0xB8, 0xDF, 0x68, 0xEE, 0x09, 0x79, 0x9F, 0x15
+    };
+    static unsigned char constant[] = {
+        0x00, 0x00, 0x00, 0x02, 0x99
+    };
+    static const unsigned char expected[sizeof(out)] = {
+        0x34, 0x28, 0x0A, 0x38, 0x2B, 0xC9, 0x27, 0x69,
+        0xB2, 0xDA, 0x2F, 0x9E, 0xF0, 0x66, 0x85, 0x4B
+    };
+
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_CIPHER,
+                                            (char *)"AES-128-CBC",
+                                            sizeof("AES-128-CBC"));
+    *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, key,
+                                             sizeof(key));
+    *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_CONSTANT,
+                                             constant, sizeof(constant));
+    *p = OSSL_PARAM_construct_end();
+
+    ret =
+        TEST_ptr(kctx = get_kdfbyname(OSSL_KDF_NAME_KRB5KDF))
+        && TEST_true(EVP_KDF_CTX_set_params(kctx, params))
+        && TEST_int_gt(EVP_KDF_derive(kctx, out, sizeof(out)), 0)
+        && TEST_mem_eq(out, sizeof(out), expected, sizeof(expected));
+
+    EVP_KDF_CTX_free(kctx);
+    return ret;
+}
+
 int setup_tests(void)
 {
     ADD_TEST(test_kdf_kbkdf_6803_128);
@@ -753,5 +790,6 @@ int setup_tests(void)
 #ifndef OPENSSL_NO_CMS
     ADD_TEST(test_kdf_x942_asn1);
 #endif
+    ADD_TEST(test_kdf_krb5kdf);
     return 1;
 }
index b68ad3b..1039c21 100644 (file)
@@ -2116,6 +2116,15 @@ static int kdf_test_ctrl(EVP_TEST *t, EVP_KDF_CTX *kctx,
         if (nid != NID_undef && EVP_get_digestbynid(nid) == NULL)
             t->skip = 1;
     }
+    if (p != NULL && strcmp(name, "cipher") == 0) {
+        /* If p has an OID and lookup fails assume disabled algorithm */
+        int nid = OBJ_sn2nid(p);
+
+        if (nid == NID_undef)
+             nid = OBJ_ln2nid(p);
+        if (nid != NID_undef && EVP_get_cipherbynid(nid) == NULL)
+            t->skip = 1;
+    }
     OPENSSL_free(name);
     return 1;
 }
index 3905f17..73d3b3c 100644 (file)
@@ -6523,3 +6523,123 @@ Ctrl.hexsecret = hexsecret:000102030405060708090a0b0c0d0e0f10111213
 Ctrl.cekalg = cekalg:id-smime-alg-CMSRC2wrap
 Ctrl.hexukm = hexukm:0123456789abcdeffedcba98765432010123456789abcdeffedcba98765432010123456789abcdeffedcba98765432010123456789abcdeffedcba9876543201
 Output = 48950c46e0530075403cce72889604e0
+
+Title = KRB5KDF tests (from RFC 3961 test vectors and krb5 sources)
+
+#RFC3961
+KDF = KRB5KDF
+Ctrl.cipher = cipher:DES-EDE3-CBC
+Ctrl.hexkey = hexkey:dce06b1f64c857a11c3db57c51899b2cc1791008ce973b92
+Ctrl.hexconstant = hexconstant:0000000155
+Output = 925179d04591a79b5d3192c4a7e9c289b049c71f6ee604cd
+
+KDF = KRB5KDF
+Ctrl.cipher = cipher:DES-EDE3-CBC
+Ctrl.hexkey = hexkey:5e13d31c70ef765746578531cb51c15bf11ca82c97cee9f2
+Ctrl.hexconstant = hexconstant:00000001aa
+Output = 9e58e5a146d9942a101c469845d67a20e3c4259ed913f207
+
+KDF = KRB5KDF
+Ctrl.cipher = cipher:DES-EDE3-CBC
+Ctrl.hexkey = hexkey:98e6fd8a04a4b6859b75a176540b9752bad3ecd610a252bc
+Ctrl.hexconstant = hexconstant:0000000155
+Output = 13fef80d763e94ec6d13fd2ca1d085070249dad39808eabf
+
+KDF = KRB5KDF
+Ctrl.cipher = cipher:DES-EDE3-CBC
+Ctrl.hexkey = hexkey:622aec25a2fe2cad7094680b7c64940280084c1a7cec92b5
+Ctrl.hexconstant = hexconstant:00000001aa
+Output = f8dfbf04b097e6d9dc0702686bcb3489d91fd9a4516b703e
+
+KDF = KRB5KDF
+Ctrl.cipher = cipher:DES-EDE3-CBC
+Ctrl.hexkey = hexkey:d3f8298ccb166438dcb9b93ee5a7629286a491f838f802fb
+Ctrl.hexconstant = hexconstant:6b65726265726f73
+Output = 2370da575d2a3da864cebfdc5204d56df779a7df43d9da43
+
+KDF = KRB5KDF
+Ctrl.cipher = cipher:DES-EDE3-CBC
+Ctrl.hexkey = hexkey:c1081649ada74362e6a1459d01dfd30d67c2234c940704da
+Ctrl.hexconstant = hexconstant:0000000155
+Output = 348057ec98fdc48016161c2a4c7a943e92ae492c989175f7
+
+KDF = KRB5KDF
+Ctrl.cipher = cipher:DES-EDE3-CBC
+Ctrl.hexkey = hexkey:5d154af238f46713155719d55e2f1f790dd661f279a7917c
+Ctrl.hexconstant = hexconstant:00000001aa
+Output = a8808ac267dada3dcbe9a7c84626fbc761c294b01315e5c1
+
+KDF = KRB5KDF
+Ctrl.cipher = cipher:DES-EDE3-CBC
+Ctrl.hexkey = hexkey:798562e049852f57dc8c343ba17f2ca1d97394efc8adc443
+Ctrl.hexconstant = hexconstant:0000000155
+Output = c813f88a3be3b334f75425ce9175fbe3c8493b89c8703b49
+
+KDF = KRB5KDF
+Ctrl.cipher = cipher:DES-EDE3-CBC
+Ctrl.hexkey = hexkey:26dce334b545292f2feab9a8701a89a4b99eb9942cecd016
+Ctrl.hexconstant = hexconstant:00000001aa
+Output = f48ffd6e83f83e7354e694fd252cf83bfe58f7d5ba37ec5d
+
+#Krb5 sources
+KDF = KRB5KDF
+Ctrl.cipher = cipher:DES-EDE3-CBC
+Ctrl.hexkey = hexkey:850BB51358548CD05E86768C313E3BFEF7511937DCF72C3E
+Ctrl.hexconstant = hexconstant:0000000299
+Output = F78C496D16E6C2DAE0E0B6C24057A84C0426AEEF26FD6DCE
+
+KDF = KRB5KDF
+Ctrl.cipher = cipher:DES-EDE3-CBC
+Ctrl.hexkey = hexkey:850BB51358548CD05E86768C313E3BFEF7511937DCF72C3E
+Ctrl.hexconstant = hexconstant:00000002AA
+Output = 5B5723D0B634CB684C3EBA5264E9A70D52E683231AD3C4CE
+
+KDF = KRB5KDF
+Ctrl.cipher = cipher:DES-EDE3-CBC
+Ctrl.hexkey = hexkey:850BB51358548CD05E86768C313E3BFEF7511937DCF72C3E
+Ctrl.hexconstant = hexconstant:0000000255
+Output = A77C94980E9B7345A81525C423A737CE67F4CD91B6B3DA45
+
+KDF = KRB5KDF
+Ctrl.cipher = cipher:AES-128-CBC
+Ctrl.hexkey = hexkey:42263C6E89F4FC28B8DF68EE09799F15
+Ctrl.hexconstant = hexconstant:0000000299
+Output = 34280A382BC92769B2DA2F9EF066854B
+
+KDF = KRB5KDF
+Ctrl.cipher = cipher:AES-128-CBC
+Ctrl.hexkey = hexkey:42263C6E89F4FC28B8DF68EE09799F15
+Ctrl.hexconstant = hexconstant:00000002AA
+Output = 5B14FC4E250E14DDF9DCCF1AF6674F53
+
+KDF = KRB5KDF
+Ctrl.cipher = cipher:AES-128-CBC
+Ctrl.hexkey = hexkey:42263C6E89F4FC28B8DF68EE09799F15
+Ctrl.hexconstant = hexconstant:0000000255
+Output = 4ED31063621684F09AE8D89991AF3E8F
+
+KDF = KRB5KDF
+Ctrl.cipher = cipher:AES-256-CBC
+Ctrl.hexkey = hexkey:FE697B52BC0D3CE14432BA036A92E65BBB52280990A2FA27883998D72AF30161
+Ctrl.hexconstant = hexconstant:0000000299
+Output = BFAB388BDCB238E9F9C98D6A878304F04D30C82556375AC507A7A852790F4674
+
+KDF = KRB5KDF
+Ctrl.cipher = cipher:AES-256-CBC
+Ctrl.hexkey = hexkey:FE697B52BC0D3CE14432BA036A92E65BBB52280990A2FA27883998D72AF30161
+Ctrl.hexconstant = hexconstant:00000002AA
+Output = C7CFD9CD75FE793A586A542D87E0D1396F1134A104BB1A9190B8C90ADA3DDF37
+
+KDF = KRB5KDF
+Ctrl.cipher = cipher:AES-256-CBC
+Ctrl.hexkey = hexkey:FE697B52BC0D3CE14432BA036A92E65BBB52280990A2FA27883998D72AF30161
+Ctrl.hexconstant = hexconstant:0000000255
+Output = 97151B4C76945063E2EB0529DC067D97D7BBA90776D8126D91F34F3101AEA8BA
+
+#Same as the first but with no "fixup"
+KDF = KRB5KDF
+Ctrl.cipher = cipher:DES-EDE3-CBC
+Ctrl.hexkey = hexkey:dce06b1f64c857a11c3db57c51899b2cc1791008ce973b92
+Ctrl.hexconstant = hexconstant:0000000155
+Output = 935079d14490a75c3093c4a6e8c3b049c71e6ee705
+