| 1 | local cmds = require('commands') |
| 2 | local getopt = require('getopt') |
| 3 | local lib14b = require('read14b') |
| 4 | local utils = require('utils') |
| 5 | local iso7816 = require('7816_error') |
| 6 | |
| 7 | example = "script runs 14b raw commands to query a CAPLYPSO tag" |
| 8 | author = "Iceman, 2016" |
| 9 | desc = |
| 10 | [[ |
| 11 | This is a script to communicate with a CALYSPO / 14443b tag using the '14b raw' commands |
| 12 | |
| 13 | Arguments: |
| 14 | -b 123 |
| 15 | Examples : |
| 16 | script run f -b 11223344 |
| 17 | script run f |
| 18 | |
| 19 | Examples : |
| 20 | |
| 21 | # 1. Connect and don't disconnect |
| 22 | script run f |
| 23 | # 2. Send mf auth, read response |
| 24 | script run f |
| 25 | # 3. disconnect |
| 26 | script run f |
| 27 | |
| 28 | ]] |
| 29 | |
| 30 | --[[ |
| 31 | This script communicates with /armsrc/iso14443b.c, |
| 32 | Check there for details about data format and how commands are interpreted on the |
| 33 | device-side. |
| 34 | ]] |
| 35 | |
| 36 | --- |
| 37 | -- |
| 38 | local function calypso_switch_on_field() |
| 39 | local flags = lib14b.ISO14B_COMMAND.ISO14B_CONNECT |
| 40 | local c = Command:new{cmd = cmds.CMD_ISO_14443B_COMMAND, arg1 = flags} |
| 41 | return lib14b.sendToDevice(c, true) |
| 42 | end |
| 43 | --- |
| 44 | -- Disconnect (poweroff) the antenna forcing a disconnect of a 14b tag. |
| 45 | local function calypso_switch_off_field() |
| 46 | local flags = lib14b.ISO14B_COMMAND.ISO14B_DISCONNECT |
| 47 | local c = Command:new{cmd = cmds.CMD_ISO_14443B_COMMAND, arg1 = flags} |
| 48 | return lib14b.sendToDevice(c, true) |
| 49 | end |
| 50 | |
| 51 | local function calypso_parse(result) |
| 52 | local r = Command.parse(result) |
| 53 | local len = r.arg2 * 2 |
| 54 | r.data = string.sub(r.data, 0, len); |
| 55 | print('GOT:', r.data) |
| 56 | if r.arg1 == 0 then |
| 57 | return r, nil |
| 58 | end |
| 59 | return nil,nil |
| 60 | end |
| 61 | --- |
| 62 | -- A debug printout-function |
| 63 | local function dbg(args) |
| 64 | if DEBUG then |
| 65 | print("###", args) |
| 66 | end |
| 67 | end |
| 68 | --- |
| 69 | -- This is only meant to be used when errors occur |
| 70 | local function oops(err) |
| 71 | print("ERROR: ",err) |
| 72 | calypso_switch_off_field() |
| 73 | return nil,err |
| 74 | end |
| 75 | --- |
| 76 | -- Usage help |
| 77 | local function help() |
| 78 | print(desc) |
| 79 | print("Example usage") |
| 80 | print(example) |
| 81 | end |
| 82 | -- |
| 83 | -- helper function, give current count of items in lua-table. |
| 84 | local function tablelen(T) |
| 85 | local count = 0 |
| 86 | for _ in pairs(T) do count = count + 1 end |
| 87 | return count |
| 88 | end |
| 89 | --- |
| 90 | -- helper function, gives a sorted table from table t, |
| 91 | -- order can be a seperate sorting-order function. |
| 92 | local function spairs(t, order) |
| 93 | -- collect the keys |
| 94 | local keys = {} |
| 95 | for k in pairs(t) do keys[#keys+1] = k end |
| 96 | |
| 97 | -- if order function given, sort by it by passing the table and keys a, b, |
| 98 | -- otherwise just sort the keys |
| 99 | if order then |
| 100 | table.sort(keys, function(a,b) return order(t, a, b) end) |
| 101 | else |
| 102 | table.sort(keys) |
| 103 | end |
| 104 | |
| 105 | -- return the iterator function |
| 106 | local i = 0 |
| 107 | return function() |
| 108 | i = i + 1 |
| 109 | if keys[i] then |
| 110 | return keys[i], t[keys[i]] |
| 111 | end |
| 112 | end |
| 113 | end |
| 114 | --- |
| 115 | -- Sends a usbpackage , "hf 14b raw" |
| 116 | -- if it reads the response, it converts it to a lua object "Command" first and the Data is cut to correct length. |
| 117 | local function calypso_send_cmd_raw(data, ignoreresponse ) |
| 118 | |
| 119 | local command, flags, result, err |
| 120 | flags = lib14b.ISO14B_COMMAND.ISO14B_RAW + |
| 121 | lib14b.ISO14B_COMMAND.ISO14B_APPEND_CRC |
| 122 | |
| 123 | data = data or "00" |
| 124 | |
| 125 | command = Command:new{cmd = cmds.CMD_ISO_14443B_COMMAND, |
| 126 | arg1 = flags, |
| 127 | arg2 = #data/2, -- LEN of data, half the length of the ASCII-string hex string |
| 128 | arg3 = 0, |
| 129 | data = data} -- data bytes (commands etc) |
| 130 | result, err = lib14b.sendToDevice(command, false) |
| 131 | |
| 132 | if ignoreresponse then return response, err end |
| 133 | |
| 134 | if result then |
| 135 | local r = calypso_parse(result) |
| 136 | return r, nil |
| 137 | end |
| 138 | return respone, err |
| 139 | end |
| 140 | --- |
| 141 | -- calypso_card_num : Reads card number from ATR and |
| 142 | -- writes it in the tree in decimal format. |
| 143 | local function calypso_card_num(card) |
| 144 | if not card then return end |
| 145 | local card_num = tonumber( card.uid:sub(1,8),16 ) |
| 146 | print('Card UID', card.uid) |
| 147 | print('Card Number', card_num) |
| 148 | end |
| 149 | --- |
| 150 | -- analyse CALYPSO apdu status bytes. |
| 151 | local function calypso_apdu_status(apdu) |
| 152 | -- last two is CRC |
| 153 | -- next two is APDU status bytes. |
| 154 | local status = false |
| 155 | local mess = 'FAIL' |
| 156 | local sw = apdu:sub( #apdu-7, #apdu-4) |
| 157 | desc, err = iso7816.tostring(sw) |
| 158 | print ('SW', sw, desc, err ) |
| 159 | |
| 160 | status = ( sw == '9000' ) |
| 161 | |
| 162 | return status |
| 163 | end |
| 164 | |
| 165 | local _calypso_cmds = { |
| 166 | |
| 167 | -- Break down of command bytes: |
| 168 | -- A4 = select |
| 169 | -- Master File 3F00 |
| 170 | -- 0x3F = master file |
| 171 | -- 0x00 = master file id, is constant to 0x00. |
| 172 | |
| 173 | -- DF Dedicated File 38nn |
| 174 | -- can be seen as directories |
| 175 | -- 0x38 |
| 176 | -- 0xNN id |
| 177 | -- ["01.Select ICC file"] = '0294 a4 080004 3f00 0002', |
| 178 | |
| 179 | -- EF Elementary File |
| 180 | -- EF1 Pin file |
| 181 | -- EF2 Key file |
| 182 | -- Grey Lock file |
| 183 | -- Electronic deposit file |
| 184 | -- Electronic Purse file |
| 185 | -- Electronic Transaction log file |
| 186 | |
| 187 | |
| 188 | --["01.Select ICC file"] = '0294 a4 00 0002 3f00', |
| 189 | ["01.Select ICC file"] = '0294 a4 080004 3f00 0002', |
| 190 | ["02.ICC"] = '0394 b2 01 041d', |
| 191 | ["03.Select EnvHol file"] = '0294 a4 080004 2000 2001', |
| 192 | ["04.EnvHol1"] = '0394 b2 01 041d', |
| 193 | ["05.Select EvLog file"] = '0294 a4 080004 2000 2010', |
| 194 | ["06.EvLog1"] = '0394 b2 01 041d', |
| 195 | ["07.EvLog2"] = '0294 b2 02 041d', |
| 196 | ["08.EvLog3"] = '0394 b2 03 041d', |
| 197 | ["09.Select ConList file"] ='0294 a4 080004 2000 2050', |
| 198 | ["10.ConList"] = '0394 b2 01 041d', |
| 199 | ["11.Select Contra file"] = '0294 a4 080004 2000 2020', |
| 200 | ["12.Contra1"] = '0394 b2 01 041d', |
| 201 | ["13.Contra2"] = '0294 b2 02 041d', |
| 202 | ["14.Contra3"] = '0394 b2 03 041d', |
| 203 | ["15.Contra4"] = '0294 b2 04 041d', |
| 204 | ["16.Select Counter file"]= '0394 a4 080004 2000 2069', |
| 205 | ["17.Counter"] = '0294 b2 01 041d', |
| 206 | ["18.Select SpecEv file"]= '0394 a4 080004 2000 2040', |
| 207 | ["19.SpecEv1"] = '0294 b2 01 041d', |
| 208 | } |
| 209 | |
| 210 | --- |
| 211 | -- The main entry point |
| 212 | function main(args) |
| 213 | |
| 214 | local data, apdu, flags, uid, cid, result, err, card |
| 215 | -- Read the parameters |
| 216 | for o, a in getopt.getopt(args, 'h') do |
| 217 | if o == "h" then return help() end |
| 218 | end |
| 219 | |
| 220 | calypso_switch_on_field() |
| 221 | |
| 222 | -- Select 14b tag. |
| 223 | card, err = lib14b.waitFor14443b() |
| 224 | if not card then return oops(err) end |
| 225 | |
| 226 | calypso_card_num(card) |
| 227 | cid = card.cid |
| 228 | |
| 229 | --[[ |
| 230 | NAME VALUE APDU_POS |
| 231 | PCB 0x0A 0 |
| 232 | CID 0x00 1 |
| 233 | CLA 0x94 2 |
| 234 | SELECT FILE 0xA4 3 |
| 235 | READ FILE 0xB2 3 |
| 236 | P1 4 |
| 237 | P2 5 |
| 238 | LEN_ |
| 239 | 0 1 2 3 4 5 6 7 |
| 240 | apdu = '02 94 a4 08 00 04 3f 00 00 02' --select ICC file |
| 241 | DF_NAME = "1TIC.ICA" |
| 242 | --]] |
| 243 | --for i = 1,10 do |
| 244 | --result, err = calypso_send_cmd_raw('0294a40800043f000002',false) --select ICC file |
| 245 | for i, apdu in spairs(_calypso_cmds) do |
| 246 | print('>>', i ) |
| 247 | apdu = apdu:gsub("%s+","") |
| 248 | result, err = calypso_send_cmd_raw(apdu , false) |
| 249 | if result then |
| 250 | calypso_apdu_status(result.data) |
| 251 | print('<<', result.data ) |
| 252 | else |
| 253 | print('<< no answer') |
| 254 | end |
| 255 | end |
| 256 | calypso_switch_off_field() |
| 257 | end |
| 258 | --- |
| 259 | -- a simple selftest function, tries to convert |
| 260 | function selftest() |
| 261 | DEBUG = true |
| 262 | dbg("Performing test") |
| 263 | dbg("Tests done") |
| 264 | end |
| 265 | -- Flip the switch here to perform a sanity check. |
| 266 | -- It read a nonce in two different ways, as specified in the usage-section |
| 267 | if "--test"==args then |
| 268 | selftest() |
| 269 | else |
| 270 | -- Call the main |
| 271 | main(args) |
| 272 | end |