blob: b9dc3b78f4c67a5222841a912df48c79de15a0a2 [file] [log] [blame]
Kévin Redon70a84862018-10-10 00:30:23 +02001/* apdu_dispatch - State machine to determine Rx/Tx phases of APDU
2 *
3 * (C) 2016 by Harald Welte <hwelte@hmw-consulting.de>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19#include <stdint.h>
20#include <stdlib.h>
21#include <string.h>
22#include <stdbool.h>
23#include <stdio.h>
24#include <errno.h>
25
26#include <osmocom/core/utils.h>
27#include <osmocom/sim/sim.h>
28#include <osmocom/sim/class_tables.h>
29
30#include "apdu_dispatch.h"
31
32/*! \brief Has the command-data phase been completed yet? */
33static inline bool is_dc_complete(struct apdu_context *ac)
34{
35 return (ac->lc.tot == ac->lc.cur);
36}
37
38/*! \brief Has the expected-data phase been completed yet? */
39static inline bool is_de_complete(struct apdu_context *ac)
40{
41 return (ac->le.tot == ac->le.cur);
42}
43
44static const char *dump_apdu_hdr(const struct osim_apdu_cmd_hdr *h)
45{
46 static char buf[256];
47 sprintf(buf, "CLA=%02x INS=%02x P1=%02x P2=%02x P3=%02x",
48 h->cla, h->ins, h->p1, h->p2, h->p3);
49
50 return buf;
51}
52
53static void dump_apdu_ctx(const struct apdu_context *ac)
54{
55 printf("%s; case=%d, lc=%d(%d), le=%d(%d)\n",
56 dump_apdu_hdr(&ac->hdr), ac->apdu_case,
57 ac->lc.tot, ac->lc.cur,
58 ac->le.tot, ac->le.cur);
59}
60
61/*! \brief input function for APDU segmentation
Martin Hauke4e87beb2019-07-17 22:10:47 +020062 * \param ac APDU context across successive calls
Kévin Redon70a84862018-10-10 00:30:23 +020063 * \param[in] apdu_buf APDU inpud data buffer
64 * \param[in] apdu_len Length of apdu_buf
65 * \param[in] new_apdu Is this the beginning of a new APDU?
66 *
67 * The function returns APDU_ACT_TX_CAPDU_TO_CARD once there is
68 * sufficient data of the APDU received to transmit the command-APDU to
69 * the actual card.
70 *
71 * The function retunrs APDU_ACT_RX_MORE_CAPDU_FROM_READER when there
72 * is more data to be received from the card reader (GSM Phone).
73 */
74int apdu_segment_in(struct apdu_context *ac, const uint8_t *apdu_buf,
75 unsigned int apdu_len, bool new_apdu)
76{
77 int rc = 0;
78
79 if (new_apdu) {
80 /* initialize the apdu context structure */
81 memset(ac, 0, sizeof(*ac));
82 /* copy APDU header over */
83 memcpy(&ac->hdr, apdu_buf, sizeof(ac->hdr));
84 ac->apdu_case = osim_determine_apdu_case(&osim_uicc_sim_cic_profile, apdu_buf);
85 switch (ac->apdu_case) {
86 case 1: /* P3 == 0, No Lc/Le */
87 ac->le.tot = ac->lc.tot = 0;
88 break;
89 case 2: /* P3 == Le */
90 ac->le.tot = ac->hdr.p3;
91 break;
92 case 3: /* P3 = Lc */
93 ac->lc.tot = ac->hdr.p3;
94 /* copy Dc */
95 ac->lc.cur = apdu_len - sizeof(ac->hdr);
96 memcpy(ac->dc, apdu_buf + sizeof(ac->hdr),
97 ac->lc.cur);
98 break;
99 case 4: /* P3 = Lc; SW with Le */
100 ac->lc.tot = ac->hdr.p3;
101 /* copy Dc */
102 ac->lc.cur = apdu_len - sizeof(ac->hdr);
103 memcpy(ac->dc, apdu_buf + sizeof(ac->hdr),
104 ac->lc.cur);
105 break;
106 case 0:
107 default:
108 fprintf(stderr, "Unknown APDU case %d\n", ac->apdu_case);
109 return -1;
110 }
111 } else {
112 /* copy more data, if available */
113 int cpy_len;
114 switch (ac->apdu_case) {
115 case 1:
116 case 2:
117 break;
118 case 3:
119 case 4:
120 cpy_len = ac->lc.tot - ac->lc.cur;
121 if (cpy_len > apdu_len)
122 cpy_len = apdu_len;
123 memcpy(ac->dc+ac->lc.cur, apdu_buf, cpy_len);
124 ac->lc.cur += cpy_len;
125 break;
126 default:
127 fprintf(stderr, "Unknown APDU case %d\n", ac->apdu_case);
128 break;
129 }
130 }
131
132 /* take some decisions... */
133 switch (ac->apdu_case) {
134 case 1: /* P3 == 0, No Lc/Le */
135 /* send C-APDU to card */
136 /* receive SW from card, forward to reader */
137 rc |= APDU_ACT_TX_CAPDU_TO_CARD;
138 break;
139 case 2: /* P3 == Le */
140 /* send C-APDU to card */
141 /* receive Le bytes + SW from card, forward to reader */
142 rc |= APDU_ACT_TX_CAPDU_TO_CARD;
143 break;
144 case 3: /* P3 = Lc */
145 if (!is_dc_complete(ac)) {
146 /* send PB + read further Lc bytes from reader */
147 rc |= APDU_ACT_RX_MORE_CAPDU_FROM_READER;
148 } else {
149 /* send C-APDU to card */
150 /* receive SW from card, forward to reader */
151 rc |= APDU_ACT_TX_CAPDU_TO_CARD;
152 }
153 break;
154 case 4: /* P3 = Lc; SW with Le */
155 if (!is_dc_complete(ac)) {
156 /* send PB + read further Lc bytes from reader */
157 rc |= APDU_ACT_RX_MORE_CAPDU_FROM_READER;
158 } else {
159 /* send C-APDU to card */
160 /* receive SW from card, forward to reader */
161 rc |= APDU_ACT_TX_CAPDU_TO_CARD;
162 }
163 break;
164 case 0:
165 default:
166 fprintf(stderr, "Unknown APDU case %d\n", ac->apdu_case);
167 break;
168 }
169
170 dump_apdu_ctx(ac);
171
172 return rc;
173}