Loading ccsrc/Externals/NG_security_ext.cc +24 −24 Original line number Diff line number Diff line Loading @@ -61,32 +61,32 @@ OCTETSTRING fx__NG__NasIntegrityAlgorithm(const OCTETSTRING& p_encoded_nas_pdu, return p_encoded_nas_pdu; // no integrity protection } else 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( // bit2int(p_integrity_algorithm), // static_cast<const unsigned char*>(bit2oct(p_knas_int)), // nas_count, // bit2int(p_bearer_id), // p_direction, // static_cast<const unsigned char*>(p_encoded_nas_pdu), // p_encoded_nas_pdu.lengthof(), // &mac, // &mac_length // ); ia1_128 enc; result = enc.mac( bit2int(p_integrity_algorithm), static_cast<const unsigned char*>(bit2oct(p_knas_int)), nas_count, bit2int(p_bearer_id), p_direction, static_cast<const unsigned char*>(p_encoded_nas_pdu), p_encoded_nas_pdu.lengthof(), &mac, &mac_length ); } 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( // bit2int(p_integrity_algorithm), // static_cast<const unsigned char*>(bit2oct(p_knas_int)), // nas_count, // bit2int(p_bearer_id), // p_direction, // static_cast<const unsigned char*>(p_encoded_nas_pdu), // p_encoded_nas_pdu.lengthof(), // &mac, // &mac_length // ); ia2_128 enc; result = enc.encrypt( bit2int(p_integrity_algorithm), static_cast<const unsigned char*>(bit2oct(p_knas_int)), nas_count, bit2int(p_bearer_id), p_direction, static_cast<const unsigned char*>(p_encoded_nas_pdu), p_encoded_nas_pdu.lengthof(), &mac, &mac_length ); } else if (bit2int(p_integrity_algorithm) == 3) { // IA3_128/NEA3_128: ZUC based algorithm loggers::get_instance().log("fx__NG__NasIntegrityAlgorithm: ia3_128 selected"); ia3_128 enc; Loading ccsrc/Externals/ia1_128.cc +153 −5 Original line number Diff line number Diff line Loading @@ -21,6 +21,11 @@ namespace { constexpr size_t IV_SIZE = 16; // Initialization vector size } void ia1_128::reset() { loggers::get_instance().log("ia1_128::reset"); std::memset(&_ctx, 0, sizeof(_ctx)); } int ia1_128::encrypt(const uint8_t algo_id, const unsigned char* knas_enc, const uint32_t count, Loading Loading @@ -57,7 +62,8 @@ int ia1_128::encrypt(const uint8_t algo_id, return -1; } snow3g_context_t ctx; snow3g_initialize(count, bearer, direction, (const char *)knas_enc, &ctx); ctx.ciphering = true; snow3g_initialize(count, bearer, direction, knas_enc, &ctx); snow3g_generate(payload_length, payload, *cyphered, &ctx); *cyphered_length = payload_length; loggers::get_instance().log("ia1_128::encrypt: cyphered_length: %d", *cyphered_length); Loading Loading @@ -88,7 +94,118 @@ int ia1_128::decrypt(const uint8_t algo_id, cyphered, cyphered_length, payload, payload_length); } int ia1_128::snow3g_initialize(uint32_t count, uint8_t bearer, uint8_t direction, const char *knas_enc, snow3g_context_t *ctx) { int ia1_128::mac(const uint8_t algo_id, const unsigned char* knas_int, const uint32_t count, const uint8_t bearer, const uint8_t direction, const unsigned char* payload, const uint32_t payload_length, unsigned char** mac, uint32_t* mac_length) { loggers::get_instance().log_to_hexa(">>> ia1_128::mac: knas_int", knas_int, KEY_SIZE); loggers::get_instance().log(">>> ia1_128::mac: payload_length: %d", payload_length); loggers::get_instance().log_to_hexa(">>> ia1_128::mac: payload", payload, payload_length); loggers::get_instance().log(">>> ia1_128::mac: algo_id: %d", algo_id); loggers::get_instance().log(">>> ia1_128::mac: count: %u", count); loggers::get_instance().log(">>> ia1_128::mac: bearer: %d", bearer); loggers::get_instance().log(">>> ia1_128::mac: direction: %d", direction); // Input validation if (!knas_int || !payload || !mac || !mac_length) { loggers::get_instance().error("ia1_128::mac: Wrong input parameters"); return -1; } if (payload_length == 0) { loggers::get_instance().error("ia1_128::mac: Wrong payload length"); *mac = nullptr; *mac_length = 0; return 0; } // Allocate memory for ciphertext *mac = static_cast<unsigned char*>(std::malloc(4)); if (!*mac) { loggers::get_instance().error("ia1_128::mac: Failed to allocate memory"); return -1; } *mac_length = 4; snow3g_context_t ctx; ctx.ciphering = false; snow3g_initialize(count, bearer, direction, knas_int, &ctx); uint32_t z[5] = {0}; snow3g_generate(5, (uint32_t*)z, &ctx); loggers::get_instance().log_to_hexa("ia1_128::mac: z: ", (const unsigned char*)z, 5 * sizeof(uint32_t)); uint64_t p = (uint64_t)z[0] << 32 | (uint64_t)z[1]; uint64_t q = (uint64_t)z[2] << 32 | (uint64_t)z[3]; uint32_t d; uint64_t length = payload_length * 8; // Length in bits loggers::get_instance().log("ia1_128::mac: length: %ld", length); if ((length % 64) == 0) d = (length>>6) + 1; else d = (length>>6) + 2; loggers::get_instance().log("ia1_128::mac: d: %u", d); uint64_t c = 0x1b; uint64_t eval = 0; uint64_t v = 0; for (uint64_t i = 0; i < d - 2; i++) { v = eval ^ ((uint64_t)payload[8*i ]<<56 | (uint64_t)payload[8*i+1] << 48 | (uint64_t)payload[8*i+2]<<40 | (uint64_t)payload[8*i+3] << 32 | (uint64_t)payload[8*i+4]<<24 | (uint64_t)payload[8*i+5] << 16 | (uint64_t)payload[8*i+6]<< 8 | (uint64_t)payload[8*i+7] ); eval = mul64(v, p, c); } loggers::get_instance().log_to_hexa("ia1_128::mac: eval: ", (const unsigned char*)&eval, sizeof(uint64_t)); uint64_t rem = length % 64; if (rem == 0) { rem = 64; } loggers::get_instance().log("ia1_128::mac:rem: %d", rem); uint64_t m_d_2 = 0; uint8_t i = 0; while (rem > 7) { m_d_2 |= (uint64_t)payload[8*(d-2)+i] << (8*(7-i)); rem -= 8; i++; } if (rem > 0) { m_d_2 |= (uint64_t)(payload[8*(d-2)+i] & mask8bit(rem)) << (8*(7-i)); } loggers::get_instance().log_to_hexa("ia1_128::mac: m_d_2: ", (const unsigned char*)&m_d_2, sizeof(uint64_t)); v = eval ^ m_d_2; eval = mul64(v, p, c); loggers::get_instance().log_to_hexa("ia1_128::mac: eval: ", (const unsigned char*)&eval, sizeof(uint64_t)); /* for d-1 */ eval ^= length; loggers::get_instance().log_to_hexa("ia1_128::mac: eval: ", (const unsigned char*)&eval, sizeof(uint64_t)); /* Multiply by q */ eval = mul64(eval, q, c); loggers::get_instance().log_to_hexa("ia1_128::mac: eval: ", (const unsigned char*)&eval, sizeof(uint64_t)); /* XOR with z_5: this is a modification to the reference C code, which forgot to XOR z[5] */ for (i = 0; i < 4; i++) { (*mac)[i] = ((eval >> (56-(i*8))) ^ (z[4] >> (24-(i*8)))) & 0xff; } loggers::get_instance().log_to_hexa("ia1_128::mac: mac: ", *mac, *mac_length); loggers::get_instance().log("<<< ia1_128::mac: ret: 0"); return 0; } int ia1_128::snow3g_initialize(const uint32_t count, const uint8_t bearer, const uint8_t direction, const unsigned char *knas_enc, snow3g_context_t *ctx) { loggers::get_instance().log_to_hexa(">>> ia1_128::snow3g_initialize: knas_enc", (const unsigned char*)knas_enc, 16); loggers::get_instance().log(">>> ia1_128::snow3g_initialize: count: %u", count); loggers::get_instance().log(">>> ia1_128::snow3g_initialize: bearer: %d", bearer); Loading @@ -108,9 +225,15 @@ int ia1_128::snow3g_initialize(uint32_t count, uint8_t bearer, uint8_t direction snow_key.key[0] = WORD_128(knas_enc, 3); snow_key.iv[3] = count; if (ctx->ciphering) { snow_key.iv[2] = ((bearer & 0x1F) << 27) | ((direction & 0x01) << 26); snow_key.iv[1] = snow_key.iv[3]; snow_key.iv[0] = snow_key.iv[2]; } else { snow_key.iv[2] = ((bearer & 0x1F) << 27); snow_key.iv[1] = count ^ ( direction << 31 ); snow_key.iv[0] = ((bearer & 0x1F) << 27) ^ (direction << 15); } loggers::get_instance().log_to_hexa("ia1_128::snow3g_initialize: snow_key.key", (const unsigned char*)snow_key.key, 4 * sizeof(uint32_t)); loggers::get_instance().log_to_hexa("ia1_128::snow3g_initialize: snow_key.iv", (const unsigned char*)snow_key.iv, 4 * sizeof(uint32_t)); Loading Loading @@ -162,3 +285,28 @@ int ia1_128::snow3g_generate(size_t nb_byte, const unsigned char *in, unsigned c loggers::get_instance().log_to_hexa("<<< SNOW: out", (const unsigned char*)out, nb_byte); return 0; } int ia1_128::snow3g_generate(size_t nb, uint32_t* out, snow3g_context_t* ctx) { loggers::get_instance().log(">>> snow3g_generate: nb: %u", nb); // Sanity checks if (ctx == NULL) { loggers::get_instance().error("snow3g_generate: Wrong parameters"); return -1; } size_t i = 0; /* init */ clock_fsm(ctx); lfsr_keystream(ctx); for (i = 0; i < nb; i++) { uint32_t f; out[i] = clock_fsm(ctx) ^ ctx->lfsr[0]; lfsr_keystream(ctx); } loggers::get_instance().log_to_hexa("<<< snow3g_generate: out", (const unsigned char*)out, nb); return 0; } ccsrc/Externals/ia1_128.hh +68 −4 Original line number Diff line number Diff line Loading @@ -59,6 +59,7 @@ class ia1_128 { typedef struct snow_ctx_st { uint32_t lfsr[SNOW_KEY_SIZE]; struct fsm_st fsm; bool ciphering; } snow3g_context_t; public: Loading Loading @@ -153,6 +154,35 @@ public: unsigned char** payload, uint32_t* payload_length); /** * @brief Compute NAS integrity protection using SNOW 3G algorithm * * This method computes the integrity protection using the SNOW 3G algorithm. * * @param algo_id Algorithm identifier (should be 3 for SNOW 3G) * @param knas_int Pointer to the 16-byte NAS integrity key * @param count 32-bit NAS count value * @param bearer 5-bit bearer identifier (only lower 5 bits are used) * @param direction Direction bit (0 = uplink, 1 = downlink) * @param payload Pointer to pointer that will receive the decrypted data (caller must free) * @param payload_length Pointer to variable that will receive the decrypted data length * @param mac Pointer to the mac to decrypt * @param mac_length Length of the mac in bytes * @return 0 on success, negative value on error * * @note The caller is responsible for freeing the memory allocated for payload * @note payload_length will be equal to cyphered_length for stream ciphers */ int mac(const uint8_t algo_id, const unsigned char* knas_int, const uint32_t count, const uint8_t bearer, const uint8_t direction, const unsigned char* payload, const uint32_t payload_length, unsigned char** mac, uint32_t* mac_length); private: const uint32_t S1_T0[256] = { 0xa56363c6U, 0x847c7cf8U, 0x997777eeU, 0x8d7b7bf6U, Loading Loading @@ -824,9 +854,41 @@ private: 0x9EE2651CU, 0x86ED25D1U, 0xAEFCE52FU, 0xB6F3A5E2U, }; inline uint32_t snow_s1(uint32_t in) { return S1_T0[BYTE32(in, 3)] ^ S1_T1[BYTE32(in, 2)] ^ S1_T2[BYTE32(in, 1)] ^ S1_T3[BYTE32(in, 0)]; }; snow3g_context_t _ctx; void reset(); inline uint32_t snow_s1(const uint32_t in) { return S1_T0[BYTE32(in, 3)] ^ S1_T1[BYTE32(in, 2)] ^ S1_T2[BYTE32(in, 1)] ^ S1_T3[BYTE32(in, 0)]; }; inline uint32_t snow_s2(const uint32_t in) { return S2_T0[BYTE32(in, 3)] ^ S2_T1[BYTE32(in, 2)] ^ S2_T2[BYTE32(in, 1)] ^ S2_T3[BYTE32(in, 0)]; }; inline uint64_t mul64(uint64_t V, uint64_t P, uint64_t c) { uint64_t result = 0; for (int8_t i = 0; i < 64; i++) { if ((P >> i ) & 0x1) { result ^= mul64x_pow(V, i, c); } } return result; }; inline uint64_t mul64x_pow(uint64_t V, uint8_t i, uint64_t c) { if (i == 0) { return V; } else { return mul64x(mul64x_pow(V, i-1, c) , c); } }; inline uint64_t mul64x(uint64_t V, uint64_t c) { if (V & 0x8000000000000000) { return (V << 1) ^ c; } else { return V << 1; } }; inline uint32_t snow_s2(uint32_t in) { return S2_T0[BYTE32(in, 3)] ^ S2_T1[BYTE32(in, 2)] ^ S2_T2[BYTE32(in, 1)] ^ S2_T3[BYTE32(in, 0)]; }; inline uint8_t mask8bit(int n) { return 0xFF ^ ((1<<(8-n)) - 1); }; /* Clocking operations */ inline void lfsr_init(uint32_t f, snow3g_context_t *ctx) { Loading Loading @@ -953,7 +1015,7 @@ private: * @param key 16-byte encryption key * @param iv 16-byte initialization vector */ int snow3g_initialize(uint32_t count, uint8_t bearer, uint8_t direction, const char *knas_enc, snow3g_context_t *ctx); int snow3g_initialize(const uint32_t count, const uint8_t bearer, const uint8_t direction, const unsigned char *knas_enc, snow3g_context_t *ctx); /** * @brief Generate keystream using SNOW 3G Loading @@ -963,4 +1025,6 @@ private: * @param length Length of keystream needed in bytes */ int snow3g_generate(size_t nb_byte, const unsigned char* in, unsigned char* out, snow3g_context_t *ctx); int snow3g_generate(size_t nb_byte, uint32_t* out, snow3g_context_t *ctx); }; ccsrc/Externals/ia2_128.cc +46 −0 Original line number Diff line number Diff line Loading @@ -204,6 +204,52 @@ cleanup: return ret; } int ia2_128::mac(const uint8_t algo_id, const unsigned char* knas_int, const uint32_t count, const uint8_t bearer, const uint8_t direction, const unsigned char* payload, const uint32_t payload_length, unsigned char** mac, uint32_t* mac_length) { loggers::get_instance().log_to_hexa(">>> ia2_128::mac: knas_int", knas_int, KEY_SIZE); loggers::get_instance().log(">>> ia2_128::mac: payload_length: %d", payload_length); loggers::get_instance().log_to_hexa(">>> ia2_128::mac: payload", payload, payload_length); loggers::get_instance().log(">>> ia2_128::mac: algo_id: %d", algo_id); loggers::get_instance().log(">>> ia2_128::mac: count: %u", count); loggers::get_instance().log(">>> ia2_128::mac: bearer: %d", bearer); loggers::get_instance().log(">>> ia2_128::mac: direction: %d", direction); // Input validation if (!knas_int || !payload || !mac || !mac_length) { loggers::get_instance().error("ia2_128::mac: Wrong input parameters"); return -1; } if (payload_length == 0) { loggers::get_instance().error("ia2_128::mac: Wrong payload length"); *mac = nullptr; *mac_length = 0; return 0; } // Allocate memory for ciphertext *mac = static_cast<unsigned char*>(std::malloc(4)); if (!*mac) { loggers::get_instance().error("ia2_128::mac: Failed to allocate memory"); return -1; } *mac_length = 4; // TODO: Implement AES CMAC computation according to 3GPP TS 33.401 loggers::get_instance().log_to_hexa("ia2_128::mac: mac: ", *mac, *mac_length); loggers::get_instance().log("<<< ia2_128::mac: ret: 0"); return 0; } // ETSI TS 133 401 V18.3.0 (2025-04) Annex B.1.3 128-EEA2 void ia2_128::generate_counter_block(const uint32_t count, const uint8_t bearer, Loading ccsrc/Externals/ia2_128.hh +28 −0 Original line number Diff line number Diff line Loading @@ -116,6 +116,34 @@ public: unsigned char** payload, uint32_t* payload_length); /** * @brief Compute NAS integrity protection using AES 128 CRT algorithm * * This method computes the integrity protection using the AES 128 CRT algorithm. * * @param algo_id Algorithm identifier (should be 3 for AES 128 CRT) * @param knas_int Pointer to the 16-byte NAS integrity key * @param count 32-bit NAS count value * @param bearer 5-bit bearer identifier (only lower 5 bits are used) * @param direction Direction bit (0 = uplink, 1 = downlink) * @param payload Pointer to pointer that will receive the decrypted data (caller must free) * @param payload_length Pointer to variable that will receive the decrypted data length * @param mac Pointer to the mac to decrypt * @param mac_length Length of the mac in bytes * @return 0 on success, negative value on error * * @note The caller is responsible for freeing the memory allocated for payload * @note payload_length will be equal to cyphered_length for stream ciphers */ int mac(const uint8_t algo_id, const unsigned char* knas_int, const uint32_t count, const uint8_t bearer, const uint8_t direction, const unsigned char* payload, const uint32_t payload_length, unsigned char** mac, uint32_t* mac_length); private: /** * @brief Generates the initial counter block for AES-CTR Loading Loading
ccsrc/Externals/NG_security_ext.cc +24 −24 Original line number Diff line number Diff line Loading @@ -61,32 +61,32 @@ OCTETSTRING fx__NG__NasIntegrityAlgorithm(const OCTETSTRING& p_encoded_nas_pdu, return p_encoded_nas_pdu; // no integrity protection } else 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( // bit2int(p_integrity_algorithm), // static_cast<const unsigned char*>(bit2oct(p_knas_int)), // nas_count, // bit2int(p_bearer_id), // p_direction, // static_cast<const unsigned char*>(p_encoded_nas_pdu), // p_encoded_nas_pdu.lengthof(), // &mac, // &mac_length // ); ia1_128 enc; result = enc.mac( bit2int(p_integrity_algorithm), static_cast<const unsigned char*>(bit2oct(p_knas_int)), nas_count, bit2int(p_bearer_id), p_direction, static_cast<const unsigned char*>(p_encoded_nas_pdu), p_encoded_nas_pdu.lengthof(), &mac, &mac_length ); } 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( // bit2int(p_integrity_algorithm), // static_cast<const unsigned char*>(bit2oct(p_knas_int)), // nas_count, // bit2int(p_bearer_id), // p_direction, // static_cast<const unsigned char*>(p_encoded_nas_pdu), // p_encoded_nas_pdu.lengthof(), // &mac, // &mac_length // ); ia2_128 enc; result = enc.encrypt( bit2int(p_integrity_algorithm), static_cast<const unsigned char*>(bit2oct(p_knas_int)), nas_count, bit2int(p_bearer_id), p_direction, static_cast<const unsigned char*>(p_encoded_nas_pdu), p_encoded_nas_pdu.lengthof(), &mac, &mac_length ); } else if (bit2int(p_integrity_algorithm) == 3) { // IA3_128/NEA3_128: ZUC based algorithm loggers::get_instance().log("fx__NG__NasIntegrityAlgorithm: ia3_128 selected"); ia3_128 enc; Loading
ccsrc/Externals/ia1_128.cc +153 −5 Original line number Diff line number Diff line Loading @@ -21,6 +21,11 @@ namespace { constexpr size_t IV_SIZE = 16; // Initialization vector size } void ia1_128::reset() { loggers::get_instance().log("ia1_128::reset"); std::memset(&_ctx, 0, sizeof(_ctx)); } int ia1_128::encrypt(const uint8_t algo_id, const unsigned char* knas_enc, const uint32_t count, Loading Loading @@ -57,7 +62,8 @@ int ia1_128::encrypt(const uint8_t algo_id, return -1; } snow3g_context_t ctx; snow3g_initialize(count, bearer, direction, (const char *)knas_enc, &ctx); ctx.ciphering = true; snow3g_initialize(count, bearer, direction, knas_enc, &ctx); snow3g_generate(payload_length, payload, *cyphered, &ctx); *cyphered_length = payload_length; loggers::get_instance().log("ia1_128::encrypt: cyphered_length: %d", *cyphered_length); Loading Loading @@ -88,7 +94,118 @@ int ia1_128::decrypt(const uint8_t algo_id, cyphered, cyphered_length, payload, payload_length); } int ia1_128::snow3g_initialize(uint32_t count, uint8_t bearer, uint8_t direction, const char *knas_enc, snow3g_context_t *ctx) { int ia1_128::mac(const uint8_t algo_id, const unsigned char* knas_int, const uint32_t count, const uint8_t bearer, const uint8_t direction, const unsigned char* payload, const uint32_t payload_length, unsigned char** mac, uint32_t* mac_length) { loggers::get_instance().log_to_hexa(">>> ia1_128::mac: knas_int", knas_int, KEY_SIZE); loggers::get_instance().log(">>> ia1_128::mac: payload_length: %d", payload_length); loggers::get_instance().log_to_hexa(">>> ia1_128::mac: payload", payload, payload_length); loggers::get_instance().log(">>> ia1_128::mac: algo_id: %d", algo_id); loggers::get_instance().log(">>> ia1_128::mac: count: %u", count); loggers::get_instance().log(">>> ia1_128::mac: bearer: %d", bearer); loggers::get_instance().log(">>> ia1_128::mac: direction: %d", direction); // Input validation if (!knas_int || !payload || !mac || !mac_length) { loggers::get_instance().error("ia1_128::mac: Wrong input parameters"); return -1; } if (payload_length == 0) { loggers::get_instance().error("ia1_128::mac: Wrong payload length"); *mac = nullptr; *mac_length = 0; return 0; } // Allocate memory for ciphertext *mac = static_cast<unsigned char*>(std::malloc(4)); if (!*mac) { loggers::get_instance().error("ia1_128::mac: Failed to allocate memory"); return -1; } *mac_length = 4; snow3g_context_t ctx; ctx.ciphering = false; snow3g_initialize(count, bearer, direction, knas_int, &ctx); uint32_t z[5] = {0}; snow3g_generate(5, (uint32_t*)z, &ctx); loggers::get_instance().log_to_hexa("ia1_128::mac: z: ", (const unsigned char*)z, 5 * sizeof(uint32_t)); uint64_t p = (uint64_t)z[0] << 32 | (uint64_t)z[1]; uint64_t q = (uint64_t)z[2] << 32 | (uint64_t)z[3]; uint32_t d; uint64_t length = payload_length * 8; // Length in bits loggers::get_instance().log("ia1_128::mac: length: %ld", length); if ((length % 64) == 0) d = (length>>6) + 1; else d = (length>>6) + 2; loggers::get_instance().log("ia1_128::mac: d: %u", d); uint64_t c = 0x1b; uint64_t eval = 0; uint64_t v = 0; for (uint64_t i = 0; i < d - 2; i++) { v = eval ^ ((uint64_t)payload[8*i ]<<56 | (uint64_t)payload[8*i+1] << 48 | (uint64_t)payload[8*i+2]<<40 | (uint64_t)payload[8*i+3] << 32 | (uint64_t)payload[8*i+4]<<24 | (uint64_t)payload[8*i+5] << 16 | (uint64_t)payload[8*i+6]<< 8 | (uint64_t)payload[8*i+7] ); eval = mul64(v, p, c); } loggers::get_instance().log_to_hexa("ia1_128::mac: eval: ", (const unsigned char*)&eval, sizeof(uint64_t)); uint64_t rem = length % 64; if (rem == 0) { rem = 64; } loggers::get_instance().log("ia1_128::mac:rem: %d", rem); uint64_t m_d_2 = 0; uint8_t i = 0; while (rem > 7) { m_d_2 |= (uint64_t)payload[8*(d-2)+i] << (8*(7-i)); rem -= 8; i++; } if (rem > 0) { m_d_2 |= (uint64_t)(payload[8*(d-2)+i] & mask8bit(rem)) << (8*(7-i)); } loggers::get_instance().log_to_hexa("ia1_128::mac: m_d_2: ", (const unsigned char*)&m_d_2, sizeof(uint64_t)); v = eval ^ m_d_2; eval = mul64(v, p, c); loggers::get_instance().log_to_hexa("ia1_128::mac: eval: ", (const unsigned char*)&eval, sizeof(uint64_t)); /* for d-1 */ eval ^= length; loggers::get_instance().log_to_hexa("ia1_128::mac: eval: ", (const unsigned char*)&eval, sizeof(uint64_t)); /* Multiply by q */ eval = mul64(eval, q, c); loggers::get_instance().log_to_hexa("ia1_128::mac: eval: ", (const unsigned char*)&eval, sizeof(uint64_t)); /* XOR with z_5: this is a modification to the reference C code, which forgot to XOR z[5] */ for (i = 0; i < 4; i++) { (*mac)[i] = ((eval >> (56-(i*8))) ^ (z[4] >> (24-(i*8)))) & 0xff; } loggers::get_instance().log_to_hexa("ia1_128::mac: mac: ", *mac, *mac_length); loggers::get_instance().log("<<< ia1_128::mac: ret: 0"); return 0; } int ia1_128::snow3g_initialize(const uint32_t count, const uint8_t bearer, const uint8_t direction, const unsigned char *knas_enc, snow3g_context_t *ctx) { loggers::get_instance().log_to_hexa(">>> ia1_128::snow3g_initialize: knas_enc", (const unsigned char*)knas_enc, 16); loggers::get_instance().log(">>> ia1_128::snow3g_initialize: count: %u", count); loggers::get_instance().log(">>> ia1_128::snow3g_initialize: bearer: %d", bearer); Loading @@ -108,9 +225,15 @@ int ia1_128::snow3g_initialize(uint32_t count, uint8_t bearer, uint8_t direction snow_key.key[0] = WORD_128(knas_enc, 3); snow_key.iv[3] = count; if (ctx->ciphering) { snow_key.iv[2] = ((bearer & 0x1F) << 27) | ((direction & 0x01) << 26); snow_key.iv[1] = snow_key.iv[3]; snow_key.iv[0] = snow_key.iv[2]; } else { snow_key.iv[2] = ((bearer & 0x1F) << 27); snow_key.iv[1] = count ^ ( direction << 31 ); snow_key.iv[0] = ((bearer & 0x1F) << 27) ^ (direction << 15); } loggers::get_instance().log_to_hexa("ia1_128::snow3g_initialize: snow_key.key", (const unsigned char*)snow_key.key, 4 * sizeof(uint32_t)); loggers::get_instance().log_to_hexa("ia1_128::snow3g_initialize: snow_key.iv", (const unsigned char*)snow_key.iv, 4 * sizeof(uint32_t)); Loading Loading @@ -162,3 +285,28 @@ int ia1_128::snow3g_generate(size_t nb_byte, const unsigned char *in, unsigned c loggers::get_instance().log_to_hexa("<<< SNOW: out", (const unsigned char*)out, nb_byte); return 0; } int ia1_128::snow3g_generate(size_t nb, uint32_t* out, snow3g_context_t* ctx) { loggers::get_instance().log(">>> snow3g_generate: nb: %u", nb); // Sanity checks if (ctx == NULL) { loggers::get_instance().error("snow3g_generate: Wrong parameters"); return -1; } size_t i = 0; /* init */ clock_fsm(ctx); lfsr_keystream(ctx); for (i = 0; i < nb; i++) { uint32_t f; out[i] = clock_fsm(ctx) ^ ctx->lfsr[0]; lfsr_keystream(ctx); } loggers::get_instance().log_to_hexa("<<< snow3g_generate: out", (const unsigned char*)out, nb); return 0; }
ccsrc/Externals/ia1_128.hh +68 −4 Original line number Diff line number Diff line Loading @@ -59,6 +59,7 @@ class ia1_128 { typedef struct snow_ctx_st { uint32_t lfsr[SNOW_KEY_SIZE]; struct fsm_st fsm; bool ciphering; } snow3g_context_t; public: Loading Loading @@ -153,6 +154,35 @@ public: unsigned char** payload, uint32_t* payload_length); /** * @brief Compute NAS integrity protection using SNOW 3G algorithm * * This method computes the integrity protection using the SNOW 3G algorithm. * * @param algo_id Algorithm identifier (should be 3 for SNOW 3G) * @param knas_int Pointer to the 16-byte NAS integrity key * @param count 32-bit NAS count value * @param bearer 5-bit bearer identifier (only lower 5 bits are used) * @param direction Direction bit (0 = uplink, 1 = downlink) * @param payload Pointer to pointer that will receive the decrypted data (caller must free) * @param payload_length Pointer to variable that will receive the decrypted data length * @param mac Pointer to the mac to decrypt * @param mac_length Length of the mac in bytes * @return 0 on success, negative value on error * * @note The caller is responsible for freeing the memory allocated for payload * @note payload_length will be equal to cyphered_length for stream ciphers */ int mac(const uint8_t algo_id, const unsigned char* knas_int, const uint32_t count, const uint8_t bearer, const uint8_t direction, const unsigned char* payload, const uint32_t payload_length, unsigned char** mac, uint32_t* mac_length); private: const uint32_t S1_T0[256] = { 0xa56363c6U, 0x847c7cf8U, 0x997777eeU, 0x8d7b7bf6U, Loading Loading @@ -824,9 +854,41 @@ private: 0x9EE2651CU, 0x86ED25D1U, 0xAEFCE52FU, 0xB6F3A5E2U, }; inline uint32_t snow_s1(uint32_t in) { return S1_T0[BYTE32(in, 3)] ^ S1_T1[BYTE32(in, 2)] ^ S1_T2[BYTE32(in, 1)] ^ S1_T3[BYTE32(in, 0)]; }; snow3g_context_t _ctx; void reset(); inline uint32_t snow_s1(const uint32_t in) { return S1_T0[BYTE32(in, 3)] ^ S1_T1[BYTE32(in, 2)] ^ S1_T2[BYTE32(in, 1)] ^ S1_T3[BYTE32(in, 0)]; }; inline uint32_t snow_s2(const uint32_t in) { return S2_T0[BYTE32(in, 3)] ^ S2_T1[BYTE32(in, 2)] ^ S2_T2[BYTE32(in, 1)] ^ S2_T3[BYTE32(in, 0)]; }; inline uint64_t mul64(uint64_t V, uint64_t P, uint64_t c) { uint64_t result = 0; for (int8_t i = 0; i < 64; i++) { if ((P >> i ) & 0x1) { result ^= mul64x_pow(V, i, c); } } return result; }; inline uint64_t mul64x_pow(uint64_t V, uint8_t i, uint64_t c) { if (i == 0) { return V; } else { return mul64x(mul64x_pow(V, i-1, c) , c); } }; inline uint64_t mul64x(uint64_t V, uint64_t c) { if (V & 0x8000000000000000) { return (V << 1) ^ c; } else { return V << 1; } }; inline uint32_t snow_s2(uint32_t in) { return S2_T0[BYTE32(in, 3)] ^ S2_T1[BYTE32(in, 2)] ^ S2_T2[BYTE32(in, 1)] ^ S2_T3[BYTE32(in, 0)]; }; inline uint8_t mask8bit(int n) { return 0xFF ^ ((1<<(8-n)) - 1); }; /* Clocking operations */ inline void lfsr_init(uint32_t f, snow3g_context_t *ctx) { Loading Loading @@ -953,7 +1015,7 @@ private: * @param key 16-byte encryption key * @param iv 16-byte initialization vector */ int snow3g_initialize(uint32_t count, uint8_t bearer, uint8_t direction, const char *knas_enc, snow3g_context_t *ctx); int snow3g_initialize(const uint32_t count, const uint8_t bearer, const uint8_t direction, const unsigned char *knas_enc, snow3g_context_t *ctx); /** * @brief Generate keystream using SNOW 3G Loading @@ -963,4 +1025,6 @@ private: * @param length Length of keystream needed in bytes */ int snow3g_generate(size_t nb_byte, const unsigned char* in, unsigned char* out, snow3g_context_t *ctx); int snow3g_generate(size_t nb_byte, uint32_t* out, snow3g_context_t *ctx); };
ccsrc/Externals/ia2_128.cc +46 −0 Original line number Diff line number Diff line Loading @@ -204,6 +204,52 @@ cleanup: return ret; } int ia2_128::mac(const uint8_t algo_id, const unsigned char* knas_int, const uint32_t count, const uint8_t bearer, const uint8_t direction, const unsigned char* payload, const uint32_t payload_length, unsigned char** mac, uint32_t* mac_length) { loggers::get_instance().log_to_hexa(">>> ia2_128::mac: knas_int", knas_int, KEY_SIZE); loggers::get_instance().log(">>> ia2_128::mac: payload_length: %d", payload_length); loggers::get_instance().log_to_hexa(">>> ia2_128::mac: payload", payload, payload_length); loggers::get_instance().log(">>> ia2_128::mac: algo_id: %d", algo_id); loggers::get_instance().log(">>> ia2_128::mac: count: %u", count); loggers::get_instance().log(">>> ia2_128::mac: bearer: %d", bearer); loggers::get_instance().log(">>> ia2_128::mac: direction: %d", direction); // Input validation if (!knas_int || !payload || !mac || !mac_length) { loggers::get_instance().error("ia2_128::mac: Wrong input parameters"); return -1; } if (payload_length == 0) { loggers::get_instance().error("ia2_128::mac: Wrong payload length"); *mac = nullptr; *mac_length = 0; return 0; } // Allocate memory for ciphertext *mac = static_cast<unsigned char*>(std::malloc(4)); if (!*mac) { loggers::get_instance().error("ia2_128::mac: Failed to allocate memory"); return -1; } *mac_length = 4; // TODO: Implement AES CMAC computation according to 3GPP TS 33.401 loggers::get_instance().log_to_hexa("ia2_128::mac: mac: ", *mac, *mac_length); loggers::get_instance().log("<<< ia2_128::mac: ret: 0"); return 0; } // ETSI TS 133 401 V18.3.0 (2025-04) Annex B.1.3 128-EEA2 void ia2_128::generate_counter_block(const uint32_t count, const uint8_t bearer, Loading
ccsrc/Externals/ia2_128.hh +28 −0 Original line number Diff line number Diff line Loading @@ -116,6 +116,34 @@ public: unsigned char** payload, uint32_t* payload_length); /** * @brief Compute NAS integrity protection using AES 128 CRT algorithm * * This method computes the integrity protection using the AES 128 CRT algorithm. * * @param algo_id Algorithm identifier (should be 3 for AES 128 CRT) * @param knas_int Pointer to the 16-byte NAS integrity key * @param count 32-bit NAS count value * @param bearer 5-bit bearer identifier (only lower 5 bits are used) * @param direction Direction bit (0 = uplink, 1 = downlink) * @param payload Pointer to pointer that will receive the decrypted data (caller must free) * @param payload_length Pointer to variable that will receive the decrypted data length * @param mac Pointer to the mac to decrypt * @param mac_length Length of the mac in bytes * @return 0 on success, negative value on error * * @note The caller is responsible for freeing the memory allocated for payload * @note payload_length will be equal to cyphered_length for stream ciphers */ int mac(const uint8_t algo_id, const unsigned char* knas_int, const uint32_t count, const uint8_t bearer, const uint8_t direction, const unsigned char* payload, const uint32_t payload_length, unsigned char** mac, uint32_t* mac_length); private: /** * @brief Generates the initial counter block for AES-CTR Loading