Smart Card Guy

Smart Card / Java Card, Cyber Security, IoT Device Security, Root of Trust, 標準化等

事例 - eSIM / Samsung Galaxy Watch / Samsung, G+D, STM (2018/08)

f:id:blog-guy:20180822144119p:plain

概要

  • Final Product : Samsung Galaxy Watch
  • eSIM : G+D's secure Sm@rtSIM CX platform (Java Card Based)
    • compliant with current GSMA RSP v2.2 eSIM technical specifications for consumer devices.
  • H/W : STMicroelectronics ST33G1M2 32-bit secure MCU (ARM SecurCore SC300 based)

Link

中国ICカード・スマートカードメーカー

Hengbao (恒宝)

Watchdata Technologies (握奇数据)

Fudan Microelectronics Group

Guangdong Chutian Dragon Smart Card Co.,

Eastcompeace Technology

WUHAN TIANYU (武汉天喻)

APDU (Application Protocol Data Unit) プロトコル

定義

  • ISO 7816-4で定義したSmart Card向け通信プロトコル
  • CAD(Card Acceptance Device - Reader、TerminalのようなHost)側からCardへ送るCommand APDU(C-APDU)と、Card側からCADへ送るResponse APDU(R-APDU)で構成。
  • Smart Card - CADの構成で、Smart Cardはいつもpassive(Slave)ロール。CAD側からC-APDUが来るのを待ってそれに対し、R-APDUを返す。
  • Transport LayerにはTPDUプロトコルがISO 7816-3で定義。簡単に以下の2つのパターンが存在するということだけ抑えておこう。
    • T=0 protocol : byte-oriented
    • T=1 protocol : block-oriented

Command APDU

f:id:blog-guy:20180630102534p:plain

header (Mandatory)
  • CLA : Class of Instruction
  • INS : Instruction Code
  • P1 : Parameter 1
  • P2 : Parameter 2
body (Optional)
  • Lc : length of data field
  • Data Field : Cardへ送るデータ
  • Le : Maximum of bytes expected in the data field of the response to the command

Response APDU

f:id:blog-guy:20180630102552p:plain

body (Optional)
  • Data Field
trailer (Mandatory)
  • SW1 : Status word 1
  • SW2 : Status word 2. SW1, SW2と合わせてステータスを表す。例)0x9000 : Success

C-APDU / R-APDUパターン (4 cases)

f:id:blog-guy:20190222171540p:plain

Case 1 - No command data, no reponse data
  • No data are transferred to or from the card.
  • C-APDU contains only the header.
  • R-APDU contains only the trailer status word.
Case 2 - No command data, send reponse data
  • No data are transferred to the card, but data are returned from the card.
  • The body of the C-APDU contains one byte - the Le field, which specifies the number of data bytes in the corresponding R-APDU.
Case 3 - Receive command data, no reponse data
  • Data are transferred to the card, but no data are returned from the card as a result of processing the command.
  • The body of the C-APDU includes the Lc field and the data field. The Lc byte specifies the length of the data field.
  • The R-APDU contains only the trailer status word.
Case 4 - Receive command data, send reponse data
  • Data are transferred to the card, and data are returned from the card as a result of processing the command.
  • The body of the C-APDU includes the Lc field, the data field, and the Le field.
  • The R-APDU contains both the data and the trailer status word.

Status Word

cardwerk.com

Java Card サンプルアプリ - Wallet

開発環境構築

こちらを参照 smartcardguy.hatenablog.jp

Java Card Wallet

Walletアプリ

簡易ATMカードのような機能を提供するアプリ。 預金(CREDIT)、引き出し(DEBIT)、残高照会(GET_BALANCE)などを行う。

Source code

/** 
 * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved.
 * 
 */

/*
 */

/*
 * @(#)Wallet.java 1.11 06/01/03
 */

package com.sun.jcclassic.samples.wallet;

import javacard.framework.APDU;
import javacard.framework.Applet;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import javacard.framework.OwnerPIN;

public class Wallet extends Applet {

    /* constants declaration */

    // code of CLA byte in the command APDU header
    final static byte Wallet_CLA = (byte) 0x80;

    // codes of INS byte in the command APDU header
    final static byte VERIFY = (byte) 0x20;
    final static byte CREDIT = (byte) 0x30;
    final static byte DEBIT = (byte) 0x40;
    final static byte GET_BALANCE = (byte) 0x50;

