diff options
Diffstat (limited to 'src/include/s390_cmac.h')
-rw-r--r-- | src/include/s390_cmac.h | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/src/include/s390_cmac.h b/src/include/s390_cmac.h new file mode 100644 index 0000000..1358a9e --- /dev/null +++ b/src/include/s390_cmac.h @@ -0,0 +1,194 @@ +/* This program is released under the Common Public License V1.0 + * + * You should have received a copy of Common Public License V1.0 along with + * with this program. + */ + +/** + * Authors: Ruben Straus <rstraus@de.ibm.com> + * Holger Dengler <hd@linux.vnet.ibm.com> + * + * Copyright IBM Corp. 2010, 2011 + */ + +#include <string.h> +#include <errno.h> +#include <openssl/aes.h> + +#include "ica_api.h" +#include "icastats.h" +#include "init.h" +#include "s390_crypto.h" +#include "s390_aes.h" +#include "s390_des.h" + +#ifndef S390_CMAC +#define S390_CMAC_H + +#define PARM_BLOCK_SIZE 72 + +typedef unsigned char parm_block_t[PARM_BLOCK_SIZE]; + +struct parm_block_lookup { + unsigned int block_size; + unsigned char *base; + uint8_t *ml; + unsigned char *message; + unsigned char *iv; + unsigned char *keys; +}; + +static inline void parm_block_lookup_init(struct parm_block_lookup *lookup, + parm_block_t base, + unsigned int block_size) +{ + lookup->block_size = block_size; + lookup->base = base; + lookup->ml = (uint8_t *)base; + lookup->message = (unsigned char *)(base + 8); + lookup->iv = (unsigned char *)(lookup->message + block_size); + lookup->keys = (unsigned char *)(lookup->iv + block_size); +} + +static inline unsigned int fc_block_size(unsigned int fc) +{ + unsigned int rc; + + switch(fc) { + case S390_CRYPTO_DEA_ENCRYPT: + case S390_CRYPTO_TDEA_128_ENCRYPT: + case S390_CRYPTO_TDEA_192_ENCRYPT: + rc = DES_BLOCK_SIZE; + break; + case S390_CRYPTO_AES_128_ENCRYPT: + case S390_CRYPTO_AES_192_ENCRYPT: + case S390_CRYPTO_AES_256_ENCRYPT: + default: + rc = AES_BLOCK_SIZE; + break; + } + + return rc; +} + +static inline void _stats_increment(unsigned int fc, int hw, int direction) +{ + switch(fc) { + case 1: + case 9: + stats_increment(ICA_STATS_DES_CMAC, hw, direction); + break; + case 2: + case 3: + case 10: + case 11: + stats_increment(ICA_STATS_3DES_CMAC, hw, direction); + break; + case 18: + case 19: + case 20: + case 26: + case 27: + case 28: + stats_increment(ICA_STATS_AES_CMAC, hw, direction); + break; + default: + break; + } +} + +static inline int s390_cmac_hw(unsigned long fc, + const unsigned char *message, + unsigned long message_length, + unsigned int key_size, const unsigned char *key, + unsigned int cmac_length, unsigned char *cmac, + unsigned char *iv) +{ + parm_block_t parm_block; + struct parm_block_lookup pb_lookup; + unsigned int length_tail; + unsigned long length_head; + int rc; + + /* CMAC uses encrypt function code for generate and verify. */ + fc &= S390_CRYPTO_FUNCTION_MASK; + memset(parm_block, 0, sizeof(parm_block)); + + parm_block_lookup_init(&pb_lookup, parm_block, fc_block_size(fc)); + memcpy(pb_lookup.keys, key, key_size); + + /* copy iv into param block, if available (intermediate) */ + if (iv != NULL) + memcpy(pb_lookup.iv, iv, pb_lookup.block_size); + + + if (cmac == NULL) { + /* intermediate */ + rc = s390_kmac(fc, pb_lookup.iv, message, message_length); + memset(pb_lookup.keys, 0, key_size); + if (rc < 0) + return rc; + + _stats_increment(fc, ALGO_HW, ENCRYPT); + + /* rescue iv for chained calls (intermediate) */ + memcpy(iv, pb_lookup.iv, pb_lookup.block_size); + } else { + if (message_length) { + length_tail = message_length % pb_lookup.block_size; + if (length_tail) + length_head = message_length - length_tail; + else { + length_head = message_length - pb_lookup.block_size; + length_tail = pb_lookup.block_size; + } + + if (length_head) { + rc = s390_kmac(fc, pb_lookup.iv, + message, length_head); + if (rc < 0) { + memset(pb_lookup.keys, 0, key_size); + return EIO; + } + _stats_increment(fc, ALGO_HW, ENCRYPT); + } + + *pb_lookup.ml = length_tail * 8; /* message length in bits */ + memcpy(pb_lookup.message, message + length_head, length_tail); + } + /* calculate final block (last/full) */ + rc = s390_pcc(fc, pb_lookup.base); + memset(pb_lookup.keys, 0, key_size); + if (rc < 0) + return EIO; + + _stats_increment(fc, ALGO_HW, ENCRYPT); + memcpy(cmac, pb_lookup.iv, cmac_length); + } + + return 0; +} + +static inline int s390_cmac(unsigned long fc, + const unsigned char *message, + unsigned long message_length, + unsigned int key_length, const unsigned char *key, + unsigned int mac_length, unsigned char *mac, + unsigned char *iv) +{ + int rc; + + if (*s390_msa4_functions[fc].enabled) + rc = s390_cmac_hw(s390_msa4_functions[fc].hw_fc, + message, message_length, + key_length, key, + mac_length, mac, + iv); + else { + return EPERM; + } + + return rc; +} +#endif + |