blob: 6672246fdff1bec5883fe17534bd6eafcd8b4bee [file] [log] [blame]
Andrew Scull22d14132017-04-13 17:24:03 +01001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "include/ese/app/weaver.h"
18
19/* Non-static, but visibility=hidden so they can be used in test. */
20const uint8_t kManageChannelOpen[] = {0x00, 0x70, 0x00, 0x00, 0x01};
21const uint32_t kManageChannelOpenLength = (uint32_t)sizeof(kManageChannelOpen);
22const uint8_t kManageChannelClose[] = {0x00, 0x70, 0x80, 0x00, 0x00};
Will Drewry695e3182017-04-27 20:08:37 -050023const uint8_t kSelectApplet[] = {0x00, 0xA4, 0x04, 0x00, 0x0D, 0xA0,
24 0x00, 0x00, 0x04, 0x76, 0x57, 0x56,
25 0x52, 0x43, 0x4F, 0x4D, 0x4D, 0x30};
Andrew Scull22d14132017-04-13 17:24:03 +010026const uint32_t kSelectAppletLength = (uint32_t)sizeof(kSelectApplet);
27// Supported commands.
28const uint8_t kGetNumSlots[] = {0x80, 0x02, 0x00, 0x00, 0x04};
29const uint8_t kWrite[] = {0x80, 0x04, 0x00, 0x00,
30 4 + kEseWeaverKeySize +
31 kEseWeaverValueSize}; // slotid + key + value
32const uint8_t kRead[] = {0x80, 0x06, 0x00, 0x00,
33 4 + kEseWeaverKeySize}; // slotid + key
Andrew Scull5b8f7922017-05-22 15:33:02 +010034const uint8_t kEraseValue[] = {0x80, 0x08, 0x00, 0x00, 4}; // slotid
35const uint8_t kEraseAll[] = {0x80, 0x0a, 0x00, 0x00};
Andrew Scull22d14132017-04-13 17:24:03 +010036
37// Build 32-bit int from big endian bytes
38static uint32_t get_uint32(uint8_t buf[4]) {
39 uint32_t x = buf[3];
40 x |= buf[2] << 8;
41 x |= buf[1] << 16;
42 x |= buf[0] << 24;
43 return x;
44}
45
46static void put_uint32(uint8_t buf[4], uint32_t val) {
47 buf[0] = 0xff & (val >> 24);
48 buf[1] = 0xff & (val >> 16);
49 buf[2] = 0xff & (val >> 8);
50 buf[3] = 0xff & val;
51}
52
53EseAppResult check_apdu_status(uint8_t code[2]) {
54 if (code[0] == 0x90 && code[1] == 0x00) {
55 return ESE_APP_RESULT_OK;
56 }
57 if (code[0] == 0x66 && code[1] == 0xA5) {
58 return ESE_APP_RESULT_ERROR_COOLDOWN;
59 }
60 if (code[0] == 0x6A && code[1] == 0x83) {
61 return ESE_APP_RESULT_ERROR_UNCONFIGURED;
62 }
63 /* TODO(wad) Bubble up the error code if needed. */
64 ALOGE("unhandled response %.2x %.2x", code[0], code[1]);
65 return ese_make_os_result(code[0], code[1]);
66}
67
68ESE_API void ese_weaver_session_init(struct EseWeaverSession *session) {
69 session->ese = NULL;
70 session->active = false;
71 session->channel_id = 0;
72}
73
74ESE_API EseAppResult ese_weaver_session_open(struct EseInterface *ese,
75 struct EseWeaverSession *session) {
76 struct EseSgBuffer tx[2];
77 struct EseSgBuffer rx;
78 uint8_t rx_buf[32];
79 int rx_len;
80 if (!ese || !session) {
81 ALOGE("Invalid |ese| or |session|");
82 return ESE_APP_RESULT_ERROR_ARGUMENTS;
83 }
84 if (session->active == true) {
85 ALOGE("|session| is already active");
86 return ESE_APP_RESULT_ERROR_ARGUMENTS;
87 }
88 /* Instantiate a logical channel */
89 rx_len = ese_transceive(ese, kManageChannelOpen, sizeof(kManageChannelOpen),
90 rx_buf, sizeof(rx_buf));
91 if (ese_error(ese)) {
92 ALOGE("transceive error: code:%d message:'%s'", ese_error_code(ese),
93 ese_error_message(ese));
94 return ESE_APP_RESULT_ERROR_COMM_FAILED;
95 }
96 if (rx_len < 0) {
97 ALOGE("transceive error: rx_len: %d", rx_len);
98 return ESE_APP_RESULT_ERROR_COMM_FAILED;
99 }
100 if (rx_len < 2) {
101 ALOGE("transceive error: reply too short");
102 return ESE_APP_RESULT_ERROR_COMM_FAILED;
103 }
104 EseAppResult ret;
105 ret = check_apdu_status(&rx_buf[rx_len - 2]);
106 if (ret != ESE_APP_RESULT_OK) {
107 ALOGE("MANAGE CHANNEL OPEN failed with error code: %x %x",
108 rx_buf[rx_len - 2], rx_buf[rx_len - 1]);
109 return ret;
110 }
111 if (rx_len < 3) {
112 ALOGE("transceive error: successful reply unexpectedly short");
113 return ESE_APP_RESULT_ERROR_COMM_FAILED;
114 }
115 session->ese = ese;
116 session->channel_id = rx_buf[rx_len - 3];
117
118 /* Select Weaver Applet. */
119 uint8_t chan = kSelectApplet[0] | session->channel_id;
120 tx[0].base = &chan;
121 tx[0].len = 1;
122 tx[1].base = (uint8_t *)&kSelectApplet[1];
123 tx[1].len = sizeof(kSelectApplet) - 1;
124 rx.base = &rx_buf[0];
125 rx.len = sizeof(rx_buf);
126 rx_len = ese_transceive_sg(ese, tx, 2, &rx, 1);
127 if (rx_len < 0 || ese_error(ese)) {
128 ALOGE("transceive error: caller should check ese_error()");
129 return ESE_APP_RESULT_ERROR_COMM_FAILED;
130 }
131 if (rx_len < 2) {
132 ALOGE("transceive error: reply too short");
133 return ESE_APP_RESULT_ERROR_COMM_FAILED;
134 }
135 ret = check_apdu_status(&rx_buf[rx_len - 2]);
136 if (ret != ESE_APP_RESULT_OK) {
137 ALOGE("SELECT failed with error code: %x %x", rx_buf[rx_len - 2],
138 rx_buf[rx_len - 1]);
139 return ret;
140 }
141 session->active = true;
142 return ESE_APP_RESULT_OK;
143}
144
145ESE_API EseAppResult
146ese_weaver_session_close(struct EseWeaverSession *session) {
147 uint8_t rx_buf[32];
148 int rx_len;
149 if (!session || !session->ese) {
150 return ESE_APP_RESULT_ERROR_ARGUMENTS;
151 }
152 if (!session->active || session->channel_id == 0) {
153 return ESE_APP_RESULT_ERROR_ARGUMENTS;
154 }
155 /* Release the channel */
156 uint8_t close_channel[sizeof(kManageChannelClose)];
157 ese_memcpy(close_channel, kManageChannelClose, sizeof(kManageChannelClose));
158 close_channel[0] |= session->channel_id;
159 close_channel[3] |= session->channel_id;
160 rx_len = ese_transceive(session->ese, close_channel, sizeof(close_channel),
161 rx_buf, sizeof(rx_buf));
162 if (rx_len < 0 || ese_error(session->ese)) {
163 return ESE_APP_RESULT_ERROR_COMM_FAILED;
164 }
165 if (rx_len < 2) {
166 return ESE_APP_RESULT_ERROR_COMM_FAILED;
167 }
168 EseAppResult ret;
169 ret = check_apdu_status(&rx_buf[rx_len - 2]);
170 if (ret != ESE_APP_RESULT_OK) {
171 return ret;
172 }
173 session->channel_id = 0;
174 session->active = false;
175 return ESE_APP_RESULT_OK;
176}
177
178ESE_API EseAppResult ese_weaver_get_num_slots(struct EseWeaverSession *session,
179 uint32_t *numSlots) {
180 if (!session || !session->ese || !session->active) {
181 return ESE_APP_RESULT_ERROR_ARGUMENTS;
182 }
183 if (!session->active || session->channel_id == 0) {
184 return ESE_APP_RESULT_ERROR_ARGUMENTS;
185 }
186 if (!numSlots) {
187 return ESE_APP_RESULT_ERROR_ARGUMENTS;
188 }
189
190 // Prepare command
191 uint8_t get_num_slots[sizeof(kGetNumSlots)];
192 ese_memcpy(get_num_slots, kGetNumSlots, sizeof(kGetNumSlots));
193 get_num_slots[0] |= session->channel_id;
194
195 // Send command
196 uint8_t rx_buf[6];
197 const int rx_len =
198 ese_transceive(session->ese, get_num_slots, sizeof(get_num_slots), rx_buf,
199 sizeof(rx_buf));
200
201 // Check for errors
202 if (rx_len < 2 || ese_error(session->ese)) {
203 ALOGE("Failed to get num slots");
204 return ESE_APP_RESULT_ERROR_COMM_FAILED;
205 }
206 if (rx_len == 2) {
207 ALOGE("ese_weaver_get_num_slots: SE exception");
208 EseAppResult ret = check_apdu_status(rx_buf);
209 return ret;
210 }
211 if (rx_len != 6) {
212 ALOGE("Unexpected response from Weaver applet");
213 return ESE_APP_RESULT_ERROR_COMM_FAILED;
214 }
215
216 *numSlots = get_uint32(rx_buf);
217 return ESE_APP_RESULT_OK;
218}
219
220ESE_API EseAppResult ese_weaver_write(struct EseWeaverSession *session,
221 uint32_t slotId, const uint8_t *key,
222 const uint8_t *value) {
223 if (!session || !session->ese || !session->active) {
224 return ESE_APP_RESULT_ERROR_ARGUMENTS;
225 }
226 if (!session->active || session->channel_id == 0) {
227 return ESE_APP_RESULT_ERROR_ARGUMENTS;
228 }
229 if (!key || !value) {
230 return ESE_APP_RESULT_ERROR_ARGUMENTS;
231 }
232
233 // Prepare data to send
234 struct EseSgBuffer tx[5];
235 uint8_t chan = kWrite[0] | session->channel_id;
236 tx[0].base = &chan;
237 tx[0].len = 1;
238 tx[1].base = (uint8_t *)&kWrite[1];
239 tx[1].len = sizeof(kWrite) - 1;
240
241 // Slot ID in big endian
242 uint8_t slot_id[4];
243 put_uint32(slot_id, slotId);
244 tx[2].base = slot_id;
245 tx[2].len = sizeof(slot_id);
246
247 // Key and value
248 tx[3].c_base = key;
249 tx[3].len = kEseWeaverKeySize;
250 tx[4].c_base = value;
251 tx[4].len = kEseWeaverValueSize;
252
253 // Prepare buffer for result
254 struct EseSgBuffer rx;
255 uint8_t rx_buf[2];
256 rx.base = rx_buf;
257 rx.len = sizeof(rx_buf);
258
259 // Send the command
260 const int rx_len = ese_transceive_sg(session->ese, tx, 5, &rx, 1);
261
262 // Check for errors
263 if (rx_len < 2 || ese_error(session->ese)) {
264 ALOGE("Failed to write to slot");
265 return ESE_APP_RESULT_ERROR_COMM_FAILED;
266 }
267 if (rx_len > 2) {
268 ALOGE("Unexpected response from Weaver applet");
269 return ESE_APP_RESULT_ERROR_COMM_FAILED;
270 }
271 return check_apdu_status(rx_buf);
272}
273
274ESE_API EseAppResult ese_weaver_read(struct EseWeaverSession *session,
275 uint32_t slotId, const uint8_t *key,
276 uint8_t *value, uint32_t *timeout) {
277 if (!session || !session->ese || !session->active) {
278 return ESE_APP_RESULT_ERROR_ARGUMENTS;
279 }
280 if (!session->active || session->channel_id == 0) {
281 return ESE_APP_RESULT_ERROR_ARGUMENTS;
282 }
283 if (!key || !value || !timeout) {
284 return ESE_APP_RESULT_ERROR_ARGUMENTS;
285 }
286
287 // Prepare data to send
288 struct EseSgBuffer tx[5];
289 uint8_t chan = kRead[0] | session->channel_id;
290 tx[0].base = &chan;
291 tx[0].len = 1;
292 tx[1].base = (uint8_t *)&kRead[1];
293 tx[1].len = sizeof(kRead) - 1;
294
295 // Slot ID in big endian
296 uint8_t slot_id[4];
297 put_uint32(slot_id, slotId);
298 tx[2].base = slot_id;
299 tx[2].len = sizeof(slot_id);
300
301 // Key of 16 bytes
302 tx[3].c_base = key;
303 tx[3].len = kEseWeaverKeySize;
304
305 // Value response is 16 bytes
306 const uint8_t maxResponse = 1 + kEseWeaverValueSize;
307 tx[4].c_base = &maxResponse;
308 tx[4].len = 1;
309
310 // Prepare buffer for result
Andrew Scull66872902017-04-18 23:55:26 +0100311 struct EseSgBuffer rx[3];
Andrew Scull22d14132017-04-13 17:24:03 +0100312 uint8_t appletStatus;
313 rx[0].base = &appletStatus;
314 rx[0].len = 1;
315 rx[1].base = value;
316 rx[1].len = kEseWeaverValueSize;
317 uint8_t rx_buf[2];
318 rx[2].base = rx_buf;
319 rx[2].len = sizeof(rx_buf);
320
321 // Send the command
Andrew Scull66872902017-04-18 23:55:26 +0100322 const int rx_len = ese_transceive_sg(session->ese, tx, 5, rx, 3);
Andrew Scull22d14132017-04-13 17:24:03 +0100323
324 // Check for errors
325 if (rx_len < 2 || ese_error(session->ese)) {
326 ALOGE("Failed to write to slot");
327 return ESE_APP_RESULT_ERROR_COMM_FAILED;
328 }
329 if (rx_len == 2) {
330 rx_buf[0] = appletStatus;
331 rx_buf[1] = value[0];
332 ALOGE("ese_weaver_read: SE exception");
333 EseAppResult ret = check_apdu_status(rx_buf);
334 return ret;
335 }
336 if (rx_len < 7) {
337 ALOGE("Unexpected response from Weaver applet");
338 return ESE_APP_RESULT_ERROR_COMM_FAILED;
339 }
340 const uint8_t READ_SUCCESS = 0x00;
341 const uint8_t READ_WRONG_KEY = 0x7f;
342 const uint8_t READ_BACK_OFF = 0x76;
Andrew Scullb270de22017-04-21 18:52:52 +0100343 const uint32_t millisInSecond = 1000;
Andrew Scull22d14132017-04-13 17:24:03 +0100344 // wrong key
345 if (appletStatus == READ_WRONG_KEY) {
346 ALOGI("ese_weaver_read wrong key provided");
Andrew Scullb270de22017-04-21 18:52:52 +0100347 *timeout = get_uint32(value) * millisInSecond;
Andrew Scull22d14132017-04-13 17:24:03 +0100348 return ESE_WEAVER_READ_WRONG_KEY;
349 }
350 // backoff
351 if (appletStatus == READ_BACK_OFF) {
352 ALOGI("ese_weaver_read wrong key provided");
Andrew Scullb270de22017-04-21 18:52:52 +0100353 *timeout = get_uint32(value) * millisInSecond;
Andrew Scull22d14132017-04-13 17:24:03 +0100354 return ESE_WEAVER_READ_TIMEOUT;
355 }
356 if (rx_len != 19) {
357 ALOGE("Unexpected response from Weaver applet");
358 return ESE_APP_RESULT_ERROR_COMM_FAILED;
359 }
360 return ESE_APP_RESULT_OK;
361}
362
Andrew Scull5b8f7922017-05-22 15:33:02 +0100363ESE_API EseAppResult ese_weaver_erase_value(struct EseWeaverSession *session,
364 uint32_t slotId) {
365 if (!session || !session->ese || !session->active) {
366 return ESE_APP_RESULT_ERROR_ARGUMENTS;
367 }
368 if (!session->active || session->channel_id == 0) {
369 return ESE_APP_RESULT_ERROR_ARGUMENTS;
370 }
371
372 // Prepare data to send
373 struct EseSgBuffer tx[3];
374 uint8_t chan = kEraseValue[0] | session->channel_id;
375 tx[0].base = &chan;
376 tx[0].len = 1;
377 tx[1].base = (uint8_t *)&kEraseValue[1];
378 tx[1].len = sizeof(kEraseValue) - 1;
379
380 // Slot ID in big endian
381 uint8_t slot_id[4];
382 put_uint32(slot_id, slotId);
383 tx[2].base = slot_id;
384 tx[2].len = sizeof(slot_id);
385
386 // Prepare buffer for result
387 struct EseSgBuffer rx;
388 uint8_t rx_buf[2];
389 rx.base = rx_buf;
390 rx.len = sizeof(rx_buf);
391
392 // Send the command
393 const int rx_len = ese_transceive_sg(session->ese, tx, 3, &rx, 1);
394
395 // Check for errors
396 if (rx_len < 2 || ese_error(session->ese)) {
397 ALOGE("Failed to write to slot");
398 return ESE_APP_RESULT_ERROR_COMM_FAILED;
399 }
400 if (rx_len > 2) {
401 ALOGE("Unexpected response from Weaver applet");
402 return ESE_APP_RESULT_ERROR_COMM_FAILED;
403 }
404 return check_apdu_status(rx_buf);
405}
406
Andrew Scull22d14132017-04-13 17:24:03 +0100407// TODO: erase all, not currently used