    // maximum balance
    final static short MAX_BALANCE = 0x7FFF;
    // maximum transaction amount
    final static byte MAX_TRANSACTION_AMOUNT = 127;

    // Comments by ASJ 
    // 0x7FFF => 32,767
    // 127 => 0x7F, 


    // maximum number of incorrect tries before the
    // PIN is blocked
    final static byte PIN_TRY_LIMIT = (byte) 0x03;
    // maximum size PIN
    final static byte MAX_PIN_SIZE = (byte) 0x08;

    // signal that the PIN verification failed
    final static short SW_VERIFICATION_FAILED = 0x6300;
    // signal the the PIN validation is required
    // for a credit or a debit transaction
    final static short SW_PIN_VERIFICATION_REQUIRED = 0x6301;
    // signal invalid transaction amount
    // amount > MAX_TRANSACTION_AMOUNT or amount < 0
    final static short SW_INVALID_TRANSACTION_AMOUNT = 0x6A83;

    // signal that the balance exceed the maximum
    final static short SW_EXCEED_MAXIMUM_BALANCE = 0x6A84;
    // signal the the balance becomes negative
    final static short SW_NEGATIVE_BALANCE = 0x6A85;

    /* instance variables declaration */
    OwnerPIN pin;
    short balance;

    private Wallet(byte[] bArray, short bOffset, byte bLength) {

        // It is good programming practice to allocate
        // all the memory that an applet needs during
        // its lifetime inside the constructor
        pin = new OwnerPIN(PIN_TRY_LIMIT, MAX_PIN_SIZE);

        byte iLen = bArray[bOffset]; // aid length
        bOffset = (short) (bOffset + iLen + 1);
        byte cLen = bArray[bOffset]; // info length
        bOffset = (short) (bOffset + cLen + 1);
        byte aLen = bArray[bOffset]; // applet data length

        // The installation parameters contain the PIN
        // initialization value
        pin.update(bArray, (short) (bOffset + 1), aLen);
        register();

    } // end of the constructor

    public static void install(byte[] bArray, short bOffset, byte bLength) {
        // create a Wallet applet instance
        new Wallet(bArray, bOffset, bLength);
    } // end of install method

    @Override
    public boolean select() {

        // The applet declines to be selected
        // if the pin is blocked.
        if (pin.getTriesRemaining() == 0) {
            return false;
        }

        return true;

    }// end of select method

    @Override
    public void deselect() {

        // reset the pin value
        pin.reset();

    }

    @Override
    public void process(APDU apdu) {

        // APDU object carries a byte array (buffer) to
        // transfer incoming and outgoing APDU header
        // and data bytes between card and CAD

        // At this point, only the first header bytes
        // [CLA, INS, P1, P2, P3] are available in
        // the APDU buffer.
        // The interface javacard.framework.ISO7816
        // declares constants to denote the offset of
        // these bytes in the APDU buffer

        byte[] buffer = apdu.getBuffer();
        // check SELECT APDU command

        if (apdu.isISOInterindustryCLA()) {
            if (buffer[ISO7816.OFFSET_INS] == (byte) (0xA4)) {
                return;
            }
            ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
        }

        // verify the reset of commands have the
        // correct CLA byte, which specifies the
        // command structure
        if (buffer[ISO7816.OFFSET_CLA] != Wallet_CLA) {
            ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
        }

        switch (buffer[ISO7816.OFFSET_INS]) {
            case GET_BALANCE:
                getBalance(apdu);
                return;
            case DEBIT:
                debit(apdu);
                return;
            case CREDIT:
                credit(apdu);
                return;
            case VERIFY:
                verify(apdu);
                return;
            default:
                ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
        }

    } // end of process method

