0

I need to load a simple applet to my USIM card. Authentication is OK. I'm using OTA SMS-DELIVER via SmartCard to install CAP file.

Using these:

I couldnt find the right spec explaining "C482XXYY" in Header.cap, how it should be calculated,

this answer below helped me navigate, but for Method.cap loading - it still fails https://community.oracle.com/tech/developers/discussion/1753814/globalpaltform-load-command-data-field

I've written this loader: https://pastebin.com/pSXeDYyS

Every component in CAP file is in separate APDU with LOAD instruction. First 5 is loaded successfully with DATA=00, SW=9000, however on Method it fails. As you can see from my script, I've fixed Descriptor size to 0000 in Directory field. And in Header C482xxyy, where xxyy is calculated properly, which is a sum of all size fields in Directory, e.g.

016a = 0011+001f+000c+001e+0042+0018+006d+0032+0017+0000+0000

Question: Can't figure out why Method LOAD fails? By checking GP 2.1 spec, it doesnt

help me understand the problem. Concatenated components to < 255 block size also fails, only separate components work for LOAD.

[+] Install for load

>> 80e602001207d07002ca449001000006ef04c60201850000c0000000

<< 009000

[+] LOAD - Header

>> 80e8000018c482016a010011decaffed010204000107d07002ca44900100c0000000

<< 009000

[+] LOAD - Directory

>> 80e800012202001f0011001f000c001e00420018006d003200170000000000040002002202010000c0000000

<< 009000

[+] LOAD - Import

>> 80e800022104001e02000107a0000000620101060210a0000000090003ffffffff891071000200c0000000

<< 009000

[+] LOAD - Applet

>> 80e800030f03000c0108d07002ca44900101002000c0000000

<< 009000

[+] LOAD - Class

>> 80e800041b06001843800301ff0007020000002f00398002008101010881000000c0000000

<< 009000

[!!!!] LOAD - Method (FAILED)

>> 80e800057007006d000911188c00048d00012c18197b0002037b00029210240303038b000388007a02318f00053d8c00062e1b8b00077a0120188b000860037a7a02228d00092d1d10076b101a8b000a321fae006b06188c000b7a06118d000c2c1903077b000d037b000d928b000e198b000f3b7a00c0000000

<< 9000 (should be 009000)



[-] LOAD - StaticField (FAILED due to last failed LOAD)

>> 80e80006350800.....

<< 6A86 (because prev. p2=05 is not loaded)

Applet source code:

package com.github.mrlnc.HelloSTK2;

import javacard.framework.*;
import sim.toolkit.*;

/*
Originally from: https://git.osmocom.org/sim/hello-stk/tree/hello-stk/src/org/toorcamp/HelloSTK/HelloSTK.java
*/

public class HelloSTK2 extends Applet implements ToolkitInterface, ToolkitConstants {
    // DON'T DECLARE USELESS INSTANCE VARIABLES! They get saved to the EEPROM,
    // which has a limited number of write cycles.
    private byte helloMenuItem;
    
    private HelloSTK2() {
        // This is the interface to the STK applet registry (which is separate
        // from the JavaCard applet registry!)
        ToolkitRegistry reg = ToolkitRegistry.getEntry();
        byte[] menuItemText = new byte[] { 'H', 'e', 'l', 'l', 'o'};
    
        // Define the applet Menu Entry
        helloMenuItem = reg.initMenuEntry(menuItemText, (short)0, (short)menuItemText.length,
                PRO_CMD_SELECT_ITEM, false, (byte)0, (short)0);
    }
    
    // This method is called by the card when the applet is installed. You must
    // instantiate your applet and register it here.
    public static void install(byte[] bArray, short bOffset, byte bLength) {
        HelloSTK2 applet = new HelloSTK2();
        applet.register();
    }
    
    // This processes APDUs sent directly to the applet. For STK applets, this
    // interface isn't really used.
    public void process(APDU arg0) throws ISOException {
        // ignore the applet select command dispached to the process
        if (selectingApplet())
            return;
    }

