makinako / OpenFIPS201

An open source reference card application for NIST FIPS 201-2 / NIST SP800-73-4, targeting Javacard 3.0.4+
Other
71 stars 34 forks source link

Provisioning card without secure channel #68

Open mistial-dev opened 1 year ago

mistial-dev commented 1 year ago

I'm working on some unit testing for use with OpenFIPS201, and am using jCardSim. The open source version does not support Global Platform or Secure Channel.

Looking through the code, I can see that administrative keys can be used for card administration, but I do not see a way to create an administrator key without using a secure channel.

Does the administrative user have to be defined in secure channel before it can be used for other operations?

makinako commented 1 year ago

Hi @mistial-dev, yes this is correct there is no way to avoid SCP at all in this case for a few reasons:

Philosophically speaking, we don't encourage the 9B key at all since it provides no encryption or integrity for the subsequent management commands. It is retained for backwards-compatibility, but as you have seen it really only helps you post-issuance.

I ran into the same issue with testing as you have. It is unfortunate that jCardSim doesn't support it but hey they have to make money somehow in a pretty niche market so I can't totally blame them :)

Our unit testing was all inside an old commercial tool and I tried a few different approaches, but ultimately the testing framework became Zephyr Scale (on JIRA) to define the tests generically and then export them to NXP JCShell scripts. This gave us SCP support ability to easily use the P71D600 simulator (which helps with FIPS testing).

It's a good tool, but I'm not happy that it is proprietary and requires an NXP NDA and a few hoops to get! The Zephyr Scale aspect gives the option to de-couple from this when a good solution is found/developed and one day we will, but for now it's a pragmatic option for testing.

martinpaljak commented 1 year ago

Looking at the use of GPSystem in this applet (GPSystem.getSecureChannel(), resetSecurity(), getSecurityLevel(), unwrap(), processSecurity()) then "mocking" this as a java code for jcardsim is max half day of effort. Don't know about CVM but probably the same.

makinako commented 1 year ago

Looking at the use of GPSystem in this applet (GPSystem.getSecureChannel(), resetSecurity(), getSecurityLevel(), unwrap(), processSecurity()) then "mocking" this as a java code for jcardsim is max half day of effort. Don't know about CVM but probably the same.

It's true, but only if we had a Java implementation of SCP to base it off :) For the accreditation process we are locked into the current JCShell path but if we had this available for JCardSim it would be a logical next step for the generic tests (Which are most of them)

mistial-dev commented 1 year ago

Looking at the use of GPSystem in this applet (GPSystem.getSecureChannel(), resetSecurity(), getSecurityLevel(), unwrap(), processSecurity()) then "mocking" this as a java code for jcardsim is max half day of effort. Don't know about CVM but probably the same.

I put around that much time into trying to get mocking working before asking about alternate ways. I’m not an expert Java developer, and the techniques I use in dot net don’t really translate well. jCardSim seems to want a class, and mocking synthesizes a new class. I got as far as extracting the instantiated class from the jCardSim runtime, to use with the mocking framework, but then had no way to feed it back into jCardSim.

My plan was to just make a fake security domain that would pass through APDUs untouched. That way I could keep my Gpshell scripts otherwise unmodified.

mistial-dev commented 1 year ago

ChatGPT proved surprisingly helpful.

    @org.junit.jupiter.api.Test
    void testConfiguration() {
        try (MockedStatic<GPSystem> mocked = Mockito.mockStatic(GPSystem.class)) {
            SecureChannel mockedSecureChannel = Mockito.mock(SecureChannel.class);

            // Mock getSecurityLevel() to return a value that indicates that the card is authenticated, has decryption, and has MAC
            Mockito.when(mockedSecureChannel.getSecurityLevel()).thenReturn((byte) (SecureChannel.AUTHENTICATED | SecureChannel.C_DECRYPTION | SecureChannel.C_MAC));

            // Mock getSecureChannel() to return the mocked SecureChannel
            mocked.when(GPSystem::getSecureChannel).thenReturn(mockedSecureChannel);

            // Mock `short processSecurity(APDU apdu)` to return 0 bytes of data.
            Mockito.when(mockedSecureChannel.processSecurity(Mockito.any())).thenReturn((short)0);

            // Mock `short unwrap(byte[] data, short offset, short length)` to return the length (from the third argument)
            Mockito.when(mockedSecureChannel.unwrap(Mockito.any(byte[].class), Mockito.anyShort(), Mockito.anyShort())).thenAnswer(invocation -> invocation.getArgument(2));

            // Check for a success response
            byte[] dataBytes = simulator.selectAppletWithResult(OF201_AID);

            // The last two bytes of the response should be 0x9000.  Verify that.
            assertArrayEquals(new byte[]{(byte) 0x90, (byte) 0x00}, new byte[]{dataBytes[dataBytes.length - 2], dataBytes[dataBytes.length - 1]});

            // Define the configuration command in hex
            String configurationCommand = "E4 DB 3F 00 5D 68 5B A0 24 80 01 FF 81 01 00 82 01 00 83 01 00 84 01 06 85 01 08 86 01 06 87 01 " +
                    "05 88 01 00 89 01 04 8A 01 00 8B 01 00 A1 12 80 01 FF 81 01 00 82 01 08 83 01 06 84 01 05 85 01 " +
                    "00 A2 03 80 01 00 A3 03 80 01 00 A4 15 80 01 00 81 01 00 82 01 00 83 01 00 84 01 FF 85 01 00 86 " +
                    "01 00";

            // Execute the configuration command and get the response
            CommandAPDU configurationCommandAPDU = new CommandAPDU(hexStringToByteArray(configurationCommand));
            ResponseAPDU configurationResponseAPDU = simulator.transmitCommand(configurationCommandAPDU);

            // Verify that the response is 0x9000
            assertArrayEquals(new byte[]{(byte) 0x90, (byte) 0x00}, configurationResponseAPDU.getBytes());
        }
    }
no-usernames-left commented 3 months ago

ChatGPT proved surprisingly helpful.

I wonder whose code that is, and which license it was under.

mistial-dev commented 2 months ago

I wonder whose code that is, and which license it was under.

Much of it likely is derived from the Mokito documentation, or the GlobalPlatform documentation (which has its own license, but is likely not relevant given that the use is functional, rather than creative). The Mokito library itself (which may be where some of the training is from) is MIT.

Much of it is extremely formulaic and functional, and was guided prompting. It wasn't like someone had written similar code that was just prompted.

As an example, when I asked it to mock processSecurity to return 0 bytes of data, it picked the most logical and straightforward way to do it. The comments are mine, and were used for the prompting.

            // Mock `short processSecurity(APDU apdu)` to return 0 bytes of data.
            Mockito.when(mockedSecureChannel.processSecurity(Mockito.any())).thenReturn((short)0);

It's similar to the phonebook - you can't copyright a phone number, only the creative aspects of an arrangement of phone numbers. The choice of what to mock, how to mock it, why to mock it, representation of the data (in hex), the command that was run, and the response was all mine. In other words, the creative elements.

So, if you want to know "whose code that is", it's mine, because the elements that are copyrightable are mine.