Skip to content

Commit fb24843

Browse files
author
gefeili
committed
#1743 Add LibrePGP PreferredEncryptionModes signature subpacket
2 parents 902266f + cefbc72 commit fb24843

10 files changed

+307
-10
lines changed

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

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.bouncycastle.bcpg.sig.IssuerKeyID;
1313
import org.bouncycastle.bcpg.sig.KeyExpirationTime;
1414
import org.bouncycastle.bcpg.sig.KeyFlags;
15+
import org.bouncycastle.bcpg.sig.LibrePGPPreferredEncryptionModes;
1516
import org.bouncycastle.bcpg.sig.NotationData;
1617
import org.bouncycastle.bcpg.sig.PolicyURI;
1718
import org.bouncycastle.bcpg.sig.PreferredAEADCiphersuites;
@@ -150,6 +151,8 @@ else if (flags[StreamUtil.flag_partial])
150151
case PREFERRED_HASH_ALGS:
151152
case PREFERRED_SYM_ALGS:
152153
return new PreferredAlgorithms(type, isCritical, isLongLength, data);
154+
case LIBREPGP_PREFERRED_ENCRYPTION_MODES:
155+
return new LibrePGPPreferredEncryptionModes(isCritical, isLongLength, data);
153156
case PREFERRED_AEAD_ALGORITHMS:
154157
return new PreferredAEADCiphersuites(isCritical, isLongLength, data);
155158
case KEY_FLAGS:

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ public interface SignatureSubpacketTags
3030
int SIGNATURE_TARGET = 31; // signature target
3131
int EMBEDDED_SIGNATURE = 32; // embedded signature
3232
int ISSUER_FINGERPRINT = 33; // issuer key fingerprint
33-
// public static final int PREFERRED_AEAD_ALGORITHMS = 34; // RESERVED since crypto-refresh-05
34-
int INTENDED_RECIPIENT_FINGERPRINT = 35; // intended recipient fingerprint
33+
int LIBREPGP_PREFERRED_ENCRYPTION_MODES = 34;
34+
int INTENDED_RECIPIENT_FINGERPRINT = 35; // intended recipient fingerprint
3535
int ATTESTED_CERTIFICATIONS = 37; // attested certifications (RESERVED)
3636
int KEY_BLOCK = 38; // Key Block (RESERVED)
3737
int PREFERRED_AEAD_ALGORITHMS = 39; // preferred AEAD algorithms
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package org.bouncycastle.bcpg.sig;
2+
3+
import org.bouncycastle.bcpg.SignatureSubpacketTags;
4+
5+
/**
6+
* This is a deprecated LibrePGP signature subpacket with encryption mode numbers to indicate which modes
7+
* the key holder prefers to use with OCB Encrypted Data Packets ({@link org.bouncycastle.bcpg.AEADEncDataPacket}).
8+
* Implementations SHOULD ignore this subpacket and assume {@link org.bouncycastle.bcpg.AEADAlgorithmTags#OCB}.
9+
*/
10+
public class LibrePGPPreferredEncryptionModes
11+
extends PreferredAlgorithms
12+
{
13+
14+
public LibrePGPPreferredEncryptionModes(boolean isCritical, int[] encryptionModes)
15+
{
16+
this(isCritical, false, intToByteArray(encryptionModes));
17+
}
18+
19+
public LibrePGPPreferredEncryptionModes(boolean critical, boolean isLongLength, byte[] data)
20+
{
21+
super(SignatureSubpacketTags.LIBREPGP_PREFERRED_ENCRYPTION_MODES, critical, isLongLength, data);
22+
}
23+
}

pg/src/main/java/org/bouncycastle/bcpg/sig/PreferredAEADCiphersuites.java

+48
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
import org.bouncycastle.bcpg.SignatureSubpacketTags;
55
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
66

7+
import java.util.ArrayList;
8+
import java.util.List;
9+
710
public class PreferredAEADCiphersuites
811
extends PreferredAlgorithms
912
{
@@ -144,6 +147,51 @@ private static byte[] requireEven(byte[] encodedCombinations)
144147
return encodedCombinations;
145148
}
146149

