-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
capdu.go
410 lines (380 loc) · 12 KB
/
capdu.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
/***
Copyright (c) 2020, Hector Sanjuan
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
package apdu
import (
"bytes"
"errors"
"fmt"
"github.com/hsanjuan/go-nfctype4/helpers"
)
// CAPDU.INS relevant to the Type 4 Tag Specification
const (
INSSelect = byte(0xA4)
INSRead = byte(0xB0)
INSUpdate = byte(0xD6)
)
// CAPDU represents a Command APDU
// (https://en.wikipedia.org/wiki/Smart_card_application_protocol_data_unit)
// which is used to send instructions and data to the NFC devices.
type CAPDU struct {
//
CLA byte //Class byte
INS byte //Instruction byte
P1 byte //Param byte 1
P2 byte //Param byte 2
Lc []byte //Data field length // 0, 1 or 3 bytes
Data []byte //Data field
Le []byte //Expected response length // 0, 1, 2, or 3 bytes
}
// Reset clears the fields of the CAPDU to their default values.
func (apdu *CAPDU) Reset() {
apdu.CLA = 0
apdu.INS = 0
apdu.P1 = 0
apdu.P2 = 0
apdu.Lc = []byte{}
apdu.Data = []byte{}
apdu.Le = []byte{}
}
// String provides a readable representation of the CAPDU
func (apdu *CAPDU) String() string {
str := ""
str += fmt.Sprintf("CLA: %02x | INS: %02x | P1: %02x | P2: %02x",
apdu.CLA, apdu.INS, apdu.P1, apdu.P2)
str += " | Lc: "
for _, b := range apdu.Lc {
str += fmt.Sprintf("%02x", b)
}
str += " | Data: "
for _, b := range apdu.Data {
str += fmt.Sprintf("%02x", b)
}
str += " | Le: "
for _, b := range apdu.Le {
str += fmt.Sprintf("%02x", b)
}
return str
}
// GetLc computes the actual Lc value from the Lc bytes. Lc
// indicates the length of the data sent with a Command
// APDU and goes from 0 to 2^16-1.
// Note this method will return
// 0 if it cannot make sense of the Lc bytes.
func (apdu *CAPDU) GetLc() uint16 {
switch len(apdu.Lc) {
case 0:
return uint16(0) // This goes against spec
case 1:
return uint16(apdu.Lc[0])
case 3:
return helpers.BytesToUint16([2]byte{apdu.Lc[1], apdu.Lc[2]})
default:
return 0
}
}
// SetLc allows to easily set the value of the Lc bytes making sure
// they comply to the specification.
func (apdu *CAPDU) SetLc(n uint16) {
if n == 0 {
apdu.Lc = []byte{}
} else if 1 <= n && n <= 255 { // 1-255
apdu.Lc = []byte{byte(n)}
} else {
nBytes := helpers.Uint16ToBytes(n)
apdu.Lc = []byte{0x00, nBytes[0], nBytes[1]}
}
}
// BUG(hector): APDU's Le field could theoretically be 65536 (2^16), but
// this overflows uint16 so it's unsupported by SetLe and GetLe.
// It only happens in the case when Le has two bytes and both are 0 and in this
// case GetLe returns 2^16 -1.
// GetLe computes the actual Le value from the Le bytes. Le
// indicates the maximum length of the data to be received Command
// APDU and goes from 0 to 2^16. Note this method will return
// 0 if it cannot make sense of the Le bytes.
func (apdu *CAPDU) GetLe() uint16 {
switch len(apdu.Le) {
case 0:
return uint16(0)
case 1:
n := apdu.Le[0]
if n == 0 {
return uint16(256)
}
return uint16(n)
case 2:
n0 := apdu.Le[0]
n1 := apdu.Le[1]
if n0 == 0 && n1 == 0 {
//return uint16(65536) // Overflow! FIXME!
return uint16(65535)
}
return helpers.BytesToUint16([2]byte{n0, n1})
case 3:
return helpers.BytesToUint16([2]byte{apdu.Le[1], apdu.Le[2]})
default:
return 0
}
}
// SetLe allows to easily set the value of the Le bytes making sure
// they comply to the specification.
func (apdu *CAPDU) SetLe(n uint16) {
if n == 0 {
apdu.Le = []byte{}
} else if 1 <= n && n <= 255 {
apdu.Le = []byte{byte(n)}
} else if n == 256 {
apdu.Le = []byte{byte(0)}
} else {
nBytes := helpers.Uint16ToBytes(n)
if len(apdu.Lc) > 0 { // Make it 2 bytes
apdu.Le = []byte{nBytes[0], nBytes[1]}
} else { // 3 bytes then
apdu.Le = []byte{0, nBytes[0], nBytes[1]}
}
}
}
// Check ensures that a CAPDU struct fields are in-line with the
// specification.
// This mostly means checking that Lc, Data, Le fields look ok.
//
// It returns an error when something is clearly wrong with the CAPDU
func (apdu *CAPDU) check() error {
lc := apdu.Lc
le := apdu.Le
// Test Lc
switch {
case len(lc) == 1 && lc[0] == 0:
return errors.New(
"CAPDU.Check: APDU Lc with 1 byte cannot be 0")
case len(lc) == 2:
return errors.New("CAPDU.Check: APDU Lc cannot have 2 bytes")
case len(lc) == 3:
if lc[0] != 0 {
return errors.New("CAPDU.Check: " +
"APDU 3-byte-Lc's first byte must be 0")
} else if lc[1] == 0 && lc[2] == 0 {
return errors.New("CAPDU.Check: " +
"APDU 3-byte-Lc cannot be all 0s")
}
case len(lc) > 3:
return errors.New(
"CAPDU.Check: APDU Lc cannot have more than 3 bytes")
}
// Test Le
switch {
case len(le) == 2 && len(lc) == 0:
return errors.New(
"CAPDU.Check: APDU 2-byte-Le needs Lc present")
case len(le) == 3:
if len(lc) != 0 {
return errors.New("CAPDU.Check: " +
"APDU 3-byte-Le is only " +
"compatible with empty Lc")
} else if le[0] != 0 {
return errors.New("CAPDU.Check: " +
"APDU 3-byte-Le's first byte must be 0")
}
case len(le) > 3:
return errors.New("CAPDU.Check: " +
"APDU Le cannot have more 3 bytes")
}
if int(apdu.GetLc()) != len(apdu.Data) {
return errors.New("CAPDU.Check: " +
"APDU Lc value is differs from the actual data length")
}
return nil
}
// Unmarshal parses a byte slice and sets the CAPDU fields accordingly.
// It resets the CAPDU structure before parsing.
// It returns the number of bytes parsed or an error if something goes wrong.
func (apdu *CAPDU) Unmarshal(buf []byte) (rLen int, err error) {
defer helpers.HandleErrorPanic(&err, "CAPDU.Unmarshal")
apdu.Reset()
bytesBuf := bytes.NewBuffer(buf)
apdu.CLA = helpers.GetByte(bytesBuf)
apdu.INS = helpers.GetByte(bytesBuf)
apdu.P1 = helpers.GetByte(bytesBuf)
apdu.P2 = helpers.GetByte(bytesBuf)
// See table 5 here about what's going on
// http://www.cardwerk.com/smartcards/smartcard_standard_ISO7816-4_5_basic_organizations.aspx
// I have copied the comments for each case, but in some there are
// clear typos. Also, contradicts the Wikipedia as how 3-byte values are
// coded ("0x00->1 and 0xFF->65536" instead of
// "0x00->65536 and 0x01->1...""
// We chose to follow Wikipedia on this.
bodyBytes := bytesBuf.Bytes()
bodyLen := len(bodyBytes)
b1 := byte(0)
b2 := byte(0)
b3 := byte(0)
if bodyLen > 0 {
b1 = bodyBytes[0]
}
if bodyLen > 1 {
b2 = bodyBytes[1]
}
if bodyLen > 2 {
b3 = bodyBytes[2]
}
switch {
case bodyLen == 0:
// Case 1 - L=0 : the body is empty.
// No byte is used for Lc valued to 0
// No data byte is present.
// No byte is used for Le valued to 0.
// Nothing to do here
case bodyLen == 1:
// Case 2S - L=1
// No byte is used for Lc valued to 0
// No data byte is present.
// B1 codes Le valued from 1 to 256
apdu.Le = helpers.GetBytes(bytesBuf, 1)
case bodyLen == (1+int(b1)) && b1 != 0:
// Case 3S - L=1 + (B1) and (B1) != 0
// B1 codes Lc (=0) valued from 1 to 255
// B2 to Bl are the Lc bytes of the data field
// No byte is used for Le valued to 0.
apdu.Lc = helpers.GetBytes(bytesBuf, 1)
apdu.Data = helpers.GetBytes(bytesBuf, int(b1))
case bodyLen == (2+int(b1)) && b1 != 0:
// Case 4S - L=2 + (B1) and (B1) != 0
// B1 codes Lc (!=0) valued from 1 to 255
// B2 to Bl-1 are the Lc bytes of the data field
// Bl codes Le from 1 to 256
apdu.Lc = helpers.GetBytes(bytesBuf, 1)
apdu.Data = helpers.GetBytes(bytesBuf, int(b1))
apdu.Le = helpers.GetBytes(bytesBuf, 1)
case bodyLen == 3 && b1 == 0:
// Case 2E - L=3 and (B1)=0
// No byte is used for Lc valued to 0
// No data bytes is present
// The Le field consists of the 3 bytes where B2 and
// B3 code Le valued from 1 to 65536
// apdu.Le = []byte{b1, b2, b3}
apdu.Le = helpers.GetBytes(bytesBuf, 3)
case bodyLen == (3+int(helpers.BytesToUint16([2]byte{b2, b3}))) && b1 == 0 && (b2|b3) != 0:
// Case 3E - L=3 + (B2||B3). (B1)=0 and (B2||B3)=0
// It should say: L=3 + (B2||B3). (B1)=0 and (B2||B3)!=0
// The Lc field consists of the first 3 bytes where B2 and B3 code Lc (!=0) valued from 1 to 65536
// B4 and B2 are the Lc bytes of the data field
// No byte is used for Le valued to 0
apdu.Lc = helpers.GetBytes(bytesBuf, 3)
apdu.Data = helpers.GetBytes(bytesBuf, int(apdu.GetLc()))
case bodyLen == (5+int(helpers.BytesToUint16([2]byte{b2, b3}))) && b1 == 0 && (b2|b3) != 0:
//Case 4E - L= 5 + (B2||B3),(B1)=0 and (B2||B3)=0
// The Lc field consists of the first 3 bytes where B2 and B3 code Lc (!=0) valued from 1 to 65535
//B4 to Bl-2 are the Lc bytes of the data field
//The Le field consists of the last 2 bytes Bl-1 and Bl which code Le valued from 1 to 65536
apdu.Lc = helpers.GetBytes(bytesBuf, 3)
apdu.Data = helpers.GetBytes(bytesBuf, int(apdu.GetLc()))
apdu.Le = helpers.GetBytes(bytesBuf, 2)
}
rLen = len(buf) - bytesBuf.Len()
if err := apdu.check(); err != nil {
return rLen, err
}
return rLen, nil
}
// Marshal provides the byte-slice value for a CAPDU, so it can be sent
// to the NFC device.
// It returns an error when something goes wrong (uses Test()).
func (apdu *CAPDU) Marshal() ([]byte, error) {
if err := apdu.check(); err != nil {
return nil, err
}
var buffer bytes.Buffer
buffer.WriteByte(apdu.CLA)
buffer.WriteByte(apdu.INS)
buffer.WriteByte(apdu.P1)
buffer.WriteByte(apdu.P2)
buffer.Write(apdu.Lc)
buffer.Write(apdu.Data)
buffer.Write(apdu.Le)
return buffer.Bytes(), nil
}
// NewNDEFTagApplicationSelectAPDU returns a new CAPDU
// which performs a Select operation by name with the NDEF
// Application Name.
func NewNDEFTagApplicationSelectAPDU() *CAPDU {
cApdu := &CAPDU{
CLA: byte(0x00),
INS: byte(0xA4),
P1: byte(0x04), // Select by name
P2: byte(0x00), // First or only occurrence
Data: []byte{
0xD2,
0x76,
0x00,
0x00,
0x85,
0x01,
0x01}, // NDEF app name FIXME
}
cApdu.SetLc(7)
// This would set a single-byte Le to 0, meaning response data
// field might be present(and be up to 256 bytes according to Wikipedia)
cApdu.SetLe(256)
return cApdu
}
// NewReadBinaryAPDU returns a new CAPDU to perform a binary
// read with the indicated offset and length.
func NewReadBinaryAPDU(offset uint16, length uint16) *CAPDU {
offsetBytes := helpers.Uint16ToBytes(offset)
cApdu := &CAPDU{
CLA: byte(0x00),
INS: byte(0xB0),
P1: offsetBytes[0],
P2: offsetBytes[1],
}
cApdu.SetLe(length)
return cApdu
}
// NewUpdateBinaryAPDU returns a new CAPDU to perform a binary
// update operation with the provided data and offset.
func NewUpdateBinaryAPDU(data []byte, offset uint16) *CAPDU {
offsetBytes := helpers.Uint16ToBytes(offset)
cApdu := &CAPDU{
CLA: byte(0x00),
INS: byte(0xD6),
P1: offsetBytes[0],
P2: offsetBytes[1],
Data: data,
}
cApdu.SetLc(uint16(len(data)))
return cApdu
}
// NewSelectAPDU returns a new CAPDU to perform a select
// operation by ID with the provided fileID
func NewSelectAPDU(fileID uint16) *CAPDU {
dataBuf := helpers.Uint16ToBytes(fileID)
cApdu := &CAPDU{
CLA: byte(0x00),
INS: byte(0xA4),
P1: byte(0x00), // Select by Id
P2: byte(0x0C), // First or only occurrence
Data: dataBuf[:],
}
cApdu.SetLc(2) //File ID length should be 2
return cApdu
}
// BUG(hector): Capability Containers with more than 15 bytes (because
// they include optional TLV fields), will fail, as we only read
// 15 bytes and the CCLEN will not match the parsed data size.
// NewCapabilityContainerReadAPDU returns a new CAPDU to
// perform a binary read of 15 bytes with 0 offset (the
// regular size of a standard capability container).
func NewCapabilityContainerReadAPDU() *CAPDU {
return NewReadBinaryAPDU(0, 15)
}