Skip to content

Commit 793a9b3

Browse files
committed
v2.5.0
1 parent e93c645 commit 793a9b3

File tree

52 files changed

+1297
-418
lines changed

Some content is hidden

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

52 files changed

+1297
-418
lines changed

hwsecurity-fido/build.gradle

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ android {
3030

3131
defaultConfig {
3232
minSdkVersion 14
33-
versionCode rootProject.ext.hwSdkVersionCode
3433
versionName rootProject.ext.hwSdkVersionName
3534
vectorDrawables.useSupportLibrary = true
3635
}

hwsecurity-fido/src/main/java/de/cotech/hw/fido/internal/async/FidoOperationThread.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@
3636
import androidx.lifecycle.Lifecycle.Event;
3737
import androidx.lifecycle.LifecycleObserver;
3838
import androidx.lifecycle.OnLifecycleEvent;
39-
import de.cotech.hw.exceptions.TransportGoneException;
39+
40+
import de.cotech.hw.exceptions.SecurityKeyLostException;
4041
import de.cotech.hw.fido.exceptions.FidoPresenceRequiredException;
4142
import de.cotech.hw.fido.internal.FidoU2fAppletConnection;
4243
import de.cotech.hw.util.HwTimber;
@@ -84,7 +85,7 @@ public void run() {
8485
} catch (InterruptedException e) {
8586
HwTimber.e("Fido operation was interrupted");
8687
break;
87-
} catch (TransportGoneException e) {
88+
} catch (SecurityKeyLostException e) {
8889
HwTimber.e("Transport gone during fido operation");
8990
break;
9091
} catch (FidoPresenceRequiredException e) {

hwsecurity-fido/src/main/java/de/cotech/hw/fido/internal/utils/AnimatedVectorDrawableHelper.java

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424

2525
package de.cotech.hw.fido.internal.utils;
2626

27-
import android.content.Context;
2827
import android.graphics.drawable.Animatable;
2928
import android.graphics.drawable.Drawable;
3029
import android.os.Build;
@@ -41,13 +40,13 @@
4140
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
4241
public class AnimatedVectorDrawableHelper {
4342

44-
public static void startAnimation(Context context, ImageView imageView, int resId) {
45-
startAnimation(context, imageView, resId, null);
43+
public static void startAnimation(ImageView imageView, int resId) {
44+
startAnimation(imageView, resId, null);
4645
}
4746

48-
public static void startAnimation(Context context, ImageView imageView, int resId, Animatable2Compat.AnimationCallback animationCallback) {
47+
public static void startAnimation(ImageView imageView, int resId, Animatable2Compat.AnimationCallback animationCallback) {
4948
if (Build.VERSION.SDK_INT <= 24) {
50-
AnimatedVectorDrawableCompat avdCompat = setAndStartAnimatedVectorDrawableSdk24(context, imageView, resId);
49+
AnimatedVectorDrawableCompat avdCompat = setAndStartAnimatedVectorDrawableSdk24(imageView, resId);
5150
if (animationCallback != null) {
5251
avdCompat.registerAnimationCallback(animationCallback);
5352
}
@@ -60,7 +59,7 @@ public static void startAnimation(Context context, ImageView imageView, int resI
6059
}
6160
}
6261

63-
public static void startAndLoopAnimation(Context context, ImageView imageView, int resId) {
62+
public static void startAndLoopAnimation(ImageView imageView, int resId) {
6463
Animatable2Compat.AnimationCallback animationCallback = new Animatable2Compat.AnimationCallback() {
6564
@NonNull
6665
private final Handler fHandler = new Handler(Looper.getMainLooper());
@@ -73,7 +72,7 @@ public void onAnimationEnd(@NonNull Drawable drawable) {
7372

7473
fHandler.post(() -> {
7574
if (Build.VERSION.SDK_INT <= 24) {
76-
AnimatedVectorDrawableCompat avdCompat = setAndStartAnimatedVectorDrawableSdk24(context, imageView, resId);
75+
AnimatedVectorDrawableCompat avdCompat = setAndStartAnimatedVectorDrawableSdk24(imageView, resId);
7776
avdCompat.registerAnimationCallback(this);
7877
} else {
7978
((Animatable) drawable).start();
@@ -83,7 +82,7 @@ public void onAnimationEnd(@NonNull Drawable drawable) {
8382
};
8483

8584
if (Build.VERSION.SDK_INT <= 24) {
86-
AnimatedVectorDrawableCompat avdCompat = setAndStartAnimatedVectorDrawableSdk24(context, imageView, resId);
85+
AnimatedVectorDrawableCompat avdCompat = setAndStartAnimatedVectorDrawableSdk24(imageView, resId);
8786
avdCompat.registerAnimationCallback(animationCallback);
8887
} else {
8988
imageView.setImageResource(resId);
@@ -93,8 +92,8 @@ public void onAnimationEnd(@NonNull Drawable drawable) {
9392
}
9493
}
9594

96-
private static AnimatedVectorDrawableCompat setAndStartAnimatedVectorDrawableSdk24(Context context, ImageView imageView, int resId) {
97-
AnimatedVectorDrawableCompat avdCompat = AnimatedVectorDrawableCompat.create(context, resId);
95+
private static AnimatedVectorDrawableCompat setAndStartAnimatedVectorDrawableSdk24(ImageView imageView, int resId) {
96+
AnimatedVectorDrawableCompat avdCompat = AnimatedVectorDrawableCompat.create(imageView.getContext(), resId);
9897

9998
// on SDK <= 24, the alphaFill values are not resetted properly to their initial state
10099
// The states of AnimatedVectorDrawables are stored centrally per resource.

hwsecurity-fido/src/main/java/de/cotech/hw/fido/ui/FidoDialogFragment.java

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
import android.content.Intent;
3737
import android.content.pm.ActivityInfo;
3838
import android.graphics.drawable.Drawable;
39-
import android.nfc.TagLostException;
4039
import android.os.Build;
4140
import android.os.Bundle;
4241
import android.os.Handler;
@@ -60,6 +59,7 @@
6059
import androidx.annotation.Nullable;
6160
import androidx.annotation.RequiresApi;
6261
import androidx.annotation.UiThread;
62+
import androidx.appcompat.app.AppCompatDelegate;
6363
import androidx.constraintlayout.widget.ConstraintLayout;
6464
import androidx.constraintlayout.widget.Guideline;
6565
import androidx.coordinatorlayout.widget.CoordinatorLayout;
@@ -84,6 +84,7 @@
8484
import de.cotech.hw.SecurityKeyCallback;
8585
import de.cotech.hw.SecurityKeyException;
8686
import de.cotech.hw.SecurityKeyManager;
87+
import de.cotech.hw.exceptions.SecurityKeyLostException;
8788
import de.cotech.hw.fido.FidoAuthenticateCallback;
8889
import de.cotech.hw.fido.FidoAuthenticateRequest;
8990
import de.cotech.hw.fido.FidoAuthenticateResponse;
@@ -108,6 +109,10 @@ public class FidoDialogFragment extends BottomSheetDialogFragment implements Sec
108109

109110
private static final long TIME_DELAYED_STATE_CHANGE = 3000;
110111

112+
static {
113+
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
114+
}
115+
111116
private OnFidoRegisterCallback fidoRegisterCallback;
112117
private OnFidoAuthenticateCallback fidoAuthenticateCallback;
113118

@@ -639,7 +644,7 @@ public void onAnimationEnd(Drawable drawable) {
639644
}
640645
};
641646

642-
AnimatedVectorDrawableHelper.startAnimation(getActivity(), imageNfcFullscreen, R.drawable.hwsecurity_nfc_handling, animationCallback);
647+
AnimatedVectorDrawableHelper.startAnimation(imageNfcFullscreen, R.drawable.hwsecurity_nfc_handling, animationCallback);
643648
}
644649

645650
private void fadeToNfcSweetSpot() {
@@ -715,7 +720,7 @@ private void showNfcSweetSpot() {
715720
textError.setVisibility(View.GONE);
716721
});
717722

718-
AnimatedVectorDrawableHelper.startAndLoopAnimation(getActivity(), sweetspotIndicator, R.drawable.hwsecurity_nfc_sweet_spot_a);
723+
AnimatedVectorDrawableHelper.startAndLoopAnimation(sweetspotIndicator, R.drawable.hwsecurity_nfc_sweet_spot_a);
719724
}
720725

721726
private void animateSelectUsb() {
@@ -728,7 +733,7 @@ public void onTransitionStart(@NonNull Transition transition) {
728733

729734
@Override
730735
public void onTransitionEnd(@NonNull Transition transition) {
731-
AnimatedVectorDrawableHelper.startAndLoopAnimation(getActivity(), imageUsb, R.drawable.hwsecurity_usb_handling_a);
736+
AnimatedVectorDrawableHelper.startAndLoopAnimation(imageUsb, R.drawable.hwsecurity_usb_handling_a);
732737
}
733738

734739
@Override
@@ -766,7 +771,7 @@ public void onTransitionStart(@NonNull Transition transition) {
766771

767772
@Override
768773
public void onTransitionEnd(@NonNull Transition transition) {
769-
AnimatedVectorDrawableHelper.startAndLoopAnimation(getActivity(), imageUsb, R.drawable.hwsecurity_usb_handling_b);
774+
AnimatedVectorDrawableHelper.startAndLoopAnimation(imageUsb, R.drawable.hwsecurity_usb_handling_b);
770775
}
771776

772777
@Override
@@ -797,7 +802,7 @@ public void onTransitionResume(@NonNull Transition transition) {
797802
private void animateUsbPressButton() {
798803
TransitionManager.beginDelayedTransition(innerBottomSheet);
799804
textTitle.setText(R.string.hwsecurity_title_usb_button);
800-
AnimatedVectorDrawableHelper.startAndLoopAnimation(getActivity(), imageUsb, R.drawable.hwsecurity_usb_handling_b);
805+
AnimatedVectorDrawableHelper.startAndLoopAnimation(imageUsb, R.drawable.hwsecurity_usb_handling_b);
801806
}
802807

803808
private void animateError() {
@@ -815,7 +820,7 @@ private void animateError() {
815820
textError.setVisibility(View.VISIBLE);
816821
imageError.setVisibility(View.VISIBLE);
817822

818-
AnimatedVectorDrawableHelper.startAnimation(getActivity(), imageError, R.drawable.hwsecurity_error);
823+
AnimatedVectorDrawableHelper.startAnimation(imageError, R.drawable.hwsecurity_error);
819824
}
820825

821826
@UiThread
@@ -913,7 +918,7 @@ private void handleError(IOException exception) {
913918
showError(getString(R.string.hwsecurity_error_wrong_key_handle));
914919
} catch (SecurityKeyException e) {
915920
showError(getString(R.string.hwsecurity_error_internal, e.getShortErrorName()));
916-
} catch (TagLostException e) {
921+
} catch (SecurityKeyLostException e) {
917922
// not handled
918923
} catch (IOException e) {
919924
showError(getString(R.string.hwsecurity_error_internal, e.getMessage()));
@@ -933,7 +938,8 @@ private void showError(String text) {
933938

934939
private int resolveColorFromAttr(@AttrRes int resId) {
935940
TypedValue outValue = new TypedValue();
936-
bottomSheet.getContext().getTheme().resolveAttribute(resId, outValue, true);
941+
// must be the themed context to work correctly on Android < 5
942+
innerBottomSheet.getContext().getTheme().resolveAttribute(resId, outValue, true);
937943
return outValue.data;
938944
}
939945

hwsecurity-fido/src/main/java/de/cotech/hw/fido/ui/FidoDialogOptions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public abstract static class Builder {
7777
* This sets the content of the window as 'secure', preventing it from appearing in screenshots,
7878
* screen recordings or from being viewed on non-secure displays.
7979
* <p>
80-
* Default: false (because FIDO U2F requires no PIN input)
80+
* Default: false (because FIDO U2F does not require PIN input)
8181
*/
8282
public abstract Builder setPreventScreenshots(boolean preventScreenshots);
8383

hwsecurity-openpgp/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,4 @@ dokka {
103103
prefix = "de.cotech.hw.openpgp.internal"
104104
suppress = true
105105
}
106-
}
106+
}
-3.84 MB
Binary file not shown.

hwsecurity-openpgp/src/main/java/de/cotech/hw/openpgp/OpenPgpSecurityKey.java

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676

7777

7878
public class OpenPgpSecurityKey extends SecurityKey {
79-
private static final ByteSecret DEFAULT_ADMIN_PIN = ByteSecret.unsafeFromString("12345678");
79+
private static final ByteSecret DEFAULT_PUK = ByteSecret.unsafeFromString("12345678");
8080

8181
public final OpenPgpAppletConnection openPgpAppletConnection;
8282

@@ -104,27 +104,29 @@ public PublicKey retrievePublicKey(KeyType keyType) throws IOException {
104104
*/
105105
@AnyThread
106106
public boolean isSecurityKeyEmpty() {
107-
return !openPgpAppletConnection.getOpenPgpCapabilities().hasEncryptKey();
107+
return !openPgpAppletConnection.getOpenPgpCapabilities().hasSignKey()
108+
&& !openPgpAppletConnection.getOpenPgpCapabilities().hasEncryptKey()
109+
&& !openPgpAppletConnection.getOpenPgpCapabilities().hasAuthKey();
108110
}
109111

110112
/**
111113
* This method directly performs IO with the security token, and should therefore not be called on the UI thread.
112114
*/
113115
@WorkerThread
114116
@RestrictTo(Scope.LIBRARY_GROUP)
115-
public void updatePinPukUsingDefaultPuk(ByteSecret newPin, ByteSecret newPuk) throws IOException {
117+
public void updatePinAndPukUsingDefaultPuk(ByteSecret newPin, ByteSecret newPuk) throws IOException {
116118
ModifyPinOp modifyPinOp = ModifyPinOp.create(openPgpAppletConnection);
117-
modifyPinOp.modifyPw1andPw3Pins(DEFAULT_ADMIN_PIN, newPin, newPuk);
119+
modifyPinOp.modifyPw1AndPw3(DEFAULT_PUK, newPin, newPuk);
118120
}
119121

120122
/**
121123
* This method directly performs IO with the security token, and should therefore not be called on the UI thread.
122124
*/
123125
@WorkerThread
124126
@RestrictTo(Scope.LIBRARY_GROUP)
125-
public void updatePinUsingPuk(ByteSecret puk, ByteSecret newPin) throws IOException {
127+
public void updatePinUsingPuk(ByteSecret currentPuk, ByteSecret newPin) throws IOException {
126128
ModifyPinOp modifyPinOp = ModifyPinOp.create(openPgpAppletConnection);
127-
modifyPinOp.modifyPw1Pin(puk, newPin);
129+
modifyPinOp.modifyPw1Pin(currentPuk, newPin);
128130
}
129131

130132
/**
@@ -142,19 +144,17 @@ public void updatePinUsingPuk(ByteSecret puk, ByteSecret newPin) throws IOExcept
142144
public void wipeAndVerify() throws IOException {
143145
ResetAndWipeOp resetAndWipe = ResetAndWipeOp.create(openPgpAppletConnection);
144146
resetAndWipe.resetAndWipeSecurityKey();
145-
openPgpAppletConnection.verifyAdminPin(DEFAULT_ADMIN_PIN);
147+
openPgpAppletConnection.verifyPuk(DEFAULT_PUK);
146148
}
147149

148150
@WorkerThread
149-
private boolean verifyAdminPinIfPossible() throws IOException {
150-
boolean encryptFpIsEmpty = !openPgpAppletConnection.getOpenPgpCapabilities().hasEncryptKey();
151-
if (encryptFpIsEmpty) {
151+
private boolean isInFactoryDefaultState() throws IOException {
152+
if (isSecurityKeyEmpty()) {
152153
try {
153-
// make one attempt at entering the default admin pin. if that fails, wipe the card
154-
openPgpAppletConnection.verifyAdminPin(DEFAULT_ADMIN_PIN);
154+
// make one attempt at entering the default PUK
155+
openPgpAppletConnection.verifyPuk(DEFAULT_PUK);
155156
return true;
156157
} catch (SecurityKeyException e) {
157-
// ignore, and wipe security key instead
158158
return false;
159159
}
160160
} else {
@@ -180,7 +180,8 @@ private boolean verifyAdminPinIfPossible() throws IOException {
180180
@WorkerThread
181181
public PairedSecurityKey setupPairedKey(PinProvider pinProvider) throws IOException {
182182
ByteSecret pairedPin = pinProvider.getPin(getOpenPgpInstanceAid());
183-
return setupPairedKey(pairedPin, pairedPin);
183+
ByteSecret pairedPuk = pinProvider.getPuk(getOpenPgpInstanceAid());
184+
return setupPairedKey(pairedPin, pairedPuk);
184185
}
185186

186187
@WorkerThread
@@ -190,8 +191,8 @@ public PairedSecurityKey setupPairedKey(ByteSecret newPin, ByteSecret newPuk) th
190191

191192
@WorkerThread
192193
public PairedSecurityKey setupPairedKey(ByteSecret newPin, ByteSecret newPuk, boolean encryptionOnly) throws IOException {
193-
boolean verifySuccessful = verifyAdminPinIfPossible();
194-
if (!verifySuccessful) {
194+
boolean isInFactoryDefaultState = isInFactoryDefaultState();
195+
if (!isInFactoryDefaultState) {
195196
wipeAndVerify();
196197
}
197198

@@ -213,7 +214,7 @@ public PairedSecurityKey setupPairedKey(ByteSecret newPin, ByteSecret newPuk, bo
213214
KeyPair encryptionKeyPair = rsaEncryptUtil.generateRsa2048KeyPair();
214215
byte[] encryptFingerprint = changeKeyRsaOp.changeKey(KeyType.ENCRYPT, encryptionKeyPair, timestamp);
215216

216-
updatePinPukUsingDefaultPuk(newPin, newPuk);
217+
updatePinAndPukUsingDefaultPuk(newPin, newPuk);
217218

218219
openPgpAppletConnection.refreshConnectionCapabilities();
219220

@@ -229,7 +230,7 @@ public PairedSecurityKey setupPairedKey(ByteSecret newPin, ByteSecret newPuk, bo
229230
byte[] encryptFingerprint = changeKeyRsaOp.changeKey(KeyType.ENCRYPT, encryptionKeyPair, timestamp);
230231
byte[] signFingerprint = changeKeyRsaOp.changeKey(KeyType.SIGN, signKeyPair, timestamp);
231232
byte[] authFingerprint = changeKeyRsaOp.changeKey(KeyType.AUTH, authKeyPair, timestamp);
232-
updatePinPukUsingDefaultPuk(newPin, newPuk);
233+
updatePinAndPukUsingDefaultPuk(newPin, newPuk);
233234

234235
openPgpAppletConnection.refreshConnectionCapabilities();
235236

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,15 @@
2828
import androidx.annotation.RestrictTo;
2929
import androidx.annotation.RestrictTo.Scope;
3030
import de.cotech.hw.exceptions.AuthenticationMethodBlockedException;
31-
import de.cotech.hw.exceptions.SecurityStatusNotSatisfiedException;
3231

33-
public class OpenPgpCardBlockedException extends AuthenticationMethodBlockedException {
34-
public static final int SW_CARD_BLOCKED = SW_AUTHENTICATION_METHOD_BLOCKED;
32+
public class OpenPgpLockedException extends AuthenticationMethodBlockedException {
33+
public static final int SW_OPENPGP_LOCKED = SW_AUTHENTICATION_METHOD_BLOCKED;
34+
35+
// older YubiKey NEO returns 63C0 (0 retries), reproduce with YubiKey NEO, SN 2624165
36+
public static final int SW_OPENPGP_LOCKED_YKNEO = 0x63C0;
3537

3638
@RestrictTo(Scope.LIBRARY_GROUP)
37-
public OpenPgpCardBlockedException() {
38-
super("Security Key returned error, card blocked / the PIN/PUK has been entered 3 times wrong.");
39+
public OpenPgpLockedException() {
40+
super("Security Key returned error: PIN/PUK locked, an incorrect PIN/PUK has been entered 3 times.");
3941
}
4042
}

hwsecurity-openpgp/src/main/java/de/cotech/hw/openpgp/exceptions/OpenPgpPinTooShortException.java

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,26 +30,23 @@
3030
import de.cotech.hw.exceptions.WrongRequestLengthException;
3131

3232
public class OpenPgpPinTooShortException extends SecurityKeyException {
33-
/*
34-
* Used in
35-
* - ykneo-openpgp >= 1.0.11
36-
* https://github.com/Yubico/ykneo-openpgp/commit/b49ce8241917e7c087a4dab7b2c755420ff4500f
37-
* - YubiKey 5C
38-
*/
39-
public static final int SW_WRONG_DATA = WrongDataException.SW_WRONG_DATA;
33+
/*
34+
* Used in
35+
* - ykneo-openpgp >= 1.0.11
36+
* https://github.com/Yubico/ykneo-openpgp/commit/b49ce8241917e7c087a4dab7b2c755420ff4500f
37+
* - YubiKey 5C
38+
*/
39+
public static final int SW_WRONG_DATA = WrongDataException.SW_WRONG_DATA;
4040

41-
/*
42-
* Used in
43-
* - ykneo-openpgp < 1.0.10
44-
* - SmartPGP
45-
*/
46-
public static final int SW_WRONG_REQUEST_LENGTH = WrongRequestLengthException.SW_WRONG_REQUEST_LENGTH;
41+
/*
42+
* Used in
43+
* - ykneo-openpgp < 1.0.10
44+
* - SmartPGP
45+
*/
46+
public static final int SW_WRONG_REQUEST_LENGTH = WrongRequestLengthException.SW_WRONG_REQUEST_LENGTH;
4747

48-
public OpenPgpPinTooShortException() {
49-
super("OpenPgpPinTooShortException", 0);
50-
}
48+
public OpenPgpPinTooShortException() {
49+
super("Security Key returned error: PIN too short.", 0);
50+
}
5151

52-
public OpenPgpPinTooShortException(String longErrorMessage) {
53-
super(longErrorMessage, "OpenPgpPinTooShortException", 0);
54-
}
5552
}

0 commit comments

Comments
 (0)