Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various minor improvements #29

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 39 additions & 24 deletions src/main/java/org/c02e/jpgpj/Decryptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;

import org.bouncycastle.bcpg.ArmoredInputStream;
import org.bouncycastle.openpgp.PGPCompressedData;
Expand Down Expand Up @@ -293,40 +294,54 @@ public void clearSecrets() {
*/
public FileMetadata decrypt(File ciphertext, File plaintext)
throws IOException, PGPException {
if (ciphertext.equals(plaintext))
if (Objects.equals(ciphertext.getAbsoluteFile(), plaintext.getAbsoluteFile()))
throw new IOException("cannot decrypt " + ciphertext +
" over itself");

// delete old output file
plaintext.delete();
if (plaintext.delete()) {
log.debug("decrypt({}) deleted {}", ciphertext, plaintext);
}

InputStream input = null;
OutputStream output = null;
try {
int bestBufferSize =
Util.bestFileBufferSize(ciphertext.length(), maxFileBufferSize);
input = new BufferedInputStream(
new FileInputStream(ciphertext), bestBufferSize);
output = new BufferedOutputStream(
new FileOutputStream(plaintext), bestBufferSize);
long inputSize = ciphertext.length();
try (InputStream sourceStream = new FileInputStream(ciphertext);
InputStream input = wrapSourceInputStream(sourceStream, inputSize);
OutputStream targetStream = new FileOutputStream(plaintext);
OutputStream output = wrapTargetOutputStream(targetStream, inputSize)) {
return decrypt(input, output);
} catch (Exception e) {
// delete output file if anything went wrong
if (output != null)
try {
output.close();
plaintext.delete();
} catch (Exception ee) {
log.error("failed to delete bad output file {}",
plaintext, ee);
}
if (!plaintext.delete()) {
log.warn("decrypt({}) cannot clean up {}", ciphertext, plaintext);
}
throw e;
} finally {
try { output.close(); } catch (Exception e) {}
try { input.close(); } catch (Exception e) {}
}
}

/**
* @param sourceStream Original source (ciphertext) {@link InputStream}
* @param inputSize Expected input (ciphertext) size
* @return A wrapper buffered stream optimized for the input size according to
* the current encryptor settings
* @throws IOException If failed to generate the wrapper
*/
public InputStream wrapSourceInputStream(InputStream sourceStream, long inputSize) throws IOException {
int bestFileBufferSize = Util.bestFileBufferSize(inputSize, getMaxFileBufferSize());
return new BufferedInputStream(sourceStream, bestFileBufferSize);
}

/**
* @param targetStream Original target (plaintext) {@link OutputStream}
* @param inputSize Expected input (ciphertext) size
* @return A wrapper buffered stream optimized for the input size according to
* the current encryptor settings
* @throws IOException If failed to generate the wrapper
*/
public OutputStream wrapTargetOutputStream(OutputStream targetStream, long inputSize) throws IOException {
int bestFileBufferSize = Util.bestFileBufferSize(inputSize, getMaxFileBufferSize());
return new BufferedOutputStream(targetStream, bestFileBufferSize);
}

/**
* Decrypts the specified PGP message into the specified output stream,
* and (if {@link #isVerificationRequired}) verifies the message
Expand Down Expand Up @@ -537,7 +552,7 @@ protected InputStream decrypt(Iterator<?> data)
* Decrypts the encrypted data as the returned input stream.
*/
protected InputStream decrypt(PGPPublicKeyEncryptedData data, Subkey subkey)
throws IOException, PGPException {
throws IOException, PGPException {
if (data == null || subkey == null)
throw new DecryptionException("no suitable decryption key found");

Expand Down Expand Up @@ -680,7 +695,7 @@ protected PBEDataDecryptorFactory buildSymmetricKeyDecryptor(char[] passphraseCh
new BcPGPDigestCalculatorProvider());
}

protected byte[] getCopyBuffer() {
public byte[] getCopyBuffer() {
return new byte[getCopyFileBufferSize()];
}

Expand Down
112 changes: 76 additions & 36 deletions src/main/java/org/c02e/jpgpj/Encryptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,7 @@ public void clearSecrets() {
*/
public void encrypt(File plaintext, File ciphertext)
throws IOException, PGPException {
if (plaintext.equals(ciphertext))
if (Objects.equals(plaintext.getAbsoluteFile(), ciphertext.getAbsoluteFile()))
throw new IOException("cannot encrypt " + plaintext +
" over itself");

Expand All @@ -612,33 +612,47 @@ public void encrypt(File plaintext, File ciphertext)
log.debug("encrypt({}) deleted {}", plaintext, ciphertext);
}

InputStream input = null;
OutputStream output = null;
try {
int bestFileBufferSize =
Util.bestFileBufferSize(plaintext.length(), maxFileBufferSize);
input = new BufferedInputStream(
new FileInputStream(plaintext), bestFileBufferSize);
output = new BufferedOutputStream(
new FileOutputStream(ciphertext),
estimateOutFileSize(plaintext.length()));
encrypt(input, output, new FileMetadata(plaintext));
FileMetadata meta = new FileMetadata(plaintext);
long inputSize = meta.getLength();
try (InputStream sourceStream = new FileInputStream(plaintext);
InputStream input = wrapSourceInputStream(sourceStream, inputSize);
OutputStream targetStream = new FileOutputStream(ciphertext);
OutputStream output = wrapTargetOutputStream(targetStream, inputSize)) {
encrypt(input, output, meta);
} catch (Exception e) {
// delete output file if anything went wrong
if (output != null)
try {
output.close();
ciphertext.delete();
} catch (Exception ee) {
log.error("failed to delete bad output file " + plaintext, ee);
}
if (!ciphertext.delete()) {
log.warn("encrypt({}) cannot clean up {}", plaintext, ciphertext);
}
throw e;
} finally {
try { output.close(); } catch (Exception e) {}
try { input.close(); } catch (Exception e) {}
}
}

/**
* @param sourceStream Original source (plaintext) {@link InputStream}
* @param inputSize Expected input (plaintext) size
* @return A wrapper buffered stream optimized for the input size according to
* the current encryptor settings
* @throws IOException If failed to generate the wrapper
*/
public InputStream wrapSourceInputStream(InputStream sourceStream, long inputSize) throws IOException {
int bestFileBufferSize = Util.bestFileBufferSize(inputSize, getMaxFileBufferSize());
return new BufferedInputStream(sourceStream, bestFileBufferSize);
}

/**
* @param targetStream Original target (ciphertext) {@link OutputStream}
* @param inputSize Expected input (plaintext) size
* @return A wrapper buffered stream optimized for the input size according to
* the current encryptor settings
* @throws IOException If failed to generate the wrapper
* @see #estimateOutFileBufferSize(long)
*/
public OutputStream wrapTargetOutputStream(OutputStream targetStream, long inputSize) throws IOException {
int bestFileBufferSize = estimateOutFileBufferSize(inputSize);
return new BufferedOutputStream(targetStream, bestFileBufferSize);
}

/**
* Signs, compresses, and encrypts the specified content as a PGP message
* into the specified output stream (with no optional metadata).
Expand Down Expand Up @@ -1082,27 +1096,47 @@ protected PGPContentSignerBuilder buildSignerBuilder(int keyAlgorithm, int hashA
return new BcPGPContentSignerBuilder(keyAlgorithm, hashAlgorithm);
}

protected byte[] getEncryptionBuffer(FileMetadata meta) {
return new byte[bestPacketSize(meta)];
public byte[] getEncryptionBuffer(FileMetadata meta) {
return getEncryptionBuffer((meta == null) ? 0L : meta.getLength());
}

protected byte[] getCompressionBuffer(FileMetadata meta) {
return new byte[bestPacketSize(meta)];
public byte[] getEncryptionBuffer(long inputSize) {
return new byte[bestPacketSize(inputSize)];
}

protected byte[] getLiteralBuffer(FileMetadata meta) {
return new byte[bestPacketSize(meta)];
public byte[] getCompressionBuffer(FileMetadata meta) {
return getCompressionBuffer((meta == null) ? 0L : meta.getLength());
}

protected byte[] getCopyBuffer(FileMetadata meta) {
int len = (meta == null) ? 0 : (int) meta.getLength();
public byte[] getCompressionBuffer(long inputSize) {
return new byte[bestPacketSize(inputSize)];
}

public byte[] getLiteralBuffer(FileMetadata meta) {
return getLiteralBuffer((meta == null) ? 0L : meta.getLength());
}

public byte[] getLiteralBuffer(long inputSize) {
return new byte[bestPacketSize(inputSize)];
}

public byte[] getCopyBuffer(FileMetadata meta) {
return getCopyBuffer((meta == null) ? 0L : meta.getLength());
}

public byte[] getCopyBuffer(long inputSize) {
int len = (int) inputSize;
if (len <= 0 || len > MAX_ENCRYPT_COPY_BUFFER_SIZE)
len = MAX_ENCRYPT_COPY_BUFFER_SIZE;
return new byte[len];
}

protected int bestPacketSize(FileMetadata meta) {
int len = (int) meta.getLength();
public int bestPacketSize(FileMetadata meta) {
return bestPacketSize((meta == null) ? 0L : meta.getLength());
}

public int bestPacketSize(long inputSize) {
int len = (int) inputSize;

if (len > 0) {
// add some extra space for packet flags
Expand All @@ -1112,13 +1146,19 @@ protected int bestPacketSize(FileMetadata meta) {
}

// cap size at 64k
if (len <= 0 || len > 0x10000)
len = 0x10000;
if (len <= 0 || len > MAX_ENCRYPT_COPY_BUFFER_SIZE) {
len = MAX_ENCRYPT_COPY_BUFFER_SIZE;
}

return len;
}

protected int estimateOutFileSize(long inFileSize) {
/**
* @param inFileSize Input (plaintext) file size
* @return The recommended buffering for the target (ciphertext) output stream
* @see #getMaxFileBufferSize()
*/
public int estimateOutFileBufferSize(long inFileSize) {
int maxBufSize = getMaxFileBufferSize();
if (inFileSize >= maxBufSize) return maxBufSize;

Expand All @@ -1145,7 +1185,7 @@ protected int estimateOutFileSize(long inFileSize) {

protected class SigningOutputStream extends FilterOutputStream {
protected final AtomicBoolean finished = new AtomicBoolean(false);
protected FileMetadata meta;
protected final FileMetadata meta;
protected List<PGPSignatureGenerator> sigs;

public SigningOutputStream(OutputStream out, List<Key> keys, FileMetadata meta)
Expand Down
10 changes: 5 additions & 5 deletions src/test/groovy/org/c02e/jpgpj/EncryptorSpec.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -779,7 +779,7 @@ hQEMAyne546XDHBhAQ...
setup:
def encryptor = new Encryptor();
expect:
encryptor.estimateOutFileSize(inputSize) == outputSize
encryptor.estimateOutFileBufferSize(inputSize) == outputSize
where:
inputSize << [
-1, 0, 1,
Expand All @@ -800,7 +800,7 @@ hQEMAyne546XDHBhAQ...
def encryptor = new Encryptor();
encryptor.asciiArmored = true
expect:
encryptor.estimateOutFileSize(inputSize) == outputSize
encryptor.estimateOutFileBufferSize(inputSize) == outputSize
where:
inputSize << [
-1, 0, 1,
Expand All @@ -822,7 +822,7 @@ hQEMAyne546XDHBhAQ...
def encryptor = new Encryptor(new Ring(stream('test-ring.asc')))
encryptor.ring.findAll('key-1')*.signing*.forSigning = false
expect:
encryptor.estimateOutFileSize(inputSize) == outputSize
encryptor.estimateOutFileBufferSize(inputSize) == outputSize
where:
inputSize << [
-1, 0, 1,
Expand All @@ -849,7 +849,7 @@ hQEMAyne546XDHBhAQ...
when:
def plainIn = new ByteArrayInputStream(new byte[inputSize])
encryptor.encrypt plainIn, cipherOut
def estimate = encryptor.estimateOutFileSize(inputSize)
def estimate = encryptor.estimateOutFileBufferSize(inputSize)
def actual = cipherOut.size()
then:
estimate > actual
Expand All @@ -870,7 +870,7 @@ hQEMAyne546XDHBhAQ...
when:
def plainIn = new ByteArrayInputStream(new byte[inputSize])
encryptor.encrypt plainIn, cipherOut
def estimate = encryptor.estimateOutFileSize(inputSize)
def estimate = encryptor.estimateOutFileBufferSize(inputSize)
def actual = cipherOut.size()
then:
estimate > actual
Expand Down