mirror of
https://github.com/pgpainless/pgpainless.git
synced 2025-09-09 10:19:39 +02:00
Port OpenPGPInputStream to Kotlin as OpenPGPAnimalSnifferInputStream
This commit is contained in:
parent
3a0ee1c101
commit
702db4d75c
8 changed files with 340 additions and 367 deletions
|
@ -1,340 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package org.pgpainless.decryption_verification;
|
|
||||||
|
|
||||||
import static org.bouncycastle.bcpg.PacketTags.AEAD_ENC_DATA;
|
|
||||||
import static org.bouncycastle.bcpg.PacketTags.COMPRESSED_DATA;
|
|
||||||
import static org.bouncycastle.bcpg.PacketTags.EXPERIMENTAL_1;
|
|
||||||
import static org.bouncycastle.bcpg.PacketTags.EXPERIMENTAL_2;
|
|
||||||
import static org.bouncycastle.bcpg.PacketTags.EXPERIMENTAL_3;
|
|
||||||
import static org.bouncycastle.bcpg.PacketTags.EXPERIMENTAL_4;
|
|
||||||
import static org.bouncycastle.bcpg.PacketTags.LITERAL_DATA;
|
|
||||||
import static org.bouncycastle.bcpg.PacketTags.MARKER;
|
|
||||||
import static org.bouncycastle.bcpg.PacketTags.MOD_DETECTION_CODE;
|
|
||||||
import static org.bouncycastle.bcpg.PacketTags.ONE_PASS_SIGNATURE;
|
|
||||||
import static org.bouncycastle.bcpg.PacketTags.PADDING;
|
|
||||||
import static org.bouncycastle.bcpg.PacketTags.PUBLIC_KEY;
|
|
||||||
import static org.bouncycastle.bcpg.PacketTags.PUBLIC_KEY_ENC_SESSION;
|
|
||||||
import static org.bouncycastle.bcpg.PacketTags.PUBLIC_SUBKEY;
|
|
||||||
import static org.bouncycastle.bcpg.PacketTags.RESERVED;
|
|
||||||
import static org.bouncycastle.bcpg.PacketTags.SECRET_KEY;
|
|
||||||
import static org.bouncycastle.bcpg.PacketTags.SECRET_SUBKEY;
|
|
||||||
import static org.bouncycastle.bcpg.PacketTags.SIGNATURE;
|
|
||||||
import static org.bouncycastle.bcpg.PacketTags.SYMMETRIC_KEY_ENC;
|
|
||||||
import static org.bouncycastle.bcpg.PacketTags.SYMMETRIC_KEY_ENC_SESSION;
|
|
||||||
import static org.bouncycastle.bcpg.PacketTags.SYM_ENC_INTEGRITY_PRO;
|
|
||||||
import static org.bouncycastle.bcpg.PacketTags.TRUST;
|
|
||||||
import static org.bouncycastle.bcpg.PacketTags.USER_ATTRIBUTE;
|
|
||||||
import static org.bouncycastle.bcpg.PacketTags.USER_ID;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
|
|
||||||
import org.bouncycastle.bcpg.AEADEncDataPacket;
|
|
||||||
import org.bouncycastle.bcpg.BCPGInputStream;
|
|
||||||
import org.bouncycastle.bcpg.CompressedDataPacket;
|
|
||||||
import org.bouncycastle.bcpg.LiteralDataPacket;
|
|
||||||
import org.bouncycastle.bcpg.MarkerPacket;
|
|
||||||
import org.bouncycastle.bcpg.OnePassSignaturePacket;
|
|
||||||
import org.bouncycastle.bcpg.Packet;
|
|
||||||
import org.bouncycastle.bcpg.PacketFormat;
|
|
||||||
import org.bouncycastle.bcpg.PublicKeyEncSessionPacket;
|
|
||||||
import org.bouncycastle.bcpg.PublicKeyPacket;
|
|
||||||
import org.bouncycastle.bcpg.SecretKeyPacket;
|
|
||||||
import org.bouncycastle.bcpg.SignaturePacket;
|
|
||||||
import org.bouncycastle.bcpg.SymmetricEncIntegrityPacket;
|
|
||||||
import org.bouncycastle.bcpg.SymmetricKeyEncSessionPacket;
|
|
||||||
import org.bouncycastle.bcpg.UnsupportedPacketVersionException;
|
|
||||||
import org.bouncycastle.openpgp.PGPCompressedData;
|
|
||||||
import org.bouncycastle.openpgp.PGPEncryptedData;
|
|
||||||
import org.bouncycastle.openpgp.PGPLiteralData;
|
|
||||||
import org.bouncycastle.openpgp.PGPOnePassSignature;
|
|
||||||
import org.bouncycastle.util.Arrays;
|
|
||||||
import org.pgpainless.algorithm.AEADAlgorithm;
|
|
||||||
import org.pgpainless.algorithm.CompressionAlgorithm;
|
|
||||||
import org.pgpainless.algorithm.HashAlgorithm;
|
|
||||||
import org.pgpainless.algorithm.PublicKeyAlgorithm;
|
|
||||||
import org.pgpainless.algorithm.SignatureType;
|
|
||||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* InputStream used to determine the nature of potential OpenPGP data.
|
|
||||||
*/
|
|
||||||
public class OpenPgpInputStream extends BufferedInputStream {
|
|
||||||
|
|
||||||
@SuppressWarnings("CharsetObjectCanBeUsed")
|
|
||||||
private static final byte[] ARMOR_HEADER = "-----BEGIN PGP ".getBytes(Charset.forName("UTF8"));
|
|
||||||
|
|
||||||
// Buffer beginning bytes of the data
|
|
||||||
public static final int MAX_BUFFER_SIZE = 8192 * 2;
|
|
||||||
|
|
||||||
private final byte[] buffer;
|
|
||||||
private final int bufferLen;
|
|
||||||
|
|
||||||
private boolean containsArmorHeader;
|
|
||||||
private boolean containsOpenPgpPackets;
|
|
||||||
private boolean isLikelyOpenPgpMessage;
|
|
||||||
|
|
||||||
public OpenPgpInputStream(InputStream in, boolean check) throws IOException {
|
|
||||||
super(in, MAX_BUFFER_SIZE);
|
|
||||||
|
|
||||||
mark(MAX_BUFFER_SIZE);
|
|
||||||
buffer = new byte[MAX_BUFFER_SIZE];
|
|
||||||
bufferLen = read(buffer);
|
|
||||||
reset();
|
|
||||||
|
|
||||||
if (check) {
|
|
||||||
inspectBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public OpenPgpInputStream(InputStream in) throws IOException {
|
|
||||||
this(in, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void inspectBuffer() throws IOException {
|
|
||||||
if (checkForAsciiArmor()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
checkForBinaryOpenPgp();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkForAsciiArmor() {
|
|
||||||
if (startsWithIgnoringWhitespace(buffer, ARMOR_HEADER, bufferLen)) {
|
|
||||||
containsArmorHeader = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is still brittle.
|
|
||||||
* Basically we try to parse OpenPGP packets from the buffer.
|
|
||||||
* If we run into exceptions, then we know that the data is non-OpenPGP'ish.
|
|
||||||
* <p>
|
|
||||||
* 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).
|
|
||||||
*/
|
|
||||||
private void checkForBinaryOpenPgp() throws IOException {
|
|
||||||
if (bufferLen == -1) {
|
|
||||||
// Empty data
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteArrayInputStream bufferIn = new ByteArrayInputStream(buffer, 0, bufferLen);
|
|
||||||
BCPGInputStream pIn = new BCPGInputStream(bufferIn);
|
|
||||||
try {
|
|
||||||
nonExhaustiveParseAndCheckPlausibility(pIn);
|
|
||||||
} catch (IOException | UnsupportedPacketVersionException | NegativeArraySizeException e) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void nonExhaustiveParseAndCheckPlausibility(BCPGInputStream packetIn)
|
|
||||||
throws IOException {
|
|
||||||
Packet packet = packetIn.readPacket();
|
|
||||||
switch (packet.getPacketTag()) {
|
|
||||||
case PUBLIC_KEY_ENC_SESSION:
|
|
||||||
PublicKeyEncSessionPacket pkesk = (PublicKeyEncSessionPacket) packet;
|
|
||||||
if (PublicKeyAlgorithm.fromId(pkesk.getAlgorithm()) == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SIGNATURE:
|
|
||||||
SignaturePacket sig = (SignaturePacket) packet;
|
|
||||||
if (SignatureType.fromCode(sig.getSignatureType()) == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (PublicKeyAlgorithm.fromId(sig.getKeyAlgorithm()) == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (HashAlgorithm.fromId(sig.getHashAlgorithm()) == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ONE_PASS_SIGNATURE:
|
|
||||||
OnePassSignaturePacket ops = (OnePassSignaturePacket) packet;
|
|
||||||
if (SignatureType.fromCode(ops.getSignatureType()) == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (PublicKeyAlgorithm.fromId(ops.getKeyAlgorithm()) == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (HashAlgorithm.fromId(ops.getHashAlgorithm()) == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SYMMETRIC_KEY_ENC_SESSION:
|
|
||||||
SymmetricKeyEncSessionPacket skesk = (SymmetricKeyEncSessionPacket) packet;
|
|
||||||
if (SymmetricKeyAlgorithm.fromId(skesk.getEncAlgorithm()) == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SECRET_KEY:
|
|
||||||
SecretKeyPacket secKey = (SecretKeyPacket) packet;
|
|
||||||
PublicKeyPacket sPubKey = secKey.getPublicKeyPacket();
|
|
||||||
if (PublicKeyAlgorithm.fromId(sPubKey.getAlgorithm()) == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (sPubKey.getVersion() < 3 && sPubKey.getVersion() > 6) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PUBLIC_KEY:
|
|
||||||
PublicKeyPacket pubKey = (PublicKeyPacket) packet;
|
|
||||||
if (PublicKeyAlgorithm.fromId(pubKey.getAlgorithm()) == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (pubKey.getVersion() < 3 && pubKey.getVersion() > 6) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case COMPRESSED_DATA:
|
|
||||||
CompressedDataPacket comp = (CompressedDataPacket) packet;
|
|
||||||
if (CompressionAlgorithm.fromId(comp.getAlgorithm()) == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SYMMETRIC_KEY_ENC:
|
|
||||||
// Not much we can check here
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MARKER:
|
|
||||||
MarkerPacket m = (MarkerPacket) packet;
|
|
||||||
if (!Arrays.areEqual(
|
|
||||||
m.getEncoded(PacketFormat.CURRENT),
|
|
||||||
new byte[] {(byte) 0xca, 0x03, 0x50, 0x47, 0x50})) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_DATA:
|
|
||||||
LiteralDataPacket lit = (LiteralDataPacket) packet;
|
|
||||||
if (lit.getFormat() != 'b' &&
|
|
||||||
lit.getFormat() != 'u' &&
|
|
||||||
lit.getFormat() != 't' &&
|
|
||||||
lit.getFormat() != 'l' &&
|
|
||||||
lit.getFormat() != '1' &&
|
|
||||||
lit.getFormat() != 'm') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SYM_ENC_INTEGRITY_PRO:
|
|
||||||
SymmetricEncIntegrityPacket seipd = (SymmetricEncIntegrityPacket) packet;
|
|
||||||
if (seipd.getVersion() == SymmetricEncIntegrityPacket.VERSION_1) {
|
|
||||||
break; // not much to check here
|
|
||||||
}
|
|
||||||
if (seipd.getVersion() != SymmetricEncIntegrityPacket.VERSION_2) {
|
|
||||||
if (SymmetricKeyAlgorithm.fromId(seipd.getCipherAlgorithm()) == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (AEADAlgorithm.fromId(seipd.getAeadAlgorithm()) == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AEAD_ENC_DATA:
|
|
||||||
AEADEncDataPacket oed = (AEADEncDataPacket) packet;
|
|
||||||
if (SymmetricKeyAlgorithm.fromId(oed.getAlgorithm()) == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RESERVED: // this Packet Type ID MUST NOT be used
|
|
||||||
case PUBLIC_SUBKEY: // Never found at the start of a stream
|
|
||||||
case SECRET_SUBKEY: // Never found at the start of a stream
|
|
||||||
case TRUST: // Never found at the start of a stream
|
|
||||||
case MOD_DETECTION_CODE: // At the end of SED data - Never found at the start of a stream
|
|
||||||
case USER_ID: // Never found at the start of a stream
|
|
||||||
case USER_ATTRIBUTE: // Never found at the start of a stream
|
|
||||||
case PADDING: // At the end of messages (optionally padded message) or certificates
|
|
||||||
case EXPERIMENTAL_1: // experimental
|
|
||||||
case EXPERIMENTAL_2: // experimental
|
|
||||||
case EXPERIMENTAL_3: // experimental
|
|
||||||
case EXPERIMENTAL_4: // experimental
|
|
||||||
containsOpenPgpPackets = true;
|
|
||||||
isLikelyOpenPgpMessage = false;
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
containsOpenPgpPackets = true;
|
|
||||||
if (packet.getPacketTag() != SYMMETRIC_KEY_ENC) {
|
|
||||||
isLikelyOpenPgpMessage = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean startsWithIgnoringWhitespace(byte[] bytes, byte[] subsequence, int bufferLen) {
|
|
||||||
if (bufferLen == -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < bufferLen; i++) {
|
|
||||||
// Working on bytes is not trivial with unicode data, but its good enough here
|
|
||||||
if (Character.isWhitespace(bytes[i])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((i + subsequence.length) > bytes.length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int j = 0; j < subsequence.length; j++) {
|
|
||||||
if (bytes[i + j] != subsequence[j]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAsciiArmored() {
|
|
||||||
return containsArmorHeader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true, if the data is possibly binary OpenPGP.
|
|
||||||
* The criterion for this are less strict than for {@link #isLikelyOpenPgpMessage()},
|
|
||||||
* as it also accepts other OpenPGP packets at the beginning of the data stream.
|
|
||||||
* <p>
|
|
||||||
* Use with caution.
|
|
||||||
*
|
|
||||||
* @return true if data appears to be binary OpenPGP data
|
|
||||||
*/
|
|
||||||
public boolean isBinaryOpenPgp() {
|
|
||||||
return containsOpenPgpPackets;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true, if the underlying data is very likely (more than 99,9%) an OpenPGP message.
|
|
||||||
* OpenPGP Message means here that it starts with either an {@link PGPEncryptedData},
|
|
||||||
* {@link PGPCompressedData}, {@link PGPOnePassSignature} or {@link PGPLiteralData} packet.
|
|
||||||
* The plausibility of these data packets is checked as far as possible.
|
|
||||||
*
|
|
||||||
* @return true if likely OpenPGP message
|
|
||||||
*/
|
|
||||||
public boolean isLikelyOpenPgpMessage() {
|
|
||||||
return isLikelyOpenPgpMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isNonOpenPgp() {
|
|
||||||
return !isAsciiArmored() && !isBinaryOpenPgp();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2018 Paul Schaub <vanitasvitae@fsfe.org>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Classes used to decryption and verification of OpenPGP encrypted / signed data.
|
|
||||||
*/
|
|
||||||
package org.pgpainless.decryption_verification;
|
|
|
@ -0,0 +1,321 @@
|
||||||
|
// SPDX-FileCopyrightText: 2025 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package org.pgpainless.decryption_verification
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.io.InputStream
|
||||||
|
import org.bouncycastle.bcpg.AEADEncDataPacket
|
||||||
|
import org.bouncycastle.bcpg.BCPGInputStream
|
||||||
|
import org.bouncycastle.bcpg.CompressedDataPacket
|
||||||
|
import org.bouncycastle.bcpg.LiteralDataPacket
|
||||||
|
import org.bouncycastle.bcpg.MarkerPacket
|
||||||
|
import org.bouncycastle.bcpg.OnePassSignaturePacket
|
||||||
|
import org.bouncycastle.bcpg.PacketFormat
|
||||||
|
import org.bouncycastle.bcpg.PacketTags.AEAD_ENC_DATA
|
||||||
|
import org.bouncycastle.bcpg.PacketTags.COMPRESSED_DATA
|
||||||
|
import org.bouncycastle.bcpg.PacketTags.EXPERIMENTAL_1
|
||||||
|
import org.bouncycastle.bcpg.PacketTags.EXPERIMENTAL_2
|
||||||
|
import org.bouncycastle.bcpg.PacketTags.EXPERIMENTAL_3
|
||||||
|
import org.bouncycastle.bcpg.PacketTags.EXPERIMENTAL_4
|
||||||
|
import org.bouncycastle.bcpg.PacketTags.LITERAL_DATA
|
||||||
|
import org.bouncycastle.bcpg.PacketTags.MARKER
|
||||||
|
import org.bouncycastle.bcpg.PacketTags.MOD_DETECTION_CODE
|
||||||
|
import org.bouncycastle.bcpg.PacketTags.ONE_PASS_SIGNATURE
|
||||||
|
import org.bouncycastle.bcpg.PacketTags.PADDING
|
||||||
|
import org.bouncycastle.bcpg.PacketTags.PUBLIC_KEY
|
||||||
|
import org.bouncycastle.bcpg.PacketTags.PUBLIC_KEY_ENC_SESSION
|
||||||
|
import org.bouncycastle.bcpg.PacketTags.PUBLIC_SUBKEY
|
||||||
|
import org.bouncycastle.bcpg.PacketTags.RESERVED
|
||||||
|
import org.bouncycastle.bcpg.PacketTags.SECRET_KEY
|
||||||
|
import org.bouncycastle.bcpg.PacketTags.SECRET_SUBKEY
|
||||||
|
import org.bouncycastle.bcpg.PacketTags.SIGNATURE
|
||||||
|
import org.bouncycastle.bcpg.PacketTags.SYMMETRIC_KEY_ENC
|
||||||
|
import org.bouncycastle.bcpg.PacketTags.SYMMETRIC_KEY_ENC_SESSION
|
||||||
|
import org.bouncycastle.bcpg.PacketTags.SYM_ENC_INTEGRITY_PRO
|
||||||
|
import org.bouncycastle.bcpg.PacketTags.TRUST
|
||||||
|
import org.bouncycastle.bcpg.PacketTags.USER_ATTRIBUTE
|
||||||
|
import org.bouncycastle.bcpg.PacketTags.USER_ID
|
||||||
|
import org.bouncycastle.bcpg.PublicKeyEncSessionPacket
|
||||||
|
import org.bouncycastle.bcpg.PublicKeyPacket
|
||||||
|
import org.bouncycastle.bcpg.SecretKeyPacket
|
||||||
|
import org.bouncycastle.bcpg.SignaturePacket
|
||||||
|
import org.bouncycastle.bcpg.SymmetricEncIntegrityPacket
|
||||||
|
import org.bouncycastle.bcpg.SymmetricKeyEncSessionPacket
|
||||||
|
import org.bouncycastle.util.Arrays
|
||||||
|
import org.pgpainless.algorithm.AEADAlgorithm
|
||||||
|
import org.pgpainless.algorithm.CompressionAlgorithm
|
||||||
|
import org.pgpainless.algorithm.HashAlgorithm
|
||||||
|
import org.pgpainless.algorithm.PublicKeyAlgorithm
|
||||||
|
import org.pgpainless.algorithm.SignatureType
|
||||||
|
import org.pgpainless.algorithm.SymmetricKeyAlgorithm
|
||||||
|
|
||||||
|
/**
|
||||||
|
* InputStream used to determine the nature of potential OpenPGP data.
|
||||||
|
*
|
||||||
|
* @param input underlying input stream
|
||||||
|
* @param check whether to perform the costly checking inside the constructor
|
||||||
|
*/
|
||||||
|
class OpenPGPAnimalSnifferInputStream(input: InputStream, check: Boolean) :
|
||||||
|
BufferedInputStream(input) {
|
||||||
|
|
||||||
|
private val buffer: ByteArray
|
||||||
|
private val bufferLen: Int
|
||||||
|
|
||||||
|
private var containsArmorHeader: Boolean = false
|
||||||
|
private var containsOpenPgpPackets: Boolean = false
|
||||||
|
private var resemblesMessage: Boolean = false
|
||||||
|
|
||||||
|
init {
|
||||||
|
mark(MAX_BUFFER_SIZE)
|
||||||
|
buffer = ByteArray(MAX_BUFFER_SIZE)
|
||||||
|
bufferLen = read(buffer)
|
||||||
|
reset()
|
||||||
|
|
||||||
|
if (check) {
|
||||||
|
inspectBuffer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(input: InputStream) : this(input, true)
|
||||||
|
|
||||||
|
/** Return true, if the underlying data is ASCII armored. */
|
||||||
|
val isAsciiArmored: Boolean
|
||||||
|
get() = containsArmorHeader
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true, if the data is possibly binary OpenPGP. The criterion for this are less strict
|
||||||
|
* than for [resemblesMessage], as it also accepts other OpenPGP packets at the beginning of the
|
||||||
|
* data stream.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Use with caution.
|
||||||
|
*
|
||||||
|
* @return true if data appears to be binary OpenPGP data
|
||||||
|
*/
|
||||||
|
val isBinaryOpenPgp: Boolean
|
||||||
|
get() = containsOpenPgpPackets
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true, if the underlying data is very likely (more than 99,9%) an OpenPGP message.
|
||||||
|
* OpenPGP Message means here that it starts with either a [PGPEncryptedData],
|
||||||
|
* [PGPCompressedData], [PGPOnePassSignature] or [PGPLiteralData] packet. The plausibility of
|
||||||
|
* these data packets is checked as far as possible.
|
||||||
|
*
|
||||||
|
* @return true if likely OpenPGP message
|
||||||
|
*/
|
||||||
|
val isLikelyOpenPgpMessage: Boolean
|
||||||
|
get() = resemblesMessage
|
||||||
|
|
||||||
|
/** Return true, if the underlying data is non-OpenPGP data. */
|
||||||
|
val isNonOpenPgp: Boolean
|
||||||
|
get() = !isAsciiArmored && !isBinaryOpenPgp
|
||||||
|
|
||||||
|
/** Costly perform a plausibility check of the first encountered OpenPGP packet. */
|
||||||
|
fun inspectBuffer() {
|
||||||
|
if (checkForAsciiArmor()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
checkForBinaryOpenPgp()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkForAsciiArmor(): Boolean {
|
||||||
|
if (startsWithIgnoringWhitespace(buffer, ARMOR_HEADER, bufferLen)) {
|
||||||
|
containsArmorHeader = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is still brittle. Basically we try to parse OpenPGP packets from the buffer. If
|
||||||
|
* we run into exceptions, then we know that the data is non-OpenPGP'ish.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 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).
|
||||||
|
*/
|
||||||
|
private fun checkForBinaryOpenPgp() {
|
||||||
|
if (bufferLen == -1) {
|
||||||
|
// empty data
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val bufferIn = ByteArrayInputStream(buffer, 0, bufferLen)
|
||||||
|
val pIn = BCPGInputStream(bufferIn)
|
||||||
|
try {
|
||||||
|
nonExhaustiveParseAndCheckPlausibility(pIn)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun nonExhaustiveParseAndCheckPlausibility(packetIn: BCPGInputStream) {
|
||||||
|
val packet = packetIn.readPacket()
|
||||||
|
when (packet.packetTag) {
|
||||||
|
PUBLIC_KEY_ENC_SESSION -> {
|
||||||
|
packet as PublicKeyEncSessionPacket
|
||||||
|
if (PublicKeyAlgorithm.fromId(packet.algorithm) == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SIGNATURE -> {
|
||||||
|
packet as SignaturePacket
|
||||||
|
if (SignatureType.fromCode(packet.signatureType) == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (PublicKeyAlgorithm.fromId(packet.keyAlgorithm) == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (HashAlgorithm.fromId(packet.hashAlgorithm) == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ONE_PASS_SIGNATURE -> {
|
||||||
|
packet as OnePassSignaturePacket
|
||||||
|
if (SignatureType.fromCode(packet.signatureType) == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (PublicKeyAlgorithm.fromId(packet.keyAlgorithm) == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (HashAlgorithm.fromId(packet.hashAlgorithm) == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SYMMETRIC_KEY_ENC_SESSION -> {
|
||||||
|
packet as SymmetricKeyEncSessionPacket
|
||||||
|
if (SymmetricKeyAlgorithm.fromId(packet.encAlgorithm) == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SECRET_KEY -> {
|
||||||
|
packet as SecretKeyPacket
|
||||||
|
val publicKey = packet.publicKeyPacket
|
||||||
|
if (PublicKeyAlgorithm.fromId(publicKey.algorithm) == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (publicKey.version !in 3..6) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PUBLIC_KEY -> {
|
||||||
|
packet as PublicKeyPacket
|
||||||
|
if (PublicKeyAlgorithm.fromId(packet.algorithm) == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (packet.version !in 3..6) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
COMPRESSED_DATA -> {
|
||||||
|
packet as CompressedDataPacket
|
||||||
|
if (CompressionAlgorithm.fromId(packet.algorithm) == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SYMMETRIC_KEY_ENC -> {
|
||||||
|
// Not much we can check here
|
||||||
|
}
|
||||||
|
MARKER -> {
|
||||||
|
packet as MarkerPacket
|
||||||
|
if (!Arrays.areEqual(
|
||||||
|
packet.getEncoded(PacketFormat.CURRENT),
|
||||||
|
byteArrayOf(0xca.toByte(), 0x03, 0x50, 0x47, 0x50),
|
||||||
|
)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LITERAL_DATA -> {
|
||||||
|
packet as LiteralDataPacket
|
||||||
|
if (packet.format.toChar() !in charArrayOf('b', 'u', 't', 'l', '1', 'm')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SYM_ENC_INTEGRITY_PRO -> {
|
||||||
|
packet as SymmetricEncIntegrityPacket
|
||||||
|
if (packet.version !in
|
||||||
|
intArrayOf(
|
||||||
|
SymmetricEncIntegrityPacket.VERSION_1,
|
||||||
|
SymmetricEncIntegrityPacket.VERSION_2)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet.version == SymmetricEncIntegrityPacket.VERSION_2) {
|
||||||
|
if (SymmetricKeyAlgorithm.fromId(packet.cipherAlgorithm) == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (AEADAlgorithm.fromId(packet.aeadAlgorithm) == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AEAD_ENC_DATA -> {
|
||||||
|
packet as AEADEncDataPacket
|
||||||
|
if (SymmetricKeyAlgorithm.fromId(packet.algorithm.toInt()) == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RESERVED, // this Packet Type ID MUST NOT be used
|
||||||
|
PUBLIC_SUBKEY, // Never found at the start of a stream
|
||||||
|
SECRET_SUBKEY, // Never found at the start of a stream
|
||||||
|
TRUST, // Never found at the start of a stream
|
||||||
|
MOD_DETECTION_CODE, // At the end of SED data - Never found at the start of a stream
|
||||||
|
USER_ID, // Never found at the start of a stream
|
||||||
|
USER_ATTRIBUTE, // Never found at the start of a stream
|
||||||
|
PADDING, // At the end of messages (optionally padded message) or certificates
|
||||||
|
EXPERIMENTAL_1, // experimental
|
||||||
|
EXPERIMENTAL_2, // experimental
|
||||||
|
EXPERIMENTAL_3, // experimental
|
||||||
|
EXPERIMENTAL_4 -> { // experimental
|
||||||
|
containsOpenPgpPackets = true
|
||||||
|
resemblesMessage = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else -> return
|
||||||
|
}
|
||||||
|
|
||||||
|
containsOpenPgpPackets = true
|
||||||
|
if (packet.packetTag != SYMMETRIC_KEY_ENC) {
|
||||||
|
resemblesMessage = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startsWithIgnoringWhitespace(
|
||||||
|
bytes: ByteArray,
|
||||||
|
subSequence: CharSequence,
|
||||||
|
bufferLen: Int
|
||||||
|
): Boolean {
|
||||||
|
if (bufferLen == -1) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i in 0 until bufferLen) {
|
||||||
|
// Working on bytes is not trivial with unicode data, but its good enough here
|
||||||
|
if (Character.isWhitespace(bytes[i].toInt())) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((i + subSequence.length) > bytes.size) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j in subSequence.indices) {
|
||||||
|
if (bytes[i + j].toInt().toChar() != subSequence[j]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val ARMOR_HEADER = "-----BEGIN PGP "
|
||||||
|
const val MAX_BUFFER_SIZE = 8192 * 2
|
||||||
|
}
|
||||||
|
}
|
|
@ -1114,7 +1114,7 @@ class OpenPgpMessageInputStream(
|
||||||
metadata: Layer,
|
metadata: Layer,
|
||||||
api: PGPainless
|
api: PGPainless
|
||||||
): OpenPgpMessageInputStream {
|
): OpenPgpMessageInputStream {
|
||||||
val openPgpIn = OpenPgpInputStream(inputStream)
|
val openPgpIn = OpenPGPAnimalSnifferInputStream(inputStream)
|
||||||
openPgpIn.reset()
|
openPgpIn.reset()
|
||||||
|
|
||||||
if (openPgpIn.isNonOpenPgp || options.isForceNonOpenPgpData()) {
|
if (openPgpIn.isNonOpenPgp || options.isForceNonOpenPgpData()) {
|
||||||
|
|
|
@ -21,7 +21,7 @@ import org.bouncycastle.openpgp.PGPSignature
|
||||||
import org.bouncycastle.openpgp.PGPUtil
|
import org.bouncycastle.openpgp.PGPUtil
|
||||||
import org.bouncycastle.util.io.Streams
|
import org.bouncycastle.util.io.Streams
|
||||||
import org.pgpainless.algorithm.HashAlgorithm
|
import org.pgpainless.algorithm.HashAlgorithm
|
||||||
import org.pgpainless.decryption_verification.OpenPgpInputStream
|
import org.pgpainless.decryption_verification.OpenPGPAnimalSnifferInputStream
|
||||||
import org.pgpainless.key.OpenPgpFingerprint
|
import org.pgpainless.key.OpenPgpFingerprint
|
||||||
import org.pgpainless.key.util.KeyRingUtils
|
import org.pgpainless.key.util.KeyRingUtils
|
||||||
|
|
||||||
|
@ -422,7 +422,7 @@ class ArmorUtils {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun getDecoderStream(inputStream: InputStream): InputStream =
|
fun getDecoderStream(inputStream: InputStream): InputStream =
|
||||||
OpenPgpInputStream(inputStream).let {
|
OpenPGPAnimalSnifferInputStream(inputStream).let {
|
||||||
if (it.isAsciiArmored) {
|
if (it.isAsciiArmored) {
|
||||||
PGPUtil.getDecoderStream(ArmoredInputStreamFactory.get(it))
|
PGPUtil.getDecoderStream(ArmoredInputStreamFactory.get(it))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -30,7 +30,7 @@ import org.pgpainless.encryption_signing.ProducerOptions;
|
||||||
import org.pgpainless.encryption_signing.SigningOptions;
|
import org.pgpainless.encryption_signing.SigningOptions;
|
||||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||||
|
|
||||||
public class OpenPgpInputStreamTest {
|
public class OpenPGPAnimalSnifferInputStreamTest {
|
||||||
|
|
||||||
private static final Random RANDOM = new Random();
|
private static final Random RANDOM = new Random();
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ public class OpenPgpInputStreamTest {
|
||||||
RANDOM.nextBytes(randomBytes);
|
RANDOM.nextBytes(randomBytes);
|
||||||
ByteArrayInputStream randomIn = new ByteArrayInputStream(randomBytes);
|
ByteArrayInputStream randomIn = new ByteArrayInputStream(randomBytes);
|
||||||
|
|
||||||
OpenPgpInputStream openPgpInputStream = new OpenPgpInputStream(randomIn);
|
OpenPGPAnimalSnifferInputStream openPgpInputStream = new OpenPGPAnimalSnifferInputStream(randomIn);
|
||||||
assertFalse(openPgpInputStream.isAsciiArmored());
|
assertFalse(openPgpInputStream.isAsciiArmored());
|
||||||
assertFalse(openPgpInputStream.isLikelyOpenPgpMessage(),
|
assertFalse(openPgpInputStream.isLikelyOpenPgpMessage(),
|
||||||
Hex.toHexString(randomBytes, 0, 150));
|
Hex.toHexString(randomBytes, 0, 150));
|
||||||
|
@ -56,7 +56,7 @@ public class OpenPgpInputStreamTest {
|
||||||
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
|
||||||
byte[] randomBytes = new byte[OpenPgpInputStream.MAX_BUFFER_SIZE * 10];
|
byte[] randomBytes = new byte[OpenPGPAnimalSnifferInputStream.MAX_BUFFER_SIZE * 10];
|
||||||
RANDOM.nextBytes(randomBytes);
|
RANDOM.nextBytes(randomBytes);
|
||||||
|
|
||||||
ByteArrayOutputStream compressedDataPacket = new ByteArrayOutputStream();
|
ByteArrayOutputStream compressedDataPacket = new ByteArrayOutputStream();
|
||||||
|
@ -65,7 +65,7 @@ public class OpenPgpInputStreamTest {
|
||||||
compressor.write(randomBytes);
|
compressor.write(randomBytes);
|
||||||
compressor.close();
|
compressor.close();
|
||||||
|
|
||||||
OpenPgpInputStream inputStream = new OpenPgpInputStream(new ByteArrayInputStream(compressedDataPacket.toByteArray()));
|
OpenPGPAnimalSnifferInputStream inputStream = new OpenPGPAnimalSnifferInputStream(new ByteArrayInputStream(compressedDataPacket.toByteArray()));
|
||||||
assertFalse(inputStream.isAsciiArmored());
|
assertFalse(inputStream.isAsciiArmored());
|
||||||
assertFalse(inputStream.isNonOpenPgp());
|
assertFalse(inputStream.isNonOpenPgp());
|
||||||
assertTrue(inputStream.isBinaryOpenPgp());
|
assertTrue(inputStream.isBinaryOpenPgp());
|
||||||
|
@ -90,7 +90,7 @@ public class OpenPgpInputStreamTest {
|
||||||
"-----END PGP MESSAGE-----";
|
"-----END PGP MESSAGE-----";
|
||||||
|
|
||||||
ByteArrayInputStream asciiIn = new ByteArrayInputStream(asciiArmoredMessage.getBytes(StandardCharsets.UTF_8));
|
ByteArrayInputStream asciiIn = new ByteArrayInputStream(asciiArmoredMessage.getBytes(StandardCharsets.UTF_8));
|
||||||
OpenPgpInputStream openPgpInputStream = new OpenPgpInputStream(asciiIn);
|
OpenPGPAnimalSnifferInputStream openPgpInputStream = new OpenPGPAnimalSnifferInputStream(asciiIn);
|
||||||
|
|
||||||
assertTrue(openPgpInputStream.isAsciiArmored());
|
assertTrue(openPgpInputStream.isAsciiArmored());
|
||||||
assertFalse(openPgpInputStream.isNonOpenPgp());
|
assertFalse(openPgpInputStream.isNonOpenPgp());
|
||||||
|
@ -663,9 +663,9 @@ public class OpenPgpInputStreamTest {
|
||||||
@Test
|
@Test
|
||||||
public void longAsciiArmoredMessageIsAsciiArmored() throws IOException {
|
public void longAsciiArmoredMessageIsAsciiArmored() throws IOException {
|
||||||
byte[] asciiArmoredBytes = longAsciiArmoredMessage.getBytes(StandardCharsets.UTF_8);
|
byte[] asciiArmoredBytes = longAsciiArmoredMessage.getBytes(StandardCharsets.UTF_8);
|
||||||
assertTrue(asciiArmoredBytes.length > OpenPgpInputStream.MAX_BUFFER_SIZE);
|
assertTrue(asciiArmoredBytes.length > OpenPGPAnimalSnifferInputStream.MAX_BUFFER_SIZE);
|
||||||
ByteArrayInputStream asciiIn = new ByteArrayInputStream(asciiArmoredBytes);
|
ByteArrayInputStream asciiIn = new ByteArrayInputStream(asciiArmoredBytes);
|
||||||
OpenPgpInputStream openPgpInputStream = new OpenPgpInputStream(asciiIn);
|
OpenPGPAnimalSnifferInputStream openPgpInputStream = new OpenPGPAnimalSnifferInputStream(asciiIn);
|
||||||
|
|
||||||
assertTrue(openPgpInputStream.isAsciiArmored());
|
assertTrue(openPgpInputStream.isAsciiArmored());
|
||||||
assertFalse(openPgpInputStream.isNonOpenPgp());
|
assertFalse(openPgpInputStream.isNonOpenPgp());
|
||||||
|
@ -694,7 +694,7 @@ public class OpenPgpInputStreamTest {
|
||||||
|
|
||||||
byte[] binaryBytes = binaryOut.toByteArray();
|
byte[] binaryBytes = binaryOut.toByteArray();
|
||||||
ByteArrayInputStream binaryIn = new ByteArrayInputStream(binaryBytes);
|
ByteArrayInputStream binaryIn = new ByteArrayInputStream(binaryBytes);
|
||||||
OpenPgpInputStream openPgpInputStream = new OpenPgpInputStream(binaryIn);
|
OpenPGPAnimalSnifferInputStream openPgpInputStream = new OpenPGPAnimalSnifferInputStream(binaryIn);
|
||||||
|
|
||||||
assertTrue(openPgpInputStream.isBinaryOpenPgp());
|
assertTrue(openPgpInputStream.isBinaryOpenPgp());
|
||||||
assertFalse(openPgpInputStream.isAsciiArmored());
|
assertFalse(openPgpInputStream.isAsciiArmored());
|
||||||
|
@ -714,7 +714,7 @@ public class OpenPgpInputStreamTest {
|
||||||
|
|
||||||
byte[] binaryBytes = binaryOut.toByteArray();
|
byte[] binaryBytes = binaryOut.toByteArray();
|
||||||
ByteArrayInputStream binaryIn = new ByteArrayInputStream(binaryBytes);
|
ByteArrayInputStream binaryIn = new ByteArrayInputStream(binaryBytes);
|
||||||
OpenPgpInputStream openPgpInputStream = new OpenPgpInputStream(binaryIn);
|
OpenPGPAnimalSnifferInputStream openPgpInputStream = new OpenPGPAnimalSnifferInputStream(binaryIn);
|
||||||
|
|
||||||
assertTrue(openPgpInputStream.isBinaryOpenPgp());
|
assertTrue(openPgpInputStream.isBinaryOpenPgp());
|
||||||
assertFalse(openPgpInputStream.isAsciiArmored());
|
assertFalse(openPgpInputStream.isAsciiArmored());
|
||||||
|
@ -728,7 +728,7 @@ public class OpenPgpInputStreamTest {
|
||||||
@Test
|
@Test
|
||||||
public void emptyStreamTest() throws IOException {
|
public void emptyStreamTest() throws IOException {
|
||||||
ByteArrayInputStream in = new ByteArrayInputStream(new byte[0]);
|
ByteArrayInputStream in = new ByteArrayInputStream(new byte[0]);
|
||||||
OpenPgpInputStream openPgpInputStream = new OpenPgpInputStream(in);
|
OpenPGPAnimalSnifferInputStream openPgpInputStream = new OpenPGPAnimalSnifferInputStream(in);
|
||||||
|
|
||||||
assertFalse(openPgpInputStream.isBinaryOpenPgp());
|
assertFalse(openPgpInputStream.isBinaryOpenPgp());
|
||||||
assertFalse(openPgpInputStream.isAsciiArmored());
|
assertFalse(openPgpInputStream.isAsciiArmored());
|
||||||
|
@ -755,7 +755,7 @@ public class OpenPgpInputStreamTest {
|
||||||
|
|
||||||
byte[] binary = signedOut.toByteArray();
|
byte[] binary = signedOut.toByteArray();
|
||||||
|
|
||||||
OpenPgpInputStream openPgpIn = new OpenPgpInputStream(new ByteArrayInputStream(binary));
|
OpenPGPAnimalSnifferInputStream openPgpIn = new OpenPGPAnimalSnifferInputStream(new ByteArrayInputStream(binary));
|
||||||
assertFalse(openPgpIn.isAsciiArmored());
|
assertFalse(openPgpIn.isAsciiArmored());
|
||||||
assertTrue(openPgpIn.isLikelyOpenPgpMessage());
|
assertTrue(openPgpIn.isLikelyOpenPgpMessage());
|
||||||
}
|
}
|
|
@ -10,7 +10,7 @@ import java.io.OutputStream
|
||||||
import kotlin.jvm.Throws
|
import kotlin.jvm.Throws
|
||||||
import org.bouncycastle.util.io.Streams
|
import org.bouncycastle.util.io.Streams
|
||||||
import org.pgpainless.PGPainless
|
import org.pgpainless.PGPainless
|
||||||
import org.pgpainless.decryption_verification.OpenPgpInputStream
|
import org.pgpainless.decryption_verification.OpenPGPAnimalSnifferInputStream
|
||||||
import org.pgpainless.util.ArmoredOutputStreamFactory
|
import org.pgpainless.util.ArmoredOutputStreamFactory
|
||||||
import sop.Ready
|
import sop.Ready
|
||||||
import sop.exception.SOPGPException
|
import sop.exception.SOPGPException
|
||||||
|
@ -27,7 +27,7 @@ class ArmorImpl(private val api: PGPainless) : Armor {
|
||||||
val bufferedOutputStream = BufferedOutputStream(outputStream)
|
val bufferedOutputStream = BufferedOutputStream(outputStream)
|
||||||
|
|
||||||
// Determine the nature of the given data
|
// Determine the nature of the given data
|
||||||
val openPgpIn = OpenPgpInputStream(data)
|
val openPgpIn = OpenPGPAnimalSnifferInputStream(data)
|
||||||
openPgpIn.reset()
|
openPgpIn.reset()
|
||||||
|
|
||||||
if (openPgpIn.isAsciiArmored) {
|
if (openPgpIn.isAsciiArmored) {
|
||||||
|
|
|
@ -15,7 +15,7 @@ import org.bouncycastle.openpgp.PGPOnePassSignatureList
|
||||||
import org.bouncycastle.openpgp.PGPSignatureList
|
import org.bouncycastle.openpgp.PGPSignatureList
|
||||||
import org.bouncycastle.util.io.Streams
|
import org.bouncycastle.util.io.Streams
|
||||||
import org.pgpainless.PGPainless
|
import org.pgpainless.PGPainless
|
||||||
import org.pgpainless.decryption_verification.OpenPgpInputStream
|
import org.pgpainless.decryption_verification.OpenPGPAnimalSnifferInputStream
|
||||||
import org.pgpainless.decryption_verification.cleartext_signatures.ClearsignedMessageUtil
|
import org.pgpainless.decryption_verification.cleartext_signatures.ClearsignedMessageUtil
|
||||||
import org.pgpainless.exception.WrongConsumingMethodException
|
import org.pgpainless.exception.WrongConsumingMethodException
|
||||||
import org.pgpainless.util.ArmoredOutputStreamFactory
|
import org.pgpainless.util.ArmoredOutputStreamFactory
|
||||||
|
@ -35,7 +35,7 @@ class InlineDetachImpl(private val api: PGPainless) : InlineDetach {
|
||||||
private val sigOut = ByteArrayOutputStream()
|
private val sigOut = ByteArrayOutputStream()
|
||||||
|
|
||||||
override fun writeTo(outputStream: OutputStream): Signatures {
|
override fun writeTo(outputStream: OutputStream): Signatures {
|
||||||
var pgpIn = OpenPgpInputStream(messageInputStream)
|
var pgpIn = OpenPGPAnimalSnifferInputStream(messageInputStream)
|
||||||
if (pgpIn.isNonOpenPgp) {
|
if (pgpIn.isNonOpenPgp) {
|
||||||
throw SOPGPException.BadData("Data appears to be non-OpenPGP.")
|
throw SOPGPException.BadData("Data appears to be non-OpenPGP.")
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ class InlineDetachImpl(private val api: PGPainless) : InlineDetach {
|
||||||
}
|
}
|
||||||
|
|
||||||
// else just dearmor
|
// else just dearmor
|
||||||
pgpIn = OpenPgpInputStream(armorIn)
|
pgpIn = OpenPGPAnimalSnifferInputStream(armorIn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If data was not using cleartext signature framework
|
// If data was not using cleartext signature framework
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue