Skip to content

Commit c1db09f

Browse files
committed
Merge branch 'pr-1699-1695-1702' into 'main'
majority of PGP pull requests See merge request root/bc-java!15
2 parents 55d09fd + c6c45d3 commit c1db09f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+2703
-300
lines changed

pg/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,5 +93,6 @@ artifacts {
9393
test {
9494
forkEvery = 1;
9595
maxParallelForks = 8;
96+
maxHeapSize = "3g";
9697
}
9798

pg/src/main/java/org/bouncycastle/bcpg/AEADEncDataPacket.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@
55
import org.bouncycastle.util.Arrays;
66

77
/**
8-
* Packet representing AEAD encrypted data. At the moment this appears to exist in the following
8+
* Packet representing non-standard, LibrePGP OCB (AEAD) encrypted data. At the moment this appears to exist in the following
99
* expired draft only, but it's appearing despite this.
10+
* For standardized, interoperable OpenPGP AEAD encrypted data, see {@link SymmetricEncIntegrityPacket} of version
11+
* {@link SymmetricEncIntegrityPacket#VERSION_2}.
1012
*
11-
* @ref https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-rfc4880bis-04#section-5.16
13+
* @see <a href="https://www.ietf.org/archive/id/draft-koch-librepgp-00.html#name-ocb-encrypted-data-packet-t">
14+
* LibrePGP - OCB Encrypted Data Packet</a>
1215
*/
1316
public class AEADEncDataPacket
1417
extends InputStreamPacket
@@ -37,7 +40,7 @@ public AEADEncDataPacket(BCPGInputStream in,
3740
version = (byte)in.read();
3841
if (version != VERSION_1)
3942
{
40-
throw new IllegalArgumentException("wrong AEAD packet version: " + version);
43+
throw new UnsupportedPacketVersionException("wrong AEAD packet version: " + version);
4144
}
4245

4346
algorithm = (byte)in.read();

pg/src/main/java/org/bouncycastle/bcpg/PublicKeyPacket.java

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public class PublicKeyPacket
1313
{
1414
public static final int VERSION_3 = 3;
1515
public static final int VERSION_4 = 4;
16+
public static final int LIBREPGP_5 = 5;
1617
public static final int VERSION_6 = 6;
1718

1819
private int version;
@@ -43,6 +44,26 @@ public class PublicKeyPacket
4344
this(keyTag, in, false);
4445
}
4546

47+
/**
48+
* Parse a {@link PublicKeyPacket} or {@link PublicSubkeyPacket} from an OpenPGP {@link BCPGInputStream}.
49+
* If <pre>packetTypeID</pre> is {@link #PUBLIC_KEY}, the packet is a primary key.
50+
* If instead it is {@link #PUBLIC_SUBKEY}, it is a subkey packet.
51+
* If <pre>newPacketFormat</pre> is true, the packet format is remembered as {@link PacketFormat#CURRENT},
52+
* otherwise as {@link PacketFormat#LEGACY}.
53+
* @param keyTag packet type ID
54+
* @param in packet input stream
55+
* @param newPacketFormat packet format
56+
* @throws IOException if the key packet cannot be parsed
57+
*
58+
* @see <a href="https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-13.html#name-version-3-public-keys">
59+
* C-R - Version 3 Public Keys</a>
60+
* @see <a href="https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-13.html#name-version-4-public-keys">
61+
* C-R - Version 4 Public Keys</a>
62+
* @see <a href="https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-13.html#name-version-6-public-keys">
63+
* C-R - Version 6 Public Keys</a>
64+
* @see <a href="https://www.ietf.org/archive/id/draft-koch-librepgp-01.html#name-public-key-packet-formats">
65+
* LibrePGP - Public-Key Packet Formats</a>
66+
*/
4667
PublicKeyPacket(
4768
int keyTag,
4869
BCPGInputStream in,
@@ -52,21 +73,42 @@ public class PublicKeyPacket
5273
super(keyTag, newPacketFormat);
5374

5475
version = in.read();
55-
time = ((long)in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read();
76+
if (version < 2 || version > VERSION_6)
77+
{
78+
throw new UnsupportedPacketVersionException("Unsupported Public Key Packet version encountered: " + version);
79+
}
5680

57-
if (version <= VERSION_3)
81+
time = ((long) in.read() << 24) | ((long) in.read() << 16) | ((long) in.read() << 8) | in.read();
82+
83+
if (version == 2 || version == VERSION_3)
5884
{
5985
validDays = (in.read() << 8) | in.read();
6086
}
6187

6288
algorithm = (byte)in.read();
63-
if (version == VERSION_6)
89+
long keyOctets = -1;
90+
91+
if (version == LIBREPGP_5 || version == VERSION_6)
6492
{
6593
// TODO: Use keyOctets to be able to parse unknown keys
66-
long keyOctets = ((long)in.read() << 24) | ((long)in.read() << 16) | ((long)in.read() << 8) | in.read();
94+
keyOctets = ((long)in.read() << 24) | ((long)in.read() << 16) | ((long)in.read() << 8) | in.read();
6795
}
6896

69-
switch (algorithm)
97+
parseKey(in, algorithm, keyOctets);
98+
}
99+
100+
/**
101+
* Parse algorithm-specific public key material.
102+
* @param in input stream which read just up to the public key material
103+
* @param algorithmId public key algorithm ID
104+
* @param optLen optional: Length of the public key material. -1 if not present.
105+
* @throws IOException if the pk material cannot be parsed
106+
*/
107+
private void parseKey(BCPGInputStream in, int algorithmId, long optLen)
108+
throws IOException
109+
{
110+
111+
switch (algorithmId)
70112
{
71113
case RSA_ENCRYPT:
72114
case RSA_GENERAL:
@@ -102,6 +144,12 @@ public class PublicKeyPacket
102144
key = new Ed448PublicBCPGKey(in);
103145
break;
104146
default:
147+
if (version == VERSION_6 || version == LIBREPGP_5)
148+
{
149+
// with version 5 & 6, we can gracefully handle unknown key types, as the length is known.
150+
key = new UnknownBCPGKey((int) optLen, in);
151+
break;
152+
}
105153
throw new IOException("unknown PGP public key algorithm encountered: " + algorithm);
106154
}
107155
}
@@ -112,7 +160,9 @@ public class PublicKeyPacket
112160
* @param algorithm
113161
* @param time
114162
* @param key
163+
* @deprecated use versioned {@link #PublicKeyPacket(int, int, Date, BCPGKey)} instead
115164
*/
165+
@Deprecated
116166
public PublicKeyPacket(
117167
int algorithm,
118168
Date time,
@@ -184,13 +234,10 @@ public byte[] getEncodedContents()
184234

185235
pOut.write(algorithm);
186236

187-
if (version == VERSION_6)
237+
if (version == VERSION_6 || version == LIBREPGP_5)
188238
{
189239
int keyOctets = key.getEncoded().length;
190-
pOut.write(keyOctets >> 24);
191-
pOut.write(keyOctets >> 16);
192-
pOut.write(keyOctets >> 8);
193-
pOut.write(keyOctets);
240+
StreamUtil.write4OctetLength(pOut, keyOctets);
194241
}
195242

196243
pOut.writeObject((BCPGObject)key);

pg/src/main/java/org/bouncycastle/bcpg/PublicSubkeyPacket.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ public class PublicSubkeyPacket
3131
* @param algorithm
3232
* @param time
3333
* @param key
34+
* @deprecated use versioned {@link #PublicSubkeyPacket(int, int, Date, BCPGKey)} instead
3435
*/
36+
@Deprecated
3537
public PublicSubkeyPacket(
3638
int algorithm,
3739
Date time,

pg/src/main/java/org/bouncycastle/bcpg/S2K.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ public Argon2Params(byte[] salt, int passes, int parallelism, int memSizeExp)
485485
*/
486486
public static Argon2Params universallyRecommendedParameters()
487487
{
488-
return new Argon2Params(1, 4, 21, new SecureRandom());
488+
return new Argon2Params(1, 4, 21, CryptoServicesRegistrar.getSecureRandom());
489489
}
490490

491491
/**
@@ -497,7 +497,7 @@ public static Argon2Params universallyRecommendedParameters()
497497
*/
498498
public static Argon2Params memoryConstrainedParameters()
499499
{
500-
return new Argon2Params(3, 4, 16, new SecureRandom());
500+
return new Argon2Params(3, 4, 16, CryptoServicesRegistrar.getSecureRandom());
501501
}
502502

503503
/**

pg/src/main/java/org/bouncycastle/bcpg/SecretKeyPacket.java

Lines changed: 88 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.bouncycastle.bcpg;
22

3+
import java.io.ByteArrayInputStream;
34
import java.io.ByteArrayOutputStream;
45
import java.io.IOException;
56

@@ -96,8 +97,22 @@ public class SecretKeyPacket
9697
}
9798

9899
/**
99-
* @param in
100-
* @throws IOException
100+
* Parse a {@link SecretKeyPacket} or {@link SecretSubkeyPacket} from an OpenPGP {@link BCPGInputStream}.
101+
* The return type depends on the <pre>packetTypeID</pre>:
102+
* {@link PacketTags#SECRET_KEY} means the result is a {@link SecretKeyPacket}.
103+
* {@link PacketTags#SECRET_SUBKEY} results in a {@link SecretSubkeyPacket}.
104+
*
105+
* @param keyTag packet type ID
106+
* @param in packet input stream
107+
* @param newPacketFormat packet format
108+
* @throws IOException if the secret key packet cannot be parsed
109+
*
110+
* @see <a href="https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-13.html#name-secret-key-packet-formats">
111+
* C-R - Secret-Key Packet Formats</a>
112+
* @see <a href="https://www.ietf.org/archive/id/draft-koch-librepgp-01.html#name-secret-key-packet-formats">
113+
* LibrePGP - Secret-Key Packet Formats</a>
114+
* @see <a href="https://datatracker.ietf.org/doc/draft-dkg-openpgp-hardware-secrets/">
115+
* Hardware-Backed Secret Keys</a>
101116
*/
102117
SecretKeyPacket(
103118
int keyTag,
@@ -119,10 +134,12 @@ public class SecretKeyPacket
119134
int version = pubKeyPacket.getVersion();
120135
s2kUsage = in.read();
121136

122-
if (version == 6 && s2kUsage != USAGE_NONE)
137+
int conditionalParameterLength = -1;
138+
if (version == PublicKeyPacket.LIBREPGP_5 ||
139+
(version == PublicKeyPacket.VERSION_6 && s2kUsage != USAGE_NONE))
123140
{
124141
// TODO: Use length to parse unknown parameters
125-
int conditionalParameterLength = in.read();
142+
conditionalParameterLength = in.read();
126143
}
127144

128145
if (s2kUsage == USAGE_CHECKSUM || s2kUsage == USAGE_SHA1 || s2kUsage == USAGE_AEAD)
@@ -137,40 +154,64 @@ public class SecretKeyPacket
137154
{
138155
aeadAlgorithm = in.read();
139156
}
140-
if (s2kUsage == USAGE_CHECKSUM || s2kUsage == USAGE_SHA1 || s2kUsage == USAGE_AEAD)
157+
if (version == PublicKeyPacket.VERSION_6 && (s2kUsage == USAGE_SHA1 || s2kUsage == USAGE_AEAD))
158+
{
159+
int s2KLen = in.read();
160+
byte[] s2kBytes = new byte[s2KLen];
161+
in.readFully(s2kBytes);
162+
163+
// TODO: catch UnsupportedPacketVersionException gracefully
164+
s2k = new S2K(new ByteArrayInputStream(s2kBytes));
165+
}
166+
else
141167
{
142-
if (version == PublicKeyPacket.VERSION_6)
168+
if (s2kUsage == USAGE_CHECKSUM || s2kUsage == USAGE_SHA1 || s2kUsage == USAGE_AEAD)
143169
{
144-
// TODO: Use length to parse unknown S2Ks
145-
int s2kLen = in.read();
170+
s2k = new S2K(in);
146171
}
147-
s2k = new S2K(in);
148172
}
149173
if (s2kUsage == USAGE_AEAD)
150174
{
151175
iv = new byte[AEADUtils.getIVLength(aeadAlgorithm)];
152176
Streams.readFully(in, iv);
153177
}
154-
boolean isGNUDummyNoPrivateKey = s2k != null
155-
&& s2k.getType() == S2K.GNU_DUMMY_S2K
156-
&& s2k.getProtectionMode() == S2K.GNU_PROTECTION_MODE_NO_PRIVATE_KEY;
157-
if (!(isGNUDummyNoPrivateKey))
178+
else
158179
{
159-
if (s2kUsage != 0 && iv == null)
180+
boolean isGNUDummyNoPrivateKey = s2k != null
181+
&& s2k.getType() == S2K.GNU_DUMMY_S2K
182+
&& s2k.getProtectionMode() == S2K.GNU_PROTECTION_MODE_NO_PRIVATE_KEY;
183+
if (!(isGNUDummyNoPrivateKey))
160184
{
161-
if (encAlgorithm < 7)
162-
{
163-
iv = new byte[8];
164-
}
165-
else
185+
if (s2kUsage != USAGE_NONE && iv == null)
166186
{
167-
iv = new byte[16];
187+
if (encAlgorithm < 7)
188+
{
189+
iv = new byte[8];
190+
}
191+
else
192+
{
193+
iv = new byte[16];
194+
}
195+
in.readFully(iv, 0, iv.length);
168196
}
169-
in.readFully(iv, 0, iv.length);
170197
}
171198
}
172-
173-
this.secKeyData = in.readAll();
199+
200+
if (version == PublicKeyPacket.LIBREPGP_5)
201+
{
202+
long keyOctetCount = ((long) in.read() << 24) | ((long) in.read() << 16) | ((long) in.read() << 8) | in.read();
203+
if (s2kUsage == USAGE_CHECKSUM || s2kUsage == USAGE_NONE)
204+
{
205+
// encoded keyOctetCount does not contain checksum
206+
keyOctetCount += 2;
207+
}
208+
this.secKeyData = new byte[(int) keyOctetCount];
209+
in.readFully(secKeyData);
210+
}
211+
else
212+
{
213+
this.secKeyData = in.readAll();
214+
}
174215
}
175216

176217
/**
@@ -212,6 +253,18 @@ public SecretKeyPacket(
212253
this(SECRET_KEY, pubKeyPacket, encAlgorithm, 0, s2kUsage, s2k, iv, secKeyData);
213254
}
214255

256+
public SecretKeyPacket(
257+
PublicKeyPacket pubKeyPacket,
258+
int encAlgorithm,
259+
int aeadAlgorithm,
260+
int s2kUsage,
261+
S2K s2k,
262+
byte[] iv,
263+
byte[] secKeyData)
264+
{
265+
this(SECRET_KEY, pubKeyPacket, encAlgorithm, aeadAlgorithm, s2kUsage, s2k, iv, secKeyData);
266+
}
267+
215268
SecretKeyPacket(
216269
int keyTag,
217270
PublicKeyPacket pubKeyPacket,
@@ -293,7 +346,8 @@ public byte[] getEncodedContents()
293346

294347
// conditional parameters
295348
byte[] conditionalParameters = encodeConditionalParameters();
296-
if (pubKeyPacket.getVersion() == PublicKeyPacket.VERSION_6 && s2kUsage != USAGE_NONE)
349+
if (pubKeyPacket.getVersion() == PublicKeyPacket.LIBREPGP_5 ||
350+
(pubKeyPacket.getVersion() == PublicKeyPacket.VERSION_6 && s2kUsage != USAGE_NONE))
297351
{
298352
pOut.write(conditionalParameters.length);
299353
}
@@ -302,6 +356,16 @@ public byte[] getEncodedContents()
302356
// encrypted secret key
303357
if (secKeyData != null && secKeyData.length > 0)
304358
{
359+
if (pubKeyPacket.getVersion() == PublicKeyPacket.LIBREPGP_5)
360+
{
361+
int keyOctetCount = secKeyData.length;
362+
// v5 keyOctetCount does not include checksum octets
363+
if (s2kUsage == USAGE_CHECKSUM || s2kUsage == USAGE_NONE)
364+
{
365+
keyOctetCount -= 2;
366+
}
367+
StreamUtil.write4OctetLength(pOut, keyOctetCount);
368+
}
305369
pOut.write(secKeyData);
306370
}
307371

pg/src/main/java/org/bouncycastle/bcpg/SecretSubkeyPacket.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public SecretSubkeyPacket(
8282
* @param iv optional iv for the AEAD algorithm or encryption algorithm
8383
* @param secKeyData secret key data
8484
*/
85-
SecretSubkeyPacket(
85+
public SecretSubkeyPacket(
8686
PublicKeyPacket pubKeyPacket,
8787
int encAlgorithm,
8888
int aeadAlgorithm,

0 commit comments

Comments
 (0)