Skip to content

Commit 6d1261f

Browse files
committed
v4.1.4
1 parent 5f09768 commit 6d1261f

File tree

71 files changed

+1643
-327
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+1643
-327
lines changed

build.gradle

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1+
import org.jetbrains.dokka.gradle.DokkaTask
2+
13
buildscript {
24
repositories {
35
google()
46
jcenter()
57
}
68

79
dependencies {
8-
classpath 'com.android.tools.build:gradle:4.0.0'
9-
classpath 'org.jetbrains.dokka:dokka-android-gradle-plugin:0.9.17'
10+
classpath 'com.android.tools.build:gradle:4.0.1'
11+
classpath 'org.jetbrains.dokka:dokka-gradle-plugin:1.4.0'
1012
}
1113
}
1214

@@ -15,9 +17,16 @@ allprojects {
1517
google()
1618
jcenter()
1719
}
20+
21+
// custom dokka format
22+
tasks.register("dokkaHugo", DokkaTask) {
23+
dependencies {
24+
dokkaHugoPlugin 'com.github.cotechde:dokka-hugo-plugin:d053c16110'
25+
}
26+
}
1827
}
1928

2029
ext {
2130
compileSdkVersion = 29
22-
hwSdkVersionName = '4.1.0'
31+
hwSdkVersionName = '4.1.4'
2332
}

hwsecurity/core/build.gradle

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
apply plugin: 'com.android.library'
22
apply plugin: 'maven-publish'
3-
apply plugin: 'org.jetbrains.dokka-android'
3+
apply plugin: 'org.jetbrains.dokka'
44

55
dependencies {
66
implementation 'androidx.lifecycle:lifecycle-runtime:2.2.0'
77

88
compileOnly 'androidx.annotation:annotation:1.1.0'
9+
compileOnly 'io.sentry:sentry-android:2.3.1'
910

1011
api 'com.google.auto.value:auto-value-annotations:1.6.2'
1112
annotationProcessor 'com.google.auto.value:auto-value:1.6.2'
@@ -88,18 +89,26 @@ afterEvaluate {
8889
}
8990
}
9091

91-
dokka {
92-
// uses locally built dokka extension
93-
dokkaFatJar = files('libs/dokka-hugo-fatjar-0.9.17.jar')
94-
// does not work correctly with Maven:
95-
//dokkaFatJar = 'de.cotech:dokka-hugo-fatjar:0.9.17'
96-
moduleName = 'hwsecurity'
97-
outputFormat = "hugo"
98-
outputDirectory = "$projectDir/../../hwsecurity.dev/content/reference"
99-
sourceDirs = files('src/main/java')
100-
101-
packageOptions {
102-
prefix = "de.cotech.hw.internal"
103-
suppress = true
92+
dokkaHugo {
93+
configure {
94+
outputDirectory.set(file("$projectDir/../../hwsecurity.dev/content/reference"))
95+
96+
dokkaSourceSets {
97+
register("java") {
98+
moduleDisplayName.set("hwsecurity")
99+
100+
sourceRoots.setFrom(file("src/main/java"))
101+
102+
jdkVersion.set(8) // Used for linking to JDK documentation
103+
noStdlibLink.set(false) // Disable linking to online kotlin-stdlib documentation
104+
noJdkLink.set(true) // Disable linking to online JDK documentation
105+
noAndroidSdkLink.set(false) // Disable linking to online Android documentation
106+
107+
perPackageOption {
108+
prefix.set("de.cotech.hw.internal")
109+
suppress.set(true)
110+
}
111+
}
112+
}
104113
}
105-
}
114+
}
-46.9 MB
Binary file not shown.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:tools="http://schemas.android.com/tools"
34
package="de.cotech.hw">
45

6+
<!-- This library is included by the application anyways. -->
7+
<uses-sdk tools:overrideLibrary="io.sentry.android" />
8+
59
<uses-permission android:name="android.permission.NFC" />
610
<application />
711
</manifest>

hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyAuthenticator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
* is already associated with a specific key.
4141
* <p>
4242
* Example:
43-
* <pre>{@code
43+
* <pre>
4444
* byte[] challenge = { (byte) 1, (byte) 2, (byte) 3, (byte) 4 };
4545
* PinProvider pinProvider = StaticPinProvider.getInstance(ByteSecret.unsafeFromString("123456"));
4646
*
@@ -52,7 +52,7 @@
5252
* signature.update(challenge);
5353
* boolean isVerified = signature.verify(signatureBytes);
5454
* assert isVerified;
55-
* }</pre>
55+
* </pre>
5656
*/
5757
public interface SecurityKeyAuthenticator {
5858
/**

hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyConnectionMode.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,7 @@ public abstract class SecurityKeyConnectionMode<T extends SecurityKey> {
3939
protected abstract boolean isRelevantTransport(Transport transport);
4040
@AnyThread
4141
protected abstract boolean isRelevantSecurityKey(SecurityKey securityKey);
42+
43+
@AnyThread
44+
protected void clearSentryTags() { }
4245
}

hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyManager.java

Lines changed: 97 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import androidx.lifecycle.LifecycleObserver;
5454
import androidx.lifecycle.LifecycleOwner;
5555
import androidx.lifecycle.OnLifecycleEvent;
56+
import de.cotech.hw.internal.HwSentry;
5657
import de.cotech.hw.internal.dispatch.UsbIntentDispatchActivity;
5758
import de.cotech.hw.internal.transport.Transport;
5859
import de.cotech.hw.internal.transport.nfc.NfcConnectionDispatcher;
@@ -73,23 +74,23 @@
7374
* Once initialized, this class will dispatch newly connected security keys to all currently registered listeners.
7475
* Listeners can be registered with {@link #registerCallback}.
7576
* <p>
76-
* <pre>{@code
77+
* <pre>
7778
* public void onCreate() {
7879
* super.onCreate();
7980
* SecurityKeyManager securityKeyManager = SecurityKeyManager.getInstance();
8081
* securityKeyManager.init(this);
8182
* }
82-
* }</pre>
83+
* </pre>
8384
* <p>
8485
* A callback is registered together with a {@link SecurityKeyConnectionMode}, which establishes a
85-
* connection to a particular type of Security Token, such as FIDO, PIV, or OpenPGP. Implementations
86-
* for different SecurityKeyConnectionModes are shipped as modules, such as :de.cotech:hwsecurity-fido:,
87-
* :de.cotech:hwsecurity-piv:, and :de.cotech:hwsecurity-openpgp:. Apps will typically use only a
88-
* single type of Security Key.
86+
* connection to a particular type of Security Token, such as FIDO2, FIDO, PIV, or OpenPGP.
87+
* Implementations for different SecurityKeyConnectionModes are shipped as artifacts,
88+
* such as hwsecurity-fido2, hwsecurity-fido, hwsecurity-piv, and hwsecurity-openpgp.
89+
* Apps will typically use only a single type of Security Key.
8990
* <p>
9091
* To receive callbacks in an Activity, register for a callback bound to the Activity's lifecycle:
9192
* <p>
92-
* <pre>{@code
93+
* <pre>
9394
* public void onCreate() {
9495
* super.onResume();
9596
* FidoSecurityKeyConnectionMode connectionMode = new FidoSecurityKeyConnectionMode();
@@ -98,7 +99,7 @@
9899
* public void onSecurityKeyDiscovered(FidoSecurityKey securityKey) {
99100
* // perform operations on FidoSecurityKey
100101
* }
101-
* }</pre>
102+
* </pre>
102103
* <p>
103104
* Advanced applications that want to work with different applets on the same connected Security Key
104105
* can do so using {@link de.cotech.hw.raw.RawSecurityKeyConnectionMode}.
@@ -178,6 +179,8 @@ public void init(@NonNull Application application, @NonNull SecurityKeyManagerCo
178179
HwTimber.plant(loggingTree);
179180
}
180181

182+
HwSentry.initializeIfAvailable(config);
183+
181184
if (config.isEnableDebugLogging() && HwTimber.treeCount() == 0) {
182185
HwTimber.plant(new DebugTree() {
183186
@Override
@@ -238,6 +241,8 @@ private class DispatcherActivityLifecycleCallbacks implements ActivityLifecycleC
238241
private Activity boundActivity;
239242
private UsbConnectionDispatcher activeUsbDispatcher;
240243
private NfcConnectionDispatcher activeNfcDispatcher;
244+
private boolean isResumed;
245+
private boolean isActive;
241246

242247
private void bindToActivity(Activity activity) {
243248
if (isUsbDispatchActivity(activity)) {
@@ -274,31 +279,60 @@ private void unbindFromActivity(Activity activity) {
274279
boundActivity = null;
275280
}
276281

277-
@Override
278-
public void onActivityResumed(Activity activity) {
279-
bindToActivity(activity);
282+
private void refreshActiveState() {
283+
if (boundActivity == null) {
284+
return;
285+
}
286+
boolean isActive = isResumed && (!config.isDisableWhileInactive() || !registeredCallbacks.isEmpty());
287+
if (isActive) {
288+
ensureStateActive();
289+
} else {
290+
ensureStateInactive();
291+
}
292+
}
293+
294+
private void ensureStateActive() {
295+
if (isActive) {
296+
return;
297+
}
298+
isActive = true;
299+
HwTimber.d("Switching hwsecurity state to active");
280300
if (activeUsbDispatcher != null) {
281-
activeUsbDispatcher.onResume();
301+
activeUsbDispatcher.onActive();
282302
}
283303
if (activeNfcDispatcher != null) {
284-
activeNfcDispatcher.onResume();
304+
activeNfcDispatcher.onActive();
285305
}
286306
postTriggerCallbacksActively();
287307
}
288308

289-
@Override
290-
public void onActivityPaused(Activity activity) {
291-
if (boundActivity != activity) {
309+
private void ensureStateInactive() {
310+
if (!isActive) {
292311
return;
293312
}
313+
isActive = false;
314+
HwTimber.d("Switching hwsecurity state to inactive");
294315
if (activeUsbDispatcher != null) {
295-
activeUsbDispatcher.onPause();
316+
activeUsbDispatcher.onInactive();
296317
}
297318
if (activeNfcDispatcher != null) {
298-
activeNfcDispatcher.onPause();
319+
activeNfcDispatcher.onInactive();
299320
}
300321
}
301322

323+
@Override
324+
public void onActivityResumed(Activity activity) {
325+
bindToActivity(activity);
326+
isResumed = true;
327+
refreshActiveState();
328+
}
329+
330+
@Override
331+
public void onActivityPaused(Activity activity) {
332+
isResumed = false;
333+
refreshActiveState();
334+
}
335+
302336
@Override
303337
public void onActivityDestroyed(Activity activity) {
304338
unbindFromActivity(activity);
@@ -424,6 +458,7 @@ public <T extends SecurityKey> void registerCallback(SecurityKeyConnectionMode<T
424458
RegisteredConnectionMode<T> registeredConnectionMode = new RegisteredConnectionMode<>(mode, callback, false);
425459
lifecycleOwner.getLifecycle().addObserver(registeredConnectionMode);
426460
registeredCallbacks.add(0, registeredConnectionMode);
461+
activityLifecycleCallbacks.refreshActiveState();
427462

428463
postTriggerCallbacksActively();
429464
}
@@ -439,6 +474,7 @@ public <T extends SecurityKey> void registerCallbackForever(SecurityKeyConnectio
439474
throw new IllegalStateException("SecurityKeyManager must be initialized in your Application class!");
440475
}
441476
registeredCallbacks.add(0, new RegisteredConnectionMode<>(mode, callback, true));
477+
activityLifecycleCallbacks.refreshActiveState();
442478
}
443479

444480
@SuppressWarnings("WeakerAccess") // public API
@@ -463,6 +499,24 @@ public void rediscoverConnectedSecurityKeys() {
463499
postTriggerCallbacksActively();
464500
}
465501

502+
/**
503+
* This method clears and releases all connected security keys.
504+
*
505+
* Calling this method will clear the managed state of all persistently connected Security Keys.
506+
* This operation should not be called during regular operation, since all managed devices (NFC
507+
* and USB) are automatically cleaned up when they are disconnected. However, it may still be
508+
* useful to clear managed state in order to rediscover a connected Security Key with a different
509+
* SecurityKeyConnectionMode.
510+
*
511+
* This method is not part of the public API.
512+
*/
513+
@AnyThread
514+
@RestrictTo(Scope.LIBRARY_GROUP)
515+
public void clearConnectedSecurityKeys() {
516+
nfcTagManager.clearManagedNfcTags();
517+
usbDeviceManager.clearManagedUsbDevices();
518+
}
519+
466520
/**
467521
* Returns true if USB host mode is available.
468522
*
@@ -549,11 +603,18 @@ private void maybeDeliverPostponedTransport() {
549603

550604
@UiThread
551605
boolean maybeRedeliverSecurityKey(SecurityKey securityKeyCandidate) {
552-
if (!isBoundForever && isActive && postponedTransport == null &&
553-
connectionMode.isRelevantSecurityKey(securityKeyCandidate)) {
554-
// noinspection unchecked, this is checked with isRelevantSecurityKey
555-
deliverDiscover((T) securityKeyCandidate);
556-
return true;
606+
if (!isBoundForever && isActive && postponedTransport == null) {
607+
if (connectionMode.isRelevantSecurityKey(securityKeyCandidate)) {
608+
// noinspection unchecked, this is checked with isRelevantSecurityKey
609+
deliverDiscover((T) securityKeyCandidate);
610+
return true;
611+
} else {
612+
HwTimber.d(
613+
"Connected Security Key of type %s doesn't match SecurityKeyConnectionMode %s",
614+
securityKeyCandidate.getClass().getSimpleName(),
615+
connectionMode.getClass().getSimpleName());
616+
return false;
617+
}
557618
}
558619
return false;
559620
}
@@ -566,6 +627,9 @@ private boolean attemptConnectWithRegisteredSecurityMode(Transport transport) {
566627
if (securityKey == null) {
567628
return false;
568629
}
630+
HwSentry.addBreadcrumb("Connected security key of type %s on transport %s",
631+
securityKey.getClass().getSimpleName(),
632+
securityKey.transport.getTransportType());
569633
} catch (IOException e) {
570634
callbackHandlerMain.post(() -> {
571635
if (!isActive) {
@@ -602,6 +666,12 @@ private void deliverDiscover(T securityKey) {
602666

603667
@AnyThread
604668
private void handleTransportRelease(T securityKey) {
669+
HwSentry.addBreadcrumb("Released security key of type %s on transport %s",
670+
securityKey.getClass().getSimpleName(),
671+
securityKey.transport.getTransportType());
672+
// Clear tags that may have been added by us while this Security Key was active
673+
connectionMode.clearSentryTags();
674+
605675
persistentSecurityKeys.remove(securityKey);
606676

607677
boolean isNfcTransport = securityKey.transport instanceof NfcTransport;
@@ -646,6 +716,10 @@ void onDestroy() {
646716
connectionMode.getClass().getSimpleName(), callback.getClass().getSimpleName());
647717
registeredCallbacks.remove(this);
648718
postponedTransport = null;
719+
if (persistentSecurityKeys.isEmpty()) {
720+
connectionMode.clearSentryTags();
721+
}
722+
activityLifecycleCallbacks.refreshActiveState();
649723
}
650724
}
651725
}

0 commit comments

Comments
 (0)