@Override public CertifyActionsParcel createOperationInput() { // Bail out if there is not at least one user id selected ArrayList<CertifyAction> certifyActions = mUserIdsAdapter.getSelectedCertifyActions(); if (certifyActions.isEmpty()) { Notify.create(getActivity(), "No identities selected!", Notify.Style.ERROR).show(); return null; } long selectedKeyId = mCertifyKeySpinner.getSelectedKeyId(); // fill values for this action CertifyActionsParcel actionsParcel = new CertifyActionsParcel(selectedKeyId); actionsParcel.mCertifyActions.addAll(certifyActions); if (mUploadKeyCheckbox.isChecked()) { actionsParcel.keyServerUri = Preferences.getPreferences(getActivity()).getPreferredKeyserver(); } // cached for next cryptoOperation loop cacheActionsParcel(actionsParcel); return actionsParcel; }
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE_IMPORT_KEY) { if (resultCode == Activity.RESULT_OK) { if (mCreateKeyActivity.mFirstTime) { Preferences prefs = Preferences.getPreferences(mCreateKeyActivity); prefs.setFirstTime(false); mCreateKeyActivity.finish(); } else { // just finish activity and return data mCreateKeyActivity.setResult(Activity.RESULT_OK, data); mCreateKeyActivity.finish(); } } } else { Log.e(Constants.TAG, "No valid request code!"); } }
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState == null) { mMessage = getArguments().getString(ARG_TEXT); } Preferences prefs = Preferences.getPreferences(getActivity()); Bundle args = savedInstanceState == null ? getArguments() : savedInstanceState; mUseCompression = args.getBoolean(ARG_USE_COMPRESSION, true); if (args.containsKey(ARG_USE_COMPRESSION)) { mUseCompression = args.getBoolean(ARG_USE_COMPRESSION, true); } else { mUseCompression = prefs.getTextUseCompression(); } setHasOptionsMenu(true); }
@NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final Activity activity = getActivity(); ContextThemeWrapper theme = ThemeChanger.getDialogThemeWrapper(activity); mRequiredInput = getArguments().getParcelable(EXTRA_REQUIRED_INPUT); CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(theme); // No title, see http://www.google.com/design/spec/components/dialogs.html#dialogs-alerts // alert.setTitle() if (mRequiredInput.mType == RequiredInputType.BACKUP_CODE) { LayoutInflater inflater = LayoutInflater.from(theme); View view = inflater.inflate(R.layout.passphrase_dialog_backup_code, null); alert.setView(view); mBackupCodeEditText = new EditText[4]; mBackupCodeEditText[0] = (EditText) view.findViewById(R.id.backup_code_1); mBackupCodeEditText[1] = (EditText) view.findViewById(R.id.backup_code_2); mBackupCodeEditText[2] = (EditText) view.findViewById(R.id.backup_code_3); mBackupCodeEditText[3] = (EditText) view.findViewById(R.id.backup_code_4); setupEditTextFocusNext(mBackupCodeEditText); AlertDialog dialog = alert.create(); dialog.setButton( DialogInterface.BUTTON_POSITIVE, activity.getString(R.string.btn_unlock), (DialogInterface.OnClickListener) null); return dialog; } long subKeyId = mRequiredInput.getSubKeyId(); LayoutInflater inflater = LayoutInflater.from(theme); mLayout = (ViewAnimator) inflater.inflate(R.layout.passphrase_dialog, null); alert.setView(mLayout); mPassphraseText = (TextView) mLayout.findViewById(R.id.passphrase_text); mPassphraseEditText = (EditText) mLayout.findViewById(R.id.passphrase_passphrase); View vTimeToLiveLayout = mLayout.findViewById(R.id.remember_layout); vTimeToLiveLayout.setVisibility(mRequiredInput.mSkipCaching ? View.GONE : View.VISIBLE); mTimeToLiveSpinner = (CacheTTLSpinner) mLayout.findViewById(R.id.ttl_spinner); alert.setNegativeButton( android.R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }); String userId; CanonicalizedSecretKey.SecretKeyType keyType = CanonicalizedSecretKey.SecretKeyType.PASSPHRASE; String message; String hint; if (mRequiredInput.mType == RequiredInputType.PASSPHRASE_SYMMETRIC) { message = getString(R.string.passphrase_for_symmetric_encryption); hint = getString(R.string.label_passphrase); } else { try { ProviderHelper helper = new ProviderHelper(activity); mSecretRing = helper.getCanonicalizedSecretKeyRing( KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(subKeyId)); // yes the inner try/catch block is necessary, otherwise the final variable // above can't be statically verified to have been set in all cases because // the catch clause doesn't return. try { String mainUserId = mSecretRing.getPrimaryUserIdWithFallback(); KeyRing.UserId mainUserIdSplit = KeyRing.splitUserId(mainUserId); if (mainUserIdSplit.name != null) { userId = mainUserIdSplit.name; } else { userId = getString(R.string.user_id_no_name); } } catch (PgpKeyNotFoundException e) { userId = null; } keyType = mSecretRing.getSecretKey(subKeyId).getSecretKeyType(); switch (keyType) { case PASSPHRASE: message = getString(R.string.passphrase_for, userId); hint = getString(R.string.label_passphrase); break; case PIN: message = getString(R.string.pin_for, userId); hint = getString(R.string.label_pin); break; case DIVERT_TO_CARD: message = getString(R.string.yubikey_pin_for, userId); hint = getString(R.string.label_pin); break; // special case: empty passphrase just returns the empty passphrase case PASSPHRASE_EMPTY: finishCaching(new Passphrase("")); default: throw new AssertionError("Unhandled SecretKeyType (should not happen)"); } } catch (ProviderHelper.NotFoundException e) { alert.setTitle(R.string.title_key_not_found); alert.setMessage(getString(R.string.key_not_found, mRequiredInput.getSubKeyId())); alert.setPositiveButton( android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dismiss(); } }); alert.setCancelable(false); return alert.create(); } } mPassphraseText.setText(message); mPassphraseEditText.setHint(hint); // Hack to open keyboard. // This is the only method that I found to work across all Android versions // http://turbomanage.wordpress.com/2012/05/02/show-soft-keyboard-automatically-when-edittext-receives-focus/ // Notes: * onCreateView can't be used because we want to add buttons to the dialog // * opening in onActivityCreated does not work on Android 4.4 mPassphraseEditText.setOnFocusChangeListener( new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { mPassphraseEditText.post( new Runnable() { @Override public void run() { if (getActivity() == null || mPassphraseEditText == null) { return; } InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); imm.showSoftInput(mPassphraseEditText, InputMethodManager.SHOW_IMPLICIT); } }); } }); mPassphraseEditText.requestFocus(); mPassphraseEditText.setImeActionLabel( getString(android.R.string.ok), EditorInfo.IME_ACTION_DONE); mPassphraseEditText.setOnEditorActionListener(this); if ((keyType == CanonicalizedSecretKey.SecretKeyType.DIVERT_TO_CARD && Preferences.getPreferences(activity).useNumKeypadForYubiKeyPin()) || keyType == CanonicalizedSecretKey.SecretKeyType.PIN) { mPassphraseEditText.setInputType(InputType.TYPE_CLASS_NUMBER); mPassphraseEditText.setTransformationMethod(PasswordTransformationMethod.getInstance()); } else { mPassphraseEditText.setRawInputType( InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); } mPassphraseEditText.setTransformationMethod(PasswordTransformationMethod.getInstance()); AlertDialog dialog = alert.create(); dialog.setButton( DialogInterface.BUTTON_POSITIVE, activity.getString(R.string.btn_unlock), (DialogInterface.OnClickListener) null); return dialog; }
/** Executed when service is started by intent */ @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(Constants.TAG, "PassphraseCacheService.onStartCommand()"); if (intent == null || intent.getAction() == null) { updateService(); return START_STICKY; } String action = intent.getAction(); switch (action) { case ACTION_PASSPHRASE_CACHE_ADD: { long masterKeyId = intent.getLongExtra(EXTRA_KEY_ID, -1); long subKeyId = intent.getLongExtra(EXTRA_SUBKEY_ID, -1); long timeoutTtl = intent.getIntExtra(EXTRA_TTL, DEFAULT_TTL); Passphrase passphrase = intent.getParcelableExtra(EXTRA_PASSPHRASE); String primaryUserID = intent.getStringExtra(EXTRA_USER_ID); Log.d( Constants.TAG, "PassphraseCacheService: Received ACTION_PASSPHRASE_CACHE_ADD intent in onStartCommand() with masterkeyId: " + masterKeyId + ", subKeyId: " + subKeyId + ", ttl: " + timeoutTtl + ", usrId: " + primaryUserID); // if we don't cache by specific subkey id, or the requested subkey is the master key, // just add master key id to the cache, otherwise, add this specific subkey to the cache long referenceKeyId = Preferences.getPreferences(mContext).getPassphraseCacheSubs() ? subKeyId : masterKeyId; CachedPassphrase cachedPassphrase; if (timeoutTtl == 0L) { cachedPassphrase = CachedPassphrase.getPassphraseLock(passphrase, primaryUserID); } else if (timeoutTtl >= Integer.MAX_VALUE) { cachedPassphrase = CachedPassphrase.getPassphraseNoTimeout(passphrase, primaryUserID); } else { cachedPassphrase = CachedPassphrase.getPassphraseTtlTimeout(passphrase, primaryUserID, timeoutTtl); long triggerTime = new Date().getTime() + (timeoutTtl * 1000); // register new alarm with keyId for this passphrase AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); am.set(AlarmManager.RTC_WAKEUP, triggerTime, buildIntent(this, referenceKeyId)); } mPassphraseCache.put(referenceKeyId, cachedPassphrase); break; } case ACTION_PASSPHRASE_CACHE_GET: { long masterKeyId = intent.getLongExtra(EXTRA_KEY_ID, Constants.key.symmetric); long subKeyId = intent.getLongExtra(EXTRA_SUBKEY_ID, Constants.key.symmetric); Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER); Message msg = Message.obtain(); try { // If only one of these is symmetric, error out! if (masterKeyId == Constants.key.symmetric ^ subKeyId == Constants.key.symmetric) { Log.e( Constants.TAG, "PassphraseCacheService: Bad request, missing masterKeyId or subKeyId!"); msg.what = MSG_PASSPHRASE_CACHE_GET_KEY_NOT_FOUND; } else { Passphrase passphrase = getCachedPassphraseImpl(masterKeyId, subKeyId); msg.what = MSG_PASSPHRASE_CACHE_GET_OKAY; Bundle bundle = new Bundle(); bundle.putParcelable(EXTRA_PASSPHRASE, passphrase); msg.setData(bundle); } } catch (ProviderHelper.NotFoundException e) { Log.e( Constants.TAG, "PassphraseCacheService: Passphrase for unknown key was requested!"); msg.what = MSG_PASSPHRASE_CACHE_GET_KEY_NOT_FOUND; } try { messenger.send(msg); } catch (RemoteException e) { Log.e(Constants.TAG, "PassphraseCacheService: Sending message failed", e); } break; } case ACTION_PASSPHRASE_CACHE_CLEAR: { AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); if (intent.hasExtra(EXTRA_SUBKEY_ID) && intent.hasExtra(EXTRA_KEY_ID)) { long referenceKeyId; if (Preferences.getPreferences(mContext).getPassphraseCacheSubs()) { referenceKeyId = intent.getLongExtra(EXTRA_SUBKEY_ID, 0L); } else { referenceKeyId = intent.getLongExtra(EXTRA_KEY_ID, 0L); } // Stop specific ttl alarm and am.cancel(buildIntent(this, referenceKeyId)); mPassphraseCache.delete(referenceKeyId); } else { // Stop all ttl alarms for (int i = 0; i < mPassphraseCache.size(); i++) { CachedPassphrase cachedPassphrase = mPassphraseCache.valueAt(i); if (cachedPassphrase.mTimeoutMode == TimeoutMode.TTL) { am.cancel(buildIntent(this, mPassphraseCache.keyAt(i))); } } mPassphraseCache.clear(); } break; } default: { Log.e(Constants.TAG, "PassphraseCacheService: Intent or Intent Action not supported!"); break; } } updateService(); return START_STICKY; }
/** Internal implementation to get cached passphrase. */ private Passphrase getCachedPassphraseImpl(long masterKeyId, long subKeyId) throws ProviderHelper.NotFoundException { // on "none" key, just do nothing if (masterKeyId == Constants.key.none) { return null; } // passphrase for symmetric encryption? if (masterKeyId == Constants.key.symmetric) { Log.d( Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for symmetric encryption"); CachedPassphrase cachedPassphrase = mPassphraseCache.get(Constants.key.symmetric); if (cachedPassphrase == null) { return null; } return cachedPassphrase.mPassphrase; } // try to get master key id which is used as an identifier for cached passphrases Log.d( Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for masterKeyId " + masterKeyId + ", subKeyId " + subKeyId); // get the type of key (from the database) CachedPublicKeyRing keyRing = new ProviderHelper(this).getCachedPublicKeyRing(masterKeyId); SecretKeyType keyType = keyRing.getSecretKeyType(subKeyId); switch (keyType) { case PASSPHRASE_EMPTY: return new Passphrase(""); case UNAVAILABLE: throw new ProviderHelper.NotFoundException("secret key for this subkey is not available"); case GNU_DUMMY: throw new ProviderHelper.NotFoundException( "secret key for stripped subkey is not available"); } // get cached passphrase CachedPassphrase cachedPassphrase = mPassphraseCache.get(subKeyId); if (cachedPassphrase == null) { // If we cache strictly by subkey, exit early if (Preferences.getPreferences(mContext).getPassphraseCacheSubs()) { Log.d( Constants.TAG, "PassphraseCacheService: specific subkey passphrase not (yet) cached, returning null"); // not really an error, just means the passphrase is not cached but not empty either return null; } if (subKeyId == masterKeyId) { Log.d( Constants.TAG, "PassphraseCacheService: masterkey passphrase not (yet) cached, returning null"); // not really an error, just means the passphrase is not cached but not empty either return null; } cachedPassphrase = mPassphraseCache.get(masterKeyId); // If we cache strictly by subkey, exit early if (cachedPassphrase == null) { Log.d( Constants.TAG, "PassphraseCacheService: keyring passphrase not (yet) cached, returning null"); // not really an error, just means the passphrase is not cached but not empty either return null; } } return cachedPassphrase.mPassphrase; }