Skip to content

Commit f81a5dc

Browse files
committed
Add tests for asymmetric v6 message decryption
1 parent 42c757b commit f81a5dc

File tree

2 files changed

+179
-5
lines changed

2 files changed

+179
-5
lines changed

pg/src/test/java/org/bouncycastle/bcpg/test/EncryptedMessagePacketTest.java

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,59 @@
88
import org.bouncycastle.bcpg.PublicKeyEncSessionPacket;
99
import org.bouncycastle.bcpg.SymmetricEncIntegrityPacket;
1010
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
11+
import org.bouncycastle.jce.provider.BouncyCastleProvider;
12+
import org.bouncycastle.openpgp.PGPEncryptedDataList;
13+
import org.bouncycastle.openpgp.PGPException;
14+
import org.bouncycastle.openpgp.PGPLiteralData;
15+
import org.bouncycastle.openpgp.PGPObjectFactory;
16+
import org.bouncycastle.openpgp.PGPPadding;
17+
import org.bouncycastle.openpgp.PGPPrivateKey;
18+
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
19+
import org.bouncycastle.openpgp.PGPSecretKey;
20+
import org.bouncycastle.openpgp.PGPSecretKeyRing;
21+
import org.bouncycastle.openpgp.bc.BcPGPObjectFactory;
22+
import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
23+
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
24+
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory;
25+
import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
26+
import org.bouncycastle.util.Arrays;
1127
import org.bouncycastle.util.encoders.Hex;
28+
import org.bouncycastle.util.io.Streams;
1229

1330
import java.io.ByteArrayInputStream;
1431
import java.io.IOException;
32+
import java.io.InputStream;
1533
import java.nio.charset.StandardCharsets;
1634