    private void credit(APDU apdu) {

        // access authentication
        if (!pin.isValidated()) {
            ISOException.throwIt(SW_PIN_VERIFICATION_REQUIRED);
        }

        byte[] buffer = apdu.getBuffer();

        // Lc byte denotes the number of bytes in the
        // data field of the command APDU
        byte numBytes = buffer[ISO7816.OFFSET_LC];

        // indicate that this APDU has incoming data
        // and receive data starting from the offset
        // ISO7816.OFFSET_CDATA following the 5 header
        // bytes.
        byte byteRead = (byte) (apdu.setIncomingAndReceive());

        // it is an error if the number of data bytes
        // read does not match the number in Lc byte
        if ((numBytes != 1) || (byteRead != 1)) {
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
        }

        // get the credit amount
        byte creditAmount = buffer[ISO7816.OFFSET_CDATA];

        // check the credit amount
        if ((creditAmount > MAX_TRANSACTION_AMOUNT) || (creditAmount < 0)) {
            ISOException.throwIt(SW_INVALID_TRANSACTION_AMOUNT);
        }

        // check the new balance
        if ((short) (balance + creditAmount) > MAX_BALANCE) {
            ISOException.throwIt(SW_EXCEED_MAXIMUM_BALANCE);
        }

        // credit the amount
        balance = (short) (balance + creditAmount);

    } // end of deposit method

    private void debit(APDU apdu) {

        // access authentication
        if (!pin.isValidated()) {
            ISOException.throwIt(SW_PIN_VERIFICATION_REQUIRED);
        }

        byte[] buffer = apdu.getBuffer();

        byte numBytes = (buffer[ISO7816.OFFSET_LC]);

        byte byteRead = (byte) (apdu.setIncomingAndReceive());

        if ((numBytes != 1) || (byteRead != 1)) {
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
        }

        // get debit amount
        byte debitAmount = buffer[ISO7816.OFFSET_CDATA];

        // check debit amount
        if ((debitAmount > MAX_TRANSACTION_AMOUNT) || (debitAmount < 0)) {
            ISOException.throwIt(SW_INVALID_TRANSACTION_AMOUNT);
        }

        // check the new balance
        if ((short) (balance - debitAmount) < (short) 0) {
            ISOException.throwIt(SW_NEGATIVE_BALANCE);
        }

        balance = (short) (balance - debitAmount);

    } // end of debit method

    private void getBalance(APDU apdu) {

        byte[] buffer = apdu.getBuffer();

        // inform system that the applet has finished
        // processing the command and the system should
        // now prepare to construct a response APDU
        // which contains data field
        short le = apdu.setOutgoing();

        if (le < 2) {
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
        }

        // informs the CAD the actual number of bytes
        // returned
        apdu.setOutgoingLength((byte) 2);

        // move the balance data into the APDU buffer
        // starting at the offset 0
        buffer[0] = (byte) (balance >> 8);
        buffer[1] = (byte) (balance & 0xFF);

        // send the 2-byte balance at the offset
        // 0 in the apdu buffer
        apdu.sendBytes((short) 0, (short) 2);

    } // end of getBalance method

    private void verify(APDU apdu) {

        byte[] buffer = apdu.getBuffer();
        // retrieve the PIN data for validation.
        byte byteRead = (byte) (apdu.setIncomingAndReceive());

        // check pin
        // the PIN data is read into the APDU buffer
        // at the offset ISO7816.OFFSET_CDATA
        // the PIN data length = byteRead
        if (pin.check(buffer, ISO7816.OFFSET_CDATA, byteRead) == false) {
            ISOException.throwIt(SW_VERIFICATION_FAILED);
        }

    } // end of validate method
} // end of class Wallet

wallet.scr

output on;

// create wallet applet
0x80 0xB8 0x00 0x00 0x14 0x0a 0xa0 0x0 0x0 0x0 0x62 0x3 0x1 0xc 0x6 0x1 0x08 0x0 0x0 0x05 0x01 0x02 0x03 0x04 0x05 0x7F;


/////////////////////////////////////////////////////////////////////
// Initialize Wallet
/////////////////////////////////////////////////////////////////////

//Select Wallet
0x00 0xA4 0x04 0x00 0x0a 0xa0 0x0 0x0 0x0 0x62 0x3 0x1 0xc 0x6 0x1 0x7F;
// 90 00 = SW_NO_ERROR

//Verify user pin
0x80 0x20 0x00 0x00 0x05 0x01 0x02 0x03 0x04 0x05 0x7F;
//90 00 = SW_NO_ERROR

//Get wallet balance
0x80 0x50 0x00 0x00 0x00 0x02;
//0x00 0x00 0x00 0x00 0x90 0x00 = Balance = 0 and SW_ON_ERROR

