package org.spongycastle.openpgp; import java.io.EOFException; import java.io.InputStream; import java.security.NoSuchProviderException; import java.security.Provider; import org.spongycastle.bcpg.BCPGInputStream; import org.spongycastle.bcpg.InputStreamPacket; import org.spongycastle.bcpg.SymmetricEncIntegrityPacket; import org.spongycastle.bcpg.SymmetricKeyEncSessionPacket; import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory; import org.spongycastle.openpgp.operator.PGPDataDecryptor; import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder; import org.spongycastle.util.io.TeeInputStream; /** * A password based encryption object. */ public class PGPPBEEncryptedData extends PGPEncryptedData { SymmetricKeyEncSessionPacket keyData; PGPPBEEncryptedData( SymmetricKeyEncSessionPacket keyData, InputStreamPacket encData) { super(encData); this.keyData = keyData; } /** * Return the raw input stream for the data stream. * * @return InputStream */ public InputStream getInputStream() { return encData.getInputStream(); } /** * Return the decrypted input stream, using the passed in passPhrase. * * @param passPhrase * @param provider * @return InputStream * @throws PGPException * @throws NoSuchProviderException * @deprecated use PBEDataDecryptorFactory method */ public InputStream getDataStream( char[] passPhrase, String provider) throws PGPException, NoSuchProviderException { return getDataStream(passPhrase, PGPUtil.getProvider(provider)); } /** * Return the decrypted input stream, using the passed in passPhrase. * * @param passPhrase * @param provider * @return InputStream * @throws PGPException * @deprecated use PBEDataDecryptorFactory method */ public InputStream getDataStream( char[] passPhrase, Provider provider) throws PGPException { return getDataStream(new JcePBEDataDecryptorFactoryBuilder(new JcaPGPDigestCalculatorProviderBuilder().setProvider(provider).build()).setProvider(provider).build(passPhrase)); } /** * Return the symmetric key algorithm required to decrypt the data protected by this object. * * @param dataDecryptorFactory decryptor factory to use to recover the session data. * @return the integer encryption algorithm code. * @throws PGPException if the session data cannot be recovered. */ public int getSymmetricAlgorithm( PBEDataDecryptorFactory dataDecryptorFactory) throws PGPException { byte[] key = dataDecryptorFactory.makeKeyFromPassPhrase(keyData.getEncAlgorithm(), keyData.getS2K()); byte[] sessionData = dataDecryptorFactory.recoverSessionData(keyData.getEncAlgorithm(), key, keyData.getSecKeyData()); return sessionData[0]; } /** * Open an input stream which will provide the decrypted data protected by this object. * * @param dataDecryptorFactory decryptor factory to use to recover the session data and provide the stream. * @return the resulting input stream * @throws PGPException if the session data cannot be recovered or the stream cannot be created. */ public InputStream getDataStream( PBEDataDecryptorFactory dataDecryptorFactory) throws PGPException { try { int keyAlgorithm = keyData.getEncAlgorithm(); byte[] key = dataDecryptorFactory.makeKeyFromPassPhrase(keyAlgorithm, keyData.getS2K()); boolean withIntegrityPacket = encData instanceof SymmetricEncIntegrityPacket; byte[] sessionData = dataDecryptorFactory.recoverSessionData(keyData.getEncAlgorithm(), key, keyData.getSecKeyData()); byte[] sessionKey = new byte[sessionData.length - 1]; System.arraycopy(sessionData, 1, sessionKey, 0, sessionKey.length); PGPDataDecryptor dataDecryptor = dataDecryptorFactory.createDataDecryptor(withIntegrityPacket, sessionData[0] & 0xff, sessionKey); encStream = new BCPGInputStream(dataDecryptor.getInputStream(encData.getInputStream())); if (withIntegrityPacket) { truncStream = new TruncatedStream(encStream); integrityCalculator = dataDecryptor.getIntegrityCalculator(); encStream = new TeeInputStream(truncStream, integrityCalculator.getOutputStream()); } byte[] iv = new byte[dataDecryptor.getBlockSize()]; for (int i = 0; i != iv.length; i++) { int ch = encStream.read(); if (ch < 0) { throw new EOFException("unexpected end of stream."); } iv[i] = (byte)ch; } int v1 = encStream.read(); int v2 = encStream.read(); if (v1 < 0 || v2 < 0) { throw new EOFException("unexpected end of stream."); } // Note: the oracle attack on "quick check" bytes is not deemed // a security risk for PBE (see PGPPublicKeyEncryptedData) boolean repeatCheckPassed = iv[iv.length - 2] == (byte) v1 && iv[iv.length - 1] == (byte) v2; // Note: some versions of PGP appear to produce 0 for the extra // bytes rather than repeating the two previous bytes boolean zeroesCheckPassed = v1 == 0 && v2 == 0; if (!repeatCheckPassed && !zeroesCheckPassed) { throw new PGPDataValidationException("data check failed."); } return encStream; } catch (PGPException e) { throw e; } catch (Exception e) { throw new PGPException("Exception creating cipher", e); } } }