/**************************************************************************** ** ** ** Name: lowerlayer_data_req() ** ** ** ** Description: Notify the EPS Mobility Management entity that data have ** ** to be transfered to lower layers ** ** ** ** Inputs: ueid: UE lower layer identifier ** ** data: Data to be transfered to lower layers ** ** Others: None ** ** ** ** Outputs: None ** ** Return: RETURNok, RETURNerror ** ** Others: None ** ** ** ***************************************************************************/ int lowerlayer_data_req(unsigned int ueid, const OctetString *data) { LOG_FUNC_IN; int rc; emm_sap_t emm_sap; emm_security_context_t *sctx = NULL; struct emm_data_context_s *ctx = NULL; emm_sap.primitive = EMMAS_DATA_REQ; emm_sap.u.emm_as.u.data.guti = _emm_data.guti; emm_sap.u.emm_as.u.data.ueid = 0; sctx = _emm_data.security; emm_sap.u.emm_as.u.data.NASinfo = 0; emm_sap.u.emm_as.u.data.NASmsg.length = data->length; emm_sap.u.emm_as.u.data.NASmsg.value = data->value; /* Setup EPS NAS security data */ emm_as_set_security_data(&emm_sap.u.emm_as.u.data.sctx, sctx, FALSE, TRUE); rc = emm_sap_send(&emm_sap); LOG_FUNC_RETURN (rc); }
static int sc_single_transmit(struct sc_card *card, struct sc_apdu *apdu) { struct sc_context *ctx = card->ctx; int rv; LOG_FUNC_CALLED(ctx); if (card->reader->ops->transmit == NULL) LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "cannot transmit APDU"); sc_log(ctx, "CLA:%X, INS:%X, P1:%X, P2:%X, data(%i) %p", apdu->cla, apdu->ins, apdu->p1, apdu->p2, apdu->datalen, apdu->data); #ifdef ENABLE_SM if (card->sm_ctx.sm_mode == SM_MODE_TRANSMIT) return sc_sm_single_transmit(card, apdu); #endif /* send APDU to the reader driver */ rv = card->reader->ops->transmit(card->reader, apdu); LOG_TEST_RET(ctx, rv, "unable to transmit APDU"); LOG_FUNC_RETURN(ctx, rv); }
static int esteid_select(struct sc_card *card, unsigned char p1, unsigned char id1, unsigned char id2) { struct sc_apdu apdu; unsigned char sbuf[2]; LOG_FUNC_CALLED(card->ctx); // Select EF/DF sbuf[0] = id1; sbuf[1] = id2; sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xA4, p1, 0x0C); if (id1 != 0x3F && id2 != 0x00) { apdu.cse = SC_APDU_CASE_3_SHORT; apdu.lc = 2; apdu.data = sbuf; apdu.datalen = 2; } apdu.le = 0; apdu.resplen = 0; SC_TRANSMIT_TEST_RET(card, apdu, "SELECT failed"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); }
/**************************************************************************** ** ** ** Name: _emm_attach_t3410_handler() ** ** ** ** Description: T3410 timeout handler ** ** ** ** 3GPP TS 24.301, section 5.5.1.2.6, case c ** ** Upon T3410 timer expiration, the attach procedure shall ** ** be aborted and the UE shall execute abnormal case attach ** ** procedure. ** ** The NAS signalling connection shall be released locally. ** ** ** ** Inputs: args: handler parameters ** ** Others: None ** ** ** ** Outputs: None ** ** Return: None ** ** Others: T3410 ** ** ** ***************************************************************************/ void *_emm_attach_t3410_handler(void *args) { LOG_FUNC_IN; emm_sap_t emm_sap; int rc; LOG_TRACE(WARNING, "EMM-PROC - T3410 timer expired"); /* Stop T3410 timer */ T3410.id = nas_timer_stop(T3410.id); /* Execute abnormal case attach procedure */ _emm_attach_abnormal_cases_bcd(&emm_sap); rc = emm_sap_send(&emm_sap); if (rc != RETURNerror) { /* Locally release the NAS signalling connection */ _emm_data.ecm_status = ECM_IDLE; } LOG_FUNC_RETURN(NULL); }
static int myeid_init_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) { struct sc_path path; struct sc_file *file = NULL; u8 rbuf[256]; int r; LOG_FUNC_CALLED(p15card->card->ctx); p15card->tokeninfo->flags = SC_PKCS15_TOKEN_PRN_GENERATION | SC_PKCS15_TOKEN_EID_COMPLIANT; r = sc_card_ctl(p15card->card, SC_CARDCTL_GET_SERIALNR, &rbuf); LOG_TEST_RET(p15card->card->ctx, r, "Get applet info failed"); sc_format_path("3F00", &path); r = sc_select_file(p15card->card, &path, &file); if (file) sc_file_free(file); LOG_FUNC_RETURN(p15card->card->ctx, r); }
/**************************************************************************** ** ** ** Name: _emm_attach_t3402_handler() ** ** ** ** Description: T3402 timeout handler ** ** ** ** Upon T3402 timer expiration: ** ** 3GPP TS 24.301, section 5.5.1.1 ** ** the attach attempt counter shall be reset when the UE is ** ** in substate DEREGISTERED.ATTEMPTING-TO-ATTACH; ** ** 3GPP TS 24.301, section 5.2.2.3.3 ** ** the UE shall initiate an attach or combined attach proce- ** ** dure in substate DEREGISTERED.ATTEMPTING-TO-ATTACH; ** ** ** ** Inputs: args: handler parameters ** ** Others: None ** ** ** ** Outputs: None ** ** Return: None ** ** Others: _emm_attach_data, T3402 ** ** ** ***************************************************************************/ static void *_emm_attach_t3402_handler(void *args) { LOG_FUNC_IN; emm_sap_t emm_sap; LOG_TRACE(WARNING, "EMM-PROC - T3402 timer expired"); /* Stop T3402 timer */ T3402.id = nas_timer_stop(T3402.id); /* Reset the attach attempt counter */ _emm_attach_data.attempt_count = 0; /* * Notify EMM that timer T3402 expired and attach procedure has to be * restarted */ emm_sap.primitive = EMMREG_ATTACH_INIT; emm_sap.u.emm_reg.u.attach.is_emergency = _emm_data.is_emergency; (void) emm_sap_send(&emm_sap); LOG_FUNC_RETURN(NULL); }
int sc_pkcs15_encode_pubkey_dsa(sc_context_t *ctx, struct sc_pkcs15_pubkey_dsa *key, u8 **buf, size_t *buflen) { struct sc_asn1_entry asn1_public_key[C_ASN1_PUBLIC_KEY_SIZE]; struct sc_asn1_entry asn1_dsa_pub_coefficients[C_ASN1_DSA_PUB_COEFFICIENTS_SIZE]; int r; LOG_FUNC_CALLED(ctx); sc_copy_asn1_entry(c_asn1_public_key, asn1_public_key); sc_copy_asn1_entry(c_asn1_dsa_pub_coefficients, asn1_dsa_pub_coefficients); sc_format_asn1_entry(asn1_public_key + 0, asn1_dsa_pub_coefficients, NULL, 1); sc_format_asn1_entry(asn1_dsa_pub_coefficients + 0, key->pub.data, &key->pub.len, 1); sc_format_asn1_entry(asn1_dsa_pub_coefficients + 1, key->g.data, &key->g.len, 1); sc_format_asn1_entry(asn1_dsa_pub_coefficients + 2, key->p.data, &key->p.len, 1); sc_format_asn1_entry(asn1_dsa_pub_coefficients + 3, key->q.data, &key->q.len, 1); r = sc_asn1_encode(ctx, asn1_public_key, buf, buflen); LOG_TEST_RET(ctx, r, "ASN.1 encoding failed"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); }
/**************************************************************************** ** ** ** Name: as_message_send() ** ** ** ** Description: Service provided to the EPS Mobility Management protocol ** ** at the EMMAS Access Point (EMMAS-SAP) to send AS messages ** ** to the Access Stratum sublayer. ** ** ** ** Inputs: as_msg: The AS message to send ** ** Others: _network_api_send_buffer, _network_api_id ** ** ** ** Outputs: Return: The number of bytes sent when success; ** ** RETURNerror Otherwise ** ** Others: _network_api_send_buffer ** ** ** ***************************************************************************/ int as_message_send(as_message_t* as_msg) { int bytes; LOG_FUNC_IN; LOG_TRACE(INFO, "NET-API - Send message 0x%.4x to the Access Stratum " "layer", as_msg->msgID); /* Encode the AS message */ bytes = network_api_encode_data(as_msg); if (bytes > 0) { /* Get the network file descriptor */ int fd = network_api_get_fd(); if (fd != RETURNerror) { /* Send the AS message to the network */ bytes = network_api_send_data(fd, bytes); } } LOG_FUNC_RETURN (bytes); }
/**************************************************************************** ** ** ** Name: emm_recv_security_mode_command() ** ** ** ** Description: Processes Security Mode Command message ** ** ** ** Inputs: msg: The received EMM message ** ** Others: None ** ** ** ** Outputs: emm_cause: EMM cause code ** ** Return: RETURNok, RETURNerror ** ** Others: None ** ** ** ***************************************************************************/ int emm_recv_security_mode_command(security_mode_command_msg *msg, int *emm_cause) { LOG_FUNC_IN; int rc; LOG_TRACE(INFO, "EMMAS-SAP - Received Security Mode Command message"); /* * Message processing */ /* Execute the security mode control procedure initiated by the network */ rc = emm_proc_security_mode_command( msg->naskeysetidentifier.tsc != NAS_KEY_SET_IDENTIFIER_MAPPED, msg->naskeysetidentifier.naskeysetidentifier, msg->selectednassecurityalgorithms.typeofcipheringalgorithm, msg->selectednassecurityalgorithms.typeofintegrityalgorithm, msg->replayeduesecuritycapabilities.eea, msg->replayeduesecuritycapabilities.eia); LOG_FUNC_RETURN (rc); }
static int iasecc_sm_get_challenge(struct sc_card *card, unsigned char *out, size_t len) { struct sc_context *ctx = card->ctx; struct sc_apdu apdu; unsigned char rbuf[SC_MAX_APDU_BUFFER_SIZE]; int rv; sc_log(ctx, "SM get challenge: length %i",len); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x84, 0, 0); apdu.le = len; apdu.resplen = len; apdu.resp = rbuf; rv = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(ctx, rv, "Command failed"); memcpy(out, rbuf, apdu.resplen); LOG_FUNC_RETURN(ctx, apdu.resplen); }
/** * Retrieve Root CA public key. * * Just returns (as local SM authentication) static data * @param card Pointer to card driver structure * @param root_ca_key pointer to resulting returned key * @return SC_SUCCESS if ok; else error code */ static int dnie_get_root_ca_pubkey(sc_card_t * card, EVP_PKEY ** root_ca_key) { int res=SC_SUCCESS; RSA *root_ca_rsa=NULL; BIGNUM *root_ca_rsa_n, *root_ca_rsa_e; LOG_FUNC_CALLED(card->ctx); /* compose root_ca_public key with data provided by Dnie Manual */ *root_ca_key = EVP_PKEY_new(); root_ca_rsa = RSA_new(); if (!*root_ca_key || !root_ca_rsa) { sc_log(card->ctx, "Cannot create data for root CA public key"); return SC_ERROR_OUT_OF_MEMORY; } root_ca_rsa_n = BN_bin2bn(icc_root_ca_modulus, sizeof(icc_root_ca_modulus), NULL); root_ca_rsa_e = BN_bin2bn(icc_root_ca_public_exponent, sizeof(icc_root_ca_public_exponent), NULL); if (RSA_set0_key(root_ca_rsa, root_ca_rsa_n, root_ca_rsa_e, NULL) != 1) { BN_free(root_ca_rsa_n); BN_free(root_ca_rsa_e); if (*root_ca_key) EVP_PKEY_free(*root_ca_key); if (root_ca_rsa) RSA_free(root_ca_rsa); sc_log(card->ctx, "Cannot set RSA valuses for CA public key"); return SC_ERROR_INTERNAL; } res = EVP_PKEY_assign_RSA(*root_ca_key, root_ca_rsa); if (!res) { if (*root_ca_key) EVP_PKEY_free(*root_ca_key); /*implies root_ca_rsa free() */ sc_log(card->ctx, "Cannot compose root CA public key"); return SC_ERROR_INTERNAL; } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); }
static int sc_hsm_update_ef(sc_pkcs15_card_t *p15card, u8 prefix, u8 id, int erase, u8 *buf, size_t buflen) { sc_card_t *card = p15card->card; sc_file_t *file = NULL; sc_file_t newfile; sc_path_t path; u8 fid[2]; int r; fid[0] = prefix; fid[1] = id; sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, 2, 0, -1); r = sc_select_file(card, &path, NULL); if ((r == SC_SUCCESS) && erase) { r = sc_delete_file(card, &path); LOG_TEST_RET(card->ctx, r, "Could not delete file"); r = SC_ERROR_FILE_NOT_FOUND; } if (r == SC_ERROR_FILE_NOT_FOUND) { file = sc_file_new(); file->id = (path.value[0] << 8) | path.value[1]; file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; file->size = (size_t) 0; file->status = SC_FILE_STATUS_ACTIVATED; r = sc_create_file(card, file); sc_file_free(file); LOG_TEST_RET(card->ctx, r, "Could not create file"); } r = sc_update_binary(card, 0, buf, buflen, 0); LOG_FUNC_RETURN(card->ctx, r); }
/**************************************************************************** ** ** ** Name: _authentication_t3460_handler() ** ** ** ** Description: T3460 timeout handler ** ** Upon T3460 timer expiration, the authentication request ** ** message is retransmitted and the timer restarted. When ** ** retransmission counter is exceed, the MME shall abort the ** ** authentication procedure and any ongoing EMM specific ** ** procedure and release the NAS signalling connection. ** ** ** ** 3GPP TS 24.301, section 5.4.2.7, case b ** ** ** ** Inputs: args: handler parameters ** ** Others: None ** ** ** ** Outputs: None ** ** Return: None ** ** Others: None ** ** ** ***************************************************************************/ static void *_authentication_t3460_handler(void *args) { LOG_FUNC_IN; int rc; authentication_data_t *data = (authentication_data_t *)(args); /* Increment the retransmission counter */ data->retransmission_count += 1; LOG_TRACE(WARNING, "EMM-PROC - T3460 timer expired, retransmission " "counter = %d", data->retransmission_count); if (data->retransmission_count < AUTHENTICATION_COUNTER_MAX) { /* Send authentication request message to the UE */ rc = _authentication_request(data); } else { unsigned int ueid = data->ueid; /* Set the failure notification indicator */ data->notify_failure = TRUE; /* Abort the authentication procedure */ rc = _authentication_abort(data); /* Release the NAS signalling connection */ if (rc != RETURNerror) { emm_sap_t emm_sap; emm_sap.primitive = EMMAS_RELEASE_REQ; emm_sap.u.emm_as.u.release.guti = NULL; emm_sap.u.emm_as.u.release.ueid = ueid; emm_sap.u.emm_as.u.release.cause = EMM_AS_CAUSE_AUTHENTICATION; rc = emm_sap_send(&emm_sap); } } LOG_FUNC_RETURN (NULL); }
/**************************************************************************** ** ** ** Name: emm_send_security_mode_command() ** ** ** ** Description: Builds Security Mode Command message ** ** ** ** The Security Mode Command message is sent by the network ** ** to the UE to establish NAS signalling security. ** ** ** ** Inputs: msg: The EMMAS-SAP primitive to process ** ** Others: None ** ** ** ** Outputs: emm_msg: The EMM message to be sent ** ** Return: The size of the EMM message ** ** Others: None ** ** ** ***************************************************************************/ int emm_send_security_mode_command(const emm_as_security_t *msg, security_mode_command_msg *emm_msg) { LOG_FUNC_IN; int size = EMM_HEADER_MAXIMUM_LENGTH; LOG_TRACE(INFO, "EMMAS-SAP - Send Security Mode Command message"); /* Mandatory - Message type */ emm_msg->messagetype = SECURITY_MODE_COMMAND; /* Selected NAS security algorithms */ size += NAS_SECURITY_ALGORITHMS_MAXIMUM_LENGTH; emm_msg->selectednassecurityalgorithms.typeofcipheringalgorithm = msg->selected_eea; emm_msg->selectednassecurityalgorithms.typeofintegrityalgorithm = msg->selected_eia; /* NAS key set identifier */ size += NAS_KEY_SET_IDENTIFIER_MAXIMUM_LENGTH; emm_msg->naskeysetidentifier.tsc = NAS_KEY_SET_IDENTIFIER_NATIVE; emm_msg->naskeysetidentifier.naskeysetidentifier = msg->ksi; /* Replayed UE security capabilities */ size += UE_SECURITY_CAPABILITY_MAXIMUM_LENGTH; emm_msg->replayeduesecuritycapabilities.eea = msg->eea; emm_msg->replayeduesecuritycapabilities.eia = msg->eia; emm_msg->replayeduesecuritycapabilities.umts_present = msg->umts_present; emm_msg->replayeduesecuritycapabilities.gprs_present = msg->gprs_present; emm_msg->replayeduesecuritycapabilities.uea = msg->uea; emm_msg->replayeduesecuritycapabilities.uia = msg->uia; emm_msg->replayeduesecuritycapabilities.gea = msg->gea; LOG_FUNC_RETURN (size); }
/**************************************************************************** ** ** ** Name: _eps_bearer_deactivate_t3495_handler() ** ** ** ** Description: T3495 timeout handler ** ** ** ** 3GPP TS 24.301, section 6.4.4.5, case a ** ** On the first expiry of the timer T3495, the MME shall re- ** ** send the DEACTIVATE EPS BEARER CONTEXT REQUEST and shall ** ** reset and restart timer T3495. This retransmission is ** ** repeated four times, i.e. on the fifth expiry of timer ** ** T3495, the MME shall abort the procedure and deactivate ** ** the EPS bearer context locally. ** ** ** ** Inputs: args: handler parameters ** ** Others: None ** ** ** ** Outputs: None ** ** Return: None ** ** Others: None ** ** ** ***************************************************************************/ static void *_eps_bearer_deactivate_t3495_handler(void *args) { LOG_FUNC_IN; int rc; /* Get retransmission timer parameters data */ esm_ebr_timer_data_t *data = (esm_ebr_timer_data_t *)(args); /* Increment the retransmission counter */ data->count += 1; LOG_TRACE(WARNING, "ESM-PROC - T3495 timer expired (ueid="NAS_UE_ID_FMT", ebi=%d), " "retransmission counter = %d", data->ueid, data->ebi, data->count); if (data->count < EPS_BEARER_DEACTIVATE_COUNTER_MAX) { /* Re-send deactivate EPS bearer context request message to the UE */ rc = _eps_bearer_deactivate(data->ctx, data->ebi, &data->msg); } else { /* * The maximum number of deactivate EPS bearer context request * message retransmission has exceed */ int pid, bid; /* Deactivate the EPS bearer context locally without peer-to-peer * signalling between the UE and the MME */ rc = _eps_bearer_release(data->ctx, data->ebi, &pid, &bid); if (rc != RETURNerror) { /* Stop timer T3495 */ rc = esm_ebr_stop_timer(data->ctx, data->ebi); } } LOG_FUNC_RETURN (NULL); }
static int iso7816_update_record(struct sc_card *card, unsigned int rec_nr, const u8 *buf, size_t count, unsigned long flags) { struct sc_apdu apdu; int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xDC, rec_nr, 0); apdu.p2 = (flags & SC_RECORD_EF_ID_MASK) << 3; if (flags & SC_RECORD_BY_REC_NR) apdu.p2 |= 0x04; apdu.lc = count; apdu.datalen = count; apdu.data = buf; fixup_transceive_length(card, &apdu); r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "Card returned error"); LOG_FUNC_RETURN(card->ctx, count); }
int iasecc_sm_update_binary(struct sc_card *card, unsigned se_num, size_t offs, const unsigned char *buff, size_t count) { struct sc_context *ctx = card->ctx; #ifdef ENABLE_SM struct sm_info *sm_info = &card->sm_ctx.info; struct sc_remote_data rdata; struct iasecc_sm_cmd_update_binary cmd_data; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "SM update binary: acl:%X, offs:%i, count:%i", se_num, offs, count); rv = iasecc_sm_initialize(card, se_num, SM_CMD_FILE_UPDATE); LOG_TEST_RET(ctx, rv, "iasecc_sm_update_binary() SM INITIALIZE failed"); cmd_data.offs = offs; cmd_data.count = count; cmd_data.data = buff; sm_info->cmd_data = &cmd_data; sc_remote_data_init(&rdata); rv = iasecc_sm_cmd(card, &rdata); LOG_TEST_RET(ctx, rv, "iasecc_sm_update_binary() SM 'UPDATE BINARY' failed"); rv = sm_release (card, &rdata, NULL, 0); LOG_TEST_RET(ctx, rv, "iasecc_sm_update_binary() SM release failed"); rdata.free(&rdata); LOG_FUNC_RETURN(ctx, count); #else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "built without support of Secure-Messaging"); return SC_ERROR_NOT_SUPPORTED; #endif }
/**************************************************************************** ** ** ** Name: _emm_detach_abort() ** ** ** ** Description: Aborts the detach procedure ** ** ** ** Inputs: type: not used ** ** Others: _emm_detach_data ** ** ** ** Outputs: None ** ** Return: RETURNok, RETURNerror ** ** Others: T3421 ** ** ** ***************************************************************************/ static int _emm_detach_abort(emm_proc_detach_type_t type) { LOG_FUNC_IN; emm_sap_t emm_sap; int rc ; LOG_TRACE(WARNING, "EMM-PROC - Abort the detach procedure"); /* Reset EMM procedure handler */ (void) emm_proc_lowerlayer_initialize(NULL, NULL, NULL, NULL); /* Stop timer T3421 */ T3421.id = nas_timer_stop(T3421.id); /* * Notify EMM that detach procedure failed */ emm_sap.primitive = EMMREG_DETACH_FAILED; emm_sap.u.emm_reg.u.detach.type = type; rc = emm_sap_send(&emm_sap); LOG_FUNC_RETURN (rc); }
/**************************************************************************** ** ** ** Name: user_api_decode_data() ** ** ** ** Description: Parses the message received from the user application ** ** (mainly AT commands) and fills the user data structure. ** ** Returns an AT syntax error code to the user application ** ** layer when the AT command failed to be decoded. ** ** ** ** Inputs: length: Number of bytes to decode ** ** Others: _user_api_recv_buffer ** ** ** ** Outputs: Return: The number of AT commands succeessfully ** ** decoded ** ** Others: _user_api_send_buffer, _user_data ** ** ** ***************************************************************************/ int user_api_decode_data(int length) { LOG_FUNC_IN; /* Parse the AT command line */ LOG_TRACE(INFO, "USR-API - Decode user data: %s", _user_api_recv_buffer); _user_data.n_cmd = at_command_decode(_user_api_recv_buffer, length, _user_data.cmd); if (_user_data.n_cmd > 0) { /* AT command data received from the user application layer * has been successfully decoded */ LOG_TRACE(INFO, "USR-API - %d AT command%s ha%s been successfully " "decoded", _user_data.n_cmd, (_user_data.n_cmd > 1) ? "s" : "", (_user_data.n_cmd > 1) ? "ve" : "s"); } else { int bytes; /* Failed to decode AT command data received from the user * application layer; Return syntax error code message */ LOG_TRACE(ERROR, "USR-API - Syntax error: Failed to decode " "AT command data %s", _user_api_recv_buffer); /* Encode the syntax error code message */ bytes = at_error_encode(_user_api_send_buffer, AT_ERROR_SYNTAX, AT_ERROR_OPERATION_NOT_SUPPORTED); /* Send the syntax error code message */ (void) _user_api_send_data(bytes); } LOG_FUNC_RETURN (_user_data.n_cmd); }
/**************************************************************************** ** ** ** Name: emm_proc_detach_failure() ** ** ** ** Description: Performs the detach procedure abnormal case upon receipt ** ** of transmission failure of Detach Request message. ** ** ** ** 3GPP TS 24.301, section 5.5.2.2.4, case h ** ** The UE shall restart the detach procedure. ** ** ** ** Inputs: is_initial: Not used ** ** args: Not used ** ** Others: _emm_detach_data ** ** ** ** Outputs: None ** ** Return: RETURNok, RETURNerror ** ** Others: None ** ** ** ***************************************************************************/ int emm_proc_detach_failure(int is_initial, void *args) { LOG_FUNC_IN; emm_sap_t emm_sap; int rc; LOG_TRACE(WARNING, "EMM-PROC - Network detach failure"); /* Reset EMM procedure handler */ (void) emm_proc_lowerlayer_initialize(NULL, NULL, NULL, NULL); /* Stop timer T3421 */ T3421.id = nas_timer_stop(T3421.id); /* * Notify EMM that detach procedure has to be restarted */ emm_sap.primitive = EMMREG_DETACH_INIT; emm_sap.u.emm_reg.u.detach.switch_off = _emm_detach_data.switch_off; rc = emm_sap_send(&emm_sap); LOG_FUNC_RETURN(rc); }
static int sc_pkcs15emu_sc_hsm_read_tokeninfo (sc_pkcs15_card_t * p15card) { sc_card_t *card = p15card->card; sc_file_t *file = NULL; sc_path_t path; int r; u8 efbin[512]; LOG_FUNC_CALLED(card->ctx); /* Read token info */ sc_path_set(&path, SC_PATH_TYPE_FILE_ID, (u8 *) "\x2F\x03", 2, 0, 0); r = sc_select_file(card, &path, &file); LOG_TEST_RET(card->ctx, r, "Could not select EF.TokenInfo"); sc_file_free(file); r = sc_read_binary(p15card->card, 0, efbin, sizeof(efbin), 0); LOG_TEST_RET(card->ctx, r, "Could not read EF.TokenInfo"); r = sc_pkcs15_parse_tokeninfo(card->ctx, p15card->tokeninfo, efbin, r); LOG_TEST_RET(card->ctx, r, "Could not decode EF.TokenInfo"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); }
/* * Select the PIN reference */ static int myeid_select_pin_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_auth_info_t *auth_info) { SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) { sc_log(p15card->card->ctx, "PIN_FLAG_SO_PIN, ref (%d), tries_left (%d)", auth_info->attrs.pin.reference, auth_info->tries_left); } else { sc_log(p15card->card->ctx, "PIN_FLAG_PIN, ref (%d), tries_left (%d)", auth_info->attrs.pin.reference, auth_info->tries_left); } if (auth_info->attrs.pin.reference <= 0 || auth_info->attrs.pin.reference > MYEID_MAX_PINS) auth_info->attrs.pin.reference = 1; LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); }
/**************************************************************************** ** ** ** Name: esm_proc_status() ** ** ** ** Description: Initiates ESM status procedure. ** ** ** ** Inputs: is_standalone: Not used - Always TRUE ** ** ueid: UE lower layer identifier ** ** ebi: Not used ** ** msg: Encoded ESM status message to be sent ** ** ue_triggered: Not used ** ** Others: None ** ** ** ** Outputs: None ** ** Return: RETURNok, RETURNerror ** ** Others: None ** ** ** ***************************************************************************/ int esm_proc_status ( int is_standalone, emm_data_context_t * ctx, int ebi, OctetString * msg, int ue_triggered) { LOG_FUNC_IN; int rc; emm_sap_t emm_sap; LOG_TRACE (INFO, "ESM-PROC - ESM status procedure requested"); /* * Notity EMM that ESM PDU has to be forwarded to lower layers */ emm_sap.primitive = EMMESM_UNITDATA_REQ; emm_sap.u.emm_esm.ueid = ctx->ueid; emm_sap.u.emm_esm.ctx = ctx; emm_sap.u.emm_esm.u.data.msg.length = msg->length; emm_sap.u.emm_esm.u.data.msg.value = msg->value; rc = emm_sap_send (&emm_sap); LOG_FUNC_RETURN (rc); }
int iasecc_sm_create_file(struct sc_card *card, unsigned se_num, unsigned char *fcp, size_t fcp_len) { struct sc_context *ctx = card->ctx; #ifdef ENABLE_SM struct sm_info *sm_info = &card->sm_ctx.info; struct sc_remote_data rdata; struct iasecc_sm_cmd_create_file cmd_data; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "iasecc_sm_create_file() SE#%i, fcp(%"SC_FORMAT_LEN_SIZE_T"u) '%s'", se_num, fcp_len, sc_dump_hex(fcp, fcp_len)); rv = iasecc_sm_initialize(card, se_num, SM_CMD_FILE_CREATE); LOG_TEST_RET(ctx, rv, "iasecc_sm_create_file() SM INITIALIZE failed"); cmd_data.data = fcp; cmd_data.size = fcp_len; sm_info->cmd_data = &cmd_data; sc_remote_data_init(&rdata); rv= iasecc_sm_cmd(card, &rdata); LOG_TEST_RET(ctx, rv, "iasecc_sm_create_file() SM 'UPDATE BINARY' failed"); rv = sm_release (card, &rdata, NULL, 0); LOG_TEST_RET(ctx, rv, "iasecc_sm_create_file() SM release failed"); rdata.free(&rdata); LOG_FUNC_RETURN(ctx, rv); #else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "built without support of Secure-Messaging"); return SC_ERROR_NOT_SUPPORTED; #endif }
static int iasecc_parse_pubkey(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_sdo_pubkey *pubkey) { struct sc_context *ctx = card->ctx; size_t offs = 0; int rv; LOG_FUNC_CALLED(ctx); while(offs < data_len) { struct iasecc_extended_tlv tlv; rv = iasecc_parse_get_tlv(card, data + offs, &tlv); LOG_TEST_RET(ctx, rv, "iasecc_parse_pubkey() get and parse TLV error"); sc_log(ctx, "iasecc_parse_pubkey() get and parse TLV returned %i; tag %X; size %"SC_FORMAT_LEN_SIZE_T"u", rv, tlv.tag, tlv.size); if (tlv.tag == IASECC_SDO_PUBKEY_TAG_N) pubkey->n = tlv; else if (tlv.tag == IASECC_SDO_PUBKEY_TAG_E) pubkey->e = tlv; else if (tlv.tag == IASECC_SDO_PUBKEY_TAG_CHR) pubkey->chr = tlv; else if (tlv.tag == IASECC_SDO_PUBKEY_TAG_CHA) pubkey->cha = tlv; else if (tlv.tag == IASECC_SDO_PUBKEY_TAG_COMPULSORY) pubkey->compulsory = tlv; else LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "parse error: non PubKey SDO tag"); offs += rv; } LOG_FUNC_RETURN(ctx, SC_SUCCESS); }
int sm_cwa_decode_authentication_data(struct sc_context *ctx, struct sm_cwa_keyset *keyset, struct sm_cwa_session *session_data, unsigned char *auth_data) { DES_cblock icv = {0, 0, 0, 0, 0, 0, 0, 0}; DES_cblock cblock; unsigned char *decrypted = NULL; size_t decrypted_len; int rv; LOG_FUNC_CALLED(ctx); memset(icv, 0, sizeof(icv)); rv = sm_cwa_get_mac(ctx, keyset->mac, &icv, session_data->mdata, 0x40, &cblock, 1); LOG_TEST_RET(ctx, rv, "Decode authentication data: sm_ecc_get_mac failed"); sc_log(ctx, "MAC:%s", sc_dump_hex(cblock, sizeof(cblock))); if(memcmp(session_data->mdata + 0x40, cblock, 8)) LOG_FUNC_RETURN(ctx, SC_ERROR_SM_AUTHENTICATION_FAILED); rv = sm_decrypt_des_cbc3(ctx, keyset->enc, session_data->mdata, session_data->mdata_len, &decrypted, &decrypted_len); LOG_TEST_RET(ctx, rv, "sm_ecc_decode_auth_data() DES CBC3 decrypt error"); sc_log(ctx, "sm_ecc_decode_auth_data() decrypted(%i) %s", decrypted_len, sc_dump_hex(decrypted, decrypted_len)); if (memcmp(decrypted, session_data->icc.rnd, 8)) LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); if (memcmp(decrypted + 8, session_data->icc.sn, 8)) LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); if (memcmp(decrypted + 16, session_data->ifd.rnd, 8)) LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); if (memcmp(decrypted + 24, session_data->ifd.sn, 8)) LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); memcpy(session_data->icc.k, decrypted + 32, 32); free(decrypted); LOG_FUNC_RETURN(ctx, SC_SUCCESS); }
int sm_gp_decode_card_answer(struct sc_context *ctx, struct sc_remote_data *rdata, unsigned char *out, size_t out_len) { LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); }
int sm_gp_external_authentication(struct sc_context *ctx, struct sm_info *sm_info, unsigned char *init_data, size_t init_len, struct sc_remote_data *rdata, int (*diversify_keyset)(struct sc_context *ctx, struct sm_info *sm_info, unsigned char *idata, size_t idata_len)) { struct sc_remote_apdu *new_rapdu = NULL; struct sc_apdu *apdu = NULL; unsigned char host_cryptogram[8], raw_apdu[SC_MAX_APDU_BUFFER_SIZE]; struct sm_gp_session *gp_session = &sm_info->session.gp; DES_cblock mac; int rv, offs = 0; LOG_FUNC_CALLED(ctx); if (!sm_info || !init_data || !rdata || !rdata->alloc) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); if (init_len != 0x1C) LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "SM GP authentication: invalid auth data length"); rv = sm_gp_parse_init_data(ctx, gp_session, init_data, init_len); LOG_TEST_RET(ctx, rv, "SM GP authentication: 'INIT DATA' parse error"); if (diversify_keyset) { rv = (*diversify_keyset)(ctx, sm_info, init_data, init_len); LOG_TEST_RET(ctx, rv, "SM GP authentication: keyset diversification error"); } rv = sm_gp_init_session(ctx, gp_session, init_data + 20, 8); LOG_TEST_RET(ctx, rv, "SM GP authentication: init session error"); rv = sm_gp_get_cryptogram(gp_session->session_enc, gp_session->card_challenge, gp_session->host_challenge, host_cryptogram, sizeof(host_cryptogram)); LOG_TEST_RET(ctx, rv, "SM GP authentication: get host cryptogram error"); sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP authentication: host_cryptogram:%s", sc_dump_hex(host_cryptogram, 8)); rv = rdata->alloc(rdata, &new_rapdu); LOG_TEST_RET(ctx, rv, "SM GP authentication: cannot allocate remote APDU"); apdu = &new_rapdu->apdu; offs = 0; apdu->cse = SC_APDU_CASE_3_SHORT; apdu->cla = raw_apdu[offs++] = 0x84; apdu->ins = raw_apdu[offs++] = 0x82; apdu->p1 = raw_apdu[offs++] = gp_session->params.level; apdu->p2 = raw_apdu[offs++] = 0; apdu->lc = raw_apdu[offs++] = 0x10; apdu->datalen = 0x10; memcpy(raw_apdu + offs, host_cryptogram, 8); offs += 8; rv = sm_gp_get_mac(gp_session->session_mac, &gp_session->mac_icv, raw_apdu, offs, &mac); LOG_TEST_RET(ctx, rv, "SM GP authentication: get MAC error"); memcpy(new_rapdu->sbuf, host_cryptogram, 8); memcpy(new_rapdu->sbuf + 8, mac, 8); memcpy(gp_session->mac_icv, mac, 8); LOG_FUNC_RETURN(ctx, 1); }
int sm_gp_securize_apdu(struct sc_context *ctx, struct sm_info *sm_info, char *init_data, struct sc_apdu *apdu) { unsigned char buff[SC_MAX_APDU_BUFFER_SIZE + 24]; unsigned char *apdu_data = NULL; struct sm_gp_session *gp_session = &sm_info->session.gp; unsigned gp_level = sm_info->session.gp.params.level; unsigned gp_index = sm_info->session.gp.params.index; DES_cblock mac; unsigned char *encrypted = NULL; size_t encrypted_len = 0; int rv; LOG_FUNC_CALLED(ctx); apdu_data = (unsigned char *)apdu->data; sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP securize APDU(cse:%X,cla:%X,ins:%X,data(len:%"SC_FORMAT_LEN_SIZE_T"u,%p),lc:%"SC_FORMAT_LEN_SIZE_T"u,GP level:%X,GP index:%X", apdu->cse, apdu->cla, apdu->ins, apdu->datalen, apdu->data, apdu->lc, gp_level, gp_index); if (gp_level == 0 || (apdu->cla & 0x04)) return 0; if (gp_level == SM_GP_SECURITY_MAC) { if (apdu->datalen + 8 > SC_MAX_APDU_BUFFER_SIZE) LOG_TEST_RET(ctx, SC_ERROR_WRONG_LENGTH, "SM GP securize APDU: too much data"); } else if (gp_level == SM_GP_SECURITY_ENC) { if (!gp_session->session_enc) LOG_TEST_RET(ctx, SC_ERROR_SM_INVALID_SESSION_KEY, "SM GP securize APDU: no ENC session key found"); if (sm_gp_encrypt_command_data(ctx, gp_session->session_enc, apdu->data, apdu->datalen, &encrypted, &encrypted_len)) LOG_TEST_RET(ctx, SC_ERROR_SM_ENCRYPT_FAILED, "SM GP securize APDU: data encryption error"); if (encrypted_len + 8 > SC_MAX_APDU_BUFFER_SIZE) LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "SM GP securize APDU: not enough place for encrypted data"); sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP securize APDU: encrypted length %"SC_FORMAT_LEN_SIZE_T"u", encrypted_len); } else { LOG_TEST_RET(ctx, SC_ERROR_SM_INVALID_LEVEL, "SM GP securize APDU: invalid SM level"); } buff[0] = apdu->cla | 0x04; buff[1] = apdu->ins; buff[2] = apdu->p1; buff[3] = apdu->p2; buff[4] = apdu->lc + 8; memcpy(buff + 5, apdu_data, apdu->datalen); rv = sm_gp_get_mac(gp_session->session_mac, &gp_session->mac_icv, buff, 5 + apdu->datalen, &mac); LOG_TEST_RET(ctx, rv, "SM GP securize APDU: get MAC error"); if (gp_level == SM_GP_SECURITY_MAC) { memcpy(apdu_data + apdu->datalen, mac, 8); apdu->cla |= 0x04; apdu->datalen += 8; apdu->lc = apdu->datalen; if (apdu->cse==SC_APDU_CASE_2_SHORT) apdu->cse = SC_APDU_CASE_4_SHORT; } else if (gp_level == SM_GP_SECURITY_ENC) { memcpy(apdu_data + encrypted_len, mac, 8); if (encrypted_len) memcpy(apdu_data, encrypted, encrypted_len); apdu->cla |= 0x04; apdu->datalen = encrypted_len + 8; apdu->lc = encrypted_len + 8; if (apdu->cse == SC_APDU_CASE_2_SHORT) apdu->cse = SC_APDU_CASE_4_SHORT; if (apdu->cse == SC_APDU_CASE_1) apdu->cse = SC_APDU_CASE_3_SHORT; free(encrypted); } memcpy(sm_info->session.gp.mac_icv, mac, 8); LOG_FUNC_RETURN(ctx, rv); }
static int parse_x509_cert(sc_context_t *ctx, struct sc_pkcs15_der *der, struct sc_pkcs15_cert *cert) { int r; struct sc_algorithm_id sig_alg; struct sc_pkcs15_pubkey *pubkey = NULL; unsigned char *serial = NULL, *issuer = NULL, *subject = NULL, *buf = der->value; size_t serial_len = 0, issuer_len = 0, subject_len = 0, data_len = 0, buflen = der->len; struct sc_asn1_entry asn1_version[] = { { "version", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, &cert->version, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_x509v3[] = { { "certificatePolicies", SC_ASN1_OCTET_STRING, SC_ASN1_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "subjectKeyIdentifier", SC_ASN1_OCTET_STRING, SC_ASN1_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "crlDistributionPoints", SC_ASN1_OCTET_STRING, SC_ASN1_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL | SC_ASN1_ALLOC, &cert->crl, &cert->crl_len }, { "authorityKeyIdentifier", SC_ASN1_OCTET_STRING, SC_ASN1_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "keyUsage", SC_ASN1_BOOLEAN, SC_ASN1_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_extensions[] = { { "x509v3", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, asn1_x509v3, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_tbscert[] = { { "version", SC_ASN1_STRUCT, SC_ASN1_CTX | 0 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, asn1_version, NULL }, { "serialNumber", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, &serial, &serial_len }, { "signature", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { "issuer", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_ALLOC, &issuer, &issuer_len }, { "validity", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { "subject", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_ALLOC, &subject, &subject_len }, /* Use a callback to get the algorithm, parameters and pubkey into sc_pkcs15_pubkey */ { "subjectPublicKeyInfo",SC_ASN1_CALLBACK, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, sc_pkcs15_pubkey_from_spki, &pubkey }, { "extensions", SC_ASN1_STRUCT, SC_ASN1_CTX | 3 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, asn1_extensions, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_cert[] = { { "tbsCertificate", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, asn1_tbscert, NULL }, { "signatureAlgorithm", SC_ASN1_ALGORITHM_ID, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, &sig_alg, NULL }, { "signatureValue", SC_ASN1_BIT_STRING, SC_ASN1_TAG_BIT_STRING, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_serial_number[] = { { "serialNumber", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_subject[] = { { "subject", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_ALLOC, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_issuer[] = { { "issuer", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_ALLOC, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; const u8 *obj; size_t objlen; memset(cert, 0, sizeof(*cert)); obj = sc_asn1_verify_tag(ctx, buf, buflen, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, &objlen); if (obj == NULL) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ASN1_OBJECT, "X.509 certificate not found"); data_len = objlen + (obj - buf); cert->data.value = malloc(data_len); if (!cert->data.value) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); memcpy(cert->data.value, buf, data_len); cert->data.len = data_len; r = sc_asn1_decode(ctx, asn1_cert, obj, objlen, NULL, NULL); LOG_TEST_RET(ctx, r, "ASN.1 parsing of certificate failed"); cert->version++; if (!pubkey) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ASN1_OBJECT, "Unable to decode subjectPublicKeyInfo from cert"); cert->key = pubkey; sc_asn1_clear_algorithm_id(&sig_alg); if (serial && serial_len) { sc_format_asn1_entry(asn1_serial_number + 0, serial, &serial_len, 1); r = sc_asn1_encode(ctx, asn1_serial_number, &cert->serial, &cert->serial_len); free(serial); LOG_TEST_RET(ctx, r, "ASN.1 encoding of serial failed"); } if (subject && subject_len) { sc_format_asn1_entry(asn1_subject + 0, subject, &subject_len, 1); r = sc_asn1_encode(ctx, asn1_subject, &cert->subject, &cert->subject_len); free(subject); LOG_TEST_RET(ctx, r, "ASN.1 encoding of subject"); } if (issuer && issuer_len) { sc_format_asn1_entry(asn1_issuer + 0, issuer, &issuer_len, 1); r = sc_asn1_encode(ctx, asn1_issuer, &cert->issuer, &cert->issuer_len); free(issuer); LOG_TEST_RET(ctx, r, "ASN.1 encoding of issuer"); } return SC_SUCCESS; }