OpenPGPCardTest.java

/*
 * Copyright 2023 Emmanuel Bourg
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.jsign.jca;

import java.io.File;
import java.security.PrivateKey;
import java.util.Set;
import javax.crypto.Cipher;
import javax.smartcardio.CardException;

import org.bouncycastle.util.encoders.Hex;
import org.junit.Test;

import net.jsign.PrivateKeyUtils;

import static org.junit.Assert.*;
import static org.junit.Assume.*;

public class OpenPGPCardTest {

    public static void assumeCardPresent() {
        try {
            assumeTrue("OpenPGP card not found", SmartCard.getTerminal("Nitrokey") != null);
        } catch (CardException e) {
            assumeNoException("OpenPGP card not found", e);
        }
    }

    @Test
    public void testGetCard() throws Exception {
        assumeCardPresent();

        assertNotNull("card not found", OpenPGPCard.getCard());
    }

    @Test
    public void testSignWithSignatureKey() throws Exception {
        testSign(OpenPGPCard.Key.SIGNATURE);
    }

    @Test
    public void testSignWithAuthenticationKey() throws Exception {
        testSign(OpenPGPCard.Key.AUTHENTICATION);
    }

    @Test
    public void testSignWithEncryptionKey() throws Exception {
        testSign(OpenPGPCard.Key.ENCRYPTION);
    }

    public void testSign(OpenPGPCard.Key key) throws Exception {
        assumeCardPresent();

        String message = "Hello OpenPGP card";

        OpenPGPCard pgpcard = OpenPGPCard.getCard();
        assertNotNull("card not found", pgpcard);
        pgpcard.verify("123456");

        if (key == OpenPGPCard.Key.ENCRYPTION) {
            assumeTrue("OpenPGP card version 3+ required", pgpcard.getVersion() > 3);
        }

        byte[] result = pgpcard.sign(key, message.getBytes());

        assertNotNull("result", result);
        assertEquals("result length (bits)", 2048, result.length * 8);

        File privateKeyFile = new File("target/test-classes/keystores/privatekey.pkcs1.pem");
        PrivateKey privateKey = PrivateKeyUtils.load(privateKeyFile, null);

        // encrypt the message with the private key
        Cipher encryptCipher = Cipher.getInstance("RSA");
        encryptCipher.init(Cipher.ENCRYPT_MODE, privateKey);
        encryptCipher.update(message.getBytes());
        byte[] expected = encryptCipher.doFinal();

        assertArrayEquals("encrypted message", expected, result);
    }

    @Test
    public void testGetData() throws Exception {
        assumeCardPresent();

        OpenPGPCard pgpcard = OpenPGPCard.getCard();
        assertNotNull("card not found", pgpcard);

        byte[] result = pgpcard.getData(0x004F);
        assertNotNull("result", result);
        assertEquals("result length", 16, result.length);
    }

    @Test
    public void testGetAID() throws Exception {
        assumeCardPresent();

        OpenPGPCard pgpcard = OpenPGPCard.getCard();
        assertNotNull("card not found", pgpcard);

        byte[] result = pgpcard.getAID();
        assertNotNull("result", result);
        assertEquals("result length", 16, result.length);
        assertEquals("AID", "D27600012401", Hex.toHexString(result).substring(0, 12).toUpperCase());
    }

    @Test
    public void testGetVersion() throws Exception {
        assumeCardPresent();

        OpenPGPCard pgpcard = OpenPGPCard.getCard();
        assertNotNull("card not found", pgpcard);

        float version = pgpcard.getVersion();
        assertTrue("version < 2", version > 2);
    }

    @Test
    public void testGetAvailableKeys() throws Exception {
        assumeCardPresent();

        OpenPGPCard pgpcard = OpenPGPCard.getCard();
        assertNotNull("card not found", pgpcard);

        Set<OpenPGPCard.Key> keys = pgpcard.getAvailableKeys();
        assertNotNull("keys", keys);
        assertEquals("number of keys", pgpcard.supportsManageSecurityEnvironment() ? 3 : 2, keys.size());
    }

    @Test
    public void testGetKeyInfo() throws Exception {
        assumeCardPresent();

        OpenPGPCard pgpcard = OpenPGPCard.getCard();
        assertNotNull("card not found", pgpcard);

        OpenPGPCard.KeyInfo keyInfo = pgpcard.getKeyInfo(OpenPGPCard.Key.SIGNATURE);
        assertNotNull("key info", keyInfo);

        String fingerprint = Hex.toHexString(keyInfo.fingerprint).toUpperCase();
        assertEquals("Fingerprint", "97147A24770EFC11A41979BA5D37E9FA3D904376", fingerprint);
    }

    @Test
    public void testSelectData() throws Exception {
        assumeCardPresent();

        OpenPGPCard pgpcard = OpenPGPCard.getCard();
        assertNotNull("card not found", pgpcard);

        assumeTrue("OpenPGP card version 3+ required", pgpcard.getVersion() > 3);

        pgpcard.selectData(0x7F21, 0);
        byte[] result = pgpcard.getData(0x7F21);
        assertNotEquals("result is empty", 0, result.length);

        pgpcard.selectData(0x7F21, 1);
        result = pgpcard.getData(0x7F21);
        assertEquals("result is not empty", 0, result.length);

        pgpcard.selectData(0x7F21, 2);
        result= pgpcard.getData(0x7F21);
        assertEquals("result is not empty", 0, result.length);
    }

    @Test
    public void testGetAuthenticationCertificate() throws Exception {
        assumeCardPresent();

        OpenPGPCard pgpcard = OpenPGPCard.getCard();
        assertNotNull("card not found", pgpcard);

        byte[] result = pgpcard.getCertificate(OpenPGPCard.Key.AUTHENTICATION);
        assertNotNull("certificate", result);
    }

    @Test
    public void testPutData() throws Exception {
        assumeCardPresent();

        OpenPGPCard pgpcard = OpenPGPCard.getCard();
        assertNotNull("card not found", pgpcard);
        pgpcard.verify(0x00, 0x83, "12345678");

        byte[] backup = pgpcard.getData(0x7F21);
        byte[] data = new byte[backup.length];
        pgpcard.putData(0x7F21, data);

        assertArrayEquals("new data", data, pgpcard.getData(0x7F21));

        pgpcard.putData(0x7F21, backup);

        assertArrayEquals("backup data", backup, pgpcard.getData(0x7F21));
    }

    @Test
    public void testSupportsManageSecurityEnvironment() throws Exception {
        assumeCardPresent();

        OpenPGPCard pgpcard = OpenPGPCard.getCard();
        assertNotNull("card not found", pgpcard);

        assertTrue("MSE is not supported", pgpcard.supportsManageSecurityEnvironment());
    }
}