inline int _compare_mem(uint8_t * x, uint8_t * y, size_t len) { if (0 != memcmp(x, y, len)) { printf ("X: %s\t", osmo_hexdump_nospc(x, len)); printf ("Y: %s\n", osmo_hexdump_nospc(y, len)); return 0; } return 1; }
static void dump_triplets_dat(struct osmo_auth_vector *vec) { if (vec->auth_types & OSMO_AUTH_TYPE_UMTS) { fprintf(stderr, "triplets.dat doesn't support UMTS!\n"); return; } printf("imsi,"); printf("%s,", osmo_hexdump_nospc(vec->rand, sizeof(vec->rand))); printf("%s,", osmo_hexdump_nospc(vec->sres, sizeof(vec->sres))); printf("%s\n", osmo_hexdump_nospc(vec->kc, sizeof(vec->kc))); }
static inline void print_check(char *res, uint8_t *out, uint16_t len) { uint8_t buf[len]; osmo_hexparse(res, buf, len); if (0 != memcmp(buf, out, len)) { printf("FAIL:\n"); printf("OUT: %s\n", osmo_hexdump_nospc(out, len)); printf("EXP: %s\n", osmo_hexdump_nospc(buf, len)); } else printf("\n"); }
/* From the given state and received RAND and AUTN octets, validate the * server's authenticity and formulate the matching milenage reply octets in * *tx_xres. The state is not modified. * On success, and if tx_res is not NULL, exactly 8 octets will be written to * *tx_res. If not NULL, tx_res must point at allocated memory of at least 8 * octets. The caller will want to send XRES back to the server in a challenge * response message and update the state. * Return 0 on success; -1 if OAP is disabled; -2 if rx_random and rx_autn fail * the authentication check; -3 for any other errors. */ static int oap_evaluate_challenge(const struct oap_client_state *state, const uint8_t *rx_random, const uint8_t *rx_autn, uint8_t *tx_xres) { struct osmo_auth_vector vec; struct osmo_sub_auth_data auth = { .type = OSMO_AUTH_TYPE_UMTS, .algo = OSMO_AUTH_ALG_MILENAGE, }; osmo_static_assert(sizeof(((struct osmo_sub_auth_data*)0)->u.umts.k) == sizeof(state->secret_k), _secret_k_size_match); osmo_static_assert(sizeof(((struct osmo_sub_auth_data*)0)->u.umts.opc) == sizeof(state->secret_opc), _secret_opc_size_match); switch (state->state) { case OAP_UNINITIALIZED: case OAP_DISABLED: return -1; default: break; } memcpy(auth.u.umts.k, state->secret_k, sizeof(auth.u.umts.k)); memcpy(auth.u.umts.opc, state->secret_opc, sizeof(auth.u.umts.opc)); memset(auth.u.umts.amf, '\0', sizeof(auth.u.umts.amf)); auth.u.umts.sqn = 41; /* TODO use incrementing sequence nr */ memset(&vec, 0, sizeof(vec)); osmo_auth_gen_vec(&vec, &auth, rx_random); if (vec.res_len != 8) { LOGP(DLOAP, LOGL_ERROR, "OAP: Expected XRES to be 8 octets, got %d\n", vec.res_len); return -3; } if (osmo_constant_time_cmp(vec.autn, rx_autn, sizeof(vec.autn)) != 0) { LOGP(DLOAP, LOGL_ERROR, "OAP: AUTN mismatch!\n"); LOGP(DLOAP, LOGL_INFO, "OAP: AUTN from server: %s\n", osmo_hexdump_nospc(rx_autn, sizeof(vec.autn))); LOGP(DLOAP, LOGL_INFO, "OAP: AUTN expected: %s\n", osmo_hexdump_nospc(vec.autn, sizeof(vec.autn))); return -2; } if (tx_xres != NULL) memcpy(tx_xres, vec.res, 8); return 0; }
inline bool print_a5(int n, int k, char * dir, ubit_t * out, char * block) { uint8_t len = 114 / 8 + 1, buf[len], res[len]; printf("A5/%d - %s: %s => ", n, dir, osmo_ubit_dump(out, 114)); osmo_hexparse(block, res, len); osmo_ubit2pbit(buf, out, 114); if (0 != memcmp(buf, res, len)) { printf("FAIL"); printf("\nGOT: [%d] %s", k, osmo_hexdump_nospc(buf, len)); printf("\nEXP: [%d] %s\n", k, osmo_hexdump_nospc(res, len)); return false; } printf("OK\n"); return true; }
/* Simulate a real life situation: voice frames with a few dropouts */ void test_fr_concealment_realistic() { struct osmo_ecu_fr_state state; uint8_t frame[GSM_FR_BYTES]; unsigned int frame_len; int rc, i = 0; while (fr_frames_hex[i] != NULL) { /* Debug print */ printf("Frame No. %03i:\n", i); /* Good or bad frame? */ frame_len = strlen(fr_frames_hex[i]) / 2; if (frame_len == GSM_FR_BYTES) { printf(" * input: %s\n", fr_frames_hex[i]); osmo_hexparse(fr_frames_hex[i], frame, GSM_FR_BYTES); osmo_ecu_fr_reset(&state, frame); } else { printf(" * input: (bad)\n"); memset(frame, 0x00, GSM_FR_BYTES); rc = osmo_ecu_fr_conceal(&state, frame); OSMO_ASSERT(rc == 0); } /* Print result */ printf(" * output: %s\n", osmo_hexdump_nospc(frame, GSM_FR_BYTES)); /* Go to the next frame */ i++; } }
/** * Start with a good voice frame and then simulate 20 consecutive bad frames, * watching how the error concealment decreases the XMAXC parameters. */ void test_fr_concealment(void) { struct osmo_ecu_fr_state state; uint8_t frame[GSM_FR_BYTES]; uint64_t xmaxc[4]; int i, rc; int j = 0; while (sample_frame_hex[j] != NULL) { /* Parse frame from string to hex */ osmo_hexparse(sample_frame_hex[j], frame, GSM_FR_BYTES); parse_xmaxc_frame(frame, xmaxc); printf("Start with: %s, XMAXC: [%"PRIx64", %"PRIx64", %"PRIx64", %"PRIx64"]\n", sample_frame_hex[j], xmaxc[0], xmaxc[1], xmaxc[2], xmaxc[3]); /* Reset the ECU with the proposed known good frame */ osmo_ecu_fr_reset(&state, frame); /* Now pretend that we do not receive any good frames anymore */ for (i = 0; i < 20; i++) { rc = osmo_ecu_fr_conceal(&state, frame); OSMO_ASSERT(rc == 0); parse_xmaxc_frame(frame, xmaxc); printf("conceal: %02i, result: %s XMAXC: [%"PRIx64", %"PRIx64", %"PRIx64", %"PRIx64"]\n", i, osmo_hexdump_nospc(frame, GSM_FR_BYTES), xmaxc[0], xmaxc[1], xmaxc[2], xmaxc[3]); } /* Go to the next frame */ j++; } }
static void test_unhex(const char *hex) { int rc; struct bitvec b; uint8_t d[64] = {0}; b.data = d; b.data_len = sizeof(d); b.cur_bit = 0; rc = bitvec_unhex(&b, hex); printf("%d -=> cur_bit=%u\n", rc, b.cur_bit); printf("%s\n", osmo_hexdump_nospc(d, 64)); printf("%s\n", hex); }
static void dump_auth_vec(struct osmo_auth_vector *vec) { printf("RAND:\t%s\n", osmo_hexdump_nospc(vec->rand, sizeof(vec->rand))); if (vec->auth_types & OSMO_AUTH_TYPE_UMTS) { printf("AUTN:\t%s\n", osmo_hexdump_nospc(vec->autn, sizeof(vec->autn))); printf("IK:\t%s\n", osmo_hexdump_nospc(vec->ik, sizeof(vec->ik))); printf("CK:\t%s\n", osmo_hexdump_nospc(vec->ck, sizeof(vec->ck))); printf("RES:\t%s\n", osmo_hexdump_nospc(vec->res, vec->res_len)); } if (vec->auth_types & OSMO_AUTH_TYPE_GSM) { printf("SRES:\t%s\n", osmo_hexdump_nospc(vec->sres, sizeof(vec->sres))); printf("Kc:\t%s\n", osmo_hexdump_nospc(vec->kc, sizeof(vec->kc))); } }
static void test_7bit_ussd(const char *text, const char *encoded_hex, const char *appended_after_decode) { uint8_t coded[256]; char decoded[256]; int octets_written; int buffer_size; int nchars; printf("original = %s\n", osmo_hexdump((uint8_t *)text, strlen(text))); gsm_7bit_encode_n_ussd(coded, sizeof(coded), text, &octets_written); printf("encoded = %s\n", osmo_hexdump(coded, octets_written)); OSMO_ASSERT(strcmp(encoded_hex, osmo_hexdump_nospc(coded, octets_written)) == 0); gsm_7bit_decode_n_ussd(decoded, sizeof(decoded), coded, octets_written * 8 / 7); octets_written = strlen(decoded); printf("decoded = %s\n\n", osmo_hexdump((uint8_t *)decoded, octets_written)); OSMO_ASSERT(strncmp(text, decoded, strlen(text)) == 0); OSMO_ASSERT(strcmp(appended_after_decode, decoded + strlen(text)) == 0); /* check buffer limiting */ memset(decoded, 0xaa, sizeof(decoded)); for (buffer_size = 1; buffer_size < sizeof(decoded) - 1; ++buffer_size) { nchars = gsm_7bit_decode_n_ussd(decoded, buffer_size, coded, octets_written * 8 / 7); OSMO_ASSERT(nchars <= buffer_size); OSMO_ASSERT(decoded[buffer_size] == (char)0xaa); OSMO_ASSERT(decoded[nchars] == '\0'); } memset(coded, 0xaa, sizeof(coded)); for (buffer_size = 0; buffer_size < sizeof(coded) - 1; ++buffer_size) { gsm_7bit_encode_n_ussd(coded, buffer_size, text, &octets_written); OSMO_ASSERT(octets_written <= buffer_size); OSMO_ASSERT(coded[buffer_size] == 0xaa); } }
static void test_oap_api(void) { printf("Testing OAP API\n - Config parsing\n"); struct oap_config _config; struct oap_config *config = &_config; struct oap_state _state; struct oap_state *state = &_state; memset(config, 0, sizeof(*config)); memset(state, 0, sizeof(*state)); OSMO_ASSERT(osmo_hexparse("0102030405060708090a0b0c0d0e0f10", config->secret_k, 16) == 16); OSMO_ASSERT(osmo_hexparse("1112131415161718191a1b1c1d1e1f20", config->secret_opc, 16) == 16); /* make sure filling with zeros means uninitialized */ OSMO_ASSERT(state->state == OAP_UNINITIALIZED); /* invalid client_id and shared secret */ config->client_id = 0; config->secret_k_present = 0; config->secret_opc_present = 0; OSMO_ASSERT( oap_init(config, state) == 0 ); OSMO_ASSERT(state->state == OAP_DISABLED); /* reset state */ memset(state, 0, sizeof(*state)); /* only client_id is invalid */ config->client_id = 0; config->secret_k_present = 1; config->secret_opc_present = 1; OSMO_ASSERT( oap_init(config, state) == 0 ); OSMO_ASSERT(state->state == OAP_DISABLED); memset(state, 0, sizeof(*state)); /* valid id, but omitted shared_secret (1/2) */ config->client_id = 12345; config->secret_k_present = 0; config->secret_opc_present = 1; OSMO_ASSERT( oap_init(config, state) == 0 ); OSMO_ASSERT(state->state == OAP_DISABLED); memset(state, 0, sizeof(*state)); /* valid id, but omitted shared_secret (2/2) */ config->client_id = 12345; config->secret_k_present = 1; config->secret_opc_present = 0; OSMO_ASSERT( oap_init(config, state) == 0 ); OSMO_ASSERT(state->state == OAP_DISABLED); memset(state, 0, sizeof(*state)); /* mint configuration */ config->client_id = 12345; config->secret_k_present = 1; config->secret_opc_present = 1; /*config->secret_* buffers are still set from the top */ OSMO_ASSERT( oap_init(config, state) == 0 ); OSMO_ASSERT(state->state == OAP_INITIALIZED); printf(" - AUTN failures\n"); struct oap_message oap_rx; struct oap_message oap_tx; struct msgb *msg_rx; struct msgb *msg_tx; memset(&oap_rx, 0, sizeof(oap_rx)); /* Missing challenge data */ oap_rx.message_type = OAP_MSGT_CHALLENGE_REQUEST; oap_rx.rand_present = 0; oap_rx.autn_present = 0; msg_rx = oap_encoded(&oap_rx); OSMO_ASSERT(oap_handle(state, msg_rx, &msg_tx) == -2); msgb_free(msg_rx); OSMO_ASSERT(!msg_tx); /* AUTN missing */ osmo_hexparse("0102030405060708090a0b0c0d0e0f10", oap_rx.rand, 16); oap_rx.rand_present = 1; msg_rx = oap_encoded(&oap_rx); OSMO_ASSERT(oap_handle(state, msg_rx, &msg_tx) == -2); msgb_free(msg_rx); OSMO_ASSERT(!msg_tx); /* RAND missing */ oap_rx.rand_present = 0; osmo_hexparse("cec4e3848a33000086781158ca40f136", oap_rx.autn, 16); oap_rx.autn_present = 1; msg_rx = oap_encoded(&oap_rx); OSMO_ASSERT(oap_handle(state, msg_rx, &msg_tx) == -2); msgb_free(msg_rx); OSMO_ASSERT(!msg_tx); /* wrong autn (by one bit) */ osmo_hexparse("0102030405060708090a0b0c0d0e0f10", oap_rx.rand, 16); osmo_hexparse("dec4e3848a33000086781158ca40f136", oap_rx.autn, 16); oap_rx.rand_present = 1; oap_rx.autn_present = 1; msg_rx = oap_encoded(&oap_rx); OSMO_ASSERT(oap_handle(state, msg_rx, &msg_tx) == -2); msgb_free(msg_rx); OSMO_ASSERT(!msg_tx); /* all data correct */ osmo_hexparse("cec4e3848a33000086781158ca40f136", oap_rx.autn, 16); msg_rx = oap_encoded(&oap_rx); /* but refuse to evaluate in uninitialized state */ OSMO_ASSERT(state->state == OAP_INITIALIZED); state->state = OAP_UNINITIALIZED; OSMO_ASSERT(oap_handle(state, msg_rx, &msg_tx) == -1); OSMO_ASSERT(!msg_tx); state->state = OAP_DISABLED; OSMO_ASSERT(oap_handle(state, msg_rx, &msg_tx) == -1); OSMO_ASSERT(!msg_tx); state->state = OAP_INITIALIZED; /* now everything is correct */ printf(" - AUTN success\n"); /* a successful return value here indicates correct autn */ OSMO_ASSERT(oap_handle(state, msg_rx, &msg_tx) == 0); msgb_free(msg_rx); /* Expect the challenge response in msg_tx */ OSMO_ASSERT(msg_tx); OSMO_ASSERT(oap_decode(msg_tx->data, msg_tx->len, &oap_tx) == 0); OSMO_ASSERT(oap_tx.message_type == OAP_MSGT_CHALLENGE_RESULT); OSMO_ASSERT(strcmp("e2d05b598c61d9ba", osmo_hexdump_nospc(oap_tx.xres, sizeof(oap_tx.xres))) == 0); OSMO_ASSERT(state->state == OAP_SENT_CHALLENGE_RESULT); msgb_free(msg_tx); msg_tx = 0; struct oap_state saved_state = _state; printf(" - Registration failure\n"); memset(&oap_rx, 0, sizeof(oap_rx)); oap_rx.message_type = OAP_MSGT_REGISTER_ERROR; oap_rx.cause = GMM_CAUSE_PROTO_ERR_UNSPEC; msg_rx = oap_encoded(&oap_rx); /* Receive registration error for the first time. */ OSMO_ASSERT(state->registration_failures == 0); OSMO_ASSERT(oap_handle(state, msg_rx, &msg_tx) == 0); OSMO_ASSERT(state->registration_failures == 1); OSMO_ASSERT(msg_tx); OSMO_ASSERT(oap_decode(msg_tx->data, msg_tx->len, &oap_tx) == 0); OSMO_ASSERT(oap_tx.message_type == OAP_MSGT_REGISTER_REQUEST); OSMO_ASSERT(state->state == OAP_REQUESTED_CHALLENGE); msgb_free(msg_tx); msg_tx = 0; /* Receive registration error for the Nth time. */ state->registration_failures = 999; OSMO_ASSERT(oap_handle(state, msg_rx, &msg_tx) == -11); OSMO_ASSERT(!msg_tx); OSMO_ASSERT(state->state == OAP_INITIALIZED); msgb_free(msg_tx); msg_tx = 0; msgb_free(msg_rx); printf(" - Registration success\n"); _state = saved_state; memset(&oap_rx, 0, sizeof(oap_rx)); oap_rx.message_type = OAP_MSGT_REGISTER_RESULT; msg_rx = oap_encoded(&oap_rx); OSMO_ASSERT(oap_handle(state, msg_rx, &msg_tx) == 0); OSMO_ASSERT(!msg_tx); OSMO_ASSERT(state->state == OAP_REGISTERED); msgb_free(msg_rx); }