Loading NAS_ETSI.code-workspace +0 −12 Original line number Diff line number Diff line Loading @@ -6,18 +6,6 @@ { "path": "../../frameworks/titan/titan.core" }, { "path": "../5G-AKA-simulation-in-C" }, { "path": "../free5gc" }, { "path": "../free5gc.util" }, { "path": "../5G_ciphered_NAS_decipher_tool" }, { "path": "../open5gs" }, Loading ccsrc/Externals/NG_security_ext.cc +246 −268 Original line number Diff line number Diff line Loading @@ -19,8 +19,6 @@ #include "ia2_128.hh" #include "ia3_128.hh" #include "zuc.hh" namespace NG__SecurityDefinitionsAndExternalFunctions { /** Loading Loading @@ -56,10 +54,7 @@ OCTETSTRING fx__NG__NasIntegrityAlgorithm(const OCTETSTRING& p_encoded_nas_pdu, uint32_t mac_length; uint32_t nas_count = converter::get_instance().bytes_to_int(std::vector<uint8_t>(static_cast<const unsigned char*>(p_nas_count), static_cast<const unsigned char*>(p_nas_count) + p_nas_count.lengthof())); int result = -1; if (bit2int(p_integrity_algorithm) == 0) { // IA0/NEA0: No integrity protection loggers::get_instance().log("fx__NG__NasIntegrityAlgorithm: No integrity protection"); return p_encoded_nas_pdu; // no integrity protection } else if (bit2int(p_integrity_algorithm) == 1) { // IA1_128/NEA1_128: Snow 3G based algorithm if (bit2int(p_integrity_algorithm) == 1) { // IA1_128/NEA1_128: Snow 3G based algorithm loggers::get_instance().log("fx__NG__NasIntegrityAlgorithm: ia1_128 selected"); ia1_128 enc; result = enc.mac( Loading @@ -76,7 +71,7 @@ OCTETSTRING fx__NG__NasIntegrityAlgorithm(const OCTETSTRING& p_encoded_nas_pdu, } else if (bit2int(p_integrity_algorithm) == 2) { // IA2_128/NEA2_128: AES 128 CTR based algorithm loggers::get_instance().log("fx__NG__NasIntegrityAlgorithm: ia2_128 selected"); ia2_128 enc; result = enc.encrypt( result = enc.mac( bit2int(p_integrity_algorithm), static_cast<const unsigned char*>(bit2oct(p_knas_int)), nas_count, Loading Loading @@ -117,7 +112,7 @@ OCTETSTRING fx__NG__NasIntegrityAlgorithm(const OCTETSTRING& p_encoded_nas_pdu, return int2oct(0, 0); } OCTETSTRING os(mac_length, (const unsigned char*)mac); free(mac); std::free(mac); loggers::get_instance().log_msg("<<< fx__NG__NasIntegrityAlgorithm: ", os); return os; Loading Loading @@ -180,14 +175,6 @@ OCTETSTRING fx__NG__NasCiphering(const OCTETSTRING& p_encoded_nas_pdu,const BIT ); } else if (bit2int(p_ciphering_algorithm) == 3) { // IA3_128/NEA3_128: ZUC based algorithm loggers::get_instance().log("fx__NG__NasCiphering: ia3_128 selected"); // loggers::get_instance().log("fx__NG__NasCiphering: %d", p_encoded_nas_pdu.lengthof()); // cyphered = (unsigned char*) std::malloc(p_encoded_nas_pdu.lengthof()); // zuc_eea3( // (unsigned char*)static_cast<const unsigned char*>(bit2oct(p_knas_enc)), // nas_count, (uint32_t)bit2int(p_bearer_id), p_direction, // p_encoded_nas_pdu.lengthof() * 8, (unsigned char*)static_cast<const unsigned char*>(p_encoded_nas_pdu), cyphered); // result = 0; // cyphered_length = p_encoded_nas_pdu.lengthof(); ia3_128 enc; result = enc.encrypt( bit2int(p_ciphering_algorithm), Loading Loading @@ -278,13 +265,6 @@ OCTETSTRING fx__NG__NasDeciphering(const OCTETSTRING& p_ciphered_nas_message, co &payload_length ); } else if (bit2int(p_ciphering_algorithm) == 3) { // IA3_128/NEA3_128: ZUC based algorithm // loggers::get_instance().log("fx__NG__NasDeciphering: ia1_128 selected"); // zuc_eea3( // (unsigned char*)static_cast<const unsigned char*>(bit2oct(p_knas_enc)), // nas_count, bit2int(p_bearer_id), p_direction, // p_ciphered_nas_message.lengthof() * 8, (unsigned char*)static_cast<const unsigned char*>(p_ciphered_nas_message), payload); // result = 0; // payload_length = p_ciphered_nas_message.lengthof(); ia3_128 enc; result = enc.decrypt( bit2int(p_ciphering_algorithm), Loading Loading @@ -319,6 +299,4 @@ OCTETSTRING fx__NG__NasDeciphering(const OCTETSTRING& p_ciphered_nas_message, co return os; } } // namespace NG__SecurityDefinitionsAndExternalFunctions ccsrc/Externals/ia2_128.cc +323 −199 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ #include "ia2_128.hh" #include <openssl/evp.h> #include <openssl/cmac.h> #include <openssl/aes.h> #include <cstring> #include <cstdlib> Loading @@ -18,6 +19,7 @@ namespace { constexpr size_t KEY_SIZE = 16; // 128 bits = 16 bytes constexpr size_t BLOCK_SIZE = 16; // AES block size constexpr size_t COUNTER_SIZE = 16; // Counter block size constexpr size_t MAC_SIZE = 4; // MAC size in bytes } int ia2_128::encrypt(const uint8_t algo_id, Loading Loading @@ -71,7 +73,7 @@ int ia2_128::encrypt(const uint8_t algo_id, return -1; } int ret = -1; int result = -1; int len = 0; int final_len = 0; Loading @@ -98,18 +100,18 @@ int ia2_128::encrypt(const uint8_t algo_id, *cyphered_length = static_cast<uint32_t>(len + final_len); loggers::get_instance().log("ia2_128::encrypt: cyphered_length: %d", *cyphered_length); loggers::get_instance().log_to_hexa("ia2_128::encrypt: cyphered: ", *cyphered, *cyphered_length); ret = 0; result = 0; cleanup: EVP_CIPHER_CTX_free(ctx); if (ret != 0) { if (result != 0) { std::free(*cyphered); *cyphered = nullptr; *cyphered_length = 0; } loggers::get_instance().log("<<< ia2_128::encrypt: ret: %d", ret); return ret; loggers::get_instance().log("<<< ia2_128::encrypt: result: %d", result); return result; } int ia2_128::decrypt(const uint8_t algo_id, Loading Loading @@ -163,7 +165,7 @@ int ia2_128::decrypt(const uint8_t algo_id, return -1; } int ret = -1; int result = -1; int len = 0; int final_len = 0; Loading @@ -190,18 +192,18 @@ int ia2_128::decrypt(const uint8_t algo_id, *payload_length = static_cast<uint32_t>(len + final_len); loggers::get_instance().log("ia2_128::decrypt: cyphered_length: %d", *payload_length); loggers::get_instance().log_to_hexa("ia2_128::decrypt: cyphered: ", *payload, *payload_length); ret = 0; result = 0; cleanup: EVP_CIPHER_CTX_free(ctx); if (ret != 0) { if (result != 0) { std::free(*payload); *payload = nullptr; *payload_length = 0; } loggers::get_instance().log("<<< ia2_128::decrypt: ret: %d", ret); return ret; loggers::get_instance().log("<<< ia2_128::decrypt: result: %d", result); return result; } int ia2_128::mac(const uint8_t algo_id, Loading Loading @@ -235,18 +237,73 @@ int ia2_128::mac(const uint8_t algo_id, } // Allocate memory for ciphertext *mac = static_cast<unsigned char*>(std::malloc(4)); *mac = static_cast<unsigned char*>(std::malloc(MAC_SIZE)); if (!*mac) { loggers::get_instance().error("ia2_128::mac: Failed to allocate memory"); return -1; } *mac_length = 4; *mac_length = MAC_SIZE; // TODO: Implement AES CMAC computation according to 3GPP TS 33.401 // Generate input block for CMAC unsigned char* input_block = nullptr; uint32_t input_block_length = 0; int result = generate_input_block(count, bearer, direction, payload, payload_length * 8, &input_block, &input_block_length); if (result != 0) { loggers::get_instance().error("ia2_128::mac: Failed to generate block for CMAC"); return -1; } // Compute CMAC using OpenSSL // Note: CMAC functions are deprecated in OpenSSL 3.0+, but we use them for compatibility // with older versions. For OpenSSL 3.0+, consider using EVP_MAC API instead. // CMAC for AES-128 produces 16 bytes, but we need only 4 bytes (32 bits) for 128-EIA2 unsigned char cmac_output[16]; size_t cmac_len = sizeof(cmac_output); // Use deprecated CMAC API for compatibility with OpenSSL 1.x CMAC_CTX* ctx = CMAC_CTX_new(); if (!ctx) { loggers::get_instance().error("ia2_128::mac: Failed to intialize CMAC context"); std::free(input_block); return -1; } result = -1; // Initialize CMAC context with AES-128 if (CMAC_Init(ctx, knas_int, KEY_SIZE, EVP_aes_128_cbc(), nullptr) != 1) { loggers::get_instance().error("ia2_128::mac: CMAC_Init failure"); goto cleanup; } // Update CMAC with input block if (CMAC_Update(ctx, input_block, input_block_length) != 1) { loggers::get_instance().error("ia2_128::mac: CMAC_Update failure"); goto cleanup; } // Finalize CMAC - get the MAC (16 bytes for AES-128) if (CMAC_Final(ctx, cmac_output, &cmac_len) != 1) { loggers::get_instance().error("ia2_128::mac: CMAC_Final failure"); goto cleanup; } #pragma GCC diagnostic pop // 128-EIA2 produces 32-bit (4-byte) MAC // Truncate CMAC output to 4 bytes (take the first 4 bytes) std::memcpy(*mac, cmac_output, MAC_SIZE); loggers::get_instance().log_to_hexa("ia2_128::mac: mac: ", *mac, *mac_length); loggers::get_instance().log("<<< ia2_128::mac: ret: 0"); cleanup: #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" CMAC_CTX_free(ctx); #pragma GCC diagnostic pop std::free(input_block); loggers::get_instance().log("<<< ia2_128::mac: result: 0"); return 0; } Loading @@ -264,3 +321,70 @@ void ia2_128::generate_counter_block(const uint32_t count, // Remaining bytes are zero } int ia2_128::generate_input_block(const uint32_t count, const uint8_t bearer, const uint8_t direction, const unsigned char* payload, const uint32_t payload_length, unsigned char** input_block, uint32_t* input_block_length) { loggers::get_instance().log(">>> ia2_128::generate_input_block: count: %u", count); loggers::get_instance().log(">>> ia2_128::generate_input_block: bearer: %d", bearer); loggers::get_instance().log(">>> ia2_128::generate_input_block: direction: %d", direction); loggers::get_instance().log(">>> ia2_128::generate_input_block: payload_length: %d", payload_length); loggers::get_instance().log_to_hexa(">>> ia2_128::generate_input_block: payload", payload, payload_length / 8); if (!payload || !input_block || !input_block_length) { loggers::get_instance().error("ia2_128::generate_input_block: Wrong input parameters"); return -1; } // 3GPP TS 33.401 Section 5.1.4.2: // Input block = COUNT || (BEARER || DIRECTION || 0^26) || MESSAGE // COUNT: 4 bytes // BEARER + DIRECTION + padding: 4 bytes (32 bits total) // MESSAGE: (payload_length + 7) / 8 bytes (convert bits to bytes, round up) uint32_t payload_bytes = (payload_length + 7) / 8; uint32_t total_length = 4 + 4 + payload_bytes; loggers::get_instance().log("ia2_128::generate_input_block: payload_bytes: %u", payload_bytes); loggers::get_instance().log("ia2_128::generate_input_block: total_length: %u", total_length); // Allocate input block *input_block = static_cast<unsigned char*>(std::malloc(total_length)); if (!*input_block) { loggers::get_instance().error("ia2_128::mac: Failed to allocate memory"); return -1; } // Set COUNT (bytes 0-3, big-endian) unsigned char* ptr = *input_block; ptr[0] = static_cast<unsigned char>((count >> 24) & 0xFF); ptr[1] = static_cast<unsigned char>((count >> 16) & 0xFF); ptr[2] = static_cast<unsigned char>((count >> 8) & 0xFF); ptr[3] = static_cast<unsigned char>(count & 0xFF); ptr += 4; // Set BEARER and DIRECTION (byte 4) // Bits 0-4: BEARER (5 bits), Bit 5: DIRECTION (1 bit), Bits 6-31: padding (26 bits) ptr[0] = static_cast<unsigned char>((bearer << 3) | (direction << 2)); ptr[1] = 0; ptr[2] = 0; ptr[3] = 0; ptr += 4; // Copy payload std::memcpy(ptr, payload, payload_bytes); loggers::get_instance().log_to_hexa("ia2_128::generate_input_block: ptr", ptr, total_length); // Handle partial bytes if payload_length is not a multiple of 8 if (payload_length % 8 != 0) { // Clear the unused bits in the last byte uint32_t unused_bits = 8 - (payload_length % 8); uint8_t mask = static_cast<uint8_t>(0xFF << unused_bits); ptr[payload_bytes - 1] &= ~mask; } loggers::get_instance().log("ia2_128::generate_input_block: *input_block_length: %d", *input_block_length); *input_block_length = total_length; loggers::get_instance().log_to_hexa("<<< ia2_128::generate_input_block: ptr", static_cast<const uint8_t*>(*input_block), *input_block_length * sizeof(uint32_t)); return 0; } ccsrc/Externals/ia2_128.hh +138 −117 Original line number Diff line number Diff line Loading @@ -8,8 +8,7 @@ * as specified in 3GPP TS 33.401 for NAS encryption/decryption. */ #ifndef IA2_128_H #define IA2_128_H #pragma once #include <cstdint> #include <cstddef> Loading Loading @@ -157,7 +156,29 @@ private: const uint8_t bearer, const uint8_t direction, unsigned char* counter_block); }; #endif // IA2_128_H /** * @brief Generates the input block for CMAC computation * * According to 3GPP TS 33.401, the input to CMAC consists of: * - COUNT (32 bits, big-endian) * - BEARER (5 bits) + DIRECTION (1 bit) + padding (26 bits) * - MESSAGE (variable length, in bits) * * @param count NAS count * @param bearer Bearer identifier * @param direction Direction bit * @param message Pointer to message data * @param message_length Length of message in bits * @param input_block Output buffer for the input block (will be allocated) * @param input_block_length Output variable for the input block length in bytes * @return 0 on success, negative on error */ int generate_input_block(const uint32_t count, const uint8_t bearer, const uint8_t direction, const unsigned char* message, const uint32_t message_length, unsigned char** input_block, uint32_t* input_block_length); }; ccsrc/Externals/ia3_128.cc +0 −24 Original line number Diff line number Diff line Loading @@ -14,8 +14,6 @@ #include <cstring> #include <cstdlib> #include "zuc.hh" #include "loggers.hh" namespace { Loading @@ -23,28 +21,6 @@ namespace { constexpr size_t IV_SIZE = 16; // Initialization vector size } // uint32_t ia3_128::lfsr_s0 = 0; // uint32_t ia3_128::lfsr_s1 = 0; // uint32_t ia3_128::lfsr_s2 = 0; // uint32_t ia3_128::lfsr_s3 = 0; // uint32_t ia3_128::lfsr_s4 = 0; // uint32_t ia3_128::lfsr_s5 = 0; // uint32_t ia3_128::lfsr_s6 = 0; // uint32_t ia3_128::lfsr_s7 = 0; // uint32_t ia3_128::lfsr_s8 = 0; // uint32_t ia3_128::lfsr_s9 = 0; // uint32_t ia3_128::lfsr_s10 = 0; // uint32_t ia3_128::lfsr_s11 = 0; // uint32_t ia3_128::lfsr_s12 = 0; // uint32_t ia3_128::lfsr_s13 = 0; // uint32_t ia3_128::lfsr_s14 = 0; // uint32_t ia3_128::lfsr_s15 = 0; // uint32_t ia3_128::f_r1 = 0; // uint32_t ia3_128::f_r2 = 0; // uint32_t ia3_128::brc_x0 = 0; // uint32_t ia3_128::brc_x1 = 0; // uint32_t ia3_128::brc_x2 = 0; // uint32_t ia3_128::brc_x3 = 0; void ia3_128::reset() { lfsr_s0 = 0; lfsr_s1 = 0; Loading Loading
NAS_ETSI.code-workspace +0 −12 Original line number Diff line number Diff line Loading @@ -6,18 +6,6 @@ { "path": "../../frameworks/titan/titan.core" }, { "path": "../5G-AKA-simulation-in-C" }, { "path": "../free5gc" }, { "path": "../free5gc.util" }, { "path": "../5G_ciphered_NAS_decipher_tool" }, { "path": "../open5gs" }, Loading
ccsrc/Externals/NG_security_ext.cc +246 −268 Original line number Diff line number Diff line Loading @@ -19,8 +19,6 @@ #include "ia2_128.hh" #include "ia3_128.hh" #include "zuc.hh" namespace NG__SecurityDefinitionsAndExternalFunctions { /** Loading Loading @@ -56,10 +54,7 @@ OCTETSTRING fx__NG__NasIntegrityAlgorithm(const OCTETSTRING& p_encoded_nas_pdu, uint32_t mac_length; uint32_t nas_count = converter::get_instance().bytes_to_int(std::vector<uint8_t>(static_cast<const unsigned char*>(p_nas_count), static_cast<const unsigned char*>(p_nas_count) + p_nas_count.lengthof())); int result = -1; if (bit2int(p_integrity_algorithm) == 0) { // IA0/NEA0: No integrity protection loggers::get_instance().log("fx__NG__NasIntegrityAlgorithm: No integrity protection"); return p_encoded_nas_pdu; // no integrity protection } else if (bit2int(p_integrity_algorithm) == 1) { // IA1_128/NEA1_128: Snow 3G based algorithm if (bit2int(p_integrity_algorithm) == 1) { // IA1_128/NEA1_128: Snow 3G based algorithm loggers::get_instance().log("fx__NG__NasIntegrityAlgorithm: ia1_128 selected"); ia1_128 enc; result = enc.mac( Loading @@ -76,7 +71,7 @@ OCTETSTRING fx__NG__NasIntegrityAlgorithm(const OCTETSTRING& p_encoded_nas_pdu, } else if (bit2int(p_integrity_algorithm) == 2) { // IA2_128/NEA2_128: AES 128 CTR based algorithm loggers::get_instance().log("fx__NG__NasIntegrityAlgorithm: ia2_128 selected"); ia2_128 enc; result = enc.encrypt( result = enc.mac( bit2int(p_integrity_algorithm), static_cast<const unsigned char*>(bit2oct(p_knas_int)), nas_count, Loading Loading @@ -117,7 +112,7 @@ OCTETSTRING fx__NG__NasIntegrityAlgorithm(const OCTETSTRING& p_encoded_nas_pdu, return int2oct(0, 0); } OCTETSTRING os(mac_length, (const unsigned char*)mac); free(mac); std::free(mac); loggers::get_instance().log_msg("<<< fx__NG__NasIntegrityAlgorithm: ", os); return os; Loading Loading @@ -180,14 +175,6 @@ OCTETSTRING fx__NG__NasCiphering(const OCTETSTRING& p_encoded_nas_pdu,const BIT ); } else if (bit2int(p_ciphering_algorithm) == 3) { // IA3_128/NEA3_128: ZUC based algorithm loggers::get_instance().log("fx__NG__NasCiphering: ia3_128 selected"); // loggers::get_instance().log("fx__NG__NasCiphering: %d", p_encoded_nas_pdu.lengthof()); // cyphered = (unsigned char*) std::malloc(p_encoded_nas_pdu.lengthof()); // zuc_eea3( // (unsigned char*)static_cast<const unsigned char*>(bit2oct(p_knas_enc)), // nas_count, (uint32_t)bit2int(p_bearer_id), p_direction, // p_encoded_nas_pdu.lengthof() * 8, (unsigned char*)static_cast<const unsigned char*>(p_encoded_nas_pdu), cyphered); // result = 0; // cyphered_length = p_encoded_nas_pdu.lengthof(); ia3_128 enc; result = enc.encrypt( bit2int(p_ciphering_algorithm), Loading Loading @@ -278,13 +265,6 @@ OCTETSTRING fx__NG__NasDeciphering(const OCTETSTRING& p_ciphered_nas_message, co &payload_length ); } else if (bit2int(p_ciphering_algorithm) == 3) { // IA3_128/NEA3_128: ZUC based algorithm // loggers::get_instance().log("fx__NG__NasDeciphering: ia1_128 selected"); // zuc_eea3( // (unsigned char*)static_cast<const unsigned char*>(bit2oct(p_knas_enc)), // nas_count, bit2int(p_bearer_id), p_direction, // p_ciphered_nas_message.lengthof() * 8, (unsigned char*)static_cast<const unsigned char*>(p_ciphered_nas_message), payload); // result = 0; // payload_length = p_ciphered_nas_message.lengthof(); ia3_128 enc; result = enc.decrypt( bit2int(p_ciphering_algorithm), Loading Loading @@ -319,6 +299,4 @@ OCTETSTRING fx__NG__NasDeciphering(const OCTETSTRING& p_ciphered_nas_message, co return os; } } // namespace NG__SecurityDefinitionsAndExternalFunctions
ccsrc/Externals/ia2_128.cc +323 −199 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ #include "ia2_128.hh" #include <openssl/evp.h> #include <openssl/cmac.h> #include <openssl/aes.h> #include <cstring> #include <cstdlib> Loading @@ -18,6 +19,7 @@ namespace { constexpr size_t KEY_SIZE = 16; // 128 bits = 16 bytes constexpr size_t BLOCK_SIZE = 16; // AES block size constexpr size_t COUNTER_SIZE = 16; // Counter block size constexpr size_t MAC_SIZE = 4; // MAC size in bytes } int ia2_128::encrypt(const uint8_t algo_id, Loading Loading @@ -71,7 +73,7 @@ int ia2_128::encrypt(const uint8_t algo_id, return -1; } int ret = -1; int result = -1; int len = 0; int final_len = 0; Loading @@ -98,18 +100,18 @@ int ia2_128::encrypt(const uint8_t algo_id, *cyphered_length = static_cast<uint32_t>(len + final_len); loggers::get_instance().log("ia2_128::encrypt: cyphered_length: %d", *cyphered_length); loggers::get_instance().log_to_hexa("ia2_128::encrypt: cyphered: ", *cyphered, *cyphered_length); ret = 0; result = 0; cleanup: EVP_CIPHER_CTX_free(ctx); if (ret != 0) { if (result != 0) { std::free(*cyphered); *cyphered = nullptr; *cyphered_length = 0; } loggers::get_instance().log("<<< ia2_128::encrypt: ret: %d", ret); return ret; loggers::get_instance().log("<<< ia2_128::encrypt: result: %d", result); return result; } int ia2_128::decrypt(const uint8_t algo_id, Loading Loading @@ -163,7 +165,7 @@ int ia2_128::decrypt(const uint8_t algo_id, return -1; } int ret = -1; int result = -1; int len = 0; int final_len = 0; Loading @@ -190,18 +192,18 @@ int ia2_128::decrypt(const uint8_t algo_id, *payload_length = static_cast<uint32_t>(len + final_len); loggers::get_instance().log("ia2_128::decrypt: cyphered_length: %d", *payload_length); loggers::get_instance().log_to_hexa("ia2_128::decrypt: cyphered: ", *payload, *payload_length); ret = 0; result = 0; cleanup: EVP_CIPHER_CTX_free(ctx); if (ret != 0) { if (result != 0) { std::free(*payload); *payload = nullptr; *payload_length = 0; } loggers::get_instance().log("<<< ia2_128::decrypt: ret: %d", ret); return ret; loggers::get_instance().log("<<< ia2_128::decrypt: result: %d", result); return result; } int ia2_128::mac(const uint8_t algo_id, Loading Loading @@ -235,18 +237,73 @@ int ia2_128::mac(const uint8_t algo_id, } // Allocate memory for ciphertext *mac = static_cast<unsigned char*>(std::malloc(4)); *mac = static_cast<unsigned char*>(std::malloc(MAC_SIZE)); if (!*mac) { loggers::get_instance().error("ia2_128::mac: Failed to allocate memory"); return -1; } *mac_length = 4; *mac_length = MAC_SIZE; // TODO: Implement AES CMAC computation according to 3GPP TS 33.401 // Generate input block for CMAC unsigned char* input_block = nullptr; uint32_t input_block_length = 0; int result = generate_input_block(count, bearer, direction, payload, payload_length * 8, &input_block, &input_block_length); if (result != 0) { loggers::get_instance().error("ia2_128::mac: Failed to generate block for CMAC"); return -1; } // Compute CMAC using OpenSSL // Note: CMAC functions are deprecated in OpenSSL 3.0+, but we use them for compatibility // with older versions. For OpenSSL 3.0+, consider using EVP_MAC API instead. // CMAC for AES-128 produces 16 bytes, but we need only 4 bytes (32 bits) for 128-EIA2 unsigned char cmac_output[16]; size_t cmac_len = sizeof(cmac_output); // Use deprecated CMAC API for compatibility with OpenSSL 1.x CMAC_CTX* ctx = CMAC_CTX_new(); if (!ctx) { loggers::get_instance().error("ia2_128::mac: Failed to intialize CMAC context"); std::free(input_block); return -1; } result = -1; // Initialize CMAC context with AES-128 if (CMAC_Init(ctx, knas_int, KEY_SIZE, EVP_aes_128_cbc(), nullptr) != 1) { loggers::get_instance().error("ia2_128::mac: CMAC_Init failure"); goto cleanup; } // Update CMAC with input block if (CMAC_Update(ctx, input_block, input_block_length) != 1) { loggers::get_instance().error("ia2_128::mac: CMAC_Update failure"); goto cleanup; } // Finalize CMAC - get the MAC (16 bytes for AES-128) if (CMAC_Final(ctx, cmac_output, &cmac_len) != 1) { loggers::get_instance().error("ia2_128::mac: CMAC_Final failure"); goto cleanup; } #pragma GCC diagnostic pop // 128-EIA2 produces 32-bit (4-byte) MAC // Truncate CMAC output to 4 bytes (take the first 4 bytes) std::memcpy(*mac, cmac_output, MAC_SIZE); loggers::get_instance().log_to_hexa("ia2_128::mac: mac: ", *mac, *mac_length); loggers::get_instance().log("<<< ia2_128::mac: ret: 0"); cleanup: #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" CMAC_CTX_free(ctx); #pragma GCC diagnostic pop std::free(input_block); loggers::get_instance().log("<<< ia2_128::mac: result: 0"); return 0; } Loading @@ -264,3 +321,70 @@ void ia2_128::generate_counter_block(const uint32_t count, // Remaining bytes are zero } int ia2_128::generate_input_block(const uint32_t count, const uint8_t bearer, const uint8_t direction, const unsigned char* payload, const uint32_t payload_length, unsigned char** input_block, uint32_t* input_block_length) { loggers::get_instance().log(">>> ia2_128::generate_input_block: count: %u", count); loggers::get_instance().log(">>> ia2_128::generate_input_block: bearer: %d", bearer); loggers::get_instance().log(">>> ia2_128::generate_input_block: direction: %d", direction); loggers::get_instance().log(">>> ia2_128::generate_input_block: payload_length: %d", payload_length); loggers::get_instance().log_to_hexa(">>> ia2_128::generate_input_block: payload", payload, payload_length / 8); if (!payload || !input_block || !input_block_length) { loggers::get_instance().error("ia2_128::generate_input_block: Wrong input parameters"); return -1; } // 3GPP TS 33.401 Section 5.1.4.2: // Input block = COUNT || (BEARER || DIRECTION || 0^26) || MESSAGE // COUNT: 4 bytes // BEARER + DIRECTION + padding: 4 bytes (32 bits total) // MESSAGE: (payload_length + 7) / 8 bytes (convert bits to bytes, round up) uint32_t payload_bytes = (payload_length + 7) / 8; uint32_t total_length = 4 + 4 + payload_bytes; loggers::get_instance().log("ia2_128::generate_input_block: payload_bytes: %u", payload_bytes); loggers::get_instance().log("ia2_128::generate_input_block: total_length: %u", total_length); // Allocate input block *input_block = static_cast<unsigned char*>(std::malloc(total_length)); if (!*input_block) { loggers::get_instance().error("ia2_128::mac: Failed to allocate memory"); return -1; } // Set COUNT (bytes 0-3, big-endian) unsigned char* ptr = *input_block; ptr[0] = static_cast<unsigned char>((count >> 24) & 0xFF); ptr[1] = static_cast<unsigned char>((count >> 16) & 0xFF); ptr[2] = static_cast<unsigned char>((count >> 8) & 0xFF); ptr[3] = static_cast<unsigned char>(count & 0xFF); ptr += 4; // Set BEARER and DIRECTION (byte 4) // Bits 0-4: BEARER (5 bits), Bit 5: DIRECTION (1 bit), Bits 6-31: padding (26 bits) ptr[0] = static_cast<unsigned char>((bearer << 3) | (direction << 2)); ptr[1] = 0; ptr[2] = 0; ptr[3] = 0; ptr += 4; // Copy payload std::memcpy(ptr, payload, payload_bytes); loggers::get_instance().log_to_hexa("ia2_128::generate_input_block: ptr", ptr, total_length); // Handle partial bytes if payload_length is not a multiple of 8 if (payload_length % 8 != 0) { // Clear the unused bits in the last byte uint32_t unused_bits = 8 - (payload_length % 8); uint8_t mask = static_cast<uint8_t>(0xFF << unused_bits); ptr[payload_bytes - 1] &= ~mask; } loggers::get_instance().log("ia2_128::generate_input_block: *input_block_length: %d", *input_block_length); *input_block_length = total_length; loggers::get_instance().log_to_hexa("<<< ia2_128::generate_input_block: ptr", static_cast<const uint8_t*>(*input_block), *input_block_length * sizeof(uint32_t)); return 0; }
ccsrc/Externals/ia2_128.hh +138 −117 Original line number Diff line number Diff line Loading @@ -8,8 +8,7 @@ * as specified in 3GPP TS 33.401 for NAS encryption/decryption. */ #ifndef IA2_128_H #define IA2_128_H #pragma once #include <cstdint> #include <cstddef> Loading Loading @@ -157,7 +156,29 @@ private: const uint8_t bearer, const uint8_t direction, unsigned char* counter_block); }; #endif // IA2_128_H /** * @brief Generates the input block for CMAC computation * * According to 3GPP TS 33.401, the input to CMAC consists of: * - COUNT (32 bits, big-endian) * - BEARER (5 bits) + DIRECTION (1 bit) + padding (26 bits) * - MESSAGE (variable length, in bits) * * @param count NAS count * @param bearer Bearer identifier * @param direction Direction bit * @param message Pointer to message data * @param message_length Length of message in bits * @param input_block Output buffer for the input block (will be allocated) * @param input_block_length Output variable for the input block length in bytes * @return 0 on success, negative on error */ int generate_input_block(const uint32_t count, const uint8_t bearer, const uint8_t direction, const unsigned char* message, const uint32_t message_length, unsigned char** input_block, uint32_t* input_block_length); };
ccsrc/Externals/ia3_128.cc +0 −24 Original line number Diff line number Diff line Loading @@ -14,8 +14,6 @@ #include <cstring> #include <cstdlib> #include "zuc.hh" #include "loggers.hh" namespace { Loading @@ -23,28 +21,6 @@ namespace { constexpr size_t IV_SIZE = 16; // Initialization vector size } // uint32_t ia3_128::lfsr_s0 = 0; // uint32_t ia3_128::lfsr_s1 = 0; // uint32_t ia3_128::lfsr_s2 = 0; // uint32_t ia3_128::lfsr_s3 = 0; // uint32_t ia3_128::lfsr_s4 = 0; // uint32_t ia3_128::lfsr_s5 = 0; // uint32_t ia3_128::lfsr_s6 = 0; // uint32_t ia3_128::lfsr_s7 = 0; // uint32_t ia3_128::lfsr_s8 = 0; // uint32_t ia3_128::lfsr_s9 = 0; // uint32_t ia3_128::lfsr_s10 = 0; // uint32_t ia3_128::lfsr_s11 = 0; // uint32_t ia3_128::lfsr_s12 = 0; // uint32_t ia3_128::lfsr_s13 = 0; // uint32_t ia3_128::lfsr_s14 = 0; // uint32_t ia3_128::lfsr_s15 = 0; // uint32_t ia3_128::f_r1 = 0; // uint32_t ia3_128::f_r2 = 0; // uint32_t ia3_128::brc_x0 = 0; // uint32_t ia3_128::brc_x1 = 0; // uint32_t ia3_128::brc_x2 = 0; // uint32_t ia3_128::brc_x3 = 0; void ia3_128::reset() { lfsr_s0 = 0; lfsr_s1 = 0; Loading