|
Packit |
fd8b60 |
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
Packit |
fd8b60 |
/* lib/crypto/builtin/enc_provider/camellia.c - Camellia enc provider */
|
|
Packit |
fd8b60 |
/*
|
|
Packit |
fd8b60 |
* Copyright (C) 2009, 2010 by the Massachusetts Institute of Technology.
|
|
Packit |
fd8b60 |
* All rights reserved.
|
|
Packit |
fd8b60 |
*
|
|
Packit |
fd8b60 |
* Export of this software from the United States of America may
|
|
Packit |
fd8b60 |
* require a specific license from the United States Government.
|
|
Packit |
fd8b60 |
* It is the responsibility of any person or organization contemplating
|
|
Packit |
fd8b60 |
* export to obtain such a license before exporting.
|
|
Packit |
fd8b60 |
*
|
|
Packit |
fd8b60 |
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
|
|
Packit |
fd8b60 |
* distribute this software and its documentation for any purpose and
|
|
Packit |
fd8b60 |
* without fee is hereby granted, provided that the above copyright
|
|
Packit |
fd8b60 |
* notice appear in all copies and that both that copyright notice and
|
|
Packit |
fd8b60 |
* this permission notice appear in supporting documentation, and that
|
|
Packit |
fd8b60 |
* the name of M.I.T. not be used in advertising or publicity pertaining
|
|
Packit |
fd8b60 |
* to distribution of the software without specific, written prior
|
|
Packit |
fd8b60 |
* permission. Furthermore if you modify this software you must label
|
|
Packit |
fd8b60 |
* your software as modified software and not distribute it in such a
|
|
Packit |
fd8b60 |
* fashion that it might be confused with the original M.I.T. software.
|
|
Packit |
fd8b60 |
* M.I.T. makes no representations about the suitability of
|
|
Packit |
fd8b60 |
* this software for any purpose. It is provided "as is" without express
|
|
Packit |
fd8b60 |
* or implied warranty.
|
|
Packit |
fd8b60 |
*/
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
#include "crypto_int.h"
|
|
Packit |
fd8b60 |
#include "camellia.h"
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/*
|
|
Packit |
fd8b60 |
* Private per-key data to cache after first generation. We don't want to mess
|
|
Packit |
fd8b60 |
* with the imported Camellia implementation too much, so we'll just use two
|
|
Packit |
fd8b60 |
* copies of its context, one for encryption and one for decryption, and use
|
|
Packit |
fd8b60 |
* the keybitlen field as a flag for whether we've initialized each half.
|
|
Packit |
fd8b60 |
*/
|
|
Packit |
fd8b60 |
struct camellia_key_info_cache {
|
|
Packit |
fd8b60 |
camellia_ctx enc_ctx, dec_ctx;
|
|
Packit |
fd8b60 |
};
|
|
Packit |
fd8b60 |
#define CACHE(X) ((struct camellia_key_info_cache *)((X)->cache))
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* out = out ^ in */
|
|
Packit |
fd8b60 |
static inline void
|
|
Packit |
fd8b60 |
xorblock(const unsigned char *in, unsigned char *out)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
size_t q;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
for (q = 0; q < BLOCK_SIZE; q += 4)
|
|
Packit |
fd8b60 |
store_32_n(load_32_n(out + q) ^ load_32_n(in + q), out + q);
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static inline krb5_error_code
|
|
Packit |
fd8b60 |
init_key_cache(krb5_key key)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
if (key->cache != NULL)
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
key->cache = malloc(sizeof(struct camellia_key_info_cache));
|
|
Packit |
fd8b60 |
if (key->cache == NULL)
|
|
Packit |
fd8b60 |
return ENOMEM;
|
|
Packit |
fd8b60 |
CACHE(key)->enc_ctx.keybitlen = CACHE(key)->dec_ctx.keybitlen = 0;
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static inline void
|
|
Packit |
fd8b60 |
expand_enc_key(krb5_key key)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
if (CACHE(key)->enc_ctx.keybitlen)
|
|
Packit |
fd8b60 |
return;
|
|
Packit |
fd8b60 |
if (camellia_enc_key(key->keyblock.contents, key->keyblock.length,
|
|
Packit |
fd8b60 |
&CACHE(key)->enc_ctx) != camellia_good)
|
|
Packit |
fd8b60 |
abort();
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static inline void
|
|
Packit |
fd8b60 |
expand_dec_key(krb5_key key)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
if (CACHE(key)->dec_ctx.keybitlen)
|
|
Packit |
fd8b60 |
return;
|
|
Packit |
fd8b60 |
if (camellia_dec_key(key->keyblock.contents, key->keyblock.length,
|
|
Packit |
fd8b60 |
&CACHE(key)->dec_ctx) != camellia_good)
|
|
Packit |
fd8b60 |
abort();
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* CBC encrypt nblocks blocks of data in place, using and updating iv. */
|
|
Packit |
fd8b60 |
static inline void
|
|
Packit |
fd8b60 |
cbc_enc(krb5_key key, unsigned char *data, size_t nblocks, unsigned char *iv)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
for (; nblocks > 0; nblocks--, data += BLOCK_SIZE) {
|
|
Packit |
fd8b60 |
xorblock(iv, data);
|
|
Packit |
fd8b60 |
if (camellia_enc_blk(data, data, &CACHE(key)->enc_ctx) !=
|
|
Packit |
fd8b60 |
camellia_good)
|
|
Packit |
fd8b60 |
abort();
|
|
Packit |
fd8b60 |
memcpy(iv, data, BLOCK_SIZE);
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* CBC decrypt nblocks blocks of data in place, using and updating iv. */
|
|
Packit |
fd8b60 |
static inline void
|
|
Packit |
fd8b60 |
cbc_dec(krb5_key key, unsigned char *data, size_t nblocks, unsigned char *iv)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
unsigned char last_cipherblock[BLOCK_SIZE];
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
assert(nblocks > 0);
|
|
Packit |
fd8b60 |
data += (nblocks - 1) * BLOCK_SIZE;
|
|
Packit |
fd8b60 |
memcpy(last_cipherblock, data, BLOCK_SIZE);
|
|
Packit |
fd8b60 |
for (; nblocks > 0; nblocks--, data -= BLOCK_SIZE) {
|
|
Packit |
fd8b60 |
if (camellia_dec_blk(data, data, &CACHE(key)->dec_ctx) !=
|
|
Packit |
fd8b60 |
camellia_good)
|
|
Packit |
fd8b60 |
abort();
|
|
Packit |
fd8b60 |
xorblock(nblocks == 1 ? iv : data - BLOCK_SIZE, data);
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
memcpy(iv, last_cipherblock, BLOCK_SIZE);
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static krb5_error_code
|
|
Packit |
fd8b60 |
krb5int_camellia_encrypt(krb5_key key, const krb5_data *ivec,
|
|
Packit |
fd8b60 |
krb5_crypto_iov *data, size_t num_data)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
unsigned char iv[BLOCK_SIZE], block[BLOCK_SIZE];
|
|
Packit |
fd8b60 |
unsigned char blockN2[BLOCK_SIZE], blockN1[BLOCK_SIZE];
|
|
Packit |
fd8b60 |
size_t input_length, nblocks, ncontig;
|
|
Packit |
fd8b60 |
struct iov_cursor cursor;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (init_key_cache(key))
|
|
Packit |
fd8b60 |
return ENOMEM;
|
|
Packit |
fd8b60 |
expand_enc_key(key);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
k5_iov_cursor_init(&cursor, data, num_data, BLOCK_SIZE, FALSE);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
input_length = iov_total_length(data, num_data, FALSE);
|
|
Packit |
fd8b60 |
nblocks = (input_length + BLOCK_SIZE - 1) / BLOCK_SIZE;
|
|
Packit |
fd8b60 |
if (nblocks == 1) {
|
|
Packit |
fd8b60 |
k5_iov_cursor_get(&cursor, block);
|
|
Packit |
fd8b60 |
memset(iv, 0, BLOCK_SIZE);
|
|
Packit |
fd8b60 |
cbc_enc(key, block, 1, iv);
|
|
Packit |
fd8b60 |
k5_iov_cursor_put(&cursor, block);
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (ivec != NULL)
|
|
Packit |
fd8b60 |
memcpy(iv, ivec->data, BLOCK_SIZE);
|
|
Packit |
fd8b60 |
else
|
|
Packit |
fd8b60 |
memset(iv, 0, BLOCK_SIZE);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
while (nblocks > 2) {
|
|
Packit |
fd8b60 |
ncontig = iov_cursor_contig_blocks(&cursor);
|
|
Packit |
fd8b60 |
if (ncontig > 0) {
|
|
Packit |
fd8b60 |
/* Encrypt a series of contiguous blocks in place if we can, but
|
|
Packit |
fd8b60 |
* don't touch the last two blocks. */
|
|
Packit |
fd8b60 |
ncontig = (ncontig > nblocks - 2) ? nblocks - 2 : ncontig;
|
|
Packit |
fd8b60 |
cbc_enc(key, iov_cursor_ptr(&cursor), ncontig, iv);
|
|
Packit |
fd8b60 |
iov_cursor_advance(&cursor, ncontig);
|
|
Packit |
fd8b60 |
nblocks -= ncontig;
|
|
Packit |
fd8b60 |
} else {
|
|
Packit |
fd8b60 |
k5_iov_cursor_get(&cursor, block);
|
|
Packit |
fd8b60 |
cbc_enc(key, block, 1, iv);
|
|
Packit |
fd8b60 |
k5_iov_cursor_put(&cursor, block);
|
|
Packit |
fd8b60 |
nblocks--;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Encrypt the last two blocks and put them back in reverse order, possibly
|
|
Packit |
fd8b60 |
* truncating the encrypted second-to-last block. */
|
|
Packit |
fd8b60 |
k5_iov_cursor_get(&cursor, blockN2);
|
|
Packit |
fd8b60 |
k5_iov_cursor_get(&cursor, blockN1);
|
|
Packit |
fd8b60 |
cbc_enc(key, blockN2, 1, iv);
|
|
Packit |
fd8b60 |
cbc_enc(key, blockN1, 1, iv);
|
|
Packit |
fd8b60 |
k5_iov_cursor_put(&cursor, blockN1);
|
|
Packit |
fd8b60 |
k5_iov_cursor_put(&cursor, blockN2);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (ivec != NULL)
|
|
Packit |
fd8b60 |
memcpy(ivec->data, iv, BLOCK_SIZE);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static krb5_error_code
|
|
Packit |
fd8b60 |
krb5int_camellia_decrypt(krb5_key key, const krb5_data *ivec,
|
|
Packit |
fd8b60 |
krb5_crypto_iov *data, size_t num_data)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
unsigned char iv[BLOCK_SIZE], dummy_iv[BLOCK_SIZE], block[BLOCK_SIZE];
|
|
Packit |
fd8b60 |
unsigned char blockN2[BLOCK_SIZE], blockN1[BLOCK_SIZE];
|
|
Packit |
fd8b60 |
size_t input_length, last_len, nblocks, ncontig;
|
|
Packit |
fd8b60 |
struct iov_cursor cursor;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (init_key_cache(key))
|
|
Packit |
fd8b60 |
return ENOMEM;
|
|
Packit |
fd8b60 |
expand_dec_key(key);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
k5_iov_cursor_init(&cursor, data, num_data, BLOCK_SIZE, FALSE);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
input_length = iov_total_length(data, num_data, FALSE);
|
|
Packit |
fd8b60 |
nblocks = (input_length + BLOCK_SIZE - 1) / BLOCK_SIZE;
|
|
Packit |
fd8b60 |
last_len = input_length - (nblocks - 1) * BLOCK_SIZE;
|
|
Packit |
fd8b60 |
if (nblocks == 1) {
|
|
Packit |
fd8b60 |
k5_iov_cursor_get(&cursor, block);
|
|
Packit |
fd8b60 |
memset(iv, 0, BLOCK_SIZE);
|
|
Packit |
fd8b60 |
cbc_dec(key, block, 1, iv);
|
|
Packit |
fd8b60 |
k5_iov_cursor_put(&cursor, block);
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (ivec != NULL)
|
|
Packit |
fd8b60 |
memcpy(iv, ivec->data, BLOCK_SIZE);
|
|
Packit |
fd8b60 |
else
|
|
Packit |
fd8b60 |
memset(iv, 0, BLOCK_SIZE);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
while (nblocks > 2) {
|
|
Packit |
fd8b60 |
ncontig = iov_cursor_contig_blocks(&cursor);
|
|
Packit |
fd8b60 |
if (ncontig > 0) {
|
|
Packit |
fd8b60 |
/* Encrypt a series of contiguous blocks in place if we can, but
|
|
Packit |
fd8b60 |
* don't touch the last two blocks. */
|
|
Packit |
fd8b60 |
ncontig = (ncontig > nblocks - 2) ? nblocks - 2 : ncontig;
|
|
Packit |
fd8b60 |
cbc_dec(key, iov_cursor_ptr(&cursor), ncontig, iv);
|
|
Packit |
fd8b60 |
iov_cursor_advance(&cursor, ncontig);
|
|
Packit |
fd8b60 |
nblocks -= ncontig;
|
|
Packit |
fd8b60 |
} else {
|
|
Packit |
fd8b60 |
k5_iov_cursor_get(&cursor, block);
|
|
Packit |
fd8b60 |
cbc_dec(key, block, 1, iv);
|
|
Packit |
fd8b60 |
k5_iov_cursor_put(&cursor, block);
|
|
Packit |
fd8b60 |
nblocks--;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Get the last two ciphertext blocks. Save the first as the new iv. */
|
|
Packit |
fd8b60 |
k5_iov_cursor_get(&cursor, blockN2);
|
|
Packit |
fd8b60 |
k5_iov_cursor_get(&cursor, blockN1);
|
|
Packit |
fd8b60 |
if (ivec != NULL)
|
|
Packit |
fd8b60 |
memcpy(ivec->data, blockN2, BLOCK_SIZE);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Decrypt the second-to-last ciphertext block, using the final ciphertext
|
|
Packit |
fd8b60 |
* block as the CBC IV. This produces the final plaintext block. */
|
|
Packit |
fd8b60 |
memcpy(dummy_iv, blockN1, sizeof(dummy_iv));
|
|
Packit |
fd8b60 |
cbc_dec(key, blockN2, 1, dummy_iv);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Use the final bits of the decrypted plaintext to pad the last ciphertext
|
|
Packit |
fd8b60 |
* block, and decrypt it to produce the second-to-last plaintext block. */
|
|
Packit |
fd8b60 |
memcpy(blockN1 + last_len, blockN2 + last_len, BLOCK_SIZE - last_len);
|
|
Packit |
fd8b60 |
cbc_dec(key, blockN1, 1, iv);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Put the last two plaintext blocks back into the iovec. */
|
|
Packit |
fd8b60 |
k5_iov_cursor_put(&cursor, blockN1);
|
|
Packit |
fd8b60 |
k5_iov_cursor_put(&cursor, blockN2);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
krb5_error_code
|
|
Packit |
fd8b60 |
krb5int_camellia_cbc_mac(krb5_key key, const krb5_crypto_iov *data,
|
|
Packit |
fd8b60 |
size_t num_data, const krb5_data *ivec,
|
|
Packit |
fd8b60 |
krb5_data *output)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
unsigned char iv[BLOCK_SIZE], block[BLOCK_SIZE];
|
|
Packit |
fd8b60 |
struct iov_cursor cursor;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (output->length < BLOCK_SIZE)
|
|
Packit |
fd8b60 |
return KRB5_BAD_MSIZE;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (init_key_cache(key))
|
|
Packit |
fd8b60 |
return ENOMEM;
|
|
Packit |
fd8b60 |
expand_enc_key(key);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (ivec != NULL)
|
|
Packit |
fd8b60 |
memcpy(iv, ivec->data, BLOCK_SIZE);
|
|
Packit |
fd8b60 |
else
|
|
Packit |
fd8b60 |
memset(iv, 0, BLOCK_SIZE);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
k5_iov_cursor_init(&cursor, data, num_data, BLOCK_SIZE, FALSE);
|
|
Packit |
fd8b60 |
while (k5_iov_cursor_get(&cursor, block))
|
|
Packit |
fd8b60 |
cbc_enc(key, block, 1, iv);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
output->length = BLOCK_SIZE;
|
|
Packit |
fd8b60 |
memcpy(output->data, iv, BLOCK_SIZE);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static krb5_error_code
|
|
Packit |
fd8b60 |
camellia_init_state(const krb5_keyblock *key, krb5_keyusage usage,
|
|
Packit |
fd8b60 |
krb5_data *state)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
state->length = 16;
|
|
Packit |
fd8b60 |
state->data = malloc(16);
|
|
Packit |
fd8b60 |
if (state->data == NULL)
|
|
Packit |
fd8b60 |
return ENOMEM;
|
|
Packit |
fd8b60 |
memset(state->data, 0, state->length);
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static void
|
|
Packit |
fd8b60 |
camellia_key_cleanup(krb5_key key)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
zapfree(key->cache, sizeof(struct camellia_key_info_cache));
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
const struct krb5_enc_provider krb5int_enc_camellia128 = {
|
|
Packit |
fd8b60 |
16,
|
|
Packit |
fd8b60 |
16, 16,
|
|
Packit |
fd8b60 |
krb5int_camellia_encrypt,
|
|
Packit |
fd8b60 |
krb5int_camellia_decrypt,
|
|
Packit |
fd8b60 |
krb5int_camellia_cbc_mac,
|
|
Packit |
fd8b60 |
camellia_init_state,
|
|
Packit |
fd8b60 |
krb5int_default_free_state,
|
|
Packit |
fd8b60 |
camellia_key_cleanup
|
|
Packit |
fd8b60 |
};
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
const struct krb5_enc_provider krb5int_enc_camellia256 = {
|
|
Packit |
fd8b60 |
16,
|
|
Packit |
fd8b60 |
32, 32,
|
|
Packit |
fd8b60 |
krb5int_camellia_encrypt,
|
|
Packit |
fd8b60 |
krb5int_camellia_decrypt,
|
|
Packit |
fd8b60 |
krb5int_camellia_cbc_mac,
|
|
Packit |
fd8b60 |
camellia_init_state,
|
|
Packit |
fd8b60 |
krb5int_default_free_state,
|
|
Packit |
fd8b60 |
camellia_key_cleanup
|
|
Packit |
fd8b60 |
};
|