@Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // set displayed values if (getArguments() != null) { if (getArguments().containsKey(ARG_QUERY)) { String query = getArguments().getString(ARG_QUERY); mQueryEditText.setText(query, TextView.BufferType.EDITABLE); Log.d(Constants.TAG, "query: " + query); } if (getArguments().containsKey(ARG_KEYSERVER)) { String keyserver = getArguments().getString(ARG_KEYSERVER); int keyserverPos = mServerAdapter.getPosition(keyserver); mServerSpinner.setSelection(keyserverPos); Log.d(Constants.TAG, "keyserver: " + keyserver); } if (getArguments().getBoolean(ARG_DISABLE_QUERY_EDIT, false)) { mQueryEditText.setEnabled(false); } } }
private void loadData(Bundle savedInstanceState, Uri appUri) { mAppSettings = new ApiDataAccessObject(this).getApiAppSettings(appUri); // get application name and icon from package manager String appName; Drawable appIcon = null; PackageManager pm = getApplicationContext().getPackageManager(); try { ApplicationInfo ai = pm.getApplicationInfo(mAppSettings.getPackageName(), 0); appName = (String) pm.getApplicationLabel(ai); appIcon = pm.getApplicationIcon(ai); } catch (PackageManager.NameNotFoundException e) { // fallback appName = mAppSettings.getPackageName(); } mAppNameView.setText(appName); mAppIconView.setImageDrawable(appIcon); Uri accountsUri = appUri.buildUpon().appendPath(KeychainContract.PATH_ACCOUNTS).build(); Log.d(Constants.TAG, "accountsUri: " + accountsUri); Uri allowedKeysUri = appUri.buildUpon().appendPath(KeychainContract.PATH_ALLOWED_KEYS).build(); Log.d(Constants.TAG, "allowedKeysUri: " + allowedKeysUri); startListFragments(savedInstanceState, accountsUri, allowedKeysUri); }
@Override public void onDestroy() { super.onDestroy(); Log.d(Constants.TAG, "PassphraseCacheService, onDestroy()"); unregisterReceiver(mIntentReceiver); }
private void showPassphraseDialog(final long secretKeyId) { // Message is received after passphrase is cached Handler returnHandler = new Handler() { @Override public void handleMessage(Message message) { if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { startSigning(); } } }; // Create a new Messenger for the communication back Messenger messenger = new Messenger(returnHandler); try { PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(this, messenger, secretKeyId); passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog"); } catch (PgpGeneralException e) { Log.d(Constants.TAG, "No passphrase for this secret key!"); // send message to handler to start certification directly returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY); } }
@Override public void onCreate() { super.onCreate(); mContext = this; Log.d(Constants.TAG, "PassphraseCacheService, onCreate()"); registerReceiver(); }
public static void clearCachedPassphrases(Context context) { Log.d(Constants.TAG, "PassphraseCacheService.clearCachedPassphrase()"); Intent intent = new Intent(context, PassphraseCacheService.class); intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR); context.startService(intent); }
private void updateService() { if (mPassphraseCache.size() > 0) { startForeground(Constants.Notification.PASSPHRASE_CACHE, getNotification()); } else { // stop whole service if no cached passphrases remaining Log.d( Constants.TAG, "PassphraseCacheService: No passphrases remaining in memory, stopping service!"); stopForeground(true); } }
public static void clearCachedPassphrase(Context context, long masterKeyId, long subKeyId) { Log.d(Constants.TAG, "PassphraseCacheService.clearCachedPassphrase() for " + masterKeyId); Intent intent = new Intent(context, PassphraseCacheService.class); intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR); intent.putExtra(EXTRA_KEY_ID, masterKeyId); intent.putExtra(EXTRA_SUBKEY_ID, subKeyId); context.startService(intent); }
@Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Log.d(Constants.TAG, "clicked id: " + id); long masterKeyId = getMasterKeyId(position); if (mSelectedItems.contains(masterKeyId)) { mSelectedItems.remove(masterKeyId); } else { mSelectedItems.add(masterKeyId); } notifyDataSetChanged(); }
public boolean moveCursor(int position) { if (position >= getItemCount() || position < -1) { Log.w(TAG, "Position: %d is invalid for this data set!"); return false; } if (!mDataValid) { Log.d(TAG, "Attempt to move cursor over invalid data set!"); } return mCursor.moveToPosition(position); }
public OpenPgpSignatureResult build() { if (!mSignatureAvailable) { Log.d(Constants.TAG, "RESULT_NO_SIGNATURE"); return OpenPgpSignatureResult.createWithNoSignature(); } if (!mKnownKey) { Log.d(Constants.TAG, "RESULT_KEY_MISSING"); return OpenPgpSignatureResult.createWithKeyMissing(mKeyId, mSignatureTimestamp); } if (!mValidSignature) { Log.d(Constants.TAG, "RESULT_INVALID_SIGNATURE"); return OpenPgpSignatureResult.createWithInvalidSignature(); } int signatureStatus; if (mIsKeyRevoked) { Log.d(Constants.TAG, "RESULT_INVALID_KEY_REVOKED"); signatureStatus = OpenPgpSignatureResult.RESULT_INVALID_KEY_REVOKED; } else if (mIsKeyExpired) { Log.d(Constants.TAG, "RESULT_INVALID_KEY_EXPIRED"); signatureStatus = OpenPgpSignatureResult.RESULT_INVALID_KEY_EXPIRED; } else if (mInsecure) { Log.d(Constants.TAG, "RESULT_INVALID_INSECURE"); signatureStatus = OpenPgpSignatureResult.RESULT_INVALID_KEY_INSECURE; } else if (mIsSignatureKeyCertified) { Log.d(Constants.TAG, "RESULT_VALID_CONFIRMED"); signatureStatus = OpenPgpSignatureResult.RESULT_VALID_KEY_CONFIRMED; } else { Log.d(Constants.TAG, "RESULT_VALID_UNCONFIRMED"); signatureStatus = OpenPgpSignatureResult.RESULT_VALID_KEY_UNCONFIRMED; } return OpenPgpSignatureResult.createWithValidSignature( signatureStatus, mPrimaryUserId, mKeyId, mUserIds, mConfirmedUserIds, mSenderStatusResult, mSignatureTimestamp); }
/** Called when one specific passphrase for keyId timed out. */ private void removeTimeoutedPassphrase(long keyId) { CachedPassphrase cPass = mPassphraseCache.get(keyId); if (cPass != null) { if (cPass.mPassphrase != null) { // clean internal char[] from memory! cPass.mPassphrase.removeFromMemory(); } // remove passphrase object mPassphraseCache.remove(keyId); } Log.d( Constants.TAG, "PassphraseCacheService Timeout of keyId " + keyId + ", removed from memory!"); updateService(); }
public void initValid(CanonicalizedPublicKey signingKey) { setSignatureAvailable(true); setKnownKey(true); CanonicalizedKeyRing signingRing = signingKey.getKeyRing(); // from RING setKeyId(signingRing.getMasterKeyId()); try { setPrimaryUserId(signingRing.getPrimaryUserIdWithFallback()); } catch (PgpKeyNotFoundException e) { Log.d( Constants.TAG, "No primary user id in keyring with master key id " + signingRing.getMasterKeyId()); } setSignatureKeyCertified(signingRing.getVerified() > 0); try { ArrayList<String> allUserIds = signingRing.getUnorderedUserIds(); ArrayList<String> confirmedUserIds = mProviderHelper.getConfirmedUserIds(signingRing.getMasterKeyId()); setUserIds(allUserIds, confirmedUserIds); if (mSenderAddress != null) { if (userIdListContainsAddress(mSenderAddress, confirmedUserIds)) { mSenderStatusResult = SenderStatusResult.USER_ID_CONFIRMED; } else if (userIdListContainsAddress(mSenderAddress, allUserIds)) { mSenderStatusResult = SenderStatusResult.USER_ID_UNCONFIRMED; } else { mSenderStatusResult = SenderStatusResult.USER_ID_MISSING; } } else { mSenderStatusResult = SenderStatusResult.UNKNOWN; } } catch (NotFoundException e) { throw new IllegalStateException("Key didn't exist anymore for user id query!", e); } // either master key is expired/revoked or this specific subkey is expired/revoked setKeyExpired(signingRing.isExpired() || signingKey.isExpired()); setKeyRevoked(signingRing.isRevoked() || signingKey.isRevoked()); }
private void removeScreenLockPassphrases() { for (int i = 0; i < mPassphraseCache.size(); ) { CachedPassphrase cPass = mPassphraseCache.valueAt(i); if (cPass.mTimeoutMode == TimeoutMode.LOCK) { // remove passphrase object mPassphraseCache.removeAt(i); continue; } // only do this if we didn't remove at, which continues loop by reducing size! i += 1; } Log.d( Constants.TAG, "PassphraseCacheService Removing all cached-until-lock passphrases from memory!"); updateService(); }
/** * This caches a new passphrase in memory by sending a new command to the service. An android * service is only run once. Thus, when the service is already started, new commands just add new * events to the alarm manager for new passphrases to let them timeout in the future. */ public static void addCachedPassphrase( Context context, long masterKeyId, long subKeyId, Passphrase passphrase, String primaryUserId, int timeToLiveSeconds) { Log.d(Constants.TAG, "PassphraseCacheService.addCachedPassphrase() for " + masterKeyId); Intent intent = new Intent(context, PassphraseCacheService.class); intent.setAction(ACTION_PASSPHRASE_CACHE_ADD); intent.putExtra(EXTRA_TTL, timeToLiveSeconds); intent.putExtra(EXTRA_PASSPHRASE, passphrase); intent.putExtra(EXTRA_KEY_ID, masterKeyId); intent.putExtra(EXTRA_SUBKEY_ID, subKeyId); intent.putExtra(EXTRA_USER_ID, primaryUserId); context.startService(intent); }
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mAccountsLabel = (TextView) findViewById(R.id.api_accounts_label); mAppNameView = (TextView) findViewById(R.id.api_app_settings_app_name); mAppIconView = (ImageView) findViewById(R.id.api_app_settings_app_icon); mPackageName = (TextView) findViewById(R.id.api_app_settings_package_name); mPackageSignature = (TextView) findViewById(R.id.api_app_settings_package_certificate); mStartFab = (FloatingActionButton) findViewById(R.id.fab); mStartFab.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { startApp(); } }); setFullScreenDialogClose( new View.OnClickListener() { @Override public void onClick(View v) { cancel(); } }); setTitle(null); Intent intent = getIntent(); mAppUri = intent.getData(); if (mAppUri == null) { Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!"); finish(); return; } else { Log.d(Constants.TAG, "uri: " + mAppUri); loadData(savedInstanceState, mAppUri); } }
@Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { MatrixCursor matrix = new MatrixCursor(new String[] {"_id", "user_data", "grouped"}) { @Override public byte[] getBlob(int column) { return super.getBlob(column); } }; data.moveToFirst(); long lastMasterKeyId = 0; String lastName = ""; ArrayList<String> uids = new ArrayList<>(); boolean header = true; // Iterate over all rows while (!data.isAfterLast()) { long masterKeyId = data.getLong(INDEX_MASTER_KEY_ID); String userId = data.getString(INDEX_USER_ID); KeyRing.UserId pieces = KeyRing.splitUserId(userId); // Two cases: boolean grouped = masterKeyId == lastMasterKeyId; boolean subGrouped = data.isFirst() || grouped && lastName.equals(pieces.name); // Remember for next loop lastName = pieces.name; Log.d(Constants.TAG, Long.toString(masterKeyId, 16) + (grouped ? "grouped" : "not grouped")); if (!subGrouped) { // 1. This name should NOT be grouped with the previous, so we flush the buffer Parcel p = Parcel.obtain(); p.writeStringList(uids); byte[] d = p.marshall(); p.recycle(); matrix.addRow(new Object[] {lastMasterKeyId, d, header ? 1 : 0}); // indicate that we have a header for this masterKeyId header = false; // Now clear the buffer, and add the new user id, for the next round uids.clear(); } // 2. This name should be grouped with the previous, just add to buffer uids.add(userId); lastMasterKeyId = masterKeyId; // If this one wasn't grouped, the next one's gotta be a header if (!grouped) { header = true; } // Regardless of the outcome, move to next entry data.moveToNext(); } // If there is anything left in the buffer, flush it one last time if (!uids.isEmpty()) { Parcel p = Parcel.obtain(); p.writeStringList(uids); byte[] d = p.marshall(); p.recycle(); matrix.addRow(new Object[] {lastMasterKeyId, d, header ? 1 : 0}); } mUserIdsAdapter.swapCursor(matrix); }
@Override public void onDestroy() { super.onDestroy(); Log.d(Constants.TAG, "CryptoInputParcelCacheService, onDestroy()"); }
@Override public void onCreate() { super.onCreate(); mContext = this; Log.d(Constants.TAG, "CryptoInputParcelCacheService, onCreate()"); }
/** Executed when service is started by intent */ @Override public int onStartCommand(Intent intent, int flags, int startId) { if (intent == null || intent.getAction() == null) { return START_NOT_STICKY; } String action = intent.getAction(); switch (action) { case ACTION_ADD: { long uuid1 = intent.getLongExtra(EXTRA_UUID1, 0); long uuid2 = intent.getLongExtra(EXTRA_UUID2, 0); UUID uuid = new UUID(uuid1, uuid2); CryptoInputParcel inputParcel = intent.getParcelableExtra(EXTRA_CRYPTO_INPUT_PARCEL); mCache.put(uuid, inputParcel); break; } case ACTION_GET: { long uuid1 = intent.getLongExtra(EXTRA_UUID1, 0); long uuid2 = intent.getLongExtra(EXTRA_UUID2, 0); UUID uuid = new UUID(uuid1, uuid2); Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER); Message msg = Message.obtain(); // UUID.equals isn't well documented; we use compareTo instead. if (NULL_UUID.compareTo(uuid) == 0) { msg.what = MSG_GET_NOT_FOUND; } else { CryptoInputParcel inputParcel = mCache.get(uuid); mCache.remove(uuid); msg.what = MSG_GET_OKAY; Bundle bundle = new Bundle(); bundle.putParcelable(EXTRA_CRYPTO_INPUT_PARCEL, inputParcel); msg.setData(bundle); } try { messenger.send(msg); } catch (RemoteException e) { Log.e(Constants.TAG, "CryptoInputParcelCacheService: Sending message failed", e); } break; } default: { Log.e( Constants.TAG, "CryptoInputParcelCacheService: Intent or Intent Action not supported!"); break; } } if (mCache.size() <= 0) { // stop whole service if cache is empty Log.d( Constants.TAG, "CryptoInputParcelCacheService: No passphrases remaining in memory, stopping service!"); stopSelf(); } return START_NOT_STICKY; }
/** Signs and/or encrypts data based on parameters of class */ public PgpSignEncryptResult execute( PgpSignEncryptInputParcel input, CryptoInputParcel cryptoInput, InputData inputData, OutputStream outputStream) { int indent = 0; OperationLog log = new OperationLog(); log.add(LogType.MSG_PSE, indent); indent += 1; boolean enableSignature = input.getSignatureMasterKeyId() != Constants.key.none; boolean enableEncryption = ((input.getEncryptionMasterKeyIds() != null && input.getEncryptionMasterKeyIds().length > 0) || input.getSymmetricPassphrase() != null); boolean enableCompression = (input.getCompressionId() != CompressionAlgorithmTags.UNCOMPRESSED); Log.d( Constants.TAG, "enableSignature:" + enableSignature + "\nenableEncryption:" + enableEncryption + "\nenableCompression:" + enableCompression + "\nenableAsciiArmorOutput:" + input.isEnableAsciiArmorOutput() + "\nisHiddenRecipients:" + input.isHiddenRecipients()); // add additional key id to encryption ids (mostly to do self-encryption) if (enableEncryption && input.getAdditionalEncryptId() != Constants.key.none) { input.setEncryptionMasterKeyIds( Arrays.copyOf( input.getEncryptionMasterKeyIds(), input.getEncryptionMasterKeyIds().length + 1)); input.getEncryptionMasterKeyIds()[input.getEncryptionMasterKeyIds().length - 1] = input.getAdditionalEncryptId(); } ArmoredOutputStream armorOut = null; OutputStream out; if (input.isEnableAsciiArmorOutput()) { armorOut = new ArmoredOutputStream(new BufferedOutputStream(outputStream, 1 << 16)); if (input.getVersionHeader() != null) { armorOut.setHeader("Version", input.getVersionHeader()); } // if we have a charset, put it in the header if (input.getCharset() != null) { armorOut.setHeader("Charset", input.getCharset()); } out = armorOut; } else { out = outputStream; } /* Get keys for signature generation for later usage */ CanonicalizedSecretKey signingKey = null; if (enableSignature) { updateProgress(R.string.progress_extracting_signature_key, 0, 100); try { // fetch the indicated master key id (the one whose name we sign in) CanonicalizedSecretKeyRing signingKeyRing = mProviderHelper.getCanonicalizedSecretKeyRing(input.getSignatureMasterKeyId()); // fetch the specific subkey to sign with, or just use the master key if none specified signingKey = signingKeyRing.getSecretKey(input.getSignatureSubKeyId()); // Make sure we are allowed to sign here! if (!signingKey.canSign()) { log.add(LogType.MSG_PSE_ERROR_KEY_SIGN, indent); return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); } switch (signingKey.getSecretKeyType()) { case DIVERT_TO_CARD: case PASSPHRASE_EMPTY: { if (!signingKey.unlock(new Passphrase())) { throw new AssertionError( "PASSPHRASE_EMPTY/DIVERT_TO_CARD keyphrase not unlocked with empty passphrase." + " This is a programming error!"); } break; } case PIN: case PATTERN: case PASSPHRASE: { Passphrase localPassphrase = cryptoInput.getPassphrase(); if (localPassphrase == null) { try { localPassphrase = getCachedPassphrase(signingKeyRing.getMasterKeyId(), signingKey.getKeyId()); } catch (PassphraseCacheInterface.NoSecretKeyException ignored) { } } if (localPassphrase == null) { log.add(LogType.MSG_PSE_PENDING_PASSPHRASE, indent + 1); return new PgpSignEncryptResult( log, RequiredInputParcel.createRequiredSignPassphrase( signingKeyRing.getMasterKeyId(), signingKey.getKeyId(), cryptoInput.getSignatureTime()), cryptoInput); } if (!signingKey.unlock(localPassphrase)) { log.add(LogType.MSG_PSE_ERROR_BAD_PASSPHRASE, indent); return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); } break; } case GNU_DUMMY: { log.add(LogType.MSG_PSE_ERROR_UNLOCK, indent); return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); } default: { throw new AssertionError("Unhandled SecretKeyType! (should not happen)"); } } } catch (ProviderHelper.NotFoundException e) { log.add(LogType.MSG_PSE_ERROR_SIGN_KEY, indent); return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); } catch (PgpGeneralException e) { log.add(LogType.MSG_PSE_ERROR_UNLOCK, indent); return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); } // Use preferred hash algo int requestedAlgorithm = input.getSignatureHashAlgorithm(); ArrayList<Integer> supported = signingKey.getSupportedHashAlgorithms(); if (requestedAlgorithm == PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED) { // get most preferred input.setSignatureHashAlgorithm(supported.get(0)); } else if (!supported.contains(requestedAlgorithm)) { log.add(LogType.MSG_PSE_ERROR_HASH_ALGO, indent); return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); } } updateProgress(R.string.progress_preparing_streams, 2, 100); /* Initialize PGPEncryptedDataGenerator for later usage */ PGPEncryptedDataGenerator cPk = null; if (enableEncryption) { // Use preferred encryption algo int algo = input.getSymmetricEncryptionAlgorithm(); if (algo == PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED) { // get most preferred // TODO: get from recipients algo = PgpConstants.sPreferredSymmetricAlgorithms.get(0); } // has Integrity packet enabled! JcePGPDataEncryptorBuilder encryptorBuilder = new JcePGPDataEncryptorBuilder(algo) .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME) .setWithIntegrityPacket(true); cPk = new PGPEncryptedDataGenerator(encryptorBuilder); if (input.getSymmetricPassphrase() != null) { // Symmetric encryption log.add(LogType.MSG_PSE_SYMMETRIC, indent); JcePBEKeyEncryptionMethodGenerator symmetricEncryptionGenerator = new JcePBEKeyEncryptionMethodGenerator(input.getSymmetricPassphrase().getCharArray()); cPk.addMethod(symmetricEncryptionGenerator); } else { log.add(LogType.MSG_PSE_ASYMMETRIC, indent); // Asymmetric encryption for (long id : input.getEncryptionMasterKeyIds()) { try { CanonicalizedPublicKeyRing keyRing = mProviderHelper.getCanonicalizedPublicKeyRing(KeyRings.buildUnifiedKeyRingUri(id)); Set<Long> encryptSubKeyIds = keyRing.getEncryptIds(); for (Long subKeyId : encryptSubKeyIds) { CanonicalizedPublicKey key = keyRing.getPublicKey(subKeyId); cPk.addMethod(key.getPubKeyEncryptionGenerator(input.isHiddenRecipients())); log.add( LogType.MSG_PSE_KEY_OK, indent + 1, KeyFormattingUtils.convertKeyIdToHex(subKeyId)); } if (encryptSubKeyIds.isEmpty()) { log.add( LogType.MSG_PSE_KEY_WARN, indent + 1, KeyFormattingUtils.convertKeyIdToHex(id)); if (input.isFailOnMissingEncryptionKeyIds()) { return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); } } } catch (ProviderHelper.NotFoundException e) { log.add( LogType.MSG_PSE_KEY_UNKNOWN, indent + 1, KeyFormattingUtils.convertKeyIdToHex(id)); if (input.isFailOnMissingEncryptionKeyIds()) { return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); } } } } } /* Initialize signature generator object for later usage */ PGPSignatureGenerator signatureGenerator = null; if (enableSignature) { updateProgress(R.string.progress_preparing_signature, 4, 100); try { boolean cleartext = input.isCleartextSignature() && input.isEnableAsciiArmorOutput() && !enableEncryption; signatureGenerator = signingKey.getDataSignatureGenerator( input.getSignatureHashAlgorithm(), cleartext, cryptoInput.getCryptoData(), cryptoInput.getSignatureTime()); } catch (PgpGeneralException e) { log.add(LogType.MSG_PSE_ERROR_NFC, indent); return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); } } ProgressScaler progressScaler = new ProgressScaler(mProgressable, 8, 95, 100); PGPCompressedDataGenerator compressGen = null; OutputStream pOut; OutputStream encryptionOut = null; BCPGOutputStream bcpgOut; ByteArrayOutputStream detachedByteOut = null; ArmoredOutputStream detachedArmorOut = null; BCPGOutputStream detachedBcpgOut = null; try { if (enableEncryption) { /* actual encryption */ updateProgress(R.string.progress_encrypting, 8, 100); log.add(enableSignature ? LogType.MSG_PSE_SIGCRYPTING : LogType.MSG_PSE_ENCRYPTING, indent); indent += 1; encryptionOut = cPk.open(out, new byte[1 << 16]); if (enableCompression) { log.add(LogType.MSG_PSE_COMPRESSING, indent); compressGen = new PGPCompressedDataGenerator(input.getCompressionId()); bcpgOut = new BCPGOutputStream(compressGen.open(encryptionOut)); } else { bcpgOut = new BCPGOutputStream(encryptionOut); } if (enableSignature) { signatureGenerator.generateOnePassVersion(false).encode(bcpgOut); } PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator(); char literalDataFormatTag; if (input.isCleartextSignature()) { literalDataFormatTag = PGPLiteralData.UTF8; } else { literalDataFormatTag = PGPLiteralData.BINARY; } pOut = literalGen.open( bcpgOut, literalDataFormatTag, inputData.getOriginalFilename(), new Date(), new byte[1 << 16]); long alreadyWritten = 0; int length; byte[] buffer = new byte[1 << 16]; InputStream in = inputData.getInputStream(); while ((length = in.read(buffer)) > 0) { pOut.write(buffer, 0, length); // update signature buffer if signature is requested if (enableSignature) { signatureGenerator.update(buffer, 0, length); } alreadyWritten += length; if (inputData.getSize() > 0) { long progress = 100 * alreadyWritten / inputData.getSize(); progressScaler.setProgress((int) progress, 100); } } literalGen.close(); indent -= 1; } else if (enableSignature && input.isCleartextSignature() && input.isEnableAsciiArmorOutput()) { /* cleartext signature: sign-only of ascii text */ updateProgress(R.string.progress_signing, 8, 100); log.add(LogType.MSG_PSE_SIGNING_CLEARTEXT, indent); // write -----BEGIN PGP SIGNED MESSAGE----- armorOut.beginClearText(input.getSignatureHashAlgorithm()); InputStream in = inputData.getInputStream(); final BufferedReader reader = new BufferedReader(new InputStreamReader(in)); // update signature buffer with first line processLine(reader.readLine(), armorOut, signatureGenerator); // TODO: progress: fake annealing? while (true) { String line = reader.readLine(); // end cleartext signature with newline, see http://tools.ietf.org/html/rfc4880#section-7 if (line == null) { armorOut.write(NEW_LINE); break; } armorOut.write(NEW_LINE); // update signature buffer with input line signatureGenerator.update(NEW_LINE); processLine(line, armorOut, signatureGenerator); } armorOut.endClearText(); pOut = new BCPGOutputStream(armorOut); } else if (enableSignature && input.isDetachedSignature()) { /* detached signature */ updateProgress(R.string.progress_signing, 8, 100); log.add(LogType.MSG_PSE_SIGNING_DETACHED, indent); InputStream in = inputData.getInputStream(); // handle output stream separately for detached signatures detachedByteOut = new ByteArrayOutputStream(); OutputStream detachedOut = detachedByteOut; if (input.isEnableAsciiArmorOutput()) { detachedArmorOut = new ArmoredOutputStream(new BufferedOutputStream(detachedOut, 1 << 16)); if (input.getVersionHeader() != null) { detachedArmorOut.setHeader("Version", input.getVersionHeader()); } detachedOut = detachedArmorOut; } detachedBcpgOut = new BCPGOutputStream(detachedOut); long alreadyWritten = 0; int length; byte[] buffer = new byte[1 << 16]; while ((length = in.read(buffer)) > 0) { // no output stream is written, no changed to original data! signatureGenerator.update(buffer, 0, length); alreadyWritten += length; if (inputData.getSize() > 0) { long progress = 100 * alreadyWritten / inputData.getSize(); progressScaler.setProgress((int) progress, 100); } } pOut = null; } else if (enableSignature && !input.isCleartextSignature() && !input.isDetachedSignature()) { /* sign-only binary (files/data stream) */ updateProgress(R.string.progress_signing, 8, 100); log.add(LogType.MSG_PSE_SIGNING, indent); InputStream in = inputData.getInputStream(); if (enableCompression) { compressGen = new PGPCompressedDataGenerator(input.getCompressionId()); bcpgOut = new BCPGOutputStream(compressGen.open(out)); } else { bcpgOut = new BCPGOutputStream(out); } signatureGenerator.generateOnePassVersion(false).encode(bcpgOut); PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator(); pOut = literalGen.open( bcpgOut, PGPLiteralData.BINARY, inputData.getOriginalFilename(), new Date(), new byte[1 << 16]); long alreadyWritten = 0; int length; byte[] buffer = new byte[1 << 16]; while ((length = in.read(buffer)) > 0) { pOut.write(buffer, 0, length); signatureGenerator.update(buffer, 0, length); alreadyWritten += length; if (inputData.getSize() > 0) { long progress = 100 * alreadyWritten / inputData.getSize(); progressScaler.setProgress((int) progress, 100); } } literalGen.close(); } else { pOut = null; // TODO: Is this log right? log.add(LogType.MSG_PSE_CLEARSIGN_ONLY, indent); } if (enableSignature) { updateProgress(R.string.progress_generating_signature, 95, 100); try { if (detachedBcpgOut != null) { signatureGenerator.generate().encode(detachedBcpgOut); } else { signatureGenerator.generate().encode(pOut); } } catch (NfcSyncPGPContentSignerBuilder.NfcInteractionNeeded e) { // this secret key diverts to a OpenPGP card, throw exception with hash that will be // signed log.add(LogType.MSG_PSE_PENDING_NFC, indent); return new PgpSignEncryptResult( log, RequiredInputParcel.createNfcSignOperation( signingKey.getRing().getMasterKeyId(), signingKey.getKeyId(), e.hashToSign, e.hashAlgo, cryptoInput.getSignatureTime()), cryptoInput); } } // closing outputs // NOTE: closing needs to be done in the correct order! if (encryptionOut != null) { if (compressGen != null) { compressGen.close(); } encryptionOut.close(); } // Note: Closing ArmoredOutputStream does not close the underlying stream if (armorOut != null) { armorOut.close(); } // Note: Closing ArmoredOutputStream does not close the underlying stream if (detachedArmorOut != null) { detachedArmorOut.close(); } // Also closes detachedBcpgOut if (detachedByteOut != null) { detachedByteOut.close(); } if (out != null) { out.close(); } if (outputStream != null) { outputStream.close(); } } catch (SignatureException e) { log.add(LogType.MSG_PSE_ERROR_SIG, indent); return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); } catch (PGPException e) { log.add(LogType.MSG_PSE_ERROR_PGP, indent); return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); } catch (IOException e) { log.add(LogType.MSG_PSE_ERROR_IO, indent); return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); } updateProgress(R.string.progress_done, 100, 100); log.add(LogType.MSG_PSE_OK, indent); PgpSignEncryptResult result = new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_OK, log); if (detachedByteOut != null) { try { detachedByteOut.flush(); detachedByteOut.close(); } catch (IOException e) { // silently catch } result.setDetachedSignature(detachedByteOut.toByteArray()); } return result; }
/** Export keys */ public void exportKeys(long[] masterKeyIds, boolean exportSecret) { Log.d(Constants.TAG, "exportKeys started"); // Send all information needed to service to export key in other thread final Intent intent = new Intent(mActivity, KeychainIntentService.class); intent.setAction(KeychainIntentService.ACTION_EXPORT_KEYRING); // fill values for this action Bundle data = new Bundle(); data.putString(KeychainIntentService.EXPORT_FILENAME, mExportFilename); data.putBoolean(KeychainIntentService.EXPORT_SECRET, exportSecret); if (masterKeyIds == null) { data.putBoolean(KeychainIntentService.EXPORT_ALL, true); } else { data.putLongArray(KeychainIntentService.EXPORT_KEY_RING_MASTER_KEY_ID, masterKeyIds); } intent.putExtra(KeychainIntentService.EXTRA_DATA, data); // Message is received after exporting is done in KeychainIntentService KeychainIntentServiceHandler exportHandler = new KeychainIntentServiceHandler( mActivity, mActivity.getString(R.string.progress_exporting), ProgressDialog.STYLE_HORIZONTAL, true, new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialogInterface) { mActivity.stopService(intent); } }) { public void handleMessage(Message message) { // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { // get returned data bundle Bundle returnData = message.getData(); int exported = returnData.getInt(KeychainIntentService.RESULT_EXPORT); String toastMessage; if (exported == 1) { toastMessage = mActivity.getString(R.string.key_exported); } else if (exported > 0) { toastMessage = mActivity.getString(R.string.keys_exported, exported); } else { toastMessage = mActivity.getString(R.string.no_keys_exported); } Toast.makeText(mActivity, toastMessage, Toast.LENGTH_SHORT).show(); } } }; // Create a new Messenger for the communication back Messenger messenger = new Messenger(exportHandler); intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); // show progress dialog exportHandler.showProgressDialog(mActivity); // start service with intent mActivity.startService(intent); }
private void loadData(Uri dataUri) { if (dataUri.equals(mDataUri)) { Log.d(Constants.TAG, "Same URI, no need to load the data again!"); return; } mDataUri = dataUri; Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString()); { // label whether secret key is available, and edit button if it is final long masterKeyId = ProviderHelper.getMasterKeyId(getActivity(), mDataUri); if (ProviderHelper.hasSecretKeyByMasterKeyId(getActivity(), masterKeyId)) { // set this attribute. this is a LITTLE unclean, but we have the info available // right here, so why not. mSecretKey.setTextColor(getResources().getColor(R.color.emphasis)); mSecretKey.setText(R.string.secret_key_yes); // certify button // TODO this button MIGHT be useful if the user wants to // certify a private key with another... // mActionCertify.setVisibility(View.GONE); // edit button mActionEdit.setVisibility(View.VISIBLE); mActionEdit.setOnClickListener( new View.OnClickListener() { public void onClick(View view) { Intent editIntent = new Intent(getActivity(), EditKeyActivity.class); editIntent.setData( KeychainContract.KeyRings.buildSecretKeyRingsByMasterKeyIdUri( Long.toString(masterKeyId))); editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY); startActivityForResult(editIntent, 0); } }); } else { mSecretKey.setTextColor(Color.BLACK); mSecretKey.setText(getResources().getString(R.string.secret_key_no)); // certify button mActionCertify.setVisibility(View.VISIBLE); // edit button mActionEdit.setVisibility(View.GONE); } // TODO see todo note above, doing this here for now mActionCertify.setOnClickListener( new View.OnClickListener() { public void onClick(View view) { certifyKey( KeychainContract.KeyRings.buildPublicKeyRingsByMasterKeyIdUri( Long.toString(masterKeyId))); } }); } mActionEncrypt.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { encryptToContact(mDataUri); } }); mUserIdsAdapter = new ViewKeyUserIdsAdapter(getActivity(), null, 0); mUserIds.setAdapter(mUserIdsAdapter); mKeysAdapter = new ViewKeyKeysAdapter(getActivity(), null, 0); mKeys.setAdapter(mKeysAdapter); // Prepare the loaders. Either re-connect with an existing ones, // or start new ones. getActivity().getSupportLoaderManager().initLoader(LOADER_ID_KEYRING, null, this); getActivity().getSupportLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this); getActivity().getSupportLoaderManager().initLoader(LOADER_ID_KEYS, null, this); }
@Override public void onResume() { super.onResume(); LinkedIdWizard wizard = (LinkedIdWizard) getActivity(); mFingerprint = wizard.mFingerprint; mMasterKeyId = wizard.mMasterKeyId; final String oAuthCode = wizard.oAuthGetCode(); final String oAuthState = wizard.oAuthGetState(); if (oAuthCode == null) { Log.d(Constants.TAG, "no code"); return; } final String gistText = GithubResource.generate(wizard, mFingerprint); Log.d(Constants.TAG, "got code: " + oAuthCode); new AsyncTask<Void, Void, JSONObject>() { @Override protected JSONObject doInBackground(Void... dummy) { try { long timer = System.currentTimeMillis(); JSONObject params = new JSONObject(); params.put("client_id", "7a011b66275f244d3f21"); params.put("client_secret", "eaced8a6655719d8c6848396de97b3f5d7a89fec"); params.put("code", oAuthCode); params.put("state", oAuthState); JSONObject result = jsonHttpRequest("https://github.com/login/oauth/access_token", params, null); // ux flow: this operation should take at last a second timer = System.currentTimeMillis() - timer; if (timer < 1000) try { Thread.sleep(1000 - timer); } catch (InterruptedException e) { // never mind } return result; } catch (IOException e) { Log.e(Constants.TAG, "error in request", e); } catch (JSONException e) { throw new AssertionError("json error, this is a bug!"); } return null; } @Override protected void onPostExecute(JSONObject result) { super.onPostExecute(result); Log.d(Constants.TAG, "response: " + result); if (result == null || !result.has("access_token")) { mStatus1.setDisplayedChild(3); return; } mStatus1.setDisplayedChild(2); step2PostGist(result.optString("access_token"), gistText); } }.execute(); }
/** * Gets a cached passphrase from memory by sending an intent to the service. This method is * designed to wait until the service returns the passphrase. * * @return passphrase or null (if no passphrase is cached for this keyId) */ public static Passphrase getCachedPassphrase(Context context, long masterKeyId, long subKeyId) throws KeyNotFoundException { Log.d( Constants.TAG, "PassphraseCacheService.getCachedPassphrase() for masterKeyId " + masterKeyId + ", subKeyId " + subKeyId); Intent intent = new Intent(context, PassphraseCacheService.class); intent.setAction(ACTION_PASSPHRASE_CACHE_GET); final Object mutex = new Object(); final Message returnMessage = Message.obtain(); HandlerThread handlerThread = new HandlerThread("getPassphraseThread"); handlerThread.start(); Handler returnHandler = new Handler(handlerThread.getLooper()) { @Override public void handleMessage(Message message) { // copy over result to handle after mutex.wait returnMessage.what = message.what; returnMessage.copyFrom(message); synchronized (mutex) { mutex.notify(); } // quit handlerThread getLooper().quit(); } }; // Create a new Messenger for the communication back Messenger messenger = new Messenger(returnHandler); intent.putExtra(EXTRA_KEY_ID, masterKeyId); intent.putExtra(EXTRA_SUBKEY_ID, subKeyId); intent.putExtra(EXTRA_MESSENGER, messenger); // send intent to this service context.startService(intent); // Wait on mutex until passphrase is returned to handlerThread. Note that this local // variable is used in the handler closure above, so it does make sense here! // noinspection SynchronizationOnLocalVariableOrMethodParameter synchronized (mutex) { try { mutex.wait(3000); } catch (InterruptedException e) { // don't care } } switch (returnMessage.what) { case MSG_PASSPHRASE_CACHE_GET_OKAY: Bundle returnData = returnMessage.getData(); returnData.setClassLoader(context.getClassLoader()); return returnData.getParcelable(EXTRA_PASSPHRASE); case MSG_PASSPHRASE_CACHE_GET_KEY_NOT_FOUND: throw new KeyNotFoundException(); default: Log.e(Constants.TAG, "timeout case!"); throw new KeyNotFoundException("should not happen!"); } }
/** 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; }
/** 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; }