Skip to content

Commit ded3a63

Browse files
committed
feat: add YubiOtp device tests
1 parent 2139906 commit ded3a63

File tree

12 files changed

+541
-4
lines changed

12 files changed

+541
-4
lines changed

testing-android/src/androidTest/java/com/yubico/yubikit/DeviceTests.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2024-2025 Yubico.
2+
* Copyright (C) 2024-2026 Yubico.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,6 +24,8 @@
2424
import com.yubico.yubikit.openpgp.OpenPgpTests;
2525
import com.yubico.yubikit.piv.PivTests;
2626
import com.yubico.yubikit.sd.SecurityDomainTests;
27+
import com.yubico.yubikit.yubiotp.YubiOtpOverCcidTests;
28+
import com.yubico.yubikit.yubiotp.YubiOtpTests;
2729
import org.junit.runner.RunWith;
2830
import org.junit.runners.Suite;
2931

@@ -41,6 +43,8 @@
4143
MultiProtocolResetTests.class,
4244
FidoTests.class,
4345
FidoOverCcidTests.class,
46+
YubiOtpTests.class,
47+
YubiOtpOverCcidTests.class,
4448
SmartCardProtocolTests.class
4549
})
4650
public class DeviceTests {}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright (C) 2026 Yubico.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.yubico.yubikit.yubiotp;
18+
19+
import com.yubico.yubikit.SmokeTest;
20+
import com.yubico.yubikit.core.otp.OtpConnection;
21+
import com.yubico.yubikit.core.smartcard.SmartCardConnection;
22+
import com.yubico.yubikit.framework.YubiOtpInstrumentedTests;
23+
import java.util.Collections;
24+
import org.junit.Before;
25+
import org.junit.Test;
26+
import org.junit.experimental.categories.Category;
27+
28+
public class YubiOtpOverCcidTests extends YubiOtpInstrumentedTests {
29+
30+
@Before
31+
public void setupCcidOnly() {
32+
connectionTypes = Collections.singletonList(SmartCardConnection.class);
33+
}
34+
35+
@Test
36+
public void testSlotTouchTriggered() throws Throwable {
37+
withYubiOtpSession(
38+
(otp, state) -> {
39+
YubiOtpDeviceTests.testSlotTouchTriggered(otp, state, Slot.ONE);
40+
YubiOtpDeviceTests.testSlotTouchTriggered(otp, state, Slot.TWO);
41+
});
42+
}
43+
44+
@Test
45+
@Category(SmokeTest.class)
46+
public void testSwitchTransports() throws Throwable {
47+
withYubiOtpSession(YubiOtpDeviceTests::testSlotConfigured);
48+
connectionTypes = Collections.singletonList(OtpConnection.class);
49+
withYubiOtpSession(YubiOtpDeviceTests::testSlotConfigured);
50+
connectionTypes = Collections.singletonList(SmartCardConnection.class);
51+
withYubiOtpSession(YubiOtpDeviceTests::testSlotConfigured);
52+
connectionTypes = Collections.singletonList(OtpConnection.class);
53+
withYubiOtpSession(YubiOtpDeviceTests::testCalculateHmacSha1);
54+
}
55+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright (C) 2026 Yubico.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.yubico.yubikit.yubiotp;
18+
19+
import com.yubico.yubikit.SmokeTest;
20+
import com.yubico.yubikit.framework.YubiOtpInstrumentedTests;
21+
import org.junit.Test;
22+
import org.junit.experimental.categories.Category;
23+
24+
public class YubiOtpTests extends YubiOtpInstrumentedTests {
25+
@Test
26+
@Category(SmokeTest.class)
27+
public void testSlotConfiguration() throws Throwable {
28+
withYubiOtpSession(YubiOtpDeviceTests::testSlotConfigured);
29+
}
30+
31+
@Test
32+
public void testSlotTouchTriggered() throws Throwable {
33+
withYubiOtpSession(
34+
(otp, state) -> {
35+
YubiOtpDeviceTests.testSlotTouchTriggered(otp, state, Slot.ONE);
36+
YubiOtpDeviceTests.testSlotTouchTriggered(otp, state, Slot.TWO);
37+
});
38+
}
39+
40+
@Test
41+
public void testConfigureNdef() throws Throwable {
42+
withYubiOtpSession(YubiOtpDeviceTests::testConfigureNdef);
43+
}
44+
45+
@Test
46+
@Category(SmokeTest.class)
47+
public void testCalculateHmacSha1() throws Throwable {
48+
withYubiOtpSession(YubiOtpDeviceTests::testCalculateHmacSha1);
49+
}
50+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright (C) 2026 Yubico.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.yubico.yubikit.framework;
18+
19+
import com.yubico.yubikit.TestState;
20+
import com.yubico.yubikit.core.YubiKeyConnection;
21+
import com.yubico.yubikit.core.otp.OtpConnection;
22+
import com.yubico.yubikit.core.smartcard.SmartCardConnection;
23+
import com.yubico.yubikit.yubiotp.YubiOtpSession;
24+
import com.yubico.yubikit.yubiotp.YubiOtpTestState;
25+
import java.util.Arrays;
26+
import java.util.List;
27+
28+
public class YubiOtpInstrumentedTests extends YkInstrumentedTests {
29+
30+
public static List<Class<? extends YubiKeyConnection>> connectionTypes =
31+
Arrays.asList(OtpConnection.class, SmartCardConnection.class);
32+
33+
protected void withYubiOtpSession(
34+
TestState.StatefulSessionCallback<YubiOtpSession, YubiOtpTestState> callback)
35+
throws Throwable {
36+
final YubiOtpTestState state =
37+
new YubiOtpTestState.Builder(device, connectionTypes, usbPid).scpKid(getScpKid()).build();
38+
state.withYubiOtp(callback);
39+
}
40+
}

testing-desktop/src/integrationTest/java/com/yubico/yubikit/DeviceTests.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2024-2025 Yubico.
2+
* Copyright (C) 2024-2026 Yubico.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,6 +23,8 @@
2323
import com.yubico.yubikit.openpgp.OpenPgpTests;
2424
import com.yubico.yubikit.piv.PivTests;
2525
import com.yubico.yubikit.sd.SecurityDomainTests;
26+
import com.yubico.yubikit.yubiotp.YubiOtpOverCcidTests;
27+
import com.yubico.yubikit.yubiotp.YubiOtpTests;
2628
import org.junit.runner.RunWith;
2729
import org.junit.runners.Suite;
2830

@@ -42,6 +44,8 @@
4244
MultiProtocolResetTests.class,
4345
FidoTests.class,
4446
FidoOverCcidTests.class,
47+
YubiOtpTests.class,
48+
YubiOtpOverCcidTests.class,
4549
SmartCardProtocolTests.class
4650
})
4751
public class DeviceTests {}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright (C) 2026 Yubico.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.yubico.yubikit.yubiotp;
18+
19+
import com.yubico.yubikit.SmokeTest;
20+
import com.yubico.yubikit.core.otp.OtpConnection;
21+
import com.yubico.yubikit.core.smartcard.SmartCardConnection;
22+
import com.yubico.yubikit.framework.YubiOtpInstrumentedTests;
23+
import java.util.Collections;
24+
import org.junit.Before;
25+
import org.junit.Test;
26+
import org.junit.experimental.categories.Category;
27+
28+
public class YubiOtpOverCcidTests extends YubiOtpInstrumentedTests {
29+
30+
@Before
31+
public void setupCcidOnly() {
32+
connectionTypes = Collections.singletonList(SmartCardConnection.class);
33+
}
34+
35+
@Test
36+
public void testSlotTouchTriggered() throws Throwable {
37+
withYubiOtpSession(
38+
(otp, state) -> {
39+
YubiOtpDeviceTests.testSlotTouchTriggered(otp, state, Slot.ONE);
40+
YubiOtpDeviceTests.testSlotTouchTriggered(otp, state, Slot.TWO);
41+
});
42+
}
43+
44+
@Test
45+
@Category(SmokeTest.class)
46+
public void testSwitchTransports() throws Throwable {
47+
withYubiOtpSession(YubiOtpDeviceTests::testSlotConfigured);
48+
connectionTypes = Collections.singletonList(OtpConnection.class);
49+
withYubiOtpSession(YubiOtpDeviceTests::testSlotConfigured);
50+
connectionTypes = Collections.singletonList(SmartCardConnection.class);
51+
withYubiOtpSession(YubiOtpDeviceTests::testSlotConfigured);
52+
connectionTypes = Collections.singletonList(OtpConnection.class);
53+
withYubiOtpSession(YubiOtpDeviceTests::testCalculateHmacSha1);
54+
}
55+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright (C) 2026 Yubico.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.yubico.yubikit.yubiotp;
18+
19+
import com.yubico.yubikit.SmokeTest;
20+
import com.yubico.yubikit.framework.YubiOtpInstrumentedTests;
21+
import org.junit.Test;
22+
import org.junit.experimental.categories.Category;
23+
24+
public class YubiOtpTests extends YubiOtpInstrumentedTests {
25+
@Test
26+
@Category(SmokeTest.class)
27+
public void testSlotConfiguration() throws Throwable {
28+
withYubiOtpSession(YubiOtpDeviceTests::testSlotConfigured);
29+
}
30+
31+
@Test
32+
public void testSlotTouchTriggered() throws Throwable {
33+
withYubiOtpSession(
34+
(otp, state) -> {
35+
YubiOtpDeviceTests.testSlotTouchTriggered(otp, state, Slot.ONE);
36+
YubiOtpDeviceTests.testSlotTouchTriggered(otp, state, Slot.TWO);
37+
});
38+
}
39+
40+
@Test
41+
public void testConfigureNdef() throws Throwable {
42+
withYubiOtpSession(YubiOtpDeviceTests::testConfigureNdef);
43+
}
44+
45+
@Test
46+
@Category(SmokeTest.class)
47+
public void testCalculateHmacSha1() throws Throwable {
48+
withYubiOtpSession(YubiOtpDeviceTests::testCalculateHmacSha1);
49+
}
50+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright (C) 2026 Yubico.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.yubico.yubikit.framework;
18+
19+
import com.yubico.yubikit.TestState;
20+
import com.yubico.yubikit.core.YubiKeyConnection;
21+
import com.yubico.yubikit.core.otp.OtpConnection;
22+
import com.yubico.yubikit.core.smartcard.SmartCardConnection;
23+
import com.yubico.yubikit.yubiotp.YubiOtpSession;
24+
import com.yubico.yubikit.yubiotp.YubiOtpTestState;
25+
import java.util.Arrays;
26+
import java.util.List;
27+
28+
public class YubiOtpInstrumentedTests extends YkInstrumentedTests {
29+
30+
public static List<Class<? extends YubiKeyConnection>> connectionTypes =
31+
Arrays.asList(OtpConnection.class, SmartCardConnection.class);
32+
33+
protected void withYubiOtpSession(
34+
TestState.StatefulSessionCallback<YubiOtpSession, YubiOtpTestState> callback)
35+
throws Throwable {
36+
final YubiOtpTestState state =
37+
new YubiOtpTestState.Builder(device, connectionTypes, usbPid).scpKid(getScpKid()).build();
38+
state.withYubiOtp(callback);
39+
}
40+
}

testing/src/main/java/com/yubico/yubikit/AllowList.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2025 Yubico.
2+
* Copyright (C) 2025-2026 Yubico.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
2121
import com.yubico.yubikit.core.YubiKeyConnection;
2222
import com.yubico.yubikit.core.YubiKeyDevice;
2323
import com.yubico.yubikit.core.fido.FidoConnection;
24+
import com.yubico.yubikit.core.otp.OtpConnection;
2425
import com.yubico.yubikit.core.smartcard.SmartCardConnection;
2526
import com.yubico.yubikit.management.DeviceInfo;
2627
import com.yubico.yubikit.support.DeviceUtil;
@@ -87,6 +88,14 @@ private Integer getDeviceSerialNumber(YubiKeyDevice device, @Nullable UsbPid pid
8788
}
8889
}
8990

91+
if (device.supportsConnection(OtpConnection.class)) {
92+
try (OtpConnection connection = device.openConnection(OtpConnection.class)) {
93+
return getDeviceSerialNumber(connection, pid);
94+
} catch (Exception e) {
95+
logger.error("Error opening OTP connection", e);
96+
}
97+
}
98+
9099
if (device.supportsConnection(FidoConnection.class)) {
91100
try (FidoConnection connection = device.openConnection(FidoConnection.class)) {
92101
return getDeviceSerialNumber(connection, pid);

0 commit comments

Comments
 (0)