Skip to content

Commit

Permalink
Split out signature packet tests into smaller units
Browse files Browse the repository at this point in the history
  • Loading branch information
vanitasvitae committed Apr 22, 2024
1 parent 68b01b4 commit eaadfc0
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 34 deletions.
4 changes: 3 additions & 1 deletion pg/src/test/java/org/bouncycastle/bcpg/test/AllTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ public void testPacketParsing()
Security.addProvider(new BouncyCastleProvider());

org.bouncycastle.util.test.Test[] tests = new org.bouncycastle.util.test.Test[] {
new V6SignaturePacketTest()
new SignaturePacketTest(),
new OnePassSignaturePacketTest(),
new OpenPgpMessageTest()
};

for (int i = 0; i != tests.length; i++)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package org.bouncycastle.bcpg.test;

import org.bouncycastle.bcpg.*;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;
import org.bouncycastle.util.test.SimpleTest;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class OnePassSignaturePacketTest
extends SimpleTest
{

// Version 6 OnePassSignature packet
// extracted from https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-13.html#name-sample-inline-signed-messag
public static final byte[] OPS_V6 = Hex.decode("c44606010a1b2076495f50218890f7f5e2ee3c1822514f70500f551d86e5c921e404e34a53fbaccb186c4f0609a697e4d52dfa6c722b0c1f1e27c18a56708f6525ec27bad9acc901");
// Issuer of the message
public static byte[] ISSUER = Hex.decode("CB186C4F0609A697E4D52DFA6C722B0C1F1E27C18A56708F6525EC27BAD9ACC9");
// Salt used to generate the signature
public static byte[] SALT = Hex.decode("76495F50218890F7F5E2EE3C1822514F70500F551D86E5C921E404E34A53FBAC");

// Parse v6 OPS packet and compare its values to a known-good test vector
private void testParseV6OnePassSignaturePacket() throws IOException {
ByteArrayInputStream bIn = new ByteArrayInputStream(OPS_V6);
BCPGInputStream pIn = new BCPGInputStream(bIn);

// Parse and compare the OnePassSignature packet
OnePassSignaturePacket ops = (OnePassSignaturePacket) pIn.readPacket();
isEquals("OPS packet MUST be of version 6",
OnePassSignaturePacket.VERSION_6, ops.getVersion());
isTrue("OPS packet issuer fingerprint mismatch",
Arrays.areEqual(ISSUER, ops.getFingerprint()));
isTrue("OPS packet salt mismatch",
Arrays.areEqual(SALT, ops.getSalt()));
isTrue("OPS packet isContaining mismatch",
ops.isContaining());
}

private void testEncodeV6OPS() throws IOException {
ByteArrayInputStream bIn = new ByteArrayInputStream(OPS_V6);
BCPGInputStream pIn = new BCPGInputStream(bIn);
OnePassSignaturePacket ops = (OnePassSignaturePacket) pIn.readPacket();

ByteArrayOutputStream bOut = new ByteArrayOutputStream();
BCPGOutputStream pOut = new BCPGOutputStream(bOut, true);
ops.encode(pOut);
pOut.close();

isTrue("Encoding mismatch of OPS v6 packet",
Arrays.areEqual(OPS_V6, bOut.toByteArray()));
}

@Override
public String getName() {
return OnePassSignaturePacketTest.class.getSimpleName();
}

@Override
public void performTest() throws Exception {
testParseV6OnePassSignaturePacket();
testEncodeV6OPS();
}

public static void main(String[] args) {
runTest(new OnePassSignaturePacketTest());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
import java.io.IOException;
import java.nio.charset.StandardCharsets;

public class V6SignaturePacketTest extends SimpleTest
{
public class OpenPgpMessageTest extends SimpleTest {

/*
Inline-signed message using a version 6 signature
Expand Down Expand Up @@ -51,8 +50,7 @@ public class V6SignaturePacketTest extends SimpleTest
"/FvLFuGWMbKAdA+epq7V4HOtAPlBWmU8QOd6aud+aSunHQaaEJ+iTFjP2OMW0KBr\n" +
"NK2ay45cX1IVAQ==\n" +
"-----END PGP SIGNATURE-----";
// Only the hex-encoded signature packet
public static final byte[] SIGNATURE = Hex.decode("c29806011b0a0000002905826398a363222106cb186c4f0609a697e4d52dfa6c722b0c1f1e27c18a56708f6525ec27bad9acc90000000069362076495f50218890f7f5e2ee3c1822514f70500f551d86e5c921e404e34a53fbac27d06fb80aa8fc5bcb16e19631b280740f9ea6aed5e073ad00f9415a653c40e77a6ae77e692ba71d069a109fa24c58cfd8e316d0a06b34ad9acb8e5c5f521501");

// Content of the message's LiteralData packet
public static final String CONTENT = "What we need from the grocery store:\n" +
"\n" +
Expand All @@ -64,20 +62,6 @@ public class V6SignaturePacketTest extends SimpleTest
// Salt used to generate the signature
public static byte[] SALT = Hex.decode("76495F50218890F7F5E2EE3C1822514F70500F551D86E5C921E404E34A53FBAC");

@Override
public String getName()
{
return V6SignaturePacketTest.class.getSimpleName();
}

@Override
public void performTest()
throws Exception
{
testParseV6InlineSignedMessage();
testParseV6CleartextSignedMessage();
testParseAndReencodeSignature();
}

private void testParseV6CleartextSignedMessage()
throws IOException
Expand Down Expand Up @@ -131,18 +115,6 @@ private void testParseV6InlineSignedMessage()
compareSignature(sig);
}

private void testParseAndReencodeSignature() throws IOException {
BCPGInputStream pIn = new BCPGInputStream(new ByteArrayInputStream(SIGNATURE));

// Parse and compare the Signature packet
SignaturePacket sig = (SignaturePacket) pIn.readPacket();
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
BCPGOutputStream bcOut = new BCPGOutputStream(bOut, true);
sig.encode(bcOut);
bcOut.close();
isTrue("Signature packet encoding mismatch",
Arrays.areEqual(bOut.toByteArray(), SIGNATURE));
}

private void compareLiteralData(LiteralDataPacket lit)
throws IOException
Expand Down Expand Up @@ -183,8 +155,18 @@ private void compareSignature(SignaturePacket sig)
0, sig.getUnhashedSubPackets().length);
}

public static void main(String[] args)
{
runTest(new V6SignaturePacketTest());
@Override
public String getName() {
return OpenPgpMessageTest.class.getSimpleName();
}

@Override
public void performTest() throws Exception {
testParseV6CleartextSignedMessage();
testParseV6InlineSignedMessage();
}

public static void main(String[] args) {
runTest(new OpenPgpMessageTest());
}
}
139 changes: 139 additions & 0 deletions pg/src/test/java/org/bouncycastle/bcpg/test/SignaturePacketTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package org.bouncycastle.bcpg.test;

import org.bouncycastle.bcpg.*;
import org.bouncycastle.bcpg.sig.IssuerFingerprint;
import org.bouncycastle.bcpg.sig.IssuerKeyID;
import org.bouncycastle.bcpg.sig.SignatureCreationTime;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;
import org.bouncycastle.util.test.SimpleTest;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Array;

public class SignaturePacketTest extends SimpleTest
{
@Override
public String getName()
{
return SignaturePacketTest.class.getSimpleName();
}

@Override
public void performTest()
throws Exception
{
testParseV6Signature();
testParseV4Ed25519LegacySignature();
}

private void testParseV6Signature()
throws IOException
{
// Hex-encoded OpenPGP v6 signature packet
// Extracted from https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-13.html#name-sample-inline-signed-messag
byte[] encSigPacket = Hex.decode("c29806011b0a0000002905826398a363222106cb186c4f0609a697e4d52dfa6c722b0c1f1e27c18a56708f6525ec27bad9acc90000000069362076495f50218890f7f5e2ee3c1822514f70500f551d86e5c921e404e34a53fbac27d06fb80aa8fc5bcb16e19631b280740f9ea6aed5e073ad00f9415a653c40e77a6ae77e692ba71d069a109fa24c58cfd8e316d0a06b34ad9acb8e5c5f521501");
// Issuer of the message
byte[] issuerFP = Hex.decode("CB186C4F0609A697E4D52DFA6C722B0C1F1E27C18A56708F6525EC27BAD9ACC9");
// Salt used to generate the signature
byte[] salt = Hex.decode("76495F50218890F7F5E2EE3C1822514F70500F551D86E5C921E404E34A53FBAC");

ByteArrayInputStream bIn = new ByteArrayInputStream(encSigPacket);
BCPGInputStream pIn = new BCPGInputStream(bIn);
SignaturePacket sig = (SignaturePacket) pIn.readPacket();

isEquals("SignaturePacket version mismatch",
SignaturePacket.VERSION_6, sig.getVersion());
isEquals("SignaturePacket signature type mismatch",
PGPSignature.CANONICAL_TEXT_DOCUMENT, sig.getSignatureType());
isEquals("SignaturePacket key algorithm mismatch",
PublicKeyAlgorithmTags.Ed25519, sig.getKeyAlgorithm());
isEquals("SignaturePacket hash algorithm mismatch",
HashAlgorithmTags.SHA512, sig.getHashAlgorithm());
isTrue("SignaturePacket salt mismatch",
Arrays.areEqual(salt, sig.getSalt()));
// hashed subpackets
isEquals("SignaturePacket number of hashed packets mismatch",
2, sig.getHashedSubPackets().length);
SignatureCreationTime creationTimeSubpacket = (SignatureCreationTime) sig.getHashedSubPackets()[0];
isEquals("SignaturePacket signature creation time mismatch",
1670947683000L, creationTimeSubpacket.getTime().getTime());
IssuerFingerprint issuerSubpacket = (IssuerFingerprint) sig.getHashedSubPackets()[1];
isTrue("SignaturePacket issuer fingerprint mismatch",
Arrays.areEqual(issuerFP, issuerSubpacket.getFingerprint()));
// unhashed subpackets
isEquals("SignaturePacket number of unhashed packets mismatch",
0, sig.getUnhashedSubPackets().length);

// v6 Ed25519 signatures (not LEGACY) do not use MPI encoding for the raw signature
// but rather encode into octet strings
isTrue("MPI encoding MUST be null",
sig.getSignature() == null);
isTrue("Octet string encoding mismatch", Arrays.areEqual(
Hex.decode("27d06fb80aa8fc5bcb16e19631b280740f9ea6aed5e073ad00f9415a653c40e77a6ae77e692ba71d069a109fa24c58cfd8e316d0a06b34ad9acb8e5c5f521501"),
sig.getSignatureBytes()));

ByteArrayOutputStream bOut = new ByteArrayOutputStream();
BCPGOutputStream pOut = new BCPGOutputStream(bOut, true);
sig.encode(pOut);
pOut.close();

isTrue("SignaturePacket encoding mismatch",
Arrays.areEqual(encSigPacket, bOut.toByteArray()));
}

private void testParseV4Ed25519LegacySignature() throws IOException {
// Hex-encoded v4 test signature
// see https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-13.html#name-sample-v4-ed25519legacy-sig
byte[] encSigPacket = Hex.decode("885e040016080006050255f95f95000a09108cfde12197965a9af62200ff56f90cca98e2102637bd983fdb16c131dfd27ed82bf4dde5606e0d756aed33660100d09c4fa11527f038e0f57f2201d82f2ea2c9033265fa6ceb489e854bae61b404");
ByteArrayInputStream bIn = new ByteArrayInputStream(encSigPacket);
BCPGInputStream pIn = new BCPGInputStream(bIn);
SignaturePacket sig = (SignaturePacket) pIn.readPacket();

isEquals("SignaturePacket version mismatch",
SignaturePacket.VERSION_4, sig.getVersion());
isEquals("SignaturePacket signature type mismatch",
PGPSignature.BINARY_DOCUMENT, sig.getSignatureType());
isEquals("SignaturePacket public key algorithm mismatch",
PublicKeyAlgorithmTags.EDDSA_LEGACY, sig.getKeyAlgorithm());
isEquals("SignaturePacket hash algorithm mismatch",
HashAlgorithmTags.SHA256, sig.getHashAlgorithm());
isEquals("SignaturePacket number of hashed subpackets mismatch",
1, sig.getHashedSubPackets().length);
SignatureCreationTime creationTimeSubpacket = (SignatureCreationTime) sig.getHashedSubPackets()[0];
isEquals("SignaturePacket creationTime mismatch",
1442406293000L, creationTimeSubpacket.getTime().getTime());
isEquals("SignaturePacket number of unhashed subpackets mismatch",
1, sig.getUnhashedSubPackets().length);
IssuerKeyID issuerKeyID = (IssuerKeyID) sig.getUnhashedSubPackets()[0];
isEquals("SignaturePacket issuer key-id mismatch",
-8287220204898461030L, issuerKeyID.getKeyID());

// EDDSA_LEGACY uses MPI encoding for the raw signature value
MPInteger[] mpInts = sig.getSignature();
isEquals("Signature MPI encoding mismatch",
2, mpInts.length);
isTrue("MPI encoding in signatureBytes field mismatch", Arrays.areEqual(
Hex.decode("00ff56f90cca98e2102637bd983fdb16c131dfd27ed82bf4dde5606e0d756aed33660100d09c4fa11527f038e0f57f2201d82f2ea2c9033265fa6ceb489e854bae61b404"),
sig.getSignatureBytes()));

// v4 signatures do not have salt
isTrue("Salt MUST be null", sig.getSalt() == null);

ByteArrayOutputStream bOut = new ByteArrayOutputStream();
BCPGOutputStream pOut = new BCPGOutputStream(bOut, false);
sig.encode(pOut);
pOut.close();

isTrue("SignaturePacket encoding mismatch",
Arrays.areEqual(encSigPacket, bOut.toByteArray()));
}

public static void main(String[] args)
{
runTest(new SignaturePacketTest());
}
}

0 comments on commit eaadfc0

Please sign in to comment.