| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "include/ese/app/boot.h" |
| #include "boot_private.h" |
| |
| const uint8_t kBootStateVersion = 0x1; |
| const uint16_t kBootStorageLength = 4096; |
| /* Non-static, but visibility=hidden so they can be used in test. */ |
| const uint8_t kManageChannelOpen[] = {0x00, 0x70, 0x00, 0x00, 0x01}; |
| const uint32_t kManageChannelOpenLength = (uint32_t)sizeof(kManageChannelOpen); |
| const uint8_t kManageChannelClose[] = {0x00, 0x70, 0x80, 0x00, 0x00}; |
| |
| const uint8_t kSelectApplet[] = {0x00, 0xA4, 0x04, 0x00, 0x0d, 0xA0, |
| 0x00, 0x00, 0x04, 0x76, 0x50, 0x49, |
| 0x58, 0x4C, 0x42, 0x4F, 0x4F, 0x54}; |
| const uint32_t kSelectAppletLength = (uint32_t)sizeof(kSelectApplet); |
| // Supported commands. |
| const uint8_t kGetState[] = {0x80, 0x00, 0x00, 0x00, 0x00}; |
| const uint8_t kLoadCmd[] = {0x80, 0x02}; |
| const uint8_t kStoreCmd[] = {0x80, 0x04}; |
| const uint8_t kGetLockState[] = {0x80, 0x06, 0x00, 0x00, 0x00}; |
| const uint8_t kSetLockState[] = {0x80, 0x08, 0x00, 0x00, 0x00}; |
| const uint8_t kSetProduction[] = {0x80, 0x0a}; |
| const uint8_t kCarrierLockTest[] = {0x80, 0x0c, 0x00, 0x00}; |
| const uint8_t kFactoryReset[] = {0x80, 0x0e, 0x00, 0x00}; |
| const uint8_t kLockReset[] = {0x80, 0x0e, 0x01, 0x00}; |
| const uint8_t kLoadMetaClear[] = {0x80, 0x10, 0x00, 0x00}; |
| const uint8_t kLoadMetaAppend[] = {0x80, 0x10, 0x01, 0x00}; |
| static const uint16_t kMaxMetadataLoadSize = 1024; |
| |
| EseAppResult check_apdu_status(uint8_t code[2]) { |
| if (code[0] == 0x90 && code[1] == 0x00) { |
| return ESE_APP_RESULT_OK; |
| } |
| if (code[0] == 0x66 && code[1] == 0xA5) { |
| return ESE_APP_RESULT_ERROR_COOLDOWN; |
| } |
| if (code[0] == 0x6A && code[1] == 0x83) { |
| return ESE_APP_RESULT_ERROR_UNCONFIGURED; |
| } |
| /* TODO(wad) Bubble up the error code if needed. */ |
| ALOGE("unhandled response %.2x %.2x", code[0], code[1]); |
| return ese_make_os_result(code[0], code[1]); |
| } |
| |
| ESE_API void ese_boot_session_init(struct EseBootSession *session) { |
| session->ese = NULL; |
| session->active = false; |
| session->channel_id = 0; |
| } |
| |
| ESE_API EseAppResult ese_boot_session_open(struct EseInterface *ese, |
| struct EseBootSession *session) { |
| struct EseSgBuffer tx[2]; |
| struct EseSgBuffer rx; |
| uint8_t rx_buf[32]; |
| int rx_len; |
| if (!ese || !session) { |
| ALOGE("Invalid |ese| or |session|"); |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| if (session->active == true) { |
| ALOGE("|session| is already active"); |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| /* Instantiate a logical channel */ |
| rx_len = ese_transceive(ese, kManageChannelOpen, sizeof(kManageChannelOpen), |
| rx_buf, sizeof(rx_buf)); |
| if (ese_error(ese)) { |
| ALOGE("transceive error: code:%d message:'%s'", ese_error_code(ese), |
| ese_error_message(ese)); |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| if (rx_len < 0) { |
| ALOGE("transceive error: rx_len: %d", rx_len); |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| if (rx_len < 2) { |
| ALOGE("transceive error: reply too short"); |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| EseAppResult ret; |
| ret = check_apdu_status(&rx_buf[rx_len - 2]); |
| if (ret != ESE_APP_RESULT_OK) { |
| ALOGE("MANAGE CHANNEL OPEN failed with error code: %x %x", |
| rx_buf[rx_len - 2], rx_buf[rx_len - 1]); |
| return ret; |
| } |
| if (rx_len < 3) { |
| ALOGE("transceive error: successful reply unexpectedly short"); |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| session->ese = ese; |
| session->channel_id = rx_buf[rx_len - 3]; |
| |
| /* Select Boot Applet. */ |
| uint8_t chan = kSelectApplet[0] | session->channel_id; |
| tx[0].base = &chan; |
| tx[0].len = 1; |
| tx[1].base = (uint8_t *)&kSelectApplet[1]; |
| tx[1].len = sizeof(kSelectApplet) - 1; |
| rx.base = &rx_buf[0]; |
| rx.len = sizeof(rx_buf); |
| rx_len = ese_transceive_sg(ese, tx, 2, &rx, 1); |
| if (rx_len < 0 || ese_error(ese)) { |
| ALOGE("transceive error: caller should check ese_error()"); |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| if (rx_len < 2) { |
| ALOGE("transceive error: reply too short"); |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| ret = check_apdu_status(&rx_buf[rx_len - 2]); |
| if (ret != ESE_APP_RESULT_OK) { |
| ALOGE("SELECT failed with error code: %x %x", rx_buf[rx_len - 2], |
| rx_buf[rx_len - 1]); |
| return ret; |
| } |
| session->active = true; |
| return ESE_APP_RESULT_OK; |
| } |
| |
| ESE_API EseAppResult ese_boot_session_close(struct EseBootSession *session) { |
| uint8_t rx_buf[32]; |
| int rx_len; |
| if (!session || !session->ese) { |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| if (!session->active || session->channel_id == 0) { |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| /* Release the channel */ |
| uint8_t close_channel[sizeof(kManageChannelClose)]; |
| ese_memcpy(close_channel, kManageChannelClose, sizeof(kManageChannelClose)); |
| close_channel[0] |= session->channel_id; |
| close_channel[3] |= session->channel_id; |
| rx_len = ese_transceive(session->ese, close_channel, sizeof(close_channel), |
| rx_buf, sizeof(rx_buf)); |
| if (rx_len < 0 || ese_error(session->ese)) { |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| if (rx_len < 2) { |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| EseAppResult ret; |
| ret = check_apdu_status(&rx_buf[rx_len - 2]); |
| if (ret != ESE_APP_RESULT_OK) { |
| return ret; |
| } |
| session->channel_id = 0; |
| session->active = false; |
| return ESE_APP_RESULT_OK; |
| } |
| |
| ESE_API EseAppResult ese_boot_lock_xget(struct EseBootSession *session, |
| EseBootLockId lock, uint8_t *lockData, |
| uint16_t maxSize, uint16_t *length) { |
| struct EseSgBuffer tx[4]; |
| struct EseSgBuffer rx[3]; |
| int rx_len; |
| if (!session || !session->ese || !session->active) { |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| if (lock > kEseBootLockIdMax) { |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| if (maxSize < 1 || maxSize > 4096) { |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| uint8_t chan = kGetLockState[0] | session->channel_id; |
| tx[0].base = &chan; |
| tx[0].len = 1; |
| tx[1].base = (uint8_t *)&kGetLockState[1]; |
| tx[1].len = 1; |
| |
| uint8_t p1p2[] = {lock, 0x01}; |
| tx[2].base = &p1p2[0]; |
| tx[2].len = sizeof(p1p2); |
| |
| // Accomodate the applet 2 byte status code. |
| uint8_t max_reply[] = {0x0, ((maxSize + 2) >> 8), ((maxSize + 2) & 0xff)}; |
| tx[3].base = &max_reply[0]; |
| tx[3].len = sizeof(max_reply); |
| |
| uint8_t reply[2]; // App reply or APDU error. |
| rx[0].base = &reply[0]; |
| rx[0].len = sizeof(reply); |
| // Applet data |
| rx[1].base = lockData; |
| rx[1].len = maxSize; |
| // Only used if the full maxSize is used. |
| uint8_t apdu_status[2]; |
| rx[2].base = &apdu_status[0]; |
| rx[2].len = sizeof(apdu_status); |
| |
| rx_len = ese_transceive_sg(session->ese, tx, 4, rx, 3); |
| if (rx_len < 2 || ese_error(session->ese)) { |
| ALOGE("ese_boot_lock_xget: failed to read lock state (%d)", lock); |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| if (rx_len == 2) { |
| ALOGE("ese_boot_lock_xget: SE exception"); |
| EseAppResult ret = check_apdu_status(&reply[0]); |
| return ret; |
| } |
| // Expect the full payload plus the aplet status and the completion code. |
| *length = (uint16_t)(rx_len - 4); |
| if (rx_len == 4) { |
| ALOGE("ese_boot_lock_xget: received applet error code %x %x", lockData[0], |
| lockData[1]); |
| return ese_make_app_result(lockData[0], lockData[1]); |
| } |
| return ESE_APP_RESULT_OK; |
| } |
| |
| ESE_API EseAppResult ese_boot_lock_get(struct EseBootSession *session, |
| EseBootLockId lock, uint8_t *lockVal) { |
| struct EseSgBuffer tx[3]; |
| struct EseSgBuffer rx[1]; |
| int rx_len; |
| if (!session || !session->ese || !session->active) { |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| if (lock > kEseBootLockIdMax) { |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| uint8_t chan = kGetLockState[0] | session->channel_id; |
| tx[0].base = &chan; |
| tx[0].len = 1; |
| tx[1].base = (uint8_t *)&kGetLockState[1]; |
| tx[1].len = 1; |
| |
| uint8_t p1p2[] = {lock, 0x0}; |
| tx[2].base = &p1p2[0]; |
| tx[2].len = sizeof(p1p2); |
| |
| uint8_t reply[6]; |
| rx[0].base = &reply[0]; |
| rx[0].len = sizeof(reply); |
| |
| rx_len = ese_transceive_sg(session->ese, tx, 3, rx, 1); |
| if (rx_len < 2 || ese_error(session->ese)) { |
| ALOGE("ese_boot_lock_get: failed to read lock state (%d).", lock); |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| EseAppResult ret = check_apdu_status(&reply[rx_len - 2]); |
| if (ret != ESE_APP_RESULT_OK) { |
| ALOGE("ese_boot_lock_get: SE OS error."); |
| return ret; |
| } |
| if (rx_len < 5) { |
| ALOGE("ese_boot_lock_get: communication error"); |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| // TODO: unify in the applet, then map them here. |
| if (reply[0] != 0x0 && reply[1] != 0x0) { |
| ALOGE("ese_boot_lock_get: Applet error: %x %x", reply[0], reply[1]); |
| return ese_make_app_result(reply[0], reply[1]); |
| } |
| if (lockVal) { |
| *lockVal = reply[2]; |
| return ESE_APP_RESULT_OK; |
| } |
| |
| if (reply[2] != 0) { |
| return ESE_APP_RESULT_TRUE; |
| } |
| return ESE_APP_RESULT_FALSE; |
| } |
| |
| EseAppResult ese_boot_meta_clear(struct EseBootSession *session) { |
| struct EseSgBuffer tx[2]; |
| struct EseSgBuffer rx[1]; |
| int rx_len; |
| if (!session || !session->ese || !session->active) { |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| |
| uint8_t chan = kLoadMetaClear[0] | session->channel_id; |
| tx[0].base = &chan; |
| tx[0].len = 1; |
| tx[1].base = (uint8_t *)&kLoadMetaClear[1]; |
| tx[1].len = sizeof(kLoadMetaClear) - 1; |
| |
| uint8_t reply[4]; // App reply or APDU error. |
| rx[0].base = &reply[0]; |
| rx[0].len = sizeof(reply); |
| |
| rx_len = ese_transceive_sg(session->ese, tx, 2, rx, 1); |
| if (rx_len < 2 || ese_error(session->ese)) { |
| ALOGE("ese_boot_meta_clear: communication failure"); |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| // Expect the full payload plus the applet status and the completion code. |
| if (rx_len < 4) { |
| ALOGE("ese_boot_meta_clear: SE exception"); |
| EseAppResult ret = check_apdu_status(&reply[rx_len - 2]); |
| return ret; |
| } |
| if (reply[0] != 0x0 || reply[1] != 0x0) { |
| ALOGE("ese_boot_meta_clear: received applet error code %.2x %.2x", reply[0], |
| reply[1]); |
| return ese_make_app_result(reply[0], reply[1]); |
| } |
| return ESE_APP_RESULT_OK; |
| } |
| |
| EseAppResult ese_boot_meta_append(struct EseBootSession *session, |
| const uint8_t *data, uint16_t dataLen) { |
| struct EseSgBuffer tx[4]; |
| struct EseSgBuffer rx[1]; |
| int rx_len; |
| if (!session || !session->ese || !session->active) { |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| if (dataLen > kMaxMetadataLoadSize) { |
| ALOGE("ese_boot_meta_append: too much data provided"); |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| |
| uint8_t chan = kLoadMetaAppend[0] | session->channel_id; |
| tx[0].base = &chan; |
| tx[0].len = 1; |
| tx[1].base = (uint8_t *)&kLoadMetaAppend[1]; |
| tx[1].len = sizeof(kLoadMetaAppend) - 1; |
| |
| uint8_t apdu_len[] = {0x0, (dataLen >> 8), (dataLen & 0xff)}; |
| tx[2].base = &apdu_len[0]; |
| tx[2].len = sizeof(apdu_len); |
| tx[3].c_base = data; |
| tx[3].len = dataLen; |
| |
| uint8_t reply[4]; // App reply or APDU error. |
| rx[0].base = &reply[0]; |
| rx[0].len = sizeof(reply); |
| |
| rx_len = ese_transceive_sg(session->ese, tx, 4, rx, 1); |
| if (rx_len < 2 || ese_error(session->ese)) { |
| ALOGE("ese_boot_meta_append: communication failure"); |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| // Expect the full payload plus the applet status and the completion code. |
| if (rx_len < 4) { |
| ALOGE("ese_boot_meta_append: SE exception"); |
| EseAppResult ret = check_apdu_status(&reply[rx_len - 2]); |
| return ret; |
| } |
| if (reply[0] != 0x0 || reply[1] != 0x0) { |
| ALOGE("ese_boot_meta_append: received applet error code %.2x %.2x", |
| reply[0], reply[1]); |
| return ese_make_app_result(reply[0], reply[1]); |
| } |
| return ESE_APP_RESULT_OK; |
| } |
| |
| ESE_API EseAppResult ese_boot_lock_xset(struct EseBootSession *session, |
| EseBootLockId lockId, |
| const uint8_t *lockData, |
| uint16_t dataLen) { |
| struct EseSgBuffer tx[3]; |
| struct EseSgBuffer rx[1]; |
| int rx_len; |
| if (!session || !session->ese || !session->active) { |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| if (lockId > kEseBootLockIdMax) { |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| if (dataLen < 1 || dataLen > kEseBootOwnerKeyMax + 1) { |
| ALOGE("ese_boot_lock_xset: too much data: %hu > %d", dataLen, |
| kEseBootOwnerKeyMax + 1); |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| |
| // Locks with metadata require a multi-step upload to meet the |
| // constraints of the transport. |
| EseAppResult res = ese_boot_meta_clear(session); |
| if (res != ESE_APP_RESULT_OK) { |
| ALOGE("ese_boot_lock_xset: unable to clear scratch metadata"); |
| return res; |
| } |
| // The first byte is the lock value itself, so we skip it. |
| const uint8_t *cursor = &lockData[1]; |
| uint16_t remaining = dataLen - 1; |
| while (remaining > 0) { |
| uint16_t chunk = (512 < remaining) ? 512 : remaining; |
| res = ese_boot_meta_append(session, cursor, chunk); |
| ALOGI("ese_boot_lock_xset: sending chunk %x", remaining); |
| if (res != ESE_APP_RESULT_OK) { |
| ALOGE("ese_boot_lock_xset: unable to upload metadata"); |
| return res; |
| } |
| remaining -= chunk; |
| cursor += chunk; |
| } |
| |
| uint8_t chan = kSetLockState[0] | session->channel_id; |
| tx[0].base = &chan; |
| tx[0].len = 1; |
| tx[1].base = (uint8_t *)&kSetLockState[1]; |
| tx[1].len = 1; |
| |
| uint8_t lockIdLockValueUseMeta[] = {lockId, lockData[0], 0x1, 0x1}; |
| tx[2].base = &lockIdLockValueUseMeta[0]; |
| tx[2].len = sizeof(lockIdLockValueUseMeta); |
| |
| uint8_t reply[4]; // App reply or APDU error. |
| rx[0].base = &reply[0]; |
| rx[0].len = sizeof(reply); |
| |
| rx_len = ese_transceive_sg(session->ese, tx, 3, rx, 1); |
| if (rx_len < 2 || ese_error(session->ese)) { |
| ALOGE("ese_boot_lock_xset: failed to set lock state (%d).", lockId); |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| if (rx_len == 2) { |
| ALOGE("ese_boot_lock_xset: SE exception"); |
| EseAppResult ret = check_apdu_status(&reply[0]); |
| return ret; |
| } |
| // Expect the full payload plus the applet status and the completion code. |
| if (rx_len != 4) { |
| ALOGE("ese_boot_lock_xset: communication error"); |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| if (reply[0] != 0x0 || reply[1] != 0x0) { |
| ALOGE("ese_boot_lock_xset: received applet error code %x %x", reply[0], |
| reply[1]); |
| return ese_make_app_result(reply[0], reply[1]); |
| } |
| return ESE_APP_RESULT_OK; |
| } |
| |
| ESE_API EseAppResult ese_boot_lock_set(struct EseBootSession *session, |
| EseBootLockId lockId, |
| uint8_t lockValue) { |
| struct EseSgBuffer tx[3]; |
| struct EseSgBuffer rx[1]; |
| int rx_len; |
| if (!session || !session->ese || !session->active) { |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| if (lockId > kEseBootLockIdMax) { |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| |
| uint8_t chan = kSetLockState[0] | session->channel_id; |
| tx[0].base = &chan; |
| tx[0].len = 1; |
| tx[1].base = (uint8_t *)&kSetLockState[1]; |
| tx[1].len = 1; |
| |
| uint8_t lockIdLockValueNoMeta[] = {lockId, lockValue, 0x1, 0x0}; |
| tx[2].base = &lockIdLockValueNoMeta[0]; |
| tx[2].len = sizeof(lockIdLockValueNoMeta); |
| |
| uint8_t reply[4]; // App reply or APDU error. |
| rx[0].base = &reply[0]; |
| rx[0].len = sizeof(reply); |
| |
| rx_len = ese_transceive_sg(session->ese, tx, 3, rx, 1); |
| if (rx_len < 2 || ese_error(session->ese)) { |
| ALOGE("Failed to set lock state (%d).", lockId); |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| // Expect the full payload plus the applet status and the completion code. |
| if (rx_len < 4) { |
| ALOGE("ese_boot_lock_set: SE exception"); |
| EseAppResult ret = check_apdu_status(&reply[rx_len - 2]); |
| return ret; |
| } |
| if (reply[0] != 0x0 || reply[1] != 0x0) { |
| ALOGE("Received applet error code %x %x", reply[0], reply[1]); |
| return ese_make_app_result(reply[0], reply[1]); |
| } |
| return ESE_APP_RESULT_OK; |
| } |
| |
| ESE_API EseAppResult ese_boot_rollback_index_write( |
| struct EseBootSession *session, uint8_t slot, uint64_t value) { |
| struct EseSgBuffer tx[5]; |
| struct EseSgBuffer rx[1]; |
| uint8_t chan; |
| if (!session || !session->ese || !session->active) { |
| ALOGE("ese_boot_rollback_index_write: invalid session"); |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| if (slot >= kEseBootRollbackSlotCount) { |
| ALOGE("ese_boot_rollback_index_write: slot invalid"); |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| |
| // APDU CLA |
| chan = kStoreCmd[0] | session->channel_id; |
| tx[0].base = &chan; |
| tx[0].len = 1; |
| // APDU INS |
| tx[1].base = (uint8_t *)&kStoreCmd[1]; |
| tx[1].len = 1; |
| // APDU P1 - P2 |
| const uint8_t p1p2[] = {slot, 0x0}; |
| tx[2].c_base = &p1p2[0]; |
| tx[2].len = sizeof(p1p2); |
| // APDU Lc |
| uint8_t len = (uint8_t)sizeof(value); |
| tx[3].base = &len; |
| tx[3].len = sizeof(len); |
| // APDU data |
| tx[4].base = (uint8_t *)&value; |
| tx[4].len = sizeof(value); |
| |
| uint8_t rx_buf[4]; |
| rx[0].base = &rx_buf[0]; |
| rx[0].len = sizeof(rx_buf); |
| |
| int rx_len = ese_transceive_sg(session->ese, tx, 5, rx, 1); |
| if (rx_len < 0 || ese_error(session->ese)) { |
| ALOGE("ese_boot_rollback_index_write: comm error"); |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| if (rx_len < 2) { |
| ALOGE("ese_boot_rollback_index_write: too few bytes recieved."); |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| if (rx_len < 4) { |
| ALOGE("ese_boot_rollback_index_write: APDU Error"); |
| return check_apdu_status(&rx_buf[rx_len - 2]); |
| } |
| |
| if (rx_buf[0] != 0 || rx_buf[1] != 0) { |
| ALOGE("ese_boot_rollback_index_write: applet error code %x %x", rx_buf[0], |
| rx_buf[1]); |
| return ese_make_app_result(rx_buf[0], rx_buf[1]); |
| } |
| return ESE_APP_RESULT_OK; |
| } |
| |
| ESE_API EseAppResult ese_boot_rollback_index_read( |
| struct EseBootSession *session, uint8_t slot, uint64_t *value) { |
| struct EseSgBuffer tx[4]; |
| struct EseSgBuffer rx[1]; |
| uint8_t chan; |
| if (!session || !session->ese || !session->active) { |
| ALOGE("ese_boot_rollback_index_write: invalid session"); |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| if (!value) { |
| ALOGE("ese_boot_rollback_index_write: NULL value supplied"); |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| if (slot >= kEseBootRollbackSlotCount) { |
| ALOGE("ese_boot_rollback_index_write: slot invalid"); |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| |
| // APDU CLA |
| chan = kLoadCmd[0] | session->channel_id; |
| tx[0].base = &chan; |
| tx[0].len = 1; |
| // APDU INS |
| tx[1].base = (uint8_t *)&kLoadCmd[1]; |
| tx[1].len = 1; |
| // APDU P1 - P2 |
| const uint8_t p1p2[] = {slot, 0x0}; |
| tx[2].c_base = &p1p2[0]; |
| tx[2].len = sizeof(p1p2); |
| // APDU Lc |
| uint8_t len = 0; |
| tx[3].base = &len; |
| tx[3].len = sizeof(len); |
| |
| uint8_t rx_buf[4 + sizeof(*value)]; |
| rx[0].base = &rx_buf[0]; |
| rx[0].len = sizeof(rx_buf); |
| |
| int rx_len = ese_transceive_sg(session->ese, tx, 4, rx, 1); |
| if (rx_len < 0 || ese_error(session->ese)) { |
| ALOGE("ese_boot_rollback_index_read: comm error"); |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| if (rx_len < 2) { |
| ALOGE("ese_boot_rollback_index_read: too few bytes recieved."); |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| // TODO(wad) We should check the APDU status anyway. |
| if (rx_len < 4) { |
| ALOGE("ese_boot_rollback_index_read: APDU Error"); |
| return check_apdu_status(&rx_buf[rx_len - 2]); |
| } |
| if (rx_buf[0] != 0 || rx_buf[1] != 0) { |
| ALOGE("ese_boot_rollback_index_read: applet error code %x %x", rx_buf[0], |
| rx_buf[1]); |
| return ese_make_app_result(rx_buf[0], rx_buf[1]); |
| } |
| if (rx_len != (int)sizeof(rx_buf)) { |
| ALOGE("ese_boot_rollback_index_read: unexpected partial reply (%d)", |
| rx_len); |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| *value = *((uint64_t *)&rx_buf[2]); |
| return ESE_APP_RESULT_OK; |
| } |
| |
| ESE_API EseAppResult ese_boot_carrier_lock_test(struct EseBootSession *session, |
| const uint8_t *testdata, |
| uint16_t len) { |
| struct EseSgBuffer tx[5]; |
| struct EseSgBuffer rx[1]; |
| int rx_len; |
| if (!session || !session->ese || !session->active) { |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| if (len > 2048) { |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| |
| uint8_t chan = kCarrierLockTest[0] | session->channel_id; |
| tx[0].base = &chan; |
| tx[0].len = 1; |
| tx[1].base = (uint8_t *)&kCarrierLockTest[1]; |
| tx[1].len = 1; |
| |
| uint8_t p1p2[] = {0, 0}; |
| tx[2].base = &p1p2[0]; |
| tx[2].len = sizeof(p1p2); |
| |
| uint8_t apdu_len[] = {0x0, (len >> 8), (len & 0xff)}; |
| tx[3].base = &apdu_len[0]; |
| tx[3].len = sizeof(apdu_len); |
| |
| tx[4].c_base = testdata; |
| tx[4].len = len; |
| |
| uint8_t reply[4]; // App reply or APDU error. |
| rx[0].base = &reply[0]; |
| rx[0].len = sizeof(reply); |
| |
| rx_len = ese_transceive_sg(session->ese, tx, 5, rx, 1); |
| if (rx_len < 2 || ese_error(session->ese)) { |
| ALOGE("ese_boot_carrier_lock_test: failed to test carrier vector"); |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| if (rx_len < 4) { |
| ALOGE("ese_boot_carrier_lock_test: SE exception"); |
| EseAppResult ret = check_apdu_status(&reply[rx_len - 2]); |
| return ret; |
| } |
| if (reply[0] != 0x0 || reply[1] != 0x0) { |
| ALOGE("ese_boot_carrier_lock_test: applet error %x %x", reply[0], reply[1]); |
| return ese_make_app_result(reply[0], reply[1]); |
| } |
| return ESE_APP_RESULT_OK; |
| } |
| |
| ESE_API EseAppResult ese_boot_set_production(struct EseBootSession *session, |
| bool production_mode) { |
| struct EseSgBuffer tx[3]; |
| struct EseSgBuffer rx[1]; |
| int rx_len; |
| uint8_t prodVal = production_mode ? 0x1 : 0x00; |
| if (!session || !session->ese || !session->active) { |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| |
| uint8_t chan = kSetProduction[0] | session->channel_id; |
| tx[0].base = &chan; |
| tx[0].len = 1; |
| tx[1].base = (uint8_t *)&kSetProduction[1]; |
| tx[1].len = 1; |
| |
| uint8_t p1p2[] = {prodVal, 0x0}; |
| tx[2].base = &p1p2[0]; |
| tx[2].len = sizeof(p1p2); |
| |
| uint8_t reply[4]; // App reply or APDU error. |
| rx[0].base = &reply[0]; |
| rx[0].len = sizeof(reply); |
| |
| rx_len = ese_transceive_sg(session->ese, tx, 3, rx, 1); |
| if (rx_len < 2 || ese_error(session->ese)) { |
| ALOGE("ese_boot_set_production: comms failure."); |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| if (rx_len == 2) { |
| ALOGE("ese_boot_set_production: SE exception"); |
| EseAppResult ret = check_apdu_status(&reply[0]); |
| return ret; |
| } |
| // Expect the full payload plus the aplet status and the completion code. |
| if (rx_len != 4) { |
| ALOGE("ese_boot_set_production: not enough data (%d)", rx_len); |
| return ese_make_app_result(reply[0], reply[1]); |
| } |
| if (reply[0] != 0x0 || reply[1] != 0x0) { |
| ALOGE("ese_boot_set_production: applet error code %x %x", reply[0], |
| reply[1]); |
| return ese_make_app_result(reply[0], reply[1]); |
| } |
| return ESE_APP_RESULT_OK; |
| } |
| |
| ESE_API EseAppResult ese_boot_reset_locks(struct EseBootSession *session) { |
| struct EseSgBuffer tx[2]; |
| struct EseSgBuffer rx[1]; |
| int rx_len; |
| if (!session || !session->ese || !session->active) { |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| |
| uint8_t chan = kLockReset[0] | session->channel_id; |
| tx[0].base = &chan; |
| tx[0].len = 1; |
| tx[1].base = (uint8_t *)&kLockReset[1]; |
| tx[1].len = sizeof(kLockReset) - 1; |
| |
| uint8_t reply[4]; // App reply or APDU error. |
| rx[0].base = &reply[0]; |
| rx[0].len = sizeof(reply); |
| |
| rx_len = ese_transceive_sg(session->ese, tx, 2, rx, 1); |
| if (rx_len < 2 || ese_error(session->ese)) { |
| ALOGE("ese_boot_reset_locks: comms failure."); |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| if (rx_len == 2) { |
| ALOGE("ese_boot_reset_locks: SE exception"); |
| EseAppResult ret = check_apdu_status(&reply[0]); |
| return ret; |
| } |
| // Expect the full payload plus the aplet status and the completion code. |
| if (rx_len != 4) { |
| ALOGE("ese_boot_reset_locks: not enough data (%d)", rx_len); |
| return ese_make_app_result(reply[0], reply[1]); |
| } |
| if (reply[0] != 0x0 || reply[1] != 0x0) { |
| ALOGE("ese_boot_reset_locks: applet error code %x %x", reply[0], reply[1]); |
| return ese_make_app_result(reply[0], reply[1]); |
| } |
| return ESE_APP_RESULT_OK; |
| } |
| |
| ESE_API EseAppResult ese_boot_get_state(struct EseBootSession *session, |
| uint8_t *state, uint16_t maxSize) { |
| struct EseSgBuffer tx[4]; |
| struct EseSgBuffer rx[3]; |
| int rx_len; |
| if (!session || !session->ese || !session->active) { |
| return ESE_APP_RESULT_ERROR_ARGUMENTS; |
| } |
| uint8_t chan = kGetState[0] | session->channel_id; |
| tx[0].base = &chan; |
| tx[0].len = 1; |
| tx[1].base = (uint8_t *)&kGetState[1]; |
| tx[1].len = 1; |
| |
| uint8_t p1p2[] = {0x0, 0x0}; |
| tx[2].base = &p1p2[0]; |
| tx[2].len = sizeof(p1p2); |
| |
| // Accomodate the applet 2 byte status code. |
| uint8_t max_reply[] = {0x0, ((maxSize + 2) >> 8), ((maxSize + 2) & 0xff)}; |
| tx[3].base = &max_reply[0]; |
| tx[3].len = sizeof(max_reply); |
| |
| uint8_t reply[2]; // App reply or APDU error. |
| rx[0].base = &reply[0]; |
| rx[0].len = sizeof(reply); |
| // Applet data |
| rx[1].base = state; |
| rx[1].len = maxSize; |
| // Just in case the maxSize is used. That is unlikely. |
| // TODO(wad) clean this up. |
| uint8_t apdu_status[2]; |
| rx[2].base = &apdu_status[0]; |
| rx[2].len = sizeof(apdu_status); |
| |
| rx_len = ese_transceive_sg(session->ese, tx, 4, rx, 3); |
| if (rx_len < 2 || ese_error(session->ese)) { |
| ALOGE("ese_boot_get_state: comm failure"); |
| return ESE_APP_RESULT_ERROR_COMM_FAILED; |
| } |
| if (rx_len == 2) { |
| ALOGE("ese_boot_get_state: SE exception"); |
| EseAppResult ret = check_apdu_status(&reply[0]); |
| return ret; |
| } |
| // Expect the full payload plus the aplet status and the completion code. |
| if (rx_len < 3 + 4) { |
| ALOGE("ese_boot_get_state: did not receive enough data: %d", rx_len); |
| if (rx_len == 4) { |
| ALOGE("Received applet error code %x %x", reply[0], reply[1]); |
| } |
| return ese_make_app_result(reply[0], reply[1]); |
| } |
| // Well known version (for now). |
| if (state[0] == kBootStateVersion) { |
| uint16_t expected = (state[1] << 8) | (state[2]); |
| // Reduce for version (1), status (2). |
| if ((rx_len - 3) != expected) { |
| ALOGE("ese_boot_get_state: may be truncated: %d != %d", rx_len - 5, |
| expected); |
| } |
| return ESE_APP_RESULT_OK; |
| } |
| ALOGE("ese_boot_get_state: missing version tag"); |
| return ESE_APP_RESULT_ERROR_OS; |
| } |