add rudimentary unit test for SecurityTokenConnection
This commit is contained in:
parent
c295a6815f
commit
2812f07d34
|
@ -23,6 +23,7 @@ package org.sufficientlysecure.keychain.securitytoken;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import org.bouncycastle.asn1.ASN1Encodable;
|
import org.bouncycastle.asn1.ASN1Encodable;
|
||||||
import org.bouncycastle.asn1.ASN1Integer;
|
import org.bouncycastle.asn1.ASN1Integer;
|
||||||
|
@ -99,16 +100,18 @@ public class SecurityTokenConnection {
|
||||||
public static SecurityTokenConnection getInstanceForTransport(Transport transport, Passphrase pin) {
|
public static SecurityTokenConnection getInstanceForTransport(Transport transport, Passphrase pin) {
|
||||||
if (sCachedInstance == null || !sCachedInstance.isPersistentConnectionAllowed() ||
|
if (sCachedInstance == null || !sCachedInstance.isPersistentConnectionAllowed() ||
|
||||||
!sCachedInstance.isConnected() || !sCachedInstance.mTransport.equals(transport)) {
|
!sCachedInstance.isConnected() || !sCachedInstance.mTransport.equals(transport)) {
|
||||||
sCachedInstance = new SecurityTokenConnection(transport, pin);
|
sCachedInstance = new SecurityTokenConnection(transport, pin, new OpenPgpCommandApduFactory());
|
||||||
}
|
}
|
||||||
return sCachedInstance;
|
return sCachedInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SecurityTokenConnection(@NonNull Transport transport, @NonNull Passphrase pin) {
|
@VisibleForTesting
|
||||||
|
SecurityTokenConnection(@NonNull Transport transport, @NonNull Passphrase pin,
|
||||||
|
OpenPgpCommandApduFactory commandFactory) {
|
||||||
this.mTransport = transport;
|
this.mTransport = transport;
|
||||||
this.mPin = pin;
|
this.mPin = pin;
|
||||||
|
|
||||||
commandFactory = new OpenPgpCommandApduFactory();
|
this.commandFactory = commandFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getHolderName(byte[] name) {
|
private String getHolderName(byte[] name) {
|
||||||
|
@ -172,7 +175,8 @@ public class SecurityTokenConnection {
|
||||||
/**
|
/**
|
||||||
* Connect to device and select pgp applet
|
* Connect to device and select pgp applet
|
||||||
*/
|
*/
|
||||||
private void connectToDevice(Context context) throws IOException {
|
@VisibleForTesting
|
||||||
|
void connectToDevice(Context context) throws IOException {
|
||||||
// Connect on transport layer
|
// Connect on transport layer
|
||||||
mCardCapabilities = new CardCapabilities();
|
mCardCapabilities = new CardCapabilities();
|
||||||
|
|
||||||
|
@ -187,8 +191,8 @@ public class SecurityTokenConnection {
|
||||||
throw new CardException("Initialization failed!", response.getSw());
|
throw new CardException("Initialization failed!", response.getSw());
|
||||||
}
|
}
|
||||||
|
|
||||||
mOpenPgpCapabilities = new OpenPgpCapabilities(getData(0x00, 0x6E));
|
OpenPgpCapabilities openPgpCapabilities = new OpenPgpCapabilities(getData(0x00, 0x6E));
|
||||||
mCardCapabilities = new CardCapabilities(mOpenPgpCapabilities.getHistoricalBytes());
|
setConnectionCapabilities(openPgpCapabilities);
|
||||||
|
|
||||||
mPw1ValidatedForSignature = false;
|
mPw1ValidatedForSignature = false;
|
||||||
mPw1ValidatedForDecrypt = false;
|
mPw1ValidatedForDecrypt = false;
|
||||||
|
@ -202,7 +206,12 @@ public class SecurityTokenConnection {
|
||||||
Log.e(Constants.TAG, "failed to establish secure messaging", e);
|
Log.e(Constants.TAG, "failed to establish secure messaging", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setConnectionCapabilities(OpenPgpCapabilities openPgpCapabilities) throws IOException {
|
||||||
|
this.mOpenPgpCapabilities = openPgpCapabilities;
|
||||||
|
this.mCardCapabilities = new CardCapabilities(openPgpCapabilities.getHistoricalBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resetPin(byte[] newPin, Passphrase adminPin) throws IOException {
|
public void resetPin(byte[] newPin, Passphrase adminPin) throws IOException {
|
||||||
|
|
|
@ -266,6 +266,8 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log.d(Constants.TAG, "security token exception", e);
|
||||||
|
|
||||||
// Otherwise, all status codes are fixed values.
|
// Otherwise, all status codes are fixed values.
|
||||||
switch (status) {
|
switch (status) {
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
package org.sufficientlysecure.keychain.securitytoken;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.bouncycastle.util.encoders.Hex;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InOrder;
|
||||||
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
import org.robolectric.shadows.ShadowLog;
|
||||||
|
import org.sufficientlysecure.keychain.KeychainTestRunner;
|
||||||
|
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||||
|
|
||||||
|
import static org.mockito.Matchers.eq;
|
||||||
|
import static org.mockito.Mockito.inOrder;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
|
||||||
|
@RunWith(KeychainTestRunner.class)
|
||||||
|
public class SecurityTokenConnectionTest {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
ShadowLog.stream = System.out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_connectToDevice() throws Exception {
|
||||||
|
Transport transport = mock(Transport.class);
|
||||||
|
SecurityTokenConnection securityTokenConnection =
|
||||||
|
new SecurityTokenConnection(transport, new Passphrase("123456"), new OpenPgpCommandApduFactory());
|
||||||
|
|
||||||
|
String[] dialog = { "00a4040006d27600012401", "9000",
|
||||||
|
"00ca006e00",
|
||||||
|
"6e81de4f10d27600012401020000060364311500005f520f0073000080000000000000000000007381b7c00af" +
|
||||||
|
"00000ff04c000ff00ffc106010800001103c206010800001103c306010800001103c407007f7f7f03030" +
|
||||||
|
"3c53c4ec5fee25c4e89654d58cad8492510a89d3c3d8468da7b24e15bfc624c6a792794f15b7599915f7" +
|
||||||
|
"03aab55ed25424d60b17026b7b06c6ad4b9be30a3c63c000000000000000000000000000000000000000" +
|
||||||
|
"000000000000000000000000000000000000000000000000000000000000000000000000000000000cd0" +
|
||||||
|
"c59cd0f2a59cd0af059cd0c959000"
|
||||||
|
};
|
||||||
|
expect(transport, dialog);
|
||||||
|
|
||||||
|
|
||||||
|
securityTokenConnection.connectToDevice(RuntimeEnvironment.application);
|
||||||
|
|
||||||
|
|
||||||
|
verify(transport).connect();
|
||||||
|
verifyDialog(transport, dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getTokenInfo() throws Exception {
|
||||||
|
Transport transport = mock(Transport.class);
|
||||||
|
SecurityTokenConnection securityTokenConnection =
|
||||||
|
new SecurityTokenConnection(transport, new Passphrase("123456"), new OpenPgpCommandApduFactory());
|
||||||
|
OpenPgpCapabilities openPgpCapabilities = new OpenPgpCapabilities(
|
||||||
|
Hex.decode(
|
||||||
|
"6e81de4f10d27600012401020000060364311500005f520f0073000080000000000000000000007381b7c00af" +
|
||||||
|
"00000ff04c000ff00ffc106010800001103c206010800001103c306010800001103c407007f7f7f03" +
|
||||||
|
"0303c53c4ec5fee25c4e89654d58cad8492510a89d3c3d8468da7b24e15bfc624c6a792794f15b759" +
|
||||||
|
"9915f703aab55ed25424d60b17026b7b06c6ad4b9be30a3c63c000000000000000000000000000000" +
|
||||||
|
"000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
|
||||||
|
"000000000cd0c59cd0f2a59cd0af059cd0c95"
|
||||||
|
));
|
||||||
|
securityTokenConnection.setConnectionCapabilities(openPgpCapabilities);
|
||||||
|
|
||||||
|
String[] dialog = {
|
||||||
|
"00ca006e00",
|
||||||
|
"6e81de4f10d27600012401020000060364311500005f520f0073000080000000000000000000007381b7c00af" +
|
||||||
|
"00000ff04c000ff00ffc106010800001103c206010800001103c306010800001103c407007f7f7f03030" +
|
||||||
|
"3c53c4ec5fee25c4e89654d58cad8492510a89d3c3d8468da7b24e15bfc624c6a792794f15b7599915f7" +
|
||||||
|
"03aab55ed25424d60b17026b7b06c6ad4b9be30a3c63c000000000000000000000000000000000000000" +
|
||||||
|
"000000000000000000000000000000000000000000000000000000000000000000000000000000000cd0" +
|
||||||
|
"c59cd0f2a59cd0af059cd0c959000",
|
||||||
|
"00ca004f00",
|
||||||
|
"d27600012401020000060364311500009000",
|
||||||
|
"00ca006500",
|
||||||
|
"65095b005f2d005f3501399000",
|
||||||
|
"00ca5f5000",
|
||||||
|
"9000",
|
||||||
|
"00ca00c400",
|
||||||
|
"007f7f7f0303039000"
|
||||||
|
};
|
||||||
|
expect(transport, dialog);
|
||||||
|
|
||||||
|
|
||||||
|
securityTokenConnection.getTokenInfo();
|
||||||
|
|
||||||
|
|
||||||
|
verifyDialog(transport, dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expect(Transport transport, String... dialog) throws IOException {
|
||||||
|
for (int i = 0; i < dialog.length; i += 2) {
|
||||||
|
CommandApdu command = CommandApdu.fromBytes(Hex.decode(dialog[i]));
|
||||||
|
ResponseApdu response = ResponseApdu.fromBytes(Hex.decode(dialog[i + 1]));
|
||||||
|
when(transport.transceive(eq(command))).thenReturn(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyDialog(Transport transport, String... dialog) throws IOException {
|
||||||
|
InOrder inOrder = inOrder(transport);
|
||||||
|
for (int i = 0; i < dialog.length; i += 2) {
|
||||||
|
CommandApdu command = CommandApdu.fromBytes(Hex.decode(dialog[i]));
|
||||||
|
inOrder.verify(transport).transceive(eq(command));
|
||||||
|
}
|
||||||
|
inOrder.verifyNoMoreInteractions();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue