Skip to content

Commit

Permalink
#1743 Add LibrePGP PreferredEncryptionModes signature subpacket
Browse files Browse the repository at this point in the history
  • Loading branch information
gefeili committed Sep 5, 2024
2 parents 902266f + cefbc72 commit fb24843
Show file tree
Hide file tree
Showing 10 changed files with 307 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.bouncycastle.bcpg.sig.IssuerKeyID;
import org.bouncycastle.bcpg.sig.KeyExpirationTime;
import org.bouncycastle.bcpg.sig.KeyFlags;
import org.bouncycastle.bcpg.sig.LibrePGPPreferredEncryptionModes;
import org.bouncycastle.bcpg.sig.NotationData;
import org.bouncycastle.bcpg.sig.PolicyURI;
import org.bouncycastle.bcpg.sig.PreferredAEADCiphersuites;
Expand Down Expand Up @@ -150,6 +151,8 @@ else if (flags[StreamUtil.flag_partial])
case PREFERRED_HASH_ALGS:
case PREFERRED_SYM_ALGS:
return new PreferredAlgorithms(type, isCritical, isLongLength, data);
case LIBREPGP_PREFERRED_ENCRYPTION_MODES:
return new LibrePGPPreferredEncryptionModes(isCritical, isLongLength, data);
case PREFERRED_AEAD_ALGORITHMS:
return new PreferredAEADCiphersuites(isCritical, isLongLength, data);
case KEY_FLAGS:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ public interface SignatureSubpacketTags
int SIGNATURE_TARGET = 31; // signature target
int EMBEDDED_SIGNATURE = 32; // embedded signature
int ISSUER_FINGERPRINT = 33; // issuer key fingerprint
// public static final int PREFERRED_AEAD_ALGORITHMS = 34; // RESERVED since crypto-refresh-05
int INTENDED_RECIPIENT_FINGERPRINT = 35; // intended recipient fingerprint
int LIBREPGP_PREFERRED_ENCRYPTION_MODES = 34;
int INTENDED_RECIPIENT_FINGERPRINT = 35; // intended recipient fingerprint
int ATTESTED_CERTIFICATIONS = 37; // attested certifications (RESERVED)
int KEY_BLOCK = 38; // Key Block (RESERVED)
int PREFERRED_AEAD_ALGORITHMS = 39; // preferred AEAD algorithms
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.bouncycastle.bcpg.sig;

import org.bouncycastle.bcpg.SignatureSubpacketTags;

