mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-23 17:22:16 -05:00
move yubikey import into viewkeyfragment
This commit is contained in:
parent
04c7639a5a
commit
a7c52a1c9f
@ -17,12 +17,21 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui;
|
package org.sufficientlysecure.keychain.ui;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.app.FragmentTransaction;
|
import android.support.v4.app.FragmentTransaction;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||||
|
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||||
|
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.Notify;
|
||||||
|
import org.sufficientlysecure.keychain.ui.util.Notify.ActionListener;
|
||||||
|
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
|
||||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -87,9 +96,28 @@ public class CreateKeyActivity extends BaseNfcActivity {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] scannedFingerprint = nfcGetFingerprint(0);
|
byte[] scannedFingerprints = nfcGetFingerprints();
|
||||||
Fragment frag = CreateKeyYubiFragment.createInstance(scannedFingerprint);
|
|
||||||
|
try {
|
||||||
|
long masterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(scannedFingerprints);
|
||||||
|
CachedPublicKeyRing ring = new ProviderHelper(this).getCachedPublicKeyRing(masterKeyId);
|
||||||
|
ring.getMasterKeyId();
|
||||||
|
|
||||||
|
String userId = nfcGetUserId();
|
||||||
|
byte[] nfcAid = nfcGetAid();
|
||||||
|
|
||||||
|
Intent intent = new Intent(this, ViewKeyActivity.class);
|
||||||
|
intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
|
||||||
|
intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, nfcAid);
|
||||||
|
intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, userId);
|
||||||
|
intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, scannedFingerprints);
|
||||||
|
startActivity(intent);
|
||||||
|
finish();
|
||||||
|
|
||||||
|
} catch (PgpKeyNotFoundException e) {
|
||||||
|
Fragment frag = CreateKeyYubiFragment.createInstance(scannedFingerprints);
|
||||||
loadFragment(frag, FragAction.TO_RIGHT);
|
loadFragment(frag, FragAction.TO_RIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,48 +17,33 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui;
|
package org.sufficientlysecure.keychain.ui;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.Messenger;
|
import android.os.Messenger;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.app.LoaderManager;
|
import android.support.v4.app.LoaderManager;
|
||||||
import android.support.v4.content.CursorLoader;
|
|
||||||
import android.support.v4.content.Loader;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.ViewAnimator;
|
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
||||||
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
|
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
|
||||||
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
|
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
|
||||||
import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
|
||||||
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||||
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
||||||
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
|
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
|
||||||
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.NfcListenerFragment;
|
|
||||||
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.ui.util.Notify.Style;
|
|
||||||
import org.sufficientlysecure.keychain.ui.widget.NameEditText;
|
|
||||||
import org.sufficientlysecure.keychain.util.Preferences;
|
import org.sufficientlysecure.keychain.util.Preferences;
|
||||||
|
|
||||||
|
public class CreateKeyYubiFragment extends Fragment {
|
||||||
public class CreateKeyYubiFragment extends Fragment
|
|
||||||
implements LoaderManager.LoaderCallbacks<Cursor> {
|
|
||||||
|
|
||||||
private static final String ARG_FINGERPRINT = "fingerprint";
|
private static final String ARG_FINGERPRINT = "fingerprint";
|
||||||
|
|
||||||
@ -67,12 +52,7 @@ public class CreateKeyYubiFragment extends Fragment
|
|||||||
private byte[] mScannedFingerprint;
|
private byte[] mScannedFingerprint;
|
||||||
private long mScannedMasterKeyId;
|
private long mScannedMasterKeyId;
|
||||||
|
|
||||||
private ViewAnimator mAnimator;
|
|
||||||
private TextView mUnknownFingerprint;
|
private TextView mUnknownFingerprint;
|
||||||
private TextView mFingerprint;
|
|
||||||
private TextView mUserId;
|
|
||||||
|
|
||||||
private YubiImportState mState;
|
|
||||||
|
|
||||||
public static Fragment createInstance(byte[] scannedFingerprint) {
|
public static Fragment createInstance(byte[] scannedFingerprint) {
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
@ -91,26 +71,14 @@ public class CreateKeyYubiFragment extends Fragment
|
|||||||
mScannedFingerprint = getArguments().getByteArray(ARG_FINGERPRINT);
|
mScannedFingerprint = getArguments().getByteArray(ARG_FINGERPRINT);
|
||||||
mScannedMasterKeyId = getKeyIdFromFingerprint(mScannedFingerprint);
|
mScannedMasterKeyId = getKeyIdFromFingerprint(mScannedFingerprint);
|
||||||
|
|
||||||
getLoaderManager().initLoader(0, null, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum YubiImportState {
|
|
||||||
UNKNOWN, // scanned unknown key (ready to import)
|
|
||||||
BAD_FINGERPRINT, // scanned key, bad fingerprint
|
|
||||||
IMPORTED, // imported key (ready to promote)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
View view = inflater.inflate(R.layout.create_yubikey_fragment, container, false);
|
View view = inflater.inflate(R.layout.create_yubikey_fragment, container, false);
|
||||||
|
|
||||||
mAnimator = (ViewAnimator) view.findViewById(R.id.create_yubikey_animator);
|
|
||||||
|
|
||||||
mUnknownFingerprint = (TextView) view.findViewById(R.id.create_yubikey_unknown_fp);
|
mUnknownFingerprint = (TextView) view.findViewById(R.id.create_yubikey_unknown_fp);
|
||||||
|
|
||||||
mFingerprint = (TextView) view.findViewById(R.id.create_yubikey_fingerprint);
|
|
||||||
mUserId = (TextView) view.findViewById(R.id.create_yubikey_user_id);
|
|
||||||
|
|
||||||
View mBackButton = view.findViewById(R.id.create_key_back_button);
|
View mBackButton = view.findViewById(R.id.create_key_back_button);
|
||||||
View mNextButton = view.findViewById(R.id.create_key_next_button);
|
View mNextButton = view.findViewById(R.id.create_key_next_button);
|
||||||
|
|
||||||
@ -136,115 +104,8 @@ public class CreateKeyYubiFragment extends Fragment
|
|||||||
mCreateKeyActivity = (CreateKeyActivity) getActivity();
|
mCreateKeyActivity = (CreateKeyActivity) getActivity();
|
||||||
}
|
}
|
||||||
|
|
||||||
// These are the rows that we will retrieve.
|
|
||||||
static final String[] UNIFIED_PROJECTION = new String[]{
|
|
||||||
KeychainContract.KeyRings._ID,
|
|
||||||
KeychainContract.KeyRings.MASTER_KEY_ID,
|
|
||||||
KeychainContract.KeyRings.USER_ID,
|
|
||||||
KeychainContract.KeyRings.IS_REVOKED,
|
|
||||||
KeychainContract.KeyRings.IS_EXPIRED,
|
|
||||||
KeychainContract.KeyRings.HAS_ANY_SECRET,
|
|
||||||
KeychainContract.KeyRings.FINGERPRINT,
|
|
||||||
};
|
|
||||||
|
|
||||||
static final int INDEX_MASTER_KEY_ID = 1;
|
|
||||||
static final int INDEX_USER_ID = 2;
|
|
||||||
static final int INDEX_IS_REVOKED = 3;
|
|
||||||
static final int INDEX_IS_EXPIRED = 4;
|
|
||||||
static final int INDEX_HAS_ANY_SECRET = 5;
|
|
||||||
static final int INDEX_FINGERPRINT = 6;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
|
||||||
Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(
|
|
||||||
KeyRings.buildUnifiedKeyRingUri(mScannedMasterKeyId)
|
|
||||||
);
|
|
||||||
return new CursorLoader(getActivity(), baseUri, UNIFIED_PROJECTION, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
|
||||||
if (data.moveToFirst()) {
|
|
||||||
|
|
||||||
byte[] fingerprint = data.getBlob(INDEX_FINGERPRINT);
|
|
||||||
if (!Arrays.equals(fingerprint, mScannedFingerprint)) {
|
|
||||||
mState = YubiImportState.BAD_FINGERPRINT;
|
|
||||||
Notify.create(getActivity(), "Fingerprint mismatch!", Style.ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String userId = data.getString(INDEX_USER_ID);
|
|
||||||
boolean hasSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0;
|
|
||||||
|
|
||||||
String fp = KeyFormattingUtils.convertFingerprintToHex(mScannedFingerprint);
|
|
||||||
mFingerprint.setText(KeyFormattingUtils.colorizeFingerprint(fp));
|
|
||||||
|
|
||||||
mUserId.setText(userId);
|
|
||||||
|
|
||||||
mAnimator.setDisplayedChild(2);
|
|
||||||
mState = YubiImportState.IMPORTED;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
String fp = KeyFormattingUtils.convertFingerprintToHex(mScannedFingerprint);
|
|
||||||
mUnknownFingerprint.setText(KeyFormattingUtils.colorizeFingerprint(fp));
|
|
||||||
|
|
||||||
mAnimator.setDisplayedChild(1);
|
|
||||||
mState = YubiImportState.UNKNOWN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void nextClicked() {
|
private void nextClicked() {
|
||||||
|
|
||||||
switch (mState) {
|
|
||||||
case UNKNOWN:
|
|
||||||
importKey();
|
importKey();
|
||||||
break;
|
|
||||||
case IMPORTED:
|
|
||||||
promoteKey();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void promoteKey() {
|
|
||||||
|
|
||||||
// Message is received after decrypting is done in KeychainIntentService
|
|
||||||
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity()) {
|
|
||||||
public void handleMessage(Message message) {
|
|
||||||
// handle messages by standard KeychainIntentServiceHandler first
|
|
||||||
super.handleMessage(message);
|
|
||||||
|
|
||||||
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
|
||||||
// get returned data bundle
|
|
||||||
Bundle returnData = message.getData();
|
|
||||||
|
|
||||||
PromoteKeyResult result =
|
|
||||||
returnData.getParcelable(DecryptVerifyResult.EXTRA_RESULT);
|
|
||||||
|
|
||||||
result.createNotify(getActivity()).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Send all information needed to service to decrypt in other thread
|
|
||||||
Intent intent = new Intent(getActivity(), KeychainIntentService.class);
|
|
||||||
|
|
||||||
// fill values for this action
|
|
||||||
|
|
||||||
intent.setAction(KeychainIntentService.ACTION_PROMOTE_KEYRING);
|
|
||||||
|
|
||||||
Bundle data = new Bundle();
|
|
||||||
data.putLong(KeychainIntentService.PROMOTE_MASTER_KEY_ID, mScannedMasterKeyId);
|
|
||||||
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
|
||||||
|
|
||||||
// Create a new Messenger for the communication back
|
|
||||||
Messenger messenger = new Messenger(saveHandler);
|
|
||||||
intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
|
|
||||||
|
|
||||||
// start service with intent
|
|
||||||
getActivity().startService(intent);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void importKey() {
|
public void importKey() {
|
||||||
@ -299,12 +160,6 @@ public class CreateKeyYubiFragment extends Fragment
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoaderReset(Loader<Cursor> loader) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static long getKeyIdFromFingerprint(byte[] fingerprint) {
|
static long getKeyIdFromFingerprint(byte[] fingerprint) {
|
||||||
ByteBuffer buf = ByteBuffer.wrap(fingerprint);
|
ByteBuffer buf = ByteBuffer.wrap(fingerprint);
|
||||||
// skip first 12 bytes of the fingerprint
|
// skip first 12 bytes of the fingerprint
|
||||||
|
@ -35,6 +35,7 @@ import android.os.Message;
|
|||||||
import android.os.Messenger;
|
import android.os.Messenger;
|
||||||
import android.provider.ContactsContract;
|
import android.provider.ContactsContract;
|
||||||
import android.support.v4.app.ActivityCompat;
|
import android.support.v4.app.ActivityCompat;
|
||||||
|
import android.support.v4.app.FragmentManager;
|
||||||
import android.support.v4.app.LoaderManager;
|
import android.support.v4.app.LoaderManager;
|
||||||
import android.support.v4.content.CursorLoader;
|
import android.support.v4.content.CursorLoader;
|
||||||
import android.support.v4.content.Loader;
|
import android.support.v4.content.Loader;
|
||||||
@ -60,18 +61,22 @@ import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
|
|||||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||||
import org.sufficientlysecure.keychain.pgp.KeyRing;
|
import org.sufficientlysecure.keychain.pgp.KeyRing;
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||||
|
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||||
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
||||||
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler.MessageStatus;
|
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler.MessageStatus;
|
||||||
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
||||||
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
|
import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity;
|
||||||
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
|
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
|
||||||
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
|
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
|
||||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
|
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
|
||||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||||
|
import org.sufficientlysecure.keychain.ui.util.Notify.ActionListener;
|
||||||
|
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
|
||||||
import org.sufficientlysecure.keychain.ui.util.QrCodeUtils;
|
import org.sufficientlysecure.keychain.ui.util.QrCodeUtils;
|
||||||
import org.sufficientlysecure.keychain.util.ContactHelper;
|
import org.sufficientlysecure.keychain.util.ContactHelper;
|
||||||
import org.sufficientlysecure.keychain.util.ExportHelper;
|
import org.sufficientlysecure.keychain.util.ExportHelper;
|
||||||
@ -79,12 +84,17 @@ import org.sufficientlysecure.keychain.util.Log;
|
|||||||
import org.sufficientlysecure.keychain.util.NfcHelper;
|
import org.sufficientlysecure.keychain.util.NfcHelper;
|
||||||
import org.sufficientlysecure.keychain.util.Preferences;
|
import org.sufficientlysecure.keychain.util.Preferences;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
public class ViewKeyActivity extends BaseActivity implements
|
public class ViewKeyActivity extends BaseNfcActivity implements
|
||||||
LoaderManager.LoaderCallbacks<Cursor> {
|
LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
|
|
||||||
|
public static final String EXTRA_NFC_USER_ID = "nfc_user_id";
|
||||||
|
public static final String EXTRA_NFC_AID = "nfc_aid";
|
||||||
|
public static final String EXTRA_NFC_FINGERPRINTS = "nfc_fingerprints";
|
||||||
|
|
||||||
static final int REQUEST_QR_FINGERPRINT = 1;
|
static final int REQUEST_QR_FINGERPRINT = 1;
|
||||||
static final int REQUEST_DELETE = 2;
|
static final int REQUEST_DELETE = 2;
|
||||||
static final int REQUEST_EXPORT = 3;
|
static final int REQUEST_EXPORT = 3;
|
||||||
@ -107,6 +117,8 @@ public class ViewKeyActivity extends BaseActivity implements
|
|||||||
private ImageView mQrCode;
|
private ImageView mQrCode;
|
||||||
private CardView mQrCodeLayout;
|
private CardView mQrCodeLayout;
|
||||||
|
|
||||||
|
private String mQrCodeLoaded;
|
||||||
|
|
||||||
// NFC
|
// NFC
|
||||||
private NfcHelper mNfcHelper;
|
private NfcHelper mNfcHelper;
|
||||||
|
|
||||||
@ -257,6 +269,15 @@ public class ViewKeyActivity extends BaseActivity implements
|
|||||||
mNfcHelper.initNfc(mDataUri);
|
mNfcHelper.initNfc(mDataUri);
|
||||||
|
|
||||||
startFragment(savedInstanceState, mDataUri);
|
startFragment(savedInstanceState, mDataUri);
|
||||||
|
|
||||||
|
if (savedInstanceState == null && getIntent().hasExtra(EXTRA_NFC_AID)) {
|
||||||
|
Intent intent = getIntent();
|
||||||
|
byte[] nfcFingerprints = intent.getByteArrayExtra(EXTRA_NFC_FINGERPRINTS);
|
||||||
|
String nfcUserId = intent.getStringExtra(EXTRA_NFC_USER_ID);
|
||||||
|
byte[] nfcAid = intent.getByteArrayExtra(EXTRA_NFC_AID);
|
||||||
|
showYubikeyFragment(nfcFingerprints, nfcUserId, nfcAid);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -517,6 +538,60 @@ public class ViewKeyActivity extends BaseActivity implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onNfcPerform() throws IOException {
|
||||||
|
|
||||||
|
final byte[] nfcFingerprints = nfcGetFingerprints();
|
||||||
|
final String nfcUserId = nfcGetUserId();
|
||||||
|
final byte[] nfcAid = nfcGetAid();
|
||||||
|
|
||||||
|
String fp = KeyFormattingUtils.convertFingerprintToHex(nfcFingerprints);
|
||||||
|
final long masterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(nfcFingerprints);
|
||||||
|
|
||||||
|
if (!mFingerprint.equals(fp)) {
|
||||||
|
try {
|
||||||
|
CachedPublicKeyRing ring = mProviderHelper.getCachedPublicKeyRing(masterKeyId);
|
||||||
|
ring.getMasterKeyId();
|
||||||
|
|
||||||
|
Notify.create(this, "Different key stored on Yubikey!", Notify.LENGTH_LONG,
|
||||||
|
Style.WARN, new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void onAction() {
|
||||||
|
Intent intent = new Intent(
|
||||||
|
ViewKeyActivity.this, ViewKeyActivity.class);
|
||||||
|
intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
|
||||||
|
intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, nfcAid);
|
||||||
|
intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, nfcUserId);
|
||||||
|
intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, nfcFingerprints);
|
||||||
|
startActivity(intent);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}, R.string.snack_yubikey_view).show();
|
||||||
|
return;
|
||||||
|
|
||||||
|
} catch (PgpKeyNotFoundException e) {
|
||||||
|
Notify.create(this, "Different key stored on Yubikey!", Style.ERROR).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showYubikeyFragment(nfcFingerprints, nfcUserId, nfcAid);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showYubikeyFragment(byte[] nfcFingerprints, String nfcUserId, byte[] nfcAid) {
|
||||||
|
ViewKeyYubikeyFragment frag = ViewKeyYubikeyFragment.newInstance(
|
||||||
|
nfcFingerprints, nfcUserId, nfcAid);
|
||||||
|
|
||||||
|
FragmentManager manager = getSupportFragmentManager();
|
||||||
|
|
||||||
|
manager.popBackStack("yubikey", FragmentManager.POP_BACK_STACK_INCLUSIVE);
|
||||||
|
manager.beginTransaction()
|
||||||
|
.addToBackStack("yubikey")
|
||||||
|
.replace(R.id.view_key_fragment, frag)
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
|
||||||
private void encrypt(Uri dataUri, boolean text) {
|
private void encrypt(Uri dataUri, boolean text) {
|
||||||
// If there is no encryption key, don't bother.
|
// If there is no encryption key, don't bother.
|
||||||
if (!mHasEncrypt) {
|
if (!mHasEncrypt) {
|
||||||
@ -648,6 +723,7 @@ public class ViewKeyActivity extends BaseActivity implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void onPostExecute(Bitmap qrCode) {
|
protected void onPostExecute(Bitmap qrCode) {
|
||||||
|
mQrCodeLoaded = fingerprint;
|
||||||
// scale the image up to our actual size. we do this in code rather
|
// scale the image up to our actual size. we do this in code rather
|
||||||
// than let the ImageView do this because we don't require filtering.
|
// than let the ImageView do this because we don't require filtering.
|
||||||
Bitmap scaled = Bitmap.createScaledBitmap(qrCode,
|
Bitmap scaled = Bitmap.createScaledBitmap(qrCode,
|
||||||
@ -725,7 +801,6 @@ public class ViewKeyActivity extends BaseActivity implements
|
|||||||
mName.setText(R.string.user_id_no_name);
|
mName.setText(R.string.user_id_no_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
String oldFingerprint = mFingerprint;
|
|
||||||
mMasterKeyId = data.getLong(INDEX_MASTER_KEY_ID);
|
mMasterKeyId = data.getLong(INDEX_MASTER_KEY_ID);
|
||||||
mFingerprint = KeyFormattingUtils.convertFingerprintToHex(data.getBlob(INDEX_FINGERPRINT));
|
mFingerprint = KeyFormattingUtils.convertFingerprintToHex(data.getBlob(INDEX_FINGERPRINT));
|
||||||
|
|
||||||
@ -789,7 +864,7 @@ public class ViewKeyActivity extends BaseActivity implements
|
|||||||
mStatusImage.setVisibility(View.GONE);
|
mStatusImage.setVisibility(View.GONE);
|
||||||
color = getResources().getColor(R.color.primary);
|
color = getResources().getColor(R.color.primary);
|
||||||
// reload qr code only if the fingerprint changed
|
// reload qr code only if the fingerprint changed
|
||||||
if (!mFingerprint.equals(oldFingerprint)) {
|
if (!mFingerprint.equals(mQrCodeLoaded)) {
|
||||||
loadQrCode(mFingerprint);
|
loadQrCode(mFingerprint);
|
||||||
}
|
}
|
||||||
photoTask.execute(mMasterKeyId);
|
photoTask.execute(mMasterKeyId);
|
||||||
|
@ -0,0 +1,129 @@
|
|||||||
|
package org.sufficientlysecure.keychain.ui;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.os.Messenger;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.View.OnClickListener;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.spongycastle.util.encoders.Hex;
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
|
||||||
|
import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
|
||||||
|
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||||
|
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
||||||
|
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||||
|
|
||||||
|
|
||||||
|
public class ViewKeyYubikeyFragment extends Fragment {
|
||||||
|
|
||||||
|
|
||||||
|
public static final String ARG_FINGERPRINT = "fingerprint";
|
||||||
|
public static final String ARG_USER_ID = "user_id";
|
||||||
|
public static final String ARG_AID = "aid";
|
||||||
|
private byte[] mFingerprints;
|
||||||
|
private String mUserId;
|
||||||
|
private byte[] mAid;
|
||||||
|
|
||||||
|
public static ViewKeyYubikeyFragment newInstance(byte[] fingerprints, String userId, byte[] aid) {
|
||||||
|
|
||||||
|
ViewKeyYubikeyFragment frag = new ViewKeyYubikeyFragment();
|
||||||
|
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putByteArray(ARG_FINGERPRINT, fingerprints);
|
||||||
|
args.putString(ARG_USER_ID, userId);
|
||||||
|
args.putByteArray(ARG_AID, aid);
|
||||||
|
frag.setArguments(args);
|
||||||
|
|
||||||
|
return frag;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
Bundle args = getArguments();
|
||||||
|
mFingerprints = args.getByteArray(ARG_FINGERPRINT);
|
||||||
|
mUserId = args.getString(ARG_USER_ID);
|
||||||
|
mAid = args.getByteArray(ARG_AID);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.view_key_yubikey, null);
|
||||||
|
|
||||||
|
TextView vSerNo = (TextView) view.findViewById(R.id.yubikey_serno);
|
||||||
|
TextView vUserId = (TextView) view.findViewById(R.id.yubikey_userid);
|
||||||
|
|
||||||
|
String serno = Hex.toHexString(mAid, 10, 4);
|
||||||
|
vSerNo.setText("Serial N° " + serno);
|
||||||
|
|
||||||
|
if (!mUserId.isEmpty()) {
|
||||||
|
vUserId.setText("Key holder: " + mUserId);
|
||||||
|
} else {
|
||||||
|
vUserId.setText("Key holder: " + "<unset>");
|
||||||
|
}
|
||||||
|
|
||||||
|
view.findViewById(R.id.button_import).setOnClickListener(new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
promoteToSecretKey();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void promoteToSecretKey() {
|
||||||
|
|
||||||
|
// Message is received after decrypting is done in KeychainIntentService
|
||||||
|
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity()) {
|
||||||
|
public void handleMessage(Message message) {
|
||||||
|
// handle messages by standard KeychainIntentServiceHandler first
|
||||||
|
super.handleMessage(message);
|
||||||
|
|
||||||
|
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
||||||
|
// get returned data bundle
|
||||||
|
Bundle returnData = message.getData();
|
||||||
|
|
||||||
|
PromoteKeyResult result =
|
||||||
|
returnData.getParcelable(DecryptVerifyResult.EXTRA_RESULT);
|
||||||
|
|
||||||
|
result.createNotify(getActivity()).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Send all information needed to service to decrypt in other thread
|
||||||
|
Intent intent = new Intent(getActivity(), KeychainIntentService.class);
|
||||||
|
|
||||||
|
// fill values for this action
|
||||||
|
|
||||||
|
intent.setAction(KeychainIntentService.ACTION_PROMOTE_KEYRING);
|
||||||
|
|
||||||
|
long masterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mFingerprints);
|
||||||
|
|
||||||
|
Bundle data = new Bundle();
|
||||||
|
data.putLong(KeychainIntentService.PROMOTE_MASTER_KEY_ID, masterKeyId);
|
||||||
|
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||||
|
|
||||||
|
// Create a new Messenger for the communication back
|
||||||
|
Messenger messenger = new Messenger(saveHandler);
|
||||||
|
intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
|
||||||
|
|
||||||
|
// start service with intent
|
||||||
|
getActivity().startService(intent);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -248,12 +248,17 @@ public abstract class BaseNfcActivity extends BaseActivity {
|
|||||||
return fp;
|
return fp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte[] nfcGetAid() throws IOException {
|
||||||
|
|
||||||
|
String info = "00CA004F00";
|
||||||
|
return mIsoDep.transceive(Hex.decode(info));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public String nfcGetUserId() throws IOException {
|
public String nfcGetUserId() throws IOException {
|
||||||
|
|
||||||
String info = "00CA006500";
|
String info = "00CA006500";
|
||||||
String data = "00CA005E00";
|
return nfcGetHolderName(nfcCommunicate(info));
|
||||||
return nfcGetHolderName(nfcCommunicate(info)) + " <" + (new String(Hex.decode(nfcGetDataField(
|
|
||||||
nfcCommunicate(data))))) + ">";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,6 +38,7 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
|
|||||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve;
|
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.security.DigestException;
|
import java.security.DigestException;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
@ -215,7 +216,15 @@ public class KeyFormattingUtils {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static String convertFingerprintToHex(byte[] fingerprint) {
|
public static String convertFingerprintToHex(byte[] fingerprint) {
|
||||||
return Hex.toHexString(fingerprint).toLowerCase(Locale.ENGLISH);
|
return Hex.toHexString(fingerprint, 0, 20).toLowerCase(Locale.ENGLISH);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getKeyIdFromFingerprint(byte[] fingerprint) {
|
||||||
|
ByteBuffer buf = ByteBuffer.wrap(fingerprint);
|
||||||
|
// skip first 12 bytes of the fingerprint
|
||||||
|
buf.position(12);
|
||||||
|
// the last eight bytes are the key id (big endian, which is default order in ByteBuffer)
|
||||||
|
return buf.getLong();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -9,23 +9,6 @@
|
|||||||
android:fillViewport="true"
|
android:fillViewport="true"
|
||||||
android:layout_above="@+id/create_key_buttons">
|
android:layout_above="@+id/create_key_buttons">
|
||||||
|
|
||||||
<ViewAnimator
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:inAnimation="@anim/abc_fade_in"
|
|
||||||
android:outAnimation="@anim/abc_fade_out"
|
|
||||||
android:paddingLeft="16dp"
|
|
||||||
android:paddingRight="16dp"
|
|
||||||
android:id="@+id/create_yubikey_animator"
|
|
||||||
>
|
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:indeterminate="true"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@ -51,42 +34,6 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:layout_marginLeft="8dp"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
|
||||||
android:id="@+id/create_yubikey_fingerprint"
|
|
||||||
android:text="(yubikey fingerprint)" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:layout_marginLeft="8dp"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
|
||||||
android:id="@+id/create_yubikey_user_id"
|
|
||||||
android:text="(yubikey fingerprint)" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:layout_marginLeft="8dp"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
|
||||||
android:text="Hit next to add this Yubikey to your own!"
|
|
||||||
/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</ViewAnimator>
|
|
||||||
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
98
OpenKeychain/src/main/res/layout/view_key_yubikey.xml
Normal file
98
OpenKeychain/src/main/res/layout/view_key_yubikey.xml
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingBottom="16dp"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="16dp">
|
||||||
|
|
||||||
|
<android.support.v7.widget.CardView
|
||||||
|
android:id="@+id/card_view"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:transitionName="card"
|
||||||
|
card_view:cardBackgroundColor="@android:color/white"
|
||||||
|
card_view:cardElevation="2dp"
|
||||||
|
card_view:cardUseCompatPadding="true"
|
||||||
|
card_view:cardCornerRadius="4dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/CardViewHeader"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/section_yubikey"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_margin="14dp"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:src="@drawable/yubi_icon"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/yubikey_serno"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:text="Yubikey #"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/yubikey_userid"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:text="User ID"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:text="Key matches!"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button_import"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="right|end"
|
||||||
|
android:text="Import"
|
||||||
|
android:textColor="@color/link_text_material_light"
|
||||||
|
style="?android:attr/borderlessButtonStyle"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</android.support.v7.widget.CardView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
@ -48,6 +48,7 @@
|
|||||||
|
|
||||||
<!-- section -->
|
<!-- section -->
|
||||||
<string name="section_user_ids">"Identities"</string>
|
<string name="section_user_ids">"Identities"</string>
|
||||||
|
<string name="section_yubikey">"Yubikey"</string>
|
||||||
<string name="section_linked_system_contact">"Linked System Contact"</string>
|
<string name="section_linked_system_contact">"Linked System Contact"</string>
|
||||||
<string name="section_should_you_trust">"Should you trust this key?"</string>
|
<string name="section_should_you_trust">"Should you trust this key?"</string>
|
||||||
<string name="section_proof_details">Proof verification</string>
|
<string name="section_proof_details">Proof verification</string>
|
||||||
@ -1267,5 +1268,6 @@
|
|||||||
<string name="nfc_write_succesful">Successfully written on NFC tag</string>
|
<string name="nfc_write_succesful">Successfully written on NFC tag</string>
|
||||||
<string name="unlocked">Unlocked</string>
|
<string name="unlocked">Unlocked</string>
|
||||||
<string name="nfc_settings">Settings</string>
|
<string name="nfc_settings">Settings</string>
|
||||||
|
<string name="snack_yubikey_view">View</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
Reference in New Issue
Block a user