Merge branch 'master' into encrypted-export
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ public class CreateYubiKeyImportFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNfcPostExecute() throws IOException {
|
public void onNfcPostExecute() {
|
||||||
|
|
||||||
setData();
|
setData();
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
2
extern/spongycastle
vendored
2
extern/spongycastle
vendored
Submodule extern/spongycastle updated: b767c5afff...432b7f1bfd
Reference in New Issue
Block a user