open-keychain/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenUtilsTest.java

253 lines
13 KiB
Java

/*
* Copyright (C) 2016 Nikita Mikhailov <nikita.s.mikhailov@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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
*/
}