//Attempt to debit from an empty account
0x80 0x40 0x00 0x00 0x01 0x64 0x7F; 
//0x6A85 = SW_NEGATIVE_BALANCE

//Credit $100 to the empty account
0x80 0x30 0x00 0x00 0x01 0x64 0x7F; 
//0x9000 = SW_NO_ERROR

//Get Balance
0x80 0x50 0x00 0x00 0x00 0x02;
//0x00 0x64 0x9000 = Balance = 100 and SW_NO_ERROR

//Debit $50 from the account
0x80 0x40 0x00 0x00 0x01 0x32 0x7F; 
//0x9000 = SW_NO_ERROR

//Get Balance
0x80 0x50 0x00 0x00 0x00 0x02;
//0x00 0x32 0x9000 = Balance = 50 and SW_NO_ERROR

//Credit $128 to the account
0x80 0x30 0x00 0x00 0x01 0x80 0x7F; 
//0x6A83 = SW_INVALID_TRANSACTION_AMOUNT

//Get Balance
0x80 0x50 0x00 0x00 0x00 0x02;
//0x00 0x32 0x9000 = Balance = 50 and SW_NO_ERROR

//Debit $51 from the account
0x80 0x40 0x00 0x00 0x01 0x33 0x7F;
//0x6A85 = SW_NEGATIVE_BALANCE

//Get Balance
0x80 0x50 0x00 0x00 0x00 0x02;
//0x00 0x32 0x9000 = Balance = 50 and SW_NO_ERROR

//Debit $128 from the account
0x80 0x40 0x00 0x00 0x01 0x80 0x7F;
//0x6A83 = SW_INVALID_TRANSACTION_AMOUNT

//Get Balance
0x80 0x50 0x00 0x00 0x00 0x02;
//0x00 0x32 0x9000 = Balance = 50 and SW_NO_ERROR

//Reselect Wallet applet so that userpin is reset
0x00 0xA4 0x04 0x00 0x0a 0xa0 0x0 0x0 0x0 0x62 0x3 0x1 0xc 0x6 0x1 0x7F;
// 90 00 = SW_NO_ERROR

//Credit $127 to the account before pin verification
0x80 0x30 0x00 0x00 0x01 0x7F 0x7F;
//0x6301 = SW_PIN_VERIFICATION_REQUIRED

//Verify User pin with wrong pin value
0x80 0x20 0x00 0x00 0x04 0x01 0x03 0x02 0x66 0x7F;
//0x6300 = SW_VERIFICATION_FAILED

//Verify user pin again with correct pin value 
//0x80 0x20 0x00 0x00 0x08 0xF2 0x34 0x12 0x34 0x56 0x10 0x01 0x01 0x7F;
0x80 0x20 0x00 0x00 0x05 0x01 0x02 0x03 0x04 0x05 0x7F;
//0x9000 = SW_NO_ERROR

//Get balance with incorrect LE value 
0x80 0x50 0x00 0x00 0x00 0x01;
//0x6700 = ISO7816.SW_WRONG_LENGTH

//Get balance 
0x80 0x50 0x00 0x00 0x00 0x02;
//0x00 0x32 0x9000 = Balance = 50 and SW_NO_ERROR

実行

1. Sample_Device設定

  • Java Card ViewからSample_Deviceをダブルクリック
  • Properties画面からCREFタブを選択
  • 「Input file with EEPROM data」、「Output file for EEPROM data」、 「Combined (input and output) file for EEPROM data」は空白に設定
  • 「Do not open APDU console」はチェックを外す

f:id:blog-guy:20180811154441p:plain

2. Sample_DeviceのStart

Sample_DeviceをStart。 最後にCMD> が表示される。 f:id:blog-guy:20180811154758p:plain

3. cap-com.sun.jcclassic.samples.walletを実行

ConsoleのToolbarからcap-com.sun.jcclassic.samples.walletを実行 f:id:blog-guy:20180811155211p:plain

4. wallet.scr実行

  • Package Explorerからwallet.scrを開き、全テキストをコピ。
  • Sample_Device consoleのCMDプロンプトへペースト

実行結果

CMD>output on;

// create wallet applet
0x80 0xB8 0x00 0x00 0x14 0x0a 0xa0 0x0 0x0 0x0 0x62 0x3 0x1 0xc 0x6 0x1 0x08 0x0 0x0 0x05 0x01 0x02 0x03 0x04 0x05 0x7F;


