Message ID | 1401470150-28821-1-git-send-email-robking@cisco.com |
---|---|
State | New |
Headers | show |
I'm sorry for my ignorance in crypto, however it looks like we can use generic length variables, but instead we limit them to uint8_t, 16 or 32. I didn't comment them all but there are multiple cases of them. Please see inline comments. On 2014-05-30 13:15, Robbie King wrote: > Initial linux-generic crypto implementation. Note that these changes > require installing and linking with libssl-dev. The camel case warnings > are also due to calls into libssl-dev. > > v1..v2: Fixed ARM compile warning, and modified "odp_hw_random_get" to > take length as "size_t" instead of "uint32_t". > > Signed-off-by: Robbie King <robking@cisco.com> > --- > include/odp_crypto.h | 295 ++++++++++++++ > platform/linux-generic/Makefile | 1 + > .../linux-generic/include/odp_crypto_internal.h | 86 ++++ > platform/linux-generic/source/odp_crypto.c | 435 +++++++++++++++++++++ > 4 files changed, 817 insertions(+) > create mode 100644 include/odp_crypto.h > create mode 100644 platform/linux-generic/include/odp_crypto_internal.h > create mode 100644 platform/linux-generic/source/odp_crypto.c > > diff --git a/include/odp_crypto.h b/include/odp_crypto.h > new file mode 100644 > index 0000000..725e8f1 > --- /dev/null > +++ b/include/odp_crypto.h > @@ -0,0 +1,295 @@ > +/* Copyright (c) 2013, Linaro Limited Nit: date 2014 Should be like this for all the files. > + * All rights reserved. > + * > + * SPDX-License-Identifier: BSD-3-Clause > + */ > + > + > +/** > + * @file > + * > + * ODP crypto > + */ > + > +#ifndef ODP_CRYPTO_H_ > +#define ODP_CRYPTO_H_ > + > +#ifdef __cplusplus > +extern "C" { > +#endif > + > +#include <odp_std_types.h> > +#include <odp_buffer.h> > +#include <odp_buffer_pool.h> > +#include <odp_queue.h> > +#include <odp_packet.h> > + > +/** Invalid session handle */ > +#define ODP_CRYPTO_SESSION_INVALID (-1ULL) > + > +/** > + * Crypto API opaque session handle > + */ > +typedef uint64_t odp_crypto_session_t; > + > +/** > + * Crypto API operation mode > + */ > +enum odp_crypto_op_mode { > + ODP_CRYPTO_SYNC, /**< Synchronous, return results immediately */ > + ODP_CRYPTO_ASYNC, /**< Aynchronous, return results via posted event */ > +}; > + > +/** > + * Crypto API operation type > + */ > +enum odp_crypto_op { > + ODP_CRYPTO_OP_ENCODE, /**< Encrypt and/or compute authentication ICV */ > + ODP_CRYPTO_OP_DECODE /**< Decrypt and/or verify authentication ICV */ > +}; > + > +/** > + * Crypto API cipher algorithm > + */ > +enum odp_cipher_alg { > + ODP_CIPHER_ALG_NULL, /**< No cipher algorithm specified */ > + ODP_CIPHER_ALG_DES, /**< DES */ > + ODP_CIPHER_ALG_3DES_CBC, /**< Triple DES with cipher block chaining */ > +}; > + > +/** > + * Crypto API authentication algorithm > + */ > +enum odp_auth_alg { > + ODP_AUTH_ALG_NULL, /**< No authentication algorithm specified */ > + ODP_AUTH_ALG_MD5_96, /**< HMAC-MD5 with 96 bit key */ > +}; > + > +/** > + * Crypto API operation order > + */ > +enum odp_crypto_combination { > + ODP_CRYPTO_CIPHER_ONLY, /**< Only perform cipher */ > + ODP_CRYPTO_AUTH_ONLY, /**< Only perform authentication */ > + ODP_CRYPTO_AUTH_CIPHERTEXT /**< Cipher then authentication on encode */ > +}; > + > +/** > + * Crypto API key > + */ > +typedef struct odp_key_s { > + union { > + /** DES/3DES key definition (set all same for DES) */ > + struct { > + uint8_t k1[8]; /**< First key */ > + uint8_t k2[8]; /**< Second key */ > + uint8_t k3[8]; /**< Third key */ > + } des; > + /** MD5 key */ > + struct { > + uint8_t key[16]; /**< Key up to 128 bits */ > + } md5; > + }; > +} odp_key_t; > + > +/** > + * Crypto API data range specifier > + */ > +struct odp_data_range { > + uint16_t offset; /**< Offset from beginning of buffer (chain) */ > + uint16_t length; /**< Length of data to operate on */ Why do we use uint16_t? Can't the data be bigger than this? > +}; > + > +/** > + * Crypto API session creation paramters > + * > + * TODO: add "odp_session_proc_info_t" > + */ > +struct odp_crypto_session_params { > + enum odp_crypto_op op; /**< Encode versus decode */ > + enum odp_crypto_combination comb; /**< Operation order */ > + enum odp_crypto_op_mode pref_mode; /**< Preferred sync vs async */ > + enum odp_cipher_alg cipher_alg; /**< Cipher algorithm */ > + odp_key_t *cipher_key; /**< Cipher key */ > + uint8_t *iv; /**< Cipher Initialization Vector (IV) */ Why do we use uint8_t and not uint_t? > + size_t iv_len; /**< Cipher IV length */ > + enum odp_auth_alg auth_alg; /**< Authentication algorithm */ > + odp_key_t *auth_key; /**< Authentication key */ > + odp_queue_t compl_queue; /**< Async mode completion event queue */ > +}; > + > +/** > + * Crypto API per packet operation parameters > + */ > +struct odp_crypto_op_params { > + odp_crypto_session_t session; /**< Session handle from creation */ > + odp_packet_t pkt; /**< Input packet buffer */ > + odp_packet_t out_pkt; /**< Output packet buffer (optional) */ > + uint8_t *override_iv_ptr; /**< Override session IV pointer */ > + unsigned hash_result_offset; /**< Offset from start of packet buffer for hash result */ > + struct odp_data_range cipher_range; /**< Data range to apply cipher */ > + struct odp_data_range auth_range; /**< Data range to authenticate */ > +}; > + > +/** > + * Crypto API session creation return code > + * > + * TODO: seems confusing, maybe _rc instead > + */ > +enum odp_crypto_ses_create_err { > + ODP_CRYPTO_SES_CREATE_NONE, /**< No session created? need to clarify */ > + ODP_CRYPTO_SES_CREATE_OK, /**< Session created successfully */ > +}; > + > +/** > + * Crypto API algorithm return code > + */ > +enum crypto_alg_err { > + ODP_CRYPTO_ALG_ERR_NONE, /**< Algorithm successful */ > + ODP_CRYPTO_ALG_ERR_DATA_SIZE, /**< Invalid data block size */ > + ODP_CRYPTO_ALG_ERR_KEY_SIZE, /**< Key size invalid for algorithm */ > + ODP_CRYPTO_ALG_ERR_ICV_CHECK, /**< Computed ICV value mismatch */ > +}; > + > +/** > + * Crypto API operation return code > + */ > +typedef enum odp_crypto_rc { > + ODP_CRYPTO_OP_OK, /**< Operation completed, results are valid */ > + ODP_CRYPTO_OP_POSTED, /**< Operation was posted, results delivered via completion queue */ > + ODP_CRYPTO_OP_ERROR, /**< Operation failed */ > +} odp_crypto_rc_e; > + > +/** > + * Crypto API hardware centric return code > + */ > +enum crypto_hw_err { > + ODP_CRYPTO_HW_ERR_NONE, /**< Operation completed successfully */ > + ODP_CRYPTO_HW_ERR_DMA, /**< Error detected during DMA of data */ > + ODP_CRYPTO_HW_ERR_BP_DEPLETED, /**< Operation failed due to buffer pool depletion */ > +}; > + > +/** > + * Crypto API algorithm (cipher or authentication) > + */ > +typedef union odp_crypto_alg_u { > + enum odp_cipher_alg cipher; /**< Cipher algorithm */ > + enum odp_auth_alg auth; /**< Authentication algorithm */ > +} odp_crypto_alg_t; > + > +/** > + * Cryto API per packet operation completion status > + */ > +struct odp_crypto_compl_status { > + odp_crypto_alg_t alg; /**< Requested algorithm */ > + enum crypto_alg_err alg_err; /**< Algorithm specific return code */ > + enum crypto_hw_err hw_err; /**< Hardware specific return code */ > +}; > + Is there a good reason why we are mixing enum name with typedef? > + > +/** > + * Crypto session creation > + * > + * Create a crypto session. Operation occurs asynchronously if a completion > + * queue is specified else synchronously. > + * > + * @param params Session parameters > + * @param completion_event Event by which the session creation results are > + * delivered. > + * @param completion_queue Queue by which the completion event will be > + * delivered. Ignored if ODP_QUEUE_INVALID. > + * > + * @return Operation return code indicating success or failure for > + * when synchronous operation requested, else POSTED when > + * asynchronous operation is requested. > + */ > +odp_crypto_rc_e > +odp_crypto_session_create(struct odp_crypto_session_params *params, > + odp_buffer_t completion_event, > + odp_queue_t completion_queue); > + > + > +/** > + * Crypto session creation completion status > + * > + * Accessor function for obtaining creation status from the completion event. > + * > + * @param completion_event Event containing operation results > + * @param status Pointer to store creation return code > + */ > +void > +odp_crypto_get_ses_create_compl_status(odp_buffer_t completion_event, > + enum odp_crypto_ses_create_err *status); > + > +/** > + * Crypto session creation completion return value > + * > + * Accessor function for obtaining handle for newly created session. > + * > + * @param completion_event Event containing operation results > + * @param session Pointer to store session handle > + */ > +void > +odp_crypto_get_ses_create_compl_session(odp_buffer_t completion_event, > + odp_crypto_session_t *session); > + > +/** > + * Crypto per packet operation > + * > + * Performs the cryptographic operations specified during session creation > + * on the packet. > + * > + * @param params Operation parameters > + * @param completion_event Event by which the session creation results are > + * delivered. > + * > + * @return Operation return code indicating success or failure when session > + * indicates synchronous operation, else POSTED for asynchronous > + * operation. > + */ > +odp_crypto_rc_e > +odp_crypto_operation(struct odp_crypto_op_params *params, > + odp_buffer_t completion_event); > + > + > +/** > + * Crypto per packet operation completion status > + * > + * Accessor function for obtaining operation status from the completion event. > + * > + * @param completion_event Event containing operation results > + * @param auth Pointer to store authentication results > + * @param cipher Pointer to store cipher results > + */ > +void > +odp_crypto_get_operation_compl_status(odp_buffer_t completion_event, > + struct odp_crypto_compl_status *auth, > + struct odp_crypto_compl_status *cipher); > + > +/** > + * Generate random byte string > + * > + * @param buf Pointer to store result > + * @param len Pointer to input length value as well as return value > + * @param use_entropy (TODO: needs description) > + * > + * @return 0 if succesful > + */ > +int > +odp_hw_random_get(uint8_t *buf, size_t *len, bool use_entropy); > + > +/** > + * Initialize the crypto subsystem, called once from main thread > + * > + * @param max_sessions Maximum number of sessions to support > + * > + * @return 0 if succesful > + */ > +int > +odp_crypto_init(uint32_t max_sessions); > + > +#ifdef __cplusplus > +} > +#endif > + > +#endif > diff --git a/platform/linux-generic/Makefile b/platform/linux-generic/Makefile > index ec5d4a7..57f6d18 100644 > --- a/platform/linux-generic/Makefile > +++ b/platform/linux-generic/Makefile > @@ -72,6 +72,7 @@ OBJS += $(OBJ_DIR)/odp_time.o > OBJS += $(OBJ_DIR)/odp_timer.o > OBJS += $(OBJ_DIR)/odp_ring.o > OBJS += $(OBJ_DIR)/odp_rwlock.o > +OBJS += $(OBJ_DIR)/odp_crypto.o > ifeq ($(ODP_HAVE_NETMAP),yes) > OBJS += $(OBJ_DIR)/odp_packet_netmap.o > endif > diff --git a/platform/linux-generic/include/odp_crypto_internal.h b/platform/linux-generic/include/odp_crypto_internal.h > new file mode 100644 > index 0000000..e558864 > --- /dev/null > +++ b/platform/linux-generic/include/odp_crypto_internal.h > @@ -0,0 +1,86 @@ > +/* Copyright (c) 2013, Linaro Limited > + * All rights reserved. > + * > + * SPDX-License-Identifier: BSD-3-Clause > + */ > + > + > + > +#ifndef ODP_CRYPTO_INTERNAL_H_ > +#define ODP_CRYPTO_INTERNAL_H_ > + > +#ifdef __cplusplus > +extern "C" { > +#endif > + > +#include <openssl/des.h> > + > +#define OP_RESULT_MAGIC 0x91919191 > + > +/** Forward declaration of session structure */ > +struct odp_crypto_session_s; > + > +/** > + * Algorithm handler function prototype > + */ > +typedef > +enum crypto_alg_err (*crypto_func_t)(struct odp_crypto_op_params *params, > + struct odp_crypto_session_s *session); > + > +/** > + * Per crypto session data structure > + */ > +struct odp_crypto_session_s { > + uint32_t index; Why not uint_t? > + enum odp_crypto_op op; > + enum odp_crypto_combination comb; > + odp_queue_t compl_queue; > + struct { > + enum odp_cipher_alg alg; > + struct { > + uint8_t *data; Why not uint_t? > + size_t len; > + } iv; > + union { > + struct { > + DES_key_schedule ks1; > + DES_key_schedule ks2; > + DES_key_schedule ks3; > + } des; > + } data; Is there a good reason to have a union with only one member? > + crypto_func_t func; > + } cipher; > + struct { > + enum odp_auth_alg alg; > + union { > + struct { > + uint8_t key[16]; Why are we limiting the key to 16? I guess we want to limit the key up to 128 bits right? > + } md5; > + } data; Is there a good reason to have a union with only one member? > + crypto_func_t func; > + } auth; > + > +}; > + > +/** > + * Per packet operation result > + */ > +struct odp_operation_result_s { > + uint32_t magic; > + struct odp_crypto_compl_status cipher; > + struct odp_crypto_compl_status auth; > +}; > + > +/** > + * Per session creation operation result > + */ > +struct odp_session_result_s { > + enum odp_crypto_ses_create_err rc; > + odp_crypto_session_t session; > +}; > + > +#ifdef __cplusplus > +} > +#endif > + > +#endif > diff --git a/platform/linux-generic/source/odp_crypto.c b/platform/linux-generic/source/odp_crypto.c > new file mode 100644 > index 0000000..4ae3645 > --- /dev/null > +++ b/platform/linux-generic/source/odp_crypto.c > @@ -0,0 +1,435 @@ > +/* Copyright (c) 2013, Linaro Limited > + * All rights reserved. > + * > + * SPDX-License-Identifier: BSD-3-Clause > + */ > + > +#include <odp_crypto.h> > +#include <odp_internal.h> > +#include <odp_atomic.h> > +#include <odp_spinlock.h> > +#include <odp_sync.h> > +#include <odp_debug.h> > +#include <odp_align.h> > +#include <odp_shared_memory.h> > +#include <odp_crypto_internal.h> > + > +#include <string.h> > + > +#include <openssl/des.h> > +#include <openssl/rand.h> > +#include <openssl/hmac.h> > +#include <openssl/evp.h> > + > +#define MAX_SESSIONS 32 > + > +typedef struct { > + odp_atomic_u32_t next; > + uint32_t max; > + struct odp_crypto_session_s sessions[0]; Pointer instead? Cheers, Anders > +} odp_crypto_global_t; > + > +static odp_crypto_global_t *global; > + > +/* > + * TODO: This is a serious hack to allow us to use packet buffer to convey > + * crypto operation results by placing them at the very end of the > + * packet buffer. > + */ > +static > +struct odp_operation_result_s *get_op_result_from_buffer(odp_buffer_t buf) > +{ > + uint8_t *temp; > + struct odp_operation_result_s *result; > + > + temp = odp_buffer_addr(buf); > + temp += odp_buffer_size(buf); > + temp -= sizeof(*result); > + result = (struct odp_operation_result_s *)(void *)temp; > + return result; > +} > + > +static > +struct odp_crypto_session_s *alloc_session(void) > +{ > + uint32_t idx; > + struct odp_crypto_session_s *session = NULL; > + > + idx = odp_atomic_fetch_inc_u32(&global->next); > + if (idx < global->max) { > + session = &global->sessions[idx]; > + session->index = idx; > + } > + return session; > +} > + > +static > +enum crypto_alg_err null_crypto_routine( > + struct odp_crypto_op_params *params ODP_UNUSED, > + struct odp_crypto_session_s *session ODP_UNUSED) > +{ > + return ODP_CRYPTO_ALG_ERR_NONE; > +} > + > +static > +enum crypto_alg_err md5_gen(struct odp_crypto_op_params *params, > + struct odp_crypto_session_s *session) > +{ > + uint8_t *data = odp_packet_buf_addr(params->pkt); > + uint8_t *icv = data; > + uint32_t len = params->auth_range.length; > + uint8_t hash[EVP_MAX_MD_SIZE]; > + uint32_t hlen = 12; > + > + /* Adjust pointer for beginning of area to auth */ > + data += params->auth_range.offset; > + icv += params->hash_result_offset; > + > + /* Hash it */ > + HMAC(EVP_md5(), session->auth.data.md5.key, 16, data, len, hash, &hlen); > + > + /* Copy to the output location */ > + memcpy(icv, hash, 12); > + > + return ODP_CRYPTO_ALG_ERR_NONE; > +} > + > + > +static > +enum crypto_alg_err md5_check(struct odp_crypto_op_params *params, > + struct odp_crypto_session_s *session) > +{ > + uint8_t *data = odp_packet_buf_addr(params->pkt); > + uint8_t *icv = data; > + uint32_t len = params->auth_range.length; > + uint8_t hash[EVP_MAX_MD_SIZE]; > + uint32_t hlen = 12; > + > + /* Adjust pointer for beginning of area to auth */ > + data += params->auth_range.offset; > + icv += params->hash_result_offset; > + > + /* Copy current value out and clear it before authentication */ > + memcpy(hash, icv, hlen); > + memset(icv, 0, hlen); > + > + /* Hash it */ > + HMAC(EVP_md5(), session->auth.data.md5.key, 16, data, len, icv, &hlen); > + > + /* Verify match */ > + if (0 != memcmp(icv, hash, 12)) > + return ODP_CRYPTO_ALG_ERR_ICV_CHECK; > + > + /* Matched */ > + return ODP_CRYPTO_ALG_ERR_NONE; > +} > + > +static > +enum crypto_alg_err des_encrypt(struct odp_crypto_op_params *params, > + struct odp_crypto_session_s *session) > +{ > + uint8_t *data = odp_packet_buf_addr(params->pkt); > + uint32_t len = params->cipher_range.length; > + DES_cblock *iv = (DES_cblock *)session->cipher.iv.data; > + > + /* Adjust pointer for beginning of area to cipher */ > + data += params->cipher_range.offset; > + > + /* Override IV if requested */ > + if (params->override_iv_ptr) > + iv = (DES_cblock *)params->override_iv_ptr; > + > + /* Encrypt it */ > + DES_ede3_cbc_encrypt(data, > + data, > + len, > + &session->cipher.data.des.ks1, > + &session->cipher.data.des.ks2, > + &session->cipher.data.des.ks3, > + iv, > + 1); > + > + return ODP_CRYPTO_ALG_ERR_NONE; > +} > + > +static > +enum crypto_alg_err des_decrypt(struct odp_crypto_op_params *params, > + struct odp_crypto_session_s *session) > +{ > + uint8_t *data = odp_packet_buf_addr(params->pkt); > + uint32_t len = params->cipher_range.length; > + DES_cblock *iv = (DES_cblock *)session->cipher.iv.data; > + > + /* Adjust pointer for beginning of area to cipher */ > + data += params->cipher_range.offset; > + > + /* Override IV if requested */ > + if (params->override_iv_ptr) > + iv = (DES_cblock *)params->override_iv_ptr; > + > + /* Decrypt it */ > + DES_ede3_cbc_encrypt(data, > + data, > + len, > + &session->cipher.data.des.ks1, > + &session->cipher.data.des.ks2, > + &session->cipher.data.des.ks3, > + iv, > + 0); > + > + return ODP_CRYPTO_ALG_ERR_NONE; > +} > + > +static > +int process_des_params(struct odp_crypto_session_s *session, > + struct odp_crypto_session_params *params) > +{ > + /* Verify IV len is either 0 or 8 */ > + if (!((0 == params->iv_len) || (8 == params->iv_len))) > + return -1; > + > + /* Verify IV pointer */ > + if (params->iv_len && !params->iv) > + return -1; > + > + /* Set function */ > + if (ODP_CRYPTO_OP_ENCODE == params->op) > + session->cipher.func = des_encrypt; > + else > + session->cipher.func = des_decrypt; > + > + /* Convert keys */ > + DES_set_key(¶ms->cipher_key->des.k1, &session->cipher.data.des.ks1); > + DES_set_key(¶ms->cipher_key->des.k2, &session->cipher.data.des.ks2); > + DES_set_key(¶ms->cipher_key->des.k3, &session->cipher.data.des.ks3); > + > + return 0; > +} > + > +static > +int process_md5_params(struct odp_crypto_session_s *session, > + struct odp_crypto_session_params *params) > +{ > + /* Set function */ > + if (ODP_CRYPTO_OP_ENCODE == params->op) > + session->auth.func = md5_gen; > + else > + session->auth.func = md5_check; > + > + /* Convert keys */ > + memcpy(session->auth.data.md5.key, params->auth_key->md5.key, 16); > + > + return 0; > +} > + > +odp_crypto_rc_e > +odp_crypto_session_create(struct odp_crypto_session_params *params, > + odp_buffer_t completion_event, > + odp_queue_t completion_queue) > +{ > + int rc; > + struct odp_crypto_session_s *session; > + struct odp_session_result_s *result = odp_buffer_addr(completion_event); > + > + /* Default to failure result */ > + result->rc = ODP_CRYPTO_SES_CREATE_NONE; > + result->session = ODP_CRYPTO_SESSION_INVALID; > + > + /* Allocate memory for this session */ > + session = alloc_session(); > + if (NULL == session) > + return ODP_CRYPTO_OP_ERROR; > + > + /* Copy stuff over */ > + session->op = params->op; > + session->comb = params->comb; > + session->compl_queue = params->compl_queue; > + session->cipher.alg = params->cipher_alg; > + session->cipher.iv.data = params->iv; > + session->cipher.iv.len = params->iv_len; > + session->auth.alg = params->auth_alg; > + > + /* Process based on cipher */ > + switch (params->cipher_alg) { > + case ODP_CIPHER_ALG_NULL: > + session->cipher.func = null_crypto_routine; > + rc = 0; > + break; > + case ODP_CIPHER_ALG_DES: > + case ODP_CIPHER_ALG_3DES_CBC: > + rc = process_des_params(session, params); > + break; > + default: > + rc = -1; > + } > + > + /* Check result */ > + if (rc) > + return ODP_CRYPTO_OP_ERROR; > + > + /* Process based on auth */ > + switch (params->auth_alg) { > + case ODP_AUTH_ALG_NULL: > + session->auth.func = null_crypto_routine; > + rc = 0; > + break; > + case ODP_AUTH_ALG_MD5_96: > + rc = process_md5_params(session, params); > + break; > + default: > + rc = -1; > + } > + > + /* Check result */ > + if (rc) > + return ODP_CRYPTO_OP_ERROR; > + > + /* We're happy */ > + result->rc = ODP_CRYPTO_SES_CREATE_OK; > + result->session = (intptr_t)session; > + > + /* If there is a queue post else we're good */ > + if (ODP_QUEUE_INVALID != completion_queue) { > + odp_queue_enq(completion_queue, completion_event); > + return ODP_CRYPTO_OP_POSTED; > + } > + > + return ODP_CRYPTO_OP_OK; > +} > + > + > +odp_crypto_rc_e > +odp_crypto_operation(struct odp_crypto_op_params *params, > + odp_buffer_t completion_event) > +{ > + enum crypto_alg_err rc_cipher = ODP_CRYPTO_ALG_ERR_NONE; > + enum crypto_alg_err rc_auth = ODP_CRYPTO_ALG_ERR_NONE; > + struct odp_crypto_session_s *session; > + struct odp_operation_result_s *result; > + > + session = (struct odp_crypto_session_s *)(intptr_t)params->session; > + > + /* > + * robking: need to understand infrastructure for scattered packets > + * for now just don't support them > + */ > + if (odp_buffer_is_scatter(odp_buffer_from_packet(params->pkt))) > + return ODP_CRYPTO_OP_ERROR; > + > + /* > + * robking: for now we are only going to support in place > + */ > + if (params->pkt != params->out_pkt) > + return ODP_CRYPTO_OP_ERROR; > + > + /* Invoke the functions */ > + switch (session->comb) { > + case ODP_CRYPTO_CIPHER_ONLY: > + rc_cipher = session->cipher.func(params, session); > + break; > + case ODP_CRYPTO_AUTH_ONLY: > + rc_auth = session->auth.func(params, session); > + break; > + case ODP_CRYPTO_AUTH_CIPHERTEXT: > + if (ODP_CRYPTO_OP_ENCODE == session->op) { > + rc_cipher = session->cipher.func(params, session); > + rc_auth = session->auth.func(params, session); > + } else { > + rc_auth = session->auth.func(params, session); > + rc_cipher = session->cipher.func(params, session); > + } > + break; > + } > + > + /* Build Result (no HW so no errors) */ > + result = get_op_result_from_buffer(completion_event); > + result->magic = OP_RESULT_MAGIC; > + result->cipher.alg.cipher = session->cipher.alg; > + result->cipher.alg_err = rc_cipher; > + result->cipher.hw_err = ODP_CRYPTO_HW_ERR_NONE; > + result->auth.alg.auth = session->auth.alg; > + result->auth.alg_err = rc_auth; > + result->auth.hw_err = ODP_CRYPTO_HW_ERR_NONE; > + > + /* > + * robking: a) the queue is supposed to come from session > + * b) ordering question asks whether we must > + * use the packet to return status > + */ > + if (ODP_QUEUE_INVALID != session->compl_queue) { > + odp_queue_enq(session->compl_queue, completion_event); > + return ODP_CRYPTO_OP_POSTED; > + } > + > + return ODP_CRYPTO_OP_OK; > +} > + > + > +int > +odp_crypto_init(uint32_t max_sessions) > +{ > + size_t mem_size; > + > + /* Force down to our limit */ > + if (MAX_SESSIONS < max_sessions) > + max_sessions = MAX_SESSIONS; > + > + /* Calculate the memory size we need */ > + mem_size = sizeof(*global); > + mem_size += (max_sessions * sizeof(struct odp_crypto_session_s)); > + > + /* Allocate our globally shared memory */ > + global = odp_shm_reserve("crypto_pool", mem_size, ODP_CACHE_LINE_SIZE); > + > + /* Clear it out */ > + memset(global, 0, mem_size); > + > + /* Initialize it */ > + global->max = max_sessions; > + > + return 0; > +} > + > +int > +odp_hw_random_get(uint8_t *buf, size_t *len, bool use_entropy ODP_UNUSED) > +{ > + int rc; > + rc = RAND_bytes(buf, *len); > + return ((1 == rc) ? 0 : -1); > +} > + > +void > +odp_crypto_get_operation_compl_status(odp_buffer_t completion_event, > + struct odp_crypto_compl_status *auth, > + struct odp_crypto_compl_status *cipher) > +{ > + struct odp_operation_result_s *result; > + > + result = get_op_result_from_buffer(completion_event); > + > + if (OP_RESULT_MAGIC != result->magic) > + abort(); > + > + memcpy(auth, &result->auth, sizeof(*auth)); > + memcpy(cipher, &result->cipher, sizeof(*cipher)); > +} > + > +void > +odp_crypto_get_ses_create_compl_status(odp_buffer_t completion_event, > + enum odp_crypto_ses_create_err *status) > +{ > + struct odp_session_result_s *result; > + > + result = odp_buffer_addr(completion_event); > + *status = result->rc; > +} > + > +void > +odp_crypto_get_ses_create_compl_session(odp_buffer_t completion_event, > + odp_crypto_session_t *session) > +{ > + struct odp_session_result_s *result; > + > + result = odp_buffer_addr(completion_event); > + *session = result->session; > +} > -- > 1.9.2 > > > _______________________________________________ > lng-odp mailing list > lng-odp@lists.linaro.org > http://lists.linaro.org/mailman/listinfo/lng-odp
Hi Anders, thanks for having a look. See [RK] inline. -----Original Message----- From: Anders Roxell [mailto:anders.roxell@linaro.org] Sent: Monday, June 02, 2014 2:02 PM To: Robbie King (robking) Cc: lng-odp@lists.linaro.org Subject: Re: [lng-odp] [PATCHv2] linux-generic crypto I'm sorry for my ignorance in crypto, however it looks like we can use generic length variables, but instead we limit them to uint8_t, 16 or 32. I didn't comment them all but there are multiple cases of them. Please see inline comments. On 2014-05-30 13:15, Robbie King wrote: > Initial linux-generic crypto implementation. Note that these changes > require installing and linking with libssl-dev. The camel case warnings > are also due to calls into libssl-dev. > > v1..v2: Fixed ARM compile warning, and modified "odp_hw_random_get" to > take length as "size_t" instead of "uint32_t". > > Signed-off-by: Robbie King <robking@cisco.com> > --- > include/odp_crypto.h | 295 ++++++++++++++ > platform/linux-generic/Makefile | 1 + > .../linux-generic/include/odp_crypto_internal.h | 86 ++++ > platform/linux-generic/source/odp_crypto.c | 435 +++++++++++++++++++++ > 4 files changed, 817 insertions(+) > create mode 100644 include/odp_crypto.h > create mode 100644 platform/linux-generic/include/odp_crypto_internal.h > create mode 100644 platform/linux-generic/source/odp_crypto.c > > diff --git a/include/odp_crypto.h b/include/odp_crypto.h > new file mode 100644 > index 0000000..725e8f1 > --- /dev/null > +++ b/include/odp_crypto.h > @@ -0,0 +1,295 @@ > +/* Copyright (c) 2013, Linaro Limited Nit: date 2014 Should be like this for all the files. [RK] good catch. > + * All rights reserved. > + * > + * SPDX-License-Identifier: BSD-3-Clause > + */ > + > + > +/** > + * @file > + * > + * ODP crypto > + */ > + > +#ifndef ODP_CRYPTO_H_ > +#define ODP_CRYPTO_H_ > + > +#ifdef __cplusplus > +extern "C" { > +#endif > + > +#include <odp_std_types.h> > +#include <odp_buffer.h> > +#include <odp_buffer_pool.h> > +#include <odp_queue.h> > +#include <odp_packet.h> > + > +/** Invalid session handle */ > +#define ODP_CRYPTO_SESSION_INVALID (-1ULL) > + > +/** > + * Crypto API opaque session handle > + */ > +typedef uint64_t odp_crypto_session_t; > + > +/** > + * Crypto API operation mode > + */ > +enum odp_crypto_op_mode { > + ODP_CRYPTO_SYNC, /**< Synchronous, return results immediately */ > + ODP_CRYPTO_ASYNC, /**< Aynchronous, return results via posted event */ > +}; > + > +/** > + * Crypto API operation type > + */ > +enum odp_crypto_op { > + ODP_CRYPTO_OP_ENCODE, /**< Encrypt and/or compute authentication ICV */ > + ODP_CRYPTO_OP_DECODE /**< Decrypt and/or verify authentication ICV */ > +}; > + > +/** > + * Crypto API cipher algorithm > + */ > +enum odp_cipher_alg { > + ODP_CIPHER_ALG_NULL, /**< No cipher algorithm specified */ > + ODP_CIPHER_ALG_DES, /**< DES */ > + ODP_CIPHER_ALG_3DES_CBC, /**< Triple DES with cipher block chaining */ > +}; > + > +/** > + * Crypto API authentication algorithm > + */ > +enum odp_auth_alg { > + ODP_AUTH_ALG_NULL, /**< No authentication algorithm specified */ > + ODP_AUTH_ALG_MD5_96, /**< HMAC-MD5 with 96 bit key */ > +}; > + > +/** > + * Crypto API operation order > + */ > +enum odp_crypto_combination { > + ODP_CRYPTO_CIPHER_ONLY, /**< Only perform cipher */ > + ODP_CRYPTO_AUTH_ONLY, /**< Only perform authentication */ > + ODP_CRYPTO_AUTH_CIPHERTEXT /**< Cipher then authentication on encode */ > +}; > + > +/** > + * Crypto API key > + */ > +typedef struct odp_key_s { > + union { > + /** DES/3DES key definition (set all same for DES) */ > + struct { > + uint8_t k1[8]; /**< First key */ > + uint8_t k2[8]; /**< Second key */ > + uint8_t k3[8]; /**< Third key */ > + } des; > + /** MD5 key */ > + struct { > + uint8_t key[16]; /**< Key up to 128 bits */ > + } md5; > + }; > +} odp_key_t; > + > +/** > + * Crypto API data range specifier > + */ > +struct odp_data_range { > + uint16_t offset; /**< Offset from beginning of buffer (chain) */ > + uint16_t length; /**< Length of data to operate on */ Why do we use uint16_t? Can't the data be bigger than this? [RK] this came from the ODP crypto spec, I think in general the idea is packets will never be larger than an Ethernet jumbo (roughly 10K) so no need to support larger than 64K. > +}; > + > +/** > + * Crypto API session creation paramters > + * > + * TODO: add "odp_session_proc_info_t" > + */ > +struct odp_crypto_session_params { > + enum odp_crypto_op op; /**< Encode versus decode */ > + enum odp_crypto_combination comb; /**< Operation order */ > + enum odp_crypto_op_mode pref_mode; /**< Preferred sync vs async */ > + enum odp_cipher_alg cipher_alg; /**< Cipher algorithm */ > + odp_key_t *cipher_key; /**< Cipher key */ > + uint8_t *iv; /**< Cipher Initialization Vector (IV) */ Why do we use uint8_t and not uint_t? [RK] This one also comes from the spec, my understanding is for crypto the IV is always an opaque byte string that can be different lengths for different ciphers. > + size_t iv_len; /**< Cipher IV length */ > + enum odp_auth_alg auth_alg; /**< Authentication algorithm */ > + odp_key_t *auth_key; /**< Authentication key */ > + odp_queue_t compl_queue; /**< Async mode completion event queue */ > +}; > + > +/** > + * Crypto API per packet operation parameters > + */ > +struct odp_crypto_op_params { > + odp_crypto_session_t session; /**< Session handle from creation */ > + odp_packet_t pkt; /**< Input packet buffer */ > + odp_packet_t out_pkt; /**< Output packet buffer (optional) */ > + uint8_t *override_iv_ptr; /**< Override session IV pointer */ > + unsigned hash_result_offset; /**< Offset from start of packet buffer for hash result */ > + struct odp_data_range cipher_range; /**< Data range to apply cipher */ > + struct odp_data_range auth_range; /**< Data range to authenticate */ > +}; > + > +/** > + * Crypto API session creation return code > + * > + * TODO: seems confusing, maybe _rc instead > + */ > +enum odp_crypto_ses_create_err { > + ODP_CRYPTO_SES_CREATE_NONE, /**< No session created? need to clarify */ > + ODP_CRYPTO_SES_CREATE_OK, /**< Session created successfully */ > +}; > + > +/** > + * Crypto API algorithm return code > + */ > +enum crypto_alg_err { > + ODP_CRYPTO_ALG_ERR_NONE, /**< Algorithm successful */ > + ODP_CRYPTO_ALG_ERR_DATA_SIZE, /**< Invalid data block size */ > + ODP_CRYPTO_ALG_ERR_KEY_SIZE, /**< Key size invalid for algorithm */ > + ODP_CRYPTO_ALG_ERR_ICV_CHECK, /**< Computed ICV value mismatch */ > +}; > + > +/** > + * Crypto API operation return code > + */ > +typedef enum odp_crypto_rc { > + ODP_CRYPTO_OP_OK, /**< Operation completed, results are valid */ > + ODP_CRYPTO_OP_POSTED, /**< Operation was posted, results delivered via completion queue */ > + ODP_CRYPTO_OP_ERROR, /**< Operation failed */ > +} odp_crypto_rc_e; > + > +/** > + * Crypto API hardware centric return code > + */ > +enum crypto_hw_err { > + ODP_CRYPTO_HW_ERR_NONE, /**< Operation completed successfully */ > + ODP_CRYPTO_HW_ERR_DMA, /**< Error detected during DMA of data */ > + ODP_CRYPTO_HW_ERR_BP_DEPLETED, /**< Operation failed due to buffer pool depletion */ > +}; > + > +/** > + * Crypto API algorithm (cipher or authentication) > + */ > +typedef union odp_crypto_alg_u { > + enum odp_cipher_alg cipher; /**< Cipher algorithm */ > + enum odp_auth_alg auth; /**< Authentication algorithm */ > +} odp_crypto_alg_t; > + > +/** > + * Cryto API per packet operation completion status > + */ > +struct odp_crypto_compl_status { > + odp_crypto_alg_t alg; /**< Requested algorithm */ > + enum crypto_alg_err alg_err; /**< Algorithm specific return code */ > + enum crypto_hw_err hw_err; /**< Hardware specific return code */ > +}; > + Is there a good reason why we are mixing enum name with typedef? [RK] I agree, I would like to see us perhaps agree on typedef'ing enums. This one is a little kludgy anyway, I would vote for removing the ALG from the completion status since it is simply a mirror of the ALG (cipher or auth) which was specified during session creation. > + > +/** > + * Crypto session creation > + * > + * Create a crypto session. Operation occurs asynchronously if a completion > + * queue is specified else synchronously. > + * > + * @param params Session parameters > + * @param completion_event Event by which the session creation results are > + * delivered. > + * @param completion_queue Queue by which the completion event will be > + * delivered. Ignored if ODP_QUEUE_INVALID. > + * > + * @return Operation return code indicating success or failure for > + * when synchronous operation requested, else POSTED when > + * asynchronous operation is requested. > + */ > +odp_crypto_rc_e > +odp_crypto_session_create(struct odp_crypto_session_params *params, > + odp_buffer_t completion_event, > + odp_queue_t completion_queue); > + > + > +/** > + * Crypto session creation completion status > + * > + * Accessor function for obtaining creation status from the completion event. > + * > + * @param completion_event Event containing operation results > + * @param status Pointer to store creation return code > + */ > +void > +odp_crypto_get_ses_create_compl_status(odp_buffer_t completion_event, > + enum odp_crypto_ses_create_err *status); > + > +/** > + * Crypto session creation completion return value > + * > + * Accessor function for obtaining handle for newly created session. > + * > + * @param completion_event Event containing operation results > + * @param session Pointer to store session handle > + */ > +void > +odp_crypto_get_ses_create_compl_session(odp_buffer_t completion_event, > + odp_crypto_session_t *session); > + > +/** > + * Crypto per packet operation > + * > + * Performs the cryptographic operations specified during session creation > + * on the packet. > + * > + * @param params Operation parameters > + * @param completion_event Event by which the session creation results are > + * delivered. > + * > + * @return Operation return code indicating success or failure when session > + * indicates synchronous operation, else POSTED for asynchronous > + * operation. > + */ > +odp_crypto_rc_e > +odp_crypto_operation(struct odp_crypto_op_params *params, > + odp_buffer_t completion_event); > + > + > +/** > + * Crypto per packet operation completion status > + * > + * Accessor function for obtaining operation status from the completion event. > + * > + * @param completion_event Event containing operation results > + * @param auth Pointer to store authentication results > + * @param cipher Pointer to store cipher results > + */ > +void > +odp_crypto_get_operation_compl_status(odp_buffer_t completion_event, > + struct odp_crypto_compl_status *auth, > + struct odp_crypto_compl_status *cipher); > + > +/** > + * Generate random byte string > + * > + * @param buf Pointer to store result > + * @param len Pointer to input length value as well as return value > + * @param use_entropy (TODO: needs description) > + * > + * @return 0 if succesful > + */ > +int > +odp_hw_random_get(uint8_t *buf, size_t *len, bool use_entropy); > + > +/** > + * Initialize the crypto subsystem, called once from main thread > + * > + * @param max_sessions Maximum number of sessions to support > + * > + * @return 0 if succesful > + */ > +int > +odp_crypto_init(uint32_t max_sessions); > + > +#ifdef __cplusplus > +} > +#endif > + > +#endif > diff --git a/platform/linux-generic/Makefile b/platform/linux-generic/Makefile > index ec5d4a7..57f6d18 100644 > --- a/platform/linux-generic/Makefile > +++ b/platform/linux-generic/Makefile > @@ -72,6 +72,7 @@ OBJS += $(OBJ_DIR)/odp_time.o > OBJS += $(OBJ_DIR)/odp_timer.o > OBJS += $(OBJ_DIR)/odp_ring.o > OBJS += $(OBJ_DIR)/odp_rwlock.o > +OBJS += $(OBJ_DIR)/odp_crypto.o > ifeq ($(ODP_HAVE_NETMAP),yes) > OBJS += $(OBJ_DIR)/odp_packet_netmap.o > endif > diff --git a/platform/linux-generic/include/odp_crypto_internal.h b/platform/linux-generic/include/odp_crypto_internal.h > new file mode 100644 > index 0000000..e558864 > --- /dev/null > +++ b/platform/linux-generic/include/odp_crypto_internal.h > @@ -0,0 +1,86 @@ > +/* Copyright (c) 2013, Linaro Limited > + * All rights reserved. > + * > + * SPDX-License-Identifier: BSD-3-Clause > + */ > + > + > + > +#ifndef ODP_CRYPTO_INTERNAL_H_ > +#define ODP_CRYPTO_INTERNAL_H_ > + > +#ifdef __cplusplus > +extern "C" { > +#endif > + > +#include <openssl/des.h> > + > +#define OP_RESULT_MAGIC 0x91919191 > + > +/** Forward declaration of session structure */ > +struct odp_crypto_session_s; > + > +/** > + * Algorithm handler function prototype > + */ > +typedef > +enum crypto_alg_err (*crypto_func_t)(struct odp_crypto_op_params *params, > + struct odp_crypto_session_s *session); > + > +/** > + * Per crypto session data structure > + */ > +struct odp_crypto_session_s { > + uint32_t index; Why not uint_t? [RK] OK by me, since this data structure is internal to the linux-generic implementation, and the plan is to support a very small number of sessions this could be made as small as uint8_t I think. > + enum odp_crypto_op op; > + enum odp_crypto_combination comb; > + odp_queue_t compl_queue; > + struct { > + enum odp_cipher_alg alg; > + struct { > + uint8_t *data; Why not uint_t? [RK] Same reason as above for IV since it is byte data. > + size_t len; > + } iv; > + union { > + struct { > + DES_key_schedule ks1; > + DES_key_schedule ks2; > + DES_key_schedule ks3; > + } des; > + } data; Is there a good reason to have a union with only one member? [RK] The union is here with the intention that the linux-generic implementation will add support for other ciphers (AES for example). So the union is simply laying the ground work for expansion. > + crypto_func_t func; > + } cipher; > + struct { > + enum odp_auth_alg alg; > + union { > + struct { > + uint8_t key[16]; Why are we limiting the key to 16? I guess we want to limit the key up to 128 bits right? [RK] 128 bit key is specific to MD5, other algorithems would have their own entry in the union (again for future expansion). > + } md5; > + } data; Is there a good reason to have a union with only one member? > + crypto_func_t func; > + } auth; > + > +}; > + > +/** > + * Per packet operation result > + */ > +struct odp_operation_result_s { > + uint32_t magic; > + struct odp_crypto_compl_status cipher; > + struct odp_crypto_compl_status auth; > +}; > + > +/** > + * Per session creation operation result > + */ > +struct odp_session_result_s { > + enum odp_crypto_ses_create_err rc; > + odp_crypto_session_t session; > +}; > + > +#ifdef __cplusplus > +} > +#endif > + > +#endif > diff --git a/platform/linux-generic/source/odp_crypto.c b/platform/linux-generic/source/odp_crypto.c > new file mode 100644 > index 0000000..4ae3645 > --- /dev/null > +++ b/platform/linux-generic/source/odp_crypto.c > @@ -0,0 +1,435 @@ > +/* Copyright (c) 2013, Linaro Limited > + * All rights reserved. > + * > + * SPDX-License-Identifier: BSD-3-Clause > + */ > + > +#include <odp_crypto.h> > +#include <odp_internal.h> > +#include <odp_atomic.h> > +#include <odp_spinlock.h> > +#include <odp_sync.h> > +#include <odp_debug.h> > +#include <odp_align.h> > +#include <odp_shared_memory.h> > +#include <odp_crypto_internal.h> > + > +#include <string.h> > + > +#include <openssl/des.h> > +#include <openssl/rand.h> > +#include <openssl/hmac.h> > +#include <openssl/evp.h> > + > +#define MAX_SESSIONS 32 > + > +typedef struct { > + odp_atomic_u32_t next; > + uint32_t max; > + struct odp_crypto_session_s sessions[0]; Pointer instead? [RK] This is specifically for allocating memory where the storage for sessions immediately follows the structure, allowing an arbitrary number of sessions to be supported at allocation time like shown yet accessed as if they were statically allocated: /* Calculate the memory size we need */ mem_size = sizeof(*global); mem_size += (max_sessions * sizeof(struct odp_crypto_session_s)); and you can access individual sessions like this: session = &global->sessions[idx]; I've used this a lot over the years, but if it gives folks heartburn I can allocate memory in multiple stages, etc. Cheers, Anders > +} odp_crypto_global_t; > + > +static odp_crypto_global_t *global; > + > +/* > + * TODO: This is a serious hack to allow us to use packet buffer to convey > + * crypto operation results by placing them at the very end of the > + * packet buffer. > + */ > +static > +struct odp_operation_result_s *get_op_result_from_buffer(odp_buffer_t buf) > +{ > + uint8_t *temp; > + struct odp_operation_result_s *result; > + > + temp = odp_buffer_addr(buf); > + temp += odp_buffer_size(buf); > + temp -= sizeof(*result); > + result = (struct odp_operation_result_s *)(void *)temp; > + return result; > +} > + > +static > +struct odp_crypto_session_s *alloc_session(void) > +{ > + uint32_t idx; > + struct odp_crypto_session_s *session = NULL; > + > + idx = odp_atomic_fetch_inc_u32(&global->next); > + if (idx < global->max) { > + session = &global->sessions[idx]; > + session->index = idx; > + } > + return session; > +} > + > +static > +enum crypto_alg_err null_crypto_routine( > + struct odp_crypto_op_params *params ODP_UNUSED, > + struct odp_crypto_session_s *session ODP_UNUSED) > +{ > + return ODP_CRYPTO_ALG_ERR_NONE; > +} > + > +static > +enum crypto_alg_err md5_gen(struct odp_crypto_op_params *params, > + struct odp_crypto_session_s *session) > +{ > + uint8_t *data = odp_packet_buf_addr(params->pkt); > + uint8_t *icv = data; > + uint32_t len = params->auth_range.length; > + uint8_t hash[EVP_MAX_MD_SIZE]; > + uint32_t hlen = 12; > + > + /* Adjust pointer for beginning of area to auth */ > + data += params->auth_range.offset; > + icv += params->hash_result_offset; > + > + /* Hash it */ > + HMAC(EVP_md5(), session->auth.data.md5.key, 16, data, len, hash, &hlen); > + > + /* Copy to the output location */ > + memcpy(icv, hash, 12); > + > + return ODP_CRYPTO_ALG_ERR_NONE; > +} > + > + > +static > +enum crypto_alg_err md5_check(struct odp_crypto_op_params *params, > + struct odp_crypto_session_s *session) > +{ > + uint8_t *data = odp_packet_buf_addr(params->pkt); > + uint8_t *icv = data; > + uint32_t len = params->auth_range.length; > + uint8_t hash[EVP_MAX_MD_SIZE]; > + uint32_t hlen = 12; > + > + /* Adjust pointer for beginning of area to auth */ > + data += params->auth_range.offset; > + icv += params->hash_result_offset; > + > + /* Copy current value out and clear it before authentication */ > + memcpy(hash, icv, hlen); > + memset(icv, 0, hlen); > + > + /* Hash it */ > + HMAC(EVP_md5(), session->auth.data.md5.key, 16, data, len, icv, &hlen); > + > + /* Verify match */ > + if (0 != memcmp(icv, hash, 12)) > + return ODP_CRYPTO_ALG_ERR_ICV_CHECK; > + > + /* Matched */ > + return ODP_CRYPTO_ALG_ERR_NONE; > +} > + > +static > +enum crypto_alg_err des_encrypt(struct odp_crypto_op_params *params, > + struct odp_crypto_session_s *session) > +{ > + uint8_t *data = odp_packet_buf_addr(params->pkt); > + uint32_t len = params->cipher_range.length; > + DES_cblock *iv = (DES_cblock *)session->cipher.iv.data; > + > + /* Adjust pointer for beginning of area to cipher */ > + data += params->cipher_range.offset; > + > + /* Override IV if requested */ > + if (params->override_iv_ptr) > + iv = (DES_cblock *)params->override_iv_ptr; > + > + /* Encrypt it */ > + DES_ede3_cbc_encrypt(data, > + data, > + len, > + &session->cipher.data.des.ks1, > + &session->cipher.data.des.ks2, > + &session->cipher.data.des.ks3, > + iv, > + 1); > + > + return ODP_CRYPTO_ALG_ERR_NONE; > +} > + > +static > +enum crypto_alg_err des_decrypt(struct odp_crypto_op_params *params, > + struct odp_crypto_session_s *session) > +{ > + uint8_t *data = odp_packet_buf_addr(params->pkt); > + uint32_t len = params->cipher_range.length; > + DES_cblock *iv = (DES_cblock *)session->cipher.iv.data; > + > + /* Adjust pointer for beginning of area to cipher */ > + data += params->cipher_range.offset; > + > + /* Override IV if requested */ > + if (params->override_iv_ptr) > + iv = (DES_cblock *)params->override_iv_ptr; > + > + /* Decrypt it */ > + DES_ede3_cbc_encrypt(data, > + data, > + len, > + &session->cipher.data.des.ks1, > + &session->cipher.data.des.ks2, > + &session->cipher.data.des.ks3, > + iv, > + 0); > + > + return ODP_CRYPTO_ALG_ERR_NONE; > +} > + > +static > +int process_des_params(struct odp_crypto_session_s *session, > + struct odp_crypto_session_params *params) > +{ > + /* Verify IV len is either 0 or 8 */ > + if (!((0 == params->iv_len) || (8 == params->iv_len))) > + return -1; > + > + /* Verify IV pointer */ > + if (params->iv_len && !params->iv) > + return -1; > + > + /* Set function */ > + if (ODP_CRYPTO_OP_ENCODE == params->op) > + session->cipher.func = des_encrypt; > + else > + session->cipher.func = des_decrypt; > + > + /* Convert keys */ > + DES_set_key(¶ms->cipher_key->des.k1, &session->cipher.data.des.ks1); > + DES_set_key(¶ms->cipher_key->des.k2, &session->cipher.data.des.ks2); > + DES_set_key(¶ms->cipher_key->des.k3, &session->cipher.data.des.ks3); > + > + return 0; > +} > + > +static > +int process_md5_params(struct odp_crypto_session_s *session, > + struct odp_crypto_session_params *params) > +{ > + /* Set function */ > + if (ODP_CRYPTO_OP_ENCODE == params->op) > + session->auth.func = md5_gen; > + else > + session->auth.func = md5_check; > + > + /* Convert keys */ > + memcpy(session->auth.data.md5.key, params->auth_key->md5.key, 16); > + > + return 0; > +} > + > +odp_crypto_rc_e > +odp_crypto_session_create(struct odp_crypto_session_params *params, > + odp_buffer_t completion_event, > + odp_queue_t completion_queue) > +{ > + int rc; > + struct odp_crypto_session_s *session; > + struct odp_session_result_s *result = odp_buffer_addr(completion_event); > + > + /* Default to failure result */ > + result->rc = ODP_CRYPTO_SES_CREATE_NONE; > + result->session = ODP_CRYPTO_SESSION_INVALID; > + > + /* Allocate memory for this session */ > + session = alloc_session(); > + if (NULL == session) > + return ODP_CRYPTO_OP_ERROR; > + > + /* Copy stuff over */ > + session->op = params->op; > + session->comb = params->comb; > + session->compl_queue = params->compl_queue; > + session->cipher.alg = params->cipher_alg; > + session->cipher.iv.data = params->iv; > + session->cipher.iv.len = params->iv_len; > + session->auth.alg = params->auth_alg; > + > + /* Process based on cipher */ > + switch (params->cipher_alg) { > + case ODP_CIPHER_ALG_NULL: > + session->cipher.func = null_crypto_routine; > + rc = 0; > + break; > + case ODP_CIPHER_ALG_DES: > + case ODP_CIPHER_ALG_3DES_CBC: > + rc = process_des_params(session, params); > + break; > + default: > + rc = -1; > + } > + > + /* Check result */ > + if (rc) > + return ODP_CRYPTO_OP_ERROR; > + > + /* Process based on auth */ > + switch (params->auth_alg) { > + case ODP_AUTH_ALG_NULL: > + session->auth.func = null_crypto_routine; > + rc = 0; > + break; > + case ODP_AUTH_ALG_MD5_96: > + rc = process_md5_params(session, params); > + break; > + default: > + rc = -1; > + } > + > + /* Check result */ > + if (rc) > + return ODP_CRYPTO_OP_ERROR; > + > + /* We're happy */ > + result->rc = ODP_CRYPTO_SES_CREATE_OK; > + result->session = (intptr_t)session; > + > + /* If there is a queue post else we're good */ > + if (ODP_QUEUE_INVALID != completion_queue) { > + odp_queue_enq(completion_queue, completion_event); > + return ODP_CRYPTO_OP_POSTED; > + } > + > + return ODP_CRYPTO_OP_OK; > +} > + > + > +odp_crypto_rc_e > +odp_crypto_operation(struct odp_crypto_op_params *params, > + odp_buffer_t completion_event) > +{ > + enum crypto_alg_err rc_cipher = ODP_CRYPTO_ALG_ERR_NONE; > + enum crypto_alg_err rc_auth = ODP_CRYPTO_ALG_ERR_NONE; > + struct odp_crypto_session_s *session; > + struct odp_operation_result_s *result; > + > + session = (struct odp_crypto_session_s *)(intptr_t)params->session; > + > + /* > + * robking: need to understand infrastructure for scattered packets > + * for now just don't support them > + */ > + if (odp_buffer_is_scatter(odp_buffer_from_packet(params->pkt))) > + return ODP_CRYPTO_OP_ERROR; > + > + /* > + * robking: for now we are only going to support in place > + */ > + if (params->pkt != params->out_pkt) > + return ODP_CRYPTO_OP_ERROR; > + > + /* Invoke the functions */ > + switch (session->comb) { > + case ODP_CRYPTO_CIPHER_ONLY: > + rc_cipher = session->cipher.func(params, session); > + break; > + case ODP_CRYPTO_AUTH_ONLY: > + rc_auth = session->auth.func(params, session); > + break; > + case ODP_CRYPTO_AUTH_CIPHERTEXT: > + if (ODP_CRYPTO_OP_ENCODE == session->op) { > + rc_cipher = session->cipher.func(params, session); > + rc_auth = session->auth.func(params, session); > + } else { > + rc_auth = session->auth.func(params, session); > + rc_cipher = session->cipher.func(params, session); > + } > + break; > + } > + > + /* Build Result (no HW so no errors) */ > + result = get_op_result_from_buffer(completion_event); > + result->magic = OP_RESULT_MAGIC; > + result->cipher.alg.cipher = session->cipher.alg; > + result->cipher.alg_err = rc_cipher; > + result->cipher.hw_err = ODP_CRYPTO_HW_ERR_NONE; > + result->auth.alg.auth = session->auth.alg; > + result->auth.alg_err = rc_auth; > + result->auth.hw_err = ODP_CRYPTO_HW_ERR_NONE; > + > + /* > + * robking: a) the queue is supposed to come from session > + * b) ordering question asks whether we must > + * use the packet to return status > + */ > + if (ODP_QUEUE_INVALID != session->compl_queue) { > + odp_queue_enq(session->compl_queue, completion_event); > + return ODP_CRYPTO_OP_POSTED; > + } > + > + return ODP_CRYPTO_OP_OK; > +} > + > + > +int > +odp_crypto_init(uint32_t max_sessions) > +{ > + size_t mem_size; > + > + /* Force down to our limit */ > + if (MAX_SESSIONS < max_sessions) > + max_sessions = MAX_SESSIONS; > + > + /* Calculate the memory size we need */ > + mem_size = sizeof(*global); > + mem_size += (max_sessions * sizeof(struct odp_crypto_session_s)); > + > + /* Allocate our globally shared memory */ > + global = odp_shm_reserve("crypto_pool", mem_size, ODP_CACHE_LINE_SIZE); > + > + /* Clear it out */ > + memset(global, 0, mem_size); > + > + /* Initialize it */ > + global->max = max_sessions; > + > + return 0; > +} > + > +int > +odp_hw_random_get(uint8_t *buf, size_t *len, bool use_entropy ODP_UNUSED) > +{ > + int rc; > + rc = RAND_bytes(buf, *len); > + return ((1 == rc) ? 0 : -1); > +} > + > +void > +odp_crypto_get_operation_compl_status(odp_buffer_t completion_event, > + struct odp_crypto_compl_status *auth, > + struct odp_crypto_compl_status *cipher) > +{ > + struct odp_operation_result_s *result; > + > + result = get_op_result_from_buffer(completion_event); > + > + if (OP_RESULT_MAGIC != result->magic) > + abort(); > + > + memcpy(auth, &result->auth, sizeof(*auth)); > + memcpy(cipher, &result->cipher, sizeof(*cipher)); > +} > + > +void > +odp_crypto_get_ses_create_compl_status(odp_buffer_t completion_event, > + enum odp_crypto_ses_create_err *status) > +{ > + struct odp_session_result_s *result; > + > + result = odp_buffer_addr(completion_event); > + *status = result->rc; > +} > + > +void > +odp_crypto_get_ses_create_compl_session(odp_buffer_t completion_event, > + odp_crypto_session_t *session) > +{ > + struct odp_session_result_s *result; > + > + result = odp_buffer_addr(completion_event); > + *session = result->session; > +} > -- > 1.9.2 > > > _______________________________________________ > lng-odp mailing list > lng-odp@lists.linaro.org > http://lists.linaro.org/mailman/listinfo/lng-odp
Sorry for the late reply! See inline. On 2014-06-02 18:28, Robbie King (robking) wrote: > Hi Anders, thanks for having a look. See [RK] inline. > > -----Original Message----- > From: Anders Roxell [mailto:anders.roxell@linaro.org] > Sent: Monday, June 02, 2014 2:02 PM > To: Robbie King (robking) > Cc: lng-odp@lists.linaro.org > Subject: Re: [lng-odp] [PATCHv2] linux-generic crypto > > I'm sorry for my ignorance in crypto, however it looks like we can use > generic length variables, but instead we limit them to uint8_t, 16 or 32. > I didn't comment them all but there are multiple cases of them. > Please see inline comments. > > > On 2014-05-30 13:15, Robbie King wrote: > > Initial linux-generic crypto implementation. Note that these changes > > require installing and linking with libssl-dev. The camel case warnings > > are also due to calls into libssl-dev. > > > > v1..v2: Fixed ARM compile warning, and modified "odp_hw_random_get" to > > take length as "size_t" instead of "uint32_t". > > > > Signed-off-by: Robbie King <robking@cisco.com> > > --- > > include/odp_crypto.h | 295 ++++++++++++++ > > platform/linux-generic/Makefile | 1 + > > .../linux-generic/include/odp_crypto_internal.h | 86 ++++ > > platform/linux-generic/source/odp_crypto.c | 435 +++++++++++++++++++++ > > 4 files changed, 817 insertions(+) > > create mode 100644 include/odp_crypto.h > > create mode 100644 platform/linux-generic/include/odp_crypto_internal.h > > create mode 100644 platform/linux-generic/source/odp_crypto.c > > > > diff --git a/include/odp_crypto.h b/include/odp_crypto.h > > new file mode 100644 > > index 0000000..725e8f1 > > --- /dev/null > > +++ b/include/odp_crypto.h > > @@ -0,0 +1,295 @@ > > +/* Copyright (c) 2013, Linaro Limited > > Nit: date 2014 > Should be like this for all the files. > > [RK] good catch. > > > + * All rights reserved. > > + * > > + * SPDX-License-Identifier: BSD-3-Clause > > + */ > > + > > + > > +/** > > + * @file > > + * > > + * ODP crypto > > + */ > > + > > +#ifndef ODP_CRYPTO_H_ > > +#define ODP_CRYPTO_H_ > > + > > +#ifdef __cplusplus > > +extern "C" { > > +#endif > > + > > +#include <odp_std_types.h> > > +#include <odp_buffer.h> > > +#include <odp_buffer_pool.h> > > +#include <odp_queue.h> > > +#include <odp_packet.h> > > + > > +/** Invalid session handle */ > > +#define ODP_CRYPTO_SESSION_INVALID (-1ULL) > > + > > +/** > > + * Crypto API opaque session handle > > + */ > > +typedef uint64_t odp_crypto_session_t; > > + > > +/** > > + * Crypto API operation mode > > + */ > > +enum odp_crypto_op_mode { > > + ODP_CRYPTO_SYNC, /**< Synchronous, return results immediately */ > > + ODP_CRYPTO_ASYNC, /**< Aynchronous, return results via posted event */ > > +}; > > + > > +/** > > + * Crypto API operation type > > + */ > > +enum odp_crypto_op { > > + ODP_CRYPTO_OP_ENCODE, /**< Encrypt and/or compute authentication ICV */ > > + ODP_CRYPTO_OP_DECODE /**< Decrypt and/or verify authentication ICV */ > > +}; > > + > > +/** > > + * Crypto API cipher algorithm > > + */ > > +enum odp_cipher_alg { > > + ODP_CIPHER_ALG_NULL, /**< No cipher algorithm specified */ > > + ODP_CIPHER_ALG_DES, /**< DES */ > > + ODP_CIPHER_ALG_3DES_CBC, /**< Triple DES with cipher block chaining */ > > +}; > > + > > +/** > > + * Crypto API authentication algorithm > > + */ > > +enum odp_auth_alg { > > + ODP_AUTH_ALG_NULL, /**< No authentication algorithm specified */ > > + ODP_AUTH_ALG_MD5_96, /**< HMAC-MD5 with 96 bit key */ > > +}; > > + > > +/** > > + * Crypto API operation order > > + */ > > +enum odp_crypto_combination { > > + ODP_CRYPTO_CIPHER_ONLY, /**< Only perform cipher */ > > + ODP_CRYPTO_AUTH_ONLY, /**< Only perform authentication */ > > + ODP_CRYPTO_AUTH_CIPHERTEXT /**< Cipher then authentication on encode */ > > +}; > > + > > +/** > > + * Crypto API key > > + */ > > +typedef struct odp_key_s { > > + union { > > + /** DES/3DES key definition (set all same for DES) */ > > + struct { > > + uint8_t k1[8]; /**< First key */ > > + uint8_t k2[8]; /**< Second key */ > > + uint8_t k3[8]; /**< Third key */ > > + } des; > > + /** MD5 key */ > > + struct { > > + uint8_t key[16]; /**< Key up to 128 bits */ > > + } md5; > > + }; > > +} odp_key_t; > > + > > +/** > > + * Crypto API data range specifier > > + */ > > +struct odp_data_range { > > + uint16_t offset; /**< Offset from beginning of buffer (chain) */ > > + uint16_t length; /**< Length of data to operate on */ > > Why do we use uint16_t? > Can't the data be bigger than this? > > [RK] this came from the ODP crypto spec, I think in general the idea > is packets will never be larger than an Ethernet jumbo (roughly 10K) > so no need to support larger than 64K. So was that a reason for choosing uint16_t in the spec? Or shall we change the spec? > > > +}; > > + > > +/** > > + * Crypto API session creation paramters > > + * > > + * TODO: add "odp_session_proc_info_t" > > + */ > > +struct odp_crypto_session_params { > > + enum odp_crypto_op op; /**< Encode versus decode */ > > + enum odp_crypto_combination comb; /**< Operation order */ > > + enum odp_crypto_op_mode pref_mode; /**< Preferred sync vs async */ > > + enum odp_cipher_alg cipher_alg; /**< Cipher algorithm */ > > + odp_key_t *cipher_key; /**< Cipher key */ > > + uint8_t *iv; /**< Cipher Initialization Vector (IV) */ > > Why do we use uint8_t and not uint_t? > > [RK] This one also comes from the spec, my understanding is for crypto > the IV is always an opaque byte string that can be different lengths > for different ciphers. OK, thanks. > > > + size_t iv_len; /**< Cipher IV length */ > > + enum odp_auth_alg auth_alg; /**< Authentication algorithm */ > > + odp_key_t *auth_key; /**< Authentication key */ > > + odp_queue_t compl_queue; /**< Async mode completion event queue */ > > +}; > > + > > +/** > > + * Crypto API per packet operation parameters > > + */ > > +struct odp_crypto_op_params { > > + odp_crypto_session_t session; /**< Session handle from creation */ > > + odp_packet_t pkt; /**< Input packet buffer */ > > + odp_packet_t out_pkt; /**< Output packet buffer (optional) */ > > + uint8_t *override_iv_ptr; /**< Override session IV pointer */ > > + unsigned hash_result_offset; /**< Offset from start of packet buffer for hash result */ > > + struct odp_data_range cipher_range; /**< Data range to apply cipher */ > > + struct odp_data_range auth_range; /**< Data range to authenticate */ > > +}; > > + > > +/** > > + * Crypto API session creation return code > > + * > > + * TODO: seems confusing, maybe _rc instead > > + */ > > +enum odp_crypto_ses_create_err { > > + ODP_CRYPTO_SES_CREATE_NONE, /**< No session created? need to clarify */ > > + ODP_CRYPTO_SES_CREATE_OK, /**< Session created successfully */ > > +}; > > + > > +/** > > + * Crypto API algorithm return code > > + */ > > +enum crypto_alg_err { > > + ODP_CRYPTO_ALG_ERR_NONE, /**< Algorithm successful */ > > + ODP_CRYPTO_ALG_ERR_DATA_SIZE, /**< Invalid data block size */ > > + ODP_CRYPTO_ALG_ERR_KEY_SIZE, /**< Key size invalid for algorithm */ > > + ODP_CRYPTO_ALG_ERR_ICV_CHECK, /**< Computed ICV value mismatch */ > > +}; > > + > > +/** > > + * Crypto API operation return code > > + */ > > +typedef enum odp_crypto_rc { > > + ODP_CRYPTO_OP_OK, /**< Operation completed, results are valid */ > > + ODP_CRYPTO_OP_POSTED, /**< Operation was posted, results delivered via completion queue */ > > + ODP_CRYPTO_OP_ERROR, /**< Operation failed */ > > +} odp_crypto_rc_e; > > + > > +/** > > + * Crypto API hardware centric return code > > + */ > > +enum crypto_hw_err { > > + ODP_CRYPTO_HW_ERR_NONE, /**< Operation completed successfully */ > > + ODP_CRYPTO_HW_ERR_DMA, /**< Error detected during DMA of data */ > > + ODP_CRYPTO_HW_ERR_BP_DEPLETED, /**< Operation failed due to buffer pool depletion */ > > +}; > > + > > +/** > > + * Crypto API algorithm (cipher or authentication) > > + */ > > +typedef union odp_crypto_alg_u { > > + enum odp_cipher_alg cipher; /**< Cipher algorithm */ > > + enum odp_auth_alg auth; /**< Authentication algorithm */ > > +} odp_crypto_alg_t; > > + > > +/** > > + * Cryto API per packet operation completion status > > + */ > > +struct odp_crypto_compl_status { > > + odp_crypto_alg_t alg; /**< Requested algorithm */ > > + enum crypto_alg_err alg_err; /**< Algorithm specific return code */ > > + enum crypto_hw_err hw_err; /**< Hardware specific return code */ > > +}; > > + > > Is there a good reason why we are mixing enum name with typedef? > > [RK] I agree, I would like to see us perhaps agree on typedef'ing > enums. This one is a little kludgy anyway, I would vote for removing > the ALG from the completion status since it is simply a mirror of the > ALG (cipher or auth) which was specified during session creation. I agree with you. > > > + > > +/** > > + * Crypto session creation > > + * > > + * Create a crypto session. Operation occurs asynchronously if a completion > > + * queue is specified else synchronously. > > + * > > + * @param params Session parameters > > + * @param completion_event Event by which the session creation results are > > + * delivered. > > + * @param completion_queue Queue by which the completion event will be > > + * delivered. Ignored if ODP_QUEUE_INVALID. > > + * > > + * @return Operation return code indicating success or failure for > > + * when synchronous operation requested, else POSTED when > > + * asynchronous operation is requested. > > + */ > > +odp_crypto_rc_e > > +odp_crypto_session_create(struct odp_crypto_session_params *params, > > + odp_buffer_t completion_event, > > + odp_queue_t completion_queue); > > + > > + > > +/** > > + * Crypto session creation completion status > > + * > > + * Accessor function for obtaining creation status from the completion event. > > + * > > + * @param completion_event Event containing operation results > > + * @param status Pointer to store creation return code > > + */ > > +void > > +odp_crypto_get_ses_create_compl_status(odp_buffer_t completion_event, > > + enum odp_crypto_ses_create_err *status); > > + > > +/** > > + * Crypto session creation completion return value > > + * > > + * Accessor function for obtaining handle for newly created session. > > + * > > + * @param completion_event Event containing operation results > > + * @param session Pointer to store session handle > > + */ > > +void > > +odp_crypto_get_ses_create_compl_session(odp_buffer_t completion_event, > > + odp_crypto_session_t *session); > > + > > +/** > > + * Crypto per packet operation > > + * > > + * Performs the cryptographic operations specified during session creation > > + * on the packet. > > + * > > + * @param params Operation parameters > > + * @param completion_event Event by which the session creation results are > > + * delivered. > > + * > > + * @return Operation return code indicating success or failure when session > > + * indicates synchronous operation, else POSTED for asynchronous > > + * operation. > > + */ > > +odp_crypto_rc_e > > +odp_crypto_operation(struct odp_crypto_op_params *params, > > + odp_buffer_t completion_event); > > + > > + > > +/** > > + * Crypto per packet operation completion status > > + * > > + * Accessor function for obtaining operation status from the completion event. > > + * > > + * @param completion_event Event containing operation results > > + * @param auth Pointer to store authentication results > > + * @param cipher Pointer to store cipher results > > + */ > > +void > > +odp_crypto_get_operation_compl_status(odp_buffer_t completion_event, > > + struct odp_crypto_compl_status *auth, > > + struct odp_crypto_compl_status *cipher); > > + > > +/** > > + * Generate random byte string > > + * > > + * @param buf Pointer to store result > > + * @param len Pointer to input length value as well as return value > > + * @param use_entropy (TODO: needs description) > > + * > > + * @return 0 if succesful > > + */ > > +int > > +odp_hw_random_get(uint8_t *buf, size_t *len, bool use_entropy); > > + > > +/** > > + * Initialize the crypto subsystem, called once from main thread > > + * > > + * @param max_sessions Maximum number of sessions to support > > + * > > + * @return 0 if succesful > > + */ > > +int > > +odp_crypto_init(uint32_t max_sessions); > > + > > +#ifdef __cplusplus > > +} > > +#endif > > + > > +#endif > > diff --git a/platform/linux-generic/Makefile b/platform/linux-generic/Makefile > > index ec5d4a7..57f6d18 100644 > > --- a/platform/linux-generic/Makefile > > +++ b/platform/linux-generic/Makefile > > @@ -72,6 +72,7 @@ OBJS += $(OBJ_DIR)/odp_time.o > > OBJS += $(OBJ_DIR)/odp_timer.o > > OBJS += $(OBJ_DIR)/odp_ring.o > > OBJS += $(OBJ_DIR)/odp_rwlock.o > > +OBJS += $(OBJ_DIR)/odp_crypto.o > > ifeq ($(ODP_HAVE_NETMAP),yes) > > OBJS += $(OBJ_DIR)/odp_packet_netmap.o > > endif > > diff --git a/platform/linux-generic/include/odp_crypto_internal.h b/platform/linux-generic/include/odp_crypto_internal.h > > new file mode 100644 > > index 0000000..e558864 > > --- /dev/null > > +++ b/platform/linux-generic/include/odp_crypto_internal.h > > @@ -0,0 +1,86 @@ > > +/* Copyright (c) 2013, Linaro Limited > > + * All rights reserved. > > + * > > + * SPDX-License-Identifier: BSD-3-Clause > > + */ > > + > > + > > + > > +#ifndef ODP_CRYPTO_INTERNAL_H_ > > +#define ODP_CRYPTO_INTERNAL_H_ > > + > > +#ifdef __cplusplus > > +extern "C" { > > +#endif > > + > > +#include <openssl/des.h> > > + > > +#define OP_RESULT_MAGIC 0x91919191 > > + > > +/** Forward declaration of session structure */ > > +struct odp_crypto_session_s; > > + > > +/** > > + * Algorithm handler function prototype > > + */ > > +typedef > > +enum crypto_alg_err (*crypto_func_t)(struct odp_crypto_op_params *params, > > + struct odp_crypto_session_s *session); > > + > > +/** > > + * Per crypto session data structure > > + */ > > +struct odp_crypto_session_s { > > + uint32_t index; > > Why not uint_t? > > [RK] OK by me, since this data structure is internal to the > linux-generic implementation, and the plan is to support a > very small number of sessions this could be made as small as > uint8_t I think. OK, I'm fine with that. > > > + enum odp_crypto_op op; > > + enum odp_crypto_combination comb; > > + odp_queue_t compl_queue; > > + struct { > > + enum odp_cipher_alg alg; > > + struct { > > + uint8_t *data; > > Why not uint_t? > > [RK] Same reason as above for IV since it is byte data. OK. > > > + size_t len; > > + } iv; > > + union { > > + struct { > > + DES_key_schedule ks1; > > + DES_key_schedule ks2; > > + DES_key_schedule ks3; > > + } des; > > + } data; > > Is there a good reason to have a union with only one member? > > [RK] The union is here with the intention that the linux-generic > implementation will add support for other ciphers (AES for example). > So the union is simply laying the ground work for expansion. I understand, didn't think about that! Thank you for your explanation. > > > + crypto_func_t func; > > + } cipher; > > + struct { > > + enum odp_auth_alg alg; > > + union { > > + struct { > > + uint8_t key[16]; > > Why are we limiting the key to 16? > I guess we want to limit the key up to 128 bits right? > > [RK] 128 bit key is specific to MD5, other algorithems > would have their own entry in the union (again for future > expansion). OK good. Maybe comment that it is for keys up to 128 bits? > > > + } md5; > > + } data; > > Is there a good reason to have a union with only one member? > > > + crypto_func_t func; > > + } auth; > > + > > +}; > > + > > +/** > > + * Per packet operation result > > + */ > > +struct odp_operation_result_s { > > + uint32_t magic; > > + struct odp_crypto_compl_status cipher; > > + struct odp_crypto_compl_status auth; > > +}; > > + > > +/** > > + * Per session creation operation result > > + */ > > +struct odp_session_result_s { > > + enum odp_crypto_ses_create_err rc; > > + odp_crypto_session_t session; > > +}; > > + > > +#ifdef __cplusplus > > +} > > +#endif > > + > > +#endif > > diff --git a/platform/linux-generic/source/odp_crypto.c b/platform/linux-generic/source/odp_crypto.c > > new file mode 100644 > > index 0000000..4ae3645 > > --- /dev/null > > +++ b/platform/linux-generic/source/odp_crypto.c > > @@ -0,0 +1,435 @@ > > +/* Copyright (c) 2013, Linaro Limited > > + * All rights reserved. > > + * > > + * SPDX-License-Identifier: BSD-3-Clause > > + */ > > + > > +#include <odp_crypto.h> > > +#include <odp_internal.h> > > +#include <odp_atomic.h> > > +#include <odp_spinlock.h> > > +#include <odp_sync.h> > > +#include <odp_debug.h> > > +#include <odp_align.h> > > +#include <odp_shared_memory.h> > > +#include <odp_crypto_internal.h> > > + > > +#include <string.h> > > + > > +#include <openssl/des.h> > > +#include <openssl/rand.h> > > +#include <openssl/hmac.h> > > +#include <openssl/evp.h> > > + > > +#define MAX_SESSIONS 32 > > + > > +typedef struct { > > + odp_atomic_u32_t next; > > + uint32_t max; > > + struct odp_crypto_session_s sessions[0]; > > Pointer instead? > > [RK] This is specifically for allocating memory where > the storage for sessions immediately follows the > structure, allowing an arbitrary number of sessions to > be supported at allocation time like shown yet accessed > as if they were statically allocated: > > /* Calculate the memory size we need */ > mem_size = sizeof(*global); > mem_size += (max_sessions * sizeof(struct odp_crypto_session_s)); > > and you can access individual sessions like this: > > session = &global->sessions[idx]; > > I've used this a lot over the years, but if it gives folks heartburn > I can allocate memory in multiple stages, etc. I'm fine with either way as long as we do the same, what does others think about this? > > Cheers, > Anders > > > +} odp_crypto_global_t; > > + > > +static odp_crypto_global_t *global; > > + > > +/* > > + * TODO: This is a serious hack to allow us to use packet buffer to convey > > + * crypto operation results by placing them at the very end of the > > + * packet buffer. > > + */ > > +static > > +struct odp_operation_result_s *get_op_result_from_buffer(odp_buffer_t buf) > > +{ > > + uint8_t *temp; > > + struct odp_operation_result_s *result; > > + > > + temp = odp_buffer_addr(buf); > > + temp += odp_buffer_size(buf); > > + temp -= sizeof(*result); > > + result = (struct odp_operation_result_s *)(void *)temp; > > + return result; > > +} > > + > > +static > > +struct odp_crypto_session_s *alloc_session(void) > > +{ > > + uint32_t idx; > > + struct odp_crypto_session_s *session = NULL; > > + > > + idx = odp_atomic_fetch_inc_u32(&global->next); > > + if (idx < global->max) { > > + session = &global->sessions[idx]; > > + session->index = idx; > > + } > > + return session; > > +} > > + > > +static > > +enum crypto_alg_err null_crypto_routine( > > + struct odp_crypto_op_params *params ODP_UNUSED, > > + struct odp_crypto_session_s *session ODP_UNUSED) > > +{ > > + return ODP_CRYPTO_ALG_ERR_NONE; > > +} > > + > > +static > > +enum crypto_alg_err md5_gen(struct odp_crypto_op_params *params, > > + struct odp_crypto_session_s *session) > > +{ > > + uint8_t *data = odp_packet_buf_addr(params->pkt); > > + uint8_t *icv = data; > > + uint32_t len = params->auth_range.length; > > + uint8_t hash[EVP_MAX_MD_SIZE]; > > + uint32_t hlen = 12; > > + > > + /* Adjust pointer for beginning of area to auth */ > > + data += params->auth_range.offset; > > + icv += params->hash_result_offset; > > + > > + /* Hash it */ > > + HMAC(EVP_md5(), session->auth.data.md5.key, 16, data, len, hash, &hlen); > > + > > + /* Copy to the output location */ > > + memcpy(icv, hash, 12); > > + > > + return ODP_CRYPTO_ALG_ERR_NONE; > > +} > > + > > + > > +static > > +enum crypto_alg_err md5_check(struct odp_crypto_op_params *params, > > + struct odp_crypto_session_s *session) > > +{ > > + uint8_t *data = odp_packet_buf_addr(params->pkt); > > + uint8_t *icv = data; > > + uint32_t len = params->auth_range.length; > > + uint8_t hash[EVP_MAX_MD_SIZE]; > > + uint32_t hlen = 12; > > + > > + /* Adjust pointer for beginning of area to auth */ > > + data += params->auth_range.offset; > > + icv += params->hash_result_offset; > > + > > + /* Copy current value out and clear it before authentication */ > > + memcpy(hash, icv, hlen); > > + memset(icv, 0, hlen); > > + > > + /* Hash it */ > > + HMAC(EVP_md5(), session->auth.data.md5.key, 16, data, len, icv, &hlen); > > + > > + /* Verify match */ > > + if (0 != memcmp(icv, hash, 12)) > > + return ODP_CRYPTO_ALG_ERR_ICV_CHECK; > > + > > + /* Matched */ > > + return ODP_CRYPTO_ALG_ERR_NONE; > > +} > > + > > +static > > +enum crypto_alg_err des_encrypt(struct odp_crypto_op_params *params, > > + struct odp_crypto_session_s *session) > > +{ > > + uint8_t *data = odp_packet_buf_addr(params->pkt); > > + uint32_t len = params->cipher_range.length; > > + DES_cblock *iv = (DES_cblock *)session->cipher.iv.data; > > + > > + /* Adjust pointer for beginning of area to cipher */ > > + data += params->cipher_range.offset; > > + > > + /* Override IV if requested */ > > + if (params->override_iv_ptr) > > + iv = (DES_cblock *)params->override_iv_ptr; > > + > > + /* Encrypt it */ > > + DES_ede3_cbc_encrypt(data, > > + data, > > + len, > > + &session->cipher.data.des.ks1, > > + &session->cipher.data.des.ks2, > > + &session->cipher.data.des.ks3, > > + iv, > > + 1); > > + > > + return ODP_CRYPTO_ALG_ERR_NONE; > > +} > > + > > +static > > +enum crypto_alg_err des_decrypt(struct odp_crypto_op_params *params, > > + struct odp_crypto_session_s *session) > > +{ > > + uint8_t *data = odp_packet_buf_addr(params->pkt); > > + uint32_t len = params->cipher_range.length; > > + DES_cblock *iv = (DES_cblock *)session->cipher.iv.data; > > + > > + /* Adjust pointer for beginning of area to cipher */ > > + data += params->cipher_range.offset; > > + > > + /* Override IV if requested */ > > + if (params->override_iv_ptr) > > + iv = (DES_cblock *)params->override_iv_ptr; > > + > > + /* Decrypt it */ > > + DES_ede3_cbc_encrypt(data, > > + data, > > + len, > > + &session->cipher.data.des.ks1, > > + &session->cipher.data.des.ks2, > > + &session->cipher.data.des.ks3, > > + iv, > > + 0); > > + > > + return ODP_CRYPTO_ALG_ERR_NONE; > > +} > > + > > +static > > +int process_des_params(struct odp_crypto_session_s *session, > > + struct odp_crypto_session_params *params) > > +{ > > + /* Verify IV len is either 0 or 8 */ > > + if (!((0 == params->iv_len) || (8 == params->iv_len))) > > + return -1; > > + > > + /* Verify IV pointer */ > > + if (params->iv_len && !params->iv) > > + return -1; > > + > > + /* Set function */ > > + if (ODP_CRYPTO_OP_ENCODE == params->op) > > + session->cipher.func = des_encrypt; > > + else > > + session->cipher.func = des_decrypt; > > + > > + /* Convert keys */ > > + DES_set_key(¶ms->cipher_key->des.k1, &session->cipher.data.des.ks1); > > + DES_set_key(¶ms->cipher_key->des.k2, &session->cipher.data.des.ks2); > > + DES_set_key(¶ms->cipher_key->des.k3, &session->cipher.data.des.ks3); > > + > > + return 0; > > +} > > + > > +static > > +int process_md5_params(struct odp_crypto_session_s *session, > > + struct odp_crypto_session_params *params) > > +{ > > + /* Set function */ > > + if (ODP_CRYPTO_OP_ENCODE == params->op) > > + session->auth.func = md5_gen; > > + else > > + session->auth.func = md5_check; > > + > > + /* Convert keys */ > > + memcpy(session->auth.data.md5.key, params->auth_key->md5.key, 16); > > + > > + return 0; > > +} > > + > > +odp_crypto_rc_e > > +odp_crypto_session_create(struct odp_crypto_session_params *params, > > + odp_buffer_t completion_event, > > + odp_queue_t completion_queue) > > +{ > > + int rc; > > + struct odp_crypto_session_s *session; > > + struct odp_session_result_s *result = odp_buffer_addr(completion_event); > > + > > + /* Default to failure result */ > > + result->rc = ODP_CRYPTO_SES_CREATE_NONE; > > + result->session = ODP_CRYPTO_SESSION_INVALID; > > + > > + /* Allocate memory for this session */ > > + session = alloc_session(); > > + if (NULL == session) > > + return ODP_CRYPTO_OP_ERROR; > > + > > + /* Copy stuff over */ > > + session->op = params->op; > > + session->comb = params->comb; > > + session->compl_queue = params->compl_queue; > > + session->cipher.alg = params->cipher_alg; > > + session->cipher.iv.data = params->iv; > > + session->cipher.iv.len = params->iv_len; > > + session->auth.alg = params->auth_alg; > > + > > + /* Process based on cipher */ > > + switch (params->cipher_alg) { > > + case ODP_CIPHER_ALG_NULL: > > + session->cipher.func = null_crypto_routine; > > + rc = 0; > > + break; > > + case ODP_CIPHER_ALG_DES: > > + case ODP_CIPHER_ALG_3DES_CBC: > > + rc = process_des_params(session, params); > > + break; > > + default: > > + rc = -1; > > + } > > + > > + /* Check result */ > > + if (rc) > > + return ODP_CRYPTO_OP_ERROR; > > + > > + /* Process based on auth */ > > + switch (params->auth_alg) { > > + case ODP_AUTH_ALG_NULL: > > + session->auth.func = null_crypto_routine; > > + rc = 0; > > + break; > > + case ODP_AUTH_ALG_MD5_96: > > + rc = process_md5_params(session, params); > > + break; > > + default: > > + rc = -1; > > + } > > + > > + /* Check result */ > > + if (rc) > > + return ODP_CRYPTO_OP_ERROR; > > + > > + /* We're happy */ > > + result->rc = ODP_CRYPTO_SES_CREATE_OK; > > + result->session = (intptr_t)session; > > + > > + /* If there is a queue post else we're good */ > > + if (ODP_QUEUE_INVALID != completion_queue) { > > + odp_queue_enq(completion_queue, completion_event); > > + return ODP_CRYPTO_OP_POSTED; > > + } > > + > > + return ODP_CRYPTO_OP_OK; > > +} > > + > > + > > +odp_crypto_rc_e > > +odp_crypto_operation(struct odp_crypto_op_params *params, > > + odp_buffer_t completion_event) > > +{ > > + enum crypto_alg_err rc_cipher = ODP_CRYPTO_ALG_ERR_NONE; > > + enum crypto_alg_err rc_auth = ODP_CRYPTO_ALG_ERR_NONE; > > + struct odp_crypto_session_s *session; > > + struct odp_operation_result_s *result; > > + > > + session = (struct odp_crypto_session_s *)(intptr_t)params->session; > > + > > + /* > > + * robking: need to understand infrastructure for scattered packets > > + * for now just don't support them > > + */ > > + if (odp_buffer_is_scatter(odp_buffer_from_packet(params->pkt))) > > + return ODP_CRYPTO_OP_ERROR; > > + > > + /* > > + * robking: for now we are only going to support in place > > + */ > > + if (params->pkt != params->out_pkt) > > + return ODP_CRYPTO_OP_ERROR; > > + > > + /* Invoke the functions */ > > + switch (session->comb) { > > + case ODP_CRYPTO_CIPHER_ONLY: > > + rc_cipher = session->cipher.func(params, session); > > + break; > > + case ODP_CRYPTO_AUTH_ONLY: > > + rc_auth = session->auth.func(params, session); > > + break; > > + case ODP_CRYPTO_AUTH_CIPHERTEXT: > > + if (ODP_CRYPTO_OP_ENCODE == session->op) { > > + rc_cipher = session->cipher.func(params, session); > > + rc_auth = session->auth.func(params, session); > > + } else { > > + rc_auth = session->auth.func(params, session); > > + rc_cipher = session->cipher.func(params, session); > > + } > > + break; > > + } > > + > > + /* Build Result (no HW so no errors) */ > > + result = get_op_result_from_buffer(completion_event); > > + result->magic = OP_RESULT_MAGIC; > > + result->cipher.alg.cipher = session->cipher.alg; > > + result->cipher.alg_err = rc_cipher; > > + result->cipher.hw_err = ODP_CRYPTO_HW_ERR_NONE; > > + result->auth.alg.auth = session->auth.alg; > > + result->auth.alg_err = rc_auth; > > + result->auth.hw_err = ODP_CRYPTO_HW_ERR_NONE; > > + > > + /* > > + * robking: a) the queue is supposed to come from session > > + * b) ordering question asks whether we must > > + * use the packet to return status > > + */ > > + if (ODP_QUEUE_INVALID != session->compl_queue) { > > + odp_queue_enq(session->compl_queue, completion_event); > > + return ODP_CRYPTO_OP_POSTED; > > + } > > + > > + return ODP_CRYPTO_OP_OK; > > +} > > + > > + > > +int > > +odp_crypto_init(uint32_t max_sessions) > > +{ > > + size_t mem_size; > > + > > + /* Force down to our limit */ > > + if (MAX_SESSIONS < max_sessions) > > + max_sessions = MAX_SESSIONS; > > + > > + /* Calculate the memory size we need */ > > + mem_size = sizeof(*global); > > + mem_size += (max_sessions * sizeof(struct odp_crypto_session_s)); > > + > > + /* Allocate our globally shared memory */ > > + global = odp_shm_reserve("crypto_pool", mem_size, ODP_CACHE_LINE_SIZE); > > + > > + /* Clear it out */ > > + memset(global, 0, mem_size); > > + > > + /* Initialize it */ > > + global->max = max_sessions; > > + > > + return 0; > > +} > > + > > +int > > +odp_hw_random_get(uint8_t *buf, size_t *len, bool use_entropy ODP_UNUSED) > > +{ > > + int rc; > > + rc = RAND_bytes(buf, *len); > > + return ((1 == rc) ? 0 : -1); > > +} > > + > > +void > > +odp_crypto_get_operation_compl_status(odp_buffer_t completion_event, > > + struct odp_crypto_compl_status *auth, > > + struct odp_crypto_compl_status *cipher) > > +{ > > + struct odp_operation_result_s *result; > > + > > + result = get_op_result_from_buffer(completion_event); > > + > > + if (OP_RESULT_MAGIC != result->magic) > > + abort(); > > + > > + memcpy(auth, &result->auth, sizeof(*auth)); > > + memcpy(cipher, &result->cipher, sizeof(*cipher)); > > +} > > + > > +void > > +odp_crypto_get_ses_create_compl_status(odp_buffer_t completion_event, > > + enum odp_crypto_ses_create_err *status) > > +{ > > + struct odp_session_result_s *result; > > + > > + result = odp_buffer_addr(completion_event); > > + *status = result->rc; > > +} > > + > > +void > > +odp_crypto_get_ses_create_compl_session(odp_buffer_t completion_event, > > + odp_crypto_session_t *session) > > +{ > > + struct odp_session_result_s *result; > > + > > + result = odp_buffer_addr(completion_event); > > + *session = result->session; > > +} > > -- > > 1.9.2 > > > > > > _______________________________________________ > > lng-odp mailing list > > lng-odp@lists.linaro.org > > http://lists.linaro.org/mailman/listinfo/lng-odp
Hello Robbie, are you working on new version of this patch or this patch is good to merge now? Also the same question about example app patch. Thank you, Maxim. On 06/04/2014 04:51 PM, Anders Roxell wrote: > Sorry for the late reply! > See inline. > > On 2014-06-02 18:28, Robbie King (robking) wrote: >> Hi Anders, thanks for having a look. See [RK] inline. >> >> -----Original Message----- >> From: Anders Roxell [mailto:anders.roxell@linaro.org] >> Sent: Monday, June 02, 2014 2:02 PM >> To: Robbie King (robking) >> Cc: lng-odp@lists.linaro.org >> Subject: Re: [lng-odp] [PATCHv2] linux-generic crypto >> >> I'm sorry for my ignorance in crypto, however it looks like we can use >> generic length variables, but instead we limit them to uint8_t, 16 or 32. >> I didn't comment them all but there are multiple cases of them. >> Please see inline comments. >> >> >> On 2014-05-30 13:15, Robbie King wrote: >>> Initial linux-generic crypto implementation. Note that these changes >>> require installing and linking with libssl-dev. The camel case warnings >>> are also due to calls into libssl-dev. >>> >>> v1..v2: Fixed ARM compile warning, and modified "odp_hw_random_get" to >>> take length as "size_t" instead of "uint32_t". >>> >>> Signed-off-by: Robbie King <robking@cisco.com> >>> --- >>> include/odp_crypto.h | 295 ++++++++++++++ >>> platform/linux-generic/Makefile | 1 + >>> .../linux-generic/include/odp_crypto_internal.h | 86 ++++ >>> platform/linux-generic/source/odp_crypto.c | 435 +++++++++++++++++++++ >>> 4 files changed, 817 insertions(+) >>> create mode 100644 include/odp_crypto.h >>> create mode 100644 platform/linux-generic/include/odp_crypto_internal.h >>> create mode 100644 platform/linux-generic/source/odp_crypto.c >>> >>> diff --git a/include/odp_crypto.h b/include/odp_crypto.h >>> new file mode 100644 >>> index 0000000..725e8f1 >>> --- /dev/null >>> +++ b/include/odp_crypto.h >>> @@ -0,0 +1,295 @@ >>> +/* Copyright (c) 2013, Linaro Limited >> Nit: date 2014 >> Should be like this for all the files. >> >> [RK] good catch. >> >>> + * All rights reserved. >>> + * >>> + * SPDX-License-Identifier: BSD-3-Clause >>> + */ >>> + >>> + >>> +/** >>> + * @file >>> + * >>> + * ODP crypto >>> + */ >>> + >>> +#ifndef ODP_CRYPTO_H_ >>> +#define ODP_CRYPTO_H_ >>> + >>> +#ifdef __cplusplus >>> +extern "C" { >>> +#endif >>> + >>> +#include <odp_std_types.h> >>> +#include <odp_buffer.h> >>> +#include <odp_buffer_pool.h> >>> +#include <odp_queue.h> >>> +#include <odp_packet.h> >>> + >>> +/** Invalid session handle */ >>> +#define ODP_CRYPTO_SESSION_INVALID (-1ULL) >>> + >>> +/** >>> + * Crypto API opaque session handle >>> + */ >>> +typedef uint64_t odp_crypto_session_t; >>> + >>> +/** >>> + * Crypto API operation mode >>> + */ >>> +enum odp_crypto_op_mode { >>> + ODP_CRYPTO_SYNC, /**< Synchronous, return results immediately */ >>> + ODP_CRYPTO_ASYNC, /**< Aynchronous, return results via posted event */ >>> +}; >>> + >>> +/** >>> + * Crypto API operation type >>> + */ >>> +enum odp_crypto_op { >>> + ODP_CRYPTO_OP_ENCODE, /**< Encrypt and/or compute authentication ICV */ >>> + ODP_CRYPTO_OP_DECODE /**< Decrypt and/or verify authentication ICV */ >>> +}; >>> + >>> +/** >>> + * Crypto API cipher algorithm >>> + */ >>> +enum odp_cipher_alg { >>> + ODP_CIPHER_ALG_NULL, /**< No cipher algorithm specified */ >>> + ODP_CIPHER_ALG_DES, /**< DES */ >>> + ODP_CIPHER_ALG_3DES_CBC, /**< Triple DES with cipher block chaining */ >>> +}; >>> + >>> +/** >>> + * Crypto API authentication algorithm >>> + */ >>> +enum odp_auth_alg { >>> + ODP_AUTH_ALG_NULL, /**< No authentication algorithm specified */ >>> + ODP_AUTH_ALG_MD5_96, /**< HMAC-MD5 with 96 bit key */ >>> +}; >>> + >>> +/** >>> + * Crypto API operation order >>> + */ >>> +enum odp_crypto_combination { >>> + ODP_CRYPTO_CIPHER_ONLY, /**< Only perform cipher */ >>> + ODP_CRYPTO_AUTH_ONLY, /**< Only perform authentication */ >>> + ODP_CRYPTO_AUTH_CIPHERTEXT /**< Cipher then authentication on encode */ >>> +}; >>> + >>> +/** >>> + * Crypto API key >>> + */ >>> +typedef struct odp_key_s { >>> + union { >>> + /** DES/3DES key definition (set all same for DES) */ >>> + struct { >>> + uint8_t k1[8]; /**< First key */ >>> + uint8_t k2[8]; /**< Second key */ >>> + uint8_t k3[8]; /**< Third key */ >>> + } des; >>> + /** MD5 key */ >>> + struct { >>> + uint8_t key[16]; /**< Key up to 128 bits */ >>> + } md5; >>> + }; >>> +} odp_key_t; >>> + >>> +/** >>> + * Crypto API data range specifier >>> + */ >>> +struct odp_data_range { >>> + uint16_t offset; /**< Offset from beginning of buffer (chain) */ >>> + uint16_t length; /**< Length of data to operate on */ >> Why do we use uint16_t? >> Can't the data be bigger than this? >> >> [RK] this came from the ODP crypto spec, I think in general the idea >> is packets will never be larger than an Ethernet jumbo (roughly 10K) >> so no need to support larger than 64K. > So was that a reason for choosing uint16_t in the spec? > Or shall we change the spec? > >>> +}; >>> + >>> +/** >>> + * Crypto API session creation paramters >>> + * >>> + * TODO: add "odp_session_proc_info_t" >>> + */ >>> +struct odp_crypto_session_params { >>> + enum odp_crypto_op op; /**< Encode versus decode */ >>> + enum odp_crypto_combination comb; /**< Operation order */ >>> + enum odp_crypto_op_mode pref_mode; /**< Preferred sync vs async */ >>> + enum odp_cipher_alg cipher_alg; /**< Cipher algorithm */ >>> + odp_key_t *cipher_key; /**< Cipher key */ >>> + uint8_t *iv; /**< Cipher Initialization Vector (IV) */ >> Why do we use uint8_t and not uint_t? >> >> [RK] This one also comes from the spec, my understanding is for crypto >> the IV is always an opaque byte string that can be different lengths >> for different ciphers. > OK, thanks. > >>> + size_t iv_len; /**< Cipher IV length */ >>> + enum odp_auth_alg auth_alg; /**< Authentication algorithm */ >>> + odp_key_t *auth_key; /**< Authentication key */ >>> + odp_queue_t compl_queue; /**< Async mode completion event queue */ >>> +}; >>> + >>> +/** >>> + * Crypto API per packet operation parameters >>> + */ >>> +struct odp_crypto_op_params { >>> + odp_crypto_session_t session; /**< Session handle from creation */ >>> + odp_packet_t pkt; /**< Input packet buffer */ >>> + odp_packet_t out_pkt; /**< Output packet buffer (optional) */ >>> + uint8_t *override_iv_ptr; /**< Override session IV pointer */ >>> + unsigned hash_result_offset; /**< Offset from start of packet buffer for hash result */ >>> + struct odp_data_range cipher_range; /**< Data range to apply cipher */ >>> + struct odp_data_range auth_range; /**< Data range to authenticate */ >>> +}; >>> + >>> +/** >>> + * Crypto API session creation return code >>> + * >>> + * TODO: seems confusing, maybe _rc instead >>> + */ >>> +enum odp_crypto_ses_create_err { >>> + ODP_CRYPTO_SES_CREATE_NONE, /**< No session created? need to clarify */ >>> + ODP_CRYPTO_SES_CREATE_OK, /**< Session created successfully */ >>> +}; >>> + >>> +/** >>> + * Crypto API algorithm return code >>> + */ >>> +enum crypto_alg_err { >>> + ODP_CRYPTO_ALG_ERR_NONE, /**< Algorithm successful */ >>> + ODP_CRYPTO_ALG_ERR_DATA_SIZE, /**< Invalid data block size */ >>> + ODP_CRYPTO_ALG_ERR_KEY_SIZE, /**< Key size invalid for algorithm */ >>> + ODP_CRYPTO_ALG_ERR_ICV_CHECK, /**< Computed ICV value mismatch */ >>> +}; >>> + >>> +/** >>> + * Crypto API operation return code >>> + */ >>> +typedef enum odp_crypto_rc { >>> + ODP_CRYPTO_OP_OK, /**< Operation completed, results are valid */ >>> + ODP_CRYPTO_OP_POSTED, /**< Operation was posted, results delivered via completion queue */ >>> + ODP_CRYPTO_OP_ERROR, /**< Operation failed */ >>> +} odp_crypto_rc_e; >>> + >>> +/** >>> + * Crypto API hardware centric return code >>> + */ >>> +enum crypto_hw_err { >>> + ODP_CRYPTO_HW_ERR_NONE, /**< Operation completed successfully */ >>> + ODP_CRYPTO_HW_ERR_DMA, /**< Error detected during DMA of data */ >>> + ODP_CRYPTO_HW_ERR_BP_DEPLETED, /**< Operation failed due to buffer pool depletion */ >>> +}; >>> + >>> +/** >>> + * Crypto API algorithm (cipher or authentication) >>> + */ >>> +typedef union odp_crypto_alg_u { >>> + enum odp_cipher_alg cipher; /**< Cipher algorithm */ >>> + enum odp_auth_alg auth; /**< Authentication algorithm */ >>> +} odp_crypto_alg_t; >>> + >>> +/** >>> + * Cryto API per packet operation completion status >>> + */ >>> +struct odp_crypto_compl_status { >>> + odp_crypto_alg_t alg; /**< Requested algorithm */ >>> + enum crypto_alg_err alg_err; /**< Algorithm specific return code */ >>> + enum crypto_hw_err hw_err; /**< Hardware specific return code */ >>> +}; >>> + >> Is there a good reason why we are mixing enum name with typedef? >> >> [RK] I agree, I would like to see us perhaps agree on typedef'ing >> enums. This one is a little kludgy anyway, I would vote for removing >> the ALG from the completion status since it is simply a mirror of the >> ALG (cipher or auth) which was specified during session creation. > I agree with you. > >>> + >>> +/** >>> + * Crypto session creation >>> + * >>> + * Create a crypto session. Operation occurs asynchronously if a completion >>> + * queue is specified else synchronously. >>> + * >>> + * @param params Session parameters >>> + * @param completion_event Event by which the session creation results are >>> + * delivered. >>> + * @param completion_queue Queue by which the completion event will be >>> + * delivered. Ignored if ODP_QUEUE_INVALID. >>> + * >>> + * @return Operation return code indicating success or failure for >>> + * when synchronous operation requested, else POSTED when >>> + * asynchronous operation is requested. >>> + */ >>> +odp_crypto_rc_e >>> +odp_crypto_session_create(struct odp_crypto_session_params *params, >>> + odp_buffer_t completion_event, >>> + odp_queue_t completion_queue); >>> + >>> + >>> +/** >>> + * Crypto session creation completion status >>> + * >>> + * Accessor function for obtaining creation status from the completion event. >>> + * >>> + * @param completion_event Event containing operation results >>> + * @param status Pointer to store creation return code >>> + */ >>> +void >>> +odp_crypto_get_ses_create_compl_status(odp_buffer_t completion_event, >>> + enum odp_crypto_ses_create_err *status); >>> + >>> +/** >>> + * Crypto session creation completion return value >>> + * >>> + * Accessor function for obtaining handle for newly created session. >>> + * >>> + * @param completion_event Event containing operation results >>> + * @param session Pointer to store session handle >>> + */ >>> +void >>> +odp_crypto_get_ses_create_compl_session(odp_buffer_t completion_event, >>> + odp_crypto_session_t *session); >>> + >>> +/** >>> + * Crypto per packet operation >>> + * >>> + * Performs the cryptographic operations specified during session creation >>> + * on the packet. >>> + * >>> + * @param params Operation parameters >>> + * @param completion_event Event by which the session creation results are >>> + * delivered. >>> + * >>> + * @return Operation return code indicating success or failure when session >>> + * indicates synchronous operation, else POSTED for asynchronous >>> + * operation. >>> + */ >>> +odp_crypto_rc_e >>> +odp_crypto_operation(struct odp_crypto_op_params *params, >>> + odp_buffer_t completion_event); >>> + >>> + >>> +/** >>> + * Crypto per packet operation completion status >>> + * >>> + * Accessor function for obtaining operation status from the completion event. >>> + * >>> + * @param completion_event Event containing operation results >>> + * @param auth Pointer to store authentication results >>> + * @param cipher Pointer to store cipher results >>> + */ >>> +void >>> +odp_crypto_get_operation_compl_status(odp_buffer_t completion_event, >>> + struct odp_crypto_compl_status *auth, >>> + struct odp_crypto_compl_status *cipher); >>> + >>> +/** >>> + * Generate random byte string >>> + * >>> + * @param buf Pointer to store result >>> + * @param len Pointer to input length value as well as return value >>> + * @param use_entropy (TODO: needs description) >>> + * >>> + * @return 0 if succesful >>> + */ >>> +int >>> +odp_hw_random_get(uint8_t *buf, size_t *len, bool use_entropy); >>> + >>> +/** >>> + * Initialize the crypto subsystem, called once from main thread >>> + * >>> + * @param max_sessions Maximum number of sessions to support >>> + * >>> + * @return 0 if succesful >>> + */ >>> +int >>> +odp_crypto_init(uint32_t max_sessions); >>> + >>> +#ifdef __cplusplus >>> +} >>> +#endif >>> + >>> +#endif >>> diff --git a/platform/linux-generic/Makefile b/platform/linux-generic/Makefile >>> index ec5d4a7..57f6d18 100644 >>> --- a/platform/linux-generic/Makefile >>> +++ b/platform/linux-generic/Makefile >>> @@ -72,6 +72,7 @@ OBJS += $(OBJ_DIR)/odp_time.o >>> OBJS += $(OBJ_DIR)/odp_timer.o >>> OBJS += $(OBJ_DIR)/odp_ring.o >>> OBJS += $(OBJ_DIR)/odp_rwlock.o >>> +OBJS += $(OBJ_DIR)/odp_crypto.o >>> ifeq ($(ODP_HAVE_NETMAP),yes) >>> OBJS += $(OBJ_DIR)/odp_packet_netmap.o >>> endif >>> diff --git a/platform/linux-generic/include/odp_crypto_internal.h b/platform/linux-generic/include/odp_crypto_internal.h >>> new file mode 100644 >>> index 0000000..e558864 >>> --- /dev/null >>> +++ b/platform/linux-generic/include/odp_crypto_internal.h >>> @@ -0,0 +1,86 @@ >>> +/* Copyright (c) 2013, Linaro Limited >>> + * All rights reserved. >>> + * >>> + * SPDX-License-Identifier: BSD-3-Clause >>> + */ >>> + >>> + >>> + >>> +#ifndef ODP_CRYPTO_INTERNAL_H_ >>> +#define ODP_CRYPTO_INTERNAL_H_ >>> + >>> +#ifdef __cplusplus >>> +extern "C" { >>> +#endif >>> + >>> +#include <openssl/des.h> >>> + >>> +#define OP_RESULT_MAGIC 0x91919191 >>> + >>> +/** Forward declaration of session structure */ >>> +struct odp_crypto_session_s; >>> + >>> +/** >>> + * Algorithm handler function prototype >>> + */ >>> +typedef >>> +enum crypto_alg_err (*crypto_func_t)(struct odp_crypto_op_params *params, >>> + struct odp_crypto_session_s *session); >>> + >>> +/** >>> + * Per crypto session data structure >>> + */ >>> +struct odp_crypto_session_s { >>> + uint32_t index; >> Why not uint_t? >> >> [RK] OK by me, since this data structure is internal to the >> linux-generic implementation, and the plan is to support a >> very small number of sessions this could be made as small as >> uint8_t I think. > OK, I'm fine with that. > >>> + enum odp_crypto_op op; >>> + enum odp_crypto_combination comb; >>> + odp_queue_t compl_queue; >>> + struct { >>> + enum odp_cipher_alg alg; >>> + struct { >>> + uint8_t *data; >> Why not uint_t? >> >> [RK] Same reason as above for IV since it is byte data. > OK. > >>> + size_t len; >>> + } iv; >>> + union { >>> + struct { >>> + DES_key_schedule ks1; >>> + DES_key_schedule ks2; >>> + DES_key_schedule ks3; >>> + } des; >>> + } data; >> Is there a good reason to have a union with only one member? >> >> [RK] The union is here with the intention that the linux-generic >> implementation will add support for other ciphers (AES for example). >> So the union is simply laying the ground work for expansion. > I understand, didn't think about that! > Thank you for your explanation. > >>> + crypto_func_t func; >>> + } cipher; >>> + struct { >>> + enum odp_auth_alg alg; >>> + union { >>> + struct { >>> + uint8_t key[16]; >> Why are we limiting the key to 16? >> I guess we want to limit the key up to 128 bits right? >> >> [RK] 128 bit key is specific to MD5, other algorithems >> would have their own entry in the union (again for future >> expansion). > OK good. > Maybe comment that it is for keys up to 128 bits? > >>> + } md5; >>> + } data; >> Is there a good reason to have a union with only one member? >> >>> + crypto_func_t func; >>> + } auth; >>> + >>> +}; >>> + >>> +/** >>> + * Per packet operation result >>> + */ >>> +struct odp_operation_result_s { >>> + uint32_t magic; >>> + struct odp_crypto_compl_status cipher; >>> + struct odp_crypto_compl_status auth; >>> +}; >>> + >>> +/** >>> + * Per session creation operation result >>> + */ >>> +struct odp_session_result_s { >>> + enum odp_crypto_ses_create_err rc; >>> + odp_crypto_session_t session; >>> +}; >>> + >>> +#ifdef __cplusplus >>> +} >>> +#endif >>> + >>> +#endif >>> diff --git a/platform/linux-generic/source/odp_crypto.c b/platform/linux-generic/source/odp_crypto.c >>> new file mode 100644 >>> index 0000000..4ae3645 >>> --- /dev/null >>> +++ b/platform/linux-generic/source/odp_crypto.c >>> @@ -0,0 +1,435 @@ >>> +/* Copyright (c) 2013, Linaro Limited >>> + * All rights reserved. >>> + * >>> + * SPDX-License-Identifier: BSD-3-Clause >>> + */ >>> + >>> +#include <odp_crypto.h> >>> +#include <odp_internal.h> >>> +#include <odp_atomic.h> >>> +#include <odp_spinlock.h> >>> +#include <odp_sync.h> >>> +#include <odp_debug.h> >>> +#include <odp_align.h> >>> +#include <odp_shared_memory.h> >>> +#include <odp_crypto_internal.h> >>> + >>> +#include <string.h> >>> + >>> +#include <openssl/des.h> >>> +#include <openssl/rand.h> >>> +#include <openssl/hmac.h> >>> +#include <openssl/evp.h> >>> + >>> +#define MAX_SESSIONS 32 >>> + >>> +typedef struct { >>> + odp_atomic_u32_t next; >>> + uint32_t max; >>> + struct odp_crypto_session_s sessions[0]; >> Pointer instead? >> >> [RK] This is specifically for allocating memory where >> the storage for sessions immediately follows the >> structure, allowing an arbitrary number of sessions to >> be supported at allocation time like shown yet accessed >> as if they were statically allocated: >> >> /* Calculate the memory size we need */ >> mem_size = sizeof(*global); >> mem_size += (max_sessions * sizeof(struct odp_crypto_session_s)); >> >> and you can access individual sessions like this: >> >> session = &global->sessions[idx]; >> >> I've used this a lot over the years, but if it gives folks heartburn >> I can allocate memory in multiple stages, etc. > I'm fine with either way as long as we do the same, what does others > think about this? > >> Cheers, >> Anders >> >>> +} odp_crypto_global_t; >>> + >>> +static odp_crypto_global_t *global; >>> + >>> +/* >>> + * TODO: This is a serious hack to allow us to use packet buffer to convey >>> + * crypto operation results by placing them at the very end of the >>> + * packet buffer. >>> + */ >>> +static >>> +struct odp_operation_result_s *get_op_result_from_buffer(odp_buffer_t buf) >>> +{ >>> + uint8_t *temp; >>> + struct odp_operation_result_s *result; >>> + >>> + temp = odp_buffer_addr(buf); >>> + temp += odp_buffer_size(buf); >>> + temp -= sizeof(*result); >>> + result = (struct odp_operation_result_s *)(void *)temp; >>> + return result; >>> +} >>> + >>> +static >>> +struct odp_crypto_session_s *alloc_session(void) >>> +{ >>> + uint32_t idx; >>> + struct odp_crypto_session_s *session = NULL; >>> + >>> + idx = odp_atomic_fetch_inc_u32(&global->next); >>> + if (idx < global->max) { >>> + session = &global->sessions[idx]; >>> + session->index = idx; >>> + } >>> + return session; >>> +} >>> + >>> +static >>> +enum crypto_alg_err null_crypto_routine( >>> + struct odp_crypto_op_params *params ODP_UNUSED, >>> + struct odp_crypto_session_s *session ODP_UNUSED) >>> +{ >>> + return ODP_CRYPTO_ALG_ERR_NONE; >>> +} >>> + >>> +static >>> +enum crypto_alg_err md5_gen(struct odp_crypto_op_params *params, >>> + struct odp_crypto_session_s *session) >>> +{ >>> + uint8_t *data = odp_packet_buf_addr(params->pkt); >>> + uint8_t *icv = data; >>> + uint32_t len = params->auth_range.length; >>> + uint8_t hash[EVP_MAX_MD_SIZE]; >>> + uint32_t hlen = 12; >>> + >>> + /* Adjust pointer for beginning of area to auth */ >>> + data += params->auth_range.offset; >>> + icv += params->hash_result_offset; >>> + >>> + /* Hash it */ >>> + HMAC(EVP_md5(), session->auth.data.md5.key, 16, data, len, hash, &hlen); >>> + >>> + /* Copy to the output location */ >>> + memcpy(icv, hash, 12); >>> + >>> + return ODP_CRYPTO_ALG_ERR_NONE; >>> +} >>> + >>> + >>> +static >>> +enum crypto_alg_err md5_check(struct odp_crypto_op_params *params, >>> + struct odp_crypto_session_s *session) >>> +{ >>> + uint8_t *data = odp_packet_buf_addr(params->pkt); >>> + uint8_t *icv = data; >>> + uint32_t len = params->auth_range.length; >>> + uint8_t hash[EVP_MAX_MD_SIZE]; >>> + uint32_t hlen = 12; >>> + >>> + /* Adjust pointer for beginning of area to auth */ >>> + data += params->auth_range.offset; >>> + icv += params->hash_result_offset; >>> + >>> + /* Copy current value out and clear it before authentication */ >>> + memcpy(hash, icv, hlen); >>> + memset(icv, 0, hlen); >>> + >>> + /* Hash it */ >>> + HMAC(EVP_md5(), session->auth.data.md5.key, 16, data, len, icv, &hlen); >>> + >>> + /* Verify match */ >>> + if (0 != memcmp(icv, hash, 12)) >>> + return ODP_CRYPTO_ALG_ERR_ICV_CHECK; >>> + >>> + /* Matched */ >>> + return ODP_CRYPTO_ALG_ERR_NONE; >>> +} >>> + >>> +static >>> +enum crypto_alg_err des_encrypt(struct odp_crypto_op_params *params, >>> + struct odp_crypto_session_s *session) >>> +{ >>> + uint8_t *data = odp_packet_buf_addr(params->pkt); >>> + uint32_t len = params->cipher_range.length; >>> + DES_cblock *iv = (DES_cblock *)session->cipher.iv.data; >>> + >>> + /* Adjust pointer for beginning of area to cipher */ >>> + data += params->cipher_range.offset; >>> + >>> + /* Override IV if requested */ >>> + if (params->override_iv_ptr) >>> + iv = (DES_cblock *)params->override_iv_ptr; >>> + >>> + /* Encrypt it */ >>> + DES_ede3_cbc_encrypt(data, >>> + data, >>> + len, >>> + &session->cipher.data.des.ks1, >>> + &session->cipher.data.des.ks2, >>> + &session->cipher.data.des.ks3, >>> + iv, >>> + 1); >>> + >>> + return ODP_CRYPTO_ALG_ERR_NONE; >>> +} >>> + >>> +static >>> +enum crypto_alg_err des_decrypt(struct odp_crypto_op_params *params, >>> + struct odp_crypto_session_s *session) >>> +{ >>> + uint8_t *data = odp_packet_buf_addr(params->pkt); >>> + uint32_t len = params->cipher_range.length; >>> + DES_cblock *iv = (DES_cblock *)session->cipher.iv.data; >>> + >>> + /* Adjust pointer for beginning of area to cipher */ >>> + data += params->cipher_range.offset; >>> + >>> + /* Override IV if requested */ >>> + if (params->override_iv_ptr) >>> + iv = (DES_cblock *)params->override_iv_ptr; >>> + >>> + /* Decrypt it */ >>> + DES_ede3_cbc_encrypt(data, >>> + data, >>> + len, >>> + &session->cipher.data.des.ks1, >>> + &session->cipher.data.des.ks2, >>> + &session->cipher.data.des.ks3, >>> + iv, >>> + 0); >>> + >>> + return ODP_CRYPTO_ALG_ERR_NONE; >>> +} >>> + >>> +static >>> +int process_des_params(struct odp_crypto_session_s *session, >>> + struct odp_crypto_session_params *params) >>> +{ >>> + /* Verify IV len is either 0 or 8 */ >>> + if (!((0 == params->iv_len) || (8 == params->iv_len))) >>> + return -1; >>> + >>> + /* Verify IV pointer */ >>> + if (params->iv_len && !params->iv) >>> + return -1; >>> + >>> + /* Set function */ >>> + if (ODP_CRYPTO_OP_ENCODE == params->op) >>> + session->cipher.func = des_encrypt; >>> + else >>> + session->cipher.func = des_decrypt; >>> + >>> + /* Convert keys */ >>> + DES_set_key(¶ms->cipher_key->des.k1, &session->cipher.data.des.ks1); >>> + DES_set_key(¶ms->cipher_key->des.k2, &session->cipher.data.des.ks2); >>> + DES_set_key(¶ms->cipher_key->des.k3, &session->cipher.data.des.ks3); >>> + >>> + return 0; >>> +} >>> + >>> +static >>> +int process_md5_params(struct odp_crypto_session_s *session, >>> + struct odp_crypto_session_params *params) >>> +{ >>> + /* Set function */ >>> + if (ODP_CRYPTO_OP_ENCODE == params->op) >>> + session->auth.func = md5_gen; >>> + else >>> + session->auth.func = md5_check; >>> + >>> + /* Convert keys */ >>> + memcpy(session->auth.data.md5.key, params->auth_key->md5.key, 16); >>> + >>> + return 0; >>> +} >>> + >>> +odp_crypto_rc_e >>> +odp_crypto_session_create(struct odp_crypto_session_params *params, >>> + odp_buffer_t completion_event, >>> + odp_queue_t completion_queue) >>> +{ >>> + int rc; >>> + struct odp_crypto_session_s *session; >>> + struct odp_session_result_s *result = odp_buffer_addr(completion_event); >>> + >>> + /* Default to failure result */ >>> + result->rc = ODP_CRYPTO_SES_CREATE_NONE; >>> + result->session = ODP_CRYPTO_SESSION_INVALID; >>> + >>> + /* Allocate memory for this session */ >>> + session = alloc_session(); >>> + if (NULL == session) >>> + return ODP_CRYPTO_OP_ERROR; >>> + >>> + /* Copy stuff over */ >>> + session->op = params->op; >>> + session->comb = params->comb; >>> + session->compl_queue = params->compl_queue; >>> + session->cipher.alg = params->cipher_alg; >>> + session->cipher.iv.data = params->iv; >>> + session->cipher.iv.len = params->iv_len; >>> + session->auth.alg = params->auth_alg; >>> + >>> + /* Process based on cipher */ >>> + switch (params->cipher_alg) { >>> + case ODP_CIPHER_ALG_NULL: >>> + session->cipher.func = null_crypto_routine; >>> + rc = 0; >>> + break; >>> + case ODP_CIPHER_ALG_DES: >>> + case ODP_CIPHER_ALG_3DES_CBC: >>> + rc = process_des_params(session, params); >>> + break; >>> + default: >>> + rc = -1; >>> + } >>> + >>> + /* Check result */ >>> + if (rc) >>> + return ODP_CRYPTO_OP_ERROR; >>> + >>> + /* Process based on auth */ >>> + switch (params->auth_alg) { >>> + case ODP_AUTH_ALG_NULL: >>> + session->auth.func = null_crypto_routine; >>> + rc = 0; >>> + break; >>> + case ODP_AUTH_ALG_MD5_96: >>> + rc = process_md5_params(session, params); >>> + break; >>> + default: >>> + rc = -1; >>> + } >>> + >>> + /* Check result */ >>> + if (rc) >>> + return ODP_CRYPTO_OP_ERROR; >>> + >>> + /* We're happy */ >>> + result->rc = ODP_CRYPTO_SES_CREATE_OK; >>> + result->session = (intptr_t)session; >>> + >>> + /* If there is a queue post else we're good */ >>> + if (ODP_QUEUE_INVALID != completion_queue) { >>> + odp_queue_enq(completion_queue, completion_event); >>> + return ODP_CRYPTO_OP_POSTED; >>> + } >>> + >>> + return ODP_CRYPTO_OP_OK; >>> +} >>> + >>> + >>> +odp_crypto_rc_e >>> +odp_crypto_operation(struct odp_crypto_op_params *params, >>> + odp_buffer_t completion_event) >>> +{ >>> + enum crypto_alg_err rc_cipher = ODP_CRYPTO_ALG_ERR_NONE; >>> + enum crypto_alg_err rc_auth = ODP_CRYPTO_ALG_ERR_NONE; >>> + struct odp_crypto_session_s *session; >>> + struct odp_operation_result_s *result; >>> + >>> + session = (struct odp_crypto_session_s *)(intptr_t)params->session; >>> + >>> + /* >>> + * robking: need to understand infrastructure for scattered packets >>> + * for now just don't support them >>> + */ >>> + if (odp_buffer_is_scatter(odp_buffer_from_packet(params->pkt))) >>> + return ODP_CRYPTO_OP_ERROR; >>> + >>> + /* >>> + * robking: for now we are only going to support in place >>> + */ >>> + if (params->pkt != params->out_pkt) >>> + return ODP_CRYPTO_OP_ERROR; >>> + >>> + /* Invoke the functions */ >>> + switch (session->comb) { >>> + case ODP_CRYPTO_CIPHER_ONLY: >>> + rc_cipher = session->cipher.func(params, session); >>> + break; >>> + case ODP_CRYPTO_AUTH_ONLY: >>> + rc_auth = session->auth.func(params, session); >>> + break; >>> + case ODP_CRYPTO_AUTH_CIPHERTEXT: >>> + if (ODP_CRYPTO_OP_ENCODE == session->op) { >>> + rc_cipher = session->cipher.func(params, session); >>> + rc_auth = session->auth.func(params, session); >>> + } else { >>> + rc_auth = session->auth.func(params, session); >>> + rc_cipher = session->cipher.func(params, session); >>> + } >>> + break; >>> + } >>> + >>> + /* Build Result (no HW so no errors) */ >>> + result = get_op_result_from_buffer(completion_event); >>> + result->magic = OP_RESULT_MAGIC; >>> + result->cipher.alg.cipher = session->cipher.alg; >>> + result->cipher.alg_err = rc_cipher; >>> + result->cipher.hw_err = ODP_CRYPTO_HW_ERR_NONE; >>> + result->auth.alg.auth = session->auth.alg; >>> + result->auth.alg_err = rc_auth; >>> + result->auth.hw_err = ODP_CRYPTO_HW_ERR_NONE; >>> + >>> + /* >>> + * robking: a) the queue is supposed to come from session >>> + * b) ordering question asks whether we must >>> + * use the packet to return status >>> + */ >>> + if (ODP_QUEUE_INVALID != session->compl_queue) { >>> + odp_queue_enq(session->compl_queue, completion_event); >>> + return ODP_CRYPTO_OP_POSTED; >>> + } >>> + >>> + return ODP_CRYPTO_OP_OK; >>> +} >>> + >>> + >>> +int >>> +odp_crypto_init(uint32_t max_sessions) >>> +{ >>> + size_t mem_size; >>> + >>> + /* Force down to our limit */ >>> + if (MAX_SESSIONS < max_sessions) >>> + max_sessions = MAX_SESSIONS; >>> + >>> + /* Calculate the memory size we need */ >>> + mem_size = sizeof(*global); >>> + mem_size += (max_sessions * sizeof(struct odp_crypto_session_s)); >>> + >>> + /* Allocate our globally shared memory */ >>> + global = odp_shm_reserve("crypto_pool", mem_size, ODP_CACHE_LINE_SIZE); >>> + >>> + /* Clear it out */ >>> + memset(global, 0, mem_size); >>> + >>> + /* Initialize it */ >>> + global->max = max_sessions; >>> + >>> + return 0; >>> +} >>> + >>> +int >>> +odp_hw_random_get(uint8_t *buf, size_t *len, bool use_entropy ODP_UNUSED) >>> +{ >>> + int rc; >>> + rc = RAND_bytes(buf, *len); >>> + return ((1 == rc) ? 0 : -1); >>> +} >>> + >>> +void >>> +odp_crypto_get_operation_compl_status(odp_buffer_t completion_event, >>> + struct odp_crypto_compl_status *auth, >>> + struct odp_crypto_compl_status *cipher) >>> +{ >>> + struct odp_operation_result_s *result; >>> + >>> + result = get_op_result_from_buffer(completion_event); >>> + >>> + if (OP_RESULT_MAGIC != result->magic) >>> + abort(); >>> + >>> + memcpy(auth, &result->auth, sizeof(*auth)); >>> + memcpy(cipher, &result->cipher, sizeof(*cipher)); >>> +} >>> + >>> +void >>> +odp_crypto_get_ses_create_compl_status(odp_buffer_t completion_event, >>> + enum odp_crypto_ses_create_err *status) >>> +{ >>> + struct odp_session_result_s *result; >>> + >>> + result = odp_buffer_addr(completion_event); >>> + *status = result->rc; >>> +} >>> + >>> +void >>> +odp_crypto_get_ses_create_compl_session(odp_buffer_t completion_event, >>> + odp_crypto_session_t *session) >>> +{ >>> + struct odp_session_result_s *result; >>> + >>> + result = odp_buffer_addr(completion_event); >>> + *session = result->session; >>> +} >>> -- >>> 1.9.2 >>> >>> >>> _______________________________________________ >>> lng-odp mailing list >>> lng-odp@lists.linaro.org >>> http://lists.linaro.org/mailman/listinfo/lng-odp > _______________________________________________ > lng-odp mailing list > lng-odp@lists.linaro.org > http://lists.linaro.org/mailman/listinfo/lng-odp
Hi Maxim, I am currently working on new versions of both. The "linux-generic crypto" patch (this one) still has outstanding questions from Anders. I am currently adding support for standalone operation (no packet IO required) to the example application in " IPsec example application" patch. Unless someone feels strongly otherwise, I suggest we wait until after the sprint. -----Original Message----- From: Maxim Uvarov [mailto:maxim.uvarov@linaro.org] Sent: Wednesday, June 04, 2014 12:31 PM To: Anders Roxell; Robbie King (robking) Cc: lng-odp@lists.linaro.org Subject: Re: [lng-odp] [PATCHv2] linux-generic crypto Hello Robbie, are you working on new version of this patch or this patch is good to merge now? Also the same question about example app patch. Thank you, Maxim. On 06/04/2014 04:51 PM, Anders Roxell wrote: > Sorry for the late reply! > See inline. > > On 2014-06-02 18:28, Robbie King (robking) wrote: >> Hi Anders, thanks for having a look. See [RK] inline. >> >> -----Original Message----- >> From: Anders Roxell [mailto:anders.roxell@linaro.org] >> Sent: Monday, June 02, 2014 2:02 PM >> To: Robbie King (robking) >> Cc: lng-odp@lists.linaro.org >> Subject: Re: [lng-odp] [PATCHv2] linux-generic crypto >> >> I'm sorry for my ignorance in crypto, however it looks like we can use >> generic length variables, but instead we limit them to uint8_t, 16 or 32. >> I didn't comment them all but there are multiple cases of them. >> Please see inline comments. >> >> >> On 2014-05-30 13:15, Robbie King wrote: >>> Initial linux-generic crypto implementation. Note that these changes >>> require installing and linking with libssl-dev. The camel case warnings >>> are also due to calls into libssl-dev. >>> >>> v1..v2: Fixed ARM compile warning, and modified "odp_hw_random_get" to >>> take length as "size_t" instead of "uint32_t". >>> >>> Signed-off-by: Robbie King <robking@cisco.com> >>> --- >>> include/odp_crypto.h | 295 ++++++++++++++ >>> platform/linux-generic/Makefile | 1 + >>> .../linux-generic/include/odp_crypto_internal.h | 86 ++++ >>> platform/linux-generic/source/odp_crypto.c | 435 +++++++++++++++++++++ >>> 4 files changed, 817 insertions(+) >>> create mode 100644 include/odp_crypto.h >>> create mode 100644 platform/linux-generic/include/odp_crypto_internal.h >>> create mode 100644 platform/linux-generic/source/odp_crypto.c >>> >>> diff --git a/include/odp_crypto.h b/include/odp_crypto.h >>> new file mode 100644 >>> index 0000000..725e8f1 >>> --- /dev/null >>> +++ b/include/odp_crypto.h >>> @@ -0,0 +1,295 @@ >>> +/* Copyright (c) 2013, Linaro Limited >> Nit: date 2014 >> Should be like this for all the files. >> >> [RK] good catch. >> >>> + * All rights reserved. >>> + * >>> + * SPDX-License-Identifier: BSD-3-Clause >>> + */ >>> + >>> + >>> +/** >>> + * @file >>> + * >>> + * ODP crypto >>> + */ >>> + >>> +#ifndef ODP_CRYPTO_H_ >>> +#define ODP_CRYPTO_H_ >>> + >>> +#ifdef __cplusplus >>> +extern "C" { >>> +#endif >>> + >>> +#include <odp_std_types.h> >>> +#include <odp_buffer.h> >>> +#include <odp_buffer_pool.h> >>> +#include <odp_queue.h> >>> +#include <odp_packet.h> >>> + >>> +/** Invalid session handle */ >>> +#define ODP_CRYPTO_SESSION_INVALID (-1ULL) >>> + >>> +/** >>> + * Crypto API opaque session handle >>> + */ >>> +typedef uint64_t odp_crypto_session_t; >>> + >>> +/** >>> + * Crypto API operation mode >>> + */ >>> +enum odp_crypto_op_mode { >>> + ODP_CRYPTO_SYNC, /**< Synchronous, return results immediately */ >>> + ODP_CRYPTO_ASYNC, /**< Aynchronous, return results via posted event */ >>> +}; >>> + >>> +/** >>> + * Crypto API operation type >>> + */ >>> +enum odp_crypto_op { >>> + ODP_CRYPTO_OP_ENCODE, /**< Encrypt and/or compute authentication ICV */ >>> + ODP_CRYPTO_OP_DECODE /**< Decrypt and/or verify authentication ICV */ >>> +}; >>> + >>> +/** >>> + * Crypto API cipher algorithm >>> + */ >>> +enum odp_cipher_alg { >>> + ODP_CIPHER_ALG_NULL, /**< No cipher algorithm specified */ >>> + ODP_CIPHER_ALG_DES, /**< DES */ >>> + ODP_CIPHER_ALG_3DES_CBC, /**< Triple DES with cipher block chaining */ >>> +}; >>> + >>> +/** >>> + * Crypto API authentication algorithm >>> + */ >>> +enum odp_auth_alg { >>> + ODP_AUTH_ALG_NULL, /**< No authentication algorithm specified */ >>> + ODP_AUTH_ALG_MD5_96, /**< HMAC-MD5 with 96 bit key */ >>> +}; >>> + >>> +/** >>> + * Crypto API operation order >>> + */ >>> +enum odp_crypto_combination { >>> + ODP_CRYPTO_CIPHER_ONLY, /**< Only perform cipher */ >>> + ODP_CRYPTO_AUTH_ONLY, /**< Only perform authentication */ >>> + ODP_CRYPTO_AUTH_CIPHERTEXT /**< Cipher then authentication on encode */ >>> +}; >>> + >>> +/** >>> + * Crypto API key >>> + */ >>> +typedef struct odp_key_s { >>> + union { >>> + /** DES/3DES key definition (set all same for DES) */ >>> + struct { >>> + uint8_t k1[8]; /**< First key */ >>> + uint8_t k2[8]; /**< Second key */ >>> + uint8_t k3[8]; /**< Third key */ >>> + } des; >>> + /** MD5 key */ >>> + struct { >>> + uint8_t key[16]; /**< Key up to 128 bits */ >>> + } md5; >>> + }; >>> +} odp_key_t; >>> + >>> +/** >>> + * Crypto API data range specifier >>> + */ >>> +struct odp_data_range { >>> + uint16_t offset; /**< Offset from beginning of buffer (chain) */ >>> + uint16_t length; /**< Length of data to operate on */ >> Why do we use uint16_t? >> Can't the data be bigger than this? >> >> [RK] this came from the ODP crypto spec, I think in general the idea >> is packets will never be larger than an Ethernet jumbo (roughly 10K) >> so no need to support larger than 64K. > So was that a reason for choosing uint16_t in the spec? > Or shall we change the spec? > >>> +}; >>> + >>> +/** >>> + * Crypto API session creation paramters >>> + * >>> + * TODO: add "odp_session_proc_info_t" >>> + */ >>> +struct odp_crypto_session_params { >>> + enum odp_crypto_op op; /**< Encode versus decode */ >>> + enum odp_crypto_combination comb; /**< Operation order */ >>> + enum odp_crypto_op_mode pref_mode; /**< Preferred sync vs async */ >>> + enum odp_cipher_alg cipher_alg; /**< Cipher algorithm */ >>> + odp_key_t *cipher_key; /**< Cipher key */ >>> + uint8_t *iv; /**< Cipher Initialization Vector (IV) */ >> Why do we use uint8_t and not uint_t? >> >> [RK] This one also comes from the spec, my understanding is for crypto >> the IV is always an opaque byte string that can be different lengths >> for different ciphers. > OK, thanks. > >>> + size_t iv_len; /**< Cipher IV length */ >>> + enum odp_auth_alg auth_alg; /**< Authentication algorithm */ >>> + odp_key_t *auth_key; /**< Authentication key */ >>> + odp_queue_t compl_queue; /**< Async mode completion event queue */ >>> +}; >>> + >>> +/** >>> + * Crypto API per packet operation parameters >>> + */ >>> +struct odp_crypto_op_params { >>> + odp_crypto_session_t session; /**< Session handle from creation */ >>> + odp_packet_t pkt; /**< Input packet buffer */ >>> + odp_packet_t out_pkt; /**< Output packet buffer (optional) */ >>> + uint8_t *override_iv_ptr; /**< Override session IV pointer */ >>> + unsigned hash_result_offset; /**< Offset from start of packet buffer for hash result */ >>> + struct odp_data_range cipher_range; /**< Data range to apply cipher */ >>> + struct odp_data_range auth_range; /**< Data range to authenticate */ >>> +}; >>> + >>> +/** >>> + * Crypto API session creation return code >>> + * >>> + * TODO: seems confusing, maybe _rc instead >>> + */ >>> +enum odp_crypto_ses_create_err { >>> + ODP_CRYPTO_SES_CREATE_NONE, /**< No session created? need to clarify */ >>> + ODP_CRYPTO_SES_CREATE_OK, /**< Session created successfully */ >>> +}; >>> + >>> +/** >>> + * Crypto API algorithm return code >>> + */ >>> +enum crypto_alg_err { >>> + ODP_CRYPTO_ALG_ERR_NONE, /**< Algorithm successful */ >>> + ODP_CRYPTO_ALG_ERR_DATA_SIZE, /**< Invalid data block size */ >>> + ODP_CRYPTO_ALG_ERR_KEY_SIZE, /**< Key size invalid for algorithm */ >>> + ODP_CRYPTO_ALG_ERR_ICV_CHECK, /**< Computed ICV value mismatch */ >>> +}; >>> + >>> +/** >>> + * Crypto API operation return code >>> + */ >>> +typedef enum odp_crypto_rc { >>> + ODP_CRYPTO_OP_OK, /**< Operation completed, results are valid */ >>> + ODP_CRYPTO_OP_POSTED, /**< Operation was posted, results delivered via completion queue */ >>> + ODP_CRYPTO_OP_ERROR, /**< Operation failed */ >>> +} odp_crypto_rc_e; >>> + >>> +/** >>> + * Crypto API hardware centric return code >>> + */ >>> +enum crypto_hw_err { >>> + ODP_CRYPTO_HW_ERR_NONE, /**< Operation completed successfully */ >>> + ODP_CRYPTO_HW_ERR_DMA, /**< Error detected during DMA of data */ >>> + ODP_CRYPTO_HW_ERR_BP_DEPLETED, /**< Operation failed due to buffer pool depletion */ >>> +}; >>> + >>> +/** >>> + * Crypto API algorithm (cipher or authentication) >>> + */ >>> +typedef union odp_crypto_alg_u { >>> + enum odp_cipher_alg cipher; /**< Cipher algorithm */ >>> + enum odp_auth_alg auth; /**< Authentication algorithm */ >>> +} odp_crypto_alg_t; >>> + >>> +/** >>> + * Cryto API per packet operation completion status >>> + */ >>> +struct odp_crypto_compl_status { >>> + odp_crypto_alg_t alg; /**< Requested algorithm */ >>> + enum crypto_alg_err alg_err; /**< Algorithm specific return code */ >>> + enum crypto_hw_err hw_err; /**< Hardware specific return code */ >>> +}; >>> + >> Is there a good reason why we are mixing enum name with typedef? >> >> [RK] I agree, I would like to see us perhaps agree on typedef'ing >> enums. This one is a little kludgy anyway, I would vote for removing >> the ALG from the completion status since it is simply a mirror of the >> ALG (cipher or auth) which was specified during session creation. > I agree with you. > >>> + >>> +/** >>> + * Crypto session creation >>> + * >>> + * Create a crypto session. Operation occurs asynchronously if a completion >>> + * queue is specified else synchronously. >>> + * >>> + * @param params Session parameters >>> + * @param completion_event Event by which the session creation results are >>> + * delivered. >>> + * @param completion_queue Queue by which the completion event will be >>> + * delivered. Ignored if ODP_QUEUE_INVALID. >>> + * >>> + * @return Operation return code indicating success or failure for >>> + * when synchronous operation requested, else POSTED when >>> + * asynchronous operation is requested. >>> + */ >>> +odp_crypto_rc_e >>> +odp_crypto_session_create(struct odp_crypto_session_params *params, >>> + odp_buffer_t completion_event, >>> + odp_queue_t completion_queue); >>> + >>> + >>> +/** >>> + * Crypto session creation completion status >>> + * >>> + * Accessor function for obtaining creation status from the completion event. >>> + * >>> + * @param completion_event Event containing operation results >>> + * @param status Pointer to store creation return code >>> + */ >>> +void >>> +odp_crypto_get_ses_create_compl_status(odp_buffer_t completion_event, >>> + enum odp_crypto_ses_create_err *status); >>> + >>> +/** >>> + * Crypto session creation completion return value >>> + * >>> + * Accessor function for obtaining handle for newly created session. >>> + * >>> + * @param completion_event Event containing operation results >>> + * @param session Pointer to store session handle >>> + */ >>> +void >>> +odp_crypto_get_ses_create_compl_session(odp_buffer_t completion_event, >>> + odp_crypto_session_t *session); >>> + >>> +/** >>> + * Crypto per packet operation >>> + * >>> + * Performs the cryptographic operations specified during session creation >>> + * on the packet. >>> + * >>> + * @param params Operation parameters >>> + * @param completion_event Event by which the session creation results are >>> + * delivered. >>> + * >>> + * @return Operation return code indicating success or failure when session >>> + * indicates synchronous operation, else POSTED for asynchronous >>> + * operation. >>> + */ >>> +odp_crypto_rc_e >>> +odp_crypto_operation(struct odp_crypto_op_params *params, >>> + odp_buffer_t completion_event); >>> + >>> + >>> +/** >>> + * Crypto per packet operation completion status >>> + * >>> + * Accessor function for obtaining operation status from the completion event. >>> + * >>> + * @param completion_event Event containing operation results >>> + * @param auth Pointer to store authentication results >>> + * @param cipher Pointer to store cipher results >>> + */ >>> +void >>> +odp_crypto_get_operation_compl_status(odp_buffer_t completion_event, >>> + struct odp_crypto_compl_status *auth, >>> + struct odp_crypto_compl_status *cipher); >>> + >>> +/** >>> + * Generate random byte string >>> + * >>> + * @param buf Pointer to store result >>> + * @param len Pointer to input length value as well as return value >>> + * @param use_entropy (TODO: needs description) >>> + * >>> + * @return 0 if succesful >>> + */ >>> +int >>> +odp_hw_random_get(uint8_t *buf, size_t *len, bool use_entropy); >>> + >>> +/** >>> + * Initialize the crypto subsystem, called once from main thread >>> + * >>> + * @param max_sessions Maximum number of sessions to support >>> + * >>> + * @return 0 if succesful >>> + */ >>> +int >>> +odp_crypto_init(uint32_t max_sessions); >>> + >>> +#ifdef __cplusplus >>> +} >>> +#endif >>> + >>> +#endif >>> diff --git a/platform/linux-generic/Makefile b/platform/linux-generic/Makefile >>> index ec5d4a7..57f6d18 100644 >>> --- a/platform/linux-generic/Makefile >>> +++ b/platform/linux-generic/Makefile >>> @@ -72,6 +72,7 @@ OBJS += $(OBJ_DIR)/odp_time.o >>> OBJS += $(OBJ_DIR)/odp_timer.o >>> OBJS += $(OBJ_DIR)/odp_ring.o >>> OBJS += $(OBJ_DIR)/odp_rwlock.o >>> +OBJS += $(OBJ_DIR)/odp_crypto.o >>> ifeq ($(ODP_HAVE_NETMAP),yes) >>> OBJS += $(OBJ_DIR)/odp_packet_netmap.o >>> endif >>> diff --git a/platform/linux-generic/include/odp_crypto_internal.h b/platform/linux-generic/include/odp_crypto_internal.h >>> new file mode 100644 >>> index 0000000..e558864 >>> --- /dev/null >>> +++ b/platform/linux-generic/include/odp_crypto_internal.h >>> @@ -0,0 +1,86 @@ >>> +/* Copyright (c) 2013, Linaro Limited >>> + * All rights reserved. >>> + * >>> + * SPDX-License-Identifier: BSD-3-Clause >>> + */ >>> + >>> + >>> + >>> +#ifndef ODP_CRYPTO_INTERNAL_H_ >>> +#define ODP_CRYPTO_INTERNAL_H_ >>> + >>> +#ifdef __cplusplus >>> +extern "C" { >>> +#endif >>> + >>> +#include <openssl/des.h> >>> + >>> +#define OP_RESULT_MAGIC 0x91919191 >>> + >>> +/** Forward declaration of session structure */ >>> +struct odp_crypto_session_s; >>> + >>> +/** >>> + * Algorithm handler function prototype >>> + */ >>> +typedef >>> +enum crypto_alg_err (*crypto_func_t)(struct odp_crypto_op_params *params, >>> + struct odp_crypto_session_s *session); >>> + >>> +/** >>> + * Per crypto session data structure >>> + */ >>> +struct odp_crypto_session_s { >>> + uint32_t index; >> Why not uint_t? >> >> [RK] OK by me, since this data structure is internal to the >> linux-generic implementation, and the plan is to support a >> very small number of sessions this could be made as small as >> uint8_t I think. > OK, I'm fine with that. > >>> + enum odp_crypto_op op; >>> + enum odp_crypto_combination comb; >>> + odp_queue_t compl_queue; >>> + struct { >>> + enum odp_cipher_alg alg; >>> + struct { >>> + uint8_t *data; >> Why not uint_t? >> >> [RK] Same reason as above for IV since it is byte data. > OK. > >>> + size_t len; >>> + } iv; >>> + union { >>> + struct { >>> + DES_key_schedule ks1; >>> + DES_key_schedule ks2; >>> + DES_key_schedule ks3; >>> + } des; >>> + } data; >> Is there a good reason to have a union with only one member? >> >> [RK] The union is here with the intention that the linux-generic >> implementation will add support for other ciphers (AES for example). >> So the union is simply laying the ground work for expansion. > I understand, didn't think about that! > Thank you for your explanation. > >>> + crypto_func_t func; >>> + } cipher; >>> + struct { >>> + enum odp_auth_alg alg; >>> + union { >>> + struct { >>> + uint8_t key[16]; >> Why are we limiting the key to 16? >> I guess we want to limit the key up to 128 bits right? >> >> [RK] 128 bit key is specific to MD5, other algorithems >> would have their own entry in the union (again for future >> expansion). > OK good. > Maybe comment that it is for keys up to 128 bits? > >>> + } md5; >>> + } data; >> Is there a good reason to have a union with only one member? >> >>> + crypto_func_t func; >>> + } auth; >>> + >>> +}; >>> + >>> +/** >>> + * Per packet operation result >>> + */ >>> +struct odp_operation_result_s { >>> + uint32_t magic; >>> + struct odp_crypto_compl_status cipher; >>> + struct odp_crypto_compl_status auth; >>> +}; >>> + >>> +/** >>> + * Per session creation operation result >>> + */ >>> +struct odp_session_result_s { >>> + enum odp_crypto_ses_create_err rc; >>> + odp_crypto_session_t session; >>> +}; >>> + >>> +#ifdef __cplusplus >>> +} >>> +#endif >>> + >>> +#endif >>> diff --git a/platform/linux-generic/source/odp_crypto.c b/platform/linux-generic/source/odp_crypto.c >>> new file mode 100644 >>> index 0000000..4ae3645 >>> --- /dev/null >>> +++ b/platform/linux-generic/source/odp_crypto.c >>> @@ -0,0 +1,435 @@ >>> +/* Copyright (c) 2013, Linaro Limited >>> + * All rights reserved. >>> + * >>> + * SPDX-License-Identifier: BSD-3-Clause >>> + */ >>> + >>> +#include <odp_crypto.h> >>> +#include <odp_internal.h> >>> +#include <odp_atomic.h> >>> +#include <odp_spinlock.h> >>> +#include <odp_sync.h> >>> +#include <odp_debug.h> >>> +#include <odp_align.h> >>> +#include <odp_shared_memory.h> >>> +#include <odp_crypto_internal.h> >>> + >>> +#include <string.h> >>> + >>> +#include <openssl/des.h> >>> +#include <openssl/rand.h> >>> +#include <openssl/hmac.h> >>> +#include <openssl/evp.h> >>> + >>> +#define MAX_SESSIONS 32 >>> + >>> +typedef struct { >>> + odp_atomic_u32_t next; >>> + uint32_t max; >>> + struct odp_crypto_session_s sessions[0]; >> Pointer instead? >> >> [RK] This is specifically for allocating memory where >> the storage for sessions immediately follows the >> structure, allowing an arbitrary number of sessions to >> be supported at allocation time like shown yet accessed >> as if they were statically allocated: >> >> /* Calculate the memory size we need */ >> mem_size = sizeof(*global); >> mem_size += (max_sessions * sizeof(struct odp_crypto_session_s)); >> >> and you can access individual sessions like this: >> >> session = &global->sessions[idx]; >> >> I've used this a lot over the years, but if it gives folks heartburn >> I can allocate memory in multiple stages, etc. > I'm fine with either way as long as we do the same, what does others > think about this? > >> Cheers, >> Anders >> >>> +} odp_crypto_global_t; >>> + >>> +static odp_crypto_global_t *global; >>> + >>> +/* >>> + * TODO: This is a serious hack to allow us to use packet buffer to convey >>> + * crypto operation results by placing them at the very end of the >>> + * packet buffer. >>> + */ >>> +static >>> +struct odp_operation_result_s *get_op_result_from_buffer(odp_buffer_t buf) >>> +{ >>> + uint8_t *temp; >>> + struct odp_operation_result_s *result; >>> + >>> + temp = odp_buffer_addr(buf); >>> + temp += odp_buffer_size(buf); >>> + temp -= sizeof(*result); >>> + result = (struct odp_operation_result_s *)(void *)temp; >>> + return result; >>> +} >>> + >>> +static >>> +struct odp_crypto_session_s *alloc_session(void) >>> +{ >>> + uint32_t idx; >>> + struct odp_crypto_session_s *session = NULL; >>> + >>> + idx = odp_atomic_fetch_inc_u32(&global->next); >>> + if (idx < global->max) { >>> + session = &global->sessions[idx]; >>> + session->index = idx; >>> + } >>> + return session; >>> +} >>> + >>> +static >>> +enum crypto_alg_err null_crypto_routine( >>> + struct odp_crypto_op_params *params ODP_UNUSED, >>> + struct odp_crypto_session_s *session ODP_UNUSED) >>> +{ >>> + return ODP_CRYPTO_ALG_ERR_NONE; >>> +} >>> + >>> +static >>> +enum crypto_alg_err md5_gen(struct odp_crypto_op_params *params, >>> + struct odp_crypto_session_s *session) >>> +{ >>> + uint8_t *data = odp_packet_buf_addr(params->pkt); >>> + uint8_t *icv = data; >>> + uint32_t len = params->auth_range.length; >>> + uint8_t hash[EVP_MAX_MD_SIZE]; >>> + uint32_t hlen = 12; >>> + >>> + /* Adjust pointer for beginning of area to auth */ >>> + data += params->auth_range.offset; >>> + icv += params->hash_result_offset; >>> + >>> + /* Hash it */ >>> + HMAC(EVP_md5(), session->auth.data.md5.key, 16, data, len, hash, &hlen); >>> + >>> + /* Copy to the output location */ >>> + memcpy(icv, hash, 12); >>> + >>> + return ODP_CRYPTO_ALG_ERR_NONE; >>> +} >>> + >>> + >>> +static >>> +enum crypto_alg_err md5_check(struct odp_crypto_op_params *params, >>> + struct odp_crypto_session_s *session) >>> +{ >>> + uint8_t *data = odp_packet_buf_addr(params->pkt); >>> + uint8_t *icv = data; >>> + uint32_t len = params->auth_range.length; >>> + uint8_t hash[EVP_MAX_MD_SIZE]; >>> + uint32_t hlen = 12; >>> + >>> + /* Adjust pointer for beginning of area to auth */ >>> + data += params->auth_range.offset; >>> + icv += params->hash_result_offset; >>> + >>> + /* Copy current value out and clear it before authentication */ >>> + memcpy(hash, icv, hlen); >>> + memset(icv, 0, hlen); >>> + >>> + /* Hash it */ >>> + HMAC(EVP_md5(), session->auth.data.md5.key, 16, data, len, icv, &hlen); >>> + >>> + /* Verify match */ >>> + if (0 != memcmp(icv, hash, 12)) >>> + return ODP_CRYPTO_ALG_ERR_ICV_CHECK; >>> + >>> + /* Matched */ >>> + return ODP_CRYPTO_ALG_ERR_NONE; >>> +} >>> + >>> +static >>> +enum crypto_alg_err des_encrypt(struct odp_crypto_op_params *params, >>> + struct odp_crypto_session_s *session) >>> +{ >>> + uint8_t *data = odp_packet_buf_addr(params->pkt); >>> + uint32_t len = params->cipher_range.length; >>> + DES_cblock *iv = (DES_cblock *)session->cipher.iv.data; >>> + >>> + /* Adjust pointer for beginning of area to cipher */ >>> + data += params->cipher_range.offset; >>> + >>> + /* Override IV if requested */ >>> + if (params->override_iv_ptr) >>> + iv = (DES_cblock *)params->override_iv_ptr; >>> + >>> + /* Encrypt it */ >>> + DES_ede3_cbc_encrypt(data, >>> + data, >>> + len, >>> + &session->cipher.data.des.ks1, >>> + &session->cipher.data.des.ks2, >>> + &session->cipher.data.des.ks3, >>> + iv, >>> + 1); >>> + >>> + return ODP_CRYPTO_ALG_ERR_NONE; >>> +} >>> + >>> +static >>> +enum crypto_alg_err des_decrypt(struct odp_crypto_op_params *params, >>> + struct odp_crypto_session_s *session) >>> +{ >>> + uint8_t *data = odp_packet_buf_addr(params->pkt); >>> + uint32_t len = params->cipher_range.length; >>> + DES_cblock *iv = (DES_cblock *)session->cipher.iv.data; >>> + >>> + /* Adjust pointer for beginning of area to cipher */ >>> + data += params->cipher_range.offset; >>> + >>> + /* Override IV if requested */ >>> + if (params->override_iv_ptr) >>> + iv = (DES_cblock *)params->override_iv_ptr; >>> + >>> + /* Decrypt it */ >>> + DES_ede3_cbc_encrypt(data, >>> + data, >>> + len, >>> + &session->cipher.data.des.ks1, >>> + &session->cipher.data.des.ks2, >>> + &session->cipher.data.des.ks3, >>> + iv, >>> + 0); >>> + >>> + return ODP_CRYPTO_ALG_ERR_NONE; >>> +} >>> + >>> +static >>> +int process_des_params(struct odp_crypto_session_s *session, >>> + struct odp_crypto_session_params *params) >>> +{ >>> + /* Verify IV len is either 0 or 8 */ >>> + if (!((0 == params->iv_len) || (8 == params->iv_len))) >>> + return -1; >>> + >>> + /* Verify IV pointer */ >>> + if (params->iv_len && !params->iv) >>> + return -1; >>> + >>> + /* Set function */ >>> + if (ODP_CRYPTO_OP_ENCODE == params->op) >>> + session->cipher.func = des_encrypt; >>> + else >>> + session->cipher.func = des_decrypt; >>> + >>> + /* Convert keys */ >>> + DES_set_key(¶ms->cipher_key->des.k1, &session->cipher.data.des.ks1); >>> + DES_set_key(¶ms->cipher_key->des.k2, &session->cipher.data.des.ks2); >>> + DES_set_key(¶ms->cipher_key->des.k3, &session->cipher.data.des.ks3); >>> + >>> + return 0; >>> +} >>> + >>> +static >>> +int process_md5_params(struct odp_crypto_session_s *session, >>> + struct odp_crypto_session_params *params) >>> +{ >>> + /* Set function */ >>> + if (ODP_CRYPTO_OP_ENCODE == params->op) >>> + session->auth.func = md5_gen; >>> + else >>> + session->auth.func = md5_check; >>> + >>> + /* Convert keys */ >>> + memcpy(session->auth.data.md5.key, params->auth_key->md5.key, 16); >>> + >>> + return 0; >>> +} >>> + >>> +odp_crypto_rc_e >>> +odp_crypto_session_create(struct odp_crypto_session_params *params, >>> + odp_buffer_t completion_event, >>> + odp_queue_t completion_queue) >>> +{ >>> + int rc; >>> + struct odp_crypto_session_s *session; >>> + struct odp_session_result_s *result = odp_buffer_addr(completion_event); >>> + >>> + /* Default to failure result */ >>> + result->rc = ODP_CRYPTO_SES_CREATE_NONE; >>> + result->session = ODP_CRYPTO_SESSION_INVALID; >>> + >>> + /* Allocate memory for this session */ >>> + session = alloc_session(); >>> + if (NULL == session) >>> + return ODP_CRYPTO_OP_ERROR; >>> + >>> + /* Copy stuff over */ >>> + session->op = params->op; >>> + session->comb = params->comb; >>> + session->compl_queue = params->compl_queue; >>> + session->cipher.alg = params->cipher_alg; >>> + session->cipher.iv.data = params->iv; >>> + session->cipher.iv.len = params->iv_len; >>> + session->auth.alg = params->auth_alg; >>> + >>> + /* Process based on cipher */ >>> + switch (params->cipher_alg) { >>> + case ODP_CIPHER_ALG_NULL: >>> + session->cipher.func = null_crypto_routine; >>> + rc = 0; >>> + break; >>> + case ODP_CIPHER_ALG_DES: >>> + case ODP_CIPHER_ALG_3DES_CBC: >>> + rc = process_des_params(session, params); >>> + break; >>> + default: >>> + rc = -1; >>> + } >>> + >>> + /* Check result */ >>> + if (rc) >>> + return ODP_CRYPTO_OP_ERROR; >>> + >>> + /* Process based on auth */ >>> + switch (params->auth_alg) { >>> + case ODP_AUTH_ALG_NULL: >>> + session->auth.func = null_crypto_routine; >>> + rc = 0; >>> + break; >>> + case ODP_AUTH_ALG_MD5_96: >>> + rc = process_md5_params(session, params); >>> + break; >>> + default: >>> + rc = -1; >>> + } >>> + >>> + /* Check result */ >>> + if (rc) >>> + return ODP_CRYPTO_OP_ERROR; >>> + >>> + /* We're happy */ >>> + result->rc = ODP_CRYPTO_SES_CREATE_OK; >>> + result->session = (intptr_t)session; >>> + >>> + /* If there is a queue post else we're good */ >>> + if (ODP_QUEUE_INVALID != completion_queue) { >>> + odp_queue_enq(completion_queue, completion_event); >>> + return ODP_CRYPTO_OP_POSTED; >>> + } >>> + >>> + return ODP_CRYPTO_OP_OK; >>> +} >>> + >>> + >>> +odp_crypto_rc_e >>> +odp_crypto_operation(struct odp_crypto_op_params *params, >>> + odp_buffer_t completion_event) >>> +{ >>> + enum crypto_alg_err rc_cipher = ODP_CRYPTO_ALG_ERR_NONE; >>> + enum crypto_alg_err rc_auth = ODP_CRYPTO_ALG_ERR_NONE; >>> + struct odp_crypto_session_s *session; >>> + struct odp_operation_result_s *result; >>> + >>> + session = (struct odp_crypto_session_s *)(intptr_t)params->session; >>> + >>> + /* >>> + * robking: need to understand infrastructure for scattered packets >>> + * for now just don't support them >>> + */ >>> + if (odp_buffer_is_scatter(odp_buffer_from_packet(params->pkt))) >>> + return ODP_CRYPTO_OP_ERROR; >>> + >>> + /* >>> + * robking: for now we are only going to support in place >>> + */ >>> + if (params->pkt != params->out_pkt) >>> + return ODP_CRYPTO_OP_ERROR; >>> + >>> + /* Invoke the functions */ >>> + switch (session->comb) { >>> + case ODP_CRYPTO_CIPHER_ONLY: >>> + rc_cipher = session->cipher.func(params, session); >>> + break; >>> + case ODP_CRYPTO_AUTH_ONLY: >>> + rc_auth = session->auth.func(params, session); >>> + break; >>> + case ODP_CRYPTO_AUTH_CIPHERTEXT: >>> + if (ODP_CRYPTO_OP_ENCODE == session->op) { >>> + rc_cipher = session->cipher.func(params, session); >>> + rc_auth = session->auth.func(params, session); >>> + } else { >>> + rc_auth = session->auth.func(params, session); >>> + rc_cipher = session->cipher.func(params, session); >>> + } >>> + break; >>> + } >>> + >>> + /* Build Result (no HW so no errors) */ >>> + result = get_op_result_from_buffer(completion_event); >>> + result->magic = OP_RESULT_MAGIC; >>> + result->cipher.alg.cipher = session->cipher.alg; >>> + result->cipher.alg_err = rc_cipher; >>> + result->cipher.hw_err = ODP_CRYPTO_HW_ERR_NONE; >>> + result->auth.alg.auth = session->auth.alg; >>> + result->auth.alg_err = rc_auth; >>> + result->auth.hw_err = ODP_CRYPTO_HW_ERR_NONE; >>> + >>> + /* >>> + * robking: a) the queue is supposed to come from session >>> + * b) ordering question asks whether we must >>> + * use the packet to return status >>> + */ >>> + if (ODP_QUEUE_INVALID != session->compl_queue) { >>> + odp_queue_enq(session->compl_queue, completion_event); >>> + return ODP_CRYPTO_OP_POSTED; >>> + } >>> + >>> + return ODP_CRYPTO_OP_OK; >>> +} >>> + >>> + >>> +int >>> +odp_crypto_init(uint32_t max_sessions) >>> +{ >>> + size_t mem_size; >>> + >>> + /* Force down to our limit */ >>> + if (MAX_SESSIONS < max_sessions) >>> + max_sessions = MAX_SESSIONS; >>> + >>> + /* Calculate the memory size we need */ >>> + mem_size = sizeof(*global); >>> + mem_size += (max_sessions * sizeof(struct odp_crypto_session_s)); >>> + >>> + /* Allocate our globally shared memory */ >>> + global = odp_shm_reserve("crypto_pool", mem_size, ODP_CACHE_LINE_SIZE); >>> + >>> + /* Clear it out */ >>> + memset(global, 0, mem_size); >>> + >>> + /* Initialize it */ >>> + global->max = max_sessions; >>> + >>> + return 0; >>> +} >>> + >>> +int >>> +odp_hw_random_get(uint8_t *buf, size_t *len, bool use_entropy ODP_UNUSED) >>> +{ >>> + int rc; >>> + rc = RAND_bytes(buf, *len); >>> + return ((1 == rc) ? 0 : -1); >>> +} >>> + >>> +void >>> +odp_crypto_get_operation_compl_status(odp_buffer_t completion_event, >>> + struct odp_crypto_compl_status *auth, >>> + struct odp_crypto_compl_status *cipher) >>> +{ >>> + struct odp_operation_result_s *result; >>> + >>> + result = get_op_result_from_buffer(completion_event); >>> + >>> + if (OP_RESULT_MAGIC != result->magic) >>> + abort(); >>> + >>> + memcpy(auth, &result->auth, sizeof(*auth)); >>> + memcpy(cipher, &result->cipher, sizeof(*cipher)); >>> +} >>> + >>> +void >>> +odp_crypto_get_ses_create_compl_status(odp_buffer_t completion_event, >>> + enum odp_crypto_ses_create_err *status) >>> +{ >>> + struct odp_session_result_s *result; >>> + >>> + result = odp_buffer_addr(completion_event); >>> + *status = result->rc; >>> +} >>> + >>> +void >>> +odp_crypto_get_ses_create_compl_session(odp_buffer_t completion_event, >>> + odp_crypto_session_t *session) >>> +{ >>> + struct odp_session_result_s *result; >>> + >>> + result = odp_buffer_addr(completion_event); >>> + *session = result->session; >>> +} >>> -- >>> 1.9.2 >>> >>> >>> _______________________________________________ >>> lng-odp mailing list >>> lng-odp@lists.linaro.org >>> http://lists.linaro.org/mailman/listinfo/lng-odp > _______________________________________________ > lng-odp mailing list > lng-odp@lists.linaro.org > http://lists.linaro.org/mailman/listinfo/lng-odp
On the call today we raised some points we should address at the sprint. 1. The linux-generic implementation is bleeding into the API definition, we should find a way to address this for crypto which also exhibits the problem and then retro fix other occurrences. 2. There was also a point that the implementation is not following the definition in the crypto Arch doc, we need to update the arch doc or the implementation. I think it would be better to fix both of these before accepting this patch, given that next week face to face we can solve issue 1 which will then apply to all of ODP and point 2 may well get sucked up into other changes anyway. On 4 June 2014 12:30, Maxim Uvarov <maxim.uvarov@linaro.org> wrote: > Hello Robbie, > > are you working on new version of this patch or this patch is good to > merge now? Also the same question about example app patch. > > Thank you, > Maxim. > > > On 06/04/2014 04:51 PM, Anders Roxell wrote: > >> Sorry for the late reply! >> See inline. >> >> On 2014-06-02 18:28, Robbie King (robking) wrote: >> >>> Hi Anders, thanks for having a look. See [RK] inline. >>> >>> -----Original Message----- >>> From: Anders Roxell [mailto:anders.roxell@linaro.org] >>> Sent: Monday, June 02, 2014 2:02 PM >>> To: Robbie King (robking) >>> Cc: lng-odp@lists.linaro.org >>> Subject: Re: [lng-odp] [PATCHv2] linux-generic crypto >>> >>> I'm sorry for my ignorance in crypto, however it looks like we can use >>> generic length variables, but instead we limit them to uint8_t, 16 or 32. >>> I didn't comment them all but there are multiple cases of them. >>> Please see inline comments. >>> >>> >>> On 2014-05-30 13:15, Robbie King wrote: >>> >>>> Initial linux-generic crypto implementation. Note that these changes >>>> require installing and linking with libssl-dev. The camel case warnings >>>> are also due to calls into libssl-dev. >>>> >>>> v1..v2: Fixed ARM compile warning, and modified "odp_hw_random_get" to >>>> take length as "size_t" instead of "uint32_t". >>>> >>>> Signed-off-by: Robbie King <robking@cisco.com> >>>> --- >>>> include/odp_crypto.h | 295 >>>> ++++++++++++++ >>>> platform/linux-generic/Makefile | 1 + >>>> .../linux-generic/include/odp_crypto_internal.h | 86 ++++ >>>> platform/linux-generic/source/odp_crypto.c | 435 >>>> +++++++++++++++++++++ >>>> 4 files changed, 817 insertions(+) >>>> create mode 100644 include/odp_crypto.h >>>> create mode 100644 platform/linux-generic/ >>>> include/odp_crypto_internal.h >>>> create mode 100644 platform/linux-generic/source/odp_crypto.c >>>> >>>> diff --git a/include/odp_crypto.h b/include/odp_crypto.h >>>> new file mode 100644 >>>> index 0000000..725e8f1 >>>> --- /dev/null >>>> +++ b/include/odp_crypto.h >>>> @@ -0,0 +1,295 @@ >>>> +/* Copyright (c) 2013, Linaro Limited >>>> >>> Nit: date 2014 >>> Should be like this for all the files. >>> >>> [RK] good catch. >>> >>> + * All rights reserved. >>>> + * >>>> + * SPDX-License-Identifier: BSD-3-Clause >>>> + */ >>>> + >>>> + >>>> +/** >>>> + * @file >>>> + * >>>> + * ODP crypto >>>> + */ >>>> + >>>> +#ifndef ODP_CRYPTO_H_ >>>> +#define ODP_CRYPTO_H_ >>>> + >>>> +#ifdef __cplusplus >>>> +extern "C" { >>>> +#endif >>>> + >>>> +#include <odp_std_types.h> >>>> +#include <odp_buffer.h> >>>> +#include <odp_buffer_pool.h> >>>> +#include <odp_queue.h> >>>> +#include <odp_packet.h> >>>> + >>>> +/** Invalid session handle */ >>>> +#define ODP_CRYPTO_SESSION_INVALID (-1ULL) >>>> + >>>> +/** >>>> + * Crypto API opaque session handle >>>> + */ >>>> +typedef uint64_t odp_crypto_session_t; >>>> + >>>> +/** >>>> + * Crypto API operation mode >>>> + */ >>>> +enum odp_crypto_op_mode { >>>> + ODP_CRYPTO_SYNC, /**< Synchronous, return results >>>> immediately */ >>>> + ODP_CRYPTO_ASYNC, /**< Aynchronous, return results via posted >>>> event */ >>>> +}; >>>> + >>>> +/** >>>> + * Crypto API operation type >>>> + */ >>>> +enum odp_crypto_op { >>>> + ODP_CRYPTO_OP_ENCODE, /**< Encrypt and/or compute >>>> authentication ICV */ >>>> + ODP_CRYPTO_OP_DECODE /**< Decrypt and/or verify authentication >>>> ICV */ >>>> +}; >>>> + >>>> +/** >>>> + * Crypto API cipher algorithm >>>> + */ >>>> +enum odp_cipher_alg { >>>> + ODP_CIPHER_ALG_NULL, /**< No cipher algorithm specified */ >>>> + ODP_CIPHER_ALG_DES, /**< DES */ >>>> + ODP_CIPHER_ALG_3DES_CBC, /**< Triple DES with cipher block >>>> chaining */ >>>> +}; >>>> + >>>> +/** >>>> + * Crypto API authentication algorithm >>>> + */ >>>> +enum odp_auth_alg { >>>> + ODP_AUTH_ALG_NULL, /**< No authentication algorithm specified >>>> */ >>>> + ODP_AUTH_ALG_MD5_96, /**< HMAC-MD5 with 96 bit key */ >>>> +}; >>>> + >>>> +/** >>>> + * Crypto API operation order >>>> + */ >>>> +enum odp_crypto_combination { >>>> + ODP_CRYPTO_CIPHER_ONLY, /**< Only perform cipher */ >>>> + ODP_CRYPTO_AUTH_ONLY, /**< Only perform authentication */ >>>> + ODP_CRYPTO_AUTH_CIPHERTEXT /**< Cipher then authentication on >>>> encode */ >>>> +}; >>>> + >>>> +/** >>>> + * Crypto API key >>>> + */ >>>> +typedef struct odp_key_s { >>>> + union { >>>> + /** DES/3DES key definition (set all same for DES) */ >>>> + struct { >>>> + uint8_t k1[8]; /**< First key */ >>>> + uint8_t k2[8]; /**< Second key */ >>>> + uint8_t k3[8]; /**< Third key */ >>>> + } des; >>>> + /** MD5 key */ >>>> + struct { >>>> + uint8_t key[16]; /**< Key up to 128 bits */ >>>> + } md5; >>>> + }; >>>> +} odp_key_t; >>>> + >>>> +/** >>>> + * Crypto API data range specifier >>>> + */ >>>> +struct odp_data_range { >>>> + uint16_t offset; /**< Offset from beginning of buffer (chain) >>>> */ >>>> + uint16_t length; /**< Length of data to operate on */ >>>> >>> Why do we use uint16_t? >>> Can't the data be bigger than this? >>> >>> [RK] this came from the ODP crypto spec, I think in general the idea >>> is packets will never be larger than an Ethernet jumbo (roughly 10K) >>> so no need to support larger than 64K. >>> >> So was that a reason for choosing uint16_t in the spec? >> Or shall we change the spec? >> >> +}; >>>> + >>>> +/** >>>> + * Crypto API session creation paramters >>>> + * >>>> + * TODO: add "odp_session_proc_info_t" >>>> + */ >>>> +struct odp_crypto_session_params { >>>> + enum odp_crypto_op op; /**< Encode versus decode */ >>>> + enum odp_crypto_combination comb; /**< Operation order */ >>>> + enum odp_crypto_op_mode pref_mode; /**< Preferred sync vs async >>>> */ >>>> + enum odp_cipher_alg cipher_alg; /**< Cipher algorithm */ >>>> + odp_key_t *cipher_key; /**< Cipher key */ >>>> + uint8_t *iv; /**< Cipher Initialization >>>> Vector (IV) */ >>>> >>> Why do we use uint8_t and not uint_t? >>> >>> [RK] This one also comes from the spec, my understanding is for crypto >>> the IV is always an opaque byte string that can be different lengths >>> for different ciphers. >>> >> OK, thanks. >> >> + size_t iv_len; /**< Cipher IV length */ >>>> + enum odp_auth_alg auth_alg; /**< Authentication algorithm */ >>>> + odp_key_t *auth_key; /**< Authentication key */ >>>> + odp_queue_t compl_queue; /**< Async mode completion event >>>> queue */ >>>> +}; >>>> + >>>> +/** >>>> + * Crypto API per packet operation parameters >>>> + */ >>>> +struct odp_crypto_op_params { >>>> + odp_crypto_session_t session; /**< Session handle from >>>> creation */ >>>> + odp_packet_t pkt; /**< Input packet buffer */ >>>> + odp_packet_t out_pkt; /**< Output packet buffer >>>> (optional) */ >>>> + uint8_t *override_iv_ptr; /**< Override session IV >>>> pointer */ >>>> + unsigned hash_result_offset; /**< Offset from start of >>>> packet buffer for hash result */ >>>> + struct odp_data_range cipher_range; /**< Data range to apply >>>> cipher */ >>>> + struct odp_data_range auth_range; /**< Data range to >>>> authenticate */ >>>> +}; >>>> + >>>> +/** >>>> + * Crypto API session creation return code >>>> + * >>>> + * TODO: seems confusing, maybe _rc instead >>>> + */ >>>> +enum odp_crypto_ses_create_err { >>>> + ODP_CRYPTO_SES_CREATE_NONE, /**< No session created? need to >>>> clarify */ >>>> + ODP_CRYPTO_SES_CREATE_OK, /**< Session created successfully >>>> */ >>>> +}; >>>> + >>>> +/** >>>> + * Crypto API algorithm return code >>>> + */ >>>> +enum crypto_alg_err { >>>> + ODP_CRYPTO_ALG_ERR_NONE, /**< Algorithm successful */ >>>> + ODP_CRYPTO_ALG_ERR_DATA_SIZE, /**< Invalid data block size */ >>>> + ODP_CRYPTO_ALG_ERR_KEY_SIZE, /**< Key size invalid for >>>> algorithm */ >>>> + ODP_CRYPTO_ALG_ERR_ICV_CHECK, /**< Computed ICV value mismatch >>>> */ >>>> +}; >>>> + >>>> +/** >>>> + * Crypto API operation return code >>>> + */ >>>> +typedef enum odp_crypto_rc { >>>> + ODP_CRYPTO_OP_OK, /**< Operation completed, results are >>>> valid */ >>>> + ODP_CRYPTO_OP_POSTED, /**< Operation was posted, results >>>> delivered via completion queue */ >>>> + ODP_CRYPTO_OP_ERROR, /**< Operation failed */ >>>> +} odp_crypto_rc_e; >>>> + >>>> +/** >>>> + * Crypto API hardware centric return code >>>> + */ >>>> +enum crypto_hw_err { >>>> + ODP_CRYPTO_HW_ERR_NONE, /**< Operation completed >>>> successfully */ >>>> + ODP_CRYPTO_HW_ERR_DMA, /**< Error detected during DMA >>>> of data */ >>>> + ODP_CRYPTO_HW_ERR_BP_DEPLETED, /**< Operation failed due to >>>> buffer pool depletion */ >>>> +}; >>>> + >>>> +/** >>>> + * Crypto API algorithm (cipher or authentication) >>>> + */ >>>> +typedef union odp_crypto_alg_u { >>>> + enum odp_cipher_alg cipher; /**< Cipher algorithm */ >>>> + enum odp_auth_alg auth; /**< Authentication algorithm */ >>>> +} odp_crypto_alg_t; >>>> + >>>> +/** >>>> + * Cryto API per packet operation completion status >>>> + */ >>>> +struct odp_crypto_compl_status { >>>> + odp_crypto_alg_t alg; /**< Requested algorithm */ >>>> + enum crypto_alg_err alg_err; /**< Algorithm specific return >>>> code */ >>>> + enum crypto_hw_err hw_err; /**< Hardware specific return >>>> code */ >>>> +}; >>>> + >>>> >>> Is there a good reason why we are mixing enum name with typedef? >>> >>> [RK] I agree, I would like to see us perhaps agree on typedef'ing >>> enums. This one is a little kludgy anyway, I would vote for removing >>> the ALG from the completion status since it is simply a mirror of the >>> ALG (cipher or auth) which was specified during session creation. >>> >> I agree with you. >> >> + >>>> +/** >>>> + * Crypto session creation >>>> + * >>>> + * Create a crypto session. Operation occurs asynchronously if a >>>> completion >>>> + * queue is specified else synchronously. >>>> + * >>>> + * @param params Session parameters >>>> + * @param completion_event Event by which the session creation >>>> results are >>>> + * delivered. >>>> + * @param completion_queue Queue by which the completion event will be >>>> + * delivered. Ignored if ODP_QUEUE_INVALID. >>>> + * >>>> + * @return Operation return code indicating success or failure for >>>> + * when synchronous operation requested, else POSTED when >>>> + * asynchronous operation is requested. >>>> + */ >>>> +odp_crypto_rc_e >>>> +odp_crypto_session_create(struct odp_crypto_session_params *params, >>>> + odp_buffer_t completion_event, >>>> + odp_queue_t completion_queue); >>>> + >>>> + >>>> +/** >>>> + * Crypto session creation completion status >>>> + * >>>> + * Accessor function for obtaining creation status from the completion >>>> event. >>>> + * >>>> + * @param completion_event Event containing operation results >>>> + * @param status Pointer to store creation return code >>>> + */ >>>> +void >>>> +odp_crypto_get_ses_create_compl_status(odp_buffer_t completion_event, >>>> + enum odp_crypto_ses_create_err >>>> *status); >>>> + >>>> +/** >>>> + * Crypto session creation completion return value >>>> + * >>>> + * Accessor function for obtaining handle for newly created session. >>>> + * >>>> + * @param completion_event Event containing operation results >>>> + * @param session Pointer to store session handle >>>> + */ >>>> +void >>>> +odp_crypto_get_ses_create_compl_session(odp_buffer_t completion_event, >>>> + odp_crypto_session_t *session); >>>> + >>>> +/** >>>> + * Crypto per packet operation >>>> + * >>>> + * Performs the cryptographic operations specified during session >>>> creation >>>> + * on the packet. >>>> + * >>>> + * @param params Operation parameters >>>> + * @param completion_event Event by which the session creation >>>> results are >>>> + * delivered. >>>> + * >>>> + * @return Operation return code indicating success or failure when >>>> session >>>> + * indicates synchronous operation, else POSTED for >>>> asynchronous >>>> + * operation. >>>> + */ >>>> +odp_crypto_rc_e >>>> +odp_crypto_operation(struct odp_crypto_op_params *params, >>>> + odp_buffer_t completion_event); >>>> + >>>> + >>>> +/** >>>> + * Crypto per packet operation completion status >>>> + * >>>> + * Accessor function for obtaining operation status from the >>>> completion event. >>>> + * >>>> + * @param completion_event Event containing operation results >>>> + * @param auth Pointer to store authentication results >>>> + * @param cipher Pointer to store cipher results >>>> + */ >>>> +void >>>> +odp_crypto_get_operation_compl_status(odp_buffer_t completion_event, >>>> + struct odp_crypto_compl_status >>>> *auth, >>>> + struct odp_crypto_compl_status >>>> *cipher); >>>> + >>>> +/** >>>> + * Generate random byte string >>>> + * >>>> + * @param buf Pointer to store result >>>> + * @param len Pointer to input length value as well as return >>>> value >>>> + * @param use_entropy (TODO: needs description) >>>> + * >>>> + * @return 0 if succesful >>>> + */ >>>> +int >>>> +odp_hw_random_get(uint8_t *buf, size_t *len, bool use_entropy); >>>> + >>>> +/** >>>> + * Initialize the crypto subsystem, called once from main thread >>>> + * >>>> + * @param max_sessions Maximum number of sessions to support >>>> + * >>>> + * @return 0 if succesful >>>> + */ >>>> +int >>>> +odp_crypto_init(uint32_t max_sessions); >>>> + >>>> +#ifdef __cplusplus >>>> +} >>>> +#endif >>>> + >>>> +#endif >>>> diff --git a/platform/linux-generic/Makefile b/platform/linux-generic/ >>>> Makefile >>>> index ec5d4a7..57f6d18 100644 >>>> --- a/platform/linux-generic/Makefile >>>> +++ b/platform/linux-generic/Makefile >>>> @@ -72,6 +72,7 @@ OBJS += $(OBJ_DIR)/odp_time.o >>>> OBJS += $(OBJ_DIR)/odp_timer.o >>>> OBJS += $(OBJ_DIR)/odp_ring.o >>>> OBJS += $(OBJ_DIR)/odp_rwlock.o >>>> +OBJS += $(OBJ_DIR)/odp_crypto.o >>>> ifeq ($(ODP_HAVE_NETMAP),yes) >>>> OBJS += $(OBJ_DIR)/odp_packet_netmap.o >>>> endif >>>> diff --git a/platform/linux-generic/include/odp_crypto_internal.h >>>> b/platform/linux-generic/include/odp_crypto_internal.h >>>> new file mode 100644 >>>> index 0000000..e558864 >>>> --- /dev/null >>>> +++ b/platform/linux-generic/include/odp_crypto_internal.h >>>> @@ -0,0 +1,86 @@ >>>> +/* Copyright (c) 2013, Linaro Limited >>>> + * All rights reserved. >>>> + * >>>> + * SPDX-License-Identifier: BSD-3-Clause >>>> + */ >>>> + >>>> + >>>> + >>>> +#ifndef ODP_CRYPTO_INTERNAL_H_ >>>> +#define ODP_CRYPTO_INTERNAL_H_ >>>> + >>>> +#ifdef __cplusplus >>>> +extern "C" { >>>> +#endif >>>> + >>>> +#include <openssl/des.h> >>>> + >>>> +#define OP_RESULT_MAGIC 0x91919191 >>>> + >>>> +/** Forward declaration of session structure */ >>>> +struct odp_crypto_session_s; >>>> + >>>> +/** >>>> + * Algorithm handler function prototype >>>> + */ >>>> +typedef >>>> +enum crypto_alg_err (*crypto_func_t)(struct odp_crypto_op_params >>>> *params, >>>> + struct odp_crypto_session_s >>>> *session); >>>> + >>>> +/** >>>> + * Per crypto session data structure >>>> + */ >>>> +struct odp_crypto_session_s { >>>> + uint32_t index; >>>> >>> Why not uint_t? >>> >>> [RK] OK by me, since this data structure is internal to the >>> linux-generic implementation, and the plan is to support a >>> very small number of sessions this could be made as small as >>> uint8_t I think. >>> >> OK, I'm fine with that. >> >> + enum odp_crypto_op op; >>>> + enum odp_crypto_combination comb; >>>> + odp_queue_t compl_queue; >>>> + struct { >>>> + enum odp_cipher_alg alg; >>>> + struct { >>>> + uint8_t *data; >>>> >>> Why not uint_t? >>> >>> [RK] Same reason as above for IV since it is byte data. >>> >> OK. >> >> + size_t len; >>>> + } iv; >>>> + union { >>>> + struct { >>>> + DES_key_schedule ks1; >>>> + DES_key_schedule ks2; >>>> + DES_key_schedule ks3; >>>> + } des; >>>> + } data; >>>> >>> Is there a good reason to have a union with only one member? >>> >>> [RK] The union is here with the intention that the linux-generic >>> implementation will add support for other ciphers (AES for example). >>> So the union is simply laying the ground work for expansion. >>> >> I understand, didn't think about that! >> Thank you for your explanation. >> >> + crypto_func_t func; >>>> + } cipher; >>>> + struct { >>>> + enum odp_auth_alg alg; >>>> + union { >>>> + struct { >>>> + uint8_t key[16]; >>>> >>> Why are we limiting the key to 16? >>> I guess we want to limit the key up to 128 bits right? >>> >>> [RK] 128 bit key is specific to MD5, other algorithems >>> would have their own entry in the union (again for future >>> expansion). >>> >> OK good. >> Maybe comment that it is for keys up to 128 bits? >> >> + } md5; >>>> + } data; >>>> >>> Is there a good reason to have a union with only one member? >>> >>> + crypto_func_t func; >>>> + } auth; >>>> + >>>> +}; >>>> + >>>> +/** >>>> + * Per packet operation result >>>> + */ >>>> +struct odp_operation_result_s { >>>> + uint32_t magic; >>>> + struct odp_crypto_compl_status cipher; >>>> + struct odp_crypto_compl_status auth; >>>> +}; >>>> + >>>> +/** >>>> + * Per session creation operation result >>>> + */ >>>> +struct odp_session_result_s { >>>> + enum odp_crypto_ses_create_err rc; >>>> + odp_crypto_session_t session; >>>> +}; >>>> + >>>> +#ifdef __cplusplus >>>> +} >>>> +#endif >>>> + >>>> +#endif >>>> diff --git a/platform/linux-generic/source/odp_crypto.c >>>> b/platform/linux-generic/source/odp_crypto.c >>>> new file mode 100644 >>>> index 0000000..4ae3645 >>>> --- /dev/null >>>> +++ b/platform/linux-generic/source/odp_crypto.c >>>> @@ -0,0 +1,435 @@ >>>> +/* Copyright (c) 2013, Linaro Limited >>>> + * All rights reserved. >>>> + * >>>> + * SPDX-License-Identifier: BSD-3-Clause >>>> + */ >>>> + >>>> +#include <odp_crypto.h> >>>> +#include <odp_internal.h> >>>> +#include <odp_atomic.h> >>>> +#include <odp_spinlock.h> >>>> +#include <odp_sync.h> >>>> +#include <odp_debug.h> >>>> +#include <odp_align.h> >>>> +#include <odp_shared_memory.h> >>>> +#include <odp_crypto_internal.h> >>>> + >>>> +#include <string.h> >>>> + >>>> +#include <openssl/des.h> >>>> +#include <openssl/rand.h> >>>> +#include <openssl/hmac.h> >>>> +#include <openssl/evp.h> >>>> + >>>> +#define MAX_SESSIONS 32 >>>> + >>>> +typedef struct { >>>> + odp_atomic_u32_t next; >>>> + uint32_t max; >>>> + struct odp_crypto_session_s sessions[0]; >>>> >>> Pointer instead? >>> >>> [RK] This is specifically for allocating memory where >>> the storage for sessions immediately follows the >>> structure, allowing an arbitrary number of sessions to >>> be supported at allocation time like shown yet accessed >>> as if they were statically allocated: >>> >>> /* Calculate the memory size we need */ >>> mem_size = sizeof(*global); >>> mem_size += (max_sessions * sizeof(struct odp_crypto_session_s)); >>> >>> and you can access individual sessions like this: >>> >>> session = &global->sessions[idx]; >>> >>> I've used this a lot over the years, but if it gives folks heartburn >>> I can allocate memory in multiple stages, etc. >>> >> I'm fine with either way as long as we do the same, what does others >> think about this? >> >> Cheers, >>> Anders >>> >>> +} odp_crypto_global_t; >>>> + >>>> +static odp_crypto_global_t *global; >>>> + >>>> +/* >>>> + * TODO: This is a serious hack to allow us to use packet buffer to >>>> convey >>>> + * crypto operation results by placing them at the very end of >>>> the >>>> + * packet buffer. >>>> + */ >>>> +static >>>> +struct odp_operation_result_s *get_op_result_from_buffer(odp_buffer_t >>>> buf) >>>> +{ >>>> + uint8_t *temp; >>>> + struct odp_operation_result_s *result; >>>> + >>>> + temp = odp_buffer_addr(buf); >>>> + temp += odp_buffer_size(buf); >>>> + temp -= sizeof(*result); >>>> + result = (struct odp_operation_result_s *)(void *)temp; >>>> + return result; >>>> +} >>>> + >>>> +static >>>> +struct odp_crypto_session_s *alloc_session(void) >>>> +{ >>>> + uint32_t idx; >>>> + struct odp_crypto_session_s *session = NULL; >>>> + >>>> + idx = odp_atomic_fetch_inc_u32(&global->next); >>>> + if (idx < global->max) { >>>> + session = &global->sessions[idx]; >>>> + session->index = idx; >>>> + } >>>> + return session; >>>> +} >>>> + >>>> +static >>>> +enum crypto_alg_err null_crypto_routine( >>>> + struct odp_crypto_op_params *params ODP_UNUSED, >>>> + struct odp_crypto_session_s *session ODP_UNUSED) >>>> +{ >>>> + return ODP_CRYPTO_ALG_ERR_NONE; >>>> +} >>>> + >>>> +static >>>> +enum crypto_alg_err md5_gen(struct odp_crypto_op_params *params, >>>> + struct odp_crypto_session_s *session) >>>> +{ >>>> + uint8_t *data = odp_packet_buf_addr(params->pkt); >>>> + uint8_t *icv = data; >>>> + uint32_t len = params->auth_range.length; >>>> + uint8_t hash[EVP_MAX_MD_SIZE]; >>>> + uint32_t hlen = 12; >>>> + >>>> + /* Adjust pointer for beginning of area to auth */ >>>> + data += params->auth_range.offset; >>>> + icv += params->hash_result_offset; >>>> + >>>> + /* Hash it */ >>>> + HMAC(EVP_md5(), session->auth.data.md5.key, 16, data, len, >>>> hash, &hlen); >>>> + >>>> + /* Copy to the output location */ >>>> + memcpy(icv, hash, 12); >>>> + >>>> + return ODP_CRYPTO_ALG_ERR_NONE; >>>> +} >>>> + >>>> + >>>> +static >>>> +enum crypto_alg_err md5_check(struct odp_crypto_op_params *params, >>>> + struct odp_crypto_session_s *session) >>>> +{ >>>> + uint8_t *data = odp_packet_buf_addr(params->pkt); >>>> + uint8_t *icv = data; >>>> + uint32_t len = params->auth_range.length; >>>> + uint8_t hash[EVP_MAX_MD_SIZE]; >>>> + uint32_t hlen = 12; >>>> + >>>> + /* Adjust pointer for beginning of area to auth */ >>>> + data += params->auth_range.offset; >>>> + icv += params->hash_result_offset; >>>> + >>>> + /* Copy current value out and clear it before authentication */ >>>> + memcpy(hash, icv, hlen); >>>> + memset(icv, 0, hlen); >>>> + >>>> + /* Hash it */ >>>> + HMAC(EVP_md5(), session->auth.data.md5.key, 16, data, len, icv, >>>> &hlen); >>>> + >>>> + /* Verify match */ >>>> + if (0 != memcmp(icv, hash, 12)) >>>> + return ODP_CRYPTO_ALG_ERR_ICV_CHECK; >>>> + >>>> + /* Matched */ >>>> + return ODP_CRYPTO_ALG_ERR_NONE; >>>> +} >>>> + >>>> +static >>>> +enum crypto_alg_err des_encrypt(struct odp_crypto_op_params *params, >>>> + struct odp_crypto_session_s *session) >>>> +{ >>>> + uint8_t *data = odp_packet_buf_addr(params->pkt); >>>> + uint32_t len = params->cipher_range.length; >>>> + DES_cblock *iv = (DES_cblock *)session->cipher.iv.data; >>>> + >>>> + /* Adjust pointer for beginning of area to cipher */ >>>> + data += params->cipher_range.offset; >>>> + >>>> + /* Override IV if requested */ >>>> + if (params->override_iv_ptr) >>>> + iv = (DES_cblock *)params->override_iv_ptr; >>>> + >>>> + /* Encrypt it */ >>>> + DES_ede3_cbc_encrypt(data, >>>> + data, >>>> + len, >>>> + &session->cipher.data.des.ks1, >>>> + &session->cipher.data.des.ks2, >>>> + &session->cipher.data.des.ks3, >>>> + iv, >>>> + 1); >>>> + >>>> + return ODP_CRYPTO_ALG_ERR_NONE; >>>> +} >>>> + >>>> +static >>>> +enum crypto_alg_err des_decrypt(struct odp_crypto_op_params *params, >>>> + struct odp_crypto_session_s *session) >>>> +{ >>>> + uint8_t *data = odp_packet_buf_addr(params->pkt); >>>> + uint32_t len = params->cipher_range.length; >>>> + DES_cblock *iv = (DES_cblock *)session->cipher.iv.data; >>>> + >>>> + /* Adjust pointer for beginning of area to cipher */ >>>> + data += params->cipher_range.offset; >>>> + >>>> + /* Override IV if requested */ >>>> + if (params->override_iv_ptr) >>>> + iv = (DES_cblock *)params->override_iv_ptr; >>>> + >>>> + /* Decrypt it */ >>>> + DES_ede3_cbc_encrypt(data, >>>> + data, >>>> + len, >>>> + &session->cipher.data.des.ks1, >>>> + &session->cipher.data.des.ks2, >>>> + &session->cipher.data.des.ks3, >>>> + iv, >>>> + 0); >>>> + >>>> + return ODP_CRYPTO_ALG_ERR_NONE; >>>> +} >>>> + >>>> +static >>>> +int process_des_params(struct odp_crypto_session_s *session, >>>> + struct odp_crypto_session_params *params) >>>> +{ >>>> + /* Verify IV len is either 0 or 8 */ >>>> + if (!((0 == params->iv_len) || (8 == params->iv_len))) >>>> + return -1; >>>> + >>>> + /* Verify IV pointer */ >>>> + if (params->iv_len && !params->iv) >>>> + return -1; >>>> + >>>> + /* Set function */ >>>> + if (ODP_CRYPTO_OP_ENCODE == params->op) >>>> + session->cipher.func = des_encrypt; >>>> + else >>>> + session->cipher.func = des_decrypt; >>>> + >>>> + /* Convert keys */ >>>> + DES_set_key(¶ms->cipher_key->des.k1, >>>> &session->cipher.data.des.ks1); >>>> + DES_set_key(¶ms->cipher_key->des.k2, >>>> &session->cipher.data.des.ks2); >>>> + DES_set_key(¶ms->cipher_key->des.k3, >>>> &session->cipher.data.des.ks3); >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static >>>> +int process_md5_params(struct odp_crypto_session_s *session, >>>> + struct odp_crypto_session_params *params) >>>> +{ >>>> + /* Set function */ >>>> + if (ODP_CRYPTO_OP_ENCODE == params->op) >>>> + session->auth.func = md5_gen; >>>> + else >>>> + session->auth.func = md5_check; >>>> + >>>> + /* Convert keys */ >>>> + memcpy(session->auth.data.md5.key, params->auth_key->md5.key, >>>> 16); >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +odp_crypto_rc_e >>>> +odp_crypto_session_create(struct odp_crypto_session_params *params, >>>> + odp_buffer_t completion_event, >>>> + odp_queue_t completion_queue) >>>> +{ >>>> + int rc; >>>> + struct odp_crypto_session_s *session; >>>> + struct odp_session_result_s *result = >>>> odp_buffer_addr(completion_event); >>>> + >>>> + /* Default to failure result */ >>>> + result->rc = ODP_CRYPTO_SES_CREATE_NONE; >>>> + result->session = ODP_CRYPTO_SESSION_INVALID; >>>> + >>>> + /* Allocate memory for this session */ >>>> + session = alloc_session(); >>>> + if (NULL == session) >>>> + return ODP_CRYPTO_OP_ERROR; >>>> + >>>> + /* Copy stuff over */ >>>> + session->op = params->op; >>>> + session->comb = params->comb; >>>> + session->compl_queue = params->compl_queue; >>>> + session->cipher.alg = params->cipher_alg; >>>> + session->cipher.iv.data = params->iv; >>>> + session->cipher.iv.len = params->iv_len; >>>> + session->auth.alg = params->auth_alg; >>>> + >>>> + /* Process based on cipher */ >>>> + switch (params->cipher_alg) { >>>> + case ODP_CIPHER_ALG_NULL: >>>> + session->cipher.func = null_crypto_routine; >>>> + rc = 0; >>>> + break; >>>> + case ODP_CIPHER_ALG_DES: >>>> + case ODP_CIPHER_ALG_3DES_CBC: >>>> + rc = process_des_params(session, params); >>>> + break; >>>> + default: >>>> + rc = -1; >>>> + } >>>> + >>>> + /* Check result */ >>>> + if (rc) >>>> + return ODP_CRYPTO_OP_ERROR; >>>> + >>>> + /* Process based on auth */ >>>> + switch (params->auth_alg) { >>>> + case ODP_AUTH_ALG_NULL: >>>> + session->auth.func = null_crypto_routine; >>>> + rc = 0; >>>> + break; >>>> + case ODP_AUTH_ALG_MD5_96: >>>> + rc = process_md5_params(session, params); >>>> + break; >>>> + default: >>>> + rc = -1; >>>> + } >>>> + >>>> + /* Check result */ >>>> + if (rc) >>>> + return ODP_CRYPTO_OP_ERROR; >>>> + >>>> + /* We're happy */ >>>> + result->rc = ODP_CRYPTO_SES_CREATE_OK; >>>> + result->session = (intptr_t)session; >>>> + >>>> + /* If there is a queue post else we're good */ >>>> + if (ODP_QUEUE_INVALID != completion_queue) { >>>> + odp_queue_enq(completion_queue, completion_event); >>>> + return ODP_CRYPTO_OP_POSTED; >>>> + } >>>> + >>>> + return ODP_CRYPTO_OP_OK; >>>> +} >>>> + >>>> + >>>> +odp_crypto_rc_e >>>> +odp_crypto_operation(struct odp_crypto_op_params *params, >>>> + odp_buffer_t completion_event) >>>> +{ >>>> + enum crypto_alg_err rc_cipher = ODP_CRYPTO_ALG_ERR_NONE; >>>> + enum crypto_alg_err rc_auth = ODP_CRYPTO_ALG_ERR_NONE; >>>> + struct odp_crypto_session_s *session; >>>> + struct odp_operation_result_s *result; >>>> + >>>> + session = (struct odp_crypto_session_s >>>> *)(intptr_t)params->session; >>>> + >>>> + /* >>>> + * robking: need to understand infrastructure for scattered >>>> packets >>>> + * for now just don't support them >>>> + */ >>>> + if (odp_buffer_is_scatter(odp_buffer_from_packet(params->pkt))) >>>> + return ODP_CRYPTO_OP_ERROR; >>>> + >>>> + /* >>>> + * robking: for now we are only going to support in place >>>> + */ >>>> + if (params->pkt != params->out_pkt) >>>> + return ODP_CRYPTO_OP_ERROR; >>>> + >>>> + /* Invoke the functions */ >>>> + switch (session->comb) { >>>> + case ODP_CRYPTO_CIPHER_ONLY: >>>> + rc_cipher = session->cipher.func(params, session); >>>> + break; >>>> + case ODP_CRYPTO_AUTH_ONLY: >>>> + rc_auth = session->auth.func(params, session); >>>> + break; >>>> + case ODP_CRYPTO_AUTH_CIPHERTEXT: >>>> + if (ODP_CRYPTO_OP_ENCODE == session->op) { >>>> + rc_cipher = session->cipher.func(params, >>>> session); >>>> + rc_auth = session->auth.func(params, session); >>>> + } else { >>>> + rc_auth = session->auth.func(params, session); >>>> + rc_cipher = session->cipher.func(params, >>>> session); >>>> + } >>>> + break; >>>> + } >>>> + >>>> + /* Build Result (no HW so no errors) */ >>>> + result = get_op_result_from_buffer(completion_event); >>>> + result->magic = OP_RESULT_MAGIC; >>>> + result->cipher.alg.cipher = session->cipher.alg; >>>> + result->cipher.alg_err = rc_cipher; >>>> + result->cipher.hw_err = ODP_CRYPTO_HW_ERR_NONE; >>>> + result->auth.alg.auth = session->auth.alg; >>>> + result->auth.alg_err = rc_auth; >>>> + result->auth.hw_err = ODP_CRYPTO_HW_ERR_NONE; >>>> + >>>> + /* >>>> + * robking: a) the queue is supposed to come from session >>>> + * b) ordering question asks whether we must >>>> + * use the packet to return status >>>> + */ >>>> + if (ODP_QUEUE_INVALID != session->compl_queue) { >>>> + odp_queue_enq(session->compl_queue, completion_event); >>>> + return ODP_CRYPTO_OP_POSTED; >>>> + } >>>> + >>>> + return ODP_CRYPTO_OP_OK; >>>> +} >>>> + >>>> + >>>> +int >>>> +odp_crypto_init(uint32_t max_sessions) >>>> +{ >>>> + size_t mem_size; >>>> + >>>> + /* Force down to our limit */ >>>> + if (MAX_SESSIONS < max_sessions) >>>> + max_sessions = MAX_SESSIONS; >>>> + >>>> + /* Calculate the memory size we need */ >>>> + mem_size = sizeof(*global); >>>> + mem_size += (max_sessions * sizeof(struct >>>> odp_crypto_session_s)); >>>> + >>>> + /* Allocate our globally shared memory */ >>>> + global = odp_shm_reserve("crypto_pool", mem_size, >>>> ODP_CACHE_LINE_SIZE); >>>> + >>>> + /* Clear it out */ >>>> + memset(global, 0, mem_size); >>>> + >>>> + /* Initialize it */ >>>> + global->max = max_sessions; >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +int >>>> +odp_hw_random_get(uint8_t *buf, size_t *len, bool use_entropy >>>> ODP_UNUSED) >>>> +{ >>>> + int rc; >>>> + rc = RAND_bytes(buf, *len); >>>> + return ((1 == rc) ? 0 : -1); >>>> +} >>>> + >>>> +void >>>> +odp_crypto_get_operation_compl_status(odp_buffer_t completion_event, >>>> + struct odp_crypto_compl_status >>>> *auth, >>>> + struct odp_crypto_compl_status >>>> *cipher) >>>> +{ >>>> + struct odp_operation_result_s *result; >>>> + >>>> + result = get_op_result_from_buffer(completion_event); >>>> + >>>> + if (OP_RESULT_MAGIC != result->magic) >>>> + abort(); >>>> + >>>> + memcpy(auth, &result->auth, sizeof(*auth)); >>>> + memcpy(cipher, &result->cipher, sizeof(*cipher)); >>>> +} >>>> + >>>> +void >>>> +odp_crypto_get_ses_create_compl_status(odp_buffer_t completion_event, >>>> + enum odp_crypto_ses_create_err >>>> *status) >>>> +{ >>>> + struct odp_session_result_s *result; >>>> + >>>> + result = odp_buffer_addr(completion_event); >>>> + *status = result->rc; >>>> +} >>>> + >>>> +void >>>> +odp_crypto_get_ses_create_compl_session(odp_buffer_t completion_event, >>>> + odp_crypto_session_t *session) >>>> +{ >>>> + struct odp_session_result_s *result; >>>> + >>>> + result = odp_buffer_addr(completion_event); >>>> + *session = result->session; >>>> +} >>>> -- >>>> 1.9.2 >>>> >>>> >>>> _______________________________________________ >>>> lng-odp mailing list >>>> lng-odp@lists.linaro.org >>>> http://lists.linaro.org/mailman/listinfo/lng-odp >>>> >>> _______________________________________________ >> lng-odp mailing list >> lng-odp@lists.linaro.org >> http://lists.linaro.org/mailman/listinfo/lng-odp >> > > > _______________________________________________ > lng-odp mailing list > lng-odp@lists.linaro.org > http://lists.linaro.org/mailman/listinfo/lng-odp >
Hi Robbie, Some comments from my side regarding odp_key_s structure /** * Crypto API key */ typedef struct odp_key_s { union { /** DES/3DES key definition (set all same for DES) */ struct { uint8_t k1[8]; /**< First key */ uint8_t k2[8]; /**< Second key */ uint8_t k3[8]; /**< Third key */ } des; /** MD5 key */ struct { uint8_t key[16]; /**< Key up to 128 bits */ } md5; }; } odp_key_t; odp_key_t should be a Handle with *typedef uint64_t* *or uint32_t* instead of being a pointer to a defined structure. The implementation of odp_key_t could be implementation specific and should be removed from the header file. Also on similar lines we will need access functions to set the key value for different ciphers using odp_key_t Handle. Regards, Bala On 4 June 2014 09:36, Mike Holmes <mike.holmes@linaro.org> wrote: > On the call today we raised some points we should address at the sprint. > > > 1. The linux-generic implementation is bleeding into the API > definition, we should find a way to address this for crypto which also > exhibits the problem and then retro fix other occurrences. > 2. There was also a point that the implementation is not following the > definition in the crypto Arch doc, we need to update the arch doc or the > implementation. > > > I think it would be better to fix both of these before accepting this > patch, given that next week face to face we can solve issue 1 which will > then apply to all of ODP and point 2 may well get sucked up into other > changes anyway. > > > On 4 June 2014 12:30, Maxim Uvarov <maxim.uvarov@linaro.org> wrote: > >> Hello Robbie, >> >> are you working on new version of this patch or this patch is good to >> merge now? Also the same question about example app patch. >> >> Thank you, >> Maxim. >> >> >> On 06/04/2014 04:51 PM, Anders Roxell wrote: >> >>> Sorry for the late reply! >>> See inline. >>> >>> On 2014-06-02 18:28, Robbie King (robking) wrote: >>> >>>> Hi Anders, thanks for having a look. See [RK] inline. >>>> >>>> -----Original Message----- >>>> From: Anders Roxell [mailto:anders.roxell@linaro.org] >>>> Sent: Monday, June 02, 2014 2:02 PM >>>> To: Robbie King (robking) >>>> Cc: lng-odp@lists.linaro.org >>>> Subject: Re: [lng-odp] [PATCHv2] linux-generic crypto >>>> >>>> I'm sorry for my ignorance in crypto, however it looks like we can use >>>> generic length variables, but instead we limit them to uint8_t, 16 or >>>> 32. >>>> I didn't comment them all but there are multiple cases of them. >>>> Please see inline comments. >>>> >>>> >>>> On 2014-05-30 13:15, Robbie King wrote: >>>> >>>>> Initial linux-generic crypto implementation. Note that these changes >>>>> require installing and linking with libssl-dev. The camel case >>>>> warnings >>>>> are also due to calls into libssl-dev. >>>>> >>>>> v1..v2: Fixed ARM compile warning, and modified "odp_hw_random_get" to >>>>> take length as "size_t" instead of "uint32_t". >>>>> >>>>> Signed-off-by: Robbie King <robking@cisco.com> >>>>> --- >>>>> include/odp_crypto.h | 295 >>>>> ++++++++++++++ >>>>> platform/linux-generic/Makefile | 1 + >>>>> .../linux-generic/include/odp_crypto_internal.h | 86 ++++ >>>>> platform/linux-generic/source/odp_crypto.c | 435 >>>>> +++++++++++++++++++++ >>>>> 4 files changed, 817 insertions(+) >>>>> create mode 100644 include/odp_crypto.h >>>>> create mode 100644 platform/linux-generic/ >>>>> include/odp_crypto_internal.h >>>>> create mode 100644 platform/linux-generic/source/odp_crypto.c >>>>> >>>>> diff --git a/include/odp_crypto.h b/include/odp_crypto.h >>>>> new file mode 100644 >>>>> index 0000000..725e8f1 >>>>> --- /dev/null >>>>> +++ b/include/odp_crypto.h >>>>> @@ -0,0 +1,295 @@ >>>>> +/* Copyright (c) 2013, Linaro Limited >>>>> >>>> Nit: date 2014 >>>> Should be like this for all the files. >>>> >>>> [RK] good catch. >>>> >>>> + * All rights reserved. >>>>> + * >>>>> + * SPDX-License-Identifier: BSD-3-Clause >>>>> + */ >>>>> + >>>>> + >>>>> +/** >>>>> + * @file >>>>> + * >>>>> + * ODP crypto >>>>> + */ >>>>> + >>>>> +#ifndef ODP_CRYPTO_H_ >>>>> +#define ODP_CRYPTO_H_ >>>>> + >>>>> +#ifdef __cplusplus >>>>> +extern "C" { >>>>> +#endif >>>>> + >>>>> +#include <odp_std_types.h> >>>>> +#include <odp_buffer.h> >>>>> +#include <odp_buffer_pool.h> >>>>> +#include <odp_queue.h> >>>>> +#include <odp_packet.h> >>>>> + >>>>> +/** Invalid session handle */ >>>>> +#define ODP_CRYPTO_SESSION_INVALID (-1ULL) >>>>> + >>>>> +/** >>>>> + * Crypto API opaque session handle >>>>> + */ >>>>> +typedef uint64_t odp_crypto_session_t; >>>>> + >>>>> +/** >>>>> + * Crypto API operation mode >>>>> + */ >>>>> +enum odp_crypto_op_mode { >>>>> + ODP_CRYPTO_SYNC, /**< Synchronous, return results >>>>> immediately */ >>>>> + ODP_CRYPTO_ASYNC, /**< Aynchronous, return results via >>>>> posted event */ >>>>> +}; >>>>> + >>>>> +/** >>>>> + * Crypto API operation type >>>>> + */ >>>>> +enum odp_crypto_op { >>>>> + ODP_CRYPTO_OP_ENCODE, /**< Encrypt and/or compute >>>>> authentication ICV */ >>>>> + ODP_CRYPTO_OP_DECODE /**< Decrypt and/or verify >>>>> authentication ICV */ >>>>> +}; >>>>> + >>>>> +/** >>>>> + * Crypto API cipher algorithm >>>>> + */ >>>>> +enum odp_cipher_alg { >>>>> + ODP_CIPHER_ALG_NULL, /**< No cipher algorithm specified */ >>>>> + ODP_CIPHER_ALG_DES, /**< DES */ >>>>> + ODP_CIPHER_ALG_3DES_CBC, /**< Triple DES with cipher block >>>>> chaining */ >>>>> +}; >>>>> + >>>>> +/** >>>>> + * Crypto API authentication algorithm >>>>> + */ >>>>> +enum odp_auth_alg { >>>>> + ODP_AUTH_ALG_NULL, /**< No authentication algorithm >>>>> specified */ >>>>> + ODP_AUTH_ALG_MD5_96, /**< HMAC-MD5 with 96 bit key */ >>>>> +}; >>>>> + >>>>> +/** >>>>> + * Crypto API operation order >>>>> + */ >>>>> +enum odp_crypto_combination { >>>>> + ODP_CRYPTO_CIPHER_ONLY, /**< Only perform cipher */ >>>>> + ODP_CRYPTO_AUTH_ONLY, /**< Only perform authentication */ >>>>> + ODP_CRYPTO_AUTH_CIPHERTEXT /**< Cipher then authentication on >>>>> encode */ >>>>> +}; >>>>> + >>>>> +/** >>>>> + * Crypto API key >>>>> + */ >>>>> +typedef struct odp_key_s { >>>>> + union { >>>>> + /** DES/3DES key definition (set all same for DES) */ >>>>> + struct { >>>>> + uint8_t k1[8]; /**< First key */ >>>>> + uint8_t k2[8]; /**< Second key */ >>>>> + uint8_t k3[8]; /**< Third key */ >>>>> + } des; >>>>> + /** MD5 key */ >>>>> + struct { >>>>> + uint8_t key[16]; /**< Key up to 128 bits */ >>>>> + } md5; >>>>> + }; >>>>> +} odp_key_t; >>>>> + >>>>> +/** >>>>> + * Crypto API data range specifier >>>>> + */ >>>>> +struct odp_data_range { >>>>> + uint16_t offset; /**< Offset from beginning of buffer (chain) >>>>> */ >>>>> + uint16_t length; /**< Length of data to operate on */ >>>>> >>>> Why do we use uint16_t? >>>> Can't the data be bigger than this? >>>> >>>> [RK] this came from the ODP crypto spec, I think in general the idea >>>> is packets will never be larger than an Ethernet jumbo (roughly 10K) >>>> so no need to support larger than 64K. >>>> >>> So was that a reason for choosing uint16_t in the spec? >>> Or shall we change the spec? >>> >>> +}; >>>>> + >>>>> +/** >>>>> + * Crypto API session creation paramters >>>>> + * >>>>> + * TODO: add "odp_session_proc_info_t" >>>>> + */ >>>>> +struct odp_crypto_session_params { >>>>> + enum odp_crypto_op op; /**< Encode versus decode */ >>>>> + enum odp_crypto_combination comb; /**< Operation order */ >>>>> + enum odp_crypto_op_mode pref_mode; /**< Preferred sync vs >>>>> async */ >>>>> + enum odp_cipher_alg cipher_alg; /**< Cipher algorithm */ >>>>> + odp_key_t *cipher_key; /**< Cipher key */ >>>>> + uint8_t *iv; /**< Cipher Initialization >>>>> Vector (IV) */ >>>>> >>>> Why do we use uint8_t and not uint_t? >>>> >>>> [RK] This one also comes from the spec, my understanding is for crypto >>>> the IV is always an opaque byte string that can be different lengths >>>> for different ciphers. >>>> >>> OK, thanks. >>> >>> + size_t iv_len; /**< Cipher IV length */ >>>>> + enum odp_auth_alg auth_alg; /**< Authentication algorithm */ >>>>> + odp_key_t *auth_key; /**< Authentication key */ >>>>> + odp_queue_t compl_queue; /**< Async mode completion >>>>> event queue */ >>>>> +}; >>>>> + >>>>> +/** >>>>> + * Crypto API per packet operation parameters >>>>> + */ >>>>> +struct odp_crypto_op_params { >>>>> + odp_crypto_session_t session; /**< Session handle from >>>>> creation */ >>>>> + odp_packet_t pkt; /**< Input packet buffer */ >>>>> + odp_packet_t out_pkt; /**< Output packet buffer >>>>> (optional) */ >>>>> + uint8_t *override_iv_ptr; /**< Override session IV >>>>> pointer */ >>>>> + unsigned hash_result_offset; /**< Offset from start of >>>>> packet buffer for hash result */ >>>>> + struct odp_data_range cipher_range; /**< Data range to apply >>>>> cipher */ >>>>> + struct odp_data_range auth_range; /**< Data range to >>>>> authenticate */ >>>>> +}; >>>>> + >>>>> +/** >>>>> + * Crypto API session creation return code >>>>> + * >>>>> + * TODO: seems confusing, maybe _rc instead >>>>> + */ >>>>> +enum odp_crypto_ses_create_err { >>>>> + ODP_CRYPTO_SES_CREATE_NONE, /**< No session created? need to >>>>> clarify */ >>>>> + ODP_CRYPTO_SES_CREATE_OK, /**< Session created successfully >>>>> */ >>>>> +}; >>>>> + >>>>> +/** >>>>> + * Crypto API algorithm return code >>>>> + */ >>>>> +enum crypto_alg_err { >>>>> + ODP_CRYPTO_ALG_ERR_NONE, /**< Algorithm successful */ >>>>> + ODP_CRYPTO_ALG_ERR_DATA_SIZE, /**< Invalid data block size */ >>>>> + ODP_CRYPTO_ALG_ERR_KEY_SIZE, /**< Key size invalid for >>>>> algorithm */ >>>>> + ODP_CRYPTO_ALG_ERR_ICV_CHECK, /**< Computed ICV value mismatch >>>>> */ >>>>> +}; >>>>> + >>>>> +/** >>>>> + * Crypto API operation return code >>>>> + */ >>>>> +typedef enum odp_crypto_rc { >>>>> + ODP_CRYPTO_OP_OK, /**< Operation completed, results are >>>>> valid */ >>>>> + ODP_CRYPTO_OP_POSTED, /**< Operation was posted, results >>>>> delivered via completion queue */ >>>>> + ODP_CRYPTO_OP_ERROR, /**< Operation failed */ >>>>> +} odp_crypto_rc_e; >>>>> + >>>>> +/** >>>>> + * Crypto API hardware centric return code >>>>> + */ >>>>> +enum crypto_hw_err { >>>>> + ODP_CRYPTO_HW_ERR_NONE, /**< Operation completed >>>>> successfully */ >>>>> + ODP_CRYPTO_HW_ERR_DMA, /**< Error detected during DMA >>>>> of data */ >>>>> + ODP_CRYPTO_HW_ERR_BP_DEPLETED, /**< Operation failed due to >>>>> buffer pool depletion */ >>>>> +}; >>>>> + >>>>> +/** >>>>> + * Crypto API algorithm (cipher or authentication) >>>>> + */ >>>>> +typedef union odp_crypto_alg_u { >>>>> + enum odp_cipher_alg cipher; /**< Cipher algorithm */ >>>>> + enum odp_auth_alg auth; /**< Authentication algorithm */ >>>>> +} odp_crypto_alg_t; >>>>> + >>>>> +/** >>>>> + * Cryto API per packet operation completion status >>>>> + */ >>>>> +struct odp_crypto_compl_status { >>>>> + odp_crypto_alg_t alg; /**< Requested algorithm */ >>>>> + enum crypto_alg_err alg_err; /**< Algorithm specific return >>>>> code */ >>>>> + enum crypto_hw_err hw_err; /**< Hardware specific return >>>>> code */ >>>>> +}; >>>>> + >>>>> >>>> Is there a good reason why we are mixing enum name with typedef? >>>> >>>> [RK] I agree, I would like to see us perhaps agree on typedef'ing >>>> enums. This one is a little kludgy anyway, I would vote for removing >>>> the ALG from the completion status since it is simply a mirror of the >>>> ALG (cipher or auth) which was specified during session creation. >>>> >>> I agree with you. >>> >>> + >>>>> +/** >>>>> + * Crypto session creation >>>>> + * >>>>> + * Create a crypto session. Operation occurs asynchronously if a >>>>> completion >>>>> + * queue is specified else synchronously. >>>>> + * >>>>> + * @param params Session parameters >>>>> + * @param completion_event Event by which the session creation >>>>> results are >>>>> + * delivered. >>>>> + * @param completion_queue Queue by which the completion event will >>>>> be >>>>> + * delivered. Ignored if ODP_QUEUE_INVALID. >>>>> + * >>>>> + * @return Operation return code indicating success or failure for >>>>> + * when synchronous operation requested, else POSTED when >>>>> + * asynchronous operation is requested. >>>>> + */ >>>>> +odp_crypto_rc_e >>>>> +odp_crypto_session_create(struct odp_crypto_session_params *params, >>>>> + odp_buffer_t completion_event, >>>>> + odp_queue_t completion_queue); >>>>> + >>>>> + >>>>> +/** >>>>> + * Crypto session creation completion status >>>>> + * >>>>> + * Accessor function for obtaining creation status from the >>>>> completion event. >>>>> + * >>>>> + * @param completion_event Event containing operation results >>>>> + * @param status Pointer to store creation return code >>>>> + */ >>>>> +void >>>>> +odp_crypto_get_ses_create_compl_status(odp_buffer_t completion_event, >>>>> + enum odp_crypto_ses_create_err >>>>> *status); >>>>> + >>>>> +/** >>>>> + * Crypto session creation completion return value >>>>> + * >>>>> + * Accessor function for obtaining handle for newly created session. >>>>> + * >>>>> + * @param completion_event Event containing operation results >>>>> + * @param session Pointer to store session handle >>>>> + */ >>>>> +void >>>>> +odp_crypto_get_ses_create_compl_session(odp_buffer_t >>>>> completion_event, >>>>> + odp_crypto_session_t *session); >>>>> + >>>>> +/** >>>>> + * Crypto per packet operation >>>>> + * >>>>> + * Performs the cryptographic operations specified during session >>>>> creation >>>>> + * on the packet. >>>>> + * >>>>> + * @param params Operation parameters >>>>> + * @param completion_event Event by which the session creation >>>>> results are >>>>> + * delivered. >>>>> + * >>>>> + * @return Operation return code indicating success or failure when >>>>> session >>>>> + * indicates synchronous operation, else POSTED for >>>>> asynchronous >>>>> + * operation. >>>>> + */ >>>>> +odp_crypto_rc_e >>>>> +odp_crypto_operation(struct odp_crypto_op_params *params, >>>>> + odp_buffer_t completion_event); >>>>> + >>>>> + >>>>> +/** >>>>> + * Crypto per packet operation completion status >>>>> + * >>>>> + * Accessor function for obtaining operation status from the >>>>> completion event. >>>>> + * >>>>> + * @param completion_event Event containing operation results >>>>> + * @param auth Pointer to store authentication results >>>>> + * @param cipher Pointer to store cipher results >>>>> + */ >>>>> +void >>>>> +odp_crypto_get_operation_compl_status(odp_buffer_t completion_event, >>>>> + struct odp_crypto_compl_status >>>>> *auth, >>>>> + struct odp_crypto_compl_status >>>>> *cipher); >>>>> + >>>>> +/** >>>>> + * Generate random byte string >>>>> + * >>>>> + * @param buf Pointer to store result >>>>> + * @param len Pointer to input length value as well as >>>>> return value >>>>> + * @param use_entropy (TODO: needs description) >>>>> + * >>>>> + * @return 0 if succesful >>>>> + */ >>>>> +int >>>>> +odp_hw_random_get(uint8_t *buf, size_t *len, bool use_entropy); >>>>> + >>>>> +/** >>>>> + * Initialize the crypto subsystem, called once from main thread >>>>> + * >>>>> + * @param max_sessions Maximum number of sessions to support >>>>> + * >>>>> + * @return 0 if succesful >>>>> + */ >>>>> +int >>>>> +odp_crypto_init(uint32_t max_sessions); >>>>> + >>>>> +#ifdef __cplusplus >>>>> +} >>>>> +#endif >>>>> + >>>>> +#endif >>>>> diff --git a/platform/linux-generic/Makefile b/platform/linux-generic/ >>>>> Makefile >>>>> index ec5d4a7..57f6d18 100644 >>>>> --- a/platform/linux-generic/Makefile >>>>> +++ b/platform/linux-generic/Makefile >>>>> @@ -72,6 +72,7 @@ OBJS += $(OBJ_DIR)/odp_time.o >>>>> OBJS += $(OBJ_DIR)/odp_timer.o >>>>> OBJS += $(OBJ_DIR)/odp_ring.o >>>>> OBJS += $(OBJ_DIR)/odp_rwlock.o >>>>> +OBJS += $(OBJ_DIR)/odp_crypto.o >>>>> ifeq ($(ODP_HAVE_NETMAP),yes) >>>>> OBJS += $(OBJ_DIR)/odp_packet_netmap.o >>>>> endif >>>>> diff --git a/platform/linux-generic/include/odp_crypto_internal.h >>>>> b/platform/linux-generic/include/odp_crypto_internal.h >>>>> new file mode 100644 >>>>> index 0000000..e558864 >>>>> --- /dev/null >>>>> +++ b/platform/linux-generic/include/odp_crypto_internal.h >>>>> @@ -0,0 +1,86 @@ >>>>> +/* Copyright (c) 2013, Linaro Limited >>>>> + * All rights reserved. >>>>> + * >>>>> + * SPDX-License-Identifier: BSD-3-Clause >>>>> + */ >>>>> + >>>>> + >>>>> + >>>>> +#ifndef ODP_CRYPTO_INTERNAL_H_ >>>>> +#define ODP_CRYPTO_INTERNAL_H_ >>>>> + >>>>> +#ifdef __cplusplus >>>>> +extern "C" { >>>>> +#endif >>>>> + >>>>> +#include <openssl/des.h> >>>>> + >>>>> +#define OP_RESULT_MAGIC 0x91919191 >>>>> + >>>>> +/** Forward declaration of session structure */ >>>>> +struct odp_crypto_session_s; >>>>> + >>>>> +/** >>>>> + * Algorithm handler function prototype >>>>> + */ >>>>> +typedef >>>>> +enum crypto_alg_err (*crypto_func_t)(struct odp_crypto_op_params >>>>> *params, >>>>> + struct odp_crypto_session_s >>>>> *session); >>>>> + >>>>> +/** >>>>> + * Per crypto session data structure >>>>> + */ >>>>> +struct odp_crypto_session_s { >>>>> + uint32_t index; >>>>> >>>> Why not uint_t? >>>> >>>> [RK] OK by me, since this data structure is internal to the >>>> linux-generic implementation, and the plan is to support a >>>> very small number of sessions this could be made as small as >>>> uint8_t I think. >>>> >>> OK, I'm fine with that. >>> >>> + enum odp_crypto_op op; >>>>> + enum odp_crypto_combination comb; >>>>> + odp_queue_t compl_queue; >>>>> + struct { >>>>> + enum odp_cipher_alg alg; >>>>> + struct { >>>>> + uint8_t *data; >>>>> >>>> Why not uint_t? >>>> >>>> [RK] Same reason as above for IV since it is byte data. >>>> >>> OK. >>> >>> + size_t len; >>>>> + } iv; >>>>> + union { >>>>> + struct { >>>>> + DES_key_schedule ks1; >>>>> + DES_key_schedule ks2; >>>>> + DES_key_schedule ks3; >>>>> + } des; >>>>> + } data; >>>>> >>>> Is there a good reason to have a union with only one member? >>>> >>>> [RK] The union is here with the intention that the linux-generic >>>> implementation will add support for other ciphers (AES for example). >>>> So the union is simply laying the ground work for expansion. >>>> >>> I understand, didn't think about that! >>> Thank you for your explanation. >>> >>> + crypto_func_t func; >>>>> + } cipher; >>>>> + struct { >>>>> + enum odp_auth_alg alg; >>>>> + union { >>>>> + struct { >>>>> + uint8_t key[16]; >>>>> >>>> Why are we limiting the key to 16? >>>> I guess we want to limit the key up to 128 bits right? >>>> >>>> [RK] 128 bit key is specific to MD5, other algorithems >>>> would have their own entry in the union (again for future >>>> expansion). >>>> >>> OK good. >>> Maybe comment that it is for keys up to 128 bits? >>> >>> + } md5; >>>>> + } data; >>>>> >>>> Is there a good reason to have a union with only one member? >>>> >>>> + crypto_func_t func; >>>>> + } auth; >>>>> + >>>>> +}; >>>>> + >>>>> +/** >>>>> + * Per packet operation result >>>>> + */ >>>>> +struct odp_operation_result_s { >>>>> + uint32_t magic; >>>>> + struct odp_crypto_compl_status cipher; >>>>> + struct odp_crypto_compl_status auth; >>>>> +}; >>>>> + >>>>> +/** >>>>> + * Per session creation operation result >>>>> + */ >>>>> +struct odp_session_result_s { >>>>> + enum odp_crypto_ses_create_err rc; >>>>> + odp_crypto_session_t session; >>>>> +}; >>>>> + >>>>> +#ifdef __cplusplus >>>>> +} >>>>> +#endif >>>>> + >>>>> +#endif >>>>> diff --git a/platform/linux-generic/source/odp_crypto.c >>>>> b/platform/linux-generic/source/odp_crypto.c >>>>> new file mode 100644 >>>>> index 0000000..4ae3645 >>>>> --- /dev/null >>>>> +++ b/platform/linux-generic/source/odp_crypto.c >>>>> @@ -0,0 +1,435 @@ >>>>> +/* Copyright (c) 2013, Linaro Limited >>>>> + * All rights reserved. >>>>> + * >>>>> + * SPDX-License-Identifier: BSD-3-Clause >>>>> + */ >>>>> + >>>>> +#include <odp_crypto.h> >>>>> +#include <odp_internal.h> >>>>> +#include <odp_atomic.h> >>>>> +#include <odp_spinlock.h> >>>>> +#include <odp_sync.h> >>>>> +#include <odp_debug.h> >>>>> +#include <odp_align.h> >>>>> +#include <odp_shared_memory.h> >>>>> +#include <odp_crypto_internal.h> >>>>> + >>>>> +#include <string.h> >>>>> + >>>>> +#include <openssl/des.h> >>>>> +#include <openssl/rand.h> >>>>> +#include <openssl/hmac.h> >>>>> +#include <openssl/evp.h> >>>>> + >>>>> +#define MAX_SESSIONS 32 >>>>> + >>>>> +typedef struct { >>>>> + odp_atomic_u32_t next; >>>>> + uint32_t max; >>>>> + struct odp_crypto_session_s sessions[0]; >>>>> >>>> Pointer instead? >>>> >>>> [RK] This is specifically for allocating memory where >>>> the storage for sessions immediately follows the >>>> structure, allowing an arbitrary number of sessions to >>>> be supported at allocation time like shown yet accessed >>>> as if they were statically allocated: >>>> >>>> /* Calculate the memory size we need */ >>>> mem_size = sizeof(*global); >>>> mem_size += (max_sessions * sizeof(struct >>>> odp_crypto_session_s)); >>>> >>>> and you can access individual sessions like this: >>>> >>>> session = &global->sessions[idx]; >>>> >>>> I've used this a lot over the years, but if it gives folks heartburn >>>> I can allocate memory in multiple stages, etc. >>>> >>> I'm fine with either way as long as we do the same, what does others >>> think about this? >>> >>> Cheers, >>>> Anders >>>> >>>> +} odp_crypto_global_t; >>>>> + >>>>> +static odp_crypto_global_t *global; >>>>> + >>>>> +/* >>>>> + * TODO: This is a serious hack to allow us to use packet buffer to >>>>> convey >>>>> + * crypto operation results by placing them at the very end of >>>>> the >>>>> + * packet buffer. >>>>> + */ >>>>> +static >>>>> +struct odp_operation_result_s *get_op_result_from_buffer(odp_buffer_t >>>>> buf) >>>>> +{ >>>>> + uint8_t *temp; >>>>> + struct odp_operation_result_s *result; >>>>> + >>>>> + temp = odp_buffer_addr(buf); >>>>> + temp += odp_buffer_size(buf); >>>>> + temp -= sizeof(*result); >>>>> + result = (struct odp_operation_result_s *)(void *)temp; >>>>> + return result; >>>>> +} >>>>> + >>>>> +static >>>>> +struct odp_crypto_session_s *alloc_session(void) >>>>> +{ >>>>> + uint32_t idx; >>>>> + struct odp_crypto_session_s *session = NULL; >>>>> + >>>>> + idx = odp_atomic_fetch_inc_u32(&global->next); >>>>> + if (idx < global->max) { >>>>> + session = &global->sessions[idx]; >>>>> + session->index = idx; >>>>> + } >>>>> + return session; >>>>> +} >>>>> + >>>>> +static >>>>> +enum crypto_alg_err null_crypto_routine( >>>>> + struct odp_crypto_op_params *params ODP_UNUSED, >>>>> + struct odp_crypto_session_s *session ODP_UNUSED) >>>>> +{ >>>>> + return ODP_CRYPTO_ALG_ERR_NONE; >>>>> +} >>>>> + >>>>> +static >>>>> +enum crypto_alg_err md5_gen(struct odp_crypto_op_params *params, >>>>> + struct odp_crypto_session_s *session) >>>>> +{ >>>>> + uint8_t *data = odp_packet_buf_addr(params->pkt); >>>>> + uint8_t *icv = data; >>>>> + uint32_t len = params->auth_range.length; >>>>> + uint8_t hash[EVP_MAX_MD_SIZE]; >>>>> + uint32_t hlen = 12; >>>>> + >>>>> + /* Adjust pointer for beginning of area to auth */ >>>>> + data += params->auth_range.offset; >>>>> + icv += params->hash_result_offset; >>>>> + >>>>> + /* Hash it */ >>>>> + HMAC(EVP_md5(), session->auth.data.md5.key, 16, data, len, >>>>> hash, &hlen); >>>>> + >>>>> + /* Copy to the output location */ >>>>> + memcpy(icv, hash, 12); >>>>> + >>>>> + return ODP_CRYPTO_ALG_ERR_NONE; >>>>> +} >>>>> + >>>>> + >>>>> +static >>>>> +enum crypto_alg_err md5_check(struct odp_crypto_op_params *params, >>>>> + struct odp_crypto_session_s *session) >>>>> +{ >>>>> + uint8_t *data = odp_packet_buf_addr(params->pkt); >>>>> + uint8_t *icv = data; >>>>> + uint32_t len = params->auth_range.length; >>>>> + uint8_t hash[EVP_MAX_MD_SIZE]; >>>>> + uint32_t hlen = 12; >>>>> + >>>>> + /* Adjust pointer for beginning of area to auth */ >>>>> + data += params->auth_range.offset; >>>>> + icv += params->hash_result_offset; >>>>> + >>>>> + /* Copy current value out and clear it before authentication */ >>>>> + memcpy(hash, icv, hlen); >>>>> + memset(icv, 0, hlen); >>>>> + >>>>> + /* Hash it */ >>>>> + HMAC(EVP_md5(), session->auth.data.md5.key, 16, data, len, >>>>> icv, &hlen); >>>>> + >>>>> + /* Verify match */ >>>>> + if (0 != memcmp(icv, hash, 12)) >>>>> + return ODP_CRYPTO_ALG_ERR_ICV_CHECK; >>>>> + >>>>> + /* Matched */ >>>>> + return ODP_CRYPTO_ALG_ERR_NONE; >>>>> +} >>>>> + >>>>> +static >>>>> +enum crypto_alg_err des_encrypt(struct odp_crypto_op_params *params, >>>>> + struct odp_crypto_session_s *session) >>>>> +{ >>>>> + uint8_t *data = odp_packet_buf_addr(params->pkt); >>>>> + uint32_t len = params->cipher_range.length; >>>>> + DES_cblock *iv = (DES_cblock *)session->cipher.iv.data; >>>>> + >>>>> + /* Adjust pointer for beginning of area to cipher */ >>>>> + data += params->cipher_range.offset; >>>>> + >>>>> + /* Override IV if requested */ >>>>> + if (params->override_iv_ptr) >>>>> + iv = (DES_cblock *)params->override_iv_ptr; >>>>> + >>>>> + /* Encrypt it */ >>>>> + DES_ede3_cbc_encrypt(data, >>>>> + data, >>>>> + len, >>>>> + &session->cipher.data.des.ks1, >>>>> + &session->cipher.data.des.ks2, >>>>> + &session->cipher.data.des.ks3, >>>>> + iv, >>>>> + 1); >>>>> + >>>>> + return ODP_CRYPTO_ALG_ERR_NONE; >>>>> +} >>>>> + >>>>> +static >>>>> +enum crypto_alg_err des_decrypt(struct odp_crypto_op_params *params, >>>>> + struct odp_crypto_session_s *session) >>>>> +{ >>>>> + uint8_t *data = odp_packet_buf_addr(params->pkt); >>>>> + uint32_t len = params->cipher_range.length; >>>>> + DES_cblock *iv = (DES_cblock *)session->cipher.iv.data; >>>>> + >>>>> + /* Adjust pointer for beginning of area to cipher */ >>>>> + data += params->cipher_range.offset; >>>>> + >>>>> + /* Override IV if requested */ >>>>> + if (params->override_iv_ptr) >>>>> + iv = (DES_cblock *)params->override_iv_ptr; >>>>> + >>>>> + /* Decrypt it */ >>>>> + DES_ede3_cbc_encrypt(data, >>>>> + data, >>>>> + len, >>>>> + &session->cipher.data.des.ks1, >>>>> + &session->cipher.data.des.ks2, >>>>> + &session->cipher.data.des.ks3, >>>>> + iv, >>>>> + 0); >>>>> + >>>>> + return ODP_CRYPTO_ALG_ERR_NONE; >>>>> +} >>>>> + >>>>> +static >>>>> +int process_des_params(struct odp_crypto_session_s *session, >>>>> + struct odp_crypto_session_params *params) >>>>> +{ >>>>> + /* Verify IV len is either 0 or 8 */ >>>>> + if (!((0 == params->iv_len) || (8 == params->iv_len))) >>>>> + return -1; >>>>> + >>>>> + /* Verify IV pointer */ >>>>> + if (params->iv_len && !params->iv) >>>>> + return -1; >>>>> + >>>>> + /* Set function */ >>>>> + if (ODP_CRYPTO_OP_ENCODE == params->op) >>>>> + session->cipher.func = des_encrypt; >>>>> + else >>>>> + session->cipher.func = des_decrypt; >>>>> + >>>>> + /* Convert keys */ >>>>> + DES_set_key(¶ms->cipher_key->des.k1, >>>>> &session->cipher.data.des.ks1); >>>>> + DES_set_key(¶ms->cipher_key->des.k2, >>>>> &session->cipher.data.des.ks2); >>>>> + DES_set_key(¶ms->cipher_key->des.k3, >>>>> &session->cipher.data.des.ks3); >>>>> + >>>>> + return 0; >>>>> +} >>>>> + >>>>> +static >>>>> +int process_md5_params(struct odp_crypto_session_s *session, >>>>> + struct odp_crypto_session_params *params) >>>>> +{ >>>>> + /* Set function */ >>>>> + if (ODP_CRYPTO_OP_ENCODE == params->op) >>>>> + session->auth.func = md5_gen; >>>>> + else >>>>> + session->auth.func = md5_check; >>>>> + >>>>> + /* Convert keys */ >>>>> + memcpy(session->auth.data.md5.key, params->auth_key->md5.key, >>>>> 16); >>>>> + >>>>> + return 0; >>>>> +} >>>>> + >>>>> +odp_crypto_rc_e >>>>> +odp_crypto_session_create(struct odp_crypto_session_params *params, >>>>> + odp_buffer_t completion_event, >>>>> + odp_queue_t completion_queue) >>>>> +{ >>>>> + int rc; >>>>> + struct odp_crypto_session_s *session; >>>>> + struct odp_session_result_s *result = >>>>> odp_buffer_addr(completion_event); >>>>> + >>>>> + /* Default to failure result */ >>>>> + result->rc = ODP_CRYPTO_SES_CREATE_NONE; >>>>> + result->session = ODP_CRYPTO_SESSION_INVALID; >>>>> + >>>>> + /* Allocate memory for this session */ >>>>> + session = alloc_session(); >>>>> + if (NULL == session) >>>>> + return ODP_CRYPTO_OP_ERROR; >>>>> + >>>>> + /* Copy stuff over */ >>>>> + session->op = params->op; >>>>> + session->comb = params->comb; >>>>> + session->compl_queue = params->compl_queue; >>>>> + session->cipher.alg = params->cipher_alg; >>>>> + session->cipher.iv.data = params->iv; >>>>> + session->cipher.iv.len = params->iv_len; >>>>> + session->auth.alg = params->auth_alg; >>>>> + >>>>> + /* Process based on cipher */ >>>>> + switch (params->cipher_alg) { >>>>> + case ODP_CIPHER_ALG_NULL: >>>>> + session->cipher.func = null_crypto_routine; >>>>> + rc = 0; >>>>> + break; >>>>> + case ODP_CIPHER_ALG_DES: >>>>> + case ODP_CIPHER_ALG_3DES_CBC: >>>>> + rc = process_des_params(session, params); >>>>> + break; >>>>> + default: >>>>> + rc = -1; >>>>> + } >>>>> + >>>>> + /* Check result */ >>>>> + if (rc) >>>>> + return ODP_CRYPTO_OP_ERROR; >>>>> + >>>>> + /* Process based on auth */ >>>>> + switch (params->auth_alg) { >>>>> + case ODP_AUTH_ALG_NULL: >>>>> + session->auth.func = null_crypto_routine; >>>>> + rc = 0; >>>>> + break; >>>>> + case ODP_AUTH_ALG_MD5_96: >>>>> + rc = process_md5_params(session, params); >>>>> + break; >>>>> + default: >>>>> + rc = -1; >>>>> + } >>>>> + >>>>> + /* Check result */ >>>>> + if (rc) >>>>> + return ODP_CRYPTO_OP_ERROR; >>>>> + >>>>> + /* We're happy */ >>>>> + result->rc = ODP_CRYPTO_SES_CREATE_OK; >>>>> + result->session = (intptr_t)session; >>>>> + >>>>> + /* If there is a queue post else we're good */ >>>>> + if (ODP_QUEUE_INVALID != completion_queue) { >>>>> + odp_queue_enq(completion_queue, completion_event); >>>>> + return ODP_CRYPTO_OP_POSTED; >>>>> + } >>>>> + >>>>> + return ODP_CRYPTO_OP_OK; >>>>> +} >>>>> + >>>>> + >>>>> +odp_crypto_rc_e >>>>> +odp_crypto_operation(struct odp_crypto_op_params *params, >>>>> + odp_buffer_t completion_event) >>>>> +{ >>>>> + enum crypto_alg_err rc_cipher = ODP_CRYPTO_ALG_ERR_NONE; >>>>> + enum crypto_alg_err rc_auth = ODP_CRYPTO_ALG_ERR_NONE; >>>>> + struct odp_crypto_session_s *session; >>>>> + struct odp_operation_result_s *result; >>>>> + >>>>> + session = (struct odp_crypto_session_s >>>>> *)(intptr_t)params->session; >>>>> + >>>>> + /* >>>>> + * robking: need to understand infrastructure for scattered >>>>> packets >>>>> + * for now just don't support them >>>>> + */ >>>>> + if (odp_buffer_is_scatter(odp_buffer_from_packet(params-> >>>>> pkt))) >>>>> + return ODP_CRYPTO_OP_ERROR; >>>>> + >>>>> + /* >>>>> + * robking: for now we are only going to support in place >>>>> + */ >>>>> + if (params->pkt != params->out_pkt) >>>>> + return ODP_CRYPTO_OP_ERROR; >>>>> + >>>>> + /* Invoke the functions */ >>>>> + switch (session->comb) { >>>>> + case ODP_CRYPTO_CIPHER_ONLY: >>>>> + rc_cipher = session->cipher.func(params, session); >>>>> + break; >>>>> + case ODP_CRYPTO_AUTH_ONLY: >>>>> + rc_auth = session->auth.func(params, session); >>>>> + break; >>>>> + case ODP_CRYPTO_AUTH_CIPHERTEXT: >>>>> + if (ODP_CRYPTO_OP_ENCODE == session->op) { >>>>> + rc_cipher = session->cipher.func(params, >>>>> session); >>>>> + rc_auth = session->auth.func(params, session); >>>>> + } else { >>>>> + rc_auth = session->auth.func(params, session); >>>>> + rc_cipher = session->cipher.func(params, >>>>> session); >>>>> + } >>>>> + break; >>>>> + } >>>>> + >>>>> + /* Build Result (no HW so no errors) */ >>>>> + result = get_op_result_from_buffer(completion_event); >>>>> + result->magic = OP_RESULT_MAGIC; >>>>> + result->cipher.alg.cipher = session->cipher.alg; >>>>> + result->cipher.alg_err = rc_cipher; >>>>> + result->cipher.hw_err = ODP_CRYPTO_HW_ERR_NONE; >>>>> + result->auth.alg.auth = session->auth.alg; >>>>> + result->auth.alg_err = rc_auth; >>>>> + result->auth.hw_err = ODP_CRYPTO_HW_ERR_NONE; >>>>> + >>>>> + /* >>>>> + * robking: a) the queue is supposed to come from session >>>>> + * b) ordering question asks whether we must >>>>> + * use the packet to return status >>>>> + */ >>>>> + if (ODP_QUEUE_INVALID != session->compl_queue) { >>>>> + odp_queue_enq(session->compl_queue, completion_event); >>>>> + return ODP_CRYPTO_OP_POSTED; >>>>> + } >>>>> + >>>>> + return ODP_CRYPTO_OP_OK; >>>>> +} >>>>> + >>>>> + >>>>> +int >>>>> +odp_crypto_init(uint32_t max_sessions) >>>>> +{ >>>>> + size_t mem_size; >>>>> + >>>>> + /* Force down to our limit */ >>>>> + if (MAX_SESSIONS < max_sessions) >>>>> + max_sessions = MAX_SESSIONS; >>>>> + >>>>> + /* Calculate the memory size we need */ >>>>> + mem_size = sizeof(*global); >>>>> + mem_size += (max_sessions * sizeof(struct >>>>> odp_crypto_session_s)); >>>>> + >>>>> + /* Allocate our globally shared memory */ >>>>> + global = odp_shm_reserve("crypto_pool", mem_size, >>>>> ODP_CACHE_LINE_SIZE); >>>>> + >>>>> + /* Clear it out */ >>>>> + memset(global, 0, mem_size); >>>>> + >>>>> + /* Initialize it */ >>>>> + global->max = max_sessions; >>>>> + >>>>> + return 0; >>>>> +} >>>>> + >>>>> +int >>>>> +odp_hw_random_get(uint8_t *buf, size_t *len, bool use_entropy >>>>> ODP_UNUSED) >>>>> +{ >>>>> + int rc; >>>>> + rc = RAND_bytes(buf, *len); >>>>> + return ((1 == rc) ? 0 : -1); >>>>> +} >>>>> + >>>>> +void >>>>> +odp_crypto_get_operation_compl_status(odp_buffer_t completion_event, >>>>> + struct odp_crypto_compl_status >>>>> *auth, >>>>> + struct odp_crypto_compl_status >>>>> *cipher) >>>>> +{ >>>>> + struct odp_operation_result_s *result; >>>>> + >>>>> + result = get_op_result_from_buffer(completion_event); >>>>> + >>>>> + if (OP_RESULT_MAGIC != result->magic) >>>>> + abort(); >>>>> + >>>>> + memcpy(auth, &result->auth, sizeof(*auth)); >>>>> + memcpy(cipher, &result->cipher, sizeof(*cipher)); >>>>> +} >>>>> + >>>>> +void >>>>> +odp_crypto_get_ses_create_compl_status(odp_buffer_t completion_event, >>>>> + enum odp_crypto_ses_create_err >>>>> *status) >>>>> +{ >>>>> + struct odp_session_result_s *result; >>>>> + >>>>> + result = odp_buffer_addr(completion_event); >>>>> + *status = result->rc; >>>>> +} >>>>> + >>>>> +void >>>>> +odp_crypto_get_ses_create_compl_session(odp_buffer_t >>>>> completion_event, >>>>> + odp_crypto_session_t *session) >>>>> +{ >>>>> + struct odp_session_result_s *result; >>>>> + >>>>> + result = odp_buffer_addr(completion_event); >>>>> + *session = result->session; >>>>> +} >>>>> -- >>>>> 1.9.2 >>>>> >>>>> >>>>> _______________________________________________ >>>>> lng-odp mailing list >>>>> lng-odp@lists.linaro.org >>>>> http://lists.linaro.org/mailman/listinfo/lng-odp >>>>> >>>> _______________________________________________ >>> lng-odp mailing list >>> lng-odp@lists.linaro.org >>> http://lists.linaro.org/mailman/listinfo/lng-odp >>> >> >> >> _______________________________________________ >> lng-odp mailing list >> lng-odp@lists.linaro.org >> http://lists.linaro.org/mailman/listinfo/lng-odp >> > > > > -- > *Mike Holmes* > Linaro Technical Manager / Lead > LNG - ODP > > _______________________________________________ > lng-odp mailing list > lng-odp@lists.linaro.org > http://lists.linaro.org/mailman/listinfo/lng-odp > >
diff --git a/include/odp_crypto.h b/include/odp_crypto.h new file mode 100644 index 0000000..725e8f1 --- /dev/null +++ b/include/odp_crypto.h @@ -0,0 +1,295 @@ +/* Copyright (c) 2013, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +/** + * @file + * + * ODP crypto + */ + +#ifndef ODP_CRYPTO_H_ +#define ODP_CRYPTO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <odp_std_types.h> +#include <odp_buffer.h> +#include <odp_buffer_pool.h> +#include <odp_queue.h> +#include <odp_packet.h> + +/** Invalid session handle */ +#define ODP_CRYPTO_SESSION_INVALID (-1ULL) + +/** + * Crypto API opaque session handle + */ +typedef uint64_t odp_crypto_session_t; + +/** + * Crypto API operation mode + */ +enum odp_crypto_op_mode { + ODP_CRYPTO_SYNC, /**< Synchronous, return results immediately */ + ODP_CRYPTO_ASYNC, /**< Aynchronous, return results via posted event */ +}; + +/** + * Crypto API operation type + */ +enum odp_crypto_op { + ODP_CRYPTO_OP_ENCODE, /**< Encrypt and/or compute authentication ICV */ + ODP_CRYPTO_OP_DECODE /**< Decrypt and/or verify authentication ICV */ +}; + +/** + * Crypto API cipher algorithm + */ +enum odp_cipher_alg { + ODP_CIPHER_ALG_NULL, /**< No cipher algorithm specified */ + ODP_CIPHER_ALG_DES, /**< DES */ + ODP_CIPHER_ALG_3DES_CBC, /**< Triple DES with cipher block chaining */ +}; + +/** + * Crypto API authentication algorithm + */ +enum odp_auth_alg { + ODP_AUTH_ALG_NULL, /**< No authentication algorithm specified */ + ODP_AUTH_ALG_MD5_96, /**< HMAC-MD5 with 96 bit key */ +}; + +/** + * Crypto API operation order + */ +enum odp_crypto_combination { + ODP_CRYPTO_CIPHER_ONLY, /**< Only perform cipher */ + ODP_CRYPTO_AUTH_ONLY, /**< Only perform authentication */ + ODP_CRYPTO_AUTH_CIPHERTEXT /**< Cipher then authentication on encode */ +}; + +/** + * Crypto API key + */ +typedef struct odp_key_s { + union { + /** DES/3DES key definition (set all same for DES) */ + struct { + uint8_t k1[8]; /**< First key */ + uint8_t k2[8]; /**< Second key */ + uint8_t k3[8]; /**< Third key */ + } des; + /** MD5 key */ + struct { + uint8_t key[16]; /**< Key up to 128 bits */ + } md5; + }; +} odp_key_t; + +/** + * Crypto API data range specifier + */ +struct odp_data_range { + uint16_t offset; /**< Offset from beginning of buffer (chain) */ + uint16_t length; /**< Length of data to operate on */ +}; + +/** + * Crypto API session creation paramters + * + * TODO: add "odp_session_proc_info_t" + */ +struct odp_crypto_session_params { + enum odp_crypto_op op; /**< Encode versus decode */ + enum odp_crypto_combination comb; /**< Operation order */ + enum odp_crypto_op_mode pref_mode; /**< Preferred sync vs async */ + enum odp_cipher_alg cipher_alg; /**< Cipher algorithm */ + odp_key_t *cipher_key; /**< Cipher key */ + uint8_t *iv; /**< Cipher Initialization Vector (IV) */ + size_t iv_len; /**< Cipher IV length */ + enum odp_auth_alg auth_alg; /**< Authentication algorithm */ + odp_key_t *auth_key; /**< Authentication key */ + odp_queue_t compl_queue; /**< Async mode completion event queue */ +}; + +/** + * Crypto API per packet operation parameters + */ +struct odp_crypto_op_params { + odp_crypto_session_t session; /**< Session handle from creation */ + odp_packet_t pkt; /**< Input packet buffer */ + odp_packet_t out_pkt; /**< Output packet buffer (optional) */ + uint8_t *override_iv_ptr; /**< Override session IV pointer */ + unsigned hash_result_offset; /**< Offset from start of packet buffer for hash result */ + struct odp_data_range cipher_range; /**< Data range to apply cipher */ + struct odp_data_range auth_range; /**< Data range to authenticate */ +}; + +/** + * Crypto API session creation return code + * + * TODO: seems confusing, maybe _rc instead + */ +enum odp_crypto_ses_create_err { + ODP_CRYPTO_SES_CREATE_NONE, /**< No session created? need to clarify */ + ODP_CRYPTO_SES_CREATE_OK, /**< Session created successfully */ +}; + +/** + * Crypto API algorithm return code + */ +enum crypto_alg_err { + ODP_CRYPTO_ALG_ERR_NONE, /**< Algorithm successful */ + ODP_CRYPTO_ALG_ERR_DATA_SIZE, /**< Invalid data block size */ + ODP_CRYPTO_ALG_ERR_KEY_SIZE, /**< Key size invalid for algorithm */ + ODP_CRYPTO_ALG_ERR_ICV_CHECK, /**< Computed ICV value mismatch */ +}; + +/** + * Crypto API operation return code + */ +typedef enum odp_crypto_rc { + ODP_CRYPTO_OP_OK, /**< Operation completed, results are valid */ + ODP_CRYPTO_OP_POSTED, /**< Operation was posted, results delivered via completion queue */ + ODP_CRYPTO_OP_ERROR, /**< Operation failed */ +} odp_crypto_rc_e; + +/** + * Crypto API hardware centric return code + */ +enum crypto_hw_err { + ODP_CRYPTO_HW_ERR_NONE, /**< Operation completed successfully */ + ODP_CRYPTO_HW_ERR_DMA, /**< Error detected during DMA of data */ + ODP_CRYPTO_HW_ERR_BP_DEPLETED, /**< Operation failed due to buffer pool depletion */ +}; + +/** + * Crypto API algorithm (cipher or authentication) + */ +typedef union odp_crypto_alg_u { + enum odp_cipher_alg cipher; /**< Cipher algorithm */ + enum odp_auth_alg auth; /**< Authentication algorithm */ +} odp_crypto_alg_t; + +/** + * Cryto API per packet operation completion status + */ +struct odp_crypto_compl_status { + odp_crypto_alg_t alg; /**< Requested algorithm */ + enum crypto_alg_err alg_err; /**< Algorithm specific return code */ + enum crypto_hw_err hw_err; /**< Hardware specific return code */ +}; + + +/** + * Crypto session creation + * + * Create a crypto session. Operation occurs asynchronously if a completion + * queue is specified else synchronously. + * + * @param params Session parameters + * @param completion_event Event by which the session creation results are + * delivered. + * @param completion_queue Queue by which the completion event will be + * delivered. Ignored if ODP_QUEUE_INVALID. + * + * @return Operation return code indicating success or failure for + * when synchronous operation requested, else POSTED when + * asynchronous operation is requested. + */ +odp_crypto_rc_e +odp_crypto_session_create(struct odp_crypto_session_params *params, + odp_buffer_t completion_event, + odp_queue_t completion_queue); + + +/** + * Crypto session creation completion status + * + * Accessor function for obtaining creation status from the completion event. + * + * @param completion_event Event containing operation results + * @param status Pointer to store creation return code + */ +void +odp_crypto_get_ses_create_compl_status(odp_buffer_t completion_event, + enum odp_crypto_ses_create_err *status); + +/** + * Crypto session creation completion return value + * + * Accessor function for obtaining handle for newly created session. + * + * @param completion_event Event containing operation results + * @param session Pointer to store session handle + */ +void +odp_crypto_get_ses_create_compl_session(odp_buffer_t completion_event, + odp_crypto_session_t *session); + +/** + * Crypto per packet operation + * + * Performs the cryptographic operations specified during session creation + * on the packet. + * + * @param params Operation parameters + * @param completion_event Event by which the session creation results are + * delivered. + * + * @return Operation return code indicating success or failure when session + * indicates synchronous operation, else POSTED for asynchronous + * operation. + */ +odp_crypto_rc_e +odp_crypto_operation(struct odp_crypto_op_params *params, + odp_buffer_t completion_event); + + +/** + * Crypto per packet operation completion status + * + * Accessor function for obtaining operation status from the completion event. + * + * @param completion_event Event containing operation results + * @param auth Pointer to store authentication results + * @param cipher Pointer to store cipher results + */ +void +odp_crypto_get_operation_compl_status(odp_buffer_t completion_event, + struct odp_crypto_compl_status *auth, + struct odp_crypto_compl_status *cipher); + +/** + * Generate random byte string + * + * @param buf Pointer to store result + * @param len Pointer to input length value as well as return value + * @param use_entropy (TODO: needs description) + * + * @return 0 if succesful + */ +int +odp_hw_random_get(uint8_t *buf, size_t *len, bool use_entropy); + +/** + * Initialize the crypto subsystem, called once from main thread + * + * @param max_sessions Maximum number of sessions to support + * + * @return 0 if succesful + */ +int +odp_crypto_init(uint32_t max_sessions); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/linux-generic/Makefile b/platform/linux-generic/Makefile index ec5d4a7..57f6d18 100644 --- a/platform/linux-generic/Makefile +++ b/platform/linux-generic/Makefile @@ -72,6 +72,7 @@ OBJS += $(OBJ_DIR)/odp_time.o OBJS += $(OBJ_DIR)/odp_timer.o OBJS += $(OBJ_DIR)/odp_ring.o OBJS += $(OBJ_DIR)/odp_rwlock.o +OBJS += $(OBJ_DIR)/odp_crypto.o ifeq ($(ODP_HAVE_NETMAP),yes) OBJS += $(OBJ_DIR)/odp_packet_netmap.o endif diff --git a/platform/linux-generic/include/odp_crypto_internal.h b/platform/linux-generic/include/odp_crypto_internal.h new file mode 100644 index 0000000..e558864 --- /dev/null +++ b/platform/linux-generic/include/odp_crypto_internal.h @@ -0,0 +1,86 @@ +/* Copyright (c) 2013, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + + +#ifndef ODP_CRYPTO_INTERNAL_H_ +#define ODP_CRYPTO_INTERNAL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <openssl/des.h> + +#define OP_RESULT_MAGIC 0x91919191 + +/** Forward declaration of session structure */ +struct odp_crypto_session_s; + +/** + * Algorithm handler function prototype + */ +typedef +enum crypto_alg_err (*crypto_func_t)(struct odp_crypto_op_params *params, + struct odp_crypto_session_s *session); + +/** + * Per crypto session data structure + */ +struct odp_crypto_session_s { + uint32_t index; + enum odp_crypto_op op; + enum odp_crypto_combination comb; + odp_queue_t compl_queue; + struct { + enum odp_cipher_alg alg; + struct { + uint8_t *data; + size_t len; + } iv; + union { + struct { + DES_key_schedule ks1; + DES_key_schedule ks2; + DES_key_schedule ks3; + } des; + } data; + crypto_func_t func; + } cipher; + struct { + enum odp_auth_alg alg; + union { + struct { + uint8_t key[16]; + } md5; + } data; + crypto_func_t func; + } auth; + +}; + +/** + * Per packet operation result + */ +struct odp_operation_result_s { + uint32_t magic; + struct odp_crypto_compl_status cipher; + struct odp_crypto_compl_status auth; +}; + +/** + * Per session creation operation result + */ +struct odp_session_result_s { + enum odp_crypto_ses_create_err rc; + odp_crypto_session_t session; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/linux-generic/source/odp_crypto.c b/platform/linux-generic/source/odp_crypto.c new file mode 100644 index 0000000..4ae3645 --- /dev/null +++ b/platform/linux-generic/source/odp_crypto.c @@ -0,0 +1,435 @@ +/* Copyright (c) 2013, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <odp_crypto.h> +#include <odp_internal.h> +#include <odp_atomic.h> +#include <odp_spinlock.h> +#include <odp_sync.h> +#include <odp_debug.h> +#include <odp_align.h> +#include <odp_shared_memory.h> +#include <odp_crypto_internal.h> + +#include <string.h> + +#include <openssl/des.h> +#include <openssl/rand.h> +#include <openssl/hmac.h> +#include <openssl/evp.h> + +#define MAX_SESSIONS 32 + +typedef struct { + odp_atomic_u32_t next; + uint32_t max; + struct odp_crypto_session_s sessions[0]; +} odp_crypto_global_t; + +static odp_crypto_global_t *global; + +/* + * TODO: This is a serious hack to allow us to use packet buffer to convey + * crypto operation results by placing them at the very end of the + * packet buffer. + */ +static +struct odp_operation_result_s *get_op_result_from_buffer(odp_buffer_t buf) +{ + uint8_t *temp; + struct odp_operation_result_s *result; + + temp = odp_buffer_addr(buf); + temp += odp_buffer_size(buf); + temp -= sizeof(*result); + result = (struct odp_operation_result_s *)(void *)temp; + return result; +} + +static +struct odp_crypto_session_s *alloc_session(void) +{ + uint32_t idx; + struct odp_crypto_session_s *session = NULL; + + idx = odp_atomic_fetch_inc_u32(&global->next); + if (idx < global->max) { + session = &global->sessions[idx]; + session->index = idx; + } + return session; +} + +static +enum crypto_alg_err null_crypto_routine( + struct odp_crypto_op_params *params ODP_UNUSED, + struct odp_crypto_session_s *session ODP_UNUSED) +{ + return ODP_CRYPTO_ALG_ERR_NONE; +} + +static +enum crypto_alg_err md5_gen(struct odp_crypto_op_params *params, + struct odp_crypto_session_s *session) +{ + uint8_t *data = odp_packet_buf_addr(params->pkt); + uint8_t *icv = data; + uint32_t len = params->auth_range.length; + uint8_t hash[EVP_MAX_MD_SIZE]; + uint32_t hlen = 12; + + /* Adjust pointer for beginning of area to auth */ + data += params->auth_range.offset; + icv += params->hash_result_offset; + + /* Hash it */ + HMAC(EVP_md5(), session->auth.data.md5.key, 16, data, len, hash, &hlen); + + /* Copy to the output location */ + memcpy(icv, hash, 12); + + return ODP_CRYPTO_ALG_ERR_NONE; +} + + +static +enum crypto_alg_err md5_check(struct odp_crypto_op_params *params, + struct odp_crypto_session_s *session) +{ + uint8_t *data = odp_packet_buf_addr(params->pkt); + uint8_t *icv = data; + uint32_t len = params->auth_range.length; + uint8_t hash[EVP_MAX_MD_SIZE]; + uint32_t hlen = 12; + + /* Adjust pointer for beginning of area to auth */ + data += params->auth_range.offset; + icv += params->hash_result_offset; + + /* Copy current value out and clear it before authentication */ + memcpy(hash, icv, hlen); + memset(icv, 0, hlen); + + /* Hash it */ + HMAC(EVP_md5(), session->auth.data.md5.key, 16, data, len, icv, &hlen); + + /* Verify match */ + if (0 != memcmp(icv, hash, 12)) + return ODP_CRYPTO_ALG_ERR_ICV_CHECK; + + /* Matched */ + return ODP_CRYPTO_ALG_ERR_NONE; +} + +static +enum crypto_alg_err des_encrypt(struct odp_crypto_op_params *params, + struct odp_crypto_session_s *session) +{ + uint8_t *data = odp_packet_buf_addr(params->pkt); + uint32_t len = params->cipher_range.length; + DES_cblock *iv = (DES_cblock *)session->cipher.iv.data; + + /* Adjust pointer for beginning of area to cipher */ + data += params->cipher_range.offset; + + /* Override IV if requested */ + if (params->override_iv_ptr) + iv = (DES_cblock *)params->override_iv_ptr; + + /* Encrypt it */ + DES_ede3_cbc_encrypt(data, + data, + len, + &session->cipher.data.des.ks1, + &session->cipher.data.des.ks2, + &session->cipher.data.des.ks3, + iv, + 1); + + return ODP_CRYPTO_ALG_ERR_NONE; +} + +static +enum crypto_alg_err des_decrypt(struct odp_crypto_op_params *params, + struct odp_crypto_session_s *session) +{ + uint8_t *data = odp_packet_buf_addr(params->pkt); + uint32_t len = params->cipher_range.length; + DES_cblock *iv = (DES_cblock *)session->cipher.iv.data; + + /* Adjust pointer for beginning of area to cipher */ + data += params->cipher_range.offset; + + /* Override IV if requested */ + if (params->override_iv_ptr) + iv = (DES_cblock *)params->override_iv_ptr; + + /* Decrypt it */ + DES_ede3_cbc_encrypt(data, + data, + len, + &session->cipher.data.des.ks1, + &session->cipher.data.des.ks2, + &session->cipher.data.des.ks3, + iv, + 0); + + return ODP_CRYPTO_ALG_ERR_NONE; +} + +static +int process_des_params(struct odp_crypto_session_s *session, + struct odp_crypto_session_params *params) +{ + /* Verify IV len is either 0 or 8 */ + if (!((0 == params->iv_len) || (8 == params->iv_len))) + return -1; + + /* Verify IV pointer */ + if (params->iv_len && !params->iv) + return -1; + + /* Set function */ + if (ODP_CRYPTO_OP_ENCODE == params->op) + session->cipher.func = des_encrypt; + else + session->cipher.func = des_decrypt; + + /* Convert keys */ + DES_set_key(¶ms->cipher_key->des.k1, &session->cipher.data.des.ks1); + DES_set_key(¶ms->cipher_key->des.k2, &session->cipher.data.des.ks2); + DES_set_key(¶ms->cipher_key->des.k3, &session->cipher.data.des.ks3); + + return 0; +} + +static +int process_md5_params(struct odp_crypto_session_s *session, + struct odp_crypto_session_params *params) +{ + /* Set function */ + if (ODP_CRYPTO_OP_ENCODE == params->op) + session->auth.func = md5_gen; + else + session->auth.func = md5_check; + + /* Convert keys */ + memcpy(session->auth.data.md5.key, params->auth_key->md5.key, 16); + + return 0; +} + +odp_crypto_rc_e +odp_crypto_session_create(struct odp_crypto_session_params *params, + odp_buffer_t completion_event, + odp_queue_t completion_queue) +{ + int rc; + struct odp_crypto_session_s *session; + struct odp_session_result_s *result = odp_buffer_addr(completion_event); + + /* Default to failure result */ + result->rc = ODP_CRYPTO_SES_CREATE_NONE; + result->session = ODP_CRYPTO_SESSION_INVALID; + + /* Allocate memory for this session */ + session = alloc_session(); + if (NULL == session) + return ODP_CRYPTO_OP_ERROR; + + /* Copy stuff over */ + session->op = params->op; + session->comb = params->comb; + session->compl_queue = params->compl_queue; + session->cipher.alg = params->cipher_alg; + session->cipher.iv.data = params->iv; + session->cipher.iv.len = params->iv_len; + session->auth.alg = params->auth_alg; + + /* Process based on cipher */ + switch (params->cipher_alg) { + case ODP_CIPHER_ALG_NULL: + session->cipher.func = null_crypto_routine; + rc = 0; + break; + case ODP_CIPHER_ALG_DES: + case ODP_CIPHER_ALG_3DES_CBC: + rc = process_des_params(session, params); + break; + default: + rc = -1; + } + + /* Check result */ + if (rc) + return ODP_CRYPTO_OP_ERROR; + + /* Process based on auth */ + switch (params->auth_alg) { + case ODP_AUTH_ALG_NULL: + session->auth.func = null_crypto_routine; + rc = 0; + break; + case ODP_AUTH_ALG_MD5_96: + rc = process_md5_params(session, params); + break; + default: + rc = -1; + } + + /* Check result */ + if (rc) + return ODP_CRYPTO_OP_ERROR; + + /* We're happy */ + result->rc = ODP_CRYPTO_SES_CREATE_OK; + result->session = (intptr_t)session; + + /* If there is a queue post else we're good */ + if (ODP_QUEUE_INVALID != completion_queue) { + odp_queue_enq(completion_queue, completion_event); + return ODP_CRYPTO_OP_POSTED; + } + + return ODP_CRYPTO_OP_OK; +} + + +odp_crypto_rc_e +odp_crypto_operation(struct odp_crypto_op_params *params, + odp_buffer_t completion_event) +{ + enum crypto_alg_err rc_cipher = ODP_CRYPTO_ALG_ERR_NONE; + enum crypto_alg_err rc_auth = ODP_CRYPTO_ALG_ERR_NONE; + struct odp_crypto_session_s *session; + struct odp_operation_result_s *result; + + session = (struct odp_crypto_session_s *)(intptr_t)params->session; + + /* + * robking: need to understand infrastructure for scattered packets + * for now just don't support them + */ + if (odp_buffer_is_scatter(odp_buffer_from_packet(params->pkt))) + return ODP_CRYPTO_OP_ERROR; + + /* + * robking: for now we are only going to support in place + */ + if (params->pkt != params->out_pkt) + return ODP_CRYPTO_OP_ERROR; + + /* Invoke the functions */ + switch (session->comb) { + case ODP_CRYPTO_CIPHER_ONLY: + rc_cipher = session->cipher.func(params, session); + break; + case ODP_CRYPTO_AUTH_ONLY: + rc_auth = session->auth.func(params, session); + break; + case ODP_CRYPTO_AUTH_CIPHERTEXT: + if (ODP_CRYPTO_OP_ENCODE == session->op) { + rc_cipher = session->cipher.func(params, session); + rc_auth = session->auth.func(params, session); + } else { + rc_auth = session->auth.func(params, session); + rc_cipher = session->cipher.func(params, session); + } + break; + } + + /* Build Result (no HW so no errors) */ + result = get_op_result_from_buffer(completion_event); + result->magic = OP_RESULT_MAGIC; + result->cipher.alg.cipher = session->cipher.alg; + result->cipher.alg_err = rc_cipher; + result->cipher.hw_err = ODP_CRYPTO_HW_ERR_NONE; + result->auth.alg.auth = session->auth.alg; + result->auth.alg_err = rc_auth; + result->auth.hw_err = ODP_CRYPTO_HW_ERR_NONE; + + /* + * robking: a) the queue is supposed to come from session + * b) ordering question asks whether we must + * use the packet to return status + */ + if (ODP_QUEUE_INVALID != session->compl_queue) { + odp_queue_enq(session->compl_queue, completion_event); + return ODP_CRYPTO_OP_POSTED; + } + + return ODP_CRYPTO_OP_OK; +} + + +int +odp_crypto_init(uint32_t max_sessions) +{ + size_t mem_size; + + /* Force down to our limit */ + if (MAX_SESSIONS < max_sessions) + max_sessions = MAX_SESSIONS; + + /* Calculate the memory size we need */ + mem_size = sizeof(*global); + mem_size += (max_sessions * sizeof(struct odp_crypto_session_s)); + + /* Allocate our globally shared memory */ + global = odp_shm_reserve("crypto_pool", mem_size, ODP_CACHE_LINE_SIZE); + + /* Clear it out */ + memset(global, 0, mem_size); + + /* Initialize it */ + global->max = max_sessions; + + return 0; +} + +int +odp_hw_random_get(uint8_t *buf, size_t *len, bool use_entropy ODP_UNUSED) +{ + int rc; + rc = RAND_bytes(buf, *len); + return ((1 == rc) ? 0 : -1); +} + +void +odp_crypto_get_operation_compl_status(odp_buffer_t completion_event, + struct odp_crypto_compl_status *auth, + struct odp_crypto_compl_status *cipher) +{ + struct odp_operation_result_s *result; + + result = get_op_result_from_buffer(completion_event); + + if (OP_RESULT_MAGIC != result->magic) + abort(); + + memcpy(auth, &result->auth, sizeof(*auth)); + memcpy(cipher, &result->cipher, sizeof(*cipher)); +} + +void +odp_crypto_get_ses_create_compl_status(odp_buffer_t completion_event, + enum odp_crypto_ses_create_err *status) +{ + struct odp_session_result_s *result; + + result = odp_buffer_addr(completion_event); + *status = result->rc; +} + +void +odp_crypto_get_ses_create_compl_session(odp_buffer_t completion_event, + odp_crypto_session_t *session) +{ + struct odp_session_result_s *result; + + result = odp_buffer_addr(completion_event); + *session = result->session; +}
Initial linux-generic crypto implementation. Note that these changes require installing and linking with libssl-dev. The camel case warnings are also due to calls into libssl-dev. v1..v2: Fixed ARM compile warning, and modified "odp_hw_random_get" to take length as "size_t" instead of "uint32_t". Signed-off-by: Robbie King <robking@cisco.com> --- include/odp_crypto.h | 295 ++++++++++++++ platform/linux-generic/Makefile | 1 + .../linux-generic/include/odp_crypto_internal.h | 86 ++++ platform/linux-generic/source/odp_crypto.c | 435 +++++++++++++++++++++ 4 files changed, 817 insertions(+) create mode 100644 include/odp_crypto.h create mode 100644 platform/linux-generic/include/odp_crypto_internal.h create mode 100644 platform/linux-generic/source/odp_crypto.c