150+
/**
151+
* Return a {@link Builder} for constructing a {@link PreferredAEADCiphersuites} packet.
152+
* @param isCritical true if the packet is considered critical.
153+
* @return builder
154+
*/
155+
public static Builder builder(boolean isCritical)
156+
{
157+
return new Builder(isCritical);
158+
}
159+
160+
public static final class Builder
161+
{
162+
163+
private final List<Combination> combinations = new ArrayList<>();
164+
private final boolean isCritical;
165+
166+
private Builder(boolean isCritical)
167+
{
168+
this.isCritical = isCritical;
169+
}
170+
171+
/**
172+
* Add a combination of cipher- and AEAD algorithm to the list of supported ciphersuites.
173+
* @see SymmetricKeyAlgorithmTags for cipher algorithms
174+
* @see AEADAlgorithmTags for AEAD algorithms
175+
* @param symmetricAlgorithmId symmetric cipher algorithm ID
176+
* @param aeadAlgorithmId AEAD algorithm ID
177+
* @return builder
178+
*/
179+
public Builder addCombination(int symmetricAlgorithmId, int aeadAlgorithmId)
180+
{
181+
combinations.add(new Combination(symmetricAlgorithmId, aeadAlgorithmId));
182+
return this;
183+
}
184+
185+
/**
186+
* Build a {@link PreferredAEADCiphersuites} from this builder.
187+
* @return finished packet
188+
*/
189+
public PreferredAEADCiphersuites build()
190+
{
191+
return new PreferredAEADCiphersuites(isCritical, combinations.toArray(new Combination[0]));
192+
}
193+
}
194+
147195
/**
148196
* Algorithm combination of a {@link SymmetricKeyAlgorithmTags} and a {@link AEADAlgorithmTags}.
149197
*/

