Debug signature verification using debug build of bcpg

This commit is contained in:
Paul Schaub 2021-08-23 15:47:21 +02:00
parent 9fc4b0b42e
commit 4d6ca80e25
5 changed files with 59 additions and 77 deletions

View File

@ -8,7 +8,12 @@ dependencies {
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
implementation "org.bouncycastle:bcprov-jdk15on:$bouncyCastleVersion"
//*
api "org.bouncycastle:bcpg-jdk15on:$bouncyCastleVersion"
/*/
api files("libs/bcpg-jdk18on-1.70-SNAPSHOT.jar")
// */
implementation 'org.slf4j:slf4j-api:1.7.32'
testImplementation 'ch.qos.logback:logback-classic:1.2.5'

View File

@ -373,8 +373,21 @@ public final class SignatureVerifier {
signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), signingKey);
int read;
byte[] buf = new byte[8192];
byte lastByte = -1;
while ((read = signedData.read(buf)) != -1) {
signature.update(buf, 0, read);
// If we previously omitted a newline, but the stream is not yet empty, add it now
if (lastByte == (byte) '\n') {
signature.update(lastByte);
}
lastByte = buf[read - 1];
if (lastByte == (byte) '\n') {
// if last byte in buffer is newline, omit it for now
signature.update(buf, 0, read - 1);
} else {
// otherwise, write buffer as usual
signature.update(buf, 0, read);
}
}
} catch (PGPException e) {
throw new SignatureValidationException("Cannot init signature.", e);

View File

@ -15,7 +15,6 @@
*/
package org.pgpainless.signature.cleartext_signatures;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@ -23,11 +22,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import org.bouncycastle.bcpg.ArmoredInputStream;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPObjectFactory;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.PGPSignatureList;
import org.bouncycastle.util.Strings;
import org.pgpainless.implementation.ImplementationFactory;
@ -69,19 +64,17 @@ public final class ClearsignedMessageUtil {
if (lookAhead != -1 && in.isClearText()) {
byte[] line = lineOut.toByteArray();
out.write(line, 0, getLengthWithoutSeparatorOrTrailingWhitespace(line));
out.write(lineSep);
while (lookAhead != -1 && in.isClearText()) {
lookAhead = readInputLine(lineOut, lookAhead, in);
line = lineOut.toByteArray();
out.write(line, 0, getLengthWithoutSeparatorOrTrailingWhitespace(line));
out.write(lineSep);
out.write(line, 0, getLengthWithoutSeparatorOrTrailingWhitespace(line));
}
} else {
if (lookAhead != -1) {
byte[] line = lineOut.toByteArray();
out.write(line, 0, getLengthWithoutSeparatorOrTrailingWhitespace(line));
out.write(lineSep);
}
}
} finally {
@ -94,38 +87,6 @@ public final class ClearsignedMessageUtil {
return signatures;
}
/**
* Initialize the given signature by processing the data from the messageData input stream.
*
* @param signature uninitialized signature
* @param signingKey public signing key
* @param messageData input stream containing the data to which the signature belongs
* @return initialized signature
*
* @throws PGPException if the signature cannot be initialized
* @throws IOException if an IO error happens
*/
public static PGPSignature initializeSignature(PGPSignature signature, PGPPublicKey signingKey, InputStream messageData)
throws PGPException, IOException {
signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), signingKey);
InputStream sigIn = new BufferedInputStream(messageData);
ByteArrayOutputStream lineOut = new ByteArrayOutputStream();
int lookAhead = readInputLine(lineOut, sigIn);
processLine(signature, lineOut.toByteArray());
if (lookAhead != -1) {
do {
lookAhead = readInputLine(lineOut, lookAhead, sigIn);
signature.update((byte) '\r');
signature.update((byte) '\n');
processLine(signature, lineOut.toByteArray());
} while (lookAhead != -1);
}
sigIn.close();
return signature;
}
public static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn)
throws IOException {
bOut.reset();
@ -190,25 +151,6 @@ public final class ClearsignedMessageUtil {
return nlBytes;
}
public static void processLine(PGPSignature sig, byte[] line) {
int length = getLengthWithoutWhiteSpace(line);
if (length > 0) {
sig.update(line, 0, length);
}
}
public static void processLine(OutputStream aOut, PGPSignatureGenerator sGen, byte[] line)
throws IOException {
// note: trailing white space needs to be removed from the end of
// each line for signature calculation RFC 4880 Section 7.1
int length = getLengthWithoutWhiteSpace(line);
if (length > 0) {
sGen.update(line, 0, length);
}
aOut.write(line, 0, line.length);
}
private static int getLengthWithoutSeparatorOrTrailingWhitespace(byte[] line) {
int end = line.length - 1;
@ -223,16 +165,6 @@ public final class ClearsignedMessageUtil {
return b == '\r' || b == '\n';
}
private static int getLengthWithoutWhiteSpace(byte[] line) {
int end = line.length - 1;
while (end >= 0 && isWhiteSpace(line[end])) {
end--;
}
return end + 1;
}
private static boolean isWhiteSpace(byte b) {
return isLineEnding(b) || b == '\t' || b == ' ';
}

View File

@ -34,6 +34,7 @@ import org.bouncycastle.openpgp.PGPSignatureList;
import org.pgpainless.PGPainless;
import org.pgpainless.exception.SignatureValidationException;
import org.pgpainless.signature.CertificateValidator;
import org.pgpainless.signature.SignatureVerifier;
import org.pgpainless.util.ArmoredInputStreamFactory;
/**
@ -93,7 +94,7 @@ public class CleartextSignatureProcessor {
}
try {
ClearsignedMessageUtil.initializeSignature(signature, signingKey, multiPassStrategy.getMessageInputStream());
SignatureVerifier.initializeSignatureAndUpdateWithSignedData(signature, multiPassStrategy.getMessageInputStream(), signingKey);
CertificateValidator.validateCertificateAndVerifyInitializedSignature(signature, certificate, PGPainless.getPolicy());
return signature;
} catch (SignatureValidationException e) {

View File

@ -30,15 +30,17 @@ import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.util.io.Streams;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
import org.pgpainless.key.TestKeys;
import org.pgpainless.signature.CertificateValidator;
import org.pgpainless.signature.SignatureUtils;
import org.pgpainless.signature.cleartext_signatures.ClearsignedMessageUtil;
import org.pgpainless.signature.SignatureVerifier;
import org.pgpainless.signature.cleartext_signatures.CleartextSignatureProcessor;
import org.pgpainless.signature.cleartext_signatures.InMemoryMultiPassStrategy;
import org.pgpainless.signature.cleartext_signatures.MultiPassStrategy;
import org.pgpainless.util.ArmorUtils;
import org.pgpainless.util.TestUtils;
public class CleartextSignatureVerificationTest {
@ -48,7 +50,7 @@ public class CleartextSignatureVerificationTest {
"To blazon it, then sweeten with thy breath\n" +
"This neighbor air, and let rich musics tongue\n" +
"Unfold the imagined happiness that both\n" +
"Receive in either by this dear encounter.\n";
"Receive in either by this dear encounter.";
public static final String MESSAGE_SIGNED = "-----BEGIN PGP SIGNED MESSAGE-----\n" +
"Hash: SHA512\n" +
"\n" +
@ -118,11 +120,40 @@ public class CleartextSignatureVerificationTest {
PGPSignature signature = SignatureUtils.readSignatures(SIGNATURE).get(0);
PGPPublicKey signingKey = signingKeys.getPublicKey(signature.getKeyID());
/*
SignatureVerifier.initializeSignatureAndUpdateWithSignedData(signature, new ByteArrayInputStream(MESSAGE_BODY.getBytes(StandardCharsets.UTF_8)), signingKey);
/*/
ClearsignedMessageUtil.initializeSignature(signature, signingKey, new ByteArrayInputStream(MESSAGE_BODY.getBytes(StandardCharsets.UTF_8)));
//*/
CertificateValidator.validateCertificateAndVerifyInitializedSignature(signature, signingKeys, PGPainless.getPolicy());
}
@Test
@Disabled
public void print() throws IOException {
// CHECKSTYLE:OFF
PGPPublicKeyRing keys = TestKeys.getEmilPublicKeyRing();
System.out.println(ArmorUtils.toAsciiArmoredString(keys));
System.out.println(MESSAGE_SIGNED);
System.out.println(MESSAGE_BODY);
System.out.println(SIGNATURE);
// CHECKSTYLE:ON
}
@Test
public void testOutputOfSigVerification() throws IOException, PGPException {
PGPSignature signature = SignatureUtils.readSignatures(SIGNATURE).get(0);
ConsumerOptions options = new ConsumerOptions()
.addVerificationCert(TestKeys.getEmilPublicKeyRing())
.addVerificationOfDetachedSignature(signature);
DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify()
.onInputStream(new ByteArrayInputStream(MESSAGE_BODY.getBytes(StandardCharsets.UTF_8)))
.withOptions(options);
ByteArrayOutputStream out = new ByteArrayOutputStream();
Streams.pipeAll(decryptionStream, out);
decryptionStream.close();
OpenPgpMetadata metadata = decryptionStream.getResult();
assertEquals(1, metadata.getVerifiedSignatures().size());
}
}