1348 lines
45 KiB
C
1348 lines
45 KiB
C
/* 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 <ipcom_type.h>
|
|
#include <ipcom_cstyle.h>
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_err.h>
|
|
#include <ipcom_sock.h>
|
|
#include <ipcom_syslog.h>
|
|
#include <ipcom_sysvar.h>
|
|
#include <ipcom_errno.h>
|
|
#include <ipcom_err.h>
|
|
#include <ipcom_os.h>
|
|
|
|
#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
|
|
|
|
|