1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-11-20 11:22:05 +01:00

Test for detection of uncompressed, signed messages, and improve decryption of seip messages

This commit is contained in:
Paul Schaub 2022-05-05 12:43:44 +02:00
parent 7b7707b3a9
commit 64a50266f1
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
3 changed files with 46 additions and 8 deletions

View file

@ -140,7 +140,10 @@ public final class DecryptionStreamFactory {
return new DecryptionStream(pgpInStream, resultBuilder, integrityProtectedEncryptedInputStream, null); return new DecryptionStream(pgpInStream, resultBuilder, integrityProtectedEncryptedInputStream, null);
} }
if (openPgpIn.isLikelyOpenPgpMessage()) { // Data appears to be OpenPGP message,
// or we handle it as such, since user provided a session-key for decryption
if (openPgpIn.isLikelyOpenPgpMessage() ||
(openPgpIn.isBinaryOpenPgp() && options.getSessionKey() != null)) {
outerDecodingStream = openPgpIn; outerDecodingStream = openPgpIn;
objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory(outerDecodingStream); objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory(outerDecodingStream);
// Parse OpenPGP message // Parse OpenPGP message

View file

@ -77,14 +77,14 @@ public class OpenPgpInputStream extends BufferedInputStream {
} }
private void inspectBuffer() throws IOException { private void inspectBuffer() throws IOException {
if (determineIsArmored()) { if (checkForAsciiArmor()) {
return; return;
} }
determineIsBinaryOpenPgp(); checkForBinaryOpenPgp();
} }
private boolean determineIsArmored() { private boolean checkForAsciiArmor() {
if (startsWithIgnoringWhitespace(buffer, ARMOR_HEADER, bufferLen)) { if (startsWithIgnoringWhitespace(buffer, ARMOR_HEADER, bufferLen)) {
containsArmorHeader = true; containsArmorHeader = true;
return true; return true;
@ -100,7 +100,7 @@ public class OpenPgpInputStream extends BufferedInputStream {
* This breaks down though if we read plausible garbage where the data accidentally makes sense, * This breaks down though if we read plausible garbage where the data accidentally makes sense,
* or valid, yet incomplete packets (remember, we are still only working on a portion of the data). * or valid, yet incomplete packets (remember, we are still only working on a portion of the data).
*/ */
private void determineIsBinaryOpenPgp() throws IOException { private void checkForBinaryOpenPgp() throws IOException {
if (bufferLen == -1) { if (bufferLen == -1) {
// Empty data // Empty data
return; return;
@ -210,7 +210,6 @@ public class OpenPgpInputStream extends BufferedInputStream {
} }
containsOpenPgpPackets = true; containsOpenPgpPackets = true;
isLikelyOpenPgpMessage = true;
break; break;
case SYMMETRIC_KEY_ENC_SESSION: case SYMMETRIC_KEY_ENC_SESSION:
@ -295,6 +294,8 @@ public class OpenPgpInputStream extends BufferedInputStream {
case SYMMETRIC_KEY_ENC: case SYMMETRIC_KEY_ENC:
// No data to compare :( // No data to compare :(
containsOpenPgpPackets = true; containsOpenPgpPackets = true;
// While this is a valid OpenPGP message, enabling the line below would lead to too many false positives
// isLikelyOpenPgpMessage = true;
break; break;
case MARKER: case MARKER:

View file

@ -13,20 +13,30 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.util.Random; import java.util.Random;
import org.bouncycastle.bcpg.ArmoredInputStream; import org.bouncycastle.bcpg.ArmoredInputStream;
import org.bouncycastle.bcpg.CompressionAlgorithmTags; import org.bouncycastle.bcpg.CompressionAlgorithmTags;
import org.bouncycastle.openpgp.PGPCompressedDataGenerator; import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.util.io.Streams; import org.bouncycastle.util.io.Streams;
import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.CompressionAlgorithm;
import org.pgpainless.encryption_signing.EncryptionStream;
import org.pgpainless.encryption_signing.ProducerOptions;
import org.pgpainless.encryption_signing.SigningOptions;
import org.pgpainless.key.protection.SecretKeyRingProtector;
public class OpenPgpInputStreamTest { public class OpenPgpInputStreamTest {
private static final Random RANDOM = new Random(); private static final Random RANDOM = new Random();
@RepeatedTest(10) @RepeatedTest(100)
public void randomBytesDoNotContainOpenPgpData() throws IOException { public void randomBytesDoNotContainOpenPgpData() throws IOException {
byte[] randomBytes = new byte[1000000]; byte[] randomBytes = new byte[1000000];
RANDOM.nextBytes(randomBytes); RANDOM.nextBytes(randomBytes);
@ -43,7 +53,7 @@ public class OpenPgpInputStreamTest {
assertArrayEquals(randomBytes, outBytes); assertArrayEquals(randomBytes, outBytes);
} }
@RepeatedTest(10) @Test
public void largeCompressedDataIsBinaryOpenPgp() throws IOException { public void largeCompressedDataIsBinaryOpenPgp() throws IOException {
// Since we are compressing RANDOM data, the output will likely be roughly the same size // Since we are compressing RANDOM data, the output will likely be roughly the same size
// So we very likely will end up with data larger than the MAX_BUFFER_SIZE // So we very likely will end up with data larger than the MAX_BUFFER_SIZE
@ -725,4 +735,28 @@ public class OpenPgpInputStreamTest {
assertFalse(openPgpInputStream.isAsciiArmored()); assertFalse(openPgpInputStream.isAsciiArmored());
assertTrue(openPgpInputStream.isNonOpenPgp()); assertTrue(openPgpInputStream.isNonOpenPgp());
} }
@Test
public void testSignedMessageConsumption() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
ByteArrayInputStream plaintext = new ByteArrayInputStream("Hello, World!\n".getBytes(StandardCharsets.UTF_8));
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
.modernKeyRing("Sigmund <sigmund@exmplample.com>", null);
ByteArrayOutputStream signedOut = new ByteArrayOutputStream();
EncryptionStream signer = PGPainless.encryptAndOrSign()
.onOutputStream(signedOut)
.withOptions(ProducerOptions.sign(new SigningOptions()
.addSignature(SecretKeyRingProtector.unprotectedKeys(), secretKeys))
.setAsciiArmor(false)
.overrideCompressionAlgorithm(CompressionAlgorithm.UNCOMPRESSED));
Streams.pipeAll(plaintext, signer);
signer.close();
byte[] binary = signedOut.toByteArray();
OpenPgpInputStream openPgpIn = new OpenPgpInputStream(new ByteArrayInputStream(binary));
assertFalse(openPgpIn.isAsciiArmored());
assertTrue(openPgpIn.isLikelyOpenPgpMessage());
}
} }