strongswan/src/libstrongswan/plugins/cmac/cmac.c

398 lines
7.5 KiB
C

/*
* Copyright (C) 2012 Tobias Brunner
* HSR Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include <string.h>
#include "cmac.h"
#include <utils/debug.h>
#include <crypto/mac.h>
#include <crypto/prfs/mac_prf.h>
#include <crypto/signers/mac_signer.h>
typedef struct private_mac_t private_mac_t;
/**
* Private data of a mac_t object.
*
* The variable names are the same as in the RFC.
*/
struct private_mac_t {
/**
* Public interface.
*/
mac_t public;
/**
* Block size, in bytes
*/
uint8_t b;
/**
* Crypter with key K
*/
crypter_t *k;
/**
* K1
*/
uint8_t *k1;
/**
* K2
*/
uint8_t *k2;
/**
* T
*/
uint8_t *t;
/**
* remaining, unprocessed bytes in append mode
*/
uint8_t *remaining;
/**
* number of bytes in remaining
*/
int remaining_bytes;
};
/**
* process supplied data, but do not run final operation
*/
static bool update(private_mac_t *this, chunk_t data)
{
chunk_t iv;
if (this->remaining_bytes + data.len <= this->b)
{ /* no complete block (or last block), just copy into remaining */
memcpy(this->remaining + this->remaining_bytes, data.ptr, data.len);
this->remaining_bytes += data.len;
return TRUE;
}
iv = chunk_alloca(this->b);
memset(iv.ptr, 0, iv.len);
/* T := 0x00000000000000000000000000000000 (initially)
* for each block M_i (except the last)
* X := T XOR M_i;
* T := AES-128(K, X);
*/
/* append data to remaining bytes, process block M_1 */
memcpy(this->remaining + this->remaining_bytes, data.ptr,
this->b - this->remaining_bytes);
data = chunk_skip(data, this->b - this->remaining_bytes);
memxor(this->t, this->remaining, this->b);
if (!this->k->encrypt(this->k, chunk_create(this->t, this->b), iv, NULL))
{
return FALSE;
}
/* process blocks M_2 ... M_n-1 */
while (data.len > this->b)
{
memcpy(this->remaining, data.ptr, this->b);
data = chunk_skip(data, this->b);
memxor(this->t, this->remaining, this->b);
if (!this->k->encrypt(this->k, chunk_create(this->t, this->b), iv, NULL))
{
return FALSE;
}
}
/* store remaining bytes of block M_n */
memcpy(this->remaining, data.ptr, data.len);
this->remaining_bytes = data.len;
return TRUE;
}
/**
* process last block M_last
*/
static bool final(private_mac_t *this, uint8_t *out)
{
chunk_t iv;
iv = chunk_alloca(this->b);
memset(iv.ptr, 0, iv.len);
/* if last block is complete
* M_last := M_n XOR K1;
* else
* M_last := padding(M_n) XOR K2;
*/
if (this->remaining_bytes == this->b)
{
memxor(this->remaining, this->k1, this->b);
}
else
{
/* padding(x) = x || 10^i where i is 128-8*r-1
* That is, padding(x) is the concatenation of x and a single '1',
* followed by the minimum number of '0's, so that the total length is
* equal to 128 bits.
*/
if (this->remaining_bytes < this->b)
{
this->remaining[this->remaining_bytes] = 0x80;
while (++this->remaining_bytes < this->b)
{
this->remaining[this->remaining_bytes] = 0x00;
}
}
memxor(this->remaining, this->k2, this->b);
}
/* T := M_last XOR T;
* T := AES-128(K,T);
*/
memxor(this->t, this->remaining, this->b);
if (!this->k->encrypt(this->k, chunk_create(this->t, this->b), iv, NULL))
{
return FALSE;
}
memcpy(out, this->t, this->b);
/* reset state */
memset(this->t, 0, this->b);
this->remaining_bytes = 0;
return TRUE;
}
METHOD(mac_t, get_mac, bool,
private_mac_t *this, chunk_t data, uint8_t *out)
{
/* update T, do not process last block */
if (!update(this, data))
{
return FALSE;
}
if (out)
{ /* if not in append mode, process last block and output result */
return final(this, out);
}
return TRUE;
}
METHOD(mac_t, get_mac_size, size_t,
private_mac_t *this)
{
return this->b;
}
/**
* Left-shift the given chunk by one bit.
*/
static void bit_shift(chunk_t chunk)
{
size_t i;
for (i = 0; i < chunk.len; i++)
{
chunk.ptr[i] <<= 1;
if (i < chunk.len - 1 && chunk.ptr[i + 1] & 0x80)
{
chunk.ptr[i] |= 0x01;
}
}
}
/**
* Apply the following key derivation (in-place):
* if MSB(C) == 0
* C := C << 1
* else
* C := (C << 1) XOR 0x00000000000000000000000000000087
*/
static void derive_key(chunk_t chunk)
{
if (chunk.ptr[0] & 0x80)
{
chunk_t rb;
rb = chunk_alloca(chunk.len);
memset(rb.ptr, 0, rb.len);
rb.ptr[rb.len - 1] = 0x87;
bit_shift(chunk);
memxor(chunk.ptr, rb.ptr, chunk.len);
}
else
{
bit_shift(chunk);
}
}
METHOD(mac_t, set_key, bool,
private_mac_t *this, chunk_t key)
{
chunk_t resized, iv, l;
memset(this->t, 0, this->b);
this->remaining_bytes = 0;
/* we support variable keys as defined in RFC 4615 */
if (key.len == this->b)
{
resized = key;
}
else
{ /* use cmac recursively to resize longer or shorter keys */
resized = chunk_alloca(this->b);
memset(resized.ptr, 0, resized.len);
if (!set_key(this, resized) ||
!get_mac(this, key, resized.ptr))
{
return FALSE;
}
}
/*
* Rb = 0x00000000000000000000000000000087
* L = 0x00000000000000000000000000000000 encrypted with K
* if MSB(L) == 0
* K1 = L << 1
* else
* K1 = (L << 1) XOR Rb
* if MSB(K1) == 0
* K2 = K1 << 1
* else
* K2 = (K1 << 1) XOR Rb
*/
iv = chunk_alloca(this->b);
memset(iv.ptr, 0, iv.len);
l = chunk_alloca(this->b);
memset(l.ptr, 0, l.len);
if (!this->k->set_key(this->k, resized) ||
!this->k->encrypt(this->k, l, iv, NULL))
{
return FALSE;
}
derive_key(l);
memcpy(this->k1, l.ptr, l.len);
derive_key(l);
memcpy(this->k2, l.ptr, l.len);
memwipe(l.ptr, l.len);
return TRUE;
}
METHOD(mac_t, destroy, void,
private_mac_t *this)
{
this->k->destroy(this->k);
memwipe(this->k1, this->b);
free(this->k1);
memwipe(this->k2, this->b);
free(this->k2);
free(this->t);
free(this->remaining);
free(this);
}
/*
* Described in header
*/
mac_t *cmac_create(encryption_algorithm_t algo, size_t key_size)
{
private_mac_t *this;
crypter_t *crypter;
uint8_t b;
crypter = lib->crypto->create_crypter(lib->crypto, algo, key_size);
if (!crypter)
{
return NULL;
}
b = crypter->get_block_size(crypter);
/* input and output of crypter must be equal for cmac */
if (b != key_size)
{
crypter->destroy(crypter);
return NULL;
}
INIT(this,
.public = {
.get_mac = _get_mac,
.get_mac_size = _get_mac_size,
.set_key = _set_key,
.destroy = _destroy,
},
.b = b,
.k = crypter,
.k1 = malloc(b),
.k2 = malloc(b),
.t = malloc(b),
.remaining = malloc(b),
);
memset(this->t, 0, b);
return &this->public;
}
/*
* Described in header.
*/
prf_t *cmac_prf_create(pseudo_random_function_t algo)
{
mac_t *cmac;
switch (algo)
{
case PRF_AES128_CMAC:
cmac = cmac_create(ENCR_AES_CBC, 16);
break;
default:
return NULL;
}
if (cmac)
{
return mac_prf_create(cmac);
}
return NULL;
}
/*
* Described in header
*/
signer_t *cmac_signer_create(integrity_algorithm_t algo)
{
size_t truncation;
mac_t *cmac;
switch (algo)
{
case AUTH_AES_CMAC_96:
cmac = cmac_create(ENCR_AES_CBC, 16);
truncation = 12;
break;
default:
return NULL;
}
if (cmac)
{
return mac_signer_create(cmac, truncation);
}
return NULL;
}