diff options
author | Matt Barden <matt.barden@nexenta.com> | 2017-09-04 07:25:59 -0500 |
---|---|---|
committer | Gordon Ross <gwr@nexenta.com> | 2017-11-30 19:44:43 -0500 |
commit | cd964fce751ca752af4158842063a9579a2d4331 (patch) | |
tree | 968b949de63f645bbbd84739221812eb3093530d /usr/src/common | |
parent | 6dfcdabd85f09409c5d2f9fb25a3013384ffaf74 (diff) | |
download | illumos-joyent-cd964fce751ca752af4158842063a9579a2d4331.tar.gz |
5869 Need AES CMAC support in KCF+PKCS11
Portions contributed by: Jason King <jason.king@joyent.com>
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: Dan McDonald <danmcd@joyent.com>
Approved by: Gordon Ross <gwr@nexenta.com>
Diffstat (limited to 'usr/src/common')
-rw-r--r-- | usr/src/common/crypto/aes/aes_impl.h | 3 | ||||
-rw-r--r-- | usr/src/common/crypto/aes/aes_modes.c | 2 | ||||
-rw-r--r-- | usr/src/common/crypto/modes/cbc.c | 190 | ||||
-rw-r--r-- | usr/src/common/crypto/modes/ctr.c | 24 | ||||
-rw-r--r-- | usr/src/common/crypto/modes/modes.c | 218 | ||||
-rw-r--r-- | usr/src/common/crypto/modes/modes.h | 15 |
6 files changed, 412 insertions, 40 deletions
diff --git a/usr/src/common/crypto/aes/aes_impl.h b/usr/src/common/crypto/aes/aes_impl.h index d44e261dda..d73729c03d 100644 --- a/usr/src/common/crypto/aes/aes_impl.h +++ b/usr/src/common/crypto/aes/aes_impl.h @@ -159,7 +159,8 @@ typedef enum aes_mech_type { AES_CTR_MECH_INFO_TYPE, /* SUN_CKM_AES_CTR */ AES_CCM_MECH_INFO_TYPE, /* SUN_CKM_AES_CCM */ AES_GCM_MECH_INFO_TYPE, /* SUN_CKM_AES_GCM */ - AES_GMAC_MECH_INFO_TYPE /* SUN_CKM_AES_GMAC */ + AES_GMAC_MECH_INFO_TYPE, /* SUN_CKM_AES_GMAC */ + AES_CMAC_MECH_INFO_TYPE /* SUN_CKM_AES_CMAC */ } aes_mech_type_t; #endif /* _KERNEL */ diff --git a/usr/src/common/crypto/aes/aes_modes.c b/usr/src/common/crypto/aes/aes_modes.c index a41f4d9fe4..884bfa934c 100644 --- a/usr/src/common/crypto/aes/aes_modes.c +++ b/usr/src/common/crypto/aes/aes_modes.c @@ -93,7 +93,7 @@ aes_encrypt_contiguous_blocks(void *ctx, char *data, size_t length, out, AES_BLOCK_LEN, aes_encrypt_block, aes_copy_block, aes_xor_block); #endif - } else if (aes_ctx->ac_flags & CBC_MODE) { + } else if (aes_ctx->ac_flags & (CBC_MODE|CMAC_MODE)) { rv = cbc_encrypt_contiguous_blocks(ctx, data, length, out, AES_BLOCK_LEN, aes_encrypt_block, aes_copy_block, aes_xor_block); diff --git a/usr/src/common/crypto/modes/cbc.c b/usr/src/common/crypto/modes/cbc.c index 3fb17ee173..69f43eba61 100644 --- a/usr/src/common/crypto/modes/cbc.c +++ b/usr/src/common/crypto/modes/cbc.c @@ -21,6 +21,7 @@ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ #ifndef _KERNEL @@ -30,10 +31,16 @@ #include <security/cryptoki.h> #endif +#include <sys/debug.h> #include <sys/types.h> #include <modes/modes.h> #include <sys/crypto/common.h> #include <sys/crypto/impl.h> +#include <aes/aes_impl.h> + +/* These are the CMAC Rb constants from NIST SP 800-38B */ +#define CONST_RB_128 0x87 +#define CONST_RB_64 0x1B /* * Algorithm independent CBC functions. @@ -56,7 +63,7 @@ cbc_encrypt_contiguous_blocks(cbc_ctx_t *ctx, char *data, size_t length, uint8_t *out_data_2; size_t out_data_1_len; - if (length + ctx->cbc_remainder_len < block_size) { + if (length + ctx->cbc_remainder_len < ctx->max_remain) { /* accumulate bytes here and return */ bcopy(datap, (uint8_t *)ctx->cbc_remainder + ctx->cbc_remainder_len, @@ -97,7 +104,8 @@ cbc_encrypt_contiguous_blocks(cbc_ctx_t *ctx, char *data, size_t length, ctx->cbc_lastp = blockp; lastp = blockp; - if (ctx->cbc_remainder_len > 0) { + if ((ctx->cbc_flags & CMAC_MODE) == 0 && + ctx->cbc_remainder_len > 0) { bcopy(blockp, ctx->cbc_copy_to, ctx->cbc_remainder_len); bcopy(blockp + ctx->cbc_remainder_len, datap, @@ -110,22 +118,31 @@ cbc_encrypt_contiguous_blocks(cbc_ctx_t *ctx, char *data, size_t length, */ xor_block(blockp, lastp); encrypt(ctx->cbc_keysched, lastp, lastp); - crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1, - &out_data_1_len, &out_data_2, block_size); - /* copy block to where it belongs */ - if (out_data_1_len == block_size) { - copy_block(lastp, out_data_1); - } else { - bcopy(lastp, out_data_1, out_data_1_len); - if (out_data_2 != NULL) { - bcopy(lastp + out_data_1_len, - out_data_2, - block_size - out_data_1_len); + /* + * CMAC doesn't output until encrypt_final + */ + if ((ctx->cbc_flags & CMAC_MODE) == 0) { + crypto_get_ptrs(out, &iov_or_mp, &offset, + &out_data_1, &out_data_1_len, + &out_data_2, block_size); + + /* copy block to where it belongs */ + if (out_data_1_len == block_size) { + copy_block(lastp, out_data_1); + } else { + bcopy(lastp, out_data_1, + out_data_1_len); + if (out_data_2 != NULL) { + bcopy(lastp + out_data_1_len, + out_data_2, + block_size - + out_data_1_len); + } } + /* update offset */ + out->cd_offset += block_size; } - /* update offset */ - out->cd_offset += block_size; } /* Update pointer to next block of data to be processed. */ @@ -139,7 +156,7 @@ cbc_encrypt_contiguous_blocks(cbc_ctx_t *ctx, char *data, size_t length, remainder = (size_t)&data[length] - (size_t)datap; /* Incomplete last block. */ - if (remainder > 0 && remainder < block_size) { + if (remainder > 0 && remainder < ctx->max_remain) { bcopy(datap, ctx->cbc_remainder, remainder); ctx->cbc_remainder_len = remainder; ctx->cbc_copy_to = datap; @@ -299,14 +316,19 @@ cbc_init_ctx(cbc_ctx_t *cbc_ctx, char *param, size_t param_len, cbc_ctx->cbc_lastp = (uint8_t *)&cbc_ctx->cbc_iv[0]; cbc_ctx->cbc_flags |= CBC_MODE; + cbc_ctx->max_remain = block_size; return (CRYPTO_SUCCESS); } /* ARGSUSED */ -void * -cbc_alloc_ctx(int kmflag) +static void * +cbc_cmac_alloc_ctx(int kmflag, uint32_t mode) { cbc_ctx_t *cbc_ctx; + uint32_t modeval = mode & (CBC_MODE|CMAC_MODE); + + /* Only one of the two modes can be set */ + VERIFY(modeval == CBC_MODE || modeval == CMAC_MODE); #ifdef _KERNEL if ((cbc_ctx = kmem_zalloc(sizeof (cbc_ctx_t), kmflag)) == NULL) @@ -315,6 +337,136 @@ cbc_alloc_ctx(int kmflag) #endif return (NULL); - cbc_ctx->cbc_flags = CBC_MODE; + cbc_ctx->cbc_flags = mode; return (cbc_ctx); } + +void * +cbc_alloc_ctx(int kmflag) +{ + return (cbc_cmac_alloc_ctx(kmflag, CBC_MODE)); +} + +/* + * Algorithms for supporting AES-CMAC + * NOTE: CMAC is generally just a wrapper for CBC + */ + +void * +cmac_alloc_ctx(int kmflag) +{ + return (cbc_cmac_alloc_ctx(kmflag, CMAC_MODE)); +} + + +/* + * Typically max_remain is set to block_size - 1, since we usually + * will process the data once we have a full block. However with CMAC, + * we must preprocess the final block of data. Since we cannot know + * when we've received the final block of data until the _final() method + * is called, we must not process the last block of data until we know + * it is the last block, or we receive a new block of data. As such, + * max_remain for CMAC is block_size + 1. + */ +int +cmac_init_ctx(cbc_ctx_t *cbc_ctx, size_t block_size) +{ + /* + * CMAC is only approved for block sizes 64 and 128 bits / + * 8 and 16 bytes. + */ + + if (block_size != 16 && block_size != 8) + return (CRYPTO_INVALID_CONTEXT); + + /* + * For CMAC, cbc_iv is always 0. + */ + + cbc_ctx->cbc_iv[0] = 0; + cbc_ctx->cbc_iv[1] = 0; + + cbc_ctx->cbc_lastp = (uint8_t *)&cbc_ctx->cbc_iv[0]; + cbc_ctx->cbc_flags |= CMAC_MODE; + + cbc_ctx->max_remain = block_size + 1; + return (CRYPTO_SUCCESS); +} + +/* + * Left shifts blocks by one and returns the leftmost bit + */ +static uint8_t +cmac_left_shift_block_by1(uint8_t *block, size_t block_size) +{ + uint8_t carry = 0, old; + size_t i; + for (i = block_size; i > 0; i--) { + old = carry; + carry = (block[i - 1] & 0x80) ? 1 : 0; + block[i - 1] = (block[i - 1] << 1) | old; + } + return (carry); +} + +/* + * Generate subkeys to preprocess the last block according to RFC 4493. + * Store the final block_size MAC generated in 'out'. + */ +int +cmac_mode_final(cbc_ctx_t *cbc_ctx, crypto_data_t *out, + int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), + void (*xor_block)(uint8_t *, uint8_t *)) +{ + uint8_t buf[AES_BLOCK_LEN] = {0}; + uint8_t *M_last = (uint8_t *)cbc_ctx->cbc_remainder; + size_t length = cbc_ctx->cbc_remainder_len; + size_t block_size = cbc_ctx->max_remain - 1; + uint8_t const_rb; + + if (length > block_size) + return (CRYPTO_INVALID_CONTEXT); + + if (out->cd_length < block_size) + return (CRYPTO_DATA_LEN_RANGE); + + if (block_size == 16) + const_rb = CONST_RB_128; + else if (block_size == 8) + const_rb = CONST_RB_64; + else + return (CRYPTO_INVALID_CONTEXT); + + /* k_0 = E_k(0) */ + encrypt_block(cbc_ctx->cbc_keysched, buf, buf); + + if (cmac_left_shift_block_by1(buf, block_size)) + buf[block_size - 1] ^= const_rb; + + if (length == block_size) { + /* Last block complete, so m_n = k_1 + m_n' */ + xor_block(buf, M_last); + xor_block(cbc_ctx->cbc_lastp, M_last); + encrypt_block(cbc_ctx->cbc_keysched, M_last, M_last); + } else { + /* Last block incomplete, so m_n = k_2 + (m_n' | 100...0_bin) */ + if (cmac_left_shift_block_by1(buf, block_size)) + buf[block_size - 1] ^= const_rb; + + M_last[length] = 0x80; + bzero(M_last + length + 1, block_size - length - 1); + xor_block(buf, M_last); + xor_block(cbc_ctx->cbc_lastp, M_last); + encrypt_block(cbc_ctx->cbc_keysched, M_last, M_last); + } + + /* + * zero out the sub-key. + */ +#ifndef _KERNEL + explicit_bzero(&buf, sizeof (buf)); +#else + bzero(&buf, sizeof (buf)); +#endif + return (crypto_put_output_data(M_last, out, block_size)); +} diff --git a/usr/src/common/crypto/modes/ctr.c b/usr/src/common/crypto/modes/ctr.c index e44dc3e642..919ed3ab53 100644 --- a/usr/src/common/crypto/modes/ctr.c +++ b/usr/src/common/crypto/modes/ctr.c @@ -169,13 +169,9 @@ ctr_mode_final(ctr_ctx_t *ctx, crypto_data_t *out, int (*encrypt_block)(const void *, const uint8_t *, uint8_t *)) { uint8_t *lastp; - void *iov_or_mp; - offset_t offset; - uint8_t *out_data_1; - uint8_t *out_data_2; - size_t out_data_1_len; uint8_t *p; int i; + int rv; if (out->cd_length < ctx->ctr_remainder_len) return (CRYPTO_DATA_LEN_RANGE); @@ -189,23 +185,17 @@ ctr_mode_final(ctr_ctx_t *ctx, crypto_data_t *out, p[i] ^= lastp[i]; } - crypto_init_ptrs(out, &iov_or_mp, &offset); - crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1, - &out_data_1_len, &out_data_2, ctx->ctr_remainder_len); - - bcopy(p, out_data_1, out_data_1_len); - if (out_data_2 != NULL) { - bcopy((uint8_t *)p + out_data_1_len, - out_data_2, ctx->ctr_remainder_len - out_data_1_len); + rv = crypto_put_output_data(p, out, ctx->ctr_remainder_len); + if (rv == CRYPTO_SUCCESS) { + out->cd_offset += ctx->ctr_remainder_len; + ctx->ctr_remainder_len = 0; } - out->cd_offset += ctx->ctr_remainder_len; - ctx->ctr_remainder_len = 0; - return (CRYPTO_SUCCESS); + return (rv); } int ctr_init_ctx(ctr_ctx_t *ctr_ctx, ulong_t count, uint8_t *cb, -void (*copy_block)(uint8_t *, uint8_t *)) + void (*copy_block)(uint8_t *, uint8_t *)) { uint64_t upper_mask = 0; uint64_t lower_mask = 0; diff --git a/usr/src/common/crypto/modes/modes.c b/usr/src/common/crypto/modes/modes.c index 19bf8cc16c..fbf66c0531 100644 --- a/usr/src/common/crypto/modes/modes.c +++ b/usr/src/common/crypto/modes/modes.c @@ -21,10 +21,14 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #ifndef _KERNEL #include <stdlib.h> +#include <assert.h> +#include <strings.h> #endif #include <sys/strsun.h> @@ -170,8 +174,8 @@ crypto_free_mode_ctx(void *ctx) { common_ctx_t *common_ctx = (common_ctx_t *)ctx; - switch (common_ctx->cc_flags & - (ECB_MODE|CBC_MODE|CTR_MODE|CCM_MODE|GCM_MODE|GMAC_MODE)) { + switch (common_ctx->cc_flags & (ECB_MODE|CBC_MODE|CMAC_MODE|CTR_MODE| + CCM_MODE|GCM_MODE|GMAC_MODE)) { case ECB_MODE: #ifdef _KERNEL kmem_free(common_ctx, sizeof (ecb_ctx_t)); @@ -181,6 +185,7 @@ crypto_free_mode_ctx(void *ctx) break; case CBC_MODE: + case CMAC_MODE: #ifdef _KERNEL kmem_free(common_ctx, sizeof (cbc_ctx_t)); #else @@ -225,3 +230,212 @@ crypto_free_mode_ctx(void *ctx) #endif } } + +/* + * Utility routine to apply the command, 'cmd', to the + * data in the uio structure. + */ +int +crypto_uio_data(crypto_data_t *data, uchar_t *buf, int len, cmd_type_t cmd, + void *digest_ctx, void (*update)()) +{ + uio_t *uiop = data->cd_uio; + off_t offset = data->cd_offset; + size_t length = len; + uint_t vec_idx; + size_t cur_len; + uchar_t *datap; + +#ifdef _KERNEL + ASSERT3U(data->cd_format, ==, CRYPTO_DATA_UIO); +#else + assert(data->cd_format == CRYPTO_DATA_UIO); +#endif + if (uiop->uio_segflg != UIO_SYSSPACE) { + return (CRYPTO_ARGUMENTS_BAD); + } + + /* + * Jump to the first iovec containing data to be + * processed. + */ + for (vec_idx = 0; vec_idx < uiop->uio_iovcnt && + offset >= uiop->uio_iov[vec_idx].iov_len; + offset -= uiop->uio_iov[vec_idx++].iov_len) + ; + + if (vec_idx == uiop->uio_iovcnt) { + /* + * The caller specified an offset that is larger than + * the total size of the buffers it provided. + */ + return (CRYPTO_DATA_LEN_RANGE); + } + + while (vec_idx < uiop->uio_iovcnt && length > 0) { + cur_len = MIN(uiop->uio_iov[vec_idx].iov_len - + offset, length); + + datap = (uchar_t *)(uiop->uio_iov[vec_idx].iov_base + + offset); + switch (cmd) { + case COPY_FROM_DATA: + bcopy(datap, buf, cur_len); + buf += cur_len; + break; + case COPY_TO_DATA: + bcopy(buf, datap, cur_len); + buf += cur_len; + break; + case COMPARE_TO_DATA: + if (bcmp(datap, buf, cur_len)) + return (CRYPTO_SIGNATURE_INVALID); + buf += cur_len; + break; + case MD5_DIGEST_DATA: + case SHA1_DIGEST_DATA: + case SHA2_DIGEST_DATA: + case GHASH_DATA: + update(digest_ctx, datap, cur_len); + break; + } + + length -= cur_len; + vec_idx++; + offset = 0; + } + + if (vec_idx == uiop->uio_iovcnt && length > 0) { + /* + * The end of the specified iovec's was reached but + * the length requested could not be processed. + */ + switch (cmd) { + case COPY_TO_DATA: + data->cd_length = len; + return (CRYPTO_BUFFER_TOO_SMALL); + default: + return (CRYPTO_DATA_LEN_RANGE); + } + } + + return (CRYPTO_SUCCESS); +} + +/* + * Utility routine to apply the command, 'cmd', to the + * data in the mblk structure. + */ +int +crypto_mblk_data(crypto_data_t *data, uchar_t *buf, int len, cmd_type_t cmd, + void *digest_ctx, void (*update)()) +{ + off_t offset = data->cd_offset; + size_t length = len; + mblk_t *mp; + size_t cur_len; + uchar_t *datap; + +#ifdef _KERNEL + ASSERT3U(data->cd_format, ==, CRYPTO_DATA_MBLK); +#else + assert(data->cd_format == CRYPTO_DATA_MBLK); +#endif + /* + * Jump to the first mblk_t containing data to be processed. + */ + for (mp = data->cd_mp; mp != NULL && offset >= MBLKL(mp); + offset -= MBLKL(mp), mp = mp->b_cont) + ; + if (mp == NULL) { + /* + * The caller specified an offset that is larger + * than the total size of the buffers it provided. + */ + return (CRYPTO_DATA_LEN_RANGE); + } + + /* + * Now do the processing on the mblk chain. + */ + while (mp != NULL && length > 0) { + cur_len = MIN(MBLKL(mp) - offset, length); + + datap = (uchar_t *)(mp->b_rptr + offset); + switch (cmd) { + case COPY_FROM_DATA: + bcopy(datap, buf, cur_len); + buf += cur_len; + break; + case COPY_TO_DATA: + bcopy(buf, datap, cur_len); + buf += cur_len; + break; + case COMPARE_TO_DATA: + if (bcmp(datap, buf, cur_len)) + return (CRYPTO_SIGNATURE_INVALID); + buf += cur_len; + break; + case MD5_DIGEST_DATA: + case SHA1_DIGEST_DATA: + case SHA2_DIGEST_DATA: + case GHASH_DATA: + update(digest_ctx, datap, cur_len); + break; + } + + length -= cur_len; + offset = 0; + mp = mp->b_cont; + } + + if (mp == NULL && length > 0) { + /* + * The end of the mblk was reached but the length + * requested could not be processed. + */ + switch (cmd) { + case COPY_TO_DATA: + data->cd_length = len; + return (CRYPTO_BUFFER_TOO_SMALL); + default: + return (CRYPTO_DATA_LEN_RANGE); + } + } + + return (CRYPTO_SUCCESS); +} + +/* + * Utility routine to copy a buffer to a crypto_data structure. + */ +int +crypto_put_output_data(uchar_t *buf, crypto_data_t *output, int len) +{ + switch (output->cd_format) { + case CRYPTO_DATA_RAW: + if (MAXOFF_T - output->cd_offset < (off_t)len) { + return (CRYPTO_ARGUMENTS_BAD); + } + if (output->cd_raw.iov_len < len + output->cd_offset) { + output->cd_length = len; + return (CRYPTO_BUFFER_TOO_SMALL); + } + bcopy(buf, (uchar_t *)(output->cd_raw.iov_base + + output->cd_offset), len); + break; + + case CRYPTO_DATA_UIO: + return (crypto_uio_data(output, buf, len, + COPY_TO_DATA, NULL, NULL)); + + case CRYPTO_DATA_MBLK: + return (crypto_mblk_data(output, buf, len, + COPY_TO_DATA, NULL, NULL)); + + default: + return (CRYPTO_ARGUMENTS_BAD); + } + + return (CRYPTO_SUCCESS); +} diff --git a/usr/src/common/crypto/modes/modes.h b/usr/src/common/crypto/modes/modes.h index 26f3b37b0e..efb3770eea 100644 --- a/usr/src/common/crypto/modes/modes.h +++ b/usr/src/common/crypto/modes/modes.h @@ -21,6 +21,8 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #ifndef _COMMON_CRYPTO_MODES_H @@ -46,6 +48,7 @@ extern "C" { #define CCM_MODE 0x00000010 #define GCM_MODE 0x00000020 #define GMAC_MODE 0x00000040 +#define CMAC_MODE 0x00000080 /* * cc_keysched: Pointer to key schedule. @@ -100,9 +103,13 @@ typedef struct ecb_ctx { #define ecb_copy_to ecb_common.cc_copy_to #define ecb_flags ecb_common.cc_flags +/* + * max_remain max bytes in cbc_remainder + */ typedef struct cbc_ctx { struct common_ctx cbc_common; uint64_t cbc_lastblock[2]; + size_t max_remain; } cbc_ctx_t; #define cbc_keysched cbc_common.cc_keysched @@ -345,12 +352,18 @@ extern int gcm_decrypt_final(gcm_ctx_t *, crypto_data_t *, size_t, int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), void (*xor_block)(uint8_t *, uint8_t *)); +extern int cmac_mode_final(cbc_ctx_t *, crypto_data_t *, + int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), + void (*xor_block)(uint8_t *, uint8_t *)); + extern int ctr_mode_final(ctr_ctx_t *, crypto_data_t *, int (*encrypt_block)(const void *, const uint8_t *, uint8_t *)); extern int cbc_init_ctx(cbc_ctx_t *, char *, size_t, size_t, void (*copy_block)(uint8_t *, uint64_t *)); +extern int cmac_init_ctx(cbc_ctx_t *, size_t); + extern int ctr_init_ctx(ctr_ctx_t *, ulong_t, uint8_t *, void (*copy_block)(uint8_t *, uint8_t *)); @@ -379,12 +392,14 @@ extern void crypto_get_ptrs(crypto_data_t *, void **, offset_t *, extern void *ecb_alloc_ctx(int); extern void *cbc_alloc_ctx(int); +extern void *cmac_alloc_ctx(int); extern void *ctr_alloc_ctx(int); extern void *ccm_alloc_ctx(int); extern void *gcm_alloc_ctx(int); extern void *gmac_alloc_ctx(int); extern void crypto_free_mode_ctx(void *); extern void gcm_set_kmflag(gcm_ctx_t *, int); +extern int crypto_put_output_data(uchar_t *, crypto_data_t *, int); #ifdef __cplusplus } |