/* ipmacsec_crypto_if.c - MACsec Crypto Interface implementation */ /* * Copyright (c) 2010 Wind River Systems, Inc. * * The right to copy, distribute, modify, or otherwise make use * of this software may be licensed only pursuant to the terms * of an applicable Wind River license agreement. * */ /* modification history -------------------- 01a,19feb10,mry Created */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* * Implements the MACsec Cryptography Interface Module which abstracts the * cryptographic operations of the selected cipher. */ /* **************************************************************************** * 2 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include #include #include #include #include "ipmacsec_debug.h" #include "ipmacsec_crypto_if.h" #include "ipmacsec_h.h" #include "ipmacsec.h" #include "ipmacsec_sysvar.h" #include "openssl/aes.h" #include "openssl/evp.h" #include "openssl/aes_gcm.h" /* **************************************************************************** * 3 CONFIGURATION **************************************************************************** */ #include "ipmacsec_config.h" /* **************************************************************************** * 4 DEFINES **************************************************************************** */ #define IPMACSEC_CRYPTO_IVBITS ((IPMACSEC_PN_LEN+IPMACSEC_SCI_LEN)*8) /* in bits */ #define IPMACSEC_CRYPTO_IVLEN (IPMACSEC_CRYPTO_IVBITS/8) /* in octets */ /* By default, use the Max acceleration level */ #define IPMACSEC_DEFAULT_GCMAES_ACCEL_LEVEL 4 /* **************************************************************************** * 5 TYPES **************************************************************************** */ /* Cipher context */ typedef struct { void *cs_ctx; /* cipher specific context */ IP_CONST Ip_u8 *key; IP_CONST Ip_u8 *iv; IP_CONST Ip_u8 *aad; /* Additional Authenticated data */ Ip_u8 *pt; /* plain text */ Ip_u8 *ct; /* cipher text */ Ip_u8 *tag; /* Authentication Tag (T), i.e. ICV */ Ip_u32 keylen; /* in octets */ Ip_u32 ivlen; /* in octets */ Ip_u32 aadlen; /* in octets */ Ip_u32 ptlen; /* in octets ; maybe updated by cipher suite when decrypting*/ Ip_u32 ctlen; /* in octets; maybe updated by cipher suite when encrypting*/ Ip_u32 taglen; /* in octets; maybe updated by cipher suite */ Ip_bool confidentiality; /* Request confidentiality (encryption/decryption) */ Ip_u32 confidentialityOffset; /* # of octets to skip during confidentiality operations if applicable */ Ip_bool valid; /* Updated by cipher validate function */ union { struct { GCM_ACCEL accel_level; /* Acceleration level */ } gcmaes; } cipher_options; /* cipher specific options */ } Ipmacsec_cipher_ctx; /* Cipher specific structure. See 802.1AE section 10.7.24 */ typedef struct Ipmacsec_crypto_cipher_struct { Ipmacsec_ciphersuite_descriptor desc; /* Cipher suite descriptor */ int (* init)(IP_CONST struct Ipmacsec_crypto_cipher_struct *cipher, void **cs_ctx); int (* destroy)(IP_CONST struct Ipmacsec_crypto_cipher_struct *cipher, void **cs_ctx); int (* protect)(IP_CONST struct Ipmacsec_crypto_cipher_struct *cipher, Ipmacsec_cipher_ctx *ctx); int (* validate)(IP_CONST struct Ipmacsec_crypto_cipher_struct *cipher, Ipmacsec_cipher_ctx *ctx); } Ipmacsec_crypto_cipher; /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ IP_STATIC int ipmacsec_crypto_init(struct Ipmacsec_crypto_if_struct *crypto, Ip_u8 key_index, Ipmacsec_secy_sak *sak); IP_STATIC int ipmacsec_crypto_destroy(struct Ipmacsec_crypto_if_struct *crypto, Ip_u8 key_index); IP_STATIC int ipmacsec_crypto_protect(Ipmacsec_crypto_if *crypto, Ipmacsec_crypto_ctx *ctx); IP_STATIC int ipmacsec_crypto_validate(Ipmacsec_crypto_if *crypto, Ipmacsec_crypto_ctx *ctx, Ip_bool *valid); IP_STATIC Ip_bool ipmacsec_crypto_verify(Ipnet_secy *secy); IP_STATIC Ipmacsec_crypto_cipher IP_CONST *ipmacsec_crypto_find_cipher(IP_CONST Ipmacsec_cipher_suite_index cipher_suite_index); IP_STATIC Ip_bool ipmacsec_crypto_gcm_aes_config_verify(Ipmacsec_crypto_if IP_CONST *crypto); /* Cipher specific functions */ IP_STATIC int ipmacsec_gcm_aes_128_init(IP_CONST Ipmacsec_crypto_cipher *cipher, void **cs_ctx); IP_STATIC int ipmacsec_gcm_aes_128_destroy(IP_CONST Ipmacsec_crypto_cipher *cipher, void **cs_ctx); IP_STATIC int ipmacsec_gcm_aes_128_protect(IP_CONST Ipmacsec_crypto_cipher *cipher, Ipmacsec_cipher_ctx *ctx); IP_STATIC int ipmacsec_gcm_aes_128_validate(IP_CONST Ipmacsec_crypto_cipher *cipher, Ipmacsec_cipher_ctx *ctx); #ifdef IPMACSEC_CRYPTO_ALLOW_NULL_CIPHER /* Cipher specific functions */ IP_STATIC int ipmacsec_null_cipher_128_init(IP_CONST Ipmacsec_crypto_cipher *cipher, void **cs_ctx); IP_STATIC int ipmacsec_null_cipher_128_destroy(IP_CONST Ipmacsec_crypto_cipher *cipher, void **cs_ctx); IP_STATIC int ipmacsec_null_cipher_128_protect(IP_CONST Ipmacsec_crypto_cipher *cipher, Ipmacsec_cipher_ctx *ctx); IP_STATIC int ipmacsec_null_cipher_128_validate(IP_CONST Ipmacsec_crypto_cipher *cipher, Ipmacsec_cipher_ctx *ctx); #endif /* * **************************************************************************** * 8 DATA **************************************************************************** */ /* Supported IPMACSEC Cipher Suites */ IP_STATIC IP_CONST Ipmacsec_crypto_cipher ipmacsec_ciphers[] = { { { "GCM-AES-128", IPMACSEC_CIPHER_SUITE_ID_GCM_AES_128, IPMACSEC_CIPHERSUITE_GCM_AES_128, IP_TRUE /*integrity*/ , IP_TRUE /*confidentiality*/, IP_TRUE /*offsetConfidentiality*/, IP_FALSE /*changesDataLength*/, IP_TRUE /*mandatory*/, 128 /*key_bits*/, 128 /*icv_bits*/, 1 /*min_iv_bits*/, 0xFFFFFFFFFFFFFFFFLL/*max_iv_bits*/ }, ipmacsec_gcm_aes_128_init, ipmacsec_gcm_aes_128_destroy, ipmacsec_gcm_aes_128_protect, ipmacsec_gcm_aes_128_validate } #ifdef IPMACSEC_CRYPTO_ALLOW_NULL_CIPHER #warning IPMACSEC_CRYPTO_ALLOW_NULL_CIPHER is turned on. USE ONLY FOR DEVELOPMENT TESTING , { { "NULL-CIPHER-128", { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, IPMACSEC_CIPHERSUITE_NULL_CIPHER_128, IP_TRUE /*integrity*/ , IP_TRUE /*confidentiality*/, IP_TRUE /*offsetConfidentiality*/, IP_FALSE /*changesDataLength*/, IP_FALSE /*mandatory*/, 128 /*key_bits*/, 128 /*icv_bits*/, 1 /*min_iv_bits*/, 0xFFFFFFFFFFFFFFFFLL/*max_iv_bits*/ }, ipmacsec_null_cipher_128_init, ipmacsec_null_cipher_128_destroy, ipmacsec_null_cipher_128_protect, ipmacsec_null_cipher_128_validate } #endif }; IP_STATIC Ip_u32 ipmacsec_num_ciphers = sizeof(ipmacsec_ciphers)/sizeof(ipmacsec_ciphers[0]); /* **************************************************************************** * 9 PRIVATE FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipmacsec_crypto_cipher_suite_supported *=========================================================================== * Description: See "ipmacsec_crypto_if.h" * Parameters: * Returns: * */ IPMACSEC_PRIVATE Ip_bool ipmacsec_crypto_cipher_suite_supported(IP_CONST Ipmacsec_cipher_suite_index cipher_suite_index) { if (!IPMACSEC_SECY_VERIFY_CIPHER_SUITE_INDEX(cipher_suite_index)) { return IP_FALSE; } return (ipmacsec_crypto_find_cipher(cipher_suite_index) != IP_NULL); } /* *=========================================================================== * ipmacsec_crypto_cipher_suite_get_info *=========================================================================== * Description: See "ipmacsec_crypto_if.h" * Parameters: * Returns: * */ IPMACSEC_PRIVATE int ipmacsec_crypto_cipher_suite_get_info(IP_CONST Ipmacsec_cipher_suite_index cipher_suite_index, Ipmacsec_ciphersuite_descriptor *info) { Ipmacsec_crypto_cipher IP_CONST *cipher = IP_NULL; ip_assert(info); if (!IPMACSEC_SECY_VERIFY_CIPHER_SUITE_INDEX(cipher_suite_index)) { return IP_FALSE; } cipher = ipmacsec_crypto_find_cipher(cipher_suite_index); if (cipher == IP_NULL) { return -1; } ipcom_memcpy(info, &cipher->desc, sizeof(Ipmacsec_ciphersuite_descriptor)); return 0; } /* *=========================================================================== * ipmacsec_crypto_cipher_suite_id_resolve *=========================================================================== * Description: See "ipmacsec_crypto_if.h" * Parameters: * Returns: */ IPMACSEC_PRIVATE int ipmacsec_crypto_cipher_suite_id_resolve(IP_CONST Ipmacsec_cipher_suite_index cipher_suite_index, Ip_u8 *cipher_suite_id) { Ipmacsec_crypto_cipher IP_CONST *cipher; ip_assert(cipher_suite_id); cipher = ipmacsec_crypto_find_cipher(cipher_suite_index); if (!cipher) { return -1; } IPMACSEC_COPY_CIPHER_SUITE_ID(cipher_suite_id, cipher->desc.id); return 0; } /* *=========================================================================== * ipmacsec_crypto_cipher_suite_name *=========================================================================== * Description: See "ipmacsec_crypto_if.h" * Parameters: * Returns: */ IPMACSEC_PRIVATE IP_CONST char * ipmacsec_crypto_cipher_suite_name(IP_CONST Ipmacsec_cipher_suite_index cipher_suite_index) { Ipmacsec_crypto_cipher IP_CONST *cipher; if (!IPMACSEC_SECY_VERIFY_CIPHER_SUITE_INDEX(cipher_suite_index)) { return "***Invalid Cipher***"; } cipher = ipmacsec_crypto_find_cipher(cipher_suite_index); if (!cipher) { return "***Unknown Cipher***"; } return cipher->desc.name; } /* *=========================================================================== * ipmacsec_crypto_cipher_suite_index_resolve *=========================================================================== * Description: See "ipmacsec_crypto_if.h" * Parameters: * Returns: */ IPMACSEC_PRIVATE int ipmacsec_crypto_cipher_suite_index_resolve(IP_CONST Ip_u8 *cipher_suite_id, Ipmacsec_cipher_suite_index *cipher_suite_index) { Ip_u32 i; ip_assert(cipher_suite_id); ip_assert(cipher_suite_index); *cipher_suite_index = __IPMACSEC_CIPHERSUITE_INVALID; for (i = 0; i < ipmacsec_num_ciphers; i++) { if (ipcom_memcmp(&ipmacsec_ciphers[i].desc.id, cipher_suite_id, IPMACSEC_CIPHER_SUITE_ID_LEN) == 0) { *cipher_suite_index = ipmacsec_ciphers[i].desc.index; return 0; } } return -1; } /* *=========================================================================== * ipmacsec_crypto_get_icvbits *=========================================================================== * Description: See "ipmacsec_crypto_if.h" * Parameters: * Returns: * */ IPMACSEC_PRIVATE Ip_u16 ipmacsec_crypto_get_icvbits(Ipnet_secy *secy) { Ipmacsec_crypto_cipher IP_CONST *cipher; ip_assert(secy); cipher = ipmacsec_crypto_find_cipher(secy->config.cipher_suite_index); if (!cipher) { return 0; } return (Ip_u16)cipher->desc.icv_bits; } /* *=========================================================================== * ipmacsec_crypto_create *=========================================================================== * Description: See "ipmacsec_crypto_if.h" * Parameters: * Returns: * */ IPMACSEC_PRIVATE Ipmacsec_crypto_if * ipmacsec_crypto_create(Ipnet_secy *secy) { Ipmacsec_crypto_if *crypto; Ipmacsec_crypto_cipher IP_CONST *cipher; ip_assert(secy); ip_assert(secy->netif); if (!ipmacsec_crypto_verify(secy)) { IPCOM_LOG1(CRIT, "IPMACSEC-CRYPTO: Invalid cipher suite configuration for interface %s", secy->netif->ipcom.name); return IP_NULL; } crypto = ipcom_calloc(1, sizeof(Ipmacsec_crypto_if)); if (!crypto) { IPCOM_LOG1(CRIT, "IPMACSEC-CRYPTO: Crypto memory allocation failed for interface %s", secy->netif->ipcom.name); return IP_NULL; } crypto->priv.secy = secy; crypto->priv.result_buf_len = secy->netif->ipcom.mtu+IPMACSEC_RESERVED_OCTETS+16; /* add 16 for any padding */ crypto->priv.result_buf = ipcom_malloc(crypto->priv.result_buf_len); if (crypto->priv.result_buf == IP_NULL) { ipcom_free(crypto); return IP_NULL; } cipher = ipmacsec_crypto_find_cipher(secy->config.cipher_suite_index); ip_assert(cipher); crypto->priv.cipher = cipher; crypto->icvbits = (Ip_u16)cipher->desc.icv_bits; crypto->changesDataLength = cipher->desc.changesDataLength; crypto->cipher_set = IP_TRUE; crypto->init_cipher = ipmacsec_crypto_init; crypto->destroy_cipher = ipmacsec_crypto_destroy; crypto->protect = ipmacsec_crypto_protect; crypto->validate = ipmacsec_crypto_validate; crypto->confidentialityOffset = secy->config.confidentialityOffset; crypto->gcmaes_accel_level = (Ip_u16)ipcom_sysvar_get_as_int(IPMACSEC_SYSVAR_GCMAES_ACCEL_LEVEL, IPMACSEC_DEFAULT_GCMAES_ACCEL_LEVEL); if (secy->config.cipher_suite_index == IPMACSEC_CIPHERSUITE_GCM_AES_128) { if (!ipmacsec_crypto_gcm_aes_config_verify(crypto)) { ipcom_free(crypto->priv.result_buf); ipcom_free(crypto); return IP_NULL; } } crypto->valid = IP_TRUE; #ifdef IPMACSEC_DEBUG_CRYPTO IPCOM_LOG2(INFO, "IPMACSEC-CRYPTO: Initialized SecY crypto cipher %s for interface %s", cipher->desc.name, secy->netif->ipcom.name); #endif return crypto; } /* *=========================================================================== * ipmacsec_crypto_delete *=========================================================================== * Description: See "ipmacsec_crypto_if.h" * Parameters: * Returns: * */ IPMACSEC_PRIVATE int ipmacsec_crypto_delete(struct Ipnet_secy_struct *secy, Ipmacsec_crypto_if *crypto) { Ip_u8 key_index; IPCOM_UNUSED_ARG(secy); ip_assert(secy); if (crypto) { if (crypto->priv.result_buf) { ipcom_memset(crypto->priv.result_buf, 0, crypto->priv.result_buf_len); ipcom_free(crypto->priv.result_buf); crypto->priv.result_buf = 0; crypto->priv.result_buf_len = 0; } for (key_index = 0 ; key_index < IPMACSEC_MAX_SA; key_index++) { crypto->destroy_cipher(crypto, key_index); } ipcom_memset(crypto, 0 , sizeof(Ipmacsec_crypto_if)); ipcom_free(crypto); } return 0; } /* **************************************************************************** * 10 STATIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipmacsec_crypto_find_cipher *=========================================================================== * Description: Find a cipher in the supported cipher list. * Parameters: * Returns: * */ IP_STATIC Ipmacsec_crypto_cipher IP_CONST * ipmacsec_crypto_find_cipher(IP_CONST Ipmacsec_cipher_suite_index cipher_suite_index) { Ip_u32 i; for (i = 0; i < ipmacsec_num_ciphers; i++) { if (ipmacsec_ciphers[i].desc.index == cipher_suite_index) { return &ipmacsec_ciphers[i]; } } return IP_NULL; } /* *=========================================================================== * ipmacsec_crypto_gcm_aes_config_verify *=========================================================================== * Description: Verify the relevant configuration values for GCM-AES. * Parameters: * Returns: * */ IP_STATIC Ip_bool ipmacsec_crypto_gcm_aes_config_verify(Ipmacsec_crypto_if IP_CONST *crypto) { ip_assert(crypto); switch (crypto->gcmaes_accel_level) { case GCM_ACCEL_noaccel: case GCM_ACCEL_level1: case GCM_ACCEL_level2: case GCM_ACCEL_level3: case GCM_ACCEL_level4: break; default: IPCOM_LOG1(CRIT, "IPMACSEC-CRYPTO: Invalid GCM-AES acceleration level %u", crypto->gcmaes_accel_level); return IP_FALSE; } return IP_TRUE; } /* *=========================================================================== * ipmacsec_crypto_verify *=========================================================================== * Description: Verify the Crypto Interface configuration. * Parameters: * Returns: * */ IP_STATIC Ip_bool ipmacsec_crypto_verify(Ipnet_secy *secy) { Ipmacsec_crypto_cipher IP_CONST *cipher = IP_NULL; if (!IPMACSEC_SECY_VERIFY_CIPHER_SUITE_INDEX(secy->config.cipher_suite_index)) { IPCOM_LOG1(CRIT, "IPMACSEC-CRYPTO: Invalid cipher suite selector specified (%u)", (Ip_u32)secy->config.cipher_suite_index); return IP_FALSE; } #ifdef IPMACSEC_CRYPTO_ALLOW_NULL_CIPHER if (secy->config.cipher_suite_index == IPMACSEC_CIPHERSUITE_NULL_CIPHER_128) { IPCOM_LOG1(WARNING,"IPMACSEC-SECY_CRTL: Cipher Suite NULL_CIPHER_128 selected for interface %s", secy->netif->ipcom.name); } #endif cipher = ipmacsec_crypto_find_cipher(secy->config.cipher_suite_index); if (!cipher) { IPCOM_LOG1(CRIT, "IPMACSEC-CRYPTO: No supported cipher suite for specified selector (%u)", (Ip_u32)secy->config.cipher_suite_index); return IP_FALSE; } /* for now since KaY is not setting the Id, we copy it. Later when Kay is supported we may need to check * for initialization using the id instead of the index */ ipcom_memcpy(secy->config.cipher_suite_id, cipher->desc.id, sizeof(cipher->desc.id)); if ((IPMACSEC_CRYPTO_IVBITS < cipher->desc.min_iv_bits) || (IPMACSEC_CRYPTO_IVBITS > cipher->desc.max_iv_bits)) { IPCOM_LOG4(CRIT, "IPMACSEC-CRYPTO: cipher %s has incompatible iv size (%u not between %llu-%llu bits)", cipher->desc.name, IPMACSEC_CRYPTO_IVBITS, cipher->desc.min_iv_bits, cipher->desc.max_iv_bits); return IP_FALSE; } if (secy->config.confidentialityOffset) { if (!IPMACSEC_SECY_VERIFY_CONFIDENTIALITY_OFFSET(secy->config.confidentialityOffset)) { IPCOM_LOG2(CRIT, "IPMACSEC-CRYPTO: Invalid confidentialityOffset (%u) for specified cipher suite selector (%u)", secy->config.confidentialityOffset, (Ip_u32)secy->config.cipher_suite_index); return IP_FALSE; } if (!cipher->desc.offsetConfidentiality) { IPCOM_LOG1(CRIT, "IPMACSEC-CRYPTO: offsetConfidentiality not supported for specified cipher suite selector (%u)", (Ip_u32)secy->config.cipher_suite_index); return IP_FALSE; } } return IP_TRUE; } /* *=========================================================================== * ipmacsec_crypto_init *=========================================================================== * Description: Initialize the Crypto Interface for the specified key. * Parameters: * Returns: * */ IP_STATIC int ipmacsec_crypto_init(struct Ipmacsec_crypto_if_struct *crypto, Ip_u8 key_index, Ipmacsec_secy_sak *sak) { ip_assert(crypto); ip_assert(key_index < IPMACSEC_MAX_SA); if (!crypto->priv.cipher) { IPCOM_LOG1(CRIT, "IPMACSEC-CRYPTO: no crypto found for interface %s", crypto->priv.secy->netif->ipcom.name); return -1; } if (ipmacsec_sak_copy(&crypto->priv.sak[key_index], sak) != 0) { IPCOM_LOG2(CRIT, "IPMACSEC-CRYPTO: Failed to create an SAK copy for key_index %u for interface %s", key_index, crypto->priv.secy->netif->ipcom.name); return -1; } if (crypto->priv.cipher->init(crypto->priv.cipher, &crypto->priv.cs_ctx[key_index]) != 0) { IPCOM_LOG2(CRIT, "IPMACSEC-CRYPTO: Failed to initialize cipher for key_index %u for interface %s", key_index, crypto->priv.secy->netif->ipcom.name); (void)ipmacsec_sak_delete(&crypto->priv.sak[key_index]); return -1; } return 0; } /* *=========================================================================== * ipmacsec_crypto_destroy *=========================================================================== * Description: Destroy the Crypto Interface for the specified key. * Parameters: * Returns: * */ IP_STATIC int ipmacsec_crypto_destroy(struct Ipmacsec_crypto_if_struct *crypto, Ip_u8 key_index) { ip_assert(crypto); ip_assert(key_index < IPMACSEC_MAX_SA); if (crypto->priv.cipher) (void)crypto->priv.cipher->destroy(crypto->priv.cipher, &crypto->priv.cs_ctx[key_index]); (void)ipmacsec_sak_delete(&crypto->priv.sak[key_index]); return 0; } /* *=========================================================================== * ipmacsec_crypto_protect *=========================================================================== * Description: Crypto Interface Secure Frame Protection function * Parameters: crypto - ptr to the crypto_if structure * ctx - Context containing the crypto information for the current operation * Returns: 0 upon success, < 0 on error * */ IP_STATIC int ipmacsec_crypto_protect(Ipmacsec_crypto_if *crypto, Ipmacsec_crypto_ctx *ctx) { Ipmacsec_crypto_cipher IP_CONST *cipher; Ipmacsec_cipher_ctx cipher_ctx; Ip_u8 iv[IPMACSEC_CRYPTO_IVLEN]; ip_assert(crypto); ip_assert(ctx); cipher = crypto->priv.cipher; if (!cipher || !crypto->cipher_set) { #ifdef IPMACSEC_DEBUG_CRYPTO_PROTECT IPCOM_LOG0(ERR, "IPMACSEC-CRYPTO_P: cipher not set"); #endif return -1; } if (!cipher->protect) { #ifdef IPMACSEC_DEBUG_CRYPTO_PROTECT IPCOM_LOG0(ERR, "IPMACSEC-CRYPTO_P: cipher protect function not found"); #endif return -1; } if (!ctx->macsec_hdr || !ctx->macsec_hdr_len) { #ifdef IPMACSEC_DEBUG_CRYPTO_PROTECT IPCOM_LOG0(ERR, "IPMACSEC-CRYPTO_P: invalid macsec header"); #endif return -1; } if (!ctx->userdata || !ctx->userdata_len) { #ifdef IPMACSEC_DEBUG_CRYPTO_PROTECT IPCOM_LOG0(ERR, "IPMACSEC-CRYPTO_P: invalid userdata"); #endif return -1; } if (!ctx->icv || (ctx->icv_len < cipher->desc.icv_bits/8)) { #ifdef IPMACSEC_DEBUG_CRYPTO_PROTECT IPCOM_LOG0(ERR, "IPMACSEC-CRYPTO_P: invalid icv"); #endif return -1; } if (ctx->key_index >= IPMACSEC_MAX_SA) { #ifdef IPMACSEC_DEBUG_CRYPTO_VALIDATE IPCOM_LOG1(ERR, "IPMACSEC-CRYPTO_P: invalid key_index %u", ctx->key_index); #endif return -1; } if (ctx->oper.encrypt) { if (!cipher->desc.confidentialityProtection) { #ifdef IPMACSEC_DEBUG_CRYPTO_PROTECT IPCOM_LOG0(ERR, "IPMACSEC-CRYPTO_P: cipher confidentialityProtection off but encryption requested"); #endif return -1; } if (cipher->desc.changesDataLength) { #ifdef IPMACSEC_DEBUG_CRYPTO_PROTECT IPCOM_LOG0(ERR, "IPMACSEC-CRYPTO_P: cipher changesDataLength; not supported"); #endif return -1; /* Not supported for now */ } if (!ctx->securedata) { /* Use our preallocated result buffer */ ctx->securedata_len = (Ip_u16)crypto->priv.result_buf_len; ctx->securedata = crypto->priv.result_buf; } if (!ctx->securedata || (ctx->securedata_len < ctx->userdata_len)) { #ifdef IPMACSEC_DEBUG_CRYPTO_PROTECT IPCOM_LOG2(ERR, "IPMACSEC-CRYPTO_P: invalid securedata_len (%u < %u)", ctx->securedata_len, ctx->userdata_len); #endif return -1; } } else { if (!cipher->desc.integrityProtection) { #ifdef IPMACSEC_DEBUG_CRYPTO_PROTECT IPCOM_LOG0(CRIT, "IPMACSEC-CRYPTO_P: cipher integrityProtection off"); #endif return -1; } } cipher_ctx.cs_ctx = crypto->priv.cs_ctx[ctx->key_index]; cipher_ctx.key = crypto->priv.sak[ctx->key_index].buf; cipher_ctx.keylen = crypto->priv.sak[ctx->key_index].num_octets; ipcom_memcpy(iv, ctx->sci, IPMACSEC_SCI_LEN); ipcom_memcpy(&iv[IPMACSEC_SCI_LEN], &ctx->pn, IPMACSEC_PN_LEN); cipher_ctx.iv = iv; cipher_ctx.ivlen = IPMACSEC_CRYPTO_IVLEN; cipher_ctx.aad = ctx->macsec_hdr; cipher_ctx.aadlen = ctx->macsec_hdr_len; cipher_ctx.pt = ctx->userdata; cipher_ctx.ptlen = ctx->userdata_len; cipher_ctx.ct = ctx->securedata; cipher_ctx.ctlen = ctx->securedata_len; cipher_ctx.tag = ctx->icv; cipher_ctx.taglen = ctx->icv_len; cipher_ctx.confidentiality = ctx->oper.encrypt; cipher_ctx.confidentialityOffset = crypto->confidentialityOffset; cipher_ctx.cipher_options.gcmaes.accel_level = (GCM_ACCEL)crypto->gcmaes_accel_level; if (cipher->protect(cipher, &cipher_ctx) != 0) { #ifdef IPMACSEC_DEBUG_CRYPTO_PROTECT IPCOM_LOG0(ERR, "IPMACSEC-CRYPTO_P: cipher protection failed"); #endif return -1; } ip_assert(cipher_ctx.ctlen <= ctx->securedata_len); /* Successful, so update length fields */ ctx->securedata_len = (Ip_u16)cipher_ctx.ctlen; ctx->icv_len = (Ip_u8)cipher_ctx.taglen; return 0; } /* *=========================================================================== * ipmacsec_crypto_validate *=========================================================================== * Description: Crypto Interface Secure Frame Validation function * Parameters: crypto - ptr to the crypto_if structure * ctx - Context containing the crypto information for the current operation * Returns: 0 upon success, < 0 on error * */ IP_STATIC int ipmacsec_crypto_validate(Ipmacsec_crypto_if *crypto, Ipmacsec_crypto_ctx *ctx, Ip_bool *valid) { Ipmacsec_crypto_cipher IP_CONST *cipher; Ipmacsec_cipher_ctx cipher_ctx; Ip_u8 iv[IPMACSEC_CRYPTO_IVLEN]; ip_assert(crypto); ip_assert(ctx); ip_assert(valid); *valid = IP_FALSE; cipher = crypto->priv.cipher; if (!cipher || !crypto->cipher_set) { #ifdef IPMACSEC_DEBUG_CRYPTO_VALIDATE IPCOM_LOG0(ERR, "IPMACSEC-CRYPTO_V: cipher not set"); #endif return -1; } if (!cipher->validate) { #ifdef IPMACSEC_DEBUG_CRYPTO_VALIDATE IPCOM_LOG0(ERR, "IPMACSEC-CRYPTO_V: cipher validate function not found"); #endif return -1; } if (!ctx->macsec_hdr || !ctx->macsec_hdr_len) { #ifdef IPMACSEC_DEBUG_CRYPTO_VALIDATE IPCOM_LOG0(ERR, "IPMACSEC-CRYPTO_V: invalid macsec header"); #endif return -1; } if (!ctx->securedata || !ctx->securedata_len) { #ifdef IPMACSEC_DEBUG_CRYPTO_VALIDATE IPCOM_LOG0(ERR, "IPMACSEC-CRYPTO_V: invalid securedata"); #endif return -1; } if (!ctx->icv || (ctx->icv_len < cipher->desc.icv_bits/8)) { #ifdef IPMACSEC_DEBUG_CRYPTO_VALIDATE IPCOM_LOG0(ERR, "IPMACSEC-CRYPTO_V: invalid icv"); #endif return -1; } if (ctx->key_index >= IPMACSEC_MAX_SA) { #ifdef IPMACSEC_DEBUG_CRYPTO_VALIDATE IPCOM_LOG1(ERR, "IPMACSEC-CRYPTO_V: invalid key_index %u", ctx->key_index); #endif return -1; } if (ctx->oper.decrypt) { if (!cipher->desc.confidentialityProtection) { #ifdef IPMACSEC_DEBUG_CRYPTO_VALIDATE IPCOM_LOG0(ERR, "IPMACSEC-CRYPTO_V: cipher confidentialityProtection off but decryption requested"); #endif return -1; } if (cipher->desc.changesDataLength) { #ifdef IPMACSEC_DEBUG_CRYPTO_VALIDATE IPCOM_LOG0(ERR, "IPMACSEC-CRYPTO_V: cipher changesDataLength; not supported"); #endif return -1; /* Not supported for now */ } if (!ctx->userdata) { /* Use our preallocated result buffer */ ctx->userdata_len = (Ip_u16)crypto->priv.result_buf_len; ctx->userdata = crypto->priv.result_buf; } if (!ctx->userdata || (ctx->userdata_len < ctx->securedata_len)) { #ifdef IPMACSEC_DEBUG_CRYPTO_VALIDATE IPCOM_LOG2(ERR, "IPMACSEC-CRYPTO_V: invalid userdata_len (%u < %u)", ctx->userdata_len, ctx->securedata_len); #endif return -1; } } else { if (!cipher->desc.integrityProtection) { #ifdef IPMACSEC_DEBUG_CRYPTO_VALIDATE IPCOM_LOG0(CRIT, "IPMACSEC-CRYPTO_V: cipher integrityProtection off"); #endif return -1; } } cipher_ctx.cs_ctx = crypto->priv.cs_ctx[ctx->key_index]; cipher_ctx.key = crypto->priv.sak[ctx->key_index].buf; cipher_ctx.keylen = crypto->priv.sak[ctx->key_index].num_octets; ipcom_memcpy(iv, ctx->sci, IPMACSEC_SCI_LEN); ipcom_memcpy(&iv[IPMACSEC_SCI_LEN], &ctx->pn, IPMACSEC_PN_LEN); cipher_ctx.iv = iv; cipher_ctx.ivlen = IPMACSEC_CRYPTO_IVLEN; cipher_ctx.aad = ctx->macsec_hdr; cipher_ctx.aadlen = ctx->macsec_hdr_len; cipher_ctx.pt = ctx->userdata; cipher_ctx.ptlen = ctx->userdata_len; cipher_ctx.ct = ctx->securedata; cipher_ctx.ctlen = ctx->securedata_len; cipher_ctx.tag = ctx->icv; cipher_ctx.taglen = ctx->icv_len; cipher_ctx.confidentiality = ctx->oper.decrypt; cipher_ctx.confidentialityOffset = crypto->confidentialityOffset; cipher_ctx.cipher_options.gcmaes.accel_level = (GCM_ACCEL)crypto->gcmaes_accel_level; if (cipher->validate(cipher, &cipher_ctx) != 0) { #ifdef IPMACSEC_DEBUG_CRYPTO_VALIDATE IPCOM_LOG0(ERR, "IPMACSEC-CRYPTO_V: cipher validation failed"); #endif return -1; } ip_assert(cipher_ctx.ptlen <= ctx->userdata_len); /* Successful, so update length field and valid state */ ctx->userdata_len = (Ip_u16)cipher_ctx.ptlen; *valid = cipher_ctx.valid; return 0; } /* *=========================================================================== * ipmacsec_gcm_aes_128_init *=========================================================================== * Description: GCM-AES cipher specific initialization function. * Parameters: cipher - ptr to the GCM-AES cipher structure * cs_ctx - Pointer to a cipher specific context that is updated by this function * Returns: 0 upon success, < 0 on error * */ IP_STATIC int ipmacsec_gcm_aes_128_init(IP_CONST Ipmacsec_crypto_cipher *cipher, void **cs_ctx) { AES_GCM_CTX *gcmctx = IP_NULL; IPCOM_UNUSED_ARG(cipher); ip_assert(cipher); ip_assert(cs_ctx); gcmctx = (AES_GCM_CTX *)*cs_ctx; if (gcmctx) return 0; /* Allocate context */ gcmctx = AES_GCM_CTX_new(); if (gcmctx == IP_NULL) { #ifdef IPMACSEC_DEBUG_CRYPTO_CIPH_GCMAES IPCOM_LOG0(ERR, "IPMACSEC-GCMAES_CIPH_I: malloc of AES_GCM_CTX failed"); #endif return -1; } *cs_ctx = (void *)gcmctx; return 0; } /* *=========================================================================== * ipmacsec_gcm_aes_128_destroy *=========================================================================== * Description: GCM-AES cipher specific destructor * Parameters: cipher - ptr to the GCM-AES cipher structure * cs_ctx - Pointer to a cipher specific context that is was initialized * by the cipher specific init function * * Returns: 0 upon success, < 0 on error * */ IP_STATIC int ipmacsec_gcm_aes_128_destroy(IP_CONST Ipmacsec_crypto_cipher *cipher, void **cs_ctx) { IPCOM_UNUSED_ARG(cipher); ip_assert(cipher); ip_assert(cs_ctx); if (*cs_ctx) { AES_GCM_CTX_free((AES_GCM_CTX *)*cs_ctx); *cs_ctx = IP_NULL; } return 0; } /* *=========================================================================== * ipmacsec_gcm_aes_128_protect *=========================================================================== * Description: GCM-AES cipher specific Secure Frame Protection function * Parameters: cipher - ptr to the GCM-AES cipher structure * ctx - Context containing the cipher information for the current operation * Returns: 0 upon success, < 0 on error * */ IP_STATIC int ipmacsec_gcm_aes_128_protect(IP_CONST Ipmacsec_crypto_cipher *cipher, Ipmacsec_cipher_ctx *ctx) { AES_GCM_CTX *gcmctx = IP_NULL; unsigned long inlen = 0, outlen = 0; unsigned long total_out = 0; unsigned char *in = IP_NULL, *out = IP_NULL; int ret = -1; ip_assert(cipher); ip_assert(ctx); #ifdef IPMACSEC_DEBUG_CRYPTO_CIPH_GCMAES_DETAIL IPCOM_LOG6(DEBUG2, "IPMACSEC-GCMAES_CIPH_P: key 0x%x, iv 0x%x, aad 0x%x, pt 0x%x, ct 0x%x, tag 0x%x", ctx->key, ctx->iv, ctx->aad, ctx->pt, ctx->ct, ctx->tag); IPCOM_LOG8(DEBUG2, "IPMACSEC-GCMAES_CIPH_P: keylen %u, ivlen %u, aadlen %u, ptlen %u, ctlen %u, " "taglen %u, confidentiality %s, confidentialityOffset %u", ctx->keylen, ctx->ivlen, ctx->aadlen, ctx->ptlen, ctx->ctlen, ctx->taglen, IPMACSEC_BOOL_STRING(ctx->confidentiality), ctx->confidentialityOffset); #endif gcmctx = (AES_GCM_CTX *)ctx->cs_ctx; if (gcmctx == IP_NULL) { IPCOM_LOG0(CRIT, "IPMACSEC-GCMAES_CIPH_P: invalid cipher specific context"); return -1; } if (!ctx->key) { IPCOM_LOG0(ERR, "IPMACSEC-GCMAES_CIPH_P: invalid key"); return -1; } if (ctx->keylen != cipher->desc.key_bits/8) { IPCOM_LOG1(ERR, "IPMACSEC-GCMAES_CIPH_P: invalid keylen %u", ctx->keylen); return -1; } if (ctx->taglen < AES_BLOCK_SIZE) { IPCOM_LOG2(ERR, "IPMACSEC-GCMAES_CIPH_P: invalid ktaglen %u (< %u)", ctx->taglen, AES_BLOCK_SIZE); return -1; } #ifdef IPMACSEC_DEBUG_CRYPTO_CIPH_GCMAES_DETAIL ipmacsec_dump_buffer("key", ctx->key, ctx->keylen); ipmacsec_dump_buffer("iv", ctx->iv, ctx->ivlen); ipmacsec_dump_buffer("aad", ctx->aad, ctx->aadlen); ipmacsec_dump_buffer("pt", ctx->pt, ctx->ptlen); #endif /* 1. Init aes gcm with the key and iv. Set the acceleration level */ if (!AES_GCM_Init(gcmctx, (unsigned char *)ctx->iv, ctx->ivlen, (unsigned char *)ctx->key, ctx->keylen)) { #ifdef IPMACSEC_DEBUG_CRYPTO_CIPH_GCMAES IPCOM_LOG0(ERR, "IPMACSEC-GCMAES_CIPH_P: AES_GCM_Init failed."); #endif goto exit_protect; } AES_GCM_CTX_ctrl(gcmctx, AES_GCM_CTRL_SET_ACCEL, ctx->cipher_options.gcmaes.accel_level, IP_NULL); /* 2. Update with associated data (auth only) */ if (ctx->aadlen != 0) { if (!AES_GCM_EncryptUpdate(gcmctx, (unsigned char *)ctx->aad, ctx->aadlen, IP_NULL, 0, IP_NULL, IP_NULL)) { #ifdef IPMACSEC_DEBUG_CRYPTO_CIPH_GCMAES IPCOM_LOG0(ERR, "IPMACSEC-GCMAES_CIPH_P: AAES_GCM_EncryptUpdate failed for associated data"); #endif goto exit_protect; } } /* 3. Update with plain text data (auth only) */ if (!ctx->confidentiality) { if (!AES_GCM_EncryptUpdate(gcmctx, (unsigned char *)ctx->pt, ctx->ptlen, IP_NULL, 0, IP_NULL, IP_NULL)) { #ifdef IPMACSEC_DEBUG_CRYPTO_CIPH_GCMAES IPCOM_LOG0(ERR, "IPMACSEC-GCMAES_CIPH_P: AAES_GCM_EncryptUpdate failed for confidentialityOffset"); #endif goto exit_protect; } goto finalize_encrypt; } /* 4. This is skipped if we are not doing encryption */ in = (unsigned char *)ctx->pt; inlen = ctx->ptlen; out = (unsigned char *)ctx->ct; /* 4.1 Update with Confidentiality offset octets (auth only) */ if (ctx->confidentialityOffset != 0) { Ip_u32 skip_len = (Ip_u32)IP_MIN(ctx->confidentialityOffset, inlen); ipcom_memcpy(out, in, skip_len); if (skip_len < inlen) { if (!AES_GCM_EncryptUpdate(gcmctx, in, skip_len, IP_NULL, 0, IP_NULL, IP_NULL)) { #ifdef IPMACSEC_DEBUG_CRYPTO_CIPH_GCMAES IPCOM_LOG0(ERR, "IPMACSEC-GCMAES_CIPH_P: AAES_GCM_EncryptUpdate failed for confidentialityOffset"); #endif goto exit_protect; } in += skip_len; inlen -= skip_len; } else { in = IP_NULL; inlen = 0; } total_out += skip_len; } /* 4.2. Update with plain text (encrypt) */ if (!AES_GCM_EncryptUpdate(gcmctx, IP_NULL, 0, in, inlen, out+total_out, &outlen)) { if (inlen != 0) { #ifdef IPMACSEC_DEBUG_CRYPTO_CIPH_GCMAES IPCOM_LOG0(ERR, "IPMACSEC-GCMAES_CIPH_P: AES_GCM_EncryptUpdate failed for plain text (encrypt)"); #endif goto exit_protect; } } total_out += outlen; finalize_encrypt: /* 5. Finalize and get the tag */ if (!AES_GCM_EncryptFinal(gcmctx, out+total_out, &outlen, ctx->tag)) { #ifdef IPMACSEC_DEBUG_CRYPTO_CIPH_GCMAES IPCOM_LOG0(ERR, "IPMACSEC-GCMAES_CIPH_P: AES_GCM_EncryptFinal failed"); #endif goto exit_protect; } total_out += outlen; ctx->ctlen = (Ip_u32)total_out; ctx->taglen = AES_BLOCK_SIZE; #ifdef IPMACSEC_DEBUG_CRYPTO_CIPH_GCMAES_DETAIL ipmacsec_dump_buffer("computed tag", ctx->tag, ctx->taglen); if (ctx->confidentiality) ipmacsec_dump_buffer("encrypted ct", ctx->ct, ctx->ctlen); #endif ret = 0; exit_protect: return ret; } /* *=========================================================================== * ipmacsec_gcm_aes_128_validate *=========================================================================== * Description: GCM-AES cipher specific Secure Frame Validation function * Parameters: cipher - ptr to the GCM-AES cipher structure * ctx - Context containing the cipher information for the current operation * Returns: 0 upon success, < 0 on error * */ IP_STATIC int ipmacsec_gcm_aes_128_validate(IP_CONST Ipmacsec_crypto_cipher *cipher, Ipmacsec_cipher_ctx *ctx) { AES_GCM_CTX *gcmctx = IP_NULL; unsigned long inlen = 0, outlen = 0; unsigned long total_out = 0; unsigned char *in = IP_NULL, *out = IP_NULL; int ret = -1; ip_assert(cipher); ip_assert(ctx); #ifdef IPMACSEC_DEBUG_CRYPTO_CIPH_GCMAES_DETAIL IPCOM_LOG6(DEBUG2, "IPMACSEC-GCMAES_CIPH_P: key 0x%x, iv 0x%x, aad 0x%x, pt 0x%x, ct 0x%x, tag 0x%x", ctx->key, ctx->iv, ctx->aad, ctx->pt, ctx->ct, ctx->tag); IPCOM_LOG8(DEBUG2, "IPMACSEC-GCMAES_CIPH_P: keylen %u, ivlen %u, aadlen %u, ptlen %u, ctlen %u, " "taglen %u, confidentiality %s, confidentialityOffset %u", ctx->keylen, ctx->ivlen, ctx->aadlen, ctx->ptlen, ctx->ctlen, ctx->taglen, IPMACSEC_BOOL_STRING(ctx->confidentiality), ctx->confidentialityOffset); #endif ctx->valid = IP_FALSE; gcmctx = (AES_GCM_CTX *)ctx->cs_ctx; if (gcmctx == IP_NULL) { IPCOM_LOG0(CRIT, "IPMACSEC-GCMAES_CIPH_V: invalid cipher specific context"); return -1; } if (!ctx->key) { IPCOM_LOG0(ERR, "IPMACSEC-GCMAES_CIPH_V: invalid key"); return -1; } if (ctx->keylen != cipher->desc.key_bits/8) { IPCOM_LOG1(ERR, "IPMACSEC-GCMAES_CIPH_V: invalid keylen %u", ctx->keylen); return -1; } if (ctx->taglen < AES_BLOCK_SIZE) { IPCOM_LOG2(ERR, "IPMACSEC-GCMAES_CIPH_V: invalid ktaglen %u (< %u)", ctx->taglen, AES_BLOCK_SIZE); return -1; } if ((ctx->confidentiality) && (ctx->ptlen < ctx->ctlen)) { IPCOM_LOG2(ERR, "IPMACSEC-GCMAES_CIPH_V: ptlen %u < ctlen %u", ctx->ptlen, ctx->ctlen); return -1; } #ifdef IPMACSEC_DEBUG_CRYPTO_CIPH_GCMAES_DETAIL ipmacsec_dump_buffer("key", ctx->key, ctx->keylen); ipmacsec_dump_buffer("iv", ctx->iv, ctx->ivlen); ipmacsec_dump_buffer("aad", ctx->aad, ctx->aadlen); ipmacsec_dump_buffer("ct", ctx->ct, ctx->ctlen); ipmacsec_dump_buffer("tag", ctx->tag, ctx->taglen); #endif /* 1. Init aes gcm with the key and iv. Set the acceleration level */ if (!AES_GCM_Init(gcmctx, (unsigned char *)ctx->iv, ctx->ivlen, (unsigned char *)ctx->key, ctx->keylen)) { #ifdef IPMACSEC_DEBUG_CRYPTO_CIPH_GCMAES IPCOM_LOG0(ERR, "IPMACSEC-GCMAES_CIPH_V: AES_GCM_Init failed."); #endif goto exit_verify; } AES_GCM_CTX_ctrl(gcmctx, AES_GCM_CTRL_SET_ACCEL, ctx->cipher_options.gcmaes.accel_level, IP_NULL); /* 2. Update with associated data (auth only) */ if (ctx->aadlen != 0) { if (!AES_GCM_DecryptUpdate(gcmctx, (unsigned char *)ctx->aad, ctx->aadlen, IP_NULL, 0, IP_NULL, IP_NULL)) { #ifdef IPMACSEC_DEBUG_CRYPTO_CIPH_GCMAES IPCOM_LOG0(ERR, "IPMACSEC-GCMAES_CIPH_V: AES_GCM_DecryptUpdate failed for associated data"); #endif goto exit_verify; } } if (!ctx->confidentiality) { if (!AES_GCM_DecryptUpdate(gcmctx, (unsigned char *)ctx->ct, ctx->ctlen, IP_NULL, 0, IP_NULL, IP_NULL)) { #ifdef IPMACSEC_DEBUG_CRYPTO_CIPH_GCMAES IPCOM_LOG0(ERR, "IPMACSEC-GCMAES_CIPH_V: AES_GCM_DecryptUpdate failed for confidentialityOffset"); #endif goto exit_verify; } goto finalize_decrypt; } /* 4. This is skipped if we are not doing decryption */ in = (unsigned char *)ctx->ct; inlen = ctx->ctlen; out = (unsigned char *)ctx->pt; /* 4.1 Update with Confidentiality offset octets (auth only) */ if (ctx->confidentialityOffset != 0) { Ip_u32 skip_len = (Ip_u32)IP_MIN(ctx->confidentialityOffset, inlen); ipcom_memcpy(out, in, skip_len); if (skip_len < inlen) { if (!AES_GCM_DecryptUpdate(gcmctx, in, skip_len, IP_NULL, 0, IP_NULL, IP_NULL)) { #ifdef IPMACSEC_DEBUG_CRYPTO_CIPH_GCMAES IPCOM_LOG0(ERR, "IPMACSEC-GCMAES_CIPH_V: AES_GCM_DecryptUpdate failed for confidentialityOffset"); #endif goto exit_verify; } in += skip_len; inlen -= skip_len; } else { in = IP_NULL; inlen = 0; } total_out += skip_len; } /* 4.2. Update with plain text (decrypt) */ if (!AES_GCM_DecryptUpdate(gcmctx, IP_NULL, 0, in, inlen, out+total_out, &outlen)) { if (inlen != 0) { #ifdef IPMACSEC_DEBUG_CRYPTO_CIPH_GCMAES IPCOM_LOG0(ERR, "IPMACSEC-GCMAES_CIPH_V: AES_GCM_DecryptUpdate failed for plain text (decrypt)"); #endif goto exit_verify; } } total_out += outlen; finalize_decrypt: /* 5. Finalize and get the tag */ if (!AES_GCM_DecryptFinal(gcmctx, out+total_out, &outlen, ctx->tag, ctx->taglen)) { #ifdef IPMACSEC_DEBUG_CRYPTO_CIPH_GCMAES IPCOM_LOG0(ERR, "IPMACSEC-GCMAES_CIPH_V: AES_GCM_DecryptFinal failed"); #endif goto exit_verify; } total_out += outlen; ctx->ptlen = (Ip_u32)total_out; #ifdef IPMACSEC_DEBUG_CRYPTO_CIPH_GCMAES_DETAIL if (ctx->confidentiality) ipmacsec_dump_buffer("decrypted pt", ctx->pt, ctx->ptlen); #endif ctx->valid = IP_TRUE; ret = 0; exit_verify: return ret; } #ifdef IPMACSEC_CRYPTO_ALLOW_NULL_CIPHER /* This code implements the NULL cipher and is to be used for Unit testing purposes ONLY. * It provides no security whatsoever. DO NOT USE IN PRODUCTION CODE. */ /* *=========================================================================== * ipmacsec_null_cipher_128_init *=========================================================================== * Description: NULL Cipher initialization function. * Parameters: * Returns: * */ IP_STATIC int ipmacsec_null_cipher_128_init(IP_CONST Ipmacsec_crypto_cipher *cipher, void **cs_ctx) { Ip_u8 *tag; ip_assert(cipher); ip_assert(cs_ctx); tag = ipcom_malloc(cipher->desc.icv_bits/8); if (!tag) { IPCOM_LOG0(CRIT, "IPMACSEC-NULL_CIPH_V: tag alloc fail"); return -1; } ipcom_memset(tag, 0xae, cipher->desc.icv_bits/8); *cs_ctx = (void *)tag; return 0; } /* *=========================================================================== * ipmacsec_null_cipher_128_destroy *=========================================================================== * Description: NULL Cipher destructor * Parameters: * Returns: * */ IP_STATIC int ipmacsec_null_cipher_128_destroy(IP_CONST Ipmacsec_crypto_cipher *cipher, void **cs_ctx) { Ip_u8 *tag; ip_assert(cipher); ip_assert(cs_ctx); tag = (Ip_u8 *)*cs_ctx; if (tag) { ipcom_free(tag); *cs_ctx = IP_NULL; } return 0; } /* *=========================================================================== * ipmacsec_null_cipher_128_protect *=========================================================================== * Description: NULL Cipher Secure Frame Protection function * Parameters: * Returns: * */ IP_STATIC int ipmacsec_null_cipher_128_protect(IP_CONST Ipmacsec_crypto_cipher *cipher, Ipmacsec_cipher_ctx *ctx) { Ip_u8 *tag; ip_assert(cipher); ip_assert(ctx); #ifdef IPMACSEC_DEBUG_CRYPTO_NULL_CIPHER IPCOM_LOG6(DEBUG2, "IPMACSEC-NULL_CIPH_P: key 0x%x, iv 0x%x, aad 0x%x, pt 0x%x, ct 0x%x, tag 0x%x", ctx->key, ctx->iv, ctx->aad, ctx->pt, ctx->ct, ctx->tag); IPCOM_LOG8(DEBUG2, "IPMACSEC-NULL_CIPH_P: keylen %u, ivlen %u, aadlen %u, ptlen %u, ctlen %u, " "taglen %u, confidentiality %s, confidentialityOffset %u", ctx->keylen, ctx->ivlen, ctx->aadlen, ctx->ptlen, ctx->ctlen, ctx->taglen, IPMACSEC_BOOL_STRING(ctx->confidentiality), ctx->confidentialityOffset); #endif if (ctx->keylen != cipher->desc.key_bits/8) { IPCOM_LOG1(ERR, "IPMACSEC-NULL_CIPH_P: invalid keylen %u", ctx->keylen); return -1; } tag = (Ip_u8 *)ctx->cs_ctx; if (!tag) { IPCOM_LOG0(ERR, "IPMACSEC-NULL_CIPH_P: invalid tag"); return -1; } if (ctx->confidentiality) { if (ctx->ctlen < ctx->ptlen) { IPCOM_LOG2(ERR, "IPMACSEC-NULL_CIPH_P: ctlen %u < ptlen %u", ctx->ctlen, ctx->ptlen); return -1; } ipcom_memcpy(ctx->ct, ctx->pt, ctx->ptlen); ctx->ctlen = ctx->ptlen; } ipcom_memcpy(ctx->tag, tag, ctx->taglen); /* ctx->taglen unchanged */ return 0; } /* *=========================================================================== * ipmacsec_null_cipher_128_validate *=========================================================================== * Description: NULL Cipher Secure Frame Validation function * Parameters: * Returns: * */ IP_STATIC int ipmacsec_null_cipher_128_validate(IP_CONST Ipmacsec_crypto_cipher *cipher, Ipmacsec_cipher_ctx *ctx) { Ip_u8 *tag; ip_assert(cipher); ip_assert(ctx); #ifdef IPMACSEC_DEBUG_CRYPTO_NULL_CIPHER IPCOM_LOG6(DEBUG2, "IPMACSEC-NULL_CIPH_V: key 0x%x, iv 0x%x, aad 0x%x, pt 0x%x, ct 0x%x, tag 0x%x", ctx->key, ctx->iv, ctx->aad, ctx->pt, ctx->ct, ctx->tag); IPCOM_LOG8(DEBUG2, "IPMACSEC-NULL_CIPH_V keylen %u, ivlen %u, aadlen %u, ptlen %u, ctlen %u, " "taglen %u, confidentiality %s, confidentialityOffset %u", ctx->keylen, ctx->ivlen, ctx->aadlen, ctx->ptlen, ctx->ctlen, ctx->taglen, IPMACSEC_BOOL_STRING(ctx->confidentiality), ctx->confidentialityOffset); #endif if (ctx->keylen != cipher->desc.key_bits/8) { IPCOM_LOG1(ERR, "IPMACSEC-NULL_CIPH_V: invalid keylen %u", ctx->keylen); return -1; } if ((ctx->confidentiality) && (ctx->ptlen < ctx->ctlen)) { IPCOM_LOG2(ERR, "IPMACSEC-NULL_CIPH_V: ptlen %u < ctlen %u", ctx->ptlen, ctx->ctlen); return -1; } tag = (Ip_u8 *)ctx->cs_ctx; if (!tag) { IPCOM_LOG0(ERR, "IPMACSEC-NULL_CIPH_V: invalid tag"); return -1; } if (ipcom_memcmp(tag, ctx->tag, ctx->taglen) != 0) { ctx->valid = IP_FALSE; IPCOM_LOG0(ERR, "IPMACSEC-NULL_CIPH_V: tag mismatch, validation failed"); return 0; } if (ctx->confidentiality) { ipcom_memcpy(ctx->pt, ctx->ct, ctx->ctlen); ctx->ptlen = ctx->ctlen; } /* ctx->taglen unchanged */ ctx->valid = IP_TRUE; return 0; } #endif