2022-09-19 13:07:33 +02:00
|
|
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
2022-09-13 19:23:59 +02:00
|
|
|
package org.pgpainless.decryption_verification;
|
|
|
|
|
2022-09-29 17:45:32 +02:00
|
|
|
import java.io.ByteArrayOutputStream;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InputStream;
|
|
|
|
import java.io.OutputStream;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Collection;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Stack;
|
|
|
|
|
2022-09-13 19:23:59 +02:00
|
|
|
import org.bouncycastle.bcpg.BCPGInputStream;
|
|
|
|
import org.bouncycastle.openpgp.PGPCompressedData;
|
|
|
|
import org.bouncycastle.openpgp.PGPEncryptedData;
|
|
|
|
import org.bouncycastle.openpgp.PGPEncryptedDataList;
|
|
|
|
import org.bouncycastle.openpgp.PGPException;
|
|
|
|
import org.bouncycastle.openpgp.PGPLiteralData;
|
|
|
|
import org.bouncycastle.openpgp.PGPOnePassSignature;
|
|
|
|
import org.bouncycastle.openpgp.PGPPBEEncryptedData;
|
|
|
|
import org.bouncycastle.openpgp.PGPPrivateKey;
|
|
|
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
|
|
|
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
|
2022-09-16 00:51:49 +02:00
|
|
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
2022-09-13 19:23:59 +02:00
|
|
|
import org.bouncycastle.openpgp.PGPSecretKey;
|
|
|
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
|
|
|
import org.bouncycastle.openpgp.PGPSignature;
|
2022-09-29 17:45:32 +02:00
|
|
|
import org.bouncycastle.openpgp.PGPUtil;
|
2022-09-13 19:23:59 +02:00
|
|
|
import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory;
|
2022-09-16 00:51:49 +02:00
|
|
|
import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider;
|
2022-09-13 19:23:59 +02:00
|
|
|
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
|
2022-09-13 20:22:31 +02:00
|
|
|
import org.bouncycastle.openpgp.operator.SessionKeyDataDecryptorFactory;
|
2022-09-29 17:45:32 +02:00
|
|
|
import org.bouncycastle.util.encoders.Hex;
|
2022-09-13 19:23:59 +02:00
|
|
|
import org.pgpainless.PGPainless;
|
2022-09-19 13:07:33 +02:00
|
|
|
import org.pgpainless.algorithm.CompressionAlgorithm;
|
2022-09-13 19:23:59 +02:00
|
|
|
import org.pgpainless.algorithm.EncryptionPurpose;
|
|
|
|
import org.pgpainless.algorithm.OpenPgpPacket;
|
2022-09-26 18:21:06 +02:00
|
|
|
import org.pgpainless.algorithm.StreamEncoding;
|
2022-09-19 13:07:33 +02:00
|
|
|
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
2022-09-13 19:23:59 +02:00
|
|
|
import org.pgpainless.decryption_verification.automaton.InputAlphabet;
|
|
|
|
import org.pgpainless.decryption_verification.automaton.PDA;
|
2022-09-14 19:29:47 +02:00
|
|
|
import org.pgpainless.decryption_verification.automaton.StackAlphabet;
|
|
|
|
import org.pgpainless.exception.MalformedOpenPgpMessageException;
|
2022-09-13 19:23:59 +02:00
|
|
|
import org.pgpainless.exception.MessageNotIntegrityProtectedException;
|
2022-09-13 20:22:31 +02:00
|
|
|
import org.pgpainless.exception.MissingDecryptionMethodException;
|
2022-09-13 19:23:59 +02:00
|
|
|
import org.pgpainless.implementation.ImplementationFactory;
|
|
|
|
import org.pgpainless.key.info.KeyRingInfo;
|
|
|
|
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
|
|
|
import org.pgpainless.key.protection.UnlockSecretKey;
|
2022-09-16 00:51:49 +02:00
|
|
|
import org.pgpainless.signature.SignatureUtils;
|
2022-09-13 19:23:59 +02:00
|
|
|
import org.pgpainless.util.Passphrase;
|
|
|
|
import org.pgpainless.util.Tuple;
|
2022-09-27 16:11:55 +02:00
|
|
|
import org.slf4j.Logger;
|
|
|
|
import org.slf4j.LoggerFactory;
|
2022-09-13 19:23:59 +02:00
|
|
|
|
2022-10-08 13:57:53 +02:00
|
|
|
import javax.annotation.Nonnull;
|
|
|
|
|
2022-09-13 19:23:59 +02:00
|
|
|
public class OpenPgpMessageInputStream extends InputStream {
|
|
|
|
|
2022-09-27 16:11:55 +02:00
|
|
|
private static final Logger LOGGER = LoggerFactory.getLogger(OpenPgpMessageInputStream.class);
|
|
|
|
|
2022-09-28 16:55:08 +02:00
|
|
|
// Options to consume the data
|
2022-09-13 19:23:59 +02:00
|
|
|
protected final ConsumerOptions options;
|
2022-09-19 13:07:33 +02:00
|
|
|
protected final OpenPgpMetadata.Builder resultBuilder;
|
2022-09-28 16:55:08 +02:00
|
|
|
// Pushdown Automaton to verify validity of OpenPGP packet sequence in an OpenPGP message
|
|
|
|
protected final PDA automaton = new PDA();
|
2022-10-08 13:57:53 +02:00
|
|
|
// InputStream of OpenPGP packets
|
|
|
|
protected TeeBCPGInputStream packetInputStream;
|
2022-09-28 16:55:08 +02:00
|
|
|
// InputStream of a nested data packet
|
|
|
|
protected InputStream nestedInputStream;
|
2022-09-13 19:23:59 +02:00
|
|
|
|
2022-09-14 19:29:47 +02:00
|
|
|
private boolean closed = false;
|
|
|
|
|
2022-09-28 16:55:08 +02:00
|
|
|
private final Signatures signatures;
|
2022-10-08 13:57:53 +02:00
|
|
|
private final MessageMetadata.Layer metadata;
|
2022-09-13 19:23:59 +02:00
|
|
|
|
|
|
|
public OpenPgpMessageInputStream(InputStream inputStream, ConsumerOptions options)
|
|
|
|
throws IOException, PGPException {
|
2022-09-26 18:21:06 +02:00
|
|
|
this(inputStream, options, new MessageMetadata.Message());
|
2022-09-19 13:07:33 +02:00
|
|
|
}
|
|
|
|
|
2022-09-26 18:21:06 +02:00
|
|
|
OpenPgpMessageInputStream(InputStream inputStream, ConsumerOptions options, MessageMetadata.Layer metadata)
|
2022-09-19 13:07:33 +02:00
|
|
|
throws PGPException, IOException {
|
2022-09-13 19:23:59 +02:00
|
|
|
|
2022-09-14 19:29:47 +02:00
|
|
|
this.options = options;
|
2022-09-26 18:21:06 +02:00
|
|
|
this.metadata = metadata;
|
2022-09-19 13:07:33 +02:00
|
|
|
this.resultBuilder = OpenPgpMetadata.getBuilder();
|
2022-09-16 00:51:49 +02:00
|
|
|
this.signatures = new Signatures(options);
|
2022-09-13 19:23:59 +02:00
|
|
|
|
2022-09-28 16:55:08 +02:00
|
|
|
// Add detached signatures only on the outermost OpenPgpMessageInputStream
|
|
|
|
if (metadata instanceof MessageMetadata.Message) {
|
|
|
|
this.signatures.addDetachedSignatures(options.getDetachedSignatures());
|
|
|
|
}
|
|
|
|
|
2022-10-08 13:57:53 +02:00
|
|
|
packetInputStream = new TeeBCPGInputStream(BCPGInputStream.wrap(inputStream), signatures);
|
2022-09-28 16:55:08 +02:00
|
|
|
|
|
|
|
// *omnomnom*
|
|
|
|
consumePackets();
|
2022-09-19 13:07:33 +02:00
|
|
|
}
|
|
|
|
|
2022-09-14 19:29:47 +02:00
|
|
|
/**
|
2022-09-28 16:55:08 +02:00
|
|
|
* Consume OpenPGP packets from the current {@link BCPGInputStream}.
|
|
|
|
* Once an OpenPGP packet with nested data (Literal Data, Compressed Data, Encrypted Data) is reached,
|
|
|
|
* set <pre>nestedInputStream</pre> to the nested stream and breaks the loop.
|
2022-09-14 19:29:47 +02:00
|
|
|
* The nested stream is either a simple {@link InputStream} (in case of Literal Data), or another
|
|
|
|
* {@link OpenPgpMessageInputStream} in case of Compressed and Encrypted Data.
|
2022-09-28 16:55:08 +02:00
|
|
|
* Once the nested data is processed, this method is called again to consume the remainder
|
|
|
|
* of packets following the nested data packet.
|
2022-09-14 19:29:47 +02:00
|
|
|
*
|
2022-09-28 16:55:08 +02:00
|
|
|
* @throws IOException in case of an IO error
|
|
|
|
* @throws PGPException in case of an OpenPGP error
|
|
|
|
* @throws MissingDecryptionMethodException if there is an encrypted data packet which cannot be decrypted
|
|
|
|
* due to missing decryption methods (no key, no password, no sessionkey)
|
|
|
|
* @throws MalformedOpenPgpMessageException if the message is made of an invalid packet sequence which
|
|
|
|
* does not follow the packet syntax of RFC4880.
|
2022-09-14 19:29:47 +02:00
|
|
|
*/
|
|
|
|
private void consumePackets()
|
|
|
|
throws IOException, PGPException {
|
2022-10-06 21:52:23 +02:00
|
|
|
OpenPgpPacket nextPacket;
|
2022-10-08 13:57:53 +02:00
|
|
|
loop: while ((nextPacket = packetInputStream.nextPacketTag()) != null) {
|
2022-09-29 17:45:32 +02:00
|
|
|
signatures.nextPacket(nextPacket);
|
2022-09-13 19:23:59 +02:00
|
|
|
switch (nextPacket) {
|
2022-09-14 19:29:47 +02:00
|
|
|
|
|
|
|
// Literal Data - the literal data content is the new input stream
|
2022-09-13 19:23:59 +02:00
|
|
|
case LIT:
|
|
|
|
automaton.next(InputAlphabet.LiteralData);
|
2022-09-26 18:21:06 +02:00
|
|
|
processLiteralData();
|
2022-09-13 19:23:59 +02:00
|
|
|
break loop;
|
|
|
|
|
2022-09-14 19:29:47 +02:00
|
|
|
// Compressed Data - the content contains another OpenPGP message
|
2022-09-13 19:23:59 +02:00
|
|
|
case COMP:
|
|
|
|
automaton.next(InputAlphabet.CompressedData);
|
2022-09-26 18:21:06 +02:00
|
|
|
processCompressedData();
|
2022-09-13 19:23:59 +02:00
|
|
|
break loop;
|
|
|
|
|
2022-09-28 16:55:08 +02:00
|
|
|
// One Pass Signature
|
2022-09-13 19:23:59 +02:00
|
|
|
case OPS:
|
2022-10-08 13:57:53 +02:00
|
|
|
automaton.next(InputAlphabet.OnePassSignature);
|
2022-09-29 17:45:32 +02:00
|
|
|
PGPOnePassSignature onePassSignature = readOnePassSignature();
|
|
|
|
signatures.addOnePassSignature(onePassSignature);
|
2022-09-13 19:23:59 +02:00
|
|
|
break;
|
|
|
|
|
2022-09-28 16:55:08 +02:00
|
|
|
// Signature - either prepended to the message, or corresponding to a One Pass Signature
|
2022-09-13 19:23:59 +02:00
|
|
|
case SIG:
|
2022-10-08 13:57:53 +02:00
|
|
|
// true if Signature corresponds to OnePassSignature
|
2022-09-19 13:07:33 +02:00
|
|
|
boolean isSigForOPS = automaton.peekStack() == StackAlphabet.ops;
|
2022-10-08 13:57:53 +02:00
|
|
|
automaton.next(InputAlphabet.Signature);
|
2022-09-29 00:15:18 +02:00
|
|
|
|
2022-10-08 13:57:53 +02:00
|
|
|
processSignature(isSigForOPS);
|
2022-09-29 00:15:18 +02:00
|
|
|
|
2022-09-13 19:23:59 +02:00
|
|
|
break;
|
|
|
|
|
2022-09-14 19:29:47 +02:00
|
|
|
// Encrypted Data (ESKs and SED/SEIPD are parsed the same by BC)
|
2022-09-13 19:23:59 +02:00
|
|
|
case PKESK:
|
|
|
|
case SKESK:
|
|
|
|
case SED:
|
|
|
|
case SEIPD:
|
|
|
|
automaton.next(InputAlphabet.EncryptedData);
|
2022-09-19 13:07:33 +02:00
|
|
|
if (processEncryptedData()) {
|
|
|
|
break loop;
|
2022-09-13 19:23:59 +02:00
|
|
|
}
|
|
|
|
|
2022-09-13 20:22:31 +02:00
|
|
|
throw new MissingDecryptionMethodException("No working decryption method found.");
|
2022-09-13 19:23:59 +02:00
|
|
|
|
2022-09-29 00:15:18 +02:00
|
|
|
// Marker Packets need to be skipped and ignored
|
2022-09-13 19:23:59 +02:00
|
|
|
case MARKER:
|
2022-10-08 13:57:53 +02:00
|
|
|
packetInputStream.readMarker();
|
2022-09-13 19:23:59 +02:00
|
|
|
break;
|
|
|
|
|
2022-09-14 19:29:47 +02:00
|
|
|
// Key Packets are illegal in this context
|
2022-09-13 19:23:59 +02:00
|
|
|
case SK:
|
|
|
|
case PK:
|
|
|
|
case SSK:
|
|
|
|
case PSK:
|
|
|
|
case TRUST:
|
|
|
|
case UID:
|
|
|
|
case UATTR:
|
2022-09-19 13:07:33 +02:00
|
|
|
throw new MalformedOpenPgpMessageException("Illegal Packet in Stream: " + nextPacket);
|
2022-09-13 19:23:59 +02:00
|
|
|
|
2022-09-29 00:15:18 +02:00
|
|
|
// MDC packet is usually processed by PGPEncryptedDataList, so it is very likely we encounter this
|
|
|
|
// packet out of order
|
2022-09-19 13:07:33 +02:00
|
|
|
case MDC:
|
2022-09-14 19:29:47 +02:00
|
|
|
throw new MalformedOpenPgpMessageException("Unexpected Packet in Stream: " + nextPacket);
|
2022-09-13 19:23:59 +02:00
|
|
|
|
2022-09-14 20:10:42 +02:00
|
|
|
// Experimental Packets are not supported
|
2022-09-13 19:23:59 +02:00
|
|
|
case EXP_1:
|
|
|
|
case EXP_2:
|
|
|
|
case EXP_3:
|
|
|
|
case EXP_4:
|
2022-09-14 19:29:47 +02:00
|
|
|
throw new MalformedOpenPgpMessageException("Unsupported Packet in Stream: " + nextPacket);
|
2022-09-13 19:23:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-08 13:57:53 +02:00
|
|
|
private void processSignature(boolean isSigForOPS) throws PGPException, IOException {
|
|
|
|
PGPSignature signature = readSignature();
|
2022-09-28 16:55:08 +02:00
|
|
|
if (isSigForOPS) {
|
2022-10-08 13:57:53 +02:00
|
|
|
signatures.leaveNesting(); // TODO: Only leave nesting if all OPSs are dealt with
|
2022-09-29 00:15:18 +02:00
|
|
|
signatures.addCorrespondingOnePassSignature(signature);
|
2022-09-28 16:55:08 +02:00
|
|
|
} else {
|
|
|
|
signatures.addPrependedSignature(signature);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-26 18:21:06 +02:00
|
|
|
private void processCompressedData() throws IOException, PGPException {
|
2022-10-08 13:57:53 +02:00
|
|
|
signatures.enterNesting();
|
|
|
|
PGPCompressedData compressedData = packetInputStream.readCompressedData();
|
2022-09-26 18:21:06 +02:00
|
|
|
MessageMetadata.CompressedData compressionLayer = new MessageMetadata.CompressedData(
|
|
|
|
CompressionAlgorithm.fromId(compressedData.getAlgorithm()));
|
2022-09-28 16:55:08 +02:00
|
|
|
nestedInputStream = new OpenPgpMessageInputStream(compressedData.getDataStream(), options, compressionLayer);
|
2022-09-26 18:21:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private void processLiteralData() throws IOException {
|
2022-10-08 13:57:53 +02:00
|
|
|
PGPLiteralData literalData = packetInputStream.readLiteralData();
|
2022-09-26 18:21:06 +02:00
|
|
|
this.metadata.setChild(new MessageMetadata.LiteralData(literalData.getFileName(), literalData.getModificationTime(),
|
|
|
|
StreamEncoding.requireFromCode(literalData.getFormat())));
|
2022-09-28 16:55:08 +02:00
|
|
|
nestedInputStream = literalData.getDataStream();
|
2022-09-26 18:21:06 +02:00
|
|
|
}
|
|
|
|
|
2022-09-19 13:07:33 +02:00
|
|
|
private boolean processEncryptedData() throws IOException, PGPException {
|
2022-10-08 13:57:53 +02:00
|
|
|
PGPEncryptedDataList encDataList = packetInputStream.readEncryptedDataList();
|
2022-09-19 13:07:33 +02:00
|
|
|
|
|
|
|
// TODO: Replace with !encDataList.isIntegrityProtected()
|
|
|
|
if (!encDataList.get(0).isIntegrityProtected()) {
|
|
|
|
throw new MessageNotIntegrityProtectedException();
|
|
|
|
}
|
|
|
|
|
|
|
|
SortedESKs esks = new SortedESKs(encDataList);
|
|
|
|
|
|
|
|
// Try session key
|
|
|
|
if (options.getSessionKey() != null) {
|
|
|
|
SessionKeyDataDecryptorFactory decryptorFactory = ImplementationFactory.getInstance()
|
|
|
|
.getSessionKeyDataDecryptorFactory(options.getSessionKey());
|
|
|
|
// TODO: Replace with encDataList.addSessionKeyDecryptionMethod(sessionKey)
|
|
|
|
PGPEncryptedData esk = esks.all().get(0);
|
|
|
|
try {
|
2022-09-26 18:21:06 +02:00
|
|
|
MessageMetadata.EncryptedData encryptedData = new MessageMetadata.EncryptedData(options.getSessionKey().getAlgorithm());
|
2022-09-19 13:07:33 +02:00
|
|
|
if (esk instanceof PGPPBEEncryptedData) {
|
|
|
|
PGPPBEEncryptedData skesk = (PGPPBEEncryptedData) esk;
|
2022-09-28 16:55:08 +02:00
|
|
|
nestedInputStream = new OpenPgpMessageInputStream(skesk.getDataStream(decryptorFactory), options, encryptedData);
|
2022-09-19 13:07:33 +02:00
|
|
|
return true;
|
|
|
|
} else if (esk instanceof PGPPublicKeyEncryptedData) {
|
|
|
|
PGPPublicKeyEncryptedData pkesk = (PGPPublicKeyEncryptedData) esk;
|
2022-09-28 16:55:08 +02:00
|
|
|
nestedInputStream = new OpenPgpMessageInputStream(pkesk.getDataStream(decryptorFactory), options, encryptedData);
|
2022-09-19 13:07:33 +02:00
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
throw new RuntimeException("Unknown ESK class type: " + esk.getClass().getName());
|
|
|
|
}
|
|
|
|
} catch (PGPException e) {
|
|
|
|
// Session key mismatch?
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try passwords
|
|
|
|
for (PGPPBEEncryptedData skesk : esks.skesks) {
|
|
|
|
for (Passphrase passphrase : options.getDecryptionPassphrases()) {
|
|
|
|
PBEDataDecryptorFactory decryptorFactory = ImplementationFactory.getInstance()
|
|
|
|
.getPBEDataDecryptorFactory(passphrase);
|
|
|
|
try {
|
|
|
|
InputStream decrypted = skesk.getDataStream(decryptorFactory);
|
2022-09-26 18:21:06 +02:00
|
|
|
MessageMetadata.EncryptedData encryptedData = new MessageMetadata.EncryptedData(
|
|
|
|
SymmetricKeyAlgorithm.requireFromId(skesk.getSymmetricAlgorithm(decryptorFactory)));
|
2022-09-28 16:55:08 +02:00
|
|
|
nestedInputStream = new OpenPgpMessageInputStream(decrypted, options, encryptedData);
|
2022-09-19 13:07:33 +02:00
|
|
|
return true;
|
|
|
|
} catch (PGPException e) {
|
|
|
|
// password mismatch? Try next password
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try (known) secret keys
|
|
|
|
for (PGPPublicKeyEncryptedData pkesk : esks.pkesks) {
|
|
|
|
long keyId = pkesk.getKeyID();
|
|
|
|
PGPSecretKeyRing decryptionKeys = getDecryptionKey(keyId);
|
|
|
|
if (decryptionKeys == null) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
SecretKeyRingProtector protector = options.getSecretKeyProtector(decryptionKeys);
|
|
|
|
PGPSecretKey decryptionKey = decryptionKeys.getSecretKey(keyId);
|
|
|
|
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(decryptionKey, protector);
|
|
|
|
|
|
|
|
PublicKeyDataDecryptorFactory decryptorFactory = ImplementationFactory.getInstance()
|
|
|
|
.getPublicKeyDataDecryptorFactory(privateKey);
|
|
|
|
try {
|
|
|
|
InputStream decrypted = pkesk.getDataStream(decryptorFactory);
|
2022-09-26 18:21:06 +02:00
|
|
|
MessageMetadata.EncryptedData encryptedData = new MessageMetadata.EncryptedData(
|
|
|
|
SymmetricKeyAlgorithm.requireFromId(pkesk.getSymmetricAlgorithm(decryptorFactory)));
|
2022-09-29 17:45:32 +02:00
|
|
|
|
|
|
|
nestedInputStream = new OpenPgpMessageInputStream(PGPUtil.getDecoderStream(decrypted), options, encryptedData);
|
2022-09-19 13:07:33 +02:00
|
|
|
return true;
|
|
|
|
} catch (PGPException e) {
|
|
|
|
// hm :/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// try anonymous secret keys
|
|
|
|
for (PGPPublicKeyEncryptedData pkesk : esks.anonPkesks) {
|
|
|
|
for (Tuple<PGPSecretKeyRing, PGPSecretKey> decryptionKeyCandidate : findPotentialDecryptionKeys(pkesk)) {
|
|
|
|
SecretKeyRingProtector protector = options.getSecretKeyProtector(decryptionKeyCandidate.getA());
|
|
|
|
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(decryptionKeyCandidate.getB(), protector);
|
|
|
|
PublicKeyDataDecryptorFactory decryptorFactory = ImplementationFactory.getInstance()
|
|
|
|
.getPublicKeyDataDecryptorFactory(privateKey);
|
|
|
|
|
|
|
|
try {
|
|
|
|
InputStream decrypted = pkesk.getDataStream(decryptorFactory);
|
2022-09-26 18:21:06 +02:00
|
|
|
MessageMetadata.EncryptedData encryptedData = new MessageMetadata.EncryptedData(
|
|
|
|
SymmetricKeyAlgorithm.requireFromId(pkesk.getSymmetricAlgorithm(decryptorFactory)));
|
2022-09-28 16:55:08 +02:00
|
|
|
nestedInputStream = new OpenPgpMessageInputStream(decrypted, options, encryptedData);
|
2022-09-19 13:07:33 +02:00
|
|
|
return true;
|
|
|
|
} catch (PGPException e) {
|
|
|
|
// hm :/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// we did not yet succeed in decrypting any session key :/
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-09-13 19:23:59 +02:00
|
|
|
private List<Tuple<PGPSecretKeyRing, PGPSecretKey>> findPotentialDecryptionKeys(PGPPublicKeyEncryptedData pkesk) {
|
|
|
|
int algorithm = pkesk.getAlgorithm();
|
|
|
|
List<Tuple<PGPSecretKeyRing, PGPSecretKey>> decryptionKeyCandidates = new ArrayList<>();
|
|
|
|
|
|
|
|
for (PGPSecretKeyRing secretKeys : options.getDecryptionKeys()) {
|
|
|
|
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
|
|
|
|
for (PGPPublicKey publicKey : info.getEncryptionSubkeys(EncryptionPurpose.ANY)) {
|
|
|
|
if (publicKey.getAlgorithm() == algorithm && info.isSecretKeyAvailable(publicKey.getKeyID())) {
|
|
|
|
PGPSecretKey candidate = secretKeys.getSecretKey(publicKey.getKeyID());
|
|
|
|
decryptionKeyCandidates.add(new Tuple<>(secretKeys, candidate));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return decryptionKeyCandidates;
|
|
|
|
}
|
|
|
|
|
|
|
|
private PGPSecretKeyRing getDecryptionKey(long keyID) {
|
|
|
|
for (PGPSecretKeyRing secretKeys : options.getDecryptionKeys()) {
|
|
|
|
PGPSecretKey decryptionKey = secretKeys.getSecretKey(keyID);
|
|
|
|
if (decryptionKey == null) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
return secretKeys;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2022-09-28 16:55:08 +02:00
|
|
|
private PGPOnePassSignature readOnePassSignature()
|
|
|
|
throws PGPException, IOException {
|
2022-10-08 13:57:53 +02:00
|
|
|
return packetInputStream.readOnePassSignature();
|
2022-09-28 16:55:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private PGPSignature readSignature()
|
|
|
|
throws PGPException, IOException {
|
2022-10-08 13:57:53 +02:00
|
|
|
return packetInputStream.readSignature();
|
2022-09-13 19:23:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int read() throws IOException {
|
2022-09-28 16:55:08 +02:00
|
|
|
if (nestedInputStream == null) {
|
2022-09-14 20:10:42 +02:00
|
|
|
automaton.assertValid();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int r;
|
|
|
|
try {
|
2022-09-28 16:55:08 +02:00
|
|
|
r = nestedInputStream.read();
|
2022-09-14 20:10:42 +02:00
|
|
|
} catch (IOException e) {
|
|
|
|
r = -1;
|
2022-09-13 19:23:59 +02:00
|
|
|
}
|
2022-09-14 20:10:42 +02:00
|
|
|
boolean eos = r == -1;
|
|
|
|
if (!eos) {
|
2022-09-14 19:29:47 +02:00
|
|
|
byte b = (byte) r;
|
2022-09-29 17:45:32 +02:00
|
|
|
signatures.updateLiteral(b);
|
2022-09-14 19:29:47 +02:00
|
|
|
} else {
|
2022-09-28 16:55:08 +02:00
|
|
|
nestedInputStream.close();
|
2022-09-26 18:21:06 +02:00
|
|
|
collectMetadata();
|
2022-09-28 16:55:08 +02:00
|
|
|
nestedInputStream = null;
|
2022-09-16 00:51:49 +02:00
|
|
|
|
|
|
|
try {
|
|
|
|
consumePackets();
|
|
|
|
} catch (PGPException e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
|
|
|
signatures.finish();
|
2022-09-14 20:10:42 +02:00
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2022-10-08 13:57:53 +02:00
|
|
|
public int read(@Nonnull byte[] b, int off, int len)
|
2022-09-14 20:10:42 +02:00
|
|
|
throws IOException {
|
|
|
|
|
2022-09-28 16:55:08 +02:00
|
|
|
if (nestedInputStream == null) {
|
2022-09-14 20:10:42 +02:00
|
|
|
automaton.assertValid();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-09-28 16:55:08 +02:00
|
|
|
int r = nestedInputStream.read(b, off, len);
|
2022-09-29 17:45:32 +02:00
|
|
|
if (r != -1) {
|
|
|
|
signatures.updateLiteral(b, off, r);
|
|
|
|
}
|
|
|
|
else {
|
2022-09-28 16:55:08 +02:00
|
|
|
nestedInputStream.close();
|
2022-09-26 18:21:06 +02:00
|
|
|
collectMetadata();
|
2022-09-28 16:55:08 +02:00
|
|
|
nestedInputStream = null;
|
2022-09-16 00:51:49 +02:00
|
|
|
|
|
|
|
try {
|
|
|
|
consumePackets();
|
|
|
|
} catch (PGPException e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
|
|
|
signatures.finish();
|
2022-09-13 19:23:59 +02:00
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void close() throws IOException {
|
2022-09-14 19:29:47 +02:00
|
|
|
if (closed) {
|
2022-09-14 20:10:42 +02:00
|
|
|
automaton.assertValid();
|
2022-09-13 20:22:31 +02:00
|
|
|
return;
|
|
|
|
}
|
2022-09-14 19:29:47 +02:00
|
|
|
|
2022-09-28 16:55:08 +02:00
|
|
|
if (nestedInputStream != null) {
|
|
|
|
nestedInputStream.close();
|
2022-09-26 18:21:06 +02:00
|
|
|
collectMetadata();
|
2022-09-28 16:55:08 +02:00
|
|
|
nestedInputStream = null;
|
2022-09-13 19:23:59 +02:00
|
|
|
}
|
|
|
|
|
2022-09-16 00:51:49 +02:00
|
|
|
try {
|
|
|
|
consumePackets();
|
|
|
|
} catch (PGPException e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
|
|
|
|
2022-09-14 19:41:22 +02:00
|
|
|
automaton.next(InputAlphabet.EndOfSequence);
|
|
|
|
automaton.assertValid();
|
2022-09-14 19:29:47 +02:00
|
|
|
closed = true;
|
2022-09-13 19:23:59 +02:00
|
|
|
}
|
|
|
|
|
2022-09-26 18:21:06 +02:00
|
|
|
private void collectMetadata() {
|
2022-09-28 16:55:08 +02:00
|
|
|
if (nestedInputStream instanceof OpenPgpMessageInputStream) {
|
|
|
|
OpenPgpMessageInputStream child = (OpenPgpMessageInputStream) nestedInputStream;
|
2022-10-08 13:57:53 +02:00
|
|
|
this.metadata.setChild((MessageMetadata.Nested) child.metadata);
|
2022-09-26 18:21:06 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public MessageMetadata getMetadata() {
|
|
|
|
if (!closed) {
|
|
|
|
throw new IllegalStateException("Stream must be closed before access to metadata can be granted.");
|
|
|
|
}
|
|
|
|
return new MessageMetadata((MessageMetadata.Message) metadata);
|
|
|
|
}
|
|
|
|
|
2022-09-13 19:23:59 +02:00
|
|
|
private static class SortedESKs {
|
|
|
|
|
2022-10-08 13:57:53 +02:00
|
|
|
private final List<PGPPBEEncryptedData> skesks = new ArrayList<>();
|
|
|
|
private final List<PGPPublicKeyEncryptedData> pkesks = new ArrayList<>();
|
|
|
|
private final List<PGPPublicKeyEncryptedData> anonPkesks = new ArrayList<>();
|
2022-09-13 19:23:59 +02:00
|
|
|
|
|
|
|
SortedESKs(PGPEncryptedDataList esks) {
|
|
|
|
for (PGPEncryptedData esk : esks) {
|
|
|
|
if (esk instanceof PGPPBEEncryptedData) {
|
|
|
|
skesks.add((PGPPBEEncryptedData) esk);
|
|
|
|
} else if (esk instanceof PGPPublicKeyEncryptedData) {
|
|
|
|
PGPPublicKeyEncryptedData pkesk = (PGPPublicKeyEncryptedData) esk;
|
|
|
|
if (pkesk.getKeyID() != 0) {
|
|
|
|
pkesks.add(pkesk);
|
|
|
|
} else {
|
|
|
|
anonPkesks.add(pkesk);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
throw new IllegalArgumentException("Unknown ESK class type.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-09-13 20:22:31 +02:00
|
|
|
|
|
|
|
public List<PGPEncryptedData> all() {
|
|
|
|
List<PGPEncryptedData> esks = new ArrayList<>();
|
|
|
|
esks.addAll(skesks);
|
|
|
|
esks.addAll(pkesks);
|
|
|
|
esks.addAll(anonPkesks);
|
|
|
|
return esks;
|
|
|
|
}
|
2022-09-13 19:23:59 +02:00
|
|
|
}
|
2022-09-14 19:29:47 +02:00
|
|
|
|
2022-10-08 13:57:53 +02:00
|
|
|
// In 'OPS LIT("Foo") SIG', OPS is only updated with "Foo"
|
|
|
|
// In 'OPS[1] OPS LIT("Foo") SIG SIG', OPS[1] (nested) is updated with OPS LIT("Foo") SIG.
|
|
|
|
// Therefore, we need to handle the innermost signature layer differently when updating with Literal data.
|
|
|
|
// Furthermore, For 'OPS COMP(LIT("Foo")) SIG', the signature is updated with "Foo". CHAOS!!!
|
2022-09-28 16:55:08 +02:00
|
|
|
private static final class Signatures extends OutputStream {
|
2022-09-16 00:51:49 +02:00
|
|
|
final ConsumerOptions options;
|
2022-10-06 21:52:23 +02:00
|
|
|
final List<SIG> detachedSignatures;
|
|
|
|
final List<SIG> prependedSignatures;
|
2022-09-29 17:45:32 +02:00
|
|
|
final List<OPS> onePassSignatures;
|
|
|
|
final Stack<List<OPS>> opsUpdateStack;
|
|
|
|
List<OPS> literalOPS = new ArrayList<>();
|
2022-09-28 17:38:20 +02:00
|
|
|
final List<PGPSignature> correspondingSignatures;
|
2022-10-06 21:52:23 +02:00
|
|
|
boolean isLiteral = true;
|
2022-09-14 19:29:47 +02:00
|
|
|
|
2022-09-16 00:51:49 +02:00
|
|
|
private Signatures(ConsumerOptions options) {
|
|
|
|
this.options = options;
|
2022-09-28 17:38:20 +02:00
|
|
|
this.detachedSignatures = new ArrayList<>();
|
|
|
|
this.prependedSignatures = new ArrayList<>();
|
2022-09-29 00:15:18 +02:00
|
|
|
this.onePassSignatures = new ArrayList<>();
|
|
|
|
this.opsUpdateStack = new Stack<>();
|
2022-09-28 17:38:20 +02:00
|
|
|
this.correspondingSignatures = new ArrayList<>();
|
2022-09-16 00:51:49 +02:00
|
|
|
}
|
|
|
|
|
2022-09-14 19:29:47 +02:00
|
|
|
void addDetachedSignatures(Collection<PGPSignature> signatures) {
|
2022-09-16 00:51:49 +02:00
|
|
|
for (PGPSignature signature : signatures) {
|
2022-09-28 16:55:08 +02:00
|
|
|
addDetachedSignature(signature);
|
2022-09-16 00:51:49 +02:00
|
|
|
}
|
2022-09-28 16:55:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void addDetachedSignature(PGPSignature signature) {
|
|
|
|
long keyId = SignatureUtils.determineIssuerKeyId(signature);
|
|
|
|
PGPPublicKeyRing certificate = findCertificate(keyId);
|
|
|
|
initialize(signature, certificate, keyId);
|
2022-10-06 21:52:23 +02:00
|
|
|
this.detachedSignatures.add(new SIG(signature));
|
2022-09-14 19:29:47 +02:00
|
|
|
}
|
|
|
|
|
2022-09-28 16:55:08 +02:00
|
|
|
void addPrependedSignature(PGPSignature signature) {
|
|
|
|
long keyId = SignatureUtils.determineIssuerKeyId(signature);
|
|
|
|
PGPPublicKeyRing certificate = findCertificate(keyId);
|
|
|
|
initialize(signature, certificate, keyId);
|
2022-10-06 21:52:23 +02:00
|
|
|
this.prependedSignatures.add(new SIG(signature));
|
2022-09-28 16:55:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void addOnePassSignature(PGPOnePassSignature signature) {
|
|
|
|
PGPPublicKeyRing certificate = findCertificate(signature.getKeyID());
|
2022-09-29 17:45:32 +02:00
|
|
|
OPS ops = new OPS(signature);
|
|
|
|
ops.init(certificate);
|
|
|
|
onePassSignatures.add(ops);
|
2022-09-28 16:55:08 +02:00
|
|
|
|
2022-09-29 17:45:32 +02:00
|
|
|
literalOPS.add(ops);
|
|
|
|
if (signature.isContaining()) {
|
2022-10-08 13:57:53 +02:00
|
|
|
enterNesting();
|
2022-09-29 00:15:18 +02:00
|
|
|
}
|
2022-09-14 19:29:47 +02:00
|
|
|
}
|
|
|
|
|
2022-09-29 00:15:18 +02:00
|
|
|
void addCorrespondingOnePassSignature(PGPSignature signature) {
|
2022-09-29 17:45:32 +02:00
|
|
|
for (int i = onePassSignatures.size() - 1; i >= 0; i--) {
|
|
|
|
OPS onePassSignature = onePassSignatures.get(i);
|
|
|
|
if (onePassSignature.signature.getKeyID() != signature.getKeyID()) {
|
2022-09-29 00:15:18 +02:00
|
|
|
continue;
|
|
|
|
}
|
2022-09-29 17:45:32 +02:00
|
|
|
if (onePassSignature.finished) {
|
|
|
|
continue;
|
2022-09-29 00:15:18 +02:00
|
|
|
}
|
2022-09-29 17:45:32 +02:00
|
|
|
|
|
|
|
boolean verified = onePassSignature.verify(signature);
|
|
|
|
log("One-Pass-Signature by " + Long.toHexString(onePassSignature.signature.getKeyID()) + " is " + (verified ? "verified" : "unverified"));
|
|
|
|
System.out.println(onePassSignature);
|
|
|
|
break;
|
2022-09-29 00:15:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-08 13:57:53 +02:00
|
|
|
void enterNesting() {
|
2022-09-29 17:45:32 +02:00
|
|
|
opsUpdateStack.push(literalOPS);
|
|
|
|
literalOPS = new ArrayList<>();
|
2022-09-29 00:15:18 +02:00
|
|
|
}
|
|
|
|
|
2022-10-08 13:57:53 +02:00
|
|
|
void leaveNesting() {
|
2022-09-29 17:45:32 +02:00
|
|
|
if (opsUpdateStack.isEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
2022-09-29 00:15:18 +02:00
|
|
|
opsUpdateStack.pop();
|
|
|
|
}
|
|
|
|
|
2022-10-06 21:52:23 +02:00
|
|
|
private static void initialize(PGPSignature signature, PGPPublicKeyRing certificate, long keyId) {
|
2022-09-16 00:51:49 +02:00
|
|
|
if (certificate == null) {
|
|
|
|
// SHIT
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
PGPContentVerifierBuilderProvider verifierProvider = ImplementationFactory.getInstance()
|
|
|
|
.getPGPContentVerifierBuilderProvider();
|
|
|
|
try {
|
|
|
|
signature.init(verifierProvider, certificate.getPublicKey(keyId));
|
|
|
|
} catch (PGPException e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-29 17:45:32 +02:00
|
|
|
private static void initialize(PGPOnePassSignature ops, PGPPublicKeyRing certificate) {
|
2022-09-16 00:51:49 +02:00
|
|
|
if (certificate == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
PGPContentVerifierBuilderProvider verifierProvider = ImplementationFactory.getInstance()
|
|
|
|
.getPGPContentVerifierBuilderProvider();
|
|
|
|
try {
|
|
|
|
ops.init(verifierProvider, certificate.getPublicKey(ops.getKeyID()));
|
|
|
|
} catch (PGPException e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private PGPPublicKeyRing findCertificate(long keyId) {
|
|
|
|
for (PGPPublicKeyRing cert : options.getCertificates()) {
|
|
|
|
PGPPublicKey verificationKey = cert.getPublicKey(keyId);
|
|
|
|
if (verificationKey != null) {
|
|
|
|
return cert;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null; // TODO: Missing cert for sig
|
|
|
|
}
|
|
|
|
|
2022-09-29 17:45:32 +02:00
|
|
|
public void updateLiteral(byte b) {
|
|
|
|
for (OPS ops : literalOPS) {
|
|
|
|
ops.update(b);
|
2022-09-29 00:15:18 +02:00
|
|
|
}
|
|
|
|
|
2022-10-06 21:52:23 +02:00
|
|
|
for (SIG detached : detachedSignatures) {
|
2022-09-29 17:45:32 +02:00
|
|
|
detached.update(b);
|
2022-09-16 00:51:49 +02:00
|
|
|
}
|
2022-09-29 17:45:32 +02:00
|
|
|
}
|
2022-09-29 00:15:18 +02:00
|
|
|
|
2022-09-29 17:45:32 +02:00
|
|
|
public void updateLiteral(byte[] b, int off, int len) {
|
|
|
|
for (OPS ops : literalOPS) {
|
|
|
|
ops.update(b, off, len);
|
|
|
|
}
|
|
|
|
|
2022-10-06 21:52:23 +02:00
|
|
|
for (SIG detached : detachedSignatures) {
|
2022-09-29 17:45:32 +02:00
|
|
|
detached.update(b, off, len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void updatePacket(byte b) {
|
2022-10-06 21:52:23 +02:00
|
|
|
for (SIG detached : detachedSignatures) {
|
|
|
|
detached.update(b);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (SIG prepended : prependedSignatures) {
|
|
|
|
prepended.update(b);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = opsUpdateStack.size() - 1; i >= 0; i--) {
|
|
|
|
List<OPS> nestedOPSs = opsUpdateStack.get(i);
|
2022-09-29 17:45:32 +02:00
|
|
|
for (OPS ops : nestedOPSs) {
|
2022-09-28 16:55:08 +02:00
|
|
|
ops.update(b);
|
|
|
|
}
|
2022-09-16 00:51:49 +02:00
|
|
|
}
|
2022-09-29 17:45:32 +02:00
|
|
|
}
|
2022-09-29 00:15:18 +02:00
|
|
|
|
2022-09-29 17:45:32 +02:00
|
|
|
public void updatePacket(byte[] buf, int off, int len) {
|
2022-10-06 21:52:23 +02:00
|
|
|
for (SIG detached : detachedSignatures) {
|
|
|
|
detached.update(buf, off, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (SIG prepended : prependedSignatures) {
|
|
|
|
prepended.update(buf, off, len);
|
|
|
|
}
|
|
|
|
|
2022-09-29 17:45:32 +02:00
|
|
|
for (int i = opsUpdateStack.size() - 1; i >= 0; i--) {
|
|
|
|
List<OPS> nestedOPSs = opsUpdateStack.get(i);
|
|
|
|
for (OPS ops : nestedOPSs) {
|
|
|
|
ops.update(buf, off, len);
|
|
|
|
}
|
2022-09-16 00:51:49 +02:00
|
|
|
}
|
2022-09-14 19:29:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void finish() {
|
2022-10-06 21:52:23 +02:00
|
|
|
for (SIG detached : detachedSignatures) {
|
|
|
|
boolean verified = detached.verify();
|
|
|
|
log("Detached Signature by " + Long.toHexString(detached.signature.getKeyID()) + " is " + (verified ? "verified" : "unverified"));
|
|
|
|
System.out.println(detached);
|
2022-09-16 00:51:49 +02:00
|
|
|
}
|
2022-09-14 19:29:47 +02:00
|
|
|
|
2022-10-06 21:52:23 +02:00
|
|
|
for (SIG prepended : prependedSignatures) {
|
|
|
|
boolean verified = prepended.verify();
|
|
|
|
log("Prepended Signature by " + Long.toHexString(prepended.signature.getKeyID()) + " is " + (verified ? "verified" : "unverified"));
|
|
|
|
System.out.println(prepended);
|
2022-09-14 19:29:47 +02:00
|
|
|
}
|
|
|
|
}
|
2022-09-28 16:55:08 +02:00
|
|
|
|
|
|
|
@Override
|
2022-09-29 17:45:32 +02:00
|
|
|
public void write(int b) {
|
|
|
|
updatePacket((byte) b);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2022-10-08 13:57:53 +02:00
|
|
|
public void write(@Nonnull byte[] b, int off, int len) {
|
2022-09-29 17:45:32 +02:00
|
|
|
updatePacket(b, off, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void nextPacket(OpenPgpPacket nextPacket) {
|
|
|
|
if (nextPacket == OpenPgpPacket.LIT) {
|
2022-10-06 21:52:23 +02:00
|
|
|
isLiteral = true;
|
2022-09-29 17:45:32 +02:00
|
|
|
if (literalOPS.isEmpty() && !opsUpdateStack.isEmpty()) {
|
|
|
|
literalOPS = opsUpdateStack.pop();
|
|
|
|
}
|
2022-10-06 21:52:23 +02:00
|
|
|
} else {
|
|
|
|
isLiteral = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static class SIG {
|
|
|
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
|
|
|
PGPSignature signature;
|
|
|
|
boolean finished;
|
|
|
|
boolean valid;
|
|
|
|
|
|
|
|
public SIG(PGPSignature signature) {
|
|
|
|
this.signature = signature;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void init(PGPPublicKeyRing certificate) {
|
|
|
|
initialize(signature, certificate, signature.getKeyID());
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean verify() {
|
|
|
|
finished = true;
|
|
|
|
try {
|
|
|
|
valid = this.signature.verify();
|
|
|
|
} catch (PGPException e) {
|
|
|
|
log("Cannot verify SIG " + signature.getKeyID());
|
|
|
|
}
|
|
|
|
return valid;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void update(byte b) {
|
|
|
|
if (finished) {
|
|
|
|
log("Updating finished sig!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
signature.update(b);
|
|
|
|
bytes.write(b);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void update(byte[] bytes, int off, int len) {
|
|
|
|
if (finished) {
|
|
|
|
log("Updating finished sig!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
signature.update(bytes, off, len);
|
|
|
|
this.bytes.write(bytes, off, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
String OPS = "c40d03000a01fbfcc82a015e733001";
|
|
|
|
String LIT_H = "cb28620000000000";
|
|
|
|
String LIT = "656e637279707420e28898207369676e20e28898207369676e20e28898207369676e";
|
|
|
|
String SIG1 = "c2c10400010a006f058262c806350910fbfcc82a015e7330471400000000001e002073616c74406e6f746174696f6e732e736571756f69612d7067702e6f7267b0409ed8ea96dac66447bdff5b7b60c9f80a0ab91d257029153dc3b6d8c27b98162104d1a66e1a23b182c9980f788cfbfcc82a015e7330000029640c00846b5096d92474fd446cc7edaf9f14572cab93a80e12384c1e829f95debc6e8373c2ce5402be53dc1a18cf92a0ed909e0fb38855713ef8ffb13502ffac7c830fa254cc1aa6c666a97b0cc3bc176538f6913d3b8e8981a65cc42df10e0f39e4d0a06dfe961437b59a71892f4fca1116aed15123ea0d86a7b2ce47dd9d3ef22d920631bc011e82babe03ad5d72b3ba7f95bf646f20ccf6f7a4d95de37397c76c7d53741458e51ab6074007f61181c7b88b7c98f5b7510c8dfa3be01f4841501679478b15c5249d928e2a10d15ec63efa1500b994d5bfb32ffb174a976116930eb97a111e6dfd4c5e43e04a5d76ba74806a62fda63a8c3f53f6eebaf852892340e81dd08bbf348454a2cf525aeb512cf33aeeee78465ee4c442e41cc45ac4e3bb0c3333677aa60332ee7f464d9020f8554b82d619872477cca18d8431888f4ae8abe5894e9720f759c410cd7991db12703dc147040dd0d3758223e0b75de6ceae49c1a0c2c45efedeb7114ae785cc886afdc45c82172e4476e1ab5b86dc4314dd76";
|
|
|
|
String SIG1f = "c2c13b0400010a006f058262c806350910fbfcc82a015e7330471400000000001e002073616c74406e6f746174696f6e732e736571756f69612d7067702e6f7267b0409ed8ea96dac66447bdff5b7b60c9f80a0ab91d257029153dc3b6d8c27b98162104d1a66e1a23b182c9980f788cfbfcc82a015e7330000029640c00846b5096d92474fd446cc7edaf9f14572cab93a80e12384c1e829f95debc6e8373c2ce5402be53dc1a18cf92a0ed909e0fb38855713ef8ffb13502ffac7c830fa254cc1aa6c666a97b0cc3bc176538f6913d3b8e8981a65cc42df10e0f39e4d0a06dfe961437b59a71892f4fca1116aed15123ea0d86a7b2ce47dd9d3ef22d920631bc011e82babe03ad5d72b3ba7f95bf646f20ccf6f7a4d95de37397c76c7d53741458e51ab6074007f61181c7b88b7c98f5b7510c8dfa3be01f4841501679478b15c5249d928e2a10d15ec63efa1500b994d5bfb32ffb174a976116930eb97a111e6dfd4c5e43e04a5d76ba74806a62fda63a8c3f53f6eebaf852892340e81dd08bbf348454a2cf525aeb512cf33aeeee78465ee4c442e41cc45ac4e3bb0c3333677aa60332ee7f464d9020f8554b82d619872477cca18d8431888f4ae8abe5894e9720f759c410cd7991db12703dc147040dd0d3758223e0b75de6ceae49c1a0c2c45efedeb7114ae785cc886afdc45c82172e4476e1ab5b86dc4314dd76";
|
|
|
|
String SIG2 = "c2c10400010a006f058262c806350910fbfcc82a015e7330471400000000001e002073616c74406e6f746174696f6e732e736571756f69612d7067702e6f7267a4d9c117dc7ba3a7e9270856f128d2ab271743eac3cb5750b22a89bd5fd60753162104d1a66e1a23b182c9980f788cfbfcc82a015e73300000b8400bff796c20fa8b25ff7a42686338e06417a2966e85a0fc2723c928bef6cd19d34cf5e7d55ada33080613012dadb79e0278e59d9e7ed7d2d6102912a5f768c2e75b60099225c3d8bfe0c123240188b80dbee89b9b3bd5b13ccc662abc37e2129b6968adac9aba43aa778c0fe4fe337591ee87a96a29a013debc83555293c877144fc676aa1b03782c501949521a320adf6ad96c4f2e036b52a18369c637fdc49033696a84d03a69580b953187fce5aca6fb26fc8815da9f3b513bfe8e304f33ecb4b521aeb7d09c4a284ea66123bd0d6a358b2526d762ca110e1f7f20b3038d774b64d5cfd34e2213765828359d7afc5bf24d5270e99d80c3c1568fa01624b6ea1e9ce4e6890ce9bacf6611a45d41e2671f68f5b096446bf08d27ce75608425b2e3ab92146229ad1fcd8224aca5b5f73960506e7df07bfbf3664348e8ecbfb2eb467b9cfe412cb377a6ee2eb5fd11be9cf9208fe9a74c296f52cfa02a1eb0519ad9a8349bf6ccd6495feb7e391451bf96e08a0798883dee5974e47cbf3b51f111b6d3";
|
|
|
|
String SIG2f = "c2c13b0400010a006f058262c806350910fbfcc82a015e7330471400000000001e002073616c74406e6f746174696f6e732e736571756f69612d7067702e6f7267a4d9c117dc7ba3a7e9270856f128d2ab271743eac3cb5750b22a89bd5fd60753162104d1a66e1a23b182c9980f788cfbfcc82a015e73300000b8400bff796c20fa8b25ff7a42686338e06417a2966e85a0fc2723c928bef6cd19d34cf5e7d55ada33080613012dadb79e0278e59d9e7ed7d2d6102912a5f768c2e75b60099225c3d8bfe0c123240188b80dbee89b9b3bd5b13ccc662abc37e2129b6968adac9aba43aa778c0fe4fe337591ee87a96a29a013debc83555293c877144fc676aa1b03782c501949521a320adf6ad96c4f2e036b52a18369c637fdc49033696a84d03a69580b953187fce5aca6fb26fc8815da9f3b513bfe8e304f33ecb4b521aeb7d09c4a284ea66123bd0d6a358b2526d762ca110e1f7f20b3038d774b64d5cfd34e2213765828359d7afc5bf24d5270e99d80c3c1568fa01624b6ea1e9ce4e6890ce9bacf6611a45d41e2671f68f5b096446bf08d27ce75608425b2e3ab92146229ad1fcd8224aca5b5f73960506e7df07bfbf3664348e8ecbfb2eb467b9cfe412cb377a6ee2eb5fd11be9cf9208fe9a74c296f52cfa02a1eb0519ad9a8349bf6ccd6495feb7e391451bf96e08a0798883dee5974e47cbf3b51f111b6d3";
|
|
|
|
String out = "";
|
|
|
|
|
|
|
|
String hex = Hex.toHexString(bytes.toByteArray());
|
|
|
|
while (hex.contains(OPS)) {
|
|
|
|
hex = hex.replace(OPS, "[OPS]");
|
|
|
|
}
|
|
|
|
while (hex.contains(LIT_H)) {
|
|
|
|
hex = hex.replace(LIT_H, "[LIT]");
|
|
|
|
}
|
|
|
|
while (hex.contains(LIT)) {
|
|
|
|
hex = hex.replace(LIT, "<content>");
|
|
|
|
}
|
|
|
|
while (hex.contains(SIG1)) {
|
|
|
|
hex = hex.replace(SIG1, "[SIG1]");
|
|
|
|
}
|
|
|
|
while (hex.contains(SIG1f)) {
|
|
|
|
hex = hex.replace(SIG1f, "[SIG1f]");
|
|
|
|
}
|
|
|
|
while (hex.contains(SIG2)) {
|
|
|
|
hex = hex.replace(SIG2, "[SIG2]");
|
|
|
|
}
|
|
|
|
while (hex.contains(SIG2f)) {
|
|
|
|
hex = hex.replace(SIG2f, "[SIG2f]");
|
|
|
|
}
|
|
|
|
|
|
|
|
return out + hex;
|
2022-09-29 17:45:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static class OPS {
|
|
|
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
|
|
|
PGPOnePassSignature signature;
|
|
|
|
boolean finished;
|
|
|
|
boolean valid;
|
|
|
|
|
|
|
|
public OPS(PGPOnePassSignature signature) {
|
|
|
|
this.signature = signature;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void init(PGPPublicKeyRing certificate) {
|
|
|
|
initialize(signature, certificate);
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean verify(PGPSignature signature) {
|
|
|
|
if (this.signature.getKeyID() != signature.getKeyID()) {
|
|
|
|
// nope
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
finished = true;
|
|
|
|
try {
|
|
|
|
valid = this.signature.verify(signature);
|
|
|
|
} catch (PGPException e) {
|
|
|
|
log("Cannot verify OPS " + signature.getKeyID());
|
|
|
|
}
|
|
|
|
return valid;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void update(byte b) {
|
|
|
|
if (finished) {
|
|
|
|
log("Updating finished sig!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
signature.update(b);
|
|
|
|
bytes.write(b);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void update(byte[] bytes, int off, int len) {
|
|
|
|
if (finished) {
|
|
|
|
log("Updating finished sig!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
signature.update(bytes, off, len);
|
|
|
|
this.bytes.write(bytes, off, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
String OPS = "c40d03000a01fbfcc82a015e733001";
|
|
|
|
String LIT_H = "cb28620000000000";
|
|
|
|
String LIT = "656e637279707420e28898207369676e20e28898207369676e20e28898207369676e";
|
|
|
|
String SIG1 = "c2c10400010a006f058262c806350910fbfcc82a015e7330471400000000001e002073616c74406e6f746174696f6e732e736571756f69612d7067702e6f7267b0409ed8ea96dac66447bdff5b7b60c9f80a0ab91d257029153dc3b6d8c27b98162104d1a66e1a23b182c9980f788cfbfcc82a015e7330000029640c00846b5096d92474fd446cc7edaf9f14572cab93a80e12384c1e829f95debc6e8373c2ce5402be53dc1a18cf92a0ed909e0fb38855713ef8ffb13502ffac7c830fa254cc1aa6c666a97b0cc3bc176538f6913d3b8e8981a65cc42df10e0f39e4d0a06dfe961437b59a71892f4fca1116aed15123ea0d86a7b2ce47dd9d3ef22d920631bc011e82babe03ad5d72b3ba7f95bf646f20ccf6f7a4d95de37397c76c7d53741458e51ab6074007f61181c7b88b7c98f5b7510c8dfa3be01f4841501679478b15c5249d928e2a10d15ec63efa1500b994d5bfb32ffb174a976116930eb97a111e6dfd4c5e43e04a5d76ba74806a62fda63a8c3f53f6eebaf852892340e81dd08bbf348454a2cf525aeb512cf33aeeee78465ee4c442e41cc45ac4e3bb0c3333677aa60332ee7f464d9020f8554b82d619872477cca18d8431888f4ae8abe5894e9720f759c410cd7991db12703dc147040dd0d3758223e0b75de6ceae49c1a0c2c45efedeb7114ae785cc886afdc45c82172e4476e1ab5b86dc4314dd76";
|
2022-10-06 21:52:23 +02:00
|
|
|
String SIG1f = "c2c13b0400010a006f058262c806350910fbfcc82a015e7330471400000000001e002073616c74406e6f746174696f6e732e736571756f69612d7067702e6f7267b0409ed8ea96dac66447bdff5b7b60c9f80a0ab91d257029153dc3b6d8c27b98162104d1a66e1a23b182c9980f788cfbfcc82a015e7330000029640c00846b5096d92474fd446cc7edaf9f14572cab93a80e12384c1e829f95debc6e8373c2ce5402be53dc1a18cf92a0ed909e0fb38855713ef8ffb13502ffac7c830fa254cc1aa6c666a97b0cc3bc176538f6913d3b8e8981a65cc42df10e0f39e4d0a06dfe961437b59a71892f4fca1116aed15123ea0d86a7b2ce47dd9d3ef22d920631bc011e82babe03ad5d72b3ba7f95bf646f20ccf6f7a4d95de37397c76c7d53741458e51ab6074007f61181c7b88b7c98f5b7510c8dfa3be01f4841501679478b15c5249d928e2a10d15ec63efa1500b994d5bfb32ffb174a976116930eb97a111e6dfd4c5e43e04a5d76ba74806a62fda63a8c3f53f6eebaf852892340e81dd08bbf348454a2cf525aeb512cf33aeeee78465ee4c442e41cc45ac4e3bb0c3333677aa60332ee7f464d9020f8554b82d619872477cca18d8431888f4ae8abe5894e9720f759c410cd7991db12703dc147040dd0d3758223e0b75de6ceae49c1a0c2c45efedeb7114ae785cc886afdc45c82172e4476e1ab5b86dc4314dd76";
|
2022-09-29 17:45:32 +02:00
|
|
|
String SIG2 = "c2c10400010a006f058262c806350910fbfcc82a015e7330471400000000001e002073616c74406e6f746174696f6e732e736571756f69612d7067702e6f7267a4d9c117dc7ba3a7e9270856f128d2ab271743eac3cb5750b22a89bd5fd60753162104d1a66e1a23b182c9980f788cfbfcc82a015e73300000b8400bff796c20fa8b25ff7a42686338e06417a2966e85a0fc2723c928bef6cd19d34cf5e7d55ada33080613012dadb79e0278e59d9e7ed7d2d6102912a5f768c2e75b60099225c3d8bfe0c123240188b80dbee89b9b3bd5b13ccc662abc37e2129b6968adac9aba43aa778c0fe4fe337591ee87a96a29a013debc83555293c877144fc676aa1b03782c501949521a320adf6ad96c4f2e036b52a18369c637fdc49033696a84d03a69580b953187fce5aca6fb26fc8815da9f3b513bfe8e304f33ecb4b521aeb7d09c4a284ea66123bd0d6a358b2526d762ca110e1f7f20b3038d774b64d5cfd34e2213765828359d7afc5bf24d5270e99d80c3c1568fa01624b6ea1e9ce4e6890ce9bacf6611a45d41e2671f68f5b096446bf08d27ce75608425b2e3ab92146229ad1fcd8224aca5b5f73960506e7df07bfbf3664348e8ecbfb2eb467b9cfe412cb377a6ee2eb5fd11be9cf9208fe9a74c296f52cfa02a1eb0519ad9a8349bf6ccd6495feb7e391451bf96e08a0798883dee5974e47cbf3b51f111b6d3";
|
2022-10-06 21:52:23 +02:00
|
|
|
String SIG2f = "c2c13b0400010a006f058262c806350910fbfcc82a015e7330471400000000001e002073616c74406e6f746174696f6e732e736571756f69612d7067702e6f7267a4d9c117dc7ba3a7e9270856f128d2ab271743eac3cb5750b22a89bd5fd60753162104d1a66e1a23b182c9980f788cfbfcc82a015e73300000b8400bff796c20fa8b25ff7a42686338e06417a2966e85a0fc2723c928bef6cd19d34cf5e7d55ada33080613012dadb79e0278e59d9e7ed7d2d6102912a5f768c2e75b60099225c3d8bfe0c123240188b80dbee89b9b3bd5b13ccc662abc37e2129b6968adac9aba43aa778c0fe4fe337591ee87a96a29a013debc83555293c877144fc676aa1b03782c501949521a320adf6ad96c4f2e036b52a18369c637fdc49033696a84d03a69580b953187fce5aca6fb26fc8815da9f3b513bfe8e304f33ecb4b521aeb7d09c4a284ea66123bd0d6a358b2526d762ca110e1f7f20b3038d774b64d5cfd34e2213765828359d7afc5bf24d5270e99d80c3c1568fa01624b6ea1e9ce4e6890ce9bacf6611a45d41e2671f68f5b096446bf08d27ce75608425b2e3ab92146229ad1fcd8224aca5b5f73960506e7df07bfbf3664348e8ecbfb2eb467b9cfe412cb377a6ee2eb5fd11be9cf9208fe9a74c296f52cfa02a1eb0519ad9a8349bf6ccd6495feb7e391451bf96e08a0798883dee5974e47cbf3b51f111b6d3";
|
|
|
|
String out = "last=" + signature.isContaining() + "\n";
|
2022-09-29 17:45:32 +02:00
|
|
|
|
2022-10-06 21:52:23 +02:00
|
|
|
String hex = Hex.toHexString(bytes.toByteArray());
|
|
|
|
while (hex.contains(OPS)) {
|
|
|
|
hex = hex.replace(OPS, "[OPS]");
|
|
|
|
}
|
|
|
|
while (hex.contains(LIT_H)) {
|
|
|
|
hex = hex.replace(LIT_H, "[LIT]");
|
|
|
|
}
|
|
|
|
while (hex.contains(LIT)) {
|
|
|
|
hex = hex.replace(LIT, "<content>");
|
|
|
|
}
|
|
|
|
while (hex.contains(SIG1)) {
|
|
|
|
hex = hex.replace(SIG1, "[SIG1]");
|
|
|
|
}
|
|
|
|
while (hex.contains(SIG1f)) {
|
|
|
|
hex = hex.replace(SIG1f, "[SIG1f]");
|
|
|
|
}
|
|
|
|
while (hex.contains(SIG2)) {
|
|
|
|
hex = hex.replace(SIG2, "[SIG2]");
|
|
|
|
}
|
|
|
|
while (hex.contains(SIG2f)) {
|
|
|
|
hex = hex.replace(SIG2f, "[SIG2f]");
|
|
|
|
}
|
2022-09-29 17:45:32 +02:00
|
|
|
|
2022-10-06 21:52:23 +02:00
|
|
|
return out + hex;
|
2022-09-29 17:45:32 +02:00
|
|
|
}
|
2022-09-28 16:55:08 +02:00
|
|
|
}
|
2022-09-14 19:29:47 +02:00
|
|
|
}
|
2022-09-29 00:15:18 +02:00
|
|
|
|
|
|
|
static void log(String message) {
|
|
|
|
LOGGER.debug(message);
|
|
|
|
// CHECKSTYLE:OFF
|
|
|
|
System.out.println(message);
|
|
|
|
// CHECKSTYLE:ON
|
|
|
|
}
|
|
|
|
|
|
|
|
static void log(String message, Throwable e) {
|
|
|
|
log(message);
|
|
|
|
// CHECKSTYLE:OFF
|
|
|
|
e.printStackTrace();
|
|
|
|
// CHECKSTYLE:ON
|
|
|
|
}
|
2022-09-13 19:23:59 +02:00
|
|
|
}
|