1735
public class EncryptedMessagePacketTest
1836
extends AbstractPacketTest
1937
{
38+
// https://www.rfc-editor.org/rfc/rfc9580.html#name-sample-version-6-secret-key
39+
final String V6_SECRET_KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
40+
"\n" +
41+
"xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB\n" +
42+
"exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ\n" +
43+
"BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6\n" +
44+
"2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh\n" +
45+
"RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe\n" +
46+
"7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/\n" +
47+
"LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG\n" +
48+
"GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6\n" +
49+
"2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE\n" +
50+
"M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr\n" +
51+
"k0mXubZvyl4GBg==\n" +
52+
"-----END PGP PRIVATE KEY BLOCK-----\n";
53+
54+
// https://www.rfc-editor.org/rfc/rfc9580.html#name-complete-x25519-aead-ocb-en
55+
final String X25519_AEAD_OCB_MESSAGE = "-----BEGIN PGP MESSAGE-----\n" +
56+
"\n" +
57+
"wV0GIQYSyD8ecG9jCP4VGkF3Q6HwM3kOk+mXhIjR2zeNqZMIhRmHzxjV8bU/gXzO\n" +
58+
"WgBM85PMiVi93AZfJfhK9QmxfdNnZBjeo1VDeVZheQHgaVf7yopqR6W1FT6NOrfS\n" +
59+
"aQIHAgZhZBZTW+CwcW1g4FKlbExAf56zaw76/prQoN+bAzxpohup69LA7JW/Vp0l\n" +
60+
"yZnuSj3hcFj0DfqLTGgr4/u717J+sPWbtQBfgMfG9AOIwwrUBqsFE9zW+f1zdlYo\n" +
61+
"bhF30A+IitsxxA==\n" +
62+
"-----END PGP MESSAGE-----";
63+
2064
@Override
2165
public String getName()
2266
{
@@ -27,9 +71,125 @@ public String getName()
2771
public void performTest()
2872
throws Exception
2973
{
74+
testX25519AEADOCBTestVector_bc();
75+
testX25519AEADOCBTestVector_jce();
76+
testPKESK6SEIPD2FromTestVector();
3077
testPKESK6SEIPD2();
3178
}
3279

80+
private void testPKESK6SEIPD2FromTestVector()
81+
throws IOException, PGPException
82+
{
83+
// https://www.rfc-editor.org/rfc/rfc9580.html#name-sample-version-6-public-key
84+
byte[] pkesk = Hex.decode("c15d06210612c83f" +
85+
"1e706f6308fe151a" +
86+
"417743a1f033790e" +
87+
"93e9978488d1db37" +
88+
"8da99308851987cf" +
89+
"18d5f1b53f817cce" +
90+
"5a004cf393cc8958" +
91+
"bddc065f25f84af5" +
92+
"09b17dd3676418de" +
93+
"a355437956617901" +
94+
"e06957fbca8a6a47" +
95+
"a5b5153e8d3ab7");
96+
97+
// https://www.rfc-editor.org/rfc/rfc9580.html#name-sample-v2-seipd-packet
98+
byte[] seipd = Hex.decode("d269020702066164" +
99+
"16535be0b0716d60" +
100+
"e052a56c4c407f9e" +
101+
"b36b0efafe9ad0a0" +
102+
"df9b033c69a21ba9" +
103+
"ebd2c0ec95bf569d" +
104+
"25c999ee4a3de170" +
105+
"58f40dfa8b4c682b" +
106+
"e3fbbbd7b27eb0f5" +
107+
"9bb5005f80c7c6f4" +
108+
"0388c30ad406ab05" +
109+
"13dcd6f9fd737656" +
110+
"286e1177d00f888a" +
111+
"db31c4");
112+
113+
ByteArrayInputStream bIn = new ByteArrayInputStream(V6_SECRET_KEY.getBytes(StandardCharsets.UTF_8));
114+
ArmoredInputStream aIn = new ArmoredInputStream(bIn);
115+
BCPGInputStream pIn = new BCPGInputStream(aIn);
116+
PGPObjectFactory objFac = new BcPGPObjectFactory(pIn);
117+
PGPSecretKeyRing secretKeys = (PGPSecretKeyRing) objFac.nextObject();
118+
119+
bIn = new ByteArrayInputStream(Arrays.concatenate(pkesk, seipd));
120+
pIn = new BCPGInputStream(bIn);
121+
objFac = new BcPGPObjectFactory(pIn);
122+
PGPEncryptedDataList encList = (PGPEncryptedDataList) objFac.nextObject();
123+
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) encList.get(0);
124+
PGPSecretKey decKey = secretKeys.getSecretKey(encData.getKeyIdentifier());
125+
PGPPrivateKey privKey = decKey.extractPrivateKey(null);
126+
PublicKeyDataDecryptorFactory decryptor = new BcPublicKeyDataDecryptorFactory(privKey);
127+
InputStream in = encData.getDataStream(decryptor);
128+
objFac = new BcPGPObjectFactory(in);
129+
PGPLiteralData literalData = (PGPLiteralData) objFac.nextObject();
130+
byte[] msg = Streams.readAll(literalData.getDataStream());
131+
isEncodingEqual("Hello, world!".getBytes(StandardCharsets.UTF_8), msg);
132+
PGPPadding padding = (PGPPadding) objFac.nextObject();
133+
isEncodingEqual(Hex.decode("c5a293072991628147d72c8f86b7"), padding.getPadding());
134+
}
135+
136+
private void testX25519AEADOCBTestVector_bc()
137+
throws IOException, PGPException
138+
{
139+
ByteArrayInputStream bIn = new ByteArrayInputStream(V6_SECRET_KEY.getBytes(StandardCharsets.UTF_8));
140+
ArmoredInputStream aIn = new ArmoredInputStream(bIn);
141+
BCPGInputStream pIn = new BCPGInputStream(aIn);
142+
PGPObjectFactory objFac = new BcPGPObjectFactory(pIn);
143+
PGPSecretKeyRing secretKeys = (PGPSecretKeyRing) objFac.nextObject();
144+
145+
bIn = new ByteArrayInputStream(X25519_AEAD_OCB_MESSAGE.getBytes());
146+
aIn = new ArmoredInputStream(bIn);
147+
pIn = new BCPGInputStream(aIn);
148+
objFac = new BcPGPObjectFactory(pIn);
149+
PGPEncryptedDataList encList = (PGPEncryptedDataList) objFac.nextObject();
150+
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) encList.get(0);
151+
PGPSecretKey decKey = secretKeys.getSecretKey(encData.getKeyIdentifier());
152+
PGPPrivateKey privKey = decKey.extractPrivateKey(null);
153+
PublicKeyDataDecryptorFactory decryptor = new BcPublicKeyDataDecryptorFactory(privKey);
154+
InputStream in = encData.getDataStream(decryptor);
155+
objFac = new BcPGPObjectFactory(in);
156+
PGPLiteralData literalData = (PGPLiteralData) objFac.nextObject();
157+
byte[] plaintext = Streams.readAll(literalData.getDataStream());
158+
isEncodingEqual("Hello, world!".getBytes(StandardCharsets.UTF_8), plaintext);
159+
PGPPadding padding = (PGPPadding) objFac.nextObject();
160+
isEncodingEqual(Hex.decode("c5a293072991628147d72c8f86b7"), padding.getPadding());
161+
}
162+
163+
private void testX25519AEADOCBTestVector_jce()
164+
throws IOException, PGPException
165+
{
166+
ByteArrayInputStream bIn = new ByteArrayInputStream(V6_SECRET_KEY.getBytes(StandardCharsets.UTF_8));
167+
ArmoredInputStream aIn = new ArmoredInputStream(bIn);
168+
BCPGInputStream pIn = new BCPGInputStream(aIn);
169+
PGPObjectFactory objFac = new JcaPGPObjectFactory(pIn);
170+
PGPSecretKeyRing secretKeys = (PGPSecretKeyRing) objFac.nextObject();
171+
172+
bIn = new ByteArrayInputStream(X25519_AEAD_OCB_MESSAGE.getBytes());
173+
aIn = new ArmoredInputStream(bIn);
174+
pIn = new BCPGInputStream(aIn);
175+
objFac = new JcaPGPObjectFactory(pIn);
176+
PGPEncryptedDataList encList = (PGPEncryptedDataList) objFac.nextObject();
177+
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) encList.get(0);
178+
PGPSecretKey decKey = secretKeys.getSecretKey(encData.getKeyIdentifier());
179+
PGPPrivateKey privKey = decKey.extractPrivateKey(null);
180+
PublicKeyDataDecryptorFactory decryptor = new JcePublicKeyDataDecryptorFactoryBuilder()
181+
.setProvider(new BouncyCastleProvider())
182+
.setContentProvider(new BouncyCastleProvider())
183+
.build(privKey);
184+
InputStream in = encData.getDataStream(decryptor);
185+
objFac = new JcaPGPObjectFactory(in);
186+
PGPLiteralData literalData = (PGPLiteralData) objFac.nextObject();
187+
byte[] plaintext = Streams.readAll(literalData.getDataStream());
188+
isEncodingEqual("Hello, world!".getBytes(StandardCharsets.UTF_8), plaintext);
189+
PGPPadding padding = (PGPPadding) objFac.nextObject();
190+
isEncodingEqual(Hex.decode("c5a293072991628147d72c8f86b7"), padding.getPadding());
191+
}
192+
33193
private void testPKESK6SEIPD2()
34194
throws IOException
35195
{

pg/src/test/java/org/bouncycastle/openpgp/test/PGPv6MessageDecryptionTest.java

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,25 @@
11
package org.bouncycastle.openpgp.test;
22

3-
import org.bouncycastle.asn1.cms.KEKIdentifier;
43
import org.bouncycastle.bcpg.ArmoredInputStream;
54
import org.bouncycastle.bcpg.BCPGInputStream;
65
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
76
import org.bouncycastle.bcpg.PublicKeyEncSessionPacket;
8-
import org.bouncycastle.bcpg.PublicKeyPacket;
97
import org.bouncycastle.openpgp.PGPEncryptedDataList;
8+
import org.bouncycastle.openpgp.PGPException;
9+
import org.bouncycastle.openpgp.PGPLiteralData;
1010
import org.bouncycastle.openpgp.PGPObjectFactory;
11+
import org.bouncycastle.openpgp.PGPPrivateKey;
1112
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
13+
import org.bouncycastle.openpgp.PGPSecretKey;
1214
import org.bouncycastle.openpgp.PGPSecretKeyRing;
1315
import org.bouncycastle.openpgp.bc.BcPGPObjectFactory;
14-
import org.bouncycastle.util.encoders.Hex;
16+
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
17+
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory;
18+
import org.bouncycastle.util.io.Streams;
1519

1620
import java.io.ByteArrayInputStream;
1721
import java.io.IOException;
22+
import java.io.InputStream;
1823
import java.nio.charset.StandardCharsets;
1924

2025
public class PGPv6MessageDecryptionTest
@@ -34,7 +39,7 @@ public void performTest()
3439
}
3540

