1/*
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};
23const uint8_t kSelectApplet[] = {0x00, 0xA4, 0x04, 0x00, 0x0D, 0xA0,
24                                 0x00, 0x00, 0x04, 0x76, 0x57, 0x56,
25                                 0x52, 0x43, 0x4F, 0x4D, 0x4D, 0x30};
26const 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
34const uint8_t kEraseValue[] = {0x80, 0x08, 0x00, 0x00, 4}; // slotid
35const uint8_t kEraseAll[] = {0x80, 0x0a, 0x00, 0x00};
36
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
311  struct EseSgBuffer rx[3];
312  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
322  const int rx_len = ese_transceive_sg(session->ese, tx, 5, rx, 3);
323
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;
343  const uint32_t millisInSecond = 1000;
344  // wrong key
345  if (appletStatus == READ_WRONG_KEY) {
346    ALOGI("ese_weaver_read wrong key provided");
347    *timeout = get_uint32(value) * millisInSecond;
348    return ESE_WEAVER_READ_WRONG_KEY;
349  }
350  // backoff
351  if (appletStatus == READ_BACK_OFF) {
352    ALOGI("ese_weaver_read wrong key provided");
353    *timeout = get_uint32(value) * millisInSecond;
354    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
363ESE_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
407// TODO: erase all, not currently used
408