    // This processes STK events.
    public void processToolkit(byte event) throws ToolkitException {
        EnvelopeHandler envHdlr = EnvelopeHandler.getTheHandler();

        if (event == EVENT_MENU_SELECTION) {
            byte selectedItemId = envHdlr.getItemIdentifier();

            if (selectedItemId == helloMenuItem) {
                byte[] welcomeMsg = new byte[] { 'W', 'e', 'l', 'c', 'o', 'm', 'e' };
                ProactiveHandler proHdlr = ProactiveHandler.getTheHandler();
                proHdlr.initDisplayText((byte)0, DCS_8_BIT_DATA, welcomeMsg, (short)0, 
                        (short)(welcomeMsg.length));
                proHdlr.send();
            }
        }
    }
}

UPDATE: Raw CAP bytes in order (Header, Directory, Import, Applet, Class, Method, StaticField, ConstantPool, RefLocation, Descriptor)

['01000fdecaffed010204000105d07002ca44',
'02001f000f001f000c00280036001800aa000a001200000003000000000000030100', '04002803030107a0000000620101060210a0000000090003ffffffff8910710002000107a0000000620001',
 '03000c0108d07002ca449001010039',
 '06001843800301ff00070200000048005280020081010108810000', '0700aa000912188c00038d00012c08900b3d031048383d041065383d05106c383d06106c383d07106f382d18191a031a9210240303038b000288007a02318f00043d8c00052e1b8b00067a0120188b000760037a7a06248d00082d1d10076b4e1a8b0009321fae006b441007900b3d031057383d041065383d05106c383d061063383d07106f383d08106d383d100610653828048d000a2805150503071504031504928b000b15058b000c3b7a',
 '08000a00000000000000000000', '050036000d02000000068109000381090b0680030001000000060000010380030103800303068108000381080c06810a0003810a1303810a16',
 '0900120002372d000c05032c08040507090a330f05',
 '0b0003000000']

UPDATE 2: I'm sending via ENVELOPE commands. KIC1,KID1 encryptions are used. They work with RFM and some RAM commands like GET STATUS etc. Here is the code for INSTALL (for load) command:

def install_for_load(self, caps, exe_aid):
        # 11.5.2.3.7 INSTALL Command Parameters, page 174
        cap_data = "".join(caps)
        apdu = "".join([
            "80",   # CLS
            "e6",   # INSTR
            "02",   # p1 0b000000_1_0 ; for load
            "00",   # p2
            "%02x", # p3 ; Lc
        ])
        # sec_aid = "a000000003000000"  # GP211 Security Domain AID
        data = "".join([
            "%02x" % int(len(exe_aid)/2),
            exe_aid,
            "00",   # security domain aid (same domain)
            # "%02x" % int(len(sec_aid)/2), # security domain aid (same domain)
            # sec_aid,

            "00",   # load file data block hash
            "%02x", # load params length
        ])
        load_params_tl = "".join([
            "ef",   # Load Parameters: System Specific Parameters tag
            "%02x", # length of load_params
        ])
        load_params_v = "".join([
            "c6",   # Non-volatile code Minimum Memory requirement. load params (mandatory)
            "02",   # length
            "%04x" % int(len(cap_data)/2),  # code size HelloSTK2.cap size.

            # "c7", # volatile data minimum memory required (ram) - optional
            # "02", # length
            # "00ff",

            # "c8", # non-volatile data minimum memory required - optional
            # "02",
            # "00ff"
        ])
        load_params = load_params_tl % int(len(load_params_v)/2) + load_params_v
        data = data % int(len(load_params)/2) + load_params
        data += "00"    # load token
        apdu = apdu % int(len(data)/2) + data
        apdu += "00c0000000"    # C-MAC + Le
        return apdu
  • BTW: What tool are you using for OTA SMS-DELIVER? – k_o_ Feb 05 '22 at 00:36
  • Implemented from scratch myself. GP's GlobalPlatformPro is not working for me. so spent 100+ hours reading ETSI documentation and made a script, better than shadysim,py – user18080531 Feb 05 '22 at 06:49

1 Answers1

0

You can have a look into the GlobalPlatform project loader. It is LPGL, so directly using it as C code in other non LGPL projects is not possible.

[!!!!] LOAD - Method (FAILED)