pg/src/main/java/org/bouncycastle/bcpg/sig/PreferredAlgorithms.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
public class PreferredAlgorithms
99
extends SignatureSubpacket
1010
{
11-
private static byte[] intToByteArray(
11+
protected static byte[] intToByteArray(
1212
int[] v)
1313
{
1414
byte[] data = new byte[v.length];

pg/src/main/java/org/bouncycastle/openpgp/PGPSignatureSubpacketGenerator.java

+54
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
import org.bouncycastle.bcpg.sig.IssuerKeyID;
1616
import org.bouncycastle.bcpg.sig.KeyExpirationTime;
1717
import org.bouncycastle.bcpg.sig.KeyFlags;
18+
import org.bouncycastle.bcpg.sig.LibrePGPPreferredEncryptionModes;
1819
import org.bouncycastle.bcpg.sig.NotationData;
1920
import org.bouncycastle.bcpg.sig.PolicyURI;
21+
import org.bouncycastle.bcpg.sig.PreferredAEADCiphersuites;
2022
import org.bouncycastle.bcpg.sig.PreferredAlgorithms;
2123
import org.bouncycastle.bcpg.sig.PrimaryUserID;
2224
import org.bouncycastle.bcpg.sig.RegularExpression;
@@ -191,17 +193,69 @@ public void setPreferredCompressionAlgorithms(boolean isCritical, int[] algorith
191193
}
192194

193195
/**
196+
* This method is BROKEN!
194197
* Specify the preferred AEAD algorithms of this key.
195198
*
196199
* @param isCritical true if should be treated as critical, false otherwise.
197200
* @param algorithms array of algorithms in descending preference
201+
* @deprecated use {@link #setPreferredAEADCiphersuites(boolean, PreferredAEADCiphersuites.Combination[])}
202+
* or {@link #setPreferredLibrePgpEncryptionModes(boolean, int[])} instead.
198203
*/
204+
@Deprecated
199205
public void setPreferredAEADAlgorithms(boolean isCritical, int[] algorithms)
200206
{
201207
packets.add(new PreferredAlgorithms(SignatureSubpacketTags.PREFERRED_AEAD_ALGORITHMS, isCritical,
202208
algorithms));
203209
}
204210

211+
/**
212+
* Specify the preferred OpenPGP AEAD ciphersuites of this key.
213+
*
214+
* @see <a href="https://www.rfc-editor.org/rfc/rfc9580.html#name-preferred-aead-ciphersuites">
215+
* RFC9580: Preferred AEAD Ciphersuites</a>
216+
*
217+
* @param isCritical true, if this packet should be treated as critical, false otherwise.
218+
* @param algorithms array of algorithms in descending preference
219+
*/
220+
public void setPreferredAEADCiphersuites(boolean isCritical, PreferredAEADCiphersuites.Combination[] algorithms)
221+
{
222+
packets.add(new PreferredAEADCiphersuites(isCritical, algorithms));
223+
}
224+
225+
/**
226+
* Specify the preferred OpenPGP AEAD ciphersuites of this key.
227+
*
228+
* @see <a href="https://www.rfc-editor.org/rfc/rfc9580.html#name-preferred-aead-ciphersuites">
229+
* RFC9580: Preferred AEAD Ciphersuites</a>
230+
*
231+
* @param builder builder to build the ciphersuites packet from
232+
*/
233+
public void setPreferredAEADCiphersuites(PreferredAEADCiphersuites.Builder builder)
234+
{
235+
packets.add(builder.build());
236+
}
237+
238+
/**
239+
* Set the preferred encryption modes for LibrePGP keys.
240+
* Note: LibrePGP is not OpenPGP. An application strictly compliant to only the OpenPGP standard will not
241+
* know how to handle LibrePGP encryption modes.
242+
* The LibrePGP spec states that this subpacket shall be ignored and the application shall instead assume
243+
* {@link org.bouncycastle.bcpg.AEADAlgorithmTags#OCB}.
244+
*
245+
* @see <a href="https://www.ietf.org/archive/id/draft-koch-librepgp-01.html#name-preferred-encryption-modes">
246+
* LibrePGP: Preferred Encryption Modes</a>
247+
* @see org.bouncycastle.bcpg.AEADAlgorithmTags for possible algorithms
248+
*
249+
* @param isCritical whether the packet is critical
250+
* @param algorithms list of algorithms
251+
* @deprecated the use of this subpacket is deprecated in LibrePGP
252+
*/
253+
@Deprecated
254+
public void setPreferredLibrePgpEncryptionModes(boolean isCritical, int[] algorithms)
255+
{
256+
packets.add(new LibrePGPPreferredEncryptionModes(isCritical, algorithms));
257+
}
258+
205259
public void addPolicyURI(boolean isCritical, String policyUri)
206260
{
207261
packets.add(new PolicyURI(isCritical, policyUri));

pg/src/main/java/org/bouncycastle/openpgp/PGPSignatureSubpacketVector.java

+43
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
import org.bouncycastle.bcpg.sig.IssuerKeyID;
1717
import org.bouncycastle.bcpg.sig.KeyExpirationTime;
1818
import org.bouncycastle.bcpg.sig.KeyFlags;
19+
import org.bouncycastle.bcpg.sig.LibrePGPPreferredEncryptionModes;
1920
import org.bouncycastle.bcpg.sig.NotationData;
2021
import org.bouncycastle.bcpg.sig.PolicyURI;
22+
import org.bouncycastle.bcpg.sig.PreferredAEADCiphersuites;
2123
import org.bouncycastle.bcpg.sig.PreferredAlgorithms;
2224
import org.bouncycastle.bcpg.sig.PrimaryUserID;
2325
import org.bouncycastle.bcpg.sig.RegularExpression;
@@ -285,6 +287,13 @@ public int[] getPreferredCompressionAlgorithms()
285287
return ((PreferredAlgorithms)p).getPreferences();
286288
}
287289

290+
/**
291+
* This method is BROKEN!
292+
* @deprecated use {@link #getPreferredAEADCiphersuites()} or {@link #getPreferredLibrePgpEncryptionModes()}
293+
* instead.
294+
* @return preferred AEAD Algorithms
295+
*/
296+
@Deprecated
288297
public int[] getPreferredAEADAlgorithms()
289298
{
290299
SignatureSubpacket p = this.getSubpacket(SignatureSubpacketTags.PREFERRED_AEAD_ALGORITHMS);
@@ -297,6 +306,40 @@ public int[] getPreferredAEADAlgorithms()
297306
return ((PreferredAlgorithms)p).getPreferences();
298307
}
299308

309+
/**
310+
* Return the preferred AEAD ciphersuites denoted in the signature.
311+
*
312+
* @return OpenPGP AEAD ciphersuites
313+
*/
314+
public PreferredAEADCiphersuites getPreferredAEADCiphersuites()
315+
{
316+
SignatureSubpacket p = this.getSubpacket(SignatureSubpacketTags.PREFERRED_AEAD_ALGORITHMS);
317+
318+
if (p == null)
319+
{
320+
return null;
321+
}
322+
return (PreferredAEADCiphersuites) p;
323+
}
324+
325+
/**
326+
* Return the preferred LibrePGP encryption modes denoted in the signature.
327+
* Note: The LibrePGP spec states that this subpacket shall be ignored and the application
328+
* shall instead assume {@link org.bouncycastle.bcpg.AEADAlgorithmTags#OCB}.
329+
*
330+
* @return LibrePGP encryption modes
331+
*/
332+
public int[] getPreferredLibrePgpEncryptionModes()
333+
{
334+
SignatureSubpacket p = this.getSubpacket(SignatureSubpacketTags.PREFERRED_AEAD_ALGORITHMS);
335+
336+
if (p == null)
337+
{
338+
return null;
339+
}
340+
return ((LibrePGPPreferredEncryptionModes) p).getPreferences();
341+
}
342+
300343
public int getKeyFlags()
301344
{
302345
SignatureSubpacket p = this.getSubpacket(SignatureSubpacketTags.KEY_FLAGS);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package org.bouncycastle.bcpg.test;
2+
3+
import org.bouncycastle.bcpg.AEADAlgorithmTags;
4+
import org.bouncycastle.bcpg.SignatureSubpacketTags;
5+
import org.bouncycastle.bcpg.sig.LibrePGPPreferredEncryptionModes;
6+
import org.bouncycastle.util.Arrays;
7+
8+
import java.io.ByteArrayOutputStream;
9+
import java.io.IOException;
10+
11+
public class SignatureSubpacketsTest
12+
extends AbstractPacketTest
13+
{
14+
@Override
15+
public String getName()
16+
{
17+
return "SignatureSubpacketsTest";
18+
}
19+
20+
@Override
21+
public void performTest()
22+
throws Exception
23+
{
24+
testLibrePGPPreferredEncryptionModesSubpacket();
25+
}
26+
27+
private void testLibrePGPPreferredEncryptionModesSubpacket()
28+
throws IOException
29+
{
30+
int[] algorithms = new int[] {AEADAlgorithmTags.EAX, AEADAlgorithmTags.OCB};
31+
LibrePGPPreferredEncryptionModes encModes = new LibrePGPPreferredEncryptionModes(
32+
false, algorithms);
33+
34+
isTrue("Encryption Modes encoding mismatch",
35+
Arrays.areEqual(algorithms, encModes.getPreferences()));
36+
isFalse("Mismatch in critical flag", encModes.isCritical());
37+
38+
// encode to byte array and check correctness
39+
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
40+
encModes.encode(bOut);
41+
42+
isEncodingEqual("Packet encoding mismatch", new byte[]{
43+
3, // length
44+
SignatureSubpacketTags.LIBREPGP_PREFERRED_ENCRYPTION_MODES,
45+
AEADAlgorithmTags.EAX,
46+
AEADAlgorithmTags.OCB
47+
}, bOut.toByteArray());
48+
}
49+
50+
public static void main(String[] args)
51+
{
52+
runTest(new SignatureSubpacketsTest());
53+
}
54+
}

0 commit comments

Comments
 (0)