From 626c08bbbe7b2522aa48facd23fad027c90b0f53 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 12 Jan 2018 16:17:28 +0100 Subject: [PATCH] extract ModifyPinUseCase --- .../securitytoken/ModifyPinUseCase.java | 76 ++++++++++++++++ .../SecurityTokenConnection.java | 89 ++++--------------- .../SecurityTokenPsoSignUseCase.java | 2 +- ...curityTokenChangePinOperationActivity.java | 3 +- .../ui/SecurityTokenOperationActivity.java | 7 +- 5 files changed, 100 insertions(+), 77 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/ModifyPinUseCase.java diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/ModifyPinUseCase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/ModifyPinUseCase.java new file mode 100644 index 000000000..66cc5e306 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/ModifyPinUseCase.java @@ -0,0 +1,76 @@ +package org.sufficientlysecure.keychain.securitytoken; + + +import java.io.IOException; + +import org.sufficientlysecure.keychain.util.Passphrase; + + +public class ModifyPinUseCase { + private static final int MAX_PW3_LENGTH_INDEX = 3; + private static final int MIN_PW3_LENGTH = 8; + + private final SecurityTokenConnection connection; + private final Passphrase adminPin; + + public static ModifyPinUseCase create(SecurityTokenConnection connection, Passphrase adminPin) { + return new ModifyPinUseCase(connection, adminPin); + } + + private ModifyPinUseCase(SecurityTokenConnection connection, + Passphrase adminPin) { + this.connection = connection; + this.adminPin = adminPin; + } + + public void modifyPw1andPw3Pins(byte[] newPin, byte[] newAdminPin) throws IOException { + // Order is important for Gnuk, otherwise it will be set up in "admin less mode". + // http://www.fsij.org/doc-gnuk/gnuk-passphrase-setting.html#set-up-pw1-pw3-and-reset-code + modifyPw3Pin(newAdminPin); + modifyPw1PinWithEffectiveAdminPin(new Passphrase(new String(newAdminPin)), newPin); + } + + public void modifyPw1Pin(byte[] newPin) throws IOException { + modifyPw1PinWithEffectiveAdminPin(adminPin, newPin); + } + + private void modifyPw1PinWithEffectiveAdminPin(Passphrase effectiveAdminPin, byte[] newPin) throws IOException { + connection.verifyAdminPin(effectiveAdminPin); + + final int MAX_PW1_LENGTH_INDEX = 1; + byte[] pwStatusBytes = connection.getPwStatusBytes(); + if (newPin.length < 6 || newPin.length > pwStatusBytes[MAX_PW1_LENGTH_INDEX]) { + throw new IOException("Invalid PIN length"); + } + + CommandApdu changePin = connection.getCommandFactory().createResetPw1Command(newPin); + ResponseApdu response = connection.communicate(changePin); + + if (!response.isSuccess()) { + throw new CardException("Failed to change PIN", response.getSw()); + } + } + + /** + * Modifies the user's PW3. Before sending, the new PIN will be validated for + * conformance to the token's requirements for key length. + */ + private void modifyPw3Pin(byte[] newAdminPin) throws IOException { + byte[] pwStatusBytes = connection.getPwStatusBytes(); + + if (newAdminPin.length < MIN_PW3_LENGTH || newAdminPin.length > pwStatusBytes[MAX_PW3_LENGTH_INDEX]) { + throw new IOException("Invalid PIN length"); + } + + byte[] pin = adminPin.toStringUnsafe().getBytes(); + + CommandApdu changePin = connection.getCommandFactory().createChangePw3Command(pin, newAdminPin); + ResponseApdu response = connection.communicate(changePin); + + connection.invalidatePw3(); + + if (!response.isSuccess()) { + throw new CardException("Failed to change PIN", response.getSw()); + } + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnection.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnection.java index 33fd48c04..26a68469b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnection.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnection.java @@ -96,12 +96,6 @@ public class SecurityTokenConnection { return commandFactory; } - void maybeInvalidatePw1() { - if (!openPgpCapabilities.isPw1ValidForMultipleSignatures()) { - isPw1ValidatedForSignature = false; - } - } - private String getHolderName(byte[] name) { try { return (new String(name, 4, name[3])).replace('<', ' '); @@ -197,67 +191,18 @@ public class SecurityTokenConnection { this.cardCapabilities = new CardCapabilities(openPgpCapabilities.getHistoricalBytes()); } - public void resetPin(byte[] newPin, Passphrase adminPin) throws IOException { - if (!isPw3Validated) { - verifyAdminPin(adminPin); - } - - final int MAX_PW1_LENGTH_INDEX = 1; - byte[] pwStatusBytes = getPwStatusBytes(); - if (newPin.length < 6 || newPin.length > pwStatusBytes[MAX_PW1_LENGTH_INDEX]) { - throw new IOException("Invalid PIN length"); - } - - // Command APDU for RESET RETRY COUNTER command (page 33) - CommandApdu changePin = commandFactory.createResetPw1Command(newPin); - ResponseApdu response = communicate(changePin); - - if (!response.isSuccess()) { - throw new CardException("Failed to change PIN", response.getSw()); - } - } - - /** - * Modifies the user's PW3. Before sending, the new PIN will be validated for - * conformance to the token's requirements for key length. - * - * @param newAdminPin The new PW3. - */ - public void modifyPw3Pin(byte[] newAdminPin, Passphrase adminPin) throws IOException { - final int MAX_PW3_LENGTH_INDEX = 3; - - byte[] pwStatusBytes = getPwStatusBytes(); - - if (newAdminPin.length < 8 || newAdminPin.length > pwStatusBytes[MAX_PW3_LENGTH_INDEX]) { - throw new IOException("Invalid PIN length"); - } - - byte[] pin = adminPin.toStringUnsafe().getBytes(); - - CommandApdu changePin = commandFactory.createChangePw3Command(pin, newAdminPin); - ResponseApdu response = communicate(changePin); - - isPw3Validated = false; - - if (!response.isSuccess()) { - throw new CardException("Failed to change PIN", response.getSw()); - } - } - - /** - * Verifies the user's PW1 with the appropriate mode. - */ void verifyPinForSignature() throws IOException { if (isPw1ValidatedForSignature) { return; } - if (cachedPin == null) { throw new IllegalStateException("Connection not initialized with Pin!"); } + byte[] pin = cachedPin.toStringUnsafe().getBytes(); - ResponseApdu response = communicate(commandFactory.createVerifyPw1ForSignatureCommand(pin)); + CommandApdu verifyPw1ForSignatureCommand = commandFactory.createVerifyPw1ForSignatureCommand(pin); + ResponseApdu response = communicate(verifyPw1ForSignatureCommand); if (!response.isSuccess()) { throw new CardException("Bad PIN!", response.getSw()); } @@ -265,9 +210,6 @@ public class SecurityTokenConnection { isPw1ValidatedForSignature = true; } - /** - * Verifies the user's PW1 with the appropriate mode. - */ void verifyPinForOther() throws IOException { if (isPw1ValidatedForOther) { return; @@ -278,8 +220,8 @@ public class SecurityTokenConnection { byte[] pin = cachedPin.toStringUnsafe().getBytes(); - // Command APDU for VERIFY command (page 32) - ResponseApdu response = communicate(commandFactory.createVerifyPw1ForOtherCommand(pin)); + CommandApdu verifyPw1ForOtherCommand = commandFactory.createVerifyPw1ForOtherCommand(pin); + ResponseApdu response = communicate(verifyPw1ForOtherCommand); if (!response.isSuccess()) { throw new CardException("Bad PIN!", response.getSw()); } @@ -287,16 +229,13 @@ public class SecurityTokenConnection { isPw1ValidatedForOther = true; } - /** - * Verifies the user's PW1 or PW3 with the appropriate mode. - */ void verifyAdminPin(Passphrase adminPin) throws IOException { if (isPw3Validated) { return; } - // Command APDU for VERIFY command (page 32) - ResponseApdu response = - communicate(commandFactory.createVerifyPw3Command(adminPin.toStringUnsafe().getBytes())); + + CommandApdu verifyPw3Command = commandFactory.createVerifyPw3Command(adminPin.toStringUnsafe().getBytes()); + ResponseApdu response = communicate(verifyPw3Command); if (!response.isSuccess()) { throw new CardException("Bad PIN!", response.getSw()); } @@ -304,6 +243,16 @@ public class SecurityTokenConnection { isPw3Validated = true; } + void invalidateSingleUsePw1() { + if (!openPgpCapabilities.isPw1ValidForMultipleSignatures()) { + isPw1ValidatedForSignature = false; + } + } + + void invalidatePw3() { + isPw3Validated = false; + } + /** * Return fingerprints of all keys from application specific data stored * on tag, or null if data not available. @@ -319,7 +268,7 @@ public class SecurityTokenConnection { * * @return Seven bytes in fixed format, plus 0x9000 status word at the end. */ - private byte[] getPwStatusBytes() throws IOException { + byte[] getPwStatusBytes() throws IOException { return openPgpCapabilities.getPwStatusBytes(); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenPsoSignUseCase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenPsoSignUseCase.java index 2ce483422..78e6811aa 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenPsoSignUseCase.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenPsoSignUseCase.java @@ -144,7 +144,7 @@ public class SecurityTokenPsoSignUseCase { CommandApdu command = connection.getCommandFactory().createComputeDigitalSignatureCommand(data); ResponseApdu response = connection.communicate(command); - connection.maybeInvalidatePw1(); + connection.invalidateSingleUsePw1(); if (!response.isSuccess()) { throw new CardException("Failed to sign", response.getSw()); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenChangePinOperationActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenChangePinOperationActivity.java index 349676e80..2bad186e2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenChangePinOperationActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenChangePinOperationActivity.java @@ -32,6 +32,7 @@ import android.widget.ViewAnimator; import nordpol.android.NfcGuideView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.securitytoken.ModifyPinUseCase; import org.sufficientlysecure.keychain.securitytoken.SecurityTokenConnection; import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo; import org.sufficientlysecure.keychain.service.input.SecurityTokenChangePinParcel; @@ -141,7 +142,7 @@ public class SecurityTokenChangePinOperationActivity extends BaseSecurityTokenAc @Override protected void doSecurityTokenInBackground(SecurityTokenConnection stConnection) throws IOException { Passphrase adminPin = new Passphrase(changePinInput.getAdminPin()); - stConnection.resetPin(changePinInput.getNewPin().getBytes(), adminPin); + ModifyPinUseCase.create(stConnection, adminPin).modifyPw1Pin(changePinInput.getNewPin().getBytes()); resultTokenInfo = stConnection.getTokenInfo(); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java index a40570538..68f472ebf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java @@ -40,6 +40,7 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing; import org.sufficientlysecure.keychain.provider.KeyRepository; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.securitytoken.KeyType; +import org.sufficientlysecure.keychain.securitytoken.ModifyPinUseCase; import org.sufficientlysecure.keychain.securitytoken.SecurityTokenConnection; import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo; import org.sufficientlysecure.keychain.securitytoken.PsoDecryptUseCase; @@ -296,11 +297,7 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity { mInputParcel = mInputParcel.withCryptoData(subkeyBytes, tokenSerialNumber); } - // First set Admin PIN, then PIN. - // Order is important for Gnuk, otherwise it will be set up in "admin less mode". - // http://www.fsij.org/doc-gnuk/gnuk-passphrase-setting.html#set-up-pw1-pw3-and-reset-code - stConnection.modifyPw3Pin(newAdminPin, adminPin); - stConnection.resetPin(newPin, new Passphrase(new String(newAdminPin))); + ModifyPinUseCase.create(stConnection, adminPin).modifyPw1andPw3Pins(newPin, newAdminPin); SecurityTokenConnection.clearCachedConnections();