OmapiTest.testLongSelectResponse()のサポート
OSMOCOM (Open Source Mobile Communications) コミュニティのSIMカードsysmoUSIM-SJS1 4FF上で動くAndroid Secure Element CTS用Java Cardアプレットを作成するこのシリーズ、前回までにOmapiTest.testTransmitApdu()の対応を終え、今回はOmapiTest.testLongSelectResponse()をサポートするための作業です。
cheerio-the-bear.hatenablog.com
testLongSelectResponse()が期待していること
OmapiTest.javaは、こちらで参照可能です。このテストケースでは、AID 'A000000476416E64726F696443545332'のアプレットを選択し、その応答として返ってくるデータがBET-TLVフォーマットになっていることを確認します。
private final static byte[] LONG_SELECT_RESPONSE_AID = new byte[]{(byte) 0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x32}; ... @Test public void testLongSelectResponse() { try { waitForConnection(); Reader[] readers = seService.getReaders(); for (Reader reader : readers) { byte[] selectResponse = testSelectableAid(reader, LONG_SELECT_RESPONSE_AID); assertTrue("Select Response is not complete", verifyBerTlvData(selectResponse)); } } catch (Exception e) { fail("unexpected exception " + e); } }
Android Secure Element CTSのページを見てみると、上記の定数名が示しているように"long"である必要もないと書かれています。
b. A000000476416E64726F696443545332
i. When selected, this AID should return a select response greater than 2 bytes that are correctly formatted using Basic Encoding Rules (BER) and tag-length-value (TLV).
とは言え、ここはひとつGoogleさんのサンプルコードと同じ、長めでそれらしい構造のデータを拝借して返しておくのが無難です。
BER-TLVフォーマットを確認するverifyBerTlvData()の実装が一見雑に見えますが、セキュアエレメントからとても長い応答データが返ってくることを想定しているわけではないので、この程度で事が足りるということなんでしょう。
/** * Verifies TLV data * @param tlv * @return true if the data is tlv formatted, false otherwise */ private static boolean verifyBerTlvData(byte[] tlv){ if (tlv == null || tlv.length == 0) { throw new RuntimeException("Invalid tlv, null"); } int i = 0; if ((tlv[i++] & 0x1F) == 0x1F) { // extra byte for TAG field i++; } int len = tlv[i++] & 0xFF; if (len > 127) { // more than 1 byte for length int bytesLength = len-128; len = 0; for(int j = bytesLength; j > 0; j--) { len += (len << 8) + (tlv[i++] & 0xFF); } } // Additional 2 bytes for the SW return (tlv.length == (i+len+2)); }
ふたつ目のアプレットの追加
これまでcapファイルに含まれるモジュールはひとつ、よってMakefileに記述するモジュールAIDもひとつでした。そこで、ふたつ目のモジュールのためのAID(下記APPLET_AID_32)を追加し、その実装には同じOmapiApplet.javaを共用させます。SELECTされたときに指定されているAIDを確認することで、同じアプレット実装にAIDに応じた異なる振る舞いをさせることが可能です。
APPLET_AID_31 = 0xA0:0x00:0x00:0x04:0x76:0x41:0x6E:0x64:0x72:0x6F:0x69:0x64:0x43:0x54:0x53:0x31 APPLET_AID_32 = 0xA0:0x00:0x00:0x04:0x76:0x41:0x6E:0x64:0x72:0x6F:0x69:0x64:0x43:0x54:0x53:0x32 APPLET_NAME = com.github.cheeriotb.cts.cardlet.OmapiApplet ... $(JAVA) -jar $(JAVACARD_SDK_DIR)/bin/converter.jar \ -d $(BUILD_JAVACARD_DIR) \ -classdir $(BUILD_CLASSES_DIR) \ -exportpath $(JAVACARD_EXPORT_DIR) \ -applet $(APPLET_AID_31) $(APPLET_NAME) \ -applet $(APPLET_AID_32) $(APPLET_NAME) \ $(PACKAGE_NAME) $(PACKAGE_AID) $(PACKAGE_VERSION)
これでビルドしたアプレットをロード・インストールした上で、GET STATUSコマンドで確認してみました。期待通りにAIDがふたつ見えています。
$ python shadysim.py --pcsc --list-applets --kic 9A665E9CDA096DAE9C04894785EB0B18 --kid 1A8DD88431450CAF8D3719F6380F0A18 ... AID: a0000000090001ffffffff8900, State: 01, Privs: 00 Instance AID: a0000000090001ffffffff8900000000 Instance AID: a0000000090001ffffffff89b00010 AID: a0000000871002ff49ffff8900, State: 01, Privs: 00 Instance AID: a0000000871002ff49ffff89040b0000 AID: ff434e52581040040203, State: 01, Privs: 00 Instance AID: ff434e52581040040203000000000000 AID: a00000047600, State: 01, Privs: 00 Instance AID: a000000476416e64726f696443545331 Instance AID: a000000476416e64726f696443545332
アプレットそのものに追加するコードは、シンプルなものです。そのコミットはこちら。
アプレットのテスト
Pythonスクリプトにも、OmapiTest.testLongSelectResponse()同等のテストケースを追加しました。
実行した結果がこちら。AID 32に対するSELECTコマンドに、6Fタグで始まるBET-TLVフォーマットのFCIテンプレートを期待通りに返しています。
$ python sects.py ... started: testLongSelectResponse C-APDU : 0070000001 R-APDU + SW : 019000 C-APDU : 01A4040010A000000476416E64726F69644354533200 R-APDU + SW : 9f8a C-APDU : 01c000008a R-APDU + SW : 6f81878410536c6374526573705465737420312e30a5736506072a864886fc6b01600b06092a864886fc6b020202630906072a864886fc6b03640b06092a864886fc6b048000640b06092a864886fc6b040255640b06092a864886fc6b040370650d060b2a864886fc6b0504020000660c060a2b060104012a026e01039f6e060077602201209f6501ff9000 C-APDU : 00708001 R-APDU + SW : 9000 finished: testLongSelectResponse
Pythonは、見よう見まねでもそれらしく書けてしまうのが、逆に良くなかったりするんじゃないかと思います。ちゃんと学習すれば、もっと綺麗なコードになるんでしょう。時間が取れれば、何か評判の良い書籍でも探しますか..