List of usage examples for org.bouncycastle.openpgp PGPPublicKeyEncryptedData getSymmetricAlgorithm
public int getSymmetricAlgorithm(PublicKeyDataDecryptorFactory dataDecryptorFactory) throws PGPException
From source file:org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyOperation.java
License:Open Source License
private EncryptStreamResult handleEncryptedPacket(PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput, PGPEncryptedDataList enc, OperationLog log, int indent, boolean useBackupCode) throws PGPException { EncryptStreamResult result = new EncryptStreamResult(); boolean asymmetricPacketFound = false; boolean symmetricPacketFound = false; boolean anyPacketFound = false; boolean decryptedSessionKeyAvailable = false; PGPPublicKeyEncryptedData encryptedDataAsymmetric = null; PGPPBEEncryptedData encryptedDataSymmetric = null; CanonicalizedSecretKey decryptionKey = null; CachingDataDecryptorFactory cachedKeyDecryptorFactory = new CachingDataDecryptorFactory( Constants.BOUNCY_CASTLE_PROVIDER_NAME, cryptoInput.getCryptoData()); ;/* w w w .ja va 2 s.c o m*/ Passphrase passphrase = null; Iterator<?> it = enc.getEncryptedDataObjects(); // go through all objects and find one we can decrypt while (it.hasNext()) { Object obj = it.next(); if (obj instanceof PGPPublicKeyEncryptedData) { anyPacketFound = true; PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj; long subKeyId = encData.getKeyID(); log.add(LogType.MSG_DC_ASYM, indent, KeyFormattingUtils.convertKeyIdToHex(subKeyId)); decryptedSessionKeyAvailable = cachedKeyDecryptorFactory.hasCachedSessionData(encData); if (decryptedSessionKeyAvailable) { asymmetricPacketFound = true; encryptedDataAsymmetric = encData; break; } CachedPublicKeyRing cachedPublicKeyRing; try { // get actual keyring object based on master key id cachedPublicKeyRing = mProviderHelper .getCachedPublicKeyRing(KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(subKeyId)); long masterKeyId = cachedPublicKeyRing.getMasterKeyId(); // allow only specific keys for decryption? if (input.getAllowedKeyIds() != null) { Log.d(Constants.TAG, "encData.getKeyID(): " + subKeyId); Log.d(Constants.TAG, "mAllowedKeyIds: " + input.getAllowedKeyIds()); Log.d(Constants.TAG, "masterKeyId: " + masterKeyId); if (!input.getAllowedKeyIds().contains(masterKeyId)) { // this key is in our db, but NOT allowed! // continue with the next packet in the while loop result.skippedDisallowedKey = true; log.add(LogType.MSG_DC_ASKIP_NOT_ALLOWED, indent + 1); continue; } } SecretKeyType secretKeyType = cachedPublicKeyRing.getSecretKeyType(subKeyId); if (!secretKeyType.isUsable()) { decryptionKey = null; log.add(LogType.MSG_DC_ASKIP_UNAVAILABLE, indent + 1); continue; } // get actual subkey which has been used for this encryption packet CanonicalizedSecretKeyRing canonicalizedSecretKeyRing = mProviderHelper .getCanonicalizedSecretKeyRing(masterKeyId); CanonicalizedSecretKey candidateDecryptionKey = canonicalizedSecretKeyRing .getSecretKey(subKeyId); if (!candidateDecryptionKey.canEncrypt()) { log.add(LogType.MSG_DC_ASKIP_BAD_FLAGS, indent + 1); continue; } if (secretKeyType == SecretKeyType.DIVERT_TO_CARD) { passphrase = null; } else if (secretKeyType == SecretKeyType.PASSPHRASE_EMPTY) { passphrase = new Passphrase(""); } else if (cryptoInput.hasPassphrase()) { passphrase = cryptoInput.getPassphrase(); } else { // if no passphrase was explicitly set try to get it from the cache service try { // returns "" if key has no passphrase passphrase = getCachedPassphrase(subKeyId); log.add(LogType.MSG_DC_PASS_CACHED, indent + 1); } catch (PassphraseCacheInterface.NoSecretKeyException e) { log.add(LogType.MSG_DC_ERROR_NO_KEY, indent + 1); return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log)); } // if passphrase was not cached, return here indicating that a passphrase is missing! if (passphrase == null) { log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent + 1); return result.with(new DecryptVerifyResult(log, RequiredInputParcel.createRequiredDecryptPassphrase(masterKeyId, subKeyId), cryptoInput)); } } // check for insecure encryption key if (!PgpSecurityConstants.isSecureKey(candidateDecryptionKey)) { log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1); result.insecureEncryptionKey = true; } // we're good, write down the data for later asymmetricPacketFound = true; encryptedDataAsymmetric = encData; decryptionKey = candidateDecryptionKey; } catch (PgpKeyNotFoundException | ProviderHelper.NotFoundException e) { // continue with the next packet in the while loop log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1); continue; } // break out of while, only decrypt the first packet where we have a key break; } else if (obj instanceof PGPPBEEncryptedData) { anyPacketFound = true; log.add(LogType.MSG_DC_SYM, indent); if (!input.isAllowSymmetricDecryption()) { log.add(LogType.MSG_DC_SYM_SKIP, indent + 1); continue; } /* * When mAllowSymmetricDecryption == true and we find a data packet here, * we do not search for other available asymmetric packets! */ symmetricPacketFound = true; encryptedDataSymmetric = (PGPPBEEncryptedData) obj; // if no passphrase is given, return here // indicating that a passphrase is missing! if (!cryptoInput.hasPassphrase()) { try { passphrase = getCachedPassphrase(key.symmetric); log.add(LogType.MSG_DC_PASS_CACHED, indent + 1); } catch (PassphraseCacheInterface.NoSecretKeyException e) { // nvm } if (passphrase == null) { log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent + 1); RequiredInputParcel requiredInputParcel = useBackupCode ? RequiredInputParcel.createRequiredBackupCode() : RequiredInputParcel.createRequiredSymmetricPassphrase(); return result.with(new DecryptVerifyResult(log, requiredInputParcel, cryptoInput)); } } else { passphrase = cryptoInput.getPassphrase(); } // break out of while, only decrypt the first packet break; } } // More data, just acknowledge and ignore. while (it.hasNext()) { Object obj = it.next(); if (obj instanceof PGPPublicKeyEncryptedData) { PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj; long subKeyId = encData.getKeyID(); log.add(LogType.MSG_DC_TRAIL_ASYM, indent, KeyFormattingUtils.convertKeyIdToHex(subKeyId)); } else if (obj instanceof PGPPBEEncryptedData) { log.add(LogType.MSG_DC_TRAIL_SYM, indent); } else { log.add(LogType.MSG_DC_TRAIL_UNKNOWN, indent); } } // we made sure above one of these two would be true if (symmetricPacketFound) { PGPDigestCalculatorProvider digestCalcProvider = new JcaPGPDigestCalculatorProviderBuilder() .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(); PBEDataDecryptorFactory decryptorFactory = new JcePBEDataDecryptorFactoryBuilder(digestCalcProvider) .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.getCharArray()); try { result.cleartextStream = encryptedDataSymmetric.getDataStream(decryptorFactory); } catch (PGPDataValidationException e) { log.add(LogType.MSG_DC_ERROR_SYM_PASSPHRASE, indent + 1); RequiredInputParcel requiredInputParcel = useBackupCode ? RequiredInputParcel.createRequiredBackupCode() : RequiredInputParcel.createRequiredSymmetricPassphrase(); return result.with(new DecryptVerifyResult(log, requiredInputParcel, cryptoInput)); } result.encryptedData = encryptedDataSymmetric; result.symmetricEncryptionAlgo = encryptedDataSymmetric.getSymmetricAlgorithm(decryptorFactory); } else if (asymmetricPacketFound) { CachingDataDecryptorFactory decryptorFactory; if (decryptedSessionKeyAvailable) { decryptorFactory = cachedKeyDecryptorFactory; } else { try { log.add(LogType.MSG_DC_UNLOCKING, indent + 1); if (!decryptionKey.unlock(passphrase)) { log.add(LogType.MSG_DC_ERROR_BAD_PASSPHRASE, indent + 1); return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log)); } } catch (PgpGeneralException e) { log.add(LogType.MSG_DC_ERROR_EXTRACT_KEY, indent + 1); return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log)); } decryptorFactory = decryptionKey.getCachingDecryptorFactory(cryptoInput); // special case: if the decryptor does not have a session key cached for this encrypted // data, and can't actually decrypt on its own, return a pending intent if (!decryptorFactory.canDecrypt() && !decryptorFactory.hasCachedSessionData(encryptedDataAsymmetric)) { log.add(LogType.MSG_DC_PENDING_NFC, indent + 1); return result.with(new DecryptVerifyResult(log, RequiredInputParcel.createSecurityTokenDecryptOperation( decryptionKey.getRing().getMasterKeyId(), decryptionKey.getKeyId(), encryptedDataAsymmetric.getSessionKey()[0]), cryptoInput)); } } try { result.cleartextStream = encryptedDataAsymmetric.getDataStream(decryptorFactory); } catch (PGPKeyValidationException | ArrayIndexOutOfBoundsException e) { log.add(LogType.MSG_DC_ERROR_CORRUPT_DATA, indent + 1); return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log)); } result.symmetricEncryptionAlgo = encryptedDataAsymmetric.getSymmetricAlgorithm(decryptorFactory); result.encryptedData = encryptedDataAsymmetric; Map<ByteBuffer, byte[]> cachedSessionKeys = decryptorFactory.getCachedSessionKeys(); cryptoInput.addCryptoData(cachedSessionKeys); if (cachedSessionKeys.size() >= 1) { Entry<ByteBuffer, byte[]> entry = cachedSessionKeys.entrySet().iterator().next(); result.sessionKey = entry.getKey().array(); result.decryptedSessionKey = entry.getValue(); } } else { // there wasn't even any useful data if (!anyPacketFound) { log.add(LogType.MSG_DC_ERROR_NO_DATA, indent + 1); return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_NO_DATA, log)); } // there was data but key wasn't allowed if (result.skippedDisallowedKey) { log.add(LogType.MSG_DC_ERROR_NO_KEY, indent + 1); return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_KEY_DISALLOWED, log)); } // no packet has been found where we have the corresponding secret key in our db log.add(LogType.MSG_DC_ERROR_NO_KEY, indent + 1); return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log)); } return result; }