Upgrade to Pro — share decks privately, control downloads, hide ads and more …

DroidKaigi 2023

ARIYAMA Keiji
September 14, 2023

DroidKaigi 2023

2023/09/14開催の DroidKaigi 2023 で発表した 「Host-based Card Emulationで簡単NFC生活」の発表資料です。

ARIYAMA Keiji

September 14, 2023
Tweet

More Decks by ARIYAMA Keiji

Other Decks in Technology

Transcript

  1. )$&ͷར༻γʔϯ ϞόΠϧӡస໔ڐূ *40*&$ 
 1FSTPOBMJEFOUJ fi DBUJPOŠ*40DPNQMJBOUESJWJOHMJDFODFŠ1BSU 
 .PCJMFESJWJOHMJDFODF N%-

    BQQMJDBUJPO (PPHMFʹΑΔN%PD͓ΑͼN%PD3FBEFSͷϦϑΝϨϯε࣮૷ IUUQTHJUIVCDPNHPPHMFJEFOUJUZDSFEFOUJBM 
  2. )PTU"QEV4FSWJDFΛ࣮૷  class HceService : HostApduService() { override fun processCommandApdu(commandApdu:

    ByteArray, extras: Bundle?): ByteArray { Log.d(TAG, "commandApdu: ${commandApdu.toHex()}") return byteArrayOf(0x90.toByte(), 0x00) } override fun onDeactivated(reason: Int) { } companion object { private val TAG: String = HceService::class.java.simpleName } } private fun ByteArray.toHex(): String = joinToString(":") { "%02x".format(it).uppercase() } private fun Byte.toUnsignedInt(): Int = (this.toInt() and 0xFF)
  3. "OESPJE.BOJGFTUΛઃఆ  <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <uses-permission android:name="android.permission.NFC"

    /> <application> <!-- লུ --> <service android:name=".HceService" android:exported="true" android:permission="android.permission.BIND_NFC_SERVICE"> <intent-filter> <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE" /> </intent-filter> <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/hceservice" /> </service> </application> </manifest>
  4. $ opensc-tool -s 00:A4:04:00:07:F0:39:41:48:14:81:00 Using reader with a card: ACS

    ACR1252 Dual Reader Sending: 00 A4 04 00 07 F0 39 41 48 14 81 00 Received (SW1=0x90, SW2=0x00) 3FDFJWFEʹଓ͍ͯɺϓϩάϥϜଆͰࢦఆͨ͠஋ʢY YʣΛड͚औΔ͜ͱ "OESPJE4UVEJPͷ-PH$BUʹ"͔Β࢝·ΔόΠτ͕දࣔ͞Ε͍ͯΔͷΛ֬ೝ͢Δ 
  5. $ opensc-tool -s 00:A4:04:00:07:F0:39:41:48:14:81:00 Using reader with a card: ACS

    ACR1252 Dual Reader Sending: 00 A4 04 00 07 F0 39 41 48 14 81 00 Received (SW1=0x90, SW2=0x00) <host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android" android:description="@string/hce_service_description" android:requireDeviceUnlock="false"> <aid-group android:category="other" android:description="@string/aid_description"> <aid-filter android:name="F0394148148100" /> </aid-group> </host-apdu-service> ૹ৴಺༰Λৄ͘͠ݟΔ 
  6. "1%6 "QQMJDBUJPO1SPUPDPM%BUB6OJU 00:A4:04:00:07:F0:39:41:48:14:81:00 */4USVDUJPOCZUF໋ྩ $-"TTCZUFΫϥεόΠτ 1ύϥϝʔλʔ 1ύϥϝʔλʔ -Dσʔλ௕ %BUB 

    Y"ˠ4&-&$5 *40*&$ *EFOUJ fi DBUJPODBSETŠ*OUFHSBUFEDJSDVJUDBSETŠ1BSU 0SHBOJ[BUJPO TFDVSJUZBOEDPNNBOETGPSJOUFSDIBOHF
  7. 4UBUVT8PSE IUUQTXXXFGUMBCDPNLOPXMFEHFCBTFDPNQMFUFMJTUPGBQEVSFTQPOTFT  48 48 ҙຯ 90 "00" ੒ޭ 61

    xx ੒ޭɻ࢒ΓxxόΠτͷϨεϙϯε͕͋Δ 63 Cx PINর߹ࣦഊɻ࢒ΓࢼߦՄೳճ਺xճ 69 82 PIN͕র߹͞Ε͍ͯͳ͍ 69 83 ϒϩοΫ͞Ε͍ͯΔʢPINࢼߦՄೳճ਺Λ௒ աͨ͠ 6A 82 αϙʔτ͍ͯ͠ͳ͍ϑΝΠϧɾΞϓϦέʔ γϣϯ 
  8. 3FBE#JOBSZΛ࣮૷͢Δ CLA: 0x00 INS: 0xB0 P1: 0x00 P2: 0x00 Le:

    0x00 $ opensc-tool \ -s 00:A4:04:00:07:F0:39:41:48:14:81:00 \ -s 00:B0:00:00:00 
  9. 3FBE#JOBSZΛ࣮૷͢Δ ύλʔϯ $-"]*/4]1]1 ύλʔϯ $-"]*/4]1]1]-F ύλʔϯ $-"]*/4]1]1]-D]%BUB ύλʔϯ $-"]*/4]1]1]-D]%BUB]-F 00:B0:00:00:00

    */4USVDUJPODPEF໋ྩ $-"TTΫϥεόΠτ 1ύϥϝʔλʔ 1ύϥϝʔλʔ -Fظ଴͢Δσʔλ௕ 
  10. "1%6ͷύʔεͱ3FBE#JOBSZͷ࣮૷ val apdu = ApduCommand.readFrom(commandApdu, 0) val cla = apdu.header.cla.toUnsignedInt()

    val ins = apdu.header.ins.toUnsignedInt() Log.d(TAG, "apdu: cla:$cla, ins:$ins") val response = when (ins) { 0xA4 -> STATUS_WORD_SUCCESS 0xB0 -> ByteArray(256) { it.toByte() }.also { it[it.lastIndex] = "X".toByteArray().first() } + STATUS_WORD_SUCCESS else -> STATUS_WORD_INS_NOT_SUPPORTED_OR_INVALID } return response 
  11. ࣮ߦ Using reader with a card: ACS ACR1252 Dual Reader

    Sending: 00 A4 04 00 07 F0 39 41 48 14 81 00 Received (SW1=0x90, SW2=0x00) Sending: 00 B0 00 00 00 Received (SW1=0x90, SW2=0x00): 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ................ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F ................ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F !"#$%&'()*+,-./ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 0123456789:;<=>? 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F @ABCDEFGHIJKLMNO 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F PQRSTUVWXYZ[\]^_ 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F `abcdefghijklmno 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F pqrstuvwxyz{|}~. 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F ................ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F ................ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF ................ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF ................ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF ................ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF ................ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF ................ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD 58 ..............X  $ opensc-tool \ -s 00:A4:04:00:07:F0:39:41:48:14:81:00 \ -s 00:B0:00:00:00
  12. ඇಉظͰ݁ՌΛฦ͢ private val job = Job() private val coroutineScope =

    CoroutineScope(Dispatchers.IO + job) override fun processCommandApdu(commandApdu: ByteArray, extras: Bundle?): ByteArray? { // লུ coroutineScope.launch { delay(1000) sendResponseApdu(response) } return null } override fun onDestroy() { super.onDestroy() job.cancel() } 
  13. ࢀߟ w ੖ߞӍಡϚΠφϯόʔΧʔυͱ"1%6Ͱ௨৴ͯ͠ॺ໊σʔλ࡞੒ w IUUQTUFYFHJUIVCJPCMPHQSPUPDPMKQLJNZOVNCFSDBSEXJUIBQEV w 4NBSU$BSE(VZ w IUUQTTNBSUDBSEHVZIBUFOBCMPHKQ w

    &5'MBC,OPXMFEHFCBTF w IUUQTXXXFGUMBCDPNLOPXMFEHFCBTF w ࡏཹΧʔυ౳ಡΈऔΓ࢓༷ॻʢҰൠެ։༻ʣW w IUUQTXXXNPKHPKQJTBDPOUFOUQEG 
  14. ࢀߟ w ӡస໔ڐূٴͼӡస໔ڐূ࡞੒γεςϜ౳࢓༷ॻʢ࢓༷ॻόʔδϣϯ൪߸ɿ  w IUUQTXXXOQBHPKQMBXTOPUJ fi DBUJPOLPVUVVNFOLZPNFOLZP@QEG w 'VODUJPOBM4QFDJ

    fi DBUJPOPGUIF0QFO1(1BQQMJDBUJPOPO*404NBSU$BSE 0QFSBUJOH4ZTUFNT w IUUQTHOVQHPSHGUQTQFDT0QFO1(1TNBSUDBSEBQQMJDBUJPOQEG w *$"0%PD.BDIJOF3FBEBCMF5SBWFM%PDVNFOU w IUUQTXXXJDBPJOUQVCMJDBUJPOTQBHFTQVCMJDBUJPOBTQY EPDOVN