Skip to content

Commit d97ecd5

Browse files
committed
Add tests for v6 OPS and SignaturePackets
1 parent fc8adcc commit d97ecd5

File tree

4 files changed

+667
-0
lines changed

4 files changed

+667
-0
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package org.bouncycastle.bcpg.test;
2+
3+
import junit.extensions.TestSetup;
4+
import junit.framework.Test;
5+
import junit.framework.TestCase;
6+
import junit.framework.TestSuite;
7+
import org.bouncycastle.jce.provider.BouncyCastleProvider;
8+
import org.bouncycastle.test.PrintTestResult;
9+
import org.bouncycastle.util.test.SimpleTestResult;
10+
11+
import java.security.Security;
12+
13+
public class AllTests
14+
extends TestCase {
15+
16+
public void testPacketParsing()
17+
{
18+
Security.addProvider(new BouncyCastleProvider());
19+
20+
org.bouncycastle.util.test.Test[] tests = new org.bouncycastle.util.test.Test[] {
21+
new SignaturePacketTest(),
22+
new OnePassSignaturePacketTest(),
23+
new OpenPgpMessageTest()
24+
};
25+
26+
for (int i = 0; i != tests.length; i++)
27+
{
28+
SimpleTestResult result = (SimpleTestResult)tests[i].perform();
29+
30+
if (!result.isSuccessful())
31+
{
32+
fail(result.toString());
33+
}
34+
}
35+
}
36+
37+
38+
public static void main(String[] args)
39+
{
40+
PrintTestResult.printResult(junit.textui.TestRunner.run(suite()));
41+
}
42+
43+
public static Test suite()
44+
{
45+
TestSuite suite = new TestSuite("OpenPGP Packet Tests");
46+
47+
suite.addTestSuite(AllTests.class);
48+
49+
return new BCPacketTests(suite);
50+
}
51+
52+
static class BCPacketTests
53+
extends TestSetup
54+
{
55+
public BCPacketTests(Test test)
56+
{
57+
super(test);
58+
}
59+
60+
protected void setUp()
61+
{
62+
Security.addProvider(new BouncyCastleProvider());
63+
}
64+
65+
protected void tearDown()
66+
{
67+
Security.removeProvider("BC");
68+
}
69+
}
70+
}
Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
package org.bouncycastle.bcpg.test;
2+
3+
import org.bouncycastle.bcpg.*;
4+
import org.bouncycastle.openpgp.PGPSignature;
5+
import org.bouncycastle.util.encoders.Hex;
6+
7+
import java.io.ByteArrayInputStream;
8+
import java.io.ByteArrayOutputStream;
9+
import java.io.IOException;
10+
import java.security.SecureRandom;
11+
12+
public class OnePassSignaturePacketTest
13+
extends AbstractPacketTest
14+
{
15+
16+
// Parse v6 OPS packet and compare its values to a known-good test vector
17+
private void testParseV6OnePassSignaturePacket() throws IOException {
18+
// Version 6 OnePassSignature packet
19+
// extracted from https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-13.html#name-sample-inline-signed-messag
20+
byte[] encOPS = Hex.decode("c44606010a1b2076495f50218890f7f5e2ee3c1822514f70500f551d86e5c921e404e34a53fbaccb186c4f0609a697e4d52dfa6c722b0c1f1e27c18a56708f6525ec27bad9acc901");
21+
// Issuer of the message
22+
byte[] issuerFp = Hex.decode("CB186C4F0609A697E4D52DFA6C722B0C1F1E27C18A56708F6525EC27BAD9ACC9");
23+
// Salt used to generate the signature
24+
byte[] salt = Hex.decode("76495F50218890F7F5E2EE3C1822514F70500F551D86E5C921E404E34A53FBAC");
25+
26+
ByteArrayInputStream bIn = new ByteArrayInputStream(encOPS);
27+
BCPGInputStream pIn = new BCPGInputStream(bIn);
28+
29+
// Parse and compare the OnePassSignature packet
30+
OnePassSignaturePacket ops = (OnePassSignaturePacket) pIn.readPacket();
31+
isEquals("OPS packet MUST be of version 6",
32+
OnePassSignaturePacket.VERSION_6, ops.getVersion());
33+
isEncodingEqual("OPS packet issuer fingerprint mismatch",
34+
issuerFp, ops.getFingerprint());
35+
isTrue("OPS packet key-ID mismatch",
36+
// key-ID are the first 8 octets of the fingerprint
37+
Hex.toHexString(issuerFp).startsWith(Long.toHexString(ops.getKeyID())));
38+
isEncodingEqual("OPS packet salt mismatch",
39+
salt, ops.getSalt());
40+
isTrue("OPS packet isContaining mismatch",
41+
ops.isContaining());
42+
43+
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
44+
BCPGOutputStream pOut = new BCPGOutputStream(bOut, true);
45+
ops.encode(pOut);
46+
pOut.close();
47+
48+
isEncodingEqual("OPS Packet encoding mismatch", encOPS, bOut.toByteArray());
49+
}
50+
51+
private void roundtripV3Packet() throws IOException {
52+
OnePassSignaturePacket before = new OnePassSignaturePacket(
53+
PGPSignature.BINARY_DOCUMENT,
54+
HashAlgorithmTags.SHA256,
55+
PublicKeyAlgorithmTags.RSA_GENERAL,
56+
123L,
57+
true);
58+
59+
isEquals("Expected OPS version 3",
60+
OnePassSignaturePacket.VERSION_3, before.getVersion());
61+
isEquals("Signature type mismatch",
62+
PGPSignature.BINARY_DOCUMENT, before.getSignatureType());
63+
isEquals("Hash Algorithm mismatch",
64+
HashAlgorithmTags.SHA256, before.getHashAlgorithm());
65+
isEquals("Pulic Key Algorithm mismatch",
66+
PublicKeyAlgorithmTags.RSA_GENERAL, before.getKeyAlgorithm());
67+
isEquals("Key-ID mismatch",
68+
123L, before.getKeyID());
69+
isFalse("OPS is expected to be non-containing",
70+
before.isContaining());
71+
isNull("OPS v3 MUST NOT have a fingerprint",
72+
before.getFingerprint());
73+
isNull("OPS v3 MUST NOT have salt",
74+
before.getSalt());
75+
76+
for (boolean newTypeIdFormat : new boolean[] {true, false}) {
77+
// round-trip the packet by encoding and decoding it
78+
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
79+
BCPGOutputStream pOut = new BCPGOutputStream(bOut, newTypeIdFormat);
80+
before.encode(pOut);
81+
pOut.close();
82+
ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray());
83+
BCPGInputStream pIn = new BCPGInputStream(bIn);
84+
OnePassSignaturePacket after = (OnePassSignaturePacket) pIn.readPacket();
85+
86+
isEquals("round-tripped OPS version mismatch",
87+
before.getVersion(), after.getVersion());
88+
isEquals("round-tripped OPS signature type mismatch",
89+
before.getSignatureType(), after.getSignatureType());
90+
isEquals("round-tripped OPS hash algorithm mismatch",
91+
before.getHashAlgorithm(), after.getHashAlgorithm());
92+
isEquals("round-tripped OPS public key algorithm mismatch",
93+
before.getKeyAlgorithm(), after.getKeyAlgorithm());
94+
isEquals("round-tripped OPS key-id mismatch",
95+
before.getKeyID(), after.getKeyID());
96+
isEquals("round-tripped OPS nested flag mismatch",
97+
before.isContaining(), after.isContaining());
98+
isNull("round-tripped OPS v3 MUST NOT have fingerprint",
99+
after.getFingerprint());
100+
isNull("round-tripped OPS v3 MUST NOT have salt",
101+
after.getSalt());
102+
103+
isEncodingEqual("Packet encoding mismatch",
104+
before, after);
105+
}
106+
}
107+
108+
private void roundtripV6Packet() throws IOException {
109+
byte[] salt = new byte[32];
110+
byte[] fingerprint = Hex.decode("CB186C4F0609A697E4D52DFA6C722B0C1F1E27C18A56708F6525EC27BAD9ACC9");
111+
long keyID = ((fingerprint[0] & 0xffL) << 56) |
112+
((fingerprint[1] & 0xffL) << 48) |
113+
((fingerprint[2] & 0xffL) << 40) |
114+
((fingerprint[3] & 0xffL) << 32) |
115+
((fingerprint[4] & 0xffL) << 24) |
116+
((fingerprint[5] & 0xffL) << 16) |
117+
((fingerprint[6] & 0xffL) << 8) |
118+
((fingerprint[7] & 0xffL));
119+
120+
new SecureRandom().nextBytes(salt);
121+
OnePassSignaturePacket before = new OnePassSignaturePacket(
122+
PGPSignature.CANONICAL_TEXT_DOCUMENT,
123+
HashAlgorithmTags.SHA512,
124+
PublicKeyAlgorithmTags.EDDSA_LEGACY,
125+
salt,
126+
fingerprint,
127+
false);
128+
129+
isEquals("Expected OPS version 6",
130+
OnePassSignaturePacket.VERSION_6, before.getVersion());
131+
isEquals("Signature type mismatch",
132+
PGPSignature.CANONICAL_TEXT_DOCUMENT, before.getSignatureType());
133+
isEquals("Hash algorithm mismatch",
134+
HashAlgorithmTags.SHA512, before.getHashAlgorithm());
135+
isEquals("Public key algorithm mismatch",
136+
PublicKeyAlgorithmTags.EDDSA_LEGACY, before.getKeyAlgorithm());
137+
isEncodingEqual("Salt mismatch",
138+
salt, before.getSalt());
139+
isEncodingEqual("Fingerprint mismatch",
140+
fingerprint, before.getFingerprint());
141+
isEquals("Derived key-ID mismatch",
142+
keyID, before.getKeyID());
143+
isTrue("non-nested OPS is expected to be containing",
144+
before.isContaining());
145+
146+
for (boolean newTypeIdFormat : new boolean[] {true, false}) {
147+
// round-trip the packet by encoding and decoding it
148+
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
149+
BCPGOutputStream pOut = new BCPGOutputStream(bOut, newTypeIdFormat);
150+
before.encode(pOut);
151+
pOut.close();
152+
ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray());
153+
BCPGInputStream pIn = new BCPGInputStream(bIn);
154+
OnePassSignaturePacket after = (OnePassSignaturePacket) pIn.readPacket();
155+
156+
isEquals("round-tripped OPS version mismatch",
157+
before.getVersion(), after.getVersion());
158+
isEquals("round-tripped OPS signature type mismatch",
159+
before.getSignatureType(), after.getSignatureType());
160+
isEquals("round-tripped OPS hash algorithm mismatch",
161+
before.getHashAlgorithm(), after.getHashAlgorithm());
162+
isEquals("round-tripped OPS public key algorithm mismatch",
163+
before.getKeyAlgorithm(), after.getKeyAlgorithm());
164+
isEquals("round-tripped OPS key-id mismatch",
165+
before.getKeyID(), after.getKeyID());
166+
isEquals("round-tripped OPS nested flag mismatch",
167+
before.isContaining(), after.isContaining());
168+
isEncodingEqual("round-tripped OPS fingerprint mismatch",
169+
before.getFingerprint(), after.getFingerprint());
170+
isEncodingEqual("round-tripped OPS salt mismatch",
171+
before.getSalt(), after.getSalt());
172+
173+
isEncodingEqual(before, after);
174+
}
175+
}
176+
177+
private void roundtripV6PacketWithZeroLengthSalt() throws IOException {
178+
byte[] salt = new byte[0];
179+
byte[] fingerprint = Hex.decode("CB186C4F0609A697E4D52DFA6C722B0C1F1E27C18A56708F6525EC27BAD9ACC9");
180+
181+
OnePassSignaturePacket before = new OnePassSignaturePacket(
182+
PGPSignature.CANONICAL_TEXT_DOCUMENT,
183+
HashAlgorithmTags.SHA512,
184+
PublicKeyAlgorithmTags.EDDSA_LEGACY,
185+
salt,
186+
fingerprint,
187+
false);
188+
189+
isEncodingEqual("Salt mismatch",
190+
salt, before.getSalt());
191+
192+
for (boolean newTypeIdFormat : new boolean[] {true, false}) {
193+
// round-trip the packet by encoding and decoding it
194+
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
195+
BCPGOutputStream pOut = new BCPGOutputStream(bOut, newTypeIdFormat);
196+
before.encode(pOut);
197+
pOut.close();
198+
ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray());
199+
BCPGInputStream pIn = new BCPGInputStream(bIn);
200+
OnePassSignaturePacket after = (OnePassSignaturePacket) pIn.readPacket();
201+
202+
isEquals("round-tripped OPS version mismatch",
203+
before.getVersion(), after.getVersion());
204+
isEquals("round-tripped OPS signature type mismatch",
205+
before.getSignatureType(), after.getSignatureType());
206+
isEquals("round-tripped OPS hash algorithm mismatch",
207+
before.getHashAlgorithm(), after.getHashAlgorithm());
208+
isEquals("round-tripped OPS public key algorithm mismatch",
209+
before.getKeyAlgorithm(), after.getKeyAlgorithm());
210+
isEquals("round-tripped OPS key-id mismatch",
211+
before.getKeyID(), after.getKeyID());
212+
isEquals("round-tripped OPS nested flag mismatch",
213+
before.isContaining(), after.isContaining());
214+
isEncodingEqual("round-tripped OPS fingerprint mismatch",
215+
before.getFingerprint(), after.getFingerprint());
216+
isEncodingEqual("round-tripped OPS salt mismatch",
217+
before.getSalt(), after.getSalt());
218+
}
219+
}
220+
221+
private void parsingOfPacketWithUnknownVersionFails() {
222+
// Version 0x99 OnePassSignature packet
223+
byte[] encOPS = Hex.decode("c44699010a1b2076495f50218890f7f5e2ee3c1822514f70500f551d86e5c921e404e34a53fbaccb186c4f0609a697e4d52dfa6c722b0c1f1e27c18a56708f6525ec27bad9acc901");
224+
225+
ByteArrayInputStream bIn = new ByteArrayInputStream(encOPS);
226+
BCPGInputStream pIn = new BCPGInputStream(bIn);
227+
228+
try {
229+
pIn.readPacket();
230+
fail("Expected UnsupportedPacketVersionException");
231+
} catch (IOException e) {
232+
fail("Expected UnsupportedPacketVersionException", e);
233+
} catch (UnsupportedPacketVersionException e) {
234+
// expected
235+
}
236+
}
237+
238+
private void parsingOfPacketWithTruncatedFingerprintFails() {
239+
// Version 6 OnePassSignature packet with truncated fingerprint field (20 bytes instead of 32)
240+
// This error would happen, if a v6 OPS packet was generated with a v4 fingerprint.
241+
// extracted from https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-13.html#name-sample-inline-signed-messag
242+
byte[] encOPS = Hex.decode("c44606010a1b2076495f50218890f7f5e2ee3c1822514f70500f551d86e5c921e404e34a53fbaccb186c4f0609a697e4d52dfa6c722b0c1f1e27c101");
243+
244+
ByteArrayInputStream bIn = new ByteArrayInputStream(encOPS);
245+
BCPGInputStream pIn = new BCPGInputStream(bIn);
246+
247+
try {
248+
pIn.readPacket();
249+
fail("Expected IOException");
250+
} catch (IOException e) {
251+
// expected
252+
}
253+
}
254+
255+
@Override
256+
public String getName() {
257+
return "OnePassSignaturePacketTest";
258+
}
259+
260+
@Override
261+
public void performTest() throws Exception {
262+
testParseV6OnePassSignaturePacket();
263+
roundtripV3Packet();
264+
roundtripV6Packet();
265+
parsingOfPacketWithUnknownVersionFails();
266+
parsingOfPacketWithTruncatedFingerprintFails();
267+
roundtripV6PacketWithZeroLengthSalt();
268+
}
269+
270+
public static void main(String[] args) {
271+
runTest(new OnePassSignaturePacketTest());
272+
}
273+
}

0 commit comments

Comments
 (0)