/**
* This is a deprecated LibrePGP signature subpacket with encryption mode numbers to indicate which modes
* the key holder prefers to use with OCB Encrypted Data Packets ({@link org.bouncycastle.bcpg.AEADEncDataPacket}).
* Implementations SHOULD ignore this subpacket and assume {@link org.bouncycastle.bcpg.AEADAlgorithmTags#OCB}.
*/
public class LibrePGPPreferredEncryptionModes
extends PreferredAlgorithms
{

public LibrePGPPreferredEncryptionModes(boolean isCritical, int[] encryptionModes)
{
this(isCritical, false, intToByteArray(encryptionModes));
}

public LibrePGPPreferredEncryptionModes(boolean critical, boolean isLongLength, byte[] data)
{
super(SignatureSubpacketTags.LIBREPGP_PREFERRED_ENCRYPTION_MODES, critical, isLongLength, data);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import org.bouncycastle.bcpg.SignatureSubpacketTags;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;

import java.util.ArrayList;
import java.util.List;

public class PreferredAEADCiphersuites
extends PreferredAlgorithms
{
Expand Down Expand Up @@ -144,6 +147,51 @@ private static byte[] requireEven(byte[] encodedCombinations)
return encodedCombinations;
}

/**
* Return a {@link Builder} for constructing a {@link PreferredAEADCiphersuites} packet.
* @param isCritical true if the packet is considered critical.
* @return builder
*/
public static Builder builder(boolean isCritical)
{
return new Builder(isCritical);
}

public static final class Builder
{

private final List<Combination> combinations = new ArrayList<>();
private final boolean isCritical;

private Builder(boolean isCritical)
{
this.isCritical = isCritical;
}

/**
* Add a combination of cipher- and AEAD algorithm to the list of supported ciphersuites.
* @see SymmetricKeyAlgorithmTags for cipher algorithms
* @see AEADAlgorithmTags for AEAD algorithms
* @param symmetricAlgorithmId symmetric cipher algorithm ID
* @param aeadAlgorithmId AEAD algorithm ID
* @return builder
*/
public Builder addCombination(int symmetricAlgorithmId, int aeadAlgorithmId)
{
combinations.add(new Combination(symmetricAlgorithmId, aeadAlgorithmId));
return this;
}

/**
* Build a {@link PreferredAEADCiphersuites} from this builder.
* @return finished packet
*/
public PreferredAEADCiphersuites build()
{
return new PreferredAEADCiphersuites(isCritical, combinations.toArray(new Combination[0]));
}
}

/**
* Algorithm combination of a {@link SymmetricKeyAlgorithmTags} and a {@link AEADAlgorithmTags}.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
public class PreferredAlgorithms
extends SignatureSubpacket
{
private static byte[] intToByteArray(
protected static byte[] intToByteArray(
int[] v)
{
byte[] data = new byte[v.length];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
import org.bouncycastle.bcpg.sig.IssuerKeyID;
import org.bouncycastle.bcpg.sig.KeyExpirationTime;
import org.bouncycastle.bcpg.sig.KeyFlags;
import org.bouncycastle.bcpg.sig.LibrePGPPreferredEncryptionModes;
import org.bouncycastle.bcpg.sig.NotationData;
import org.bouncycastle.bcpg.sig.PolicyURI;
import org.bouncycastle.bcpg.sig.PreferredAEADCiphersuites;
import org.bouncycastle.bcpg.sig.PreferredAlgorithms;
import org.bouncycastle.bcpg.sig.PrimaryUserID;
import org.bouncycastle.bcpg.sig.RegularExpression;
Expand Down Expand Up @@ -191,17 +193,69 @@ public void setPreferredCompressionAlgorithms(boolean isCritical, int[] algorith
}

/**
* This method is BROKEN!
* Specify the preferred AEAD algorithms of this key.
*
* @param isCritical true if should be treated as critical, false otherwise.
* @param algorithms array of algorithms in descending preference
* @deprecated use {@link #setPreferredAEADCiphersuites(boolean, PreferredAEADCiphersuites.Combination[])}
* or {@link #setPreferredLibrePgpEncryptionModes(boolean, int[])} instead.
*/
@Deprecated
public void setPreferredAEADAlgorithms(boolean isCritical, int[] algorithms)
{
packets.add(new PreferredAlgorithms(SignatureSubpacketTags.PREFERRED_AEAD_ALGORITHMS, isCritical,
algorithms));
}

/**
* Specify the preferred OpenPGP AEAD ciphersuites of this key.
*
* @see <a href="https://www.rfc-editor.org/rfc/rfc9580.html#name-preferred-aead-ciphersuites">
* RFC9580: Preferred AEAD Ciphersuites</a>
*
* @param isCritical true, if this packet should be treated as critical, false otherwise.
* @param algorithms array of algorithms in descending preference
*/
public void setPreferredAEADCiphersuites(boolean isCritical, PreferredAEADCiphersuites.Combination[] algorithms)
{
packets.add(new PreferredAEADCiphersuites(isCritical, algorithms));
}

/**
* Specify the preferred OpenPGP AEAD ciphersuites of this key.
*
* @see <a href="https://www.rfc-editor.org/rfc/rfc9580.html#name-preferred-aead-ciphersuites">
* RFC9580: Preferred AEAD Ciphersuites</a>
*
* @param builder builder to build the ciphersuites packet from
*/
public void setPreferredAEADCiphersuites(PreferredAEADCiphersuites.Builder builder)
{
packets.add(builder.build());
}

/**
* Set the preferred encryption modes for LibrePGP keys.
* Note: LibrePGP is not OpenPGP. An application strictly compliant to only the OpenPGP standard will not
* know how to handle LibrePGP encryption modes.
* The LibrePGP spec states that this subpacket shall be ignored and the application shall instead assume
* {@link org.bouncycastle.bcpg.AEADAlgorithmTags#OCB}.
*
* @see <a href="https://www.ietf.org/archive/id/draft-koch-librepgp-01.html#name-preferred-encryption-modes">
* LibrePGP: Preferred Encryption Modes</a>
* @see org.bouncycastle.bcpg.AEADAlgorithmTags for possible algorithms
*
* @param isCritical whether the packet is critical
* @param algorithms list of algorithms
* @deprecated the use of this subpacket is deprecated in LibrePGP
*/
@Deprecated
public void setPreferredLibrePgpEncryptionModes(boolean isCritical, int[] algorithms)
{
packets.add(new LibrePGPPreferredEncryptionModes(isCritical, algorithms));
}

public void addPolicyURI(boolean isCritical, String policyUri)
{
packets.add(new PolicyURI(isCritical, policyUri));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
import org.bouncycastle.bcpg.sig.IssuerKeyID;
import org.bouncycastle.bcpg.sig.KeyExpirationTime;
import org.bouncycastle.bcpg.sig.KeyFlags;
import org.bouncycastle.bcpg.sig.LibrePGPPreferredEncryptionModes;
import org.bouncycastle.bcpg.sig.NotationData;
import org.bouncycastle.bcpg.sig.PolicyURI;
import org.bouncycastle.bcpg.sig.PreferredAEADCiphersuites;
import org.bouncycastle.bcpg.sig.PreferredAlgorithms;
import org.bouncycastle.bcpg.sig.PrimaryUserID;
import org.bouncycastle.bcpg.sig.RegularExpression;
Expand Down Expand Up @@ -285,6 +287,13 @@ public int[] getPreferredCompressionAlgorithms()
return ((PreferredAlgorithms)p).getPreferences();
}

/**
* This method is BROKEN!
* @deprecated use {@link #getPreferredAEADCiphersuites()} or {@link #getPreferredLibrePgpEncryptionModes()}
* instead.
* @return preferred AEAD Algorithms
*/
@Deprecated
public int[] getPreferredAEADAlgorithms()
{
SignatureSubpacket p = this.getSubpacket(SignatureSubpacketTags.PREFERRED_AEAD_ALGORITHMS);
Expand All @@ -297,6 +306,40 @@ public int[] getPreferredAEADAlgorithms()
return ((PreferredAlgorithms)p).getPreferences();
}

/**
* Return the preferred AEAD ciphersuites denoted in the signature.
*
* @return OpenPGP AEAD ciphersuites
*/
public PreferredAEADCiphersuites getPreferredAEADCiphersuites()
{
SignatureSubpacket p = this.getSubpacket(SignatureSubpacketTags.PREFERRED_AEAD_ALGORITHMS);

if (p == null)
{
return null;
}
return (PreferredAEADCiphersuites) p;
}

/**
* Return the preferred LibrePGP encryption modes denoted in the signature.
* Note: The LibrePGP spec states that this subpacket shall be ignored and the application
* shall instead assume {@link org.bouncycastle.bcpg.AEADAlgorithmTags#OCB}.
*
* @return LibrePGP encryption modes
*/
public int[] getPreferredLibrePgpEncryptionModes()
{
SignatureSubpacket p = this.getSubpacket(SignatureSubpacketTags.PREFERRED_AEAD_ALGORITHMS);

if (p == null)
{
return null;
}
return ((LibrePGPPreferredEncryptionModes) p).getPreferences();
}

public int getKeyFlags()
{
SignatureSubpacket p = this.getSubpacket(SignatureSubpacketTags.KEY_FLAGS);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.bouncycastle.bcpg.test;

import org.bouncycastle.bcpg.AEADAlgorithmTags;
import org.bouncycastle.bcpg.SignatureSubpacketTags;
import org.bouncycastle.bcpg.sig.LibrePGPPreferredEncryptionModes;
import org.bouncycastle.util.Arrays;

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

public class SignatureSubpacketsTest
extends AbstractPacketTest
{
@Override
public String getName()
{
return "SignatureSubpacketsTest";
}

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

private void testLibrePGPPreferredEncryptionModesSubpacket()
throws IOException
{
int[] algorithms = new int[] {AEADAlgorithmTags.EAX, AEADAlgorithmTags.OCB};
LibrePGPPreferredEncryptionModes encModes = new LibrePGPPreferredEncryptionModes(
false, algorithms);

isTrue("Encryption Modes encoding mismatch",
Arrays.areEqual(algorithms, encModes.getPreferences()));
isFalse("Mismatch in critical flag", encModes.isCritical());

// encode to byte array and check correctness
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
encModes.encode(bOut);

isEncodingEqual("Packet encoding mismatch", new byte[]{
3, // length
SignatureSubpacketTags.LIBREPGP_PREFERRED_ENCRYPTION_MODES,
AEADAlgorithmTags.EAX,
AEADAlgorithmTags.OCB
}, bOut.toByteArray());
}

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

0 comments on commit fb24843

Please sign in to comment.