3641
private void decryptMessageEncryptedUsingPKESKv6()
37-
throws IOException
42+
throws IOException, PGPException
3843
{
3944
String key = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
4045
"\n" +
@@ -78,7 +83,16 @@ private void decryptMessageEncryptedUsingPKESKv6()
7883

7984
isEquals(PublicKeyEncSessionPacket.VERSION_6, encData.getVersion());
8085
isEquals(PublicKeyAlgorithmTags.X25519, encData.getAlgorithm());
81-
86+
PGPSecretKey decryptionKey = secretKeys.getSecretKey(encData.getKeyIdentifier());
87+
isNotNull("Decryption key MUST be identifiable", decryptionKey);
88+
PGPPrivateKey privateKey = decryptionKey.extractPrivateKey(null);
89+
PublicKeyDataDecryptorFactory decryptor = new BcPublicKeyDataDecryptorFactory(privateKey);
90+
InputStream decrypted = encData.getDataStream(decryptor);
91+
PGPObjectFactory decFac = new BcPGPObjectFactory(decrypted);
92+
PGPLiteralData lit = (PGPLiteralData) decFac.nextObject();
93+
isEncodingEqual(
94+
"Hello World :)".getBytes(StandardCharsets.UTF_8),
95+
Streams.readAll(lit.getDataStream()));
8296
}
8397

8498
public static void main(String[] args)

0 commit comments

Comments
 (0)