/* * Copyright (C) 2016 Nikita Mikhailov * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.sufficientlysecure.keychain.securitytoken; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.robolectric.shadows.ShadowLog; import org.sufficientlysecure.keychain.KeychainTestRunner; import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransportException; import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo.TokenType; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; import java.security.interfaces.RSAPrivateCrtKey; @RunWith(KeychainTestRunner.class) public class SecurityTokenUtilsTest extends Mockito { @Before public void setUp() { ShadowLog.stream = System.out; } @Test public void testEncodeLength() throws Exception { // One byte Assert.assertArrayEquals(new byte[]{0x00}, SecurityTokenUtils.encodeLength(0)); Assert.assertArrayEquals(new byte[]{0x01}, SecurityTokenUtils.encodeLength(1)); Assert.assertArrayEquals(new byte[]{0x7f}, SecurityTokenUtils.encodeLength(127)); // Two bytes Assert.assertArrayEquals(new byte[]{(byte) 0x81, (byte) 0x80}, SecurityTokenUtils.encodeLength(128)); Assert.assertArrayEquals(new byte[]{(byte) 0x81, (byte) 0xFF}, SecurityTokenUtils.encodeLength(255)); // Three bytes Assert.assertArrayEquals(new byte[]{(byte) 0x82, (byte) 0x01, 0x00}, SecurityTokenUtils.encodeLength(256)); Assert.assertArrayEquals(new byte[]{(byte) 0x82, (byte) 0xFF, (byte) 0xFF}, SecurityTokenUtils.encodeLength(65535)); // Four bytes Assert.assertArrayEquals(new byte[]{(byte) 0x83, (byte) 0x01, (byte) 0x00, (byte) 0x00}, SecurityTokenUtils.encodeLength(65536)); Assert.assertArrayEquals(new byte[]{(byte) 0x83, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}, SecurityTokenUtils.encodeLength(16777215)); } @Test(expected = IllegalArgumentException.class) public void testEncodeLengthNegative() throws Exception { SecurityTokenUtils.encodeLength(-1); } @Test(expected = IllegalArgumentException.class) public void testEncodeLengthTooBig() throws Exception { SecurityTokenUtils.encodeLength(256 * 256 * 256); } @Test public void testWriteBits() { ByteArrayOutputStream stream = new ByteArrayOutputStream(); SecurityTokenUtils.writeBits(stream, new BigInteger("0"), 10); Assert.assertArrayEquals(new byte[10], stream.toByteArray()); stream.reset(); SecurityTokenUtils.writeBits(stream, new BigInteger("0"), 1); Assert.assertArrayEquals(new byte[1], stream.toByteArray()); stream.reset(); SecurityTokenUtils.writeBits(stream, new BigInteger("65537"), 3); Assert.assertArrayEquals(new byte[]{1, 0, 1}, stream.toByteArray()); stream.reset(); SecurityTokenUtils.writeBits(stream, new BigInteger("128"), 1); Assert.assertArrayEquals(new byte[]{(byte) 128}, stream.toByteArray()); stream.reset(); SecurityTokenUtils.writeBits(stream, new BigInteger("65537"), 4); Assert.assertArrayEquals(new byte[]{0, 1, 0, 1}, stream.toByteArray()); stream.reset(); SecurityTokenUtils.writeBits(stream, new BigInteger("65537"), 11); Assert.assertArrayEquals(new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1}, stream.toByteArray()); } @Test(expected = IllegalArgumentException.class) public void testWriteBitsInvalidValue() { ByteArrayOutputStream stream = new ByteArrayOutputStream(); SecurityTokenUtils.writeBits(stream, new BigInteger("-1"), 1); } @Test(expected = IllegalArgumentException.class) public void testWriteBitsInvalidBits() { ByteArrayOutputStream stream = new ByteArrayOutputStream(); SecurityTokenUtils.writeBits(stream, new BigInteger("1"), 0); } @Test(expected = IllegalArgumentException.class) public void testWriteBitsDoesntFit() { ByteArrayOutputStream stream = new ByteArrayOutputStream(); SecurityTokenUtils.writeBits(stream, new BigInteger("65537"), 2); } @Test public void testPrivateKeyTemplateSimple2048() throws Exception { RSAPrivateCrtKey key2048 = mock(RSAPrivateCrtKey.class); byte[] tmp = new byte[128]; Arrays.fill(tmp, (byte) 0x11); when(key2048.getPrimeP()).thenReturn(new BigInteger(tmp)); Arrays.fill(tmp, (byte) 0x12); when(key2048.getPrimeQ()).thenReturn(new BigInteger(tmp)); BigInteger exp = new BigInteger("65537"); when(key2048.getPublicExponent()).thenReturn(exp); Assert.assertArrayEquals( Hex.decode("4d820115" + // Header TL "a400" + // CRT "7f4808" + // 8 bytes "9103" + // e "928180" + // p "938180" + // q "5f48820103" + "010001" + "1111111111111111111111111111111111111111111111111111111111111111" + "1111111111111111111111111111111111111111111111111111111111111111" + "1111111111111111111111111111111111111111111111111111111111111111" + "1111111111111111111111111111111111111111111111111111111111111111" + "1212121212121212121212121212121212121212121212121212121212121212" + "1212121212121212121212121212121212121212121212121212121212121212" + "1212121212121212121212121212121212121212121212121212121212121212" + "1212121212121212121212121212121212121212121212121212121212121212" ), SecurityTokenUtils.createRSAPrivKeyTemplate(key2048, KeyType.AUTH, RsaKeyFormat.getInstance(2048, exp.bitLength(), RsaKeyFormat.RsaImportFormat.STANDARD))); } @Test public void testCardCapabilities() throws UsbTransportException { CardCapabilities capabilities; // Yk neo capabilities = new CardCapabilities(Hex.decode("007300008000000000000000000000"), TokenType.YUBIKEY_NEO); Assert.assertEquals(capabilities.hasChaining(), true); Assert.assertEquals(capabilities.hasExtended(), false); Assert.assertEquals(capabilities.hasLifeCycleManagement(), true); // Yk 4 capabilities = new CardCapabilities(Hex.decode("0073000080059000"), TokenType.YUBIKEY_4_5); Assert.assertEquals(capabilities.hasChaining(), true); Assert.assertEquals(capabilities.hasExtended(), false); Assert.assertEquals(capabilities.hasLifeCycleManagement(), true); // Nitrokey pro capabilities = new CardCapabilities(Hex.decode("0031c573c00140059000"), TokenType.NITROKEY_PRO); Assert.assertEquals(capabilities.hasChaining(), false); Assert.assertEquals(capabilities.hasExtended(), true); Assert.assertEquals(capabilities.hasLifeCycleManagement(), true); // GNUK without Life Cycle Management capabilities = new CardCapabilities(Hex.decode("00318473800180009000"), TokenType.GNUK_OLD); Assert.assertEquals(capabilities.hasChaining(), true); Assert.assertEquals(capabilities.hasExtended(), false); Assert.assertEquals(capabilities.hasLifeCycleManagement(), false); // GNUK with Life Cycle Management: ./configure --enable-factory-reset capabilities = new CardCapabilities(Hex.decode("00318473800180059000"), TokenType.GNUK_OLD); Assert.assertEquals(capabilities.hasChaining(), true); Assert.assertEquals(capabilities.hasExtended(), false); Assert.assertEquals(capabilities.hasLifeCycleManagement(), true); // Secalot capabilities = new CardCapabilities(Hex.decode("0031C573C00140009000"), TokenType.SECALOT); Assert.assertEquals(capabilities.hasChaining(), false); Assert.assertEquals(capabilities.hasExtended(), true); Assert.assertEquals(capabilities.hasLifeCycleManagement(), true); } @Test public void testOpenPgpCapabilities() throws IOException { byte[] data; // Yk-neo applet data = Hex.decode("6e81de4f10d27600012401020000000000000100005f520f0073000080000000" + "000000000000007381b7c00af00000ff04c000ff00ffc106010800001103c206" + "010800001103c306010800001103c407007f7f7f030303c53cce1d5a2158a4f1" + "8a7d853394e9e4c9efb468055fae77ab8ea3c68f053930e35f658fb62176e901" + "df03d249cac1e82f8289c7ffabe1a1af868620fa56c63c000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000cd0c5741e8695741e8695741e8" + "69"); OpenPgpCapabilities caps = OpenPgpCapabilities.fromBytes(data); Assert.assertEquals(caps.isHasSM(), true); } /* yk-neo 6e81de4f10d27600012401020000000000000100005f520f0073000080000000000000000000007381b7c00af00000ff04c000ff00ffc106010800001103c206010800001103c306010800001103c407007f7f7f030303c53cce1d5a2158a4f18a7d853394e9e4c9efb468055fae77ab8ea3c68f053930e35f658fb62176e901df03d249cac1e82f8289c7ffabe1a1af868620fa56c63c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd0c5741e8695741e8695741e869 6e81de4f10d27600012401020000000000000100005f520f0073000080000000000000000000007381b7c00af00000ff04c000ff00ffc106010800001103c206010800001103c306010800001103c407007f7f7f030303c53c1a2f6436e422cd4f37f9e95775195c4984609678fbc5dd767789f1b304c9fba6f68a68ac563f71ae0000000000000000000000000000000000000000c63c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd0c5744793c5744793c00000000 007300008000000000000000000000 yk neo ng 6e81de4f10d27600012401020000060364703400005f520f0073000080000000000000000000007381b7c00af00000ff04c000ff00ffc106010800001103c206010800001103c306010800001103c407007f7f7f030302c53c7fdb876b9ebc534d674e63b7400207a0f9c34a50413b851137ced1b45d1b66526b4e93a9d5c4eed3585c34e7d38ec07d50f26e0554baa1867a038ed2c63c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd0c5711219f5711219f5711219f 6e81de4f10d27600012401020000060364703400005f520f0073000080000000000000000000007381b7c00af00000ff04c000ff00ffc106010800001103c206010800001103c306010800001103c407007f7f7f030302c53c7fdb876b9ebc534d674e63b7400207a0f9c34a50413b851137ced1b45d1b66526b4e93a9d5c4eed3585c34e7d38ec07d50f26e0554baa1867a038ed2c63c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd0c5711219f5711219f5711219f 6e81dd4f10d27600012401020000060301402200005f520f0073000080000000000000000000007300c00af00000ff04c000ff00ffc106010800001103c206010800001103c306010800001103c407007f7f7f030303c53c98b23401029d2666e129e0b97e2f7e6d1026a77c198dd46ed68a107aada3a267a82e3157a024f7b4be59004e379abaf8fe7abf28deacdd05542a5acac63c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd0c5741f0165741f0165741f016 yk4 6e81dd4f10d27600012401020100060417430400005f520800730000800590007f74038101207381b7c00a3c00000004c000ff00ffc106010800001100c206010800001100c306010800001100c407ff7f7f7f030003c53c3a9150768282a1ccf13ee80c4ef0217876ba06001ee6fc5c633b764583cc2d7144b76c0c83f3bdf6671d04d2b6ca89fbbd3940ea7203396c7b1ff1bac63c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd0c5741ef475741ef475741ef47 0073000080059000 nitrokey pro 4f10d2760001240102010005000038a100005f520a0031c573c001400590007381b7c00a7c000800080008000800c106010800002000c206010800002000c306010800002000c40700202020030003c53c5dcf5fc947201ef20636c448131e71ffecf7cbf7d3ed4826257b1340c5aedd2b15530eecf173fa890859306d64641ab01be054c3d6795052cedc3c38c63c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd0c5741eec65741eec65741eec6 0031c573c00140059000 */ }