/////////////////////////////////////////////////////////////////////
// Initialize Wallet
/////////////////////////////////////////////////////////////////////

//Select Wallet
0x00 0xA4 0x04 0x00 0x0a 0xa0 0x0 0x0 0x0 0x62 0x3 0x1 0xc 0x6 0x1 0x7F;
// 90 00 = SW_NO_ERROR

//Verify user pin
0x80 0x20 0x00 0x00 0x05 0x01 0x02 0x03 0x04 0x05 0x7F;
//90 00 = SW_NO_ERROR

//Get wallet balance
0x80 0x50 0x00 0x00 0x00 0x02;
//0x00 0x00 0x00 0x00 0x90 0x00 = Balance = 0 and SW_ON_ERROR

//Attempt to debit from an empty account
0x80 0x40 0x00 0x00 0x01 0x64 0x7F; 
//0x6A85 = SW_NEGATIVE_BALANCE

//Credit $100 to the empty account
0x80 0x30 0x00 0x00 0x01 0x64 0x7F; 
//0x9000 = SW_NO_ERROR

//Get Balance
0x80 0x50 0x00 0x00 0x00 0x02;
//0x00 0x64 0x9000 = Balance = 100 and SW_NO_ERROR

//Debit $50 from the account
0x80 0x40 0x00 0x00 0x01 0x32 0x7F; 
//0x9000 = SW_NO_ERROR

//Get Balance
0x80 0x50 0x00 0x00 0x00 0x02;
//0x00 0x32 0x9000 = Balance = 50 and SW_NO_ERROR

//Credit $128 to the account
0x80 0x30 0x00 0x00 0x01 0x80 0x7F; 
//0x6A83 = SW_INVALID_TRANSACTION_AMOUNT

//Get Balance
0x80 0x50 0x00 0x00 0x00 0x02;
//0x00 0x32 0x9000 = Balance = 50 and SW_NO_ERROR

//Debit $51 from the account
0x80 0x40 0x00 0x00 0x01 0x33 0x7F;
//0x6A85 = SW_NEGATIVE_BALANCE

//Get Balance
0x80 0x50 0x00 0x00 0x00 0x02;
//0x00 0x32 0x9000 = Balance = 50 and SW_NO_ERROR

//Debit $128 from the account
0x80 0x40 0x00 0x00 0x01 0x80 0x7F;
//0x6A83 = SW_INVALID_TRANSACTION_AMOUNT

//Get Balance
0x80 0x50 0x00 0x00 0x00 0x02;
//0x00 0x32 0x9000 = Balance = 50 and SW_NO_ERROR

//Reselect Wallet applet so that userpin is reset
0x00 0xA4 0x04 0x00 0x0a 0xa0 0x0 0x0 0x0 0x62 0x3 0x1 0xc 0x6 0x1 0x7F;
// 90 00 = SW_NO_ERROR

//Credit $127 to the account before pin verification
0x80 0x30 0x00 0x00 0x01 0x7F 0x7F;
//0x6301 = SW_PIN_VERIFICATION_REQUIRED

//Verify User pin with wrong pin value
0x80 0x20 0x00 0x00 0x04 0x01 0x03 0x02 0x66 0x7F;
//0x6300 = SW_VERIFICATION_FAILED

//Verify user pin again with correct pin value 
//0x80 0x20 0x00 0x00 0x08 0xF2 0x34 0x12 0x34 0x56 0x10 0x01 0x01 0x7F;
0x80 0x20 0x00 0x00 0x05 0x01 0x02 0x03 0x04 0x05 0x7F;
//0x9000 = SW_NO_ERROR

//Get balance with incorrect LE value 
0x80 0x50 0x00 0x00 0x00 0x01;
//0x6700 = ISO7816.SW_WRONG_LENGTH

//Get balance 
0x80 0x50 0x00 0x00 0x00 0x02;
//0x00 0x32 0x9000 = Balance = 50 and SW_NO_ERROR

