diff options
Diffstat (limited to 'pg/src/main/j2me/org/spongycastle/openpgp/PGPPBEEncryptedData.java')
-rw-r--r-- | pg/src/main/j2me/org/spongycastle/openpgp/PGPPBEEncryptedData.java | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/pg/src/main/j2me/org/spongycastle/openpgp/PGPPBEEncryptedData.java b/pg/src/main/j2me/org/spongycastle/openpgp/PGPPBEEncryptedData.java new file mode 100644 index 00000000..d13edf6a --- /dev/null +++ b/pg/src/main/j2me/org/spongycastle/openpgp/PGPPBEEncryptedData.java @@ -0,0 +1,141 @@ +package org.spongycastle.openpgp; + +import java.io.EOFException; +import java.io.InputStream; + +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.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 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); + } + } +} |