Skip to content

Commit

Permalink
Merge PR #156
Browse files Browse the repository at this point in the history
  • Loading branch information
AdamVe committed Aug 30, 2024
2 parents 1516cf6 + e125ff5 commit 998a263
Show file tree
Hide file tree
Showing 30 changed files with 288 additions and 75 deletions.
9 changes: 9 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
* Version 2.7.0 (released 2024-08-30)
** new
- support for communication over SCP03 and SCP11 protocols
- support for managing SCP03 and SCP11 keys through Security Domain session
** support module:
- fixed missing property values for DeviceInfo in DeviceUtil.readInfo()
** general updates:
- improved integration tests with support to run over SCP
- updated build dependencies and libraries
* Version 2.6.0 (released 2024-06-18)
** piv module:
- support for RSA3072 and RSA4096 (keys with FW 5.7+)
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ allprojects {
}

subprojects {
version = '2.6.1-SNAPSHOT'
version = '2.7.1-SNAPSHOT'
ext.pomName = "Yubico YubiKit ${project.name.capitalize()}"


Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Yubico.
* Copyright (C) 2022,2024 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,6 +16,8 @@

package com.yubico.yubikit.core.application;

import static com.yubico.yubikit.core.application.SessionVersionOverride.overrideOf;

import com.yubico.yubikit.core.Version;

/**
Expand Down Expand Up @@ -71,7 +73,7 @@ protected String getRequiredMessage() {

@Override
public boolean isSupportedBy(Version version) {
return version.major == 0 || version.compareTo(requiredVersion) >= 0;
return overrideOf(version).compareTo(requiredVersion) >= 0;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright (C) 2024 Yubico.
*
* 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 com.yubico.yubikit.core.application;

import com.yubico.yubikit.core.Version;

import javax.annotation.Nullable;

/**
* Adds support for overriding YubiKey session version number.
* <p>
* Internal use only.
*/
public class SessionVersionOverride {

@Nullable
private static Version versionOverride = null;

/**
* Internal use only.
* <p>
* Override version of connected YubiKey with the specified version.
*
* @param version version to use instead of YubiKey version. Only applies if the major version
* of the YubiKey is 0.
*/
public static void set(@Nullable Version version) {
versionOverride = version;
}

/**
* Returns an applicable override of version.
*
* @param version The version which might be overridden.
* @return Version to use.
*/
static Version overrideOf(Version version) {
return (versionOverride != null && version.major == 0)
? versionOverride
: version;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2023 Yubico.
* Copyright (C) 2020-2024 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2022 Yubico.
* Copyright (C) 2020-2022,2024 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
39 changes: 39 additions & 0 deletions doc/security_domain.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
== Security domain
yubikit-android 2.7.0 adds support for smart card communication over secure channel protocol (SCP) and management of SCP key information. The supported SCP versions are SCP03, SCP11a, SCP11b and SCP11c.

More information about the protocols can be found at the following URLs:

SCP03: https://globalplatform.org/specs-library/secure-channel-protocol-03-amendment-d-v1-2/

SCP11: https://globalplatform.org/specs-library/secure-channel-protocol-11-amendment-f/

The support is provided directly through the SDK's smart card protocol, which handles necessary handshakes and data encryption/decryption. Secure channel key management is handled by Security Domain Session.

- SCP can be used for USB and NFC connections
- because of how SCP works, communication over NFC might be less performant
- SCP03 is supported only by Yubikeys with firmware version at least 5.3.0
- SCP11 is supported on firmware versions 5.7.2 and later
- can be used over NFC or USB smart-card connection
- any YubiKey application can be used over SCP
- only NFC hardware with extended APDUs support can be used for SCP over NFC
=== Using SCP
To use SCP, an application first needs to acquire `ScpKeyParams` which contain information needed for establishing and communicating over the secured channel.

The parameters are then used when constructing a specific session object. The following snippet shows how to create a SCP PIV session.

[source,java]
----
ScpKeyParams scpKeyParams = ...;
PivSession piv = new PivSession(smartCardConnection, scpKeyParams);
// use the session
// all communication in the session is now secured through SCP
piv.verifyPin(...);
----

For working examples of SCP key management, see `SecurityDomainTests` tests suite.



Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2023 Yubico.
* Copyright (C) 2020-2024 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023 Yubico.
* Copyright (C) 2023-2024 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion piv/src/test/java/com/yubico/yubikit/piv/KeyTypeTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2023 Yubico.
* Copyright (C) 2020-2024 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package com.yubico.yubikit.support;

import com.yubico.yubikit.core.internal.Logger;
import com.yubico.yubikit.core.Transport;
import com.yubico.yubikit.core.UsbInterface;
import com.yubico.yubikit.core.UsbPid;
Expand All @@ -26,6 +25,7 @@
import com.yubico.yubikit.core.application.ApplicationNotAvailableException;
import com.yubico.yubikit.core.application.CommandException;
import com.yubico.yubikit.core.fido.FidoConnection;
import com.yubico.yubikit.core.internal.Logger;
import com.yubico.yubikit.core.otp.OtpConnection;
import com.yubico.yubikit.core.smartcard.AppId;
import com.yubico.yubikit.core.smartcard.SmartCardConnection;
Expand Down Expand Up @@ -271,6 +271,7 @@ static boolean isPreviewVersion(Version version) {
* <p>
* The <code>pid</code> parameter must be provided whenever the YubiKey is connected via USB,
* </p>
*
* @param connection {@link SmartCardConnection}, {@link OtpConnection} or
* {@link FidoConnection} connection to the YubiKey
* @param pid USB product ID of the YubiKey, can be null if unknown
Expand Down Expand Up @@ -530,11 +531,10 @@ public static String getName(DeviceInfo info, @Nullable YubiKeyType keyType) {
}

StringBuilder builder = new StringBuilder();
for (int partCount = 0; partCount < namePartsList.size(); partCount++)
{
for (int partCount = 0; partCount < namePartsList.size(); partCount++) {
String s = namePartsList.get(partCount);
builder.append(s);
if (partCount<namePartsList.size() - 1) {
if (partCount < namePartsList.size() - 1) {
builder.append(" ");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ public void testResetPassword() throws Throwable {
public void testAccountManagement() throws Throwable {
withOathSession(OathDeviceTests::testAccountManagement);
}

@Test
public void testRenameAccount() throws Throwable {
withOathSession(OathDeviceTests::testRenameAccount);
}
}

public static class Scp11bTests extends NoScpTests {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import androidx.annotation.Nullable;

import com.yubico.yubikit.android.transport.usb.UsbYubiKeyDevice;
import com.yubico.yubikit.core.Transport;
import com.yubico.yubikit.core.smartcard.scp.ScpKid;
import com.yubico.yubikit.fido.ctap.Ctap2Session;
Expand All @@ -33,7 +34,7 @@ protected void withDevice(TestState.StatefulDeviceCallback<FidoTestState> callba
}

protected void withDevice(boolean setPin, TestState.StatefulDeviceCallback<FidoTestState> callback) throws Throwable {
FidoTestState state = new FidoTestState.Builder(device, getPinUvAuthProtocol())
FidoTestState state = new FidoTestState.Builder(device, usbPid, getPinUvAuthProtocol())
.scpKid(getScpKid())
.reconnectDeviceCallback(this::reconnectDevice)
.setPin(setPin)
Expand All @@ -43,7 +44,7 @@ protected void withDevice(boolean setPin, TestState.StatefulDeviceCallback<FidoT
}

protected void withCtap2Session(TestState.StatefulSessionCallback<Ctap2Session, FidoTestState> callback) throws Throwable {
FidoTestState state = new FidoTestState.Builder(device, getPinUvAuthProtocol())
FidoTestState state = new FidoTestState.Builder(device, usbPid, getPinUvAuthProtocol())
.scpKid(getScpKid())
.reconnectDeviceCallback(this::reconnectDevice)
.setPin(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,16 @@
public class MpeInstrumentedTests extends YKInstrumentedTests {

protected void withPivSession(TestState.StatefulSessionCallback<PivSession, MpeTestState> callback) throws Throwable {
final MpeTestState state = new MpeTestState.Builder(device).scpKid(getScpKid()).build();
final MpeTestState state = new MpeTestState.Builder(device, usbPid)
.scpKid(getScpKid())
.build();
state.withPiv(callback);
}

protected void withCtap2Session(TestState.StatefulSessionCallback<Ctap2Session, MpeTestState> callback) throws Throwable {
final MpeTestState state = new MpeTestState.Builder(device).scpKid(getScpKid()).build();
final MpeTestState state = new MpeTestState.Builder(device, usbPid)
.scpKid(getScpKid())
.build();
state.withCtap2(callback);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
public class OathInstrumentedTests extends YKInstrumentedTests {

protected void withDevice(TestState.StatefulDeviceCallback<OathTestState> callback) throws Throwable {
final OathTestState state = new OathTestState.Builder(device)
final OathTestState state = new OathTestState.Builder(device, usbPid)
.scpKid(getScpKid())
.reconnectDeviceCallback(this::reconnectDevice)
.build();
Expand All @@ -32,7 +32,9 @@ protected void withDevice(TestState.StatefulDeviceCallback<OathTestState> callba
}

protected void withOathSession(TestState.StatefulSessionCallback<OathSession, OathTestState> callback) throws Throwable {
final OathTestState state = new OathTestState.Builder(device).scpKid(getScpKid()).build();
final OathTestState state = new OathTestState.Builder(device, usbPid)
.scpKid(getScpKid())
.build();
state.withOath(callback);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@

public class OpenPgpInstrumentedTests extends YKInstrumentedTests {
protected void withOpenPgpSession(TestState.StatefulSessionCallback<OpenPgpSession, OpenPgpTestState> callback) throws Throwable {
final OpenPgpTestState state = new OpenPgpTestState.Builder(device).scpKid(getScpKid()).build();
final OpenPgpTestState state = new OpenPgpTestState.Builder(device, usbPid)
.scpKid(getScpKid())
.build();
state.withOpenPgp(callback);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@

public class PivInstrumentedTests extends YKInstrumentedTests {
protected void withPivSession(TestState.StatefulSessionCallback<PivSession, PivTestState> callback) throws Throwable {
final PivTestState state = new PivTestState.Builder(device).scpKid(getScpKid()).build();
final PivTestState state = new PivTestState.Builder(device, usbPid)
.scpKid(getScpKid())
.build();
state.withPiv(callback);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@

package com.yubico.yubikit.testing.framework;

import com.yubico.yubikit.android.transport.usb.UsbYubiKeyDevice;
import com.yubico.yubikit.testing.TestState;
import com.yubico.yubikit.testing.sd.SecurityDomainTestState;

public class SecurityDomainInstrumentedTests extends YKInstrumentedTests {

protected void withState(TestState.StatefulDeviceCallback<SecurityDomainTestState> callback) throws Throwable {
final SecurityDomainTestState state = new SecurityDomainTestState.Builder(device)
final SecurityDomainTestState state = new SecurityDomainTestState.Builder(device, usbPid)
.reconnectDeviceCallback(this::reconnectDevice)
.build();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,31 @@

import androidx.test.ext.junit.rules.ActivityScenarioRule;

import com.yubico.yubikit.android.transport.usb.UsbYubiKeyDevice;
import com.yubico.yubikit.core.Transport;
import com.yubico.yubikit.core.UsbPid;
import com.yubico.yubikit.core.Version;
import com.yubico.yubikit.core.YubiKeyDevice;
import com.yubico.yubikit.core.application.SessionVersionOverride;
import com.yubico.yubikit.core.smartcard.SmartCardConnection;
import com.yubico.yubikit.management.DeviceInfo;
import com.yubico.yubikit.support.DeviceUtil;
import com.yubico.yubikit.testing.TestActivity;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.TestName;

import java.io.IOException;

import javax.annotation.Nullable;

public class YKInstrumentedTests {

private TestActivity activity;
protected YubiKeyDevice device = null;
protected UsbPid usbPid = null;

@Rule
public final TestName name = new TestName();
Expand All @@ -44,6 +54,17 @@ public class YKInstrumentedTests {
public void getYubiKey() throws InterruptedException {
scenarioRule.getScenario().onActivity((TestActivity activity) -> this.activity = activity);
device = activity.awaitSession(getClass().getSimpleName(), name.getMethodName());
usbPid = device instanceof UsbYubiKeyDevice
? ((UsbYubiKeyDevice) device).getPid()
: null;

try (SmartCardConnection connection = device.openConnection(SmartCardConnection.class)) {
final DeviceInfo deviceInfo = DeviceUtil.readInfo(connection, usbPid);
if (deviceInfo.getVersion().major == 0) {
SessionVersionOverride.set(new Version(5, 7, 2));
}
} catch (IOException | IllegalStateException ignored) {
}
}

@After
Expand All @@ -55,6 +76,7 @@ public void releaseYubiKey() throws InterruptedException {
activity.returnSession(device);
device = null;
activity = null;
usbPid = null;
}

protected YubiKeyDevice reconnectDevice() {
Expand Down
Loading

0 comments on commit 998a263

Please sign in to comment.