APDU|OUTPUT ON;
APDU|CLA: 80, INS: b8, P1: 00, P2: 00, Lc: 14, 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 06, 01, 08, 00, 00, 05, 01, 02, 03, 04, 05, Le: 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 06, 01, SW1: 90, SW2: 00
APDU|CLA: 00, INS: a4, P1: 04, P2: 00, Lc: 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 06, 01, Le: 00, SW1: 90, SW2: 00
APDU|CLA: 80, INS: 20, P1: 00, P2: 00, Lc: 05, 01, 02, 03, 04, 05, Le: 00, SW1: 90, SW2: 00
APDU|CLA: 80, INS: 50, P1: 00, P2: 00, Lc: 00, Le: 02, 00, 00, SW1: 90, SW2: 00
APDU|CLA: 80, INS: 40, P1: 00, P2: 00, Lc: 01, 64, Le: 00, SW1: 6a, SW2: 85
APDU|CLA: 80, INS: 30, P1: 00, P2: 00, Lc: 01, 64, Le: 00, SW1: 90, SW2: 00
APDU|CLA: 80, INS: 50, P1: 00, P2: 00, Lc: 00, Le: 02, 00, 64, SW1: 90, SW2: 00
APDU|CLA: 80, INS: 40, P1: 00, P2: 00, Lc: 01, 32, Le: 00, SW1: 90, SW2: 00
APDU|CLA: 80, INS: 50, P1: 00, P2: 00, Lc: 00, Le: 02, 00, 32, SW1: 90, SW2: 00
APDU|CLA: 80, INS: 30, P1: 00, P2: 00, Lc: 01, 80, Le: 00, SW1: 6a, SW2: 83
APDU|CLA: 80, INS: 50, P1: 00, P2: 00, Lc: 00, Le: 02, 00, 32, SW1: 90, SW2: 00
APDU|CLA: 80, INS: 40, P1: 00, P2: 00, Lc: 01, 33, Le: 00, SW1: 6a, SW2: 85
APDU|CLA: 80, INS: 50, P1: 00, P2: 00, Lc: 00, Le: 02, 00, 32, SW1: 90, SW2: 00
APDU|CLA: 80, INS: 40, P1: 00, P2: 00, Lc: 01, 80, Le: 00, SW1: 6a, SW2: 83
APDU|CLA: 80, INS: 50, P1: 00, P2: 00, Lc: 00, Le: 02, 00, 32, SW1: 90, SW2: 00
APDU|CLA: 00, INS: a4, P1: 04, P2: 00, Lc: 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 06, 01, Le: 00, SW1: 90, SW2: 00
APDU|CLA: 80, INS: 30, P1: 00, P2: 00, Lc: 01, 7f, Le: 00, SW1: 63, SW2: 01
APDU|CLA: 80, INS: 20, P1: 00, P2: 00, Lc: 04, 01, 03, 02, 66, Le: 00, SW1: 63, SW2: 00
APDU|CLA: 80, INS: 20, P1: 00, P2: 00, Lc: 05, 01, 02, 03, 04, 05, Le: 00, SW1: 90, SW2: 00
APDU|CLA: 80, INS: 50, P1: 00, P2: 00, Lc: 00, Le: 00, SW1: 67, SW2: 00
APDU|CLA: 80, INS: 50, P1: 00, P2: 00, Lc: 00, Le: 02, 00, 32, SW1: 90, SW2: 00
CMD>

OUTPUTの解析

// create wallet applet
APDU|CLA: 80, INS: b8, P1: 00, P2: 00, Lc: 14, 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 06, 01, 08, 00, 00, 05, 01, 02, 03, 04, 05, Le: 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 06, 01, SW1: 90, SW2: 00

//Select Wallet
APDU|CLA: 00, INS: a4, P1: 04, P2: 00, Lc: 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 06, 01, Le: 00, SW1: 90, SW2: 00

//Verify user pin
APDU|CLA: 80, INS: 20, P1: 00, P2: 00, Lc: 05, 01, 02, 03, 04, 05, Le: 00, SW1: 90, SW2: 00

//Get wallet balance
APDU|CLA: 80, INS: 50, P1: 00, P2: 00, Lc: 00, Le: 02, 00, 00, SW1: 90, SW2: 00
//0x00 0x00 0x00 0x00 0x90 0x00 = Balance = 0 and SW_ON_ERROR

//Attempt to debit from an empty account
APDU|CLA: 80, INS: 40, P1: 00, P2: 00, Lc: 01, 64, Le: 00, SW1: 6a, SW2: 85
//0x6A85 = SW_NEGATIVE_BALANCE

