Merge branch 'master' into encrypted-export

This commit is contained in:
Vincent Breitmoser
2015-10-01 17:28:43 +02:00
13 changed files with 144 additions and 117 deletions

View File

@@ -26,7 +26,7 @@ import java.io.InputStream;
* general Java InputStream. * general Java InputStream.
* Like the various Stream-classes from Java, the BitInputStream * Like the various Stream-classes from Java, the BitInputStream
* has to be created based on another Input stream. It provides * has to be created based on another Input stream. It provides
* a function to read the next bit from the sream, as well as to read multiple * a function to read the next bit from the stream, as well as to read multiple
* bits at once and write the resulting data into an integer value. * bits at once and write the resulting data into an integer value.
* <p/> * <p/>
* source: http://developer.nokia.com/Community/Wiki/Bit_Input/Output_Stream_utility_classes_for_efficient_data_transfer * source: http://developer.nokia.com/Community/Wiki/Bit_Input/Output_Stream_utility_classes_for_efficient_data_transfer
@@ -69,11 +69,11 @@ public class BitInputStream {
* @return integer value containing the bits read from the stream. * @return integer value containing the bits read from the stream.
* @throws IOException * @throws IOException
*/ */
synchronized public int readBits(final int aNumberOfBits) synchronized public int readBits(final int aNumberOfBits, boolean stuffIfEnd)
throws IOException { throws IOException {
int value = 0; int value = 0;
for (int i = aNumberOfBits - 1; i >= 0; i--) { for (int i = aNumberOfBits - 1; i >= 0; i--) {
value |= (readBit() << i); value |= (readBit(stuffIfEnd) << i);
} }
return value; return value;
} }
@@ -92,15 +92,20 @@ public class BitInputStream {
* @return 0 if the bit is 0, 1 if the bit is 1. * @return 0 if the bit is 0, 1 if the bit is 1.
* @throws IOException * @throws IOException
*/ */
synchronized public int readBit() throws IOException { synchronized public int readBit(boolean stuffIfEnd) throws IOException {
if (iIs == null) if (iIs == null)
throw new IOException("Already closed"); throw new IOException("Already closed");
if (iNextBit == 8) { if (iNextBit == 8) {
iBuffer = iIs.read(); iBuffer = iIs.read();
if (iBuffer == -1) if (iBuffer == -1) {
throw new EOFException(); if (stuffIfEnd) {
return 1;
} else {
throw new EOFException();
}
}
iNextBit = 0; iNextBit = 0;
} }

View File

@@ -99,8 +99,12 @@ public class SentenceConfirm {
*/ */
private EntropyString getWord(List<String> words, BitInputStream bin) throws IOException { private EntropyString getWord(List<String> words, BitInputStream bin) throws IOException {
final int neededBits = log(words.size(), 2); final int neededBits = log(words.size(), 2);
Log.d(Constants.TAG, "need " + neededBits + " bits of entropy"); Log.d(Constants.TAG, "need: " + neededBits + " bits of entropy");
int bits = bin.readBits(neededBits); Log.d(Constants.TAG, "available: " + bin.available() + " bits");
if (neededBits > bin.available()) {
Log.d(Constants.TAG, "stuffed with " + (neededBits - bin.available()) + " ones!");
}
int bits = bin.readBits(neededBits, true);
Log.d(Constants.TAG, "got word " + words.get(bits) + " with " + neededBits + " bits of entropy"); Log.d(Constants.TAG, "got word " + words.get(bits) + " with " + neededBits + " bits of entropy");
return new EntropyString(words.get(bits), neededBits); return new EntropyString(words.get(bits), neededBits);
} }
@@ -108,7 +112,7 @@ public class SentenceConfirm {
private EntropyString getNounPhrase(BitInputStream bits) throws IOException { private EntropyString getNounPhrase(BitInputStream bits) throws IOException {
final EntropyString phrase = new EntropyString(); final EntropyString phrase = new EntropyString();
phrase.append(getWord(art, bits)).append(" "); phrase.append(getWord(art, bits)).append(" ");
if (bits.readBit() != 0) { if (bits.readBit(true) != 0) {
phrase.append(getWord(adj, bits)).append(" "); phrase.append(getWord(adj, bits)).append(" ");
} }
phrase.incBits(); phrase.incBits();
@@ -121,7 +125,7 @@ public class SentenceConfirm {
EntropyString getSentence(BitInputStream bits) throws IOException { EntropyString getSentence(BitInputStream bits) throws IOException {
final EntropyString sentence = new EntropyString(); final EntropyString sentence = new EntropyString();
sentence.append(getNounPhrase(bits)); // Subject sentence.append(getNounPhrase(bits)); // Subject
if (bits.readBit() != 0) { if (bits.readBit(true) != 0) {
sentence.append(" ").append(getWord(vt, bits)); // Transitive verb sentence.append(" ").append(getWord(vt, bits)); // Transitive verb
sentence.append(" ").append(getNounPhrase(bits)); // Object of transitive verb sentence.append(" ").append(getNounPhrase(bits)); // Object of transitive verb
} else { } else {
@@ -129,17 +133,17 @@ public class SentenceConfirm {
} }
sentence.incBits(); sentence.incBits();
if (bits.readBit() != 0) { if (bits.readBit(true) != 0) {
sentence.append(" ").append(getWord(adv, bits)); // Adverb sentence.append(" ").append(getWord(adv, bits)); // Adverb
} }
sentence.incBits(); sentence.incBits();
if (bits.readBit() != 0) { if (bits.readBit(true) != 0) {
sentence.append(" ").append(getWord(p, bits)); // Preposition sentence.append(" ").append(getWord(p, bits)); // Preposition
sentence.append(" ").append(getNounPhrase(bits)); // Object of preposition sentence.append(" ").append(getNounPhrase(bits)); // Object of preposition
} }
sentence.incBits(); sentence.incBits();
Log.d(Constants.TAG, "got sentence " + sentence + " with " + sentence.getBits() + " bits of entropy"); Log.d(Constants.TAG, "got sentence '" + sentence + "' with " + sentence.getBits() + " bits of entropy");
// uppercase first character, end with dot (without increasing the bits) // uppercase first character, end with dot (without increasing the bits)
sentence.getBuilder().replace(0, 1, sentence.getBuilder().replace(0, 1,

View File

@@ -191,9 +191,10 @@ public class CertifyFingerprintFragment extends LoaderFragment implements
String fingerprint; String fingerprint;
try { try {
fingerprint = new SentenceConfirm(getActivity()).fromBytes(fingerprintBlob, 16); fingerprint = new SentenceConfirm(getActivity()).fromBytes(fingerprintBlob, 20);
} catch (IOException ioe) { } catch (IOException e) {
fingerprint = "-"; fingerprint = "-";
Log.e(Constants.TAG, "Problem when creating sentence!", e);
} }
mFingerprint.setTextSize(18); mFingerprint.setTextSize(18);

View File

@@ -30,7 +30,6 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity; import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
@@ -120,13 +119,9 @@ public class CreateKeyActivity extends BaseNfcActivity {
setTitle(R.string.title_import_keys); setTitle(R.string.title_import_keys);
} else { } else {
// Fragment frag = CreateYubiKeyBlankFragment.newInstance(); Fragment frag = CreateYubiKeyBlankFragment.newInstance();
// loadFragment(frag, FragAction.START); loadFragment(frag, FragAction.START);
// setTitle(R.string.title_manage_my_keys); setTitle(R.string.title_manage_my_keys);
Notify.create(this,
"YubiKey key creation is currently not supported. Please follow our FAQ.",
Notify.Style.ERROR
).show();
} }
// done // done
@@ -160,7 +155,7 @@ public class CreateKeyActivity extends BaseNfcActivity {
} }
@Override @Override
protected void onNfcPostExecute() throws IOException { protected void onNfcPostExecute() {
if (mCurrentFragment instanceof NfcListenerFragment) { if (mCurrentFragment instanceof NfcListenerFragment) {
((NfcListenerFragment) mCurrentFragment).onNfcPostExecute(); ((NfcListenerFragment) mCurrentFragment).onNfcPostExecute();
return; return;
@@ -186,25 +181,23 @@ public class CreateKeyActivity extends BaseNfcActivity {
loadFragment(frag, FragAction.TO_RIGHT); loadFragment(frag, FragAction.TO_RIGHT);
} }
} else { } else {
// Fragment frag = CreateYubiKeyBlankFragment.newInstance(); Fragment frag = CreateYubiKeyBlankFragment.newInstance();
// loadFragment(frag, FragAction.TO_RIGHT); loadFragment(frag, FragAction.TO_RIGHT);
Notify.create(this,
"YubiKey key creation is currently not supported. Please follow our FAQ.",
Notify.Style.ERROR
).show();
} }
} }
private boolean containsKeys(byte[] scannedFingerprints) { private boolean containsKeys(byte[] scannedFingerprints) {
if (scannedFingerprints == null) {
return false;
}
// If all fingerprint bytes are 0, the card contains no keys. // If all fingerprint bytes are 0, the card contains no keys.
boolean cardContainsKeys = false;
for (byte b : scannedFingerprints) { for (byte b : scannedFingerprints) {
if (b != 0) { if (b != 0) {
cardContainsKeys = true; return true;
break;
} }
} }
return cardContainsKeys; return false;
} }
@Override @Override
@@ -264,7 +257,7 @@ public class CreateKeyActivity extends BaseNfcActivity {
interface NfcListenerFragment { interface NfcListenerFragment {
void doNfcInBackground() throws IOException; void doNfcInBackground() throws IOException;
void onNfcPostExecute() throws IOException; void onNfcPostExecute();
} }
@Override @Override

View File

@@ -208,7 +208,7 @@ public class CreateYubiKeyImportFragment
} }
@Override @Override
public void onNfcPostExecute() throws IOException { public void onNfcPostExecute() {
setData(); setData();

View File

@@ -377,7 +377,7 @@ public class ImportKeysActivity extends BaseNfcActivity
} }
@Override @Override
protected void onNfcPostExecute() throws IOException { protected void onNfcPostExecute() {
// either way, finish after NFC AsyncTask // either way, finish after NFC AsyncTask
finish(); finish();
} }

View File

@@ -27,6 +27,7 @@ import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.Button; import android.widget.Button;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import android.widget.ViewAnimator; import android.widget.ViewAnimator;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
@@ -72,7 +73,7 @@ public class NfcOperationActivity extends BaseNfcActivity {
private RequiredInputParcel mRequiredInput; private RequiredInputParcel mRequiredInput;
private Intent mServiceIntent; private Intent mServiceIntent;
private static final byte[] BLANK_FINGERPRINT = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; private static final byte[] BLANK_FINGERPRINT = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
private CryptoInputParcel mInputParcel; private CryptoInputParcel mInputParcel;
@@ -245,7 +246,7 @@ public class NfcOperationActivity extends BaseNfcActivity {
} }
@Override @Override
protected void onNfcPostExecute() throws IOException { protected void onNfcPostExecute() {
if (mServiceIntent != null) { if (mServiceIntent != null) {
// if we're triggered by OpenPgpService // if we're triggered by OpenPgpService
// save updated cryptoInputParcel in cache // save updated cryptoInputParcel in cache
@@ -276,6 +277,7 @@ public class NfcOperationActivity extends BaseNfcActivity {
} }
} }
} }
@Override @Override
protected void onPostExecute(Void result) { protected void onPostExecute(Void result) {
super.onPostExecute(result); super.onPostExecute(result);
@@ -292,25 +294,14 @@ public class NfcOperationActivity extends BaseNfcActivity {
vAnimator.setDisplayedChild(3); vAnimator.setDisplayedChild(3);
} }
private boolean shouldPutKey(byte[] fingerprint, int idx) throws IOException {
byte[] cardFingerprint = nfcGetFingerprint(idx);
// Slot is empty, or contains this key already. PUT KEY operation is safe
if (Arrays.equals(cardFingerprint, BLANK_FINGERPRINT) ||
Arrays.equals(cardFingerprint, fingerprint)) {
return true;
}
// Slot already contains a different key; don't overwrite it.
return false;
}
@Override @Override
public void handlePinError() { public void onPinError() {
// avoid a loop // avoid a loop
Preferences prefs = Preferences.getPreferences(this); Preferences prefs = Preferences.getPreferences(this);
if (prefs.useDefaultYubiKeyPin()) { if (prefs.useDefaultYubiKeyPin()) {
toast(getString(R.string.error_pin_nodefault)); // use Toast because activity is finished afterwards
Toast.makeText(this, R.string.error_pin_nodefault, Toast.LENGTH_LONG).show();
setResult(RESULT_CANCELED); setResult(RESULT_CANCELED);
finish(); finish();
return; return;
@@ -321,4 +312,23 @@ public class NfcOperationActivity extends BaseNfcActivity {
this, mRequiredInput.getMasterKeyId(), mRequiredInput.getSubKeyId()); this, mRequiredInput.getMasterKeyId(), mRequiredInput.getSubKeyId());
} }
private boolean shouldPutKey(byte[] fingerprint, int idx) throws IOException {
byte[] cardFingerprint = nfcGetMasterKeyFingerprint(idx);
// Note: special case: This should not happen, but happens with
// https://github.com/FluffyKaon/OpenPGP-Card, thus for now assume true
if (cardFingerprint == null) {
return true;
}
// Slot is empty, or contains this key already. PUT KEY operation is safe
if (Arrays.equals(cardFingerprint, BLANK_FINGERPRINT) ||
Arrays.equals(cardFingerprint, fingerprint)) {
return true;
}
// Slot already contains a different key; don't overwrite it.
return false;
}
} }

View File

@@ -555,7 +555,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements
} }
@Override @Override
protected void onNfcPostExecute() throws IOException { protected void onNfcPostExecute() {
long yubiKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mNfcFingerprints); long yubiKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mNfcFingerprints);

View File

@@ -103,7 +103,7 @@ public abstract class BaseNfcActivity extends BaseActivity {
/** /**
* Override to handle result of NFC operations (UI thread) * Override to handle result of NFC operations (UI thread)
*/ */
protected void onNfcPostExecute() throws IOException { protected void onNfcPostExecute() {
final long subKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mNfcFingerprints); final long subKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mNfcFingerprints);
@@ -134,9 +134,19 @@ public abstract class BaseNfcActivity extends BaseActivity {
Notify.create(this, error, Style.WARN).show(); Notify.create(this, error, Style.WARN).show();
} }
/**
* Override to do something when PIN is wrong, e.g., clear passphrases (UI thread)
*/
protected void onPinError() {
// use Toast because activity is finished afterwards
Toast.makeText(this, R.string.error_pin_wrong, Toast.LENGTH_LONG).show();
setResult(RESULT_CANCELED);
finish();
}
public void handleIntentInBackground(final Intent intent) { public void handleIntentInBackground(final Intent intent) {
// Actual NFC operations are executed in doInBackground to not block the UI thread // Actual NFC operations are executed in doInBackground to not block the UI thread
new AsyncTask<Void, Void, Exception>() { new AsyncTask<Void, Void, IOException>() {
@Override @Override
protected void onPreExecute() { protected void onPreExecute() {
super.onPreExecute(); super.onPreExecute();
@@ -144,11 +154,9 @@ public abstract class BaseNfcActivity extends BaseActivity {
} }
@Override @Override
protected Exception doInBackground(Void... params) { protected IOException doInBackground(Void... params) {
try { try {
handleTagDiscoveredIntent(intent); handleTagDiscoveredIntent(intent);
} catch (CardException e) {
return e;
} catch (IOException e) { } catch (IOException e) {
return e; return e;
} }
@@ -157,7 +165,7 @@ public abstract class BaseNfcActivity extends BaseActivity {
} }
@Override @Override
protected void onPostExecute(Exception exception) { protected void onPostExecute(IOException exception) {
super.onPostExecute(exception); super.onPostExecute(exception);
if (exception != null) { if (exception != null) {
@@ -165,11 +173,7 @@ public abstract class BaseNfcActivity extends BaseActivity {
return; return;
} }
try { onNfcPostExecute();
onNfcPostExecute();
} catch (IOException e) {
handleNfcError(e);
}
} }
}.execute(); }.execute();
} }
@@ -221,22 +225,30 @@ public abstract class BaseNfcActivity extends BaseActivity {
} }
} }
private void handleNfcError(Exception e) { private void handleNfcError(IOException e) {
Log.e(Constants.TAG, "nfc error", e);
if (e instanceof TagLostException) { if (e instanceof TagLostException) {
onNfcError(getString(R.string.error_nfc_tag_lost)); onNfcError(getString(R.string.error_nfc_tag_lost));
return; return;
} }
if (e instanceof IsoDepNotSupportedException) {
onNfcError(getString(R.string.error_nfc_iso_dep_not_supported));
return;
}
short status; short status;
if (e instanceof CardException) { if (e instanceof CardException) {
status = ((CardException) e).getResponseCode(); status = ((CardException) e).getResponseCode();
} else { } else {
status = -1; status = -1;
} }
// When entering a PIN, a status of 63CX indicates X attempts remaining.
if ((status & (short)0xFFF0) == 0x63C0) { // Wrong PIN, a status of 63CX indicates X attempts remaining.
if ((status & (short) 0xFFF0) == 0x63C0) {
// hook to do something different when PIN is wrong
onPinError();
int tries = status & 0x000F; int tries = status & 0x000F;
onNfcError(getResources().getQuantityString(R.plurals.error_pin, tries, tries)); onNfcError(getResources().getQuantityString(R.plurals.error_pin, tries, tries));
return; return;
@@ -307,12 +319,6 @@ public abstract class BaseNfcActivity extends BaseActivity {
} }
public void handlePinError() {
toast("Wrong PIN!");
setResult(RESULT_CANCELED);
finish();
}
/** /**
* Called when the system is about to start resuming a previous activity, * Called when the system is about to start resuming a previous activity,
* disables NFC Foreground Dispatch * disables NFC Foreground Dispatch
@@ -337,7 +343,7 @@ public abstract class BaseNfcActivity extends BaseActivity {
protected void obtainYubiKeyPin(RequiredInputParcel requiredInput) { protected void obtainYubiKeyPin(RequiredInputParcel requiredInput) {
// shortcut if we only use the default yubikey pin // shortcut if we only use the default YubiKey pin
Preferences prefs = Preferences.getPreferences(this); Preferences prefs = Preferences.getPreferences(this);
if (prefs.useDefaultYubiKeyPin()) { if (prefs.useDefaultYubiKeyPin()) {
mPin = new Passphrase("123456"); mPin = new Passphrase("123456");
@@ -345,10 +351,10 @@ public abstract class BaseNfcActivity extends BaseActivity {
} }
try { try {
Passphrase phrase = PassphraseCacheService.getCachedPassphrase(this, Passphrase passphrase = PassphraseCacheService.getCachedPassphrase(this,
requiredInput.getMasterKeyId(), requiredInput.getSubKeyId()); requiredInput.getMasterKeyId(), requiredInput.getSubKeyId());
if (phrase != null) { if (passphrase != null) {
mPin = phrase; mPin = passphrase;
return; return;
} }
@@ -363,10 +369,6 @@ public abstract class BaseNfcActivity extends BaseActivity {
} }
protected void setYubiKeyPin(Passphrase pin) {
mPin = pin;
}
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) { switch (requestCode) {
@@ -406,6 +408,9 @@ public abstract class BaseNfcActivity extends BaseActivity {
// Connect to the detected tag, setting a couple of settings // Connect to the detected tag, setting a couple of settings
mIsoDep = IsoDep.get(detectedTag); mIsoDep = IsoDep.get(detectedTag);
if (mIsoDep == null) {
throw new IsoDepNotSupportedException("Tag does not support ISO-DEP (ISO 14443-4)");
}
mIsoDep.setTimeout(TIMEOUT); // timeout is set to 100 seconds to avoid cancellation during calculation mIsoDep.setTimeout(TIMEOUT); // timeout is set to 100 seconds to avoid cancellation during calculation
mIsoDep.connect(); mIsoDep.connect();
@@ -448,7 +453,7 @@ public abstract class BaseNfcActivity extends BaseActivity {
* @return The long key id of the requested key, or null if not found. * @return The long key id of the requested key, or null if not found.
*/ */
public Long nfcGetKeyId(int idx) throws IOException { public Long nfcGetKeyId(int idx) throws IOException {
byte[] fp = nfcGetFingerprint(idx); byte[] fp = nfcGetMasterKeyFingerprint(idx);
if (fp == null) { if (fp == null) {
return null; return null;
} }
@@ -469,7 +474,7 @@ public abstract class BaseNfcActivity extends BaseActivity {
byte[] buf = mIsoDep.transceive(Hex.decode(data)); byte[] buf = mIsoDep.transceive(Hex.decode(data));
Iso7816TLV tlv = Iso7816TLV.readSingle(buf, true); Iso7816TLV tlv = Iso7816TLV.readSingle(buf, true);
Log.d(Constants.TAG, "nfc tlv data:\n" + tlv.prettyPrint()); Log.d(Constants.TAG, "nfcGetFingerprints() Iso7816TLV tlv data:\n" + tlv.prettyPrint());
Iso7816TLV fptlv = Iso7816TLV.findRecursive(tlv, 0xc5); Iso7816TLV fptlv = Iso7816TLV.findRecursive(tlv, 0xc5);
if (fptlv == null) { if (fptlv == null) {
@@ -494,8 +499,11 @@ public abstract class BaseNfcActivity extends BaseActivity {
* @param idx Index of the key to return the fingerprint from. * @param idx Index of the key to return the fingerprint from.
* @return The fingerprint of the requested key, or null if not found. * @return The fingerprint of the requested key, or null if not found.
*/ */
public byte[] nfcGetFingerprint(int idx) throws IOException { public byte[] nfcGetMasterKeyFingerprint(int idx) throws IOException {
byte[] data = nfcGetFingerprints(); byte[] data = nfcGetFingerprints();
if (data == null) {
return null;
}
// return the master key fingerprint // return the master key fingerprint
ByteBuffer fpbuf = ByteBuffer.wrap(data); ByteBuffer fpbuf = ByteBuffer.wrap(data);
@@ -507,14 +515,11 @@ public abstract class BaseNfcActivity extends BaseActivity {
} }
public byte[] nfcGetAid() throws IOException { public byte[] nfcGetAid() throws IOException {
String info = "00CA004F00"; String info = "00CA004F00";
return mIsoDep.transceive(Hex.decode(info)); return mIsoDep.transceive(Hex.decode(info));
} }
public String nfcGetUserId() throws IOException { public String nfcGetUserId() throws IOException {
String info = "00CA006500"; String info = "00CA006500";
return nfcGetHolderName(nfcCommunicate(info)); return nfcGetHolderName(nfcCommunicate(info));
} }
@@ -648,8 +653,6 @@ public abstract class BaseNfcActivity extends BaseActivity {
String decryptedSessionKey = nfcGetDataField(second); String decryptedSessionKey = nfcGetDataField(second);
Log.d(Constants.TAG, "decryptedSessionKey: " + decryptedSessionKey);
return Hex.decode(decryptedSessionKey); return Hex.decode(decryptedSessionKey);
} }
@@ -682,7 +685,6 @@ public abstract class BaseNfcActivity extends BaseActivity {
+ Hex.toHexString(pin); + Hex.toHexString(pin);
String response = nfcCommunicate(login); // login String response = nfcCommunicate(login); // login
if (!response.equals(accepted)) { if (!response.equals(accepted)) {
handlePinError();
throw new CardException("Bad PIN!", parseCardStatus(response)); throw new CardException("Bad PIN!", parseCardStatus(response));
} }
@@ -737,7 +739,6 @@ public abstract class BaseNfcActivity extends BaseActivity {
+ getHex(newPin); + getHex(newPin);
String response = nfcCommunicate(changeReferenceDataApdu); // change PIN String response = nfcCommunicate(changeReferenceDataApdu); // change PIN
if (!response.equals("9000")) { if (!response.equals("9000")) {
handlePinError();
throw new CardException("Failed to change PIN", parseCardStatus(response)); throw new CardException("Failed to change PIN", parseCardStatus(response));
} }
} }
@@ -905,15 +906,6 @@ public abstract class BaseNfcActivity extends BaseActivity {
} }
} }
/**
* Prints a message to the screen
*
* @param text the text which should be contained within the toast
*/
protected void toast(String text) {
Toast.makeText(this, text, Toast.LENGTH_LONG).show();
}
/** /**
* Receive new NFC Intents to this activity only by enabling foreground dispatch. * Receive new NFC Intents to this activity only by enabling foreground dispatch.
* This can only be done in onResume! * This can only be done in onResume!
@@ -930,12 +922,10 @@ public abstract class BaseNfcActivity extends BaseActivity {
new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED) new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED)
}; };
// https://code.google.com/p/android/issues/detail?id=62918
// maybe mNfcAdapter.enableReaderMode(); ?
try { try {
mNfcAdapter.enableForegroundDispatch(this, nfcPendingIntent, writeTagFilters, null); mNfcAdapter.enableForegroundDispatch(this, nfcPendingIntent, writeTagFilters, null);
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
Log.i(Constants.TAG, "NfcForegroundDispatch Error!", e); Log.i(Constants.TAG, "NfcForegroundDispatch Exception: Activity is not currently in the foreground?", e);
} }
Log.d(Constants.TAG, "NfcForegroundDispatch has been enabled!"); Log.d(Constants.TAG, "NfcForegroundDispatch has been enabled!");
} }
@@ -952,14 +942,23 @@ public abstract class BaseNfcActivity extends BaseActivity {
} }
public String nfcGetHolderName(String name) { public String nfcGetHolderName(String name) {
String slength; try {
int ilength; String slength;
name = name.substring(6); int ilength;
slength = name.substring(0, 2); name = name.substring(6);
ilength = Integer.parseInt(slength, 16) * 2; slength = name.substring(0, 2);
name = name.substring(2, ilength + 2); ilength = Integer.parseInt(slength, 16) * 2;
name = (new String(Hex.decode(name))).replace('<', ' '); name = name.substring(2, ilength + 2);
return (name); name = (new String(Hex.decode(name))).replace('<', ' ');
return name;
} catch (IndexOutOfBoundsException e) {
// try-catch for https://github.com/FluffyKaon/OpenPGP-Card
// Note: This should not happen, but happens with
// https://github.com/FluffyKaon/OpenPGP-Card, thus return an empty string for now!
Log.e(Constants.TAG, "Couldn't get holder name, returning empty string!", e);
return "";
}
} }
private String nfcGetDataField(String output) { private String nfcGetDataField(String output) {
@@ -974,6 +973,14 @@ public abstract class BaseNfcActivity extends BaseActivity {
return new String(Hex.encode(raw)); return new String(Hex.encode(raw));
} }
public class IsoDepNotSupportedException extends IOException {
public IsoDepNotSupportedException(String detailMessage) {
super(detailMessage);
}
}
public class CardException extends IOException { public class CardException extends IOException {
private short mResponseCode; private short mResponseCode;

View File

@@ -571,8 +571,9 @@ public class KeyFormattingUtils {
sigIcon = R.drawable.status_signature_invalid_cutout_24dp; sigIcon = R.drawable.status_signature_invalid_cutout_24dp;
sigColor = R.color.key_flag_red; sigColor = R.color.key_flag_red;
sigActionText = R.string.decrypt_result_action_show; // won't be used, but makes compiler happy
sigActionIcon = R.drawable.ic_vpn_key_grey_24dp; sigActionText = 0;
sigActionIcon = 0;
break; break;
} }
@@ -584,7 +585,8 @@ public class KeyFormattingUtils {
holder.getSignatureStatusText().setText(sigText); holder.getSignatureStatusText().setText(sigText);
holder.getSignatureStatusText().setTextColor(sigColorRes); holder.getSignatureStatusText().setTextColor(sigColorRes);
if (signatureResult.getResult() != OpenPgpSignatureResult.RESULT_NO_SIGNATURE) { if (signatureResult.getResult() != OpenPgpSignatureResult.RESULT_NO_SIGNATURE
&& signatureResult.getResult() != OpenPgpSignatureResult.RESULT_INVALID_SIGNATURE) {
// has a signature, thus display layouts // has a signature, thus display layouts
holder.getSignatureLayout().setVisibility(View.VISIBLE); holder.getSignatureLayout().setVisibility(View.VISIBLE);

View File

@@ -1535,8 +1535,10 @@
<string name="error_nfc_chaining_error">"YubiKey expected last command in a chain."</string> <string name="error_nfc_chaining_error">"YubiKey expected last command in a chain."</string>
<string name="error_nfc_header">"YubiKey reported invalid %s byte."</string> <string name="error_nfc_header">"YubiKey reported invalid %s byte."</string>
<string name="error_nfc_tag_lost">"YubiKey has been taken off too early. Keep the YubiKey at the back until the operation finishes."</string> <string name="error_nfc_tag_lost">"YubiKey has been taken off too early. Keep the YubiKey at the back until the operation finishes."</string>
<string name="error_nfc_iso_dep_not_supported">"Tag does not support ISO-DEP (ISO 14443-4)"</string>
<string name="error_nfc_try_again">"Try again"</string> <string name="error_nfc_try_again">"Try again"</string>
<string name="error_pin_nodefault">Default PIN was rejected!</string> <string name="error_pin_nodefault">Default PIN was rejected!</string>
<string name="error_pin_wrong">PIN is wrong!</string>
<string name="error_temp_file">Error creating temporary file.</string> <string name="error_temp_file">Error creating temporary file.</string>
<string name="btn_delete_original">Delete original file</string> <string name="btn_delete_original">Delete original file</string>

View File

@@ -28,6 +28,7 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog; import org.robolectric.shadows.ShadowLog;
import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.sufficientlysecure.keychain.BuildConfig; import org.sufficientlysecure.keychain.BuildConfig;
import org.sufficientlysecure.keychain.WorkaroundBuildConfig; import org.sufficientlysecure.keychain.WorkaroundBuildConfig;
import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult; import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
@@ -41,6 +42,7 @@ import org.sufficientlysecure.keychain.util.Passphrase;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.security.Security;
import java.util.Iterator; import java.util.Iterator;
import java.util.Random; import java.util.Random;
@@ -53,6 +55,7 @@ public class UncachedKeyringTest {
@BeforeClass @BeforeClass
public static void setUpOnce() throws Exception { public static void setUpOnce() throws Exception {
Security.insertProviderAt(new BouncyCastleProvider(), 1);
ShadowLog.stream = System.out; ShadowLog.stream = System.out;
SaveKeyringParcel parcel = new SaveKeyringParcel(); SaveKeyringParcel parcel = new SaveKeyringParcel();