80e800057007006d000911188c00048d00012c18197b0002037b00029210240303038b000388007a02318f00053d8c00062e1b8b00077a0120188b000860037a7a02228d00092d1d10076b101a8b000a321fae006b06188c000b7a06118d000c2c1903077b000d037b000d928b000e198b000f3b7a00c0000000

<< 9000 (should be 009000)

I assume that the log is the unwrapped logged before the commands are prepared for SCP80. A response of 9000 in OTA means usually "I received the command, but I don't know what to do with it". Is this 9000 command from the ENVELOPE response on the card? Are you sending the data over an SMPP connection?

Can you try your loading code with a simple HelloWorld Applet without any STK? It could be that your card is not supporting a version you are linking against, e.g. different ETSI releases. Then some method might not be supported and the loading fails.

k_o_
  • 5,143
  • 1
  • 34
  • 43
  • thanks, checking @k_o_, just in general, separate components are preferable way to LOAD, right? I wonder why concatenated components do not work at all. And again, C482xxyy is not explained in that GP's loadfile.c, sad – user18080531 Feb 05 '22 at 07:29
  • I've checked that file. It builds the same APDUs as in my question. Here are RAW bytes, perhaps you can run it on your own simcard and try if it works. (posted in updated question) – user18080531 Feb 05 '22 at 09:03
  • And also perhaps it is due to USIM card itself? (so if anyone checks CAP bytes that I've posted in updated question - it would localize my issue). Here are Load files and Modules I get via GET DATA – user18080531 Feb 05 '22 at 09:19
  • Could you try to use your loader code and take the resulting file with a standard OS tool (gpshell, GP Pro, ..)? Or do you not own the SCP02/03 channel keys and have only access to the OTA SCP80 keys? This would isolate problems from your OTA code (e.g. incorrect segmentation, counters, Kics, Kids, etc) from the load file structure. In additon you could try to generate a JCA file from the cap file (https://www.oracle.com/java/technologies/java-card/developing-javacard-applet.html#jcafl). The JA fiel might show the structure of the components already. – k_o_ Feb 06 '22 at 01:16
  • The CAP file (the extracted CAP file) should only be checked by the GP environment. If you are using USAT code then it would also be delegated to the USAT logic (menu, events, OTA settings, file permissions, etc). – k_o_ Feb 06 '22 at 01:18
  • > just in general, separate components are preferable way to LOAD, right No. Just sent as many as it fits into an APDU / payload. The card will assemble it. Sending each component in a single SMS would be overkill and slow. – k_o_ Feb 06 '22 at 01:19
  • See my updated answer also. – k_o_ Feb 06 '22 at 01:32
  • Yes, these are raw plaintext APDUs, I wrap them later in secured data with KIC1, KID1, and TAR for RAM. This is working. Moreover, if first 4 components loaded separately work as well and fails only on 5th - Method, means bottleneck something is different. Question: 1) How to differ whether it's GP or USAT environment? By SecDomain selected in INSTALL for load? 2) Could you please check this information as well? [https://pastebin.com/8AcZvVDR](https://pastebin.com/8AcZvVDR) There is no uicc.sim.toolkit package, if I search it via A000000087 RID. But A000000009 sim.toolkit should be enough? – user18080531 Feb 06 '22 at 09:35
  • I've updated my question again, and posted with INSTALL for load command as well. It is wrapped with ENVELOPE + encryption. – user18080531 Feb 06 '22 at 12:57
  • See my comment about using a simple HelloWorld applet first. – k_o_ Feb 06 '22 at 13:53
  • Thanks, I've tried [helloworld.cap](https://github.com/sigma/globalplatform/blob/master/gpshell/helloworld.cap) with my script and here are APDUs: [https://pastebin.com/BYSSjBjU](https://pastebin.com/BYSSjBjU), it failed at Method as well. – user18080531 Feb 06 '22 at 14:25
  • k_o_, thanks, your advise with converting to IJC helped me debug and understand the CAP structure better. I've also refactored my loader code from scratch, so this is really helpful [link](https://sourceforge.net/p/globalplatform/wiki/GPShell/?version=9#how-to-convert-a-cap-file-into-a-ijc-bin-file) – user18080531 Feb 06 '22 at 23:47