//Credit $100 to the empty account
APDU|CLA: 80, INS: 30, P1: 00, P2: 00, Lc: 01, 64, Le: 00, SW1: 90, SW2: 00
//0x9000 = SW_NO_ERROR

//Get Balance
APDU|CLA: 80, INS: 50, P1: 00, P2: 00, Lc: 00, Le: 02, 00, 64, SW1: 90, SW2: 00
//0x00 0x64 0x9000 = Balance = 100 and SW_NO_ERROR

//Debit $50 from the account
APDU|CLA: 80, INS: 40, P1: 00, P2: 00, Lc: 01, 32, Le: 00, SW1: 90, SW2: 00
//0x9000 = SW_NO_ERROR

//Get Balance
APDU|CLA: 80, INS: 50, P1: 00, P2: 00, Lc: 00, Le: 02, 00, 32, SW1: 90, SW2: 00
//0x00 0x32 0x9000 = Balance = 50 and SW_NO_ERROR

//Credit $128 to the account
APDU|CLA: 80, INS: 30, P1: 00, P2: 00, Lc: 01, 80, Le: 00, SW1: 6a, SW2: 83
//0x6A83 = SW_INVALID_TRANSACTION_AMOUNT

//Get Balance
APDU|CLA: 80, INS: 50, P1: 00, P2: 00, Lc: 00, Le: 02, 00, 32, SW1: 90, SW2: 00
//0x00 0x32 0x9000 = Balance = 50 and SW_NO_ERROR

//Debit $51 from the account
APDU|CLA: 80, INS: 40, P1: 00, P2: 00, Lc: 01, 33, Le: 00, SW1: 6a, SW2: 85
//0x6A85 = SW_NEGATIVE_BALANCE

//Get Balance
APDU|CLA: 80, INS: 50, P1: 00, P2: 00, Lc: 00, Le: 02, 00, 32, SW1: 90, SW2: 00
//0x00 0x32 0x9000 = Balance = 50 and SW_NO_ERROR

//Debit $128 from the account
APDU|CLA: 80, INS: 40, P1: 00, P2: 00, Lc: 01, 80, Le: 00, SW1: 6a, SW2: 83
//0x6A83 = SW_INVALID_TRANSACTION_AMOUNT

//Get Balance
APDU|CLA: 80, INS: 50, P1: 00, P2: 00, Lc: 00, Le: 02, 00, 32, SW1: 90, SW2: 00
//0x00 0x32 0x9000 = Balance = 50 and SW_NO_ERROR

//Reselect Wallet applet so that userpin is reset
APDU|CLA: 00, INS: a4, P1: 04, P2: 00, Lc: 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 06, 01, Le: 00, SW1: 90, SW2: 00
// 90 00 = SW_NO_ERROR

//Credit $127 to the account before pin verification
APDU|CLA: 80, INS: 30, P1: 00, P2: 00, Lc: 01, 7f, Le: 00, SW1: 63, SW2: 01
//0x6301 = SW_PIN_VERIFICATION_REQUIRED

//Credit $127 to the account before pin verification
APDU|CLA: 80, INS: 20, P1: 00, P2: 00, Lc: 04, 01, 03, 02, 66, Le: 00, SW1: 63, SW2: 00

//Verify user pin again with correct pin value 
//0x80 0x20 0x00 0x00 0x08 0xF2 0x34 0x12 0x34 0x56 0x10 0x01 0x01 0x7F;
APDU|CLA: 80, INS: 20, P1: 00, P2: 00, Lc: 05, 01, 02, 03, 04, 05, Le: 00, SW1: 90, SW2: 00

//Get balance with incorrect LE value 
APDU|CLA: 80, INS: 50, P1: 00, P2: 00, Lc: 00, Le: 00, SW1: 67, SW2: 00
// C-APDUは「0x80 0x50 0x00 0x00 0x00 0x01;」
//0x6700 = ISO7816.SW_WRONG_LENGTH

//Get balance 
APDU|CLA: 80, INS: 50, P1: 00, P2: 00, Lc: 00, Le: 02, 00, 32, SW1: 90, SW2: 00
//0x00 0x32 0x9000 = Balance = 50 and SW_NO_ERROR

docs.oracle.com

smartcardguy.hatenablog.jp