diff options
Diffstat (limited to 'libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPEncryptedDataGenerator.java')
-rw-r--r-- | libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPEncryptedDataGenerator.java | 537 |
1 files changed, 537 insertions, 0 deletions
diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPEncryptedDataGenerator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPEncryptedDataGenerator.java new file mode 100644 index 000000000..9f816e392 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPEncryptedDataGenerator.java @@ -0,0 +1,537 @@ +package org.spongycastle.openpgp; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.List; + +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.PacketTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.operator.PBEKeyEncryptionMethodGenerator; +import org.spongycastle.openpgp.operator.PGPDataEncryptor; +import org.spongycastle.openpgp.operator.PGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.openpgp.operator.PGPKeyEncryptionMethodGenerator; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator; +import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator; +import org.spongycastle.util.io.TeeOutputStream; + +/** + * Generator for encrypted objects. + */ +public class PGPEncryptedDataGenerator + implements SymmetricKeyAlgorithmTags, StreamGenerator +{ + /** + * Specifier for SHA-1 S2K PBE generator. + */ + public static final int S2K_SHA1 = HashAlgorithmTags.SHA1; + + /** + * Specifier for SHA-224 S2K PBE generator. + */ + public static final int S2K_SHA224 = HashAlgorithmTags.SHA224; + + /** + * Specifier for SHA-256 S2K PBE generator. + */ + public static final int S2K_SHA256 = HashAlgorithmTags.SHA256; + + /** + * Specifier for SHA-384 S2K PBE generator. + */ + public static final int S2K_SHA384 = HashAlgorithmTags.SHA384; + + /** + * Specifier for SHA-512 S2K PBE generator. + */ + public static final int S2K_SHA512 = HashAlgorithmTags.SHA512; + + private BCPGOutputStream pOut; + private OutputStream cOut; + private boolean oldFormat = false; + private PGPDigestCalculator digestCalc; + private OutputStream genOut; + private PGPDataEncryptorBuilder dataEncryptorBuilder; + + private List methods = new ArrayList(); + private int defAlgorithm; + private SecureRandom rand; + + private static Provider defProvider; + + /** + * Base constructor. + * + * @param encAlgorithm the symmetric algorithm to use. + * @param rand source of randomness + * @param provider the provider name to use for encryption algorithms. + * @deprecated use constructor that takes a PGPDataEncryptor + */ + public PGPEncryptedDataGenerator( + int encAlgorithm, + SecureRandom rand, + String provider) + { + this(new JcePGPDataEncryptorBuilder(encAlgorithm).setSecureRandom(rand).setProvider(provider)); + } + + /** + * Base constructor. + * + * @param encAlgorithm the symmetric algorithm to use. + * @param rand source of randomness + * @param provider the provider to use for encryption algorithms. + * @deprecated use constructor that takes a PGPDataEncryptorBuilder + */ + public PGPEncryptedDataGenerator( + int encAlgorithm, + SecureRandom rand, + Provider provider) + { + this(new JcePGPDataEncryptorBuilder(encAlgorithm).setSecureRandom(rand).setProvider(provider)); + } + + /** + * Creates a cipher stream which will have an integrity packet + * associated with it. + * + * @param encAlgorithm + * @param withIntegrityPacket + * @param rand + * @param provider + * @deprecated use constructor that takes a PGPDataEncryptorBuilder + */ + public PGPEncryptedDataGenerator( + int encAlgorithm, + boolean withIntegrityPacket, + SecureRandom rand, + String provider) + { + this(new JcePGPDataEncryptorBuilder(encAlgorithm).setWithIntegrityPacket(withIntegrityPacket).setSecureRandom(rand).setProvider(provider)); + } + + /** + * Creates a cipher stream which will have an integrity packet + * associated with it. + * + * @param encAlgorithm + * @param withIntegrityPacket + * @param rand + * @param provider + * @deprecated use constructor that takes a PGPDataEncryptorBuilder + */ + public PGPEncryptedDataGenerator( + int encAlgorithm, + boolean withIntegrityPacket, + SecureRandom rand, + Provider provider) + { + this(new JcePGPDataEncryptorBuilder(encAlgorithm).setWithIntegrityPacket(withIntegrityPacket).setSecureRandom(rand).setProvider(provider)); + } + + /** + * Base constructor. + * + * @param encAlgorithm the symmetric algorithm to use. + * @param rand source of randomness + * @param oldFormat PGP 2.6.x compatibility required. + * @param provider the provider to use for encryption algorithms. + * @deprecated use constructor that takes a PGPDataEncryptorBuilder + */ + public PGPEncryptedDataGenerator( + int encAlgorithm, + SecureRandom rand, + boolean oldFormat, + String provider) + { + this(new JcePGPDataEncryptorBuilder(encAlgorithm).setSecureRandom(rand).setProvider(provider), oldFormat); + } + + /** + * Base constructor. + * + * @param encAlgorithm the symmetric algorithm to use. + * @param rand source of randomness + * @param oldFormat PGP 2.6.x compatibility required. + * @param provider the provider to use for encryption algorithms. + * @deprecated use constructor that takes a PGPDataEncryptorBuilder + */ + public PGPEncryptedDataGenerator( + int encAlgorithm, + SecureRandom rand, + boolean oldFormat, + Provider provider) + { + this(new JcePGPDataEncryptorBuilder(encAlgorithm).setSecureRandom(rand).setProvider(provider), oldFormat); + } + + /** + * Base constructor. + * + * @param encryptorBuilder builder to create actual data encryptor. + */ + public PGPEncryptedDataGenerator(PGPDataEncryptorBuilder encryptorBuilder) + { + this(encryptorBuilder, false); + } + + /** + * Base constructor with the option to turn on formatting for PGP 2.6.x compatibility. + * + * @param encryptorBuilder builder to create actual data encryptor. + * @param oldFormat PGP 2.6.x compatibility required. + */ + public PGPEncryptedDataGenerator(PGPDataEncryptorBuilder encryptorBuilder, boolean oldFormat) + { + this.dataEncryptorBuilder = encryptorBuilder; + this.oldFormat = oldFormat; + + this.defAlgorithm = dataEncryptorBuilder.getAlgorithm(); + this.rand = dataEncryptorBuilder.getSecureRandom(); + } + + /** + * Add a PBE encryption method to the encrypted object using the default algorithm (S2K_SHA1). + * + * @param passPhrase + * @throws NoSuchProviderException + * @throws PGPException + * @deprecated use addMethod that takes PGPKeyEncryptionMethodGenerator + */ + public void addMethod( + char[] passPhrase) + throws NoSuchProviderException, PGPException + { + addMethod(passPhrase, HashAlgorithmTags.SHA1); + } + + /** + * Add a PBE encryption method to the encrypted object. + * + * @param passPhrase passphrase to use to generate key. + * @param s2kDigest digest algorithm to use for S2K calculation + * @throws NoSuchProviderException + * @throws PGPException + * @deprecated use addMethod that takes PGPKeyEncryptionMethodGenerator + */ + public void addMethod( + char[] passPhrase, + int s2kDigest) + throws NoSuchProviderException, PGPException + { + if (defProvider == null) + { + defProvider = new BouncyCastleProvider(); + } + + addMethod(new JcePBEKeyEncryptionMethodGenerator(passPhrase, new JcaPGPDigestCalculatorProviderBuilder().setProvider(defProvider).build().get(s2kDigest)).setProvider(defProvider).setSecureRandom(rand)); + } + + /** + * Add a public key encrypted session key to the encrypted object. + * + * @param key + * @throws NoSuchProviderException + * @throws PGPException + * @deprecated use addMethod that takes PGPKeyEncryptionMethodGenerator + */ + public void addMethod( + PGPPublicKey key) + throws NoSuchProviderException, PGPException + { + if (!key.isEncryptionKey()) + { + throw new IllegalArgumentException("passed in key not an encryption key!"); + } + + if (defProvider == null) + { + defProvider = new BouncyCastleProvider(); + } + + addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(key).setProvider(defProvider).setSecureRandom(rand)); + } + + /** + * Added a key encryption method to be used to encrypt the session data associated + * with this encrypted data. + * + * @param method key encryption method to use. + */ + public void addMethod(PGPKeyEncryptionMethodGenerator method) + { + methods.add(method); + } + + private void addCheckSum( + byte[] sessionInfo) + { + int check = 0; + + for (int i = 1; i != sessionInfo.length - 2; i++) + { + check += sessionInfo[i] & 0xff; + } + + sessionInfo[sessionInfo.length - 2] = (byte)(check >> 8); + sessionInfo[sessionInfo.length - 1] = (byte)(check); + } + + private byte[] createSessionInfo( + int algorithm, + byte[] keyBytes) + { + byte[] sessionInfo = new byte[keyBytes.length + 3]; + sessionInfo[0] = (byte) algorithm; + System.arraycopy(keyBytes, 0, sessionInfo, 1, keyBytes.length); + addCheckSum(sessionInfo); + return sessionInfo; + } + + /** + * If buffer is non null stream assumed to be partial, otherwise the + * length will be used to output a fixed length packet. + * <p> + * The stream created can be closed off by either calling close() + * on the stream or close() on the generator. Closing the returned + * stream does not close off the OutputStream parameter out. + * + * @param out + * @param length + * @param buffer + * @return + * @throws java.io.IOException + * @throws PGPException + * @throws IllegalStateException + */ + private OutputStream open( + OutputStream out, + long length, + byte[] buffer) + throws IOException, PGPException, IllegalStateException + { + if (cOut != null) + { + throw new IllegalStateException("generator already in open state"); + } + + if (methods.size() == 0) + { + throw new IllegalStateException("no encryption methods specified"); + } + + byte[] key = null; + + pOut = new BCPGOutputStream(out); + + defAlgorithm = dataEncryptorBuilder.getAlgorithm(); + rand = dataEncryptorBuilder.getSecureRandom(); + + if (methods.size() == 1) + { + + if (methods.get(0) instanceof PBEKeyEncryptionMethodGenerator) + { + PBEKeyEncryptionMethodGenerator m = (PBEKeyEncryptionMethodGenerator)methods.get(0); + + key = m.getKey(dataEncryptorBuilder.getAlgorithm()); + + pOut.writePacket(((PGPKeyEncryptionMethodGenerator)methods.get(0)).generate(defAlgorithm, null)); + } + else + { + key = PGPUtil.makeRandomKey(defAlgorithm, rand); + byte[] sessionInfo = createSessionInfo(defAlgorithm, key); + PGPKeyEncryptionMethodGenerator m = (PGPKeyEncryptionMethodGenerator)methods.get(0); + + pOut.writePacket(m.generate(defAlgorithm, sessionInfo)); + } + } + else // multiple methods + { + key = PGPUtil.makeRandomKey(defAlgorithm, rand); + byte[] sessionInfo = createSessionInfo(defAlgorithm, key); + + for (int i = 0; i != methods.size(); i++) + { + PGPKeyEncryptionMethodGenerator m = (PGPKeyEncryptionMethodGenerator)methods.get(i); + + pOut.writePacket(m.generate(defAlgorithm, sessionInfo)); + } + } + + try + { + PGPDataEncryptor dataEncryptor = dataEncryptorBuilder.build(key); + + digestCalc = dataEncryptor.getIntegrityCalculator(); + + if (buffer == null) + { + // + // we have to add block size + 2 for the generated IV and + 1 + 22 if integrity protected + // + if (digestCalc != null) + { + pOut = new ClosableBCPGOutputStream(out, PacketTags.SYM_ENC_INTEGRITY_PRO, length + dataEncryptor.getBlockSize() + 2 + 1 + 22); + + pOut.write(1); // version number + } + else + { + pOut = new ClosableBCPGOutputStream(out, PacketTags.SYMMETRIC_KEY_ENC, length + dataEncryptor.getBlockSize() + 2, oldFormat); + } + } + else + { + if (digestCalc != null) + { + pOut = new ClosableBCPGOutputStream(out, PacketTags.SYM_ENC_INTEGRITY_PRO, buffer); + pOut.write(1); // version number + } + else + { + pOut = new ClosableBCPGOutputStream(out, PacketTags.SYMMETRIC_KEY_ENC, buffer); + } + } + + genOut = cOut = dataEncryptor.getOutputStream(pOut); + + if (digestCalc != null) + { + genOut = new TeeOutputStream(digestCalc.getOutputStream(), cOut); + } + + byte[] inLineIv = new byte[dataEncryptor.getBlockSize() + 2]; + rand.nextBytes(inLineIv); + inLineIv[inLineIv.length - 1] = inLineIv[inLineIv.length - 3]; + inLineIv[inLineIv.length - 2] = inLineIv[inLineIv.length - 4]; + + genOut.write(inLineIv); + + return new WrappedGeneratorStream(genOut, this); + } + catch (Exception e) + { + throw new PGPException("Exception creating cipher", e); + } + } + + /** + * Return an outputstream which will encrypt the data as it is written + * to it. + * <p> + * The stream created can be closed off by either calling close() + * on the stream or close() on the generator. Closing the returned + * stream does not close off the OutputStream parameter out. + * + * @param out + * @param length + * @return OutputStream + * @throws IOException + * @throws PGPException + */ + public OutputStream open( + OutputStream out, + long length) + throws IOException, PGPException + { + return this.open(out, length, null); + } + + /** + * Return an outputstream which will encrypt the data as it is written + * to it. The stream will be written out in chunks according to the size of the + * passed in buffer. + * <p> + * The stream created can be closed off by either calling close() + * on the stream or close() on the generator. Closing the returned + * stream does not close off the OutputStream parameter out. + * <p> + * <b>Note</b>: if the buffer is not a power of 2 in length only the largest power of 2 + * bytes worth of the buffer will be used. + * + * @param out + * @param buffer the buffer to use. + * @return OutputStream + * @throws IOException + * @throws PGPException + */ + public OutputStream open( + OutputStream out, + byte[] buffer) + throws IOException, PGPException + { + return this.open(out, 0, buffer); + } + + /** + * Close off the encrypted object - this is equivalent to calling close on the stream + * returned by the open() method. + * <p> + * <b>Note</b>: This does not close the underlying output stream, only the stream on top of it created by the open() method. + * @throws java.io.IOException + */ + public void close() + throws IOException + { + if (cOut != null) + { + if (digestCalc != null) + { + // + // hand code a mod detection packet + // + BCPGOutputStream bOut = new BCPGOutputStream(genOut, PacketTags.MOD_DETECTION_CODE, 20); + + bOut.flush(); + + byte[] dig = digestCalc.getDigest(); + + cOut.write(dig); + } + + cOut.close(); + + cOut = null; + pOut = null; + } + } + + private class ClosableBCPGOutputStream + extends BCPGOutputStream + { + public ClosableBCPGOutputStream(OutputStream out, int symmetricKeyEnc, byte[] buffer) + throws IOException + { + super(out, symmetricKeyEnc, buffer); + } + + public ClosableBCPGOutputStream(OutputStream out, int symmetricKeyEnc, long length, boolean oldFormat) + throws IOException + { + super(out, symmetricKeyEnc, length, oldFormat); + } + + public ClosableBCPGOutputStream(OutputStream out, int symEncIntegrityPro, long length) + throws IOException + { + super(out, symEncIntegrityPro, length); + } + + public void close() + throws IOException + { + this.finish(); + } + } +} |