Date: Thu, 16 May 2024 13:00:31 +0700
Subject: [PATCH 0078/1644] Improve KeyAgreeRecipientIdentifier getInstance
methods
---
.../asn1/cms/KeyAgreeRecipientIdentifier.java | 34 +++++++++++--------
1 file changed, 20 insertions(+), 14 deletions(-)
diff --git a/util/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientIdentifier.java b/util/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientIdentifier.java
index f67d3b88a3..98ca19a504 100644
--- a/util/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientIdentifier.java
+++ b/util/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientIdentifier.java
@@ -3,8 +3,8 @@
import org.bouncycastle.asn1.ASN1Choice;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.ASN1Util;
import org.bouncycastle.asn1.DERTaggedObject;
/**
@@ -37,9 +37,14 @@ public static KeyAgreeRecipientIdentifier getInstance(
ASN1TaggedObject obj,
boolean explicit)
{
- return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ if (!explicit)
+ {
+ throw new IllegalArgumentException("choice item must be explicitly tagged");
+ }
+
+ return getInstance(obj.getExplicitBaseObject());
}
-
+
/**
* Return an KeyAgreeRecipientIdentifier object from the given object.
*
@@ -62,19 +67,20 @@ public static KeyAgreeRecipientIdentifier getInstance(
{
return (KeyAgreeRecipientIdentifier)obj;
}
-
- if (obj instanceof ASN1Sequence)
- {
- return new KeyAgreeRecipientIdentifier(IssuerAndSerialNumber.getInstance(obj));
- }
-
- if (obj instanceof ASN1TaggedObject && ((ASN1TaggedObject)obj).getTagNo() == 0)
+
+ if (obj instanceof ASN1TaggedObject)
{
- return new KeyAgreeRecipientIdentifier(RecipientKeyIdentifier.getInstance(
- (ASN1TaggedObject)obj, false));
+ ASN1TaggedObject taggedObject = (ASN1TaggedObject)obj;
+ if (taggedObject.hasContextTag(0))
+ {
+ return new KeyAgreeRecipientIdentifier(RecipientKeyIdentifier.getInstance(taggedObject, false));
+ }
+
+ throw new IllegalArgumentException("Invalid KeyAgreeRecipientIdentifier tag: "
+ + ASN1Util.getTagText(taggedObject));
}
-
- throw new IllegalArgumentException("Invalid KeyAgreeRecipientIdentifier: " + obj.getClass().getName());
+
+ return new KeyAgreeRecipientIdentifier(IssuerAndSerialNumber.getInstance(obj));
}
public KeyAgreeRecipientIdentifier(
From 5eed174d793ca542e36ad7907c82c17937b759ef Mon Sep 17 00:00:00 2001
From: Paul Schaub
Date: Thu, 16 May 2024 10:53:02 +0200
Subject: [PATCH 0079/1644] Use Pack class for writing and reading key-IDs from
byte arrays
---
.../bouncycastle/bcpg/FingerprintUtil.java | 71 +++++++++++++------
.../bouncycastle/bcpg/sig/IssuerKeyID.java | 17 +----
.../bcpg/test/FingerprintUtilTest.java | 35 +++++++++
3 files changed, 88 insertions(+), 35 deletions(-)
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/FingerprintUtil.java b/pg/src/main/java/org/bouncycastle/bcpg/FingerprintUtil.java
index 8c8b74e040..6e258c623f 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/FingerprintUtil.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/FingerprintUtil.java
@@ -1,5 +1,7 @@
package org.bouncycastle.bcpg;
+import org.bouncycastle.util.Pack;
+
public class FingerprintUtil
{
@@ -47,18 +49,7 @@ public static long keyIdFromV4Fingerprint(byte[] v4Fingerprint)
*/
public static long longFromLeftMostBytes(byte[] bytes)
{
- if (bytes.length < 8)
- {
- throw new IllegalArgumentException("Byte array MUST contain at least 8 bytes");
- }
- return ((bytes[0] & 0xffL) << 56) |
- ((bytes[1] & 0xffL) << 48) |
- ((bytes[2] & 0xffL) << 40) |
- ((bytes[3] & 0xffL) << 32) |
- ((bytes[4] & 0xffL) << 24) |
- ((bytes[5] & 0xffL) << 16) |
- ((bytes[6] & 0xffL) << 8) |
- ((bytes[7] & 0xffL));
+ return readKeyID(bytes);
}
/**
@@ -68,19 +59,57 @@ public static long longFromLeftMostBytes(byte[] bytes)
* @return long
*/
public static long longFromRightMostBytes(byte[] bytes)
+ {
+ return readKeyID(bytes, bytes.length - 8);
+ }
+
+ /**
+ * Read a key-ID from the first 8 octets of the given byte array.
+ * @param bytes byte array
+ * @return key-ID
+ */
+ public static long readKeyID(byte[] bytes)
+ {
+ return readKeyID(bytes, 0);
+ }
+
+ /**
+ * Read a key-ID from 8 octets of the given byte array starting at offset.
+ * @param bytes byte array
+ * @param offset offset
+ * @return key-ID
+ */
+ public static long readKeyID(byte[] bytes, int offset)
{
if (bytes.length < 8)
{
throw new IllegalArgumentException("Byte array MUST contain at least 8 bytes");
}
- int i = bytes.length;
- return ((bytes[i - 8] & 0xffL) << 56) |
- ((bytes[i - 7] & 0xffL) << 48) |
- ((bytes[i - 6] & 0xffL) << 40) |
- ((bytes[i - 5] & 0xffL) << 32) |
- ((bytes[i - 4] & 0xffL) << 24) |
- ((bytes[i - 3] & 0xffL) << 16) |
- ((bytes[i - 2] & 0xffL) << 8) |
- ((bytes[i - 1] & 0xffL));
+ return Pack.bigEndianToLong(bytes, offset);
+ }
+
+ /**
+ * Write the key-ID encoded as 8 octets to the given byte array, starting at index offset.
+ * @param keyID keyID
+ * @param bytes byte array
+ * @param offset starting offset
+ */
+ public static void writeKeyID(long keyID, byte[] bytes, int offset)
+ {
+ if (bytes.length - offset < 8)
+ {
+ throw new IllegalArgumentException("Not enough space to write key-ID to byte array.");
+ }
+ Pack.longToBigEndian(keyID, bytes, offset);
+ }
+
+ /**
+ * Write the key-ID to the first 8 octets of the given byte array.
+ * @param keyID keyID
+ * @param bytes byte array
+ */
+ public static void writeKeyID(long keyID, byte[] bytes)
+ {
+ writeKeyID(keyID, bytes, 0);
}
}
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/sig/IssuerKeyID.java b/pg/src/main/java/org/bouncycastle/bcpg/sig/IssuerKeyID.java
index 737914cdfe..7b72728bb4 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/sig/IssuerKeyID.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/sig/IssuerKeyID.java
@@ -1,5 +1,6 @@
package org.bouncycastle.bcpg.sig;
+import org.bouncycastle.bcpg.FingerprintUtil;
import org.bouncycastle.bcpg.SignatureSubpacket;
import org.bouncycastle.bcpg.SignatureSubpacketTags;
@@ -13,16 +14,7 @@ protected static byte[] keyIDToBytes(
long keyId)
{
byte[] data = new byte[8];
-
- data[0] = (byte)(keyId >> 56);
- data[1] = (byte)(keyId >> 48);
- data[2] = (byte)(keyId >> 40);
- data[3] = (byte)(keyId >> 32);
- data[4] = (byte)(keyId >> 24);
- data[5] = (byte)(keyId >> 16);
- data[6] = (byte)(keyId >> 8);
- data[7] = (byte)keyId;
-
+ FingerprintUtil.writeKeyID(keyId, data);
return data;
}
@@ -43,9 +35,6 @@ public IssuerKeyID(
public long getKeyID()
{
- long keyID = ((long)(data[0] & 0xff) << 56) | ((long)(data[1] & 0xff) << 48) | ((long)(data[2] & 0xff) << 40) | ((long)(data[3] & 0xff) << 32)
- | ((long)(data[4] & 0xff) << 24) | ((data[5] & 0xff) << 16) | ((data[6] & 0xff) << 8) | (data[7] & 0xff);
-
- return keyID;
+ return FingerprintUtil.readKeyID(data);
}
}
diff --git a/pg/src/test/java/org/bouncycastle/bcpg/test/FingerprintUtilTest.java b/pg/src/test/java/org/bouncycastle/bcpg/test/FingerprintUtilTest.java
index 94546232e6..3648c4b7fe 100644
--- a/pg/src/test/java/org/bouncycastle/bcpg/test/FingerprintUtilTest.java
+++ b/pg/src/test/java/org/bouncycastle/bcpg/test/FingerprintUtilTest.java
@@ -1,6 +1,7 @@
package org.bouncycastle.bcpg.test;
import org.bouncycastle.bcpg.FingerprintUtil;
+import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;
import org.bouncycastle.util.test.SimpleTest;
@@ -46,6 +47,38 @@ private void testLibrePgpKeyIdFromFingerprint()
-3812177997909612905L, FingerprintUtil.keyIdFromLibrePgpFingerprint(decoded));
}
+ private void testLeftMostEqualsRightMostFor8Bytes()
+ {
+ byte[] bytes = new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
+ isEquals(
+ FingerprintUtil.longFromLeftMostBytes(bytes),
+ FingerprintUtil.longFromRightMostBytes(bytes));
+ byte[] b = new byte[8];
+ FingerprintUtil.writeKeyID(FingerprintUtil.longFromLeftMostBytes(bytes), b);
+ isTrue(Arrays.areEqual(bytes, b));
+ }
+
+ private void testWriteKeyIdToBytes()
+ {
+ byte[] bytes = new byte[12];
+ long keyId = 72623859790382856L;
+ FingerprintUtil.writeKeyID(keyId, bytes, 2);
+ isTrue(Arrays.areEqual(
+ new byte[] {0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, 0x00},
+ bytes));
+
+ try
+ {
+ byte[] b = new byte[7];
+ FingerprintUtil.writeKeyID(0, b);
+ fail("Expected IllegalArgumentException for too short byte array.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // Expected
+ }
+ }
+
@Override
public String getName()
{
@@ -60,6 +93,8 @@ public void performTest()
testV6KeyIdFromFingerprint();
testKeyIdFromTooShortFails();
testLibrePgpKeyIdFromFingerprint();
+ testLeftMostEqualsRightMostFor8Bytes();
+ testWriteKeyIdToBytes();
}
public static void main(String[] args)
From 472d76808a3dd26ad4c6250ba9e9f58507d77ac2 Mon Sep 17 00:00:00 2001
From: Paul Schaub
Date: Tue, 21 May 2024 12:32:35 +0200
Subject: [PATCH 0080/1644] Add tests for conversion between JCA/BC keys
---
.../openpgp/test/AbstractPgpKeyPairTest.java | 45 +++++
.../Curve25519PrivateKeyEncodingTest.java | 187 ++++++++++++++++++
.../test/DedicatedEd25519KeyPairTest.java | 158 +++++++++++++++
.../test/DedicatedEd448KeyPairTest.java | 128 ++++++++++++
.../test/DedicatedX25519KeyPairTest.java | 128 ++++++++++++
.../test/DedicatedX448KeyPairTest.java | 128 ++++++++++++
.../test/LegacyEd25519KeyPairTest.java | 128 ++++++++++++
.../openpgp/test/LegacyEd448KeyPairTest.java | 126 ++++++++++++
.../openpgp/test/LegacyX25519KeyPairTest.java | 128 ++++++++++++
.../openpgp/test/LegacyX448KeyPairTest.java | 126 ++++++++++++
.../openpgp/test/RegressionTest.java | 14 +-
11 files changed, 1295 insertions(+), 1 deletion(-)
create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/test/AbstractPgpKeyPairTest.java
create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/test/Curve25519PrivateKeyEncodingTest.java
create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/test/DedicatedEd25519KeyPairTest.java
create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/test/DedicatedEd448KeyPairTest.java
create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/test/DedicatedX25519KeyPairTest.java
create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/test/DedicatedX448KeyPairTest.java
create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/test/LegacyEd25519KeyPairTest.java
create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/test/LegacyEd448KeyPairTest.java
create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/test/LegacyX25519KeyPairTest.java
create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/test/LegacyX448KeyPairTest.java
diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/AbstractPgpKeyPairTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/AbstractPgpKeyPairTest.java
new file mode 100644
index 0000000000..507c85c153
--- /dev/null
+++ b/pg/src/test/java/org/bouncycastle/openpgp/test/AbstractPgpKeyPairTest.java
@@ -0,0 +1,45 @@
+package org.bouncycastle.openpgp.test;
+
+import org.bouncycastle.bcpg.test.AbstractPacketTest;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.operator.bc.BcPGPKeyConverter;
+import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPair;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyConverter;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
+
+import java.security.KeyPair;
+import java.util.Date;
+
+public abstract class AbstractPgpKeyPairTest
+ extends AbstractPacketTest
+{
+
+ public Date currentTimeRounded()
+ {
+ Date now = new Date();
+ return new Date((now.getTime() / 1000) * 1000); // rounded to seconds
+ }
+
+ public BcPGPKeyPair toBcKeyPair(JcaPGPKeyPair keyPair)
+ throws PGPException
+ {
+ BcPGPKeyConverter c = new BcPGPKeyConverter();
+ return new BcPGPKeyPair(keyPair.getPublicKey().getAlgorithm(),
+ new AsymmetricCipherKeyPair(
+ c.getPublicKey(keyPair.getPublicKey()),
+ c.getPrivateKey(keyPair.getPrivateKey())),
+ keyPair.getPublicKey().getCreationTime());
+ }
+
+ public JcaPGPKeyPair toJcaKeyPair(BcPGPKeyPair keyPair)
+ throws PGPException
+ {
+ JcaPGPKeyConverter c = new JcaPGPKeyConverter();
+ return new JcaPGPKeyPair(keyPair.getPublicKey().getAlgorithm(),
+ new KeyPair(
+ c.getPublicKey(keyPair.getPublicKey()),
+ c.getPrivateKey(keyPair.getPrivateKey())),
+ keyPair.getPublicKey().getCreationTime());
+ }
+}
diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/Curve25519PrivateKeyEncodingTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/Curve25519PrivateKeyEncodingTest.java
new file mode 100644
index 0000000000..a68fceb337
--- /dev/null
+++ b/pg/src/test/java/org/bouncycastle/openpgp/test/Curve25519PrivateKeyEncodingTest.java
@@ -0,0 +1,187 @@
+package org.bouncycastle.openpgp.test;
+
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.bcpg.*;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.generators.X25519KeyPairGenerator;
+import org.bouncycastle.crypto.params.X25519KeyGenerationParameters;
+import org.bouncycastle.crypto.params.X25519PrivateKeyParameters;
+import org.bouncycastle.jcajce.spec.XDHParameterSpec;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openpgp.*;
+import org.bouncycastle.openpgp.operator.bc.BcPGPKeyConverter;
+import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPair;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyConverter;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
+import org.bouncycastle.util.Arrays;
+
+import java.io.IOException;
+import java.security.*;
+import java.util.Date;
+
+/**
+ * Curve25519Legacy ECDH Secret Key Material uses big-endian MPI form,
+ * while X25519 keys use little-endian native encoding.
+ * This test verifies that legacy X25519 keys using ECDH are reverse-encoded,
+ * while X25519 keys are natively encoded.
+ */
+public class Curve25519PrivateKeyEncodingTest
+ extends AbstractPgpKeyPairTest
+{
+ @Override
+ public String getName()
+ {
+ return "Curve25519PrivateKeyEncodingTest";
+ }
+
+ @Override
+ public void performTest()
+ throws Exception
+ {
+ containsTest();
+ verifySecretKeyReverseEncoding();
+ }
+
+ private void verifySecretKeyReverseEncoding()
+ throws PGPException, IOException, InvalidAlgorithmParameterException, NoSuchAlgorithmException
+ {
+ bc_verifySecretKeyReverseEncoding();
+ jca_verifySecretKeyReverseEncoding();
+ }
+
+ /**
+ * Verify that legacy ECDH keys over curve25519 encode the private key in reversed encoding,
+ * while dedicated X25519 keys use native encoding for the private key material.
+ * Test the JcaJce implementation.
+ *
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidAlgorithmParameterException
+ * @throws PGPException
+ * @throws IOException
+ */
+ private void jca_verifySecretKeyReverseEncoding()
+ throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, PGPException, IOException
+ {
+ JcaPGPKeyConverter c = new JcaPGPKeyConverter();
+
+ Date date = currentTimeRounded();
+ KeyPairGenerator gen = KeyPairGenerator.getInstance("XDH", new BouncyCastleProvider());
+ gen.initialize(new XDHParameterSpec("X25519"));
+ KeyPair kp = gen.generateKeyPair();
+
+ byte[] rawPrivateKey = jcaNativePrivateKey(kp.getPrivate());
+
+ // Legacy key uses reversed encoding
+ PGPKeyPair pgpECDHKeyPair = new JcaPGPKeyPair(PublicKeyAlgorithmTags.ECDH, kp, date);
+ byte[] encodedECDHPrivateKey = pgpECDHKeyPair.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
+ isTrue(containsSubsequence(encodedECDHPrivateKey, Arrays.reverse(rawPrivateKey)));
+
+ byte[] decodedECDHPrivateKey = jcaNativePrivateKey(c.getPrivateKey(pgpECDHKeyPair.getPrivateKey()));
+ isEncodingEqual(decodedECDHPrivateKey, rawPrivateKey);
+
+ // X25519 key uses native encoding
+ PGPKeyPair pgpX25519KeyPair = new JcaPGPKeyPair(PublicKeyAlgorithmTags.X25519, kp, date);
+ byte[] encodedX25519PrivateKey = pgpX25519KeyPair.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
+ isTrue(containsSubsequence(encodedX25519PrivateKey, rawPrivateKey));
+
+ byte[] decodedX25519PrivateKey = jcaNativePrivateKey(c.getPrivateKey(pgpX25519KeyPair.getPrivateKey()));
+ isEncodingEqual(rawPrivateKey, decodedX25519PrivateKey);
+ }
+
+ /**
+ * Return the native encoding of the given private key.
+ * @param privateKey private key
+ * @return native encoding
+ * @throws IOException
+ */
+ private byte[] jcaNativePrivateKey(PrivateKey privateKey)
+ throws IOException
+ {
+ PrivateKeyInfo kInfo = PrivateKeyInfo.getInstance(privateKey.getEncoded());
+ return ASN1OctetString.getInstance(kInfo.parsePrivateKey()).getOctets();
+ }
+
+ /**
+ * Verify that legacy ECDH keys over curve25519 encode the private key in reversed encoding,
+ * while dedicated X25519 keys use native encoding for the private key material.
+ * Test the BC implementation.
+ */
+ private void bc_verifySecretKeyReverseEncoding()
+ throws PGPException
+ {
+ BcPGPKeyConverter c = new BcPGPKeyConverter();
+
+ Date date = currentTimeRounded();
+ X25519KeyPairGenerator gen = new X25519KeyPairGenerator();
+ gen.init(new X25519KeyGenerationParameters(new SecureRandom()));
+ AsymmetricCipherKeyPair kp = gen.generateKeyPair();
+
+ byte[] rawPrivateKey = ((X25519PrivateKeyParameters) kp.getPrivate()).getEncoded();
+
+ // Legacy key uses reversed encoding
+ PGPKeyPair pgpECDHKeyPair = new BcPGPKeyPair(PublicKeyAlgorithmTags.ECDH, kp, date);
+ byte[] encodedECDHPrivateKey = pgpECDHKeyPair.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
+ isTrue(containsSubsequence(encodedECDHPrivateKey, Arrays.reverse(rawPrivateKey)));
+
+ byte[] decodedECDHPrivateKey = ((X25519PrivateKeyParameters) c.getPrivateKey(pgpECDHKeyPair.getPrivateKey())).getEncoded();
+ isEncodingEqual(decodedECDHPrivateKey, rawPrivateKey);
+
+ // X25519 key uses native encoding
+ PGPKeyPair pgpX25519KeyPair = new BcPGPKeyPair(PublicKeyAlgorithmTags.X25519, kp, date);
+ byte[] encodedX25519PrivateKey = pgpX25519KeyPair.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
+ isTrue(containsSubsequence(encodedX25519PrivateKey, rawPrivateKey));
+
+ byte[] decodedX25519PrivateKey = ((X25519PrivateKeyParameters) c.getPrivateKey(pgpX25519KeyPair.getPrivateKey())).getEncoded();
+ isEncodingEqual(rawPrivateKey, decodedX25519PrivateKey);
+ }
+
+ /**
+ * Return true, if the given sequence contains the given subsequence entirely.
+ * @param sequence sequence
+ * @param subsequence subsequence
+ * @return true if subsequence is a subsequence of sequence
+ */
+ public boolean containsSubsequence(byte[] sequence, byte[] subsequence)
+ {
+ outer: for (int i = 0; i < sequence.length - subsequence.length + 1; i++)
+ {
+ for (int j = 0; j < subsequence.length; j++)
+ {
+ if (sequence[i + j] != subsequence[j])
+ {
+ continue outer;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Test proper functionality of the {@link #containsSubsequence(byte[], byte[])} method.
+ */
+ private void containsTest() {
+ // Make sure our containsSubsequence method functions correctly
+ byte[] s = new byte[] {0x00, 0x01, 0x02, 0x03};
+ isTrue(containsSubsequence(s, new byte[] {0x00, 0x01}));
+ isTrue(containsSubsequence(s, new byte[] {0x01, 0x02}));
+ isTrue(containsSubsequence(s, new byte[] {0x02, 0x03}));
+ isTrue(containsSubsequence(s, new byte[] {0x00}));
+ isTrue(containsSubsequence(s, new byte[] {0x03}));
+ isTrue(containsSubsequence(s, new byte[] {0x00, 0x01, 0x02, 0x03}));
+ isTrue(containsSubsequence(s, new byte[0]));
+ isTrue(containsSubsequence(new byte[0], new byte[0]));
+
+ isFalse(containsSubsequence(s, new byte[] {0x00, 0x02}));
+ isFalse(containsSubsequence(s, new byte[] {0x00, 0x00}));
+ isFalse(containsSubsequence(s, new byte[] {0x00, 0x01, 0x02, 0x03, 0x04}));
+ isFalse(containsSubsequence(s, new byte[] {0x04}));
+ isFalse(containsSubsequence(new byte[0], new byte[] {0x00}));
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new Curve25519PrivateKeyEncodingTest());
+ }
+}
diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/DedicatedEd25519KeyPairTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/DedicatedEd25519KeyPairTest.java
new file mode 100644
index 0000000000..7a2cf69b25
--- /dev/null
+++ b/pg/src/test/java/org/bouncycastle/openpgp/test/DedicatedEd25519KeyPairTest.java
@@ -0,0 +1,158 @@
+package org.bouncycastle.openpgp.test;
+
+import org.bouncycastle.bcpg.*;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.generators.Ed25519KeyPairGenerator;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.Ed25519KeyGenerationParameters;
+import org.bouncycastle.jcajce.spec.EdDSAParameterSpec;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
+import org.bouncycastle.openpgp.operator.bc.BcPGPKeyConverter;
+import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPair;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyConverter;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
+import org.bouncycastle.util.Pack;
+import org.bouncycastle.util.encoders.Hex;
+
+import java.io.IOException;
+import java.security.*;
+import java.util.Date;
+
+public class DedicatedEd25519KeyPairTest
+ extends AbstractPgpKeyPairTest
+{
+ @Override
+ public String getName()
+ {
+ return "DedicatedEd25519KeyPairTest";
+ }
+
+ @Override
+ public void performTest()
+ throws Exception
+ {
+ testConversionOfJcaKeyPair();
+ testConversionOfBcKeyPair();
+
+ testConversionOfTestVectorKey();
+ }
+
+ private void testConversionOfJcaKeyPair()
+ throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, PGPException, IOException
+ {
+ Date date = currentTimeRounded();
+ KeyPairGenerator gen = KeyPairGenerator.getInstance("EDDSA", new BouncyCastleProvider());
+ gen.initialize(new EdDSAParameterSpec("Ed25519"));
+ KeyPair kp = gen.generateKeyPair();
+
+ JcaPGPKeyPair j1 = new JcaPGPKeyPair(PublicKeyAlgorithmTags.Ed25519, kp, date);
+ byte[] pubEnc = j1.getPublicKey().getEncoded();
+ byte[] privEnc = j1.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
+ isTrue("Dedicated Ed25519 public key MUST be instanceof Ed25519PublicBCPGKey",
+ j1.getPublicKey().getPublicKeyPacket().getKey() instanceof Ed25519PublicBCPGKey);
+ isTrue("Dedicated Ed25519 secret key MUST be instanceof Ed25519SecretBCPGKey",
+ j1.getPrivateKey().getPrivateKeyDataPacket() instanceof Ed25519SecretBCPGKey);
+
+ BcPGPKeyPair b1 = toBcKeyPair(j1);
+ isEncodingEqual(pubEnc, b1.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b1.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Dedicated Ed25519 public key MUST be instanceof Ed25519PublicBCPGKey",
+ b1.getPublicKey().getPublicKeyPacket().getKey() instanceof Ed25519PublicBCPGKey);
+ isTrue("Dedicated Ed25519 secret key MUST be instanceof Ed25519SecretBCPGKey",
+ b1.getPrivateKey().getPrivateKeyDataPacket() instanceof Ed25519SecretBCPGKey);
+
+ JcaPGPKeyPair j2 = toJcaKeyPair(b1);
+ isEncodingEqual(pubEnc, j2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Dedicated Ed25519 public key MUST be instanceof Ed25519PublicBCPGKey",
+ j2.getPublicKey().getPublicKeyPacket().getKey() instanceof Ed25519PublicBCPGKey);
+ isTrue("Dedicated Ed25519 secret key MUST be instanceof Ed25519SecretBCPGKey",
+ j2.getPrivateKey().getPrivateKeyDataPacket() instanceof Ed25519SecretBCPGKey);
+
+ BcPGPKeyPair b2 = toBcKeyPair(j2);
+ isEncodingEqual(pubEnc, b2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Dedicated Ed25519 public key MUST be instanceof Ed25519PublicBCPGKey",
+ b2.getPublicKey().getPublicKeyPacket().getKey() instanceof Ed25519PublicBCPGKey);
+ isTrue("Dedicated Ed25519 secret key MUST be instanceof Ed25519SecretBCPGKey",
+ b2.getPrivateKey().getPrivateKeyDataPacket() instanceof Ed25519SecretBCPGKey);
+
+ isEquals("Creation time is preserved",
+ date.getTime(), b2.getPublicKey().getCreationTime().getTime());
+ }
+
+ private void testConversionOfBcKeyPair()
+ throws PGPException, IOException
+ {
+ Date date = currentTimeRounded();
+ Ed25519KeyPairGenerator gen = new Ed25519KeyPairGenerator();
+ gen.init(new Ed25519KeyGenerationParameters(new SecureRandom()));
+ AsymmetricCipherKeyPair kp = gen.generateKeyPair();
+
+ BcPGPKeyPair b1 = new BcPGPKeyPair(PublicKeyAlgorithmTags.Ed25519, kp, date);
+ byte[] pubEnc = b1.getPublicKey().getEncoded();
+ byte[] privEnc = b1.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
+ isTrue("Dedicated Ed25519 public key MUST be instanceof Ed25519PublicBCPGKey",
+ b1.getPublicKey().getPublicKeyPacket().getKey() instanceof Ed25519PublicBCPGKey);
+ isTrue("Dedicated Ed25519 secret key MUST be instanceof Ed25519SecretBCPGKey",
+ b1.getPrivateKey().getPrivateKeyDataPacket() instanceof Ed25519SecretBCPGKey);
+
+ JcaPGPKeyPair j1 = toJcaKeyPair(b1);
+ isEncodingEqual(pubEnc, j1.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j1.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Dedicated Ed25519 public key MUST be instanceof Ed25519PublicBCPGKey",
+ j1.getPublicKey().getPublicKeyPacket().getKey() instanceof Ed25519PublicBCPGKey);
+ isTrue("Dedicated Ed25519 secret key MUST be instanceof Ed25519SecretBCPGKey",
+ j1.getPrivateKey().getPrivateKeyDataPacket() instanceof Ed25519SecretBCPGKey);
+
+ BcPGPKeyPair b2 = toBcKeyPair(j1);
+ isEncodingEqual(pubEnc, b2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Dedicated Ed25519 public key MUST be instanceof Ed25519PublicBCPGKey",
+ b2.getPublicKey().getPublicKeyPacket().getKey() instanceof Ed25519PublicBCPGKey);
+ isTrue("Dedicated Ed25519 secret key MUST be instanceof Ed25519SecretBCPGKey",
+ b2.getPrivateKey().getPrivateKeyDataPacket() instanceof Ed25519SecretBCPGKey);
+
+ JcaPGPKeyPair j2 = toJcaKeyPair(b2);
+ isEncodingEqual(pubEnc, j2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Dedicated Ed25519 public key MUST be instanceof Ed25519PublicBCPGKey",
+ j2.getPublicKey().getPublicKeyPacket().getKey() instanceof Ed25519PublicBCPGKey);
+ isTrue("Dedicated Ed25519 secret key MUST be instanceof Ed25519SecretBCPGKey",
+ j2.getPrivateKey().getPrivateKeyDataPacket() instanceof Ed25519SecretBCPGKey);
+
+ isEquals("Creation time is preserved",
+ date.getTime(), j2.getPublicKey().getCreationTime().getTime());
+ }
+
+ private void testConversionOfTestVectorKey() throws PGPException, IOException {
+ JcaPGPKeyConverter jc = new JcaPGPKeyConverter().setProvider(new BouncyCastleProvider());
+ BcPGPKeyConverter bc = new BcPGPKeyConverter();
+ // ed25519 public key from https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-13.html#name-hashed-data-stream-for-sign
+ // just adapted to be a version 4 key.
+ Date creationTime = new Date(Pack.bigEndianToInt(Hex.decode("63877fe3"), 0) * 1000L);
+ byte[] k = Hex.decode("f94da7bb48d60a61e567706a6587d0331999bb9d891a08242ead84543df895a3");
+ PGPPublicKey v4k = new PGPPublicKey(
+ new PublicKeyPacket(PublicKeyAlgorithmTags.Ed25519, creationTime, new Ed25519PublicBCPGKey(k)),
+ new BcKeyFingerprintCalculator()
+ );
+
+ // convert parsed key to Jca public key
+ PublicKey jcpk = jc.getPublicKey(v4k);
+ PGPPublicKey jck = jc.getPGPPublicKey(PublicKeyAlgorithmTags.Ed25519, jcpk, creationTime);
+ isEncodingEqual(v4k.getEncoded(), jck.getEncoded());
+
+ // convert parsed key to Bc public key
+ AsymmetricKeyParameter bcpk = bc.getPublicKey(v4k);
+ PGPPublicKey bck = bc.getPGPPublicKey(PublicKeyAlgorithmTags.Ed25519, null, bcpk, creationTime);
+ isEncodingEqual(v4k.getEncoded(), bck.getEncoded());
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new DedicatedEd25519KeyPairTest());
+ }
+}
diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/DedicatedEd448KeyPairTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/DedicatedEd448KeyPairTest.java
new file mode 100644
index 0000000000..e353bc9922
--- /dev/null
+++ b/pg/src/test/java/org/bouncycastle/openpgp/test/DedicatedEd448KeyPairTest.java
@@ -0,0 +1,128 @@
+package org.bouncycastle.openpgp.test;
+
+import org.bouncycastle.bcpg.Ed448PublicBCPGKey;
+import org.bouncycastle.bcpg.Ed448SecretBCPGKey;
+import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.generators.Ed448KeyPairGenerator;
+import org.bouncycastle.crypto.params.Ed448KeyGenerationParameters;
+import org.bouncycastle.jcajce.spec.EdDSAParameterSpec;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPair;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
+
+import java.io.IOException;
+import java.security.*;
+import java.util.Date;
+
+public class DedicatedEd448KeyPairTest
+ extends AbstractPgpKeyPairTest
+{
+ @Override
+ public String getName()
+ {
+ return "DedicatedEd448KeyPairTest";
+ }
+
+ @Override
+ public void performTest()
+ throws Exception
+ {
+ testConversionOfJcaKeyPair();
+ testConversionOfBcKeyPair();
+ }
+
+ private void testConversionOfJcaKeyPair()
+ throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, PGPException, IOException
+ {
+ Date date = currentTimeRounded();
+ KeyPairGenerator gen = KeyPairGenerator.getInstance("EDDSA", new BouncyCastleProvider());
+ gen.initialize(new EdDSAParameterSpec("Ed448"));
+ KeyPair kp = gen.generateKeyPair();
+
+ JcaPGPKeyPair j1 = new JcaPGPKeyPair(PublicKeyAlgorithmTags.Ed448, kp, date);
+ byte[] pubEnc = j1.getPublicKey().getEncoded();
+ byte[] privEnc = j1.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
+ isTrue("Dedicated Ed448 public key MUST be instanceof Ed448PublicBCPGKey",
+ j1.getPublicKey().getPublicKeyPacket().getKey() instanceof Ed448PublicBCPGKey);
+ isTrue("Dedicated Ed448 secret key MUST be instanceof Ed448SecretBCPGKey",
+ j1.getPrivateKey().getPrivateKeyDataPacket() instanceof Ed448SecretBCPGKey);
+
+ BcPGPKeyPair b1 = toBcKeyPair(j1);
+ isEncodingEqual(pubEnc, b1.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b1.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Dedicated Ed448 public key MUST be instanceof Ed448PublicBCPGKey",
+ b1.getPublicKey().getPublicKeyPacket().getKey() instanceof Ed448PublicBCPGKey);
+ isTrue("Dedicated Ed448 secret key MUST be instanceof Ed448SecretBCPGKey",
+ b1.getPrivateKey().getPrivateKeyDataPacket() instanceof Ed448SecretBCPGKey);
+
+ JcaPGPKeyPair j2 = toJcaKeyPair(b1);
+ isEncodingEqual(pubEnc, j2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Dedicated Ed448 public key MUST be instanceof Ed448PublicBCPGKey",
+ j2.getPublicKey().getPublicKeyPacket().getKey() instanceof Ed448PublicBCPGKey);
+ isTrue("Dedicated Ed448 secret key MUST be instanceof Ed448SecretBCPGKey",
+ j2.getPrivateKey().getPrivateKeyDataPacket() instanceof Ed448SecretBCPGKey);
+
+ BcPGPKeyPair b2 = toBcKeyPair(j2);
+ isEncodingEqual(pubEnc, b2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Dedicated Ed448 public key MUST be instanceof Ed448PublicBCPGKey",
+ b2.getPublicKey().getPublicKeyPacket().getKey() instanceof Ed448PublicBCPGKey);
+ isTrue("Dedicated Ed448 secret key MUST be instanceof Ed448SecretBCPGKey",
+ b2.getPrivateKey().getPrivateKeyDataPacket() instanceof Ed448SecretBCPGKey);
+
+ isEquals("Creation time is preserved",
+ date.getTime(), b2.getPublicKey().getCreationTime().getTime());
+ }
+
+ private void testConversionOfBcKeyPair()
+ throws PGPException, IOException
+ {
+ Date date = currentTimeRounded();
+ Ed448KeyPairGenerator gen = new Ed448KeyPairGenerator();
+ gen.init(new Ed448KeyGenerationParameters(new SecureRandom()));
+ AsymmetricCipherKeyPair kp = gen.generateKeyPair();
+
+ BcPGPKeyPair b1 = new BcPGPKeyPair(PublicKeyAlgorithmTags.Ed448, kp, date);
+ byte[] pubEnc = b1.getPublicKey().getEncoded();
+ byte[] privEnc = b1.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
+ isTrue("Dedicated Ed448 public key MUST be instanceof Ed448PublicBCPGKey",
+ b1.getPublicKey().getPublicKeyPacket().getKey() instanceof Ed448PublicBCPGKey);
+ isTrue("Dedicated Ed448 secret key MUST be instanceof Ed448SecretBCPGKey",
+ b1.getPrivateKey().getPrivateKeyDataPacket() instanceof Ed448SecretBCPGKey);
+
+ JcaPGPKeyPair j1 = toJcaKeyPair(b1);
+ isEncodingEqual(pubEnc, j1.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j1.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Dedicated Ed448 public key MUST be instanceof Ed448PublicBCPGKey",
+ j1.getPublicKey().getPublicKeyPacket().getKey() instanceof Ed448PublicBCPGKey);
+ isTrue("Dedicated Ed448 secret key MUST be instanceof Ed448SecretBCPGKey",
+ j1.getPrivateKey().getPrivateKeyDataPacket() instanceof Ed448SecretBCPGKey);
+
+ BcPGPKeyPair b2 = toBcKeyPair(j1);
+ isEncodingEqual(pubEnc, b2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Dedicated Ed448 public key MUST be instanceof Ed448PublicBCPGKey",
+ b2.getPublicKey().getPublicKeyPacket().getKey() instanceof Ed448PublicBCPGKey);
+ isTrue("Dedicated Ed448 secret key MUST be instanceof Ed448SecretBCPGKey",
+ b2.getPrivateKey().getPrivateKeyDataPacket() instanceof Ed448SecretBCPGKey);
+
+ JcaPGPKeyPair j2 = toJcaKeyPair(b2);
+ isEncodingEqual(pubEnc, j2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Dedicated Ed448 public key MUST be instanceof Ed448PublicBCPGKey",
+ j2.getPublicKey().getPublicKeyPacket().getKey() instanceof Ed448PublicBCPGKey);
+ isTrue("Dedicated Ed448 secret key MUST be instanceof Ed448SecretBCPGKey",
+ j2.getPrivateKey().getPrivateKeyDataPacket() instanceof Ed448SecretBCPGKey);
+
+ isEquals("Creation time is preserved",
+ date.getTime(), j2.getPublicKey().getCreationTime().getTime());
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new DedicatedEd448KeyPairTest());
+ }
+}
diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/DedicatedX25519KeyPairTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/DedicatedX25519KeyPairTest.java
new file mode 100644
index 0000000000..6de196eb47
--- /dev/null
+++ b/pg/src/test/java/org/bouncycastle/openpgp/test/DedicatedX25519KeyPairTest.java
@@ -0,0 +1,128 @@
+package org.bouncycastle.openpgp.test;
+
+import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
+import org.bouncycastle.bcpg.X25519PublicBCPGKey;
+import org.bouncycastle.bcpg.X25519SecretBCPGKey;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.generators.X25519KeyPairGenerator;
+import org.bouncycastle.crypto.params.X25519KeyGenerationParameters;
+import org.bouncycastle.jcajce.spec.XDHParameterSpec;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPair;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
+
+import java.io.IOException;
+import java.security.*;
+import java.util.Date;
+
+public class DedicatedX25519KeyPairTest
+ extends AbstractPgpKeyPairTest
+{
+ @Override
+ public String getName()
+ {
+ return "DedicatedX25519KeyPairTest";
+ }
+
+ @Override
+ public void performTest()
+ throws Exception
+ {
+ testConversionOfJcaKeyPair();
+ testConversionOfBcKeyPair();
+ }
+
+ private void testConversionOfJcaKeyPair()
+ throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, PGPException, IOException
+ {
+ Date date = currentTimeRounded();
+ KeyPairGenerator gen = KeyPairGenerator.getInstance("XDH", new BouncyCastleProvider());
+ gen.initialize(new XDHParameterSpec("X25519"));
+ KeyPair kp = gen.generateKeyPair();
+
+ JcaPGPKeyPair j1 = new JcaPGPKeyPair(PublicKeyAlgorithmTags.X25519, kp, date);
+ byte[] pubEnc = j1.getPublicKey().getEncoded();
+ byte[] privEnc = j1.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
+ isTrue("Dedicated X25519 public key MUST be instanceof X25519PublicBCPGKey",
+ j1.getPublicKey().getPublicKeyPacket().getKey() instanceof X25519PublicBCPGKey);
+ isTrue("Dedicated X25519 secret key MUST be instanceof X25519SecretBCPGKey",
+ j1.getPrivateKey().getPrivateKeyDataPacket() instanceof X25519SecretBCPGKey);
+
+ BcPGPKeyPair b1 = toBcKeyPair(j1);
+ isEncodingEqual(pubEnc, b1.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b1.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Dedicated X25519 public key MUST be instanceof X25519PublicBCPGKey",
+ b1.getPublicKey().getPublicKeyPacket().getKey() instanceof X25519PublicBCPGKey);
+ isTrue("Dedicated X25519 secret key MUST be instanceof X25519SecretBCPGKey",
+ b1.getPrivateKey().getPrivateKeyDataPacket() instanceof X25519SecretBCPGKey);
+
+ JcaPGPKeyPair j2 = toJcaKeyPair(b1);
+ isEncodingEqual(pubEnc, j2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Dedicated X25519 public key MUST be instanceof X25519PublicBCPGKey",
+ j2.getPublicKey().getPublicKeyPacket().getKey() instanceof X25519PublicBCPGKey);
+ isTrue("Dedicated X25519 secret key MUST be instanceof X25519SecretBCPGKey",
+ j2.getPrivateKey().getPrivateKeyDataPacket() instanceof X25519SecretBCPGKey);
+
+ BcPGPKeyPair b2 = toBcKeyPair(j2);
+ isEncodingEqual(pubEnc, b2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Dedicated X25519 public key MUST be instanceof X25519PublicBCPGKey",
+ b2.getPublicKey().getPublicKeyPacket().getKey() instanceof X25519PublicBCPGKey);
+ isTrue("Dedicated X25519 secret key MUST be instanceof X25519SecretBCPGKey",
+ b2.getPrivateKey().getPrivateKeyDataPacket() instanceof X25519SecretBCPGKey);
+
+ isEquals("Creation time is preserved",
+ date.getTime(), b2.getPublicKey().getCreationTime().getTime());
+ }
+
+ private void testConversionOfBcKeyPair()
+ throws PGPException, IOException
+ {
+ Date date = currentTimeRounded();
+ X25519KeyPairGenerator gen = new X25519KeyPairGenerator();
+ gen.init(new X25519KeyGenerationParameters(new SecureRandom()));
+ AsymmetricCipherKeyPair kp = gen.generateKeyPair();
+
+ BcPGPKeyPair b1 = new BcPGPKeyPair(PublicKeyAlgorithmTags.X25519, kp, date);
+ byte[] pubEnc = b1.getPublicKey().getEncoded();
+ byte[] privEnc = b1.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
+ isTrue("Dedicated X25519 public key MUST be instanceof X25519PublicBCPGKey",
+ b1.getPublicKey().getPublicKeyPacket().getKey() instanceof X25519PublicBCPGKey);
+ isTrue("Dedicated X25519 secret key MUST be instanceof X25519SecretBCPGKey",
+ b1.getPrivateKey().getPrivateKeyDataPacket() instanceof X25519SecretBCPGKey);
+
+ JcaPGPKeyPair j1 = toJcaKeyPair(b1);
+ isEncodingEqual(pubEnc, j1.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j1.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Dedicated X25519 public key MUST be instanceof X25519PublicBCPGKey",
+ j1.getPublicKey().getPublicKeyPacket().getKey() instanceof X25519PublicBCPGKey);
+ isTrue("Dedicated X25519 secret key MUST be instanceof X25519SecretBCPGKey",
+ j1.getPrivateKey().getPrivateKeyDataPacket() instanceof X25519SecretBCPGKey);
+
+ BcPGPKeyPair b2 = toBcKeyPair(j1);
+ isEncodingEqual(pubEnc, b2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Dedicated X25519 public key MUST be instanceof X25519PublicBCPGKey",
+ b2.getPublicKey().getPublicKeyPacket().getKey() instanceof X25519PublicBCPGKey);
+ isTrue("Dedicated X25519 secret key MUST be instanceof X25519SecretBCPGKey",
+ b2.getPrivateKey().getPrivateKeyDataPacket() instanceof X25519SecretBCPGKey);
+
+ JcaPGPKeyPair j2 = toJcaKeyPair(b2);
+ isEncodingEqual(pubEnc, j2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Dedicated X25519 public key MUST be instanceof X25519PublicBCPGKey",
+ j2.getPublicKey().getPublicKeyPacket().getKey() instanceof X25519PublicBCPGKey);
+ isTrue("Dedicated X25519 secret key MUST be instanceof X25519SecretBCPGKey",
+ j2.getPrivateKey().getPrivateKeyDataPacket() instanceof X25519SecretBCPGKey);
+
+ isEquals("Creation time is preserved",
+ date.getTime(), j2.getPublicKey().getCreationTime().getTime());
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new DedicatedX25519KeyPairTest());
+ }
+}
diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/DedicatedX448KeyPairTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/DedicatedX448KeyPairTest.java
new file mode 100644
index 0000000000..42e19a3170
--- /dev/null
+++ b/pg/src/test/java/org/bouncycastle/openpgp/test/DedicatedX448KeyPairTest.java
@@ -0,0 +1,128 @@
+package org.bouncycastle.openpgp.test;
+
+import org.bouncycastle.bcpg.X448PublicBCPGKey;
+import org.bouncycastle.bcpg.X448SecretBCPGKey;
+import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.generators.X448KeyPairGenerator;
+import org.bouncycastle.crypto.params.X448KeyGenerationParameters;
+import org.bouncycastle.jcajce.spec.XDHParameterSpec;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPair;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
+
+import java.io.IOException;
+import java.security.*;
+import java.util.Date;
+
+public class DedicatedX448KeyPairTest
+ extends AbstractPgpKeyPairTest
+{
+ @Override
+ public String getName()
+ {
+ return "DedicatedX448KeyPairTest";
+ }
+
+ @Override
+ public void performTest()
+ throws Exception
+ {
+ testConversionOfJcaKeyPair();
+ testConversionOfBcKeyPair();
+ }
+
+ private void testConversionOfJcaKeyPair()
+ throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, PGPException, IOException
+ {
+ Date date = currentTimeRounded();
+ KeyPairGenerator gen = KeyPairGenerator.getInstance("XDH", new BouncyCastleProvider());
+ gen.initialize(new XDHParameterSpec("X448"));
+ KeyPair kp = gen.generateKeyPair();
+
+ JcaPGPKeyPair j1 = new JcaPGPKeyPair(PublicKeyAlgorithmTags.X448, kp, date);
+ byte[] pubEnc = j1.getPublicKey().getEncoded();
+ byte[] privEnc = j1.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
+ isTrue("Dedicated X448 public key MUST be instanceof X448PublicBCPGKey",
+ j1.getPublicKey().getPublicKeyPacket().getKey() instanceof X448PublicBCPGKey);
+ isTrue("Dedicated X448 secret key MUST be instanceof X448SecretBCPGKey",
+ j1.getPrivateKey().getPrivateKeyDataPacket() instanceof X448SecretBCPGKey);
+
+ BcPGPKeyPair b1 = toBcKeyPair(j1);
+ isEncodingEqual(pubEnc, b1.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b1.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Dedicated X448 public key MUST be instanceof X448PublicBCPGKey",
+ b1.getPublicKey().getPublicKeyPacket().getKey() instanceof X448PublicBCPGKey);
+ isTrue("Dedicated X448 secret key MUST be instanceof X448SecretBCPGKey",
+ b1.getPrivateKey().getPrivateKeyDataPacket() instanceof X448SecretBCPGKey);
+
+ JcaPGPKeyPair j2 = toJcaKeyPair(b1);
+ isEncodingEqual(pubEnc, j2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Dedicated X448 public key MUST be instanceof X448PublicBCPGKey",
+ j2.getPublicKey().getPublicKeyPacket().getKey() instanceof X448PublicBCPGKey);
+ isTrue("Dedicated X448 secret key MUST be instanceof X448SecretBCPGKey",
+ j2.getPrivateKey().getPrivateKeyDataPacket() instanceof X448SecretBCPGKey);
+
+ BcPGPKeyPair b2 = toBcKeyPair(j2);
+ isEncodingEqual(pubEnc, b2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Dedicated X448 public key MUST be instanceof X448PublicBCPGKey",
+ b2.getPublicKey().getPublicKeyPacket().getKey() instanceof X448PublicBCPGKey);
+ isTrue("Dedicated X448 secret key MUST be instanceof X448SecretBCPGKey",
+ b2.getPrivateKey().getPrivateKeyDataPacket() instanceof X448SecretBCPGKey);
+
+ isEquals("Creation time is preserved",
+ date.getTime(), b2.getPublicKey().getCreationTime().getTime());
+ }
+
+ private void testConversionOfBcKeyPair()
+ throws PGPException, IOException
+ {
+ Date date = currentTimeRounded();
+ X448KeyPairGenerator gen = new X448KeyPairGenerator();
+ gen.init(new X448KeyGenerationParameters(new SecureRandom()));
+ AsymmetricCipherKeyPair kp = gen.generateKeyPair();
+
+ BcPGPKeyPair b1 = new BcPGPKeyPair(PublicKeyAlgorithmTags.X448, kp, date);
+ byte[] pubEnc = b1.getPublicKey().getEncoded();
+ byte[] privEnc = b1.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
+ isTrue("Dedicated X448 public key MUST be instanceof X448PublicBCPGKey",
+ b1.getPublicKey().getPublicKeyPacket().getKey() instanceof X448PublicBCPGKey);
+ isTrue("Dedicated X448 secret key MUST be instanceof X448SecretBCPGKey",
+ b1.getPrivateKey().getPrivateKeyDataPacket() instanceof X448SecretBCPGKey);
+
+ JcaPGPKeyPair j1 = toJcaKeyPair(b1);
+ isEncodingEqual(pubEnc, j1.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j1.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Dedicated X448 public key MUST be instanceof X448PublicBCPGKey",
+ j1.getPublicKey().getPublicKeyPacket().getKey() instanceof X448PublicBCPGKey);
+ isTrue("Dedicated X448 secret key MUST be instanceof X448SecretBCPGKey",
+ j1.getPrivateKey().getPrivateKeyDataPacket() instanceof X448SecretBCPGKey);
+
+ BcPGPKeyPair b2 = toBcKeyPair(j1);
+ isEncodingEqual(pubEnc, b2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Dedicated X448 public key MUST be instanceof X448PublicBCPGKey",
+ b2.getPublicKey().getPublicKeyPacket().getKey() instanceof X448PublicBCPGKey);
+ isTrue("Dedicated X448 secret key MUST be instanceof X448SecretBCPGKey",
+ b2.getPrivateKey().getPrivateKeyDataPacket() instanceof X448SecretBCPGKey);
+
+ JcaPGPKeyPair j2 = toJcaKeyPair(b2);
+ isEncodingEqual(pubEnc, j2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Dedicated X448 public key MUST be instanceof X448PublicBCPGKey",
+ j2.getPublicKey().getPublicKeyPacket().getKey() instanceof X448PublicBCPGKey);
+ isTrue("Dedicated X448 secret key MUST be instanceof X448SecretBCPGKey",
+ j2.getPrivateKey().getPrivateKeyDataPacket() instanceof X448SecretBCPGKey);
+
+ isEquals("Creation time is preserved",
+ date.getTime(), j2.getPublicKey().getCreationTime().getTime());
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new DedicatedX448KeyPairTest());
+ }
+}
diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/LegacyEd25519KeyPairTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/LegacyEd25519KeyPairTest.java
new file mode 100644
index 0000000000..fda4d0d7d2
--- /dev/null
+++ b/pg/src/test/java/org/bouncycastle/openpgp/test/LegacyEd25519KeyPairTest.java
@@ -0,0 +1,128 @@
+package org.bouncycastle.openpgp.test;
+
+import org.bouncycastle.bcpg.EdDSAPublicBCPGKey;
+import org.bouncycastle.bcpg.EdSecretBCPGKey;
+import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.generators.Ed25519KeyPairGenerator;
+import org.bouncycastle.crypto.params.Ed25519KeyGenerationParameters;
+import org.bouncycastle.jcajce.spec.EdDSAParameterSpec;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPair;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
+
+import java.io.IOException;
+import java.security.*;
+import java.util.Date;
+
+public class LegacyEd25519KeyPairTest
+ extends AbstractPgpKeyPairTest
+{
+ @Override
+ public String getName()
+ {
+ return "LegacyEd25519KeyPairTest";
+ }
+
+ @Override
+ public void performTest()
+ throws Exception
+ {
+ testConversionOfJcaKeyPair();
+ testConversionOfBcKeyPair();
+ }
+
+ private void testConversionOfJcaKeyPair()
+ throws NoSuchAlgorithmException, PGPException, InvalidAlgorithmParameterException, IOException
+ {
+ Date date = currentTimeRounded();
+ KeyPairGenerator gen = KeyPairGenerator.getInstance("EDDSA", new BouncyCastleProvider());
+ gen.initialize(new EdDSAParameterSpec("Ed25519"));
+ KeyPair kp = gen.generateKeyPair();
+
+ JcaPGPKeyPair j1 = new JcaPGPKeyPair(PublicKeyAlgorithmTags.EDDSA_LEGACY, kp, date);
+ byte[] pubEnc = j1.getPublicKey().getEncoded();
+ byte[] privEnc = j1.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
+ isTrue("Legacy Ed25519 public key MUST be instanceof EdDSAPublicBCPGKey",
+ j1.getPublicKey().getPublicKeyPacket().getKey() instanceof EdDSAPublicBCPGKey);
+ isTrue("Legacy Ed25519 secret key MUST be instanceof EdSecretBCPGKey",
+ j1.getPrivateKey().getPrivateKeyDataPacket() instanceof EdSecretBCPGKey);
+
+ BcPGPKeyPair b1 = toBcKeyPair(j1);
+ isEncodingEqual(pubEnc, b1.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b1.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy Ed25519 public key MUST be instanceof EdDSAPublicBCPGKey",
+ b1.getPublicKey().getPublicKeyPacket().getKey() instanceof EdDSAPublicBCPGKey);
+ isTrue("Legacy Ed25519 secret key MUST be instanceof EdSecretBCPGKey",
+ b1.getPrivateKey().getPrivateKeyDataPacket() instanceof EdSecretBCPGKey);
+
+ JcaPGPKeyPair j2 = toJcaKeyPair(b1);
+ isEncodingEqual(pubEnc, j2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy Ed25519 public key MUST be instanceof EdDSAPublicBCPGKey",
+ j2.getPublicKey().getPublicKeyPacket().getKey() instanceof EdDSAPublicBCPGKey);
+ isTrue("Legacy Ed25519 secret key MUST be instanceof EdSecretBCPGKey",
+ j2.getPrivateKey().getPrivateKeyDataPacket() instanceof EdSecretBCPGKey);
+
+ BcPGPKeyPair b2 = toBcKeyPair(j2);
+ isEncodingEqual(pubEnc, b2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy Ed25519 public key MUST be instanceof EdDSAPublicBCPGKey",
+ b2.getPublicKey().getPublicKeyPacket().getKey() instanceof EdDSAPublicBCPGKey);
+ isTrue("Legacy Ed25519 secret key MUST be instanceof EdSecretBCPGKey",
+ b2.getPrivateKey().getPrivateKeyDataPacket() instanceof EdSecretBCPGKey);
+
+ isEquals("Creation time is preserved",
+ date.getTime(), b2.getPublicKey().getCreationTime().getTime());
+ }
+
+ private void testConversionOfBcKeyPair()
+ throws PGPException, IOException
+ {
+ Date date = currentTimeRounded();
+ Ed25519KeyPairGenerator gen = new Ed25519KeyPairGenerator();
+ gen.init(new Ed25519KeyGenerationParameters(new SecureRandom()));
+ AsymmetricCipherKeyPair kp = gen.generateKeyPair();
+
+ BcPGPKeyPair b1 = new BcPGPKeyPair(PublicKeyAlgorithmTags.EDDSA_LEGACY, kp, date);
+ byte[] pubEnc = b1.getPublicKey().getEncoded();
+ byte[] privEnc = b1.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
+ isTrue("Legacy Ed25519 public key MUST be instanceof EdDSAPublicBCPGKey",
+ b1.getPublicKey().getPublicKeyPacket().getKey() instanceof EdDSAPublicBCPGKey);
+ isTrue("Legacy Ed25519 secret key MUST be instanceof EdSecretBCPGKey",
+ b1.getPrivateKey().getPrivateKeyDataPacket() instanceof EdSecretBCPGKey);
+
+ JcaPGPKeyPair j1 = toJcaKeyPair(b1);
+ isEncodingEqual(pubEnc, j1.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j1.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy Ed25519 public key MUST be instanceof EdDSAPublicBCPGKey",
+ j1.getPublicKey().getPublicKeyPacket().getKey() instanceof EdDSAPublicBCPGKey);
+ isTrue("Legacy Ed25519 secret key MUST be instanceof EdSecretBCPGKey",
+ j1.getPrivateKey().getPrivateKeyDataPacket() instanceof EdSecretBCPGKey);
+
+ BcPGPKeyPair b2 = toBcKeyPair(j1);
+ isEncodingEqual(pubEnc, b2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy Ed25519 public key MUST be instanceof EdDSAPublicBCPGKey",
+ b2.getPublicKey().getPublicKeyPacket().getKey() instanceof EdDSAPublicBCPGKey);
+ isTrue("Legacy Ed25519 secret key MUST be instanceof EdSecretBCPGKey",
+ b2.getPrivateKey().getPrivateKeyDataPacket() instanceof EdSecretBCPGKey);
+
+ JcaPGPKeyPair j2 = toJcaKeyPair(b2);
+ isEncodingEqual(pubEnc, j2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy Ed25519 public key MUST be instanceof EdDSAPublicBCPGKey",
+ j2.getPublicKey().getPublicKeyPacket().getKey() instanceof EdDSAPublicBCPGKey);
+ isTrue("Legacy Ed25519 secret key MUST be instanceof EdSecretBCPGKey",
+ j2.getPrivateKey().getPrivateKeyDataPacket() instanceof EdSecretBCPGKey);
+
+ isEquals("Creation time is preserved",
+ date.getTime(), j2.getPublicKey().getCreationTime().getTime());
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new LegacyEd25519KeyPairTest());
+ }
+}
diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/LegacyEd448KeyPairTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/LegacyEd448KeyPairTest.java
new file mode 100644
index 0000000000..07f320df3d
--- /dev/null
+++ b/pg/src/test/java/org/bouncycastle/openpgp/test/LegacyEd448KeyPairTest.java
@@ -0,0 +1,126 @@
+package org.bouncycastle.openpgp.test;
+
+import org.bouncycastle.bcpg.*;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.generators.Ed448KeyPairGenerator;
+import org.bouncycastle.crypto.params.Ed448KeyGenerationParameters;
+import org.bouncycastle.jcajce.spec.EdDSAParameterSpec;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openpgp.*;
+import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPair;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
+
+import java.io.IOException;
+import java.security.*;
+import java.util.Date;
+
+public class LegacyEd448KeyPairTest
+ extends AbstractPgpKeyPairTest
+{
+ @Override
+ public String getName()
+ {
+ return "LegacyEd448KeyPairTest";
+ }
+
+ @Override
+ public void performTest()
+ throws Exception
+ {
+ testConversionOfJcaKeyPair();
+ testConversionOfBcKeyPair();
+ }
+
+ private void testConversionOfJcaKeyPair()
+ throws NoSuchAlgorithmException, PGPException, InvalidAlgorithmParameterException, IOException
+ {
+ Date date = currentTimeRounded();
+ KeyPairGenerator gen = KeyPairGenerator.getInstance("EDDSA", new BouncyCastleProvider());
+ gen.initialize(new EdDSAParameterSpec("Ed448"));
+ KeyPair kp = gen.generateKeyPair();
+
+ JcaPGPKeyPair j1 = new JcaPGPKeyPair(PublicKeyAlgorithmTags.EDDSA_LEGACY, kp, date);
+ byte[] pubEnc = j1.getPublicKey().getEncoded();
+ byte[] privEnc = j1.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
+ isTrue("Legacy Ed448 public key MUST be instanceof EdDSAPublicBCPGKey",
+ j1.getPublicKey().getPublicKeyPacket().getKey() instanceof EdDSAPublicBCPGKey);
+ isTrue("Legacy Ed448 secret key MUST be instanceof EdSecretBCPGKey",
+ j1.getPrivateKey().getPrivateKeyDataPacket() instanceof EdSecretBCPGKey);
+
+ BcPGPKeyPair b1 = toBcKeyPair(j1);
+ isEncodingEqual(pubEnc, b1.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b1.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy Ed448 public key MUST be instanceof EdDSAPublicBCPGKey",
+ b1.getPublicKey().getPublicKeyPacket().getKey() instanceof EdDSAPublicBCPGKey);
+ isTrue("Legacy Ed448 secret key MUST be instanceof EdSecretBCPGKey",
+ b1.getPrivateKey().getPrivateKeyDataPacket() instanceof EdSecretBCPGKey);
+
+ JcaPGPKeyPair j2 = toJcaKeyPair(b1);
+ isEncodingEqual(pubEnc, j2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy Ed448 public key MUST be instanceof EdDSAPublicBCPGKey",
+ j2.getPublicKey().getPublicKeyPacket().getKey() instanceof EdDSAPublicBCPGKey);
+ isTrue("Legacy Ed448 secret key MUST be instanceof EdSecretBCPGKey",
+ j2.getPrivateKey().getPrivateKeyDataPacket() instanceof EdSecretBCPGKey);
+
+ BcPGPKeyPair b2 = toBcKeyPair(j2);
+ isEncodingEqual(pubEnc, b2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy Ed448 public key MUST be instanceof EdDSAPublicBCPGKey",
+ b2.getPublicKey().getPublicKeyPacket().getKey() instanceof EdDSAPublicBCPGKey);
+ isTrue("Legacy Ed448 secret key MUST be instanceof EdSecretBCPGKey",
+ b2.getPrivateKey().getPrivateKeyDataPacket() instanceof EdSecretBCPGKey);
+
+ isEquals("Creation time is preserved",
+ date.getTime(), b2.getPublicKey().getCreationTime().getTime());
+ }
+
+ private void testConversionOfBcKeyPair()
+ throws PGPException, IOException
+ {
+ Date date = currentTimeRounded();
+ Ed448KeyPairGenerator gen = new Ed448KeyPairGenerator();
+ gen.init(new Ed448KeyGenerationParameters(new SecureRandom()));
+ AsymmetricCipherKeyPair kp = gen.generateKeyPair();
+
+ BcPGPKeyPair b1 = new BcPGPKeyPair(PublicKeyAlgorithmTags.EDDSA_LEGACY, kp, date);
+ byte[] pubEnc = b1.getPublicKey().getEncoded();
+ byte[] privEnc = b1.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
+ isTrue("Legacy Ed448 public key MUST be instanceof EdDSAPublicBCPGKey",
+ b1.getPublicKey().getPublicKeyPacket().getKey() instanceof EdDSAPublicBCPGKey);
+ isTrue("Legacy Ed448 secret key MUST be instanceof EdSecretBCPGKey",
+ b1.getPrivateKey().getPrivateKeyDataPacket() instanceof EdSecretBCPGKey);
+
+ JcaPGPKeyPair j1 = toJcaKeyPair(b1);
+ isEncodingEqual(pubEnc, j1.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j1.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy Ed448 public key MUST be instanceof EdDSAPublicBCPGKey",
+ j1.getPublicKey().getPublicKeyPacket().getKey() instanceof EdDSAPublicBCPGKey);
+ isTrue("Legacy Ed448 secret key MUST be instanceof EdSecretBCPGKey",
+ j1.getPrivateKey().getPrivateKeyDataPacket() instanceof EdSecretBCPGKey);
+
+ BcPGPKeyPair b2 = toBcKeyPair(j1);
+ isEncodingEqual(pubEnc, b2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy Ed448 public key MUST be instanceof EdDSAPublicBCPGKey",
+ b2.getPublicKey().getPublicKeyPacket().getKey() instanceof EdDSAPublicBCPGKey);
+ isTrue("Legacy Ed448 secret key MUST be instanceof EdSecretBCPGKey",
+ b2.getPrivateKey().getPrivateKeyDataPacket() instanceof EdSecretBCPGKey);
+
+ JcaPGPKeyPair j2 = toJcaKeyPair(b2);
+ isEncodingEqual(pubEnc, j2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy Ed448 public key MUST be instanceof EdDSAPublicBCPGKey",
+ j2.getPublicKey().getPublicKeyPacket().getKey() instanceof EdDSAPublicBCPGKey);
+ isTrue("Legacy Ed448 secret key MUST be instanceof EdSecretBCPGKey",
+ j2.getPrivateKey().getPrivateKeyDataPacket() instanceof EdSecretBCPGKey);
+
+ isEquals("Creation time is preserved",
+ date.getTime(), j2.getPublicKey().getCreationTime().getTime());
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new LegacyEd448KeyPairTest());
+ }
+}
diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/LegacyX25519KeyPairTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/LegacyX25519KeyPairTest.java
new file mode 100644
index 0000000000..55a008875b
--- /dev/null
+++ b/pg/src/test/java/org/bouncycastle/openpgp/test/LegacyX25519KeyPairTest.java
@@ -0,0 +1,128 @@
+package org.bouncycastle.openpgp.test;
+
+import org.bouncycastle.bcpg.ECDHPublicBCPGKey;
+import org.bouncycastle.bcpg.ECSecretBCPGKey;
+import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.generators.X25519KeyPairGenerator;
+import org.bouncycastle.crypto.params.X25519KeyGenerationParameters;
+import org.bouncycastle.jcajce.spec.XDHParameterSpec;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPair;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
+
+import java.io.IOException;
+import java.security.*;
+import java.util.Date;
+
+public class LegacyX25519KeyPairTest
+ extends AbstractPgpKeyPairTest
+{
+ @Override
+ public String getName()
+ {
+ return "LegacyX25519KeyPairTest";
+ }
+
+ @Override
+ public void performTest()
+ throws Exception
+ {
+ testConversionOfJcaKeyPair();
+ testConversionOfBcKeyPair();
+ }
+
+ private void testConversionOfJcaKeyPair()
+ throws NoSuchAlgorithmException, PGPException, InvalidAlgorithmParameterException, IOException
+ {
+ Date date = currentTimeRounded();
+ KeyPairGenerator gen = KeyPairGenerator.getInstance("XDH", new BouncyCastleProvider());
+ gen.initialize(new XDHParameterSpec("X25519"));
+ KeyPair kp = gen.generateKeyPair();
+
+ JcaPGPKeyPair j1 = new JcaPGPKeyPair(PublicKeyAlgorithmTags.ECDH, kp, date);
+ byte[] pubEnc = j1.getPublicKey().getEncoded();
+ byte[] privEnc = j1.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
+ isTrue("Legacy X25519 public key MUST be instanceof ECDHPublicBCPGKey",
+ j1.getPublicKey().getPublicKeyPacket().getKey() instanceof ECDHPublicBCPGKey);
+ isTrue("Legacy X25519 secret key MUST be instanceof ECSecretBCPGKey",
+ j1.getPrivateKey().getPrivateKeyDataPacket() instanceof ECSecretBCPGKey);
+
+ BcPGPKeyPair b1 = toBcKeyPair(j1);
+ isEncodingEqual(pubEnc, b1.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b1.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy X25519 public key MUST be instanceof ECDHPublicBCPGKey",
+ b1.getPublicKey().getPublicKeyPacket().getKey() instanceof ECDHPublicBCPGKey);
+ isTrue("Legacy X25519 secret key MUST be instanceof ECSecretBCPGKey",
+ b1.getPrivateKey().getPrivateKeyDataPacket() instanceof ECSecretBCPGKey);
+
+ JcaPGPKeyPair j2 = toJcaKeyPair(b1);
+ isEncodingEqual(pubEnc, j2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy X25519 public key MUST be instanceof ECDHPublicBCPGKey",
+ j2.getPublicKey().getPublicKeyPacket().getKey() instanceof ECDHPublicBCPGKey);
+ isTrue("Legacy X25519 secret key MUST be instanceof ECSecretBCPGKey",
+ j2.getPrivateKey().getPrivateKeyDataPacket() instanceof ECSecretBCPGKey);
+
+ BcPGPKeyPair b2 = toBcKeyPair(j2);
+ isEncodingEqual(pubEnc, b2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy X25519 public key MUST be instanceof ECDHPublicBCPGKey",
+ b2.getPublicKey().getPublicKeyPacket().getKey() instanceof ECDHPublicBCPGKey);
+ isTrue("Legacy X25519 secret key MUST be instanceof ECSecretBCPGKey",
+ b2.getPrivateKey().getPrivateKeyDataPacket() instanceof ECSecretBCPGKey);
+
+ isEquals("Creation time is preserved",
+ date.getTime(), b2.getPublicKey().getCreationTime().getTime());
+ }
+
+ private void testConversionOfBcKeyPair()
+ throws PGPException, IOException
+ {
+ Date date = currentTimeRounded();
+ X25519KeyPairGenerator gen = new X25519KeyPairGenerator();
+ gen.init(new X25519KeyGenerationParameters(new SecureRandom()));
+ AsymmetricCipherKeyPair kp = gen.generateKeyPair();
+
+ BcPGPKeyPair b1 = new BcPGPKeyPair(PublicKeyAlgorithmTags.ECDH, kp, date);
+ byte[] pubEnc = b1.getPublicKey().getEncoded();
+ byte[] privEnc = b1.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
+ isTrue("Legacy X25519 public key MUST be instanceof ECDHPublicBCPGKey",
+ b1.getPublicKey().getPublicKeyPacket().getKey() instanceof ECDHPublicBCPGKey);
+ isTrue("Legacy X25519 secret key MUST be instanceof ECSecretBCPGKey",
+ b1.getPrivateKey().getPrivateKeyDataPacket() instanceof ECSecretBCPGKey);
+
+ JcaPGPKeyPair j1 = toJcaKeyPair(b1);
+ isEncodingEqual(pubEnc, j1.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j1.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy X25519 public key MUST be instanceof ECDHPublicBCPGKey",
+ j1.getPublicKey().getPublicKeyPacket().getKey() instanceof ECDHPublicBCPGKey);
+ isTrue("Legacy X25519 secret key MUST be instanceof ECSecretBCPGKey",
+ j1.getPrivateKey().getPrivateKeyDataPacket() instanceof ECSecretBCPGKey);
+
+ BcPGPKeyPair b2 = toBcKeyPair(j1);
+ isEncodingEqual(pubEnc, b2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy X25519 public key MUST be instanceof ECDHPublicBCPGKey",
+ b2.getPublicKey().getPublicKeyPacket().getKey() instanceof ECDHPublicBCPGKey);
+ isTrue("Legacy X25519 secret key MUST be instanceof ECSecretBCPGKey",
+ b2.getPrivateKey().getPrivateKeyDataPacket() instanceof ECSecretBCPGKey);
+
+ JcaPGPKeyPair j2 = toJcaKeyPair(b2);
+ isEncodingEqual(pubEnc, j2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy X25519 public key MUST be instanceof ECDHPublicBCPGKey",
+ j2.getPublicKey().getPublicKeyPacket().getKey() instanceof ECDHPublicBCPGKey);
+ isTrue("Legacy X25519 secret key MUST be instanceof ECSecretBCPGKey",
+ j2.getPrivateKey().getPrivateKeyDataPacket() instanceof ECSecretBCPGKey);
+
+ isEquals("Creation time is preserved",
+ date.getTime(), j2.getPublicKey().getCreationTime().getTime());
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new LegacyX25519KeyPairTest());
+ }
+}
diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/LegacyX448KeyPairTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/LegacyX448KeyPairTest.java
new file mode 100644
index 0000000000..61c04c5f54
--- /dev/null
+++ b/pg/src/test/java/org/bouncycastle/openpgp/test/LegacyX448KeyPairTest.java
@@ -0,0 +1,126 @@
+package org.bouncycastle.openpgp.test;
+
+import org.bouncycastle.bcpg.*;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.generators.X448KeyPairGenerator;
+import org.bouncycastle.crypto.params.X448KeyGenerationParameters;
+import org.bouncycastle.jcajce.spec.XDHParameterSpec;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPair;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
+
+import java.io.IOException;
+import java.security.*;
+import java.util.Date;
+
+public class LegacyX448KeyPairTest
+ extends AbstractPgpKeyPairTest
+{
+ @Override
+ public String getName()
+ {
+ return "LegacyX448KeyPairTest";
+ }
+
+ @Override
+ public void performTest()
+ throws Exception
+ {
+ testConversionOfJcaKeyPair();
+ testConversionOfBcKeyPair();
+ }
+
+ private void testConversionOfJcaKeyPair()
+ throws NoSuchAlgorithmException, PGPException, InvalidAlgorithmParameterException, IOException
+ {
+ Date date = currentTimeRounded();
+ KeyPairGenerator gen = KeyPairGenerator.getInstance("XDH", new BouncyCastleProvider());
+ gen.initialize(new XDHParameterSpec("X448"));
+ KeyPair kp = gen.generateKeyPair();
+
+ JcaPGPKeyPair j1 = new JcaPGPKeyPair(PublicKeyAlgorithmTags.ECDH, kp, date);
+ byte[] pubEnc = j1.getPublicKey().getEncoded();
+ byte[] privEnc = j1.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
+ isTrue("Legacy X448 public key MUST be instanceof ECDHPublicBCPGKey",
+ j1.getPublicKey().getPublicKeyPacket().getKey() instanceof ECDHPublicBCPGKey);
+ isTrue("Legacy X448 secret key MUST be instanceof ECSecretBCPGKey",
+ j1.getPrivateKey().getPrivateKeyDataPacket() instanceof ECSecretBCPGKey);
+
+ BcPGPKeyPair b1 = toBcKeyPair(j1);
+ isEncodingEqual(pubEnc, b1.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b1.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy X448 public key MUST be instanceof ECDHPublicBCPGKey",
+ b1.getPublicKey().getPublicKeyPacket().getKey() instanceof ECDHPublicBCPGKey);
+ isTrue("Legacy X448 secret key MUST be instanceof ECSecretBCPGKey",
+ b1.getPrivateKey().getPrivateKeyDataPacket() instanceof ECSecretBCPGKey);
+
+ JcaPGPKeyPair j2 = toJcaKeyPair(b1);
+ isEncodingEqual(pubEnc, j2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy X448 public key MUST be instanceof ECDHPublicBCPGKey",
+ j2.getPublicKey().getPublicKeyPacket().getKey() instanceof ECDHPublicBCPGKey);
+ isTrue("Legacy X448 secret key MUST be instanceof ECSecretBCPGKey",
+ j2.getPrivateKey().getPrivateKeyDataPacket() instanceof ECSecretBCPGKey);
+
+ BcPGPKeyPair b2 = toBcKeyPair(j2);
+ isEncodingEqual(pubEnc, b2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy X448 public key MUST be instanceof ECDHPublicBCPGKey",
+ b2.getPublicKey().getPublicKeyPacket().getKey() instanceof ECDHPublicBCPGKey);
+ isTrue("Legacy X448 secret key MUST be instanceof ECSecretBCPGKey",
+ b2.getPrivateKey().getPrivateKeyDataPacket() instanceof ECSecretBCPGKey);
+
+ isEquals("Creation time is preserved",
+ date.getTime(), b2.getPublicKey().getCreationTime().getTime());
+ }
+
+ private void testConversionOfBcKeyPair()
+ throws PGPException, IOException
+ {
+ Date date = currentTimeRounded();
+ X448KeyPairGenerator gen = new X448KeyPairGenerator();
+ gen.init(new X448KeyGenerationParameters(new SecureRandom()));
+ AsymmetricCipherKeyPair kp = gen.generateKeyPair();
+
+ BcPGPKeyPair b1 = new BcPGPKeyPair(PublicKeyAlgorithmTags.ECDH, kp, date);
+ byte[] pubEnc = b1.getPublicKey().getEncoded();
+ byte[] privEnc = b1.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
+ isTrue("Legacy X448 public key MUST be instanceof ECDHPublicBCPGKey",
+ b1.getPublicKey().getPublicKeyPacket().getKey() instanceof ECDHPublicBCPGKey);
+ isTrue("Legacy X448 secret key MUST be instanceof ECSecretBCPGKey",
+ b1.getPrivateKey().getPrivateKeyDataPacket() instanceof ECSecretBCPGKey);
+
+ JcaPGPKeyPair j1 = toJcaKeyPair(b1);
+ isEncodingEqual(pubEnc, j1.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j1.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy X448 public key MUST be instanceof ECDHPublicBCPGKey",
+ j1.getPublicKey().getPublicKeyPacket().getKey() instanceof ECDHPublicBCPGKey);
+ isTrue("Legacy X448 secret key MUST be instanceof ECSecretBCPGKey",
+ j1.getPrivateKey().getPrivateKeyDataPacket() instanceof ECSecretBCPGKey);
+
+ BcPGPKeyPair b2 = toBcKeyPair(j1);
+ isEncodingEqual(pubEnc, b2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy X448 public key MUST be instanceof ECDHPublicBCPGKey",
+ b2.getPublicKey().getPublicKeyPacket().getKey() instanceof ECDHPublicBCPGKey);
+ isTrue("Legacy X448 secret key MUST be instanceof ECSecretBCPGKey",
+ b2.getPrivateKey().getPrivateKeyDataPacket() instanceof ECSecretBCPGKey);
+
+ JcaPGPKeyPair j2 = toJcaKeyPair(b2);
+ isEncodingEqual(pubEnc, j2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy X448 public key MUST be instanceof ECDHPublicBCPGKey",
+ j2.getPublicKey().getPublicKeyPacket().getKey() instanceof ECDHPublicBCPGKey);
+ isTrue("Legacy X448 secret key MUST be instanceof ECSecretBCPGKey",
+ j2.getPrivateKey().getPrivateKeyDataPacket() instanceof ECSecretBCPGKey);
+
+ isEquals("Creation time is preserved",
+ date.getTime(), j2.getPublicKey().getCreationTime().getTime());
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new LegacyX448KeyPairTest());
+ }
+}
diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/RegressionTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/RegressionTest.java
index 2cf6ad463c..e9a4c11e3c 100644
--- a/pg/src/test/java/org/bouncycastle/openpgp/test/RegressionTest.java
+++ b/pg/src/test/java/org/bouncycastle/openpgp/test/RegressionTest.java
@@ -62,7 +62,19 @@ public class RegressionTest
new BcImplProviderTest(),
new OperatorJcajceTest(),
new OpenPGPTest(),
- new OperatorBcTest()
+ new OperatorBcTest(),
+
+ new DedicatedEd25519KeyPairTest(),
+ new DedicatedEd448KeyPairTest(),
+ new DedicatedX25519KeyPairTest(),
+ new DedicatedX448KeyPairTest(),
+
+ new LegacyEd25519KeyPairTest(),
+ new LegacyEd448KeyPairTest(),
+ new LegacyX25519KeyPairTest(),
+ new LegacyX448KeyPairTest(),
+
+ new Curve25519PrivateKeyEncodingTest()
};
public static void main(String[] args)
From 8f258b1f7e0f7b6f33ba0cac9b21b5ab9258c4c0 Mon Sep 17 00:00:00 2001
From: Paul Schaub
Date: Tue, 21 May 2024 12:33:34 +0200
Subject: [PATCH 0081/1644] Fix PGPKeyConverter classes support for
X448,Ed448,X25519,Ed25519
---
.../openpgp/operator/PGPKeyConverter.java | 8 +-
.../operator/bc/BcPGPKeyConverter.java | 272 +++++++++++-----
.../operator/jcajce/JcaPGPKeyConverter.java | 297 +++++++++++++-----
3 files changed, 413 insertions(+), 164 deletions(-)
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPKeyConverter.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPKeyConverter.java
index ccdea777b4..52ca458e0b 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPKeyConverter.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPKeyConverter.java
@@ -74,6 +74,11 @@ protected PGPKeyConverter()
* | SHA2-256 |
* AES-128 |
*
+ *
+ * | Curve448 |
+ * SHA2-512 |
+ * AES-256 |
+ *
*
*/
protected PGPKdfParameters implGetKdfParameters(ASN1ObjectIdentifier curveID, PGPAlgorithmParameters algorithmParameters)
@@ -89,7 +94,8 @@ else if (curveID.equals(SECObjectIdentifiers.secp384r1) || curveID.equals(TeleTr
{
return new PGPKdfParameters(HashAlgorithmTags.SHA384, SymmetricKeyAlgorithmTags.AES_192);
}
- else if (curveID.equals(SECObjectIdentifiers.secp521r1) || curveID.equals(TeleTrusTObjectIdentifiers.brainpoolP512r1))
+ else if (curveID.equals(SECObjectIdentifiers.secp521r1) || curveID.equals(TeleTrusTObjectIdentifiers.brainpoolP512r1)
+ || curveID.equals(EdECObjectIdentifiers.id_X448))
{
return new PGPKdfParameters(HashAlgorithmTags.SHA512, SymmetricKeyAlgorithmTags.AES_256);
}
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyConverter.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyConverter.java
index 23ab52f0e4..c7a3b5237b 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyConverter.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyConverter.java
@@ -122,40 +122,58 @@ public AsymmetricKeyParameter getPrivateKey(PGPPrivateKey privKey)
{
ECDHPublicBCPGKey ecdhPub = (ECDHPublicBCPGKey)pubPk.getKey();
+ // Legacy XDH on Curve25519 (legacy X25519)
+ // 1.3.6.1.4.1.3029.1.5.1 & 1.3.101.110
if (CryptlibObjectIdentifiers.curvey25519.equals(ecdhPub.getCurveOID()))
{
return PrivateKeyFactory.createKey(getPrivateKeyInfo(EdECObjectIdentifiers.id_X25519,
Arrays.reverseInPlace(BigIntegers.asUnsignedByteArray(((ECSecretBCPGKey)privPk).getX()))));
}
+ // Legacy X448 (1.3.101.111)
+ else if (EdECObjectIdentifiers.id_X448.equals(ecdhPub.getCurveOID()))
+ {
+ return PrivateKeyFactory.createKey(getPrivateKeyInfo(EdECObjectIdentifiers.id_X448,
+ Arrays.reverseInPlace(BigIntegers.asUnsignedByteArray(((ECSecretBCPGKey)privPk).getX()))));
+ }
+ // NIST, Brainpool etc.
else
{
return implGetPrivateKeyEC(ecdhPub, (ECSecretBCPGKey)privPk);
}
}
+ // Modern X25519 (1.3.6.1.4.1.3029.1.5.1 & 1.3.101.110)
case PublicKeyAlgorithmTags.X25519:
{
return PrivateKeyFactory.createKey(getPrivateKeyInfo(EdECObjectIdentifiers.id_X25519, X25519SecretBCPGKey.LENGTH,
- Arrays.reverseInPlace(privPk.getEncoded())));
+ privPk.getEncoded()));
}
+ // Modern X448 (1.3.101.111)
case PublicKeyAlgorithmTags.X448:
{
return PrivateKeyFactory.createKey(getPrivateKeyInfo(EdECObjectIdentifiers.id_X448, X448SecretBCPGKey.LENGTH,
- Arrays.reverseInPlace(privPk.getEncoded())));
+ privPk.getEncoded()));
}
case PublicKeyAlgorithmTags.ECDSA:
- return implGetPrivateKeyEC((ECDSAPublicBCPGKey)pubPk.getKey(), (ECSecretBCPGKey)privPk);
+ {
+ return implGetPrivateKeyEC((ECDSAPublicBCPGKey) pubPk.getKey(), (ECSecretBCPGKey) privPk);
+ }
+ // Legacy EdDSA (legacy Ed448, legacy Ed25519)
case PublicKeyAlgorithmTags.EDDSA_LEGACY:
{
+ // Legacy Ed448 (1.3.101.113)
if (((EdDSAPublicBCPGKey)pubPk.getKey()).getCurveOID().equals(EdECObjectIdentifiers.id_Ed448))
{
return implGetPrivateKeyPKCS8(EdECObjectIdentifiers.id_Ed448, Ed448.SECRET_KEY_SIZE, privPk);
}
+ // Legacy Ed25519 (1.3.6.1.4.1.11591.15.1 & 1.3.101.112)
return implGetPrivateKeyPKCS8(EdECObjectIdentifiers.id_Ed25519, Ed25519.SECRET_KEY_SIZE, privPk);
}
+ // Modern Ed22519 (1.3.6.1.4.1.11591.15.1 & 1.3.101.112)
case PublicKeyAlgorithmTags.Ed25519:
{
return PrivateKeyFactory.createKey(getPrivateKeyInfo(EdECObjectIdentifiers.id_Ed25519, Ed25519SecretBCPGKey.LENGTH, privPk.getEncoded()));
}
+ // Modern Ed448 (1.3.101.113)
case PublicKeyAlgorithmTags.Ed448:
{
return PrivateKeyFactory.createKey(getPrivateKeyInfo(EdECObjectIdentifiers.id_Ed448, Ed448SecretBCPGKey.LENGTH, privPk.getEncoded()));
@@ -178,7 +196,7 @@ public AsymmetricKeyParameter getPrivateKey(PGPPrivateKey privKey)
rsaPriv.getPrimeExponentQ(), rsaPriv.getCrtCoefficient());
}
default:
- throw new PGPException("unknown public key algorithm encountered");
+ throw new PGPException("unknown public key algorithm encountered: " + pubPk.getAlgorithm());
}
}
catch (PGPException e)
@@ -209,6 +227,8 @@ public AsymmetricKeyParameter getPublicKey(PGPPublicKey publicKey)
{
ECDHPublicBCPGKey ecdhK = (ECDHPublicBCPGKey)publicPk.getKey();
+ // Legacy XDH on Curve25519 (legacy X25519)
+ // 1.3.6.1.4.1.3029.1.5.1 & 1.3.101.110
if (ecdhK.getCurveOID().equals(CryptlibObjectIdentifiers.curvey25519))
{
byte[] pEnc = BigIntegers.asUnsignedByteArray(ecdhK.getEncodedPoint());
@@ -219,22 +239,37 @@ public AsymmetricKeyParameter getPublicKey(PGPPublicKey publicKey)
}
return implGetPublicKeyX509(EdECObjectIdentifiers.id_X25519, pEnc, 1);
}
+ // Legacy X448 (1.3.101.111)
+ else if (ecdhK.getCurveOID().equals(EdECObjectIdentifiers.id_X448))
+ {
+ byte[] pEnc = BigIntegers.asUnsignedByteArray(ecdhK.getEncodedPoint());
+ // skip the 0x40 header byte.
+ if (pEnc.length < 1 || 0x40 != pEnc[0])
+ {
+ throw new IllegalArgumentException("Invalid X448 public key");
+ }
+ return implGetPublicKeyX509(EdECObjectIdentifiers.id_X448, pEnc, 1);
+ }
else
{
return implGetPublicKeyEC(ecdhK);
}
}
+ // Modern X25519 (1.3.6.1.4.1.3029.1.5.1 & 1.3.101.110)
case PublicKeyAlgorithmTags.X25519:
{
return implGetPublicKeyX509((X25519PublicBCPGKey)publicPk.getKey(), EdECObjectIdentifiers.id_X25519);
}
+ // Modern X448 (1.3.101.111)
case PublicKeyAlgorithmTags.X448:
{
return implGetPublicKeyX509((X448PublicBCPGKey)publicPk.getKey(), EdECObjectIdentifiers.id_X448);
}
case PublicKeyAlgorithmTags.ECDSA:
+ {
return implGetPublicKeyEC((ECDSAPublicBCPGKey)publicPk.getKey());
-
+ }
+ // Legacy EdDSA (legacy Ed448, legacy Ed25519)
case PublicKeyAlgorithmTags.EDDSA_LEGACY:
{
EdDSAPublicBCPGKey eddsaK = (EdDSAPublicBCPGKey)publicPk.getKey();
@@ -246,29 +281,35 @@ public AsymmetricKeyParameter getPublicKey(PGPPublicKey publicKey)
throw new IllegalArgumentException("Invalid EdDSA public key");
}
- if (pEnc[0] == 0x40 && !eddsaK.getCurveOID().equals(EdECObjectIdentifiers.id_Ed448))
+ // Legacy Ed25519 (1.3.6.1.4.1.11591.15.1 & 1.3.101.112)
+ if (!eddsaK.getCurveOID().equals(EdECObjectIdentifiers.id_Ed448))
{
- return implGetPublicKeyX509(EdECObjectIdentifiers.id_Ed25519, pEnc, 1);
+ return implGetPublicKeyX509(EdECObjectIdentifiers.id_Ed25519, pEnc, pEnc[0] == 0x40 ? 1 : 0);
}
- else if (eddsaK.getCurveOID().equals(EdECObjectIdentifiers.id_Ed448))
+ // Legacy Ed448 (1.3.101.113)
+ if (eddsaK.getCurveOID().equals(EdECObjectIdentifiers.id_Ed448))
{
- return implGetPublicKeyX509(EdECObjectIdentifiers.id_Ed448, pEnc, 0);
+ return implGetPublicKeyX509(EdECObjectIdentifiers.id_Ed448, pEnc, pEnc[0] == 0x40 ? 1 : 0);
}
throw new IllegalArgumentException("Invalid EdDSA public key");
}
+ // Modern Ed22519 (1.3.6.1.4.1.11591.15.1 & 1.3.101.112)
case PublicKeyAlgorithmTags.Ed25519:
{
return implGetPublicKeyX509((Ed25519PublicBCPGKey)publicPk.getKey(), EdECObjectIdentifiers.id_Ed25519);
}
+ // Modern Ed448 (1.3.101.113)
case PublicKeyAlgorithmTags.Ed448:
{
return implGetPublicKeyX509((Ed448PublicBCPGKey)publicPk.getKey(), EdECObjectIdentifiers.id_Ed448);
}
case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT:
case PublicKeyAlgorithmTags.ELGAMAL_GENERAL:
- ElGamalPublicBCPGKey elK = (ElGamalPublicBCPGKey)publicPk.getKey();
+ {
+ ElGamalPublicBCPGKey elK = (ElGamalPublicBCPGKey) publicPk.getKey();
return new ElGamalPublicKeyParameters(elK.getY(), new ElGamalParameters(elK.getP(), elK.getG()));
+ }
case PublicKeyAlgorithmTags.RSA_ENCRYPT:
case PublicKeyAlgorithmTags.RSA_GENERAL:
@@ -279,7 +320,7 @@ else if (eddsaK.getCurveOID().equals(EdECObjectIdentifiers.id_Ed448))
}
default:
- throw new PGPException("unknown public key algorithm encountered");
+ throw new PGPException("unknown public key algorithm encountered: " + publicKey.getAlgorithm());
}
}
catch (PGPException e)
@@ -304,37 +345,59 @@ private BCPGKey getPrivateBCPGKey(PGPPublicKey pubKey, AsymmetricKeyParameter pr
}
case PublicKeyAlgorithmTags.ECDH:
{
+ // Legacy XDH on Curve25519 (legacy X25519)
+ // 1.3.6.1.4.1.3029.1.5.1 & 1.3.101.110
if (privKey instanceof X25519PrivateKeyParameters)
{
return new ECSecretBCPGKey(new BigInteger(1, Arrays.reverseInPlace(((X25519PrivateKeyParameters)privKey).getEncoded())));
}
+ // Legacy X448 (1.3.101.111)
+ else if (privKey instanceof X448PrivateKeyParameters)
+ {
+ return new ECSecretBCPGKey(new BigInteger(1, Arrays.reverseInPlace(((X448PrivateKeyParameters)privKey).getEncoded())));
+ }
+ // NIST, Brainpool etc.
else
{
ECPrivateKeyParameters ecK = (ECPrivateKeyParameters)privKey;
return new ECSecretBCPGKey(ecK.getD());
}
}
+ // Modern X25519 (1.3.6.1.4.1.3029.1.5.1 & 1.3.101.110)
case PublicKeyAlgorithmTags.X25519:
{
- return new X25519SecretBCPGKey(Arrays.reverseInPlace(((X25519PrivateKeyParameters)privKey).getEncoded()));
+ return new X25519SecretBCPGKey(((X25519PrivateKeyParameters)privKey).getEncoded());
}
+ // Modern X448 (1.3.101.111)
case PublicKeyAlgorithmTags.X448:
{
- return new X448SecretBCPGKey(Arrays.reverseInPlace(((X448PrivateKeyParameters)privKey).getEncoded()));
+ return new X448SecretBCPGKey(((X448PrivateKeyParameters)privKey).getEncoded());
}
case PublicKeyAlgorithmTags.ECDSA:
{
ECPrivateKeyParameters ecK = (ECPrivateKeyParameters)privKey;
return new ECSecretBCPGKey(ecK.getD());
}
+ // Legacy EdDSA
case PublicKeyAlgorithmTags.EDDSA_LEGACY:
{
- return new EdSecretBCPGKey(new BigInteger(1, ((Ed25519PrivateKeyParameters)privKey).getEncoded()));
+ // Legacy Ed25519 (1.3.101.112 & 1.3.6.1.4.1.11591.15.1)
+ if (privKey instanceof Ed25519PrivateKeyParameters)
+ {
+ return new EdSecretBCPGKey(new BigInteger(1, ((Ed25519PrivateKeyParameters)privKey).getEncoded()));
+ }
+ // Legacy Ed448 (1.3.101.113)
+ else
+ {
+ return new EdSecretBCPGKey(new BigInteger(1, ((Ed448PrivateKeyParameters) privKey).getEncoded()));
+ }
}
+ // Modern Ed22519 (1.3.6.1.4.1.11591.15.1 & 1.3.101.112)
case PublicKeyAlgorithmTags.Ed25519:
{
return new Ed25519SecretBCPGKey(((Ed25519PrivateKeyParameters)privKey).getEncoded());
}
+ // Modern Ed448 (1.3.101.113)
case PublicKeyAlgorithmTags.Ed448:
{
return new Ed448SecretBCPGKey(((Ed448PrivateKeyParameters)privKey).getEncoded());
@@ -354,98 +417,133 @@ private BCPGKey getPrivateBCPGKey(PGPPublicKey pubKey, AsymmetricKeyParameter pr
}
default:
- throw new PGPException("unknown key class");
+ throw new PGPException("unknown public key algorithm encountered: " + pubKey.getAlgorithm());
}
}
private BCPGKey getPublicBCPGKey(int algorithm, PGPAlgorithmParameters algorithmParameters, AsymmetricKeyParameter pubKey)
throws PGPException
{
- if (pubKey instanceof RSAKeyParameters)
+ switch (algorithm)
{
- RSAKeyParameters rK = (RSAKeyParameters)pubKey;
- return new RSAPublicBCPGKey(rK.getModulus(), rK.getExponent());
- }
- else if (pubKey instanceof DSAPublicKeyParameters)
- {
- DSAPublicKeyParameters dK = (DSAPublicKeyParameters)pubKey;
- DSAParameters dP = dK.getParameters();
- return new DSAPublicBCPGKey(dP.getP(), dP.getQ(), dP.getG(), dK.getY());
- }
- else if (pubKey instanceof ElGamalPublicKeyParameters)
- {
- ElGamalPublicKeyParameters eK = (ElGamalPublicKeyParameters)pubKey;
- ElGamalParameters eS = eK.getParameters();
- return new ElGamalPublicBCPGKey(eS.getP(), eS.getG(), eK.getY());
- }
- else if (pubKey instanceof ECPublicKeyParameters)
- {
- ECPublicKeyParameters ecK = (ECPublicKeyParameters)pubKey;
+ case PublicKeyAlgorithmTags.RSA_GENERAL:
+ case PublicKeyAlgorithmTags.RSA_ENCRYPT:
+ case PublicKeyAlgorithmTags.RSA_SIGN:
+ {
+ RSAKeyParameters rK = (RSAKeyParameters)pubKey;
+ return new RSAPublicBCPGKey(rK.getModulus(), rK.getExponent());
+ }
+ case PublicKeyAlgorithmTags.DSA:
+ {
+ DSAPublicKeyParameters dK = (DSAPublicKeyParameters)pubKey;
+ DSAParameters dP = dK.getParameters();
+ return new DSAPublicBCPGKey(dP.getP(), dP.getQ(), dP.getG(), dK.getY());
+ }
+ case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT:
+ case PublicKeyAlgorithmTags.ELGAMAL_GENERAL:
+ {
+ ElGamalPublicKeyParameters eK = (ElGamalPublicKeyParameters)pubKey;
+ ElGamalParameters eS = eK.getParameters();
+ return new ElGamalPublicBCPGKey(eS.getP(), eS.getG(), eK.getY());
+ }
+ // NIST, Brainpool, Legacy X25519, Legacy X448
+ case PublicKeyAlgorithmTags.ECDH:
+ {
+ // Legacy X25519 (1.3.6.1.4.1.3029.1.5.1 & 1.3.101.110)
+ if (pubKey instanceof X25519PublicKeyParameters)
+ {
+ byte[] pointEnc = new byte[1 + X25519PublicKeyParameters.KEY_SIZE];
+ pointEnc[0] = 0x40;
+ ((X25519PublicKeyParameters)pubKey).encode(pointEnc, 1);
- // TODO Should we have a way to recognize named curves when the name is missing?
- ECNamedDomainParameters parameters = (ECNamedDomainParameters)ecK.getParameters();
+ PGPKdfParameters kdfParams = implGetKdfParameters(CryptlibObjectIdentifiers.curvey25519, algorithmParameters);
- if (algorithm == PGPPublicKey.ECDH)
- {
+ return new ECDHPublicBCPGKey(CryptlibObjectIdentifiers.curvey25519, new BigInteger(1, pointEnc),
+ kdfParams.getHashAlgorithm(), kdfParams.getSymmetricWrapAlgorithm());
+ }
+ // Legacy X448 (1.3.101.111)
+ if (pubKey instanceof X448PublicKeyParameters)
+ {
+ byte[] pointEnc = new byte[1 + X448PublicKeyParameters.KEY_SIZE];
+ pointEnc[0] = 0x40;
+ ((X448PublicKeyParameters)pubKey).encode(pointEnc, 1);
+
+ PGPKdfParameters kdfParams = implGetKdfParameters(EdECObjectIdentifiers.id_X448, algorithmParameters);
+
+ return new ECDHPublicBCPGKey(EdECObjectIdentifiers.id_X448, new BigInteger(1, pointEnc),
+ kdfParams.getHashAlgorithm(), kdfParams.getSymmetricWrapAlgorithm());
+ }
+ // NIST, Brainpool etc.
+ ECPublicKeyParameters ecK = (ECPublicKeyParameters)pubKey;
+ ECNamedDomainParameters parameters = (ECNamedDomainParameters)ecK.getParameters();
PGPKdfParameters kdfParams = implGetKdfParameters(parameters.getName(), algorithmParameters);
return new ECDHPublicBCPGKey(parameters.getName(), ecK.getQ(), kdfParams.getHashAlgorithm(),
- kdfParams.getSymmetricWrapAlgorithm());
+ kdfParams.getSymmetricWrapAlgorithm());
}
- else if (algorithm == PGPPublicKey.ECDSA)
+ case PublicKeyAlgorithmTags.ECDSA:
{
+ ECPublicKeyParameters ecK = (ECPublicKeyParameters)pubKey;
+ ECNamedDomainParameters parameters = (ECNamedDomainParameters)ecK.getParameters();
return new ECDSAPublicBCPGKey(parameters.getName(), ecK.getQ());
}
- else
+ // Legacy Ed255519, Legacy Ed448
+ case PublicKeyAlgorithmTags.EDDSA_LEGACY:
{
- throw new PGPException("unknown EC algorithm");
+ // Legacy Ed25519 (1.3.6.1.4.1.11591.15.1 & 1.3.101.112)
+ if (pubKey instanceof Ed25519PublicKeyParameters)
+ {
+ byte[] pointEnc = new byte[1 + Ed25519PublicKeyParameters.KEY_SIZE];
+ pointEnc[0] = 0x40;
+ ((Ed25519PublicKeyParameters)pubKey).encode(pointEnc, 1);
+ return new EdDSAPublicBCPGKey(GNUObjectIdentifiers.Ed25519, new BigInteger(1, pointEnc));
+ }
+ // Legacy Ed448 (1.3.101.113)
+ else if (pubKey instanceof Ed448PublicKeyParameters)
+ {
+ byte[] pointEnc = new byte[1 + Ed448PublicKeyParameters.KEY_SIZE];
+ pointEnc[0] = 0x40;
+ ((Ed448PublicKeyParameters)pubKey).encode(pointEnc, 1);
+ return new EdDSAPublicBCPGKey(EdECObjectIdentifiers.id_Ed448, new BigInteger(1, pointEnc));
+ }
+ else
+ {
+ throw new PGPException("Unknown LegacyEdDSA key type: " + pubKey.getClass().getName());
+ }
+ }
+ // Modern Ed22519 (1.3.6.1.4.1.11591.15.1 & 1.3.101.112)
+ case PublicKeyAlgorithmTags.Ed25519:
+ {
+ byte[] pointEnc = new byte[Ed25519PublicKeyParameters.KEY_SIZE];
+ ((Ed25519PublicKeyParameters)pubKey).encode(pointEnc, 0);
+ return new Ed25519PublicBCPGKey(pointEnc);
+ }
+ // Modern Ed448 (1.3.101.113)
+ case PublicKeyAlgorithmTags.Ed448:
+ {
+ byte[] pointEnc = new byte[Ed448PublicKeyParameters.KEY_SIZE];
+ ((Ed448PublicKeyParameters)pubKey).encode(pointEnc, 0);
+ return new Ed448PublicBCPGKey(pointEnc);
+ }
+ // Modern X25519 (1.3.6.1.4.1.3029.1.5.1 & 1.3.101.110)
+ case PublicKeyAlgorithmTags.X25519:
+ {
+ byte[] pointEnc = new byte[X25519PublicKeyParameters.KEY_SIZE];
+ ((X25519PublicKeyParameters)pubKey).encode(pointEnc, 0);
+ return new X25519PublicBCPGKey(pointEnc);
+ }
+ // Modern X448 (1.3.101.111)
+ case PublicKeyAlgorithmTags.X448:
+ {
+ byte[] pointEnc = new byte[X448PublicKeyParameters.KEY_SIZE];
+ ((X448PublicKeyParameters)pubKey).encode(pointEnc, 0);
+ return new X448PublicBCPGKey(pointEnc);
}
- }
- else if (algorithm == PublicKeyAlgorithmTags.Ed25519)
- {
- byte[] pointEnc = new byte[Ed25519PublicKeyParameters.KEY_SIZE];
- ((Ed25519PublicKeyParameters)pubKey).encode(pointEnc, 0);
- return new Ed25519PublicBCPGKey(pointEnc);
- }
- else if (pubKey instanceof Ed25519PublicKeyParameters)
- {
- byte[] pointEnc = new byte[1 + Ed25519PublicKeyParameters.KEY_SIZE];
- pointEnc[0] = 0x40;
- ((Ed25519PublicKeyParameters)pubKey).encode(pointEnc, 1);
- return new EdDSAPublicBCPGKey(GNUObjectIdentifiers.Ed25519, new BigInteger(1, pointEnc));
- }
- else if (pubKey instanceof Ed448PublicKeyParameters)
- {
- byte[] pointEnc = new byte[Ed448PublicKeyParameters.KEY_SIZE];
- ((Ed448PublicKeyParameters)pubKey).encode(pointEnc, 0);
- return new Ed448PublicBCPGKey(pointEnc);
- }
- else if (algorithm == PublicKeyAlgorithmTags.X25519)
- {
- byte[] pointEnc = new byte[X25519PublicKeyParameters.KEY_SIZE];
- ((X25519PublicKeyParameters)pubKey).encode(pointEnc, 0);
- return new X25519PublicBCPGKey(pointEnc);
- }
- else if (pubKey instanceof X25519PublicKeyParameters)
- {
- byte[] pointEnc = new byte[1 + X25519PublicKeyParameters.KEY_SIZE];
- pointEnc[0] = 0x40;
- ((X25519PublicKeyParameters)pubKey).encode(pointEnc, 1);
-
- PGPKdfParameters kdfParams = implGetKdfParameters(CryptlibObjectIdentifiers.curvey25519, algorithmParameters);
- return new ECDHPublicBCPGKey(CryptlibObjectIdentifiers.curvey25519, new BigInteger(1, pointEnc),
- kdfParams.getHashAlgorithm(), kdfParams.getSymmetricWrapAlgorithm());
- }
- else if (pubKey instanceof X448PublicKeyParameters)
- {
- byte[] pointEnc = new byte[X448PublicKeyParameters.KEY_SIZE];
- ((X448PublicKeyParameters)pubKey).encode(pointEnc, 0);
- return new X448PublicBCPGKey(pointEnc);
- }
- else
- {
- throw new PGPException("unknown key class");
+ default:
+ {
+ throw new PGPException("unknown public key algorithm encountered: " + algorithm);
+ }
}
}
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java
index 66e0179a34..64779da731 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java
@@ -14,7 +14,6 @@
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.DSAPrivateKeySpec;
@@ -76,10 +75,13 @@
import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
import org.bouncycastle.jcajce.util.ProviderJcaJceHelper;
+import org.bouncycastle.jce.interfaces.ElGamalPublicKey;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.rfc7748.X25519;
+import org.bouncycastle.math.ec.rfc7748.X448;
import org.bouncycastle.math.ec.rfc8032.Ed25519;
+import org.bouncycastle.math.ec.rfc8032.Ed448;
import org.bouncycastle.openpgp.PGPAlgorithmParameters;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPKdfParameters;
@@ -194,41 +196,66 @@ public PrivateKey getPrivateKey(PGPPrivateKey privKey)
ECDHPublicBCPGKey ecdhPub = (ECDHPublicBCPGKey)pubPk.getKey();
ECSecretBCPGKey ecdhK = (ECSecretBCPGKey)privPk;
- if (CryptlibObjectIdentifiers.curvey25519.equals(ecdhPub.getCurveOID()))
+ // Legacy XDH on Curve25519 (legacy X25519)
+ // 1.3.6.1.4.1.3029.1.5.1 & 1.3.101.110
+ if (CryptlibObjectIdentifiers.curvey25519.equals(ecdhPub.getCurveOID()) ||
+ EdECObjectIdentifiers.id_X25519.equals(ecdhPub.getCurveOID()))
{
// 'reverse' because the native format for X25519 private keys is little-endian
return implGeneratePrivate("XDH", () -> getPrivateKeyInfo(EdECObjectIdentifiers.id_X25519,
Arrays.reverseInPlace(BigIntegers.asUnsignedByteArray(((ECSecretBCPGKey)privPk).getX()))));
}
+ // Legacy X448 (1.3.101.111)
+ else if (EdECObjectIdentifiers.id_X448.equals(ecdhPub.getCurveOID()))
+ {
+ // 'reverse' because the native format for X448 private keys is little-endian (?)
+ return implGeneratePrivate("XDH", () -> getPrivateKeyInfo(EdECObjectIdentifiers.id_X448,
+ Arrays.reverseInPlace(BigIntegers.asUnsignedByteArray(((ECSecretBCPGKey)privPk).getX()))));
+ }
+ // Brainpool, NIST etc.
else
{
return implGetPrivateKeyEC("ECDH", ecdhPub, ecdhK);
}
}
+ // Modern X25519 (1.3.6.1.4.1.3029.1.5.1 & 1.3.101.110)
case PublicKeyAlgorithmTags.X25519:
{
return implGeneratePrivate("XDH", () -> getPrivateKeyInfo(EdECObjectIdentifiers.id_X25519,
- X25519SecretBCPGKey.LENGTH, Arrays.reverseInPlace(privPk.getEncoded())));
+ X25519SecretBCPGKey.LENGTH, privPk.getEncoded()));
}
+ // Modern X448 (1.3.101.111)
case PublicKeyAlgorithmTags.X448:
{
return implGeneratePrivate("XDH", () -> getPrivateKeyInfo(EdECObjectIdentifiers.id_X448,
- X448SecretBCPGKey.LENGTH, Arrays.reverseInPlace(privPk.getEncoded())));
+ X448SecretBCPGKey.LENGTH, privPk.getEncoded()));
}
case PublicKeyAlgorithmTags.ECDSA:
{
return implGetPrivateKeyEC("ECDSA", (ECDSAPublicBCPGKey)pubPk.getKey(), (ECSecretBCPGKey)privPk);
}
+ // Legacy EdDSA (legacy Ed448, legacy Ed25519)
case PublicKeyAlgorithmTags.EDDSA_LEGACY:
{
+ EdDSAPublicBCPGKey eddsaPub = (EdDSAPublicBCPGKey) pubPk.getKey();
+ // Legacy Ed448 (1.3.101.113)
+ if (EdECObjectIdentifiers.id_Ed448.equals(eddsaPub.getCurveOID()))
+ {
+ return implGeneratePrivate("EdDSA", () -> getPrivateKeyInfo(EdECObjectIdentifiers.id_Ed448,
+ BigIntegers.asUnsignedByteArray(Ed448.SECRET_KEY_SIZE, ((EdSecretBCPGKey)privPk).getX())));
+ }
+ // Legacy Ed25519
+ // 1.3.6.1.4.1.11591.15.1 & 1.3.101.112
return implGeneratePrivate("EdDSA", () -> getPrivateKeyInfo(EdECObjectIdentifiers.id_Ed25519,
BigIntegers.asUnsignedByteArray(Ed25519.SECRET_KEY_SIZE, ((EdSecretBCPGKey)privPk).getX())));
}
+ // Modern Ed25519 (1.3.6.1.4.1.11591.15.1 & 1.3.101.112)
case PublicKeyAlgorithmTags.Ed25519:
{
return implGeneratePrivate("EdDSA", () -> getPrivateKeyInfo(EdECObjectIdentifiers.id_Ed25519,
Ed25519SecretBCPGKey.LENGTH, privPk.getEncoded()));
}
+ // Modern Ed448 (1.3.101.113)
case PublicKeyAlgorithmTags.Ed448:
{
return implGeneratePrivate("EdDSA", () -> getPrivateKeyInfo(EdECObjectIdentifiers.id_Ed448,
@@ -289,30 +316,54 @@ public PublicKey getPublicKey(PGPPublicKey publicKey)
{
ECDHPublicBCPGKey ecdhK = (ECDHPublicBCPGKey)publicPk.getKey();
+ // Legacy XDH on Curve25519 (legacy X25519)
+ // 1.3.6.1.4.1.3029.1.5.1 & 1.3.101.110
if (ecdhK.getCurveOID().equals(CryptlibObjectIdentifiers.curvey25519))
{
return get25519PublicKey(ecdhK.getEncodedPoint(), EdECObjectIdentifiers.id_X25519, "XDH", "Curve");
}
+ // Legacy X448 (1.3.101.111)
+ else if (ecdhK.getCurveOID().equals(EdECObjectIdentifiers.id_X448))
+ {
+ return get448PublicKey(ecdhK.getEncodedPoint(), EdECObjectIdentifiers.id_X448, "XDH", "Curve");
+ }
+ // Brainpool, NIST etc.
else
{
return implGetPublicKeyEC("ECDH", ecdhK);
}
}
+ // Modern X25519 (1.3.6.1.4.1.3029.1.5.1 & 1.3.101.110)
case PublicKeyAlgorithmTags.X25519:
{
return implGetPublicKeyX509(publicPk.getKey().getEncoded(), 0, EdECObjectIdentifiers.id_X25519, "XDH");
}
+ // Modern X448 (1.3.101.111)
case PublicKeyAlgorithmTags.X448:
{
return implGetPublicKeyX509(publicPk.getKey().getEncoded(), 0, EdECObjectIdentifiers.id_X448, "XDH");
}
case PublicKeyAlgorithmTags.ECDSA:
- return implGetPublicKeyEC("ECDSA", (ECDSAPublicBCPGKey)publicPk.getKey());
-
+ {
+ return implGetPublicKeyEC("ECDSA", (ECDSAPublicBCPGKey) publicPk.getKey());
+ }
+ // Legacy EdDSA (legacy Ed448, legacy Ed25519)
case PublicKeyAlgorithmTags.EDDSA_LEGACY:
{
- return get25519PublicKey(((EdDSAPublicBCPGKey)publicPk.getKey()).getEncodedPoint(), EdECObjectIdentifiers.id_Ed25519, "EdDSA", "Ed");
+ EdDSAPublicBCPGKey eddsaKey = (EdDSAPublicBCPGKey) publicPk.getKey();
+ // Legacy Ed448 (1.3.101.113)
+ if (EdECObjectIdentifiers.id_Ed448.equals(eddsaKey.getCurveOID()))
+ {
+ return get448PublicKey(eddsaKey.getEncodedPoint(), EdECObjectIdentifiers.id_Ed448, "EdDSA", "Ed");
+ }
+ // Legacy Ed25519
+ // 1.3.6.1.4.1.11591.15.1 & 1.3.101.112
+ else
+ {
+ return get25519PublicKey(eddsaKey.getEncodedPoint(), EdECObjectIdentifiers.id_Ed25519, "EdDSA", "Ed");
+ }
}
+ // Modern Ed25519 (1.3.6.1.4.1.11591.15.1 & 1.3.101.112)
case PublicKeyAlgorithmTags.Ed25519:
{
BCPGKey key = publicPk.getKey();
@@ -327,6 +378,7 @@ public PublicKey getPublicKey(PGPPublicKey publicKey)
0, EdECObjectIdentifiers.id_Ed25519, "EdDSA");
}
}
+ // Modern Ed448 (1.3.101.113)
case PublicKeyAlgorithmTags.Ed448:
{
return implGetPublicKeyX509(BigIntegers.asUnsignedByteArray(new BigInteger(1, publicPk.getKey().getEncoded())),
@@ -380,7 +432,6 @@ private BCPGKey getPrivateBCPGKey(PrivateKey privKey, BCPGKeyOperation operation
try
{
- // 'reverse' because the native format for X25519 private keys is little-endian
return operation.getBCPGKey(ASN1OctetString.getInstance(pInfo.parsePrivateKey()).getOctets());
}
catch (IOException e)
@@ -409,32 +460,35 @@ private BCPGKey getPrivateBCPGKey(PGPPublicKey pub, PrivateKey privKey)
}
else
{
- // 'reverse' because the native format for X25519 private keys is little-endian
+ // 'reverse' because the native format for X25519,X448 private keys is little-endian
return getPrivateBCPGKey(privKey, (pInfoEncoded) -> new ECSecretBCPGKey(new BigInteger(1, Arrays.reverse(pInfoEncoded))));
}
}
+ // Modern X25519 (1.3.6.1.4.1.3029.1.5.1 & 1.3.101.110)
case PublicKeyAlgorithmTags.X25519:
{
- // 'reverse' because the native format for X25519 private keys is little-endian
- return getPrivateBCPGKey(privKey, (pInfoEncoded) -> new X25519SecretBCPGKey(Arrays.reverse(pInfoEncoded)));
+ return getPrivateBCPGKey(privKey, (pInfoEncoded) -> new X25519SecretBCPGKey(pInfoEncoded));
}
+ // Modern X448 (1.3.101.111)
case PublicKeyAlgorithmTags.X448:
{
- // 'reverse' because the native format for X448 private keys is little-endian
- return getPrivateBCPGKey(privKey, (pInfoEncoded) -> new X448SecretBCPGKey(Arrays.reverse(pInfoEncoded)));
+ return getPrivateBCPGKey(privKey, (pInfoEncoded) -> new X448SecretBCPGKey(pInfoEncoded));
}
case PublicKeyAlgorithmTags.ECDSA:
{
return new ECSecretBCPGKey(((ECPrivateKey)privKey).getS());
}
+ // Legacy EdDSA (legacy Ed448, legacy Ed25519)
case PublicKeyAlgorithmTags.EDDSA_LEGACY:
{
return getPrivateBCPGKey(privKey, (pInfoEncoded) -> new EdSecretBCPGKey(new BigInteger(1, pInfoEncoded)));
}
+ // Modern Ed25519 (1.3.6.1.4.1.11591.15.1 & 1.3.101.112)
case PublicKeyAlgorithmTags.Ed25519:
{
return getPrivateBCPGKey(privKey, Ed25519SecretBCPGKey::new);
}
+ // Modern Ed448 (1.3.101.113)
case PublicKeyAlgorithmTags.Ed448:
{
return getPrivateBCPGKey(privKey, Ed448SecretBCPGKey::new);
@@ -453,88 +507,166 @@ private BCPGKey getPrivateBCPGKey(PGPPublicKey pub, PrivateKey privKey)
return new RSASecretBCPGKey(rsK.getPrivateExponent(), rsK.getPrimeP(), rsK.getPrimeQ());
}
default:
- throw new PGPException("unknown key class");
+ throw new PGPException("unknown public key algorithm encountered: " + pub.getAlgorithm());
}
}
private BCPGKey getPublicBCPGKey(int algorithm, PGPAlgorithmParameters algorithmParameters, PublicKey pubKey)
throws PGPException
{
- if (pubKey instanceof RSAPublicKey)
- {
- RSAPublicKey rK = (RSAPublicKey)pubKey;
- return new RSAPublicBCPGKey(rK.getModulus(), rK.getPublicExponent());
- }
- else if (pubKey instanceof DSAPublicKey)
- {
- DSAPublicKey dK = (DSAPublicKey)pubKey;
- DSAParams dP = dK.getParams();
- return new DSAPublicBCPGKey(dP.getP(), dP.getQ(), dP.getG(), dK.getY());
- }
- else if (pubKey instanceof DHPublicKey)
+ switch (algorithm)
{
- DHPublicKey eK = (DHPublicKey)pubKey;
- DHParameterSpec eS = eK.getParams();
- return new ElGamalPublicBCPGKey(eS.getP(), eS.getG(), eK.getY());
- }
- else if (pubKey instanceof ECPublicKey)
- {
- SubjectPublicKeyInfo keyInfo = SubjectPublicKeyInfo.getInstance(pubKey.getEncoded());
+ case PublicKeyAlgorithmTags.RSA_GENERAL:
+ case PublicKeyAlgorithmTags.RSA_ENCRYPT:
+ case PublicKeyAlgorithmTags.RSA_SIGN:
+ {
+ RSAPublicKey rK = (RSAPublicKey) pubKey;
+ return new RSAPublicBCPGKey(rK.getModulus(), rK.getPublicExponent());
+ }
+ case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT:
+ case PublicKeyAlgorithmTags.ELGAMAL_GENERAL:
+ {
+ ElGamalPublicKey egK = (ElGamalPublicKey) pubKey;
+ return new ElGamalPublicBCPGKey(egK.getParameters().getP(), egK.getParameters().getG(), egK.getY());
+ }
+ case PublicKeyAlgorithmTags.DSA:
+ {
+ DSAPublicKey dK = (DSAPublicKey) pubKey;
+ DSAParams dP = dK.getParams();
+ return new DSAPublicBCPGKey(dP.getP(), dP.getQ(), dP.getG(), dK.getY());
+ }
- // TODO: should probably match curve by comparison as well
- ASN1ObjectIdentifier curveOid = ASN1ObjectIdentifier.getInstance(keyInfo.getAlgorithm().getParameters());
+ case PublicKeyAlgorithmTags.DIFFIE_HELLMAN:
+ {
+ DHPublicKey eK = (DHPublicKey) pubKey;
+ DHParameterSpec eS = eK.getParams();
+ return new ElGamalPublicBCPGKey(eS.getP(), eS.getG(), eK.getY());
+ }
- X9ECParametersHolder params = ECNamedCurveTable.getByOIDLazy(curveOid);
+ case PublicKeyAlgorithmTags.ECDH:
+ case PublicKeyAlgorithmTags.ECDSA:
+ {
+ SubjectPublicKeyInfo keyInfo = SubjectPublicKeyInfo.getInstance(pubKey.getEncoded());
+
+ // TODO: should probably match curve by comparison as well
+ ASN1ObjectIdentifier curveOid = ASN1ObjectIdentifier.getInstance(keyInfo.getAlgorithm().getParameters());
+ if (curveOid == null)
+ {
+ // Legacy XDH on Curve25519 (legacy X25519)
+ // 1.3.6.1.4.1.3029.1.5.1 & 1.3.101.110
+ if (pubKey.getAlgorithm().regionMatches(true, 0, "X2", 0, 2))
+ {
+ PGPKdfParameters kdfParams = implGetKdfParameters(CryptlibObjectIdentifiers.curvey25519, algorithmParameters);
+
+ return new ECDHPublicBCPGKey(CryptlibObjectIdentifiers.curvey25519, new BigInteger(1, getPointEncUncompressed(pubKey, X25519.SCALAR_SIZE)),
+ kdfParams.getHashAlgorithm(), kdfParams.getSymmetricWrapAlgorithm());
+ }
+ // Legacy X448 (1.3.101.111)
+ if (pubKey.getAlgorithm().regionMatches(true, 0, "X4", 0, 2))
+ {
+
+ PGPKdfParameters kdfParams = implGetKdfParameters(EdECObjectIdentifiers.id_X448, algorithmParameters);
+
+ return new ECDHPublicBCPGKey(EdECObjectIdentifiers.id_X448, new BigInteger(1, getPointEncUncompressed(pubKey, X448.SCALAR_SIZE)),
+ kdfParams.getHashAlgorithm(), kdfParams.getSymmetricWrapAlgorithm());
+ }
+ // sun.security.ec.XDHPublicKeyImpl returns "XDH" for getAlgorithm()
+ // In this case we need to determine the curve by looking at the length of the encoding :/
+ else
+ {
+ // Legacy X25519 (1.3.6.1.4.1.3029.1.5.1 & 1.3.101.110)
+ if (X25519.SCALAR_SIZE + 12 == pubKey.getEncoded().length) // + 12 for some reason
+ {
+ PGPKdfParameters kdfParams = implGetKdfParameters(CryptlibObjectIdentifiers.curvey25519, algorithmParameters);
+
+ return new ECDHPublicBCPGKey(CryptlibObjectIdentifiers.curvey25519, new BigInteger(1, getPointEncUncompressed(pubKey, X25519.SCALAR_SIZE)),
+ kdfParams.getHashAlgorithm(), kdfParams.getSymmetricWrapAlgorithm());
+ }
+ // Legacy X448 (1.3.101.111)
+ else
+ {
+ PGPKdfParameters kdfParams = implGetKdfParameters(EdECObjectIdentifiers.id_X448, algorithmParameters);
+
+ return new ECDHPublicBCPGKey(EdECObjectIdentifiers.id_X448, new BigInteger(1, getPointEncUncompressed(pubKey, X448.SCALAR_SIZE)),
+ kdfParams.getHashAlgorithm(), kdfParams.getSymmetricWrapAlgorithm());
+ }
+ }
+ }
+
+ X9ECParametersHolder params = ECNamedCurveTable.getByOIDLazy(curveOid);
+
+ ASN1OctetString key = new DEROctetString(keyInfo.getPublicKeyData().getBytes());
+ X9ECPoint derQ = new X9ECPoint(params.getCurve(), key);
+
+ if (algorithm == PGPPublicKey.ECDH)
+ {
+
+ PGPKdfParameters kdfParams = implGetKdfParameters(curveOid, algorithmParameters);
+
+ return new ECDHPublicBCPGKey(curveOid, derQ.getPoint(), kdfParams.getHashAlgorithm(),
+ kdfParams.getSymmetricWrapAlgorithm());
+ }
+ else
+ {
+ return new ECDSAPublicBCPGKey(curveOid, derQ.getPoint());
+ }
+ }
- ASN1OctetString key = new DEROctetString(keyInfo.getPublicKeyData().getBytes());
- X9ECPoint derQ = new X9ECPoint(params.getCurve(), key);
+ case PublicKeyAlgorithmTags.EDDSA_LEGACY:
+ {
+ // Legacy Ed25519 (1.3.6.1.4.1.11591.15.1 & 1.3.101.112)
+ if (pubKey.getAlgorithm().regionMatches(true, 0, "ED2", 0, 3))
+ {
+ return new EdDSAPublicBCPGKey(GNUObjectIdentifiers.Ed25519, new BigInteger(1, getPointEncUncompressed(pubKey, Ed25519.PUBLIC_KEY_SIZE)));
+ }
+ // Legacy Ed448 (1.3.101.113)
+ if (pubKey.getAlgorithm().regionMatches(true, 0, "ED4", 0, 3))
+ {
+ return new EdDSAPublicBCPGKey(EdECObjectIdentifiers.id_Ed448, new BigInteger(1, getPointEncUncompressed(pubKey, Ed448.PUBLIC_KEY_SIZE)));
+ }
+ // Manual matching on curve encoding length
+ else
+ {
+ // sun.security.ec.ed.EdDSAPublicKeyImpl returns "EdDSA" for getAlgorithm()
+ // if algorithm is just EdDSA, we need to detect the curve based on encoding length :/
+ if (pubKey.getEncoded().length == 12 + Ed25519.PUBLIC_KEY_SIZE) // +12 for some reason
+ {
+ // Legacy Ed25519 (1.3.6.1.4.1.11591.15.1 & 1.3.101.112)
+ return new EdDSAPublicBCPGKey(GNUObjectIdentifiers.Ed25519, new BigInteger(1, getPointEncUncompressed(pubKey, Ed25519.PUBLIC_KEY_SIZE)));
+ }
+ else
+ {
+ // Legacy Ed448 (1.3.101.113)
+ return new EdDSAPublicBCPGKey(EdECObjectIdentifiers.id_Ed448, new BigInteger(1, getPointEncUncompressed(pubKey, Ed448.PUBLIC_KEY_SIZE)));
+ }
+ }
+ }
- if (algorithm == PGPPublicKey.ECDH)
+ // Modern Ed25519 (1.3.6.1.4.1.11591.15.1 & 1.3.101.112)
+ case PublicKeyAlgorithmTags.Ed25519:
{
- PGPKdfParameters kdfParams = implGetKdfParameters(curveOid, algorithmParameters);
+ return getPublicBCPGKey(pubKey, Ed25519PublicBCPGKey.LENGTH, Ed25519PublicBCPGKey::new);
+ }
- return new ECDHPublicBCPGKey(curveOid, derQ.getPoint(), kdfParams.getHashAlgorithm(),
- kdfParams.getSymmetricWrapAlgorithm());
+ // Modern Ed448 (1.3.101.113)
+ case PublicKeyAlgorithmTags.Ed448:
+ {
+ return getPublicBCPGKey(pubKey, Ed448PublicBCPGKey.LENGTH, Ed448PublicBCPGKey::new);
}
- else if (algorithm == PGPPublicKey.ECDSA)
+
+ // Modern X25519 (1.3.6.1.4.1.3029.1.5.1 & 1.3.101.110)
+ case PublicKeyAlgorithmTags.X25519:
{
- return new ECDSAPublicBCPGKey(curveOid, derQ.getPoint());
+ return getPublicBCPGKey(pubKey, X25519PublicBCPGKey.LENGTH, X25519PublicBCPGKey::new);
}
- else
+ // Modern X448 (1.3.101.111)
+ case PublicKeyAlgorithmTags.X448:
{
- throw new PGPException("unknown EC algorithm");
+ return getPublicBCPGKey(pubKey, X448PublicBCPGKey.LENGTH, X448PublicBCPGKey::new);
}
- }
- else if (algorithm == PGPPublicKey.Ed25519)
- {
- return getPublicBCPGKey(pubKey, Ed25519PublicBCPGKey.LENGTH, Ed25519PublicBCPGKey::new);
- }
- else if (pubKey.getAlgorithm().regionMatches(true, 0, "ED2", 0, 3))
- {
- return new EdDSAPublicBCPGKey(GNUObjectIdentifiers.Ed25519, new BigInteger(1, getPointEncUncompressed(pubKey, Ed25519.PUBLIC_KEY_SIZE)));
- }
- else if (algorithm == PGPPublicKey.X25519)
- {
- return getPublicBCPGKey(pubKey, X25519PublicBCPGKey.LENGTH, X25519PublicBCPGKey::new);
- }
- else if (pubKey.getAlgorithm().regionMatches(true, 0, "X2", 0, 2))
- {
- PGPKdfParameters kdfParams = implGetKdfParameters(CryptlibObjectIdentifiers.curvey25519, algorithmParameters);
- return new ECDHPublicBCPGKey(CryptlibObjectIdentifiers.curvey25519, new BigInteger(1, getPointEncUncompressed(pubKey, X25519.SCALAR_SIZE)),
- kdfParams.getHashAlgorithm(), kdfParams.getSymmetricWrapAlgorithm());
- }
- else if (algorithm == PGPPublicKey.Ed448)
- {
- return getPublicBCPGKey(pubKey, Ed448PublicBCPGKey.LENGTH, Ed448PublicBCPGKey::new);
- }
- else if (algorithm == PGPPublicKey.X448)
- {
- return getPublicBCPGKey(pubKey, X448PublicBCPGKey.LENGTH, X448PublicBCPGKey::new);
- }
- else
- {
- throw new PGPException("unknown key class");
+ default:
+ throw new PGPException("unknown public key algorithm encountered: " + algorithm);
}
}
@@ -633,4 +765,17 @@ private PublicKey get25519PublicKey(BigInteger x, ASN1ObjectIdentifier algorithm
}
return implGetPublicKeyX509(pEnc, 1, algorithm, keyAlgorithm);
}
+
+ private PublicKey get448PublicKey(BigInteger x, ASN1ObjectIdentifier algorithm, String keyAlgorithm, String name)
+ throws PGPException, GeneralSecurityException, IOException
+ {
+ byte[] pEnc = BigIntegers.asUnsignedByteArray(x);
+
+ // skip the 0x40 header byte.
+ if (pEnc.length < 1 || 0x40 != pEnc[0])
+ {
+ throw new IllegalArgumentException("Invalid " + name + "448 public key");
+ }
+ return implGetPublicKeyX509(pEnc, 1, algorithm, keyAlgorithm);
+ }
}
From f23fc367e6aded2d2aaea37583695ab7c8ddd878 Mon Sep 17 00:00:00 2001
From: Paul Schaub
Date: Tue, 21 May 2024 15:02:47 +0200
Subject: [PATCH 0082/1644] Document reversed MPI encoding / little-endian
native encoding
---
pg/src/main/java/org/bouncycastle/bcpg/ECSecretBCPGKey.java | 5 +++++
.../main/java/org/bouncycastle/bcpg/X25519SecretBCPGKey.java | 2 ++
2 files changed, 7 insertions(+)
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/ECSecretBCPGKey.java b/pg/src/main/java/org/bouncycastle/bcpg/ECSecretBCPGKey.java
index dd70dab1d7..a5b498f985 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/ECSecretBCPGKey.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/ECSecretBCPGKey.java
@@ -8,6 +8,9 @@
* This type is for use with {@link PublicKeyAlgorithmTags#ECDH} or {@link PublicKeyAlgorithmTags#ECDSA}.
* The specific curve is identified by providing an OID.
* Regarding X25519, X448, consider the following:
+ * ECDH keys using curve448 are unspecified.
+ * ECDH secret keys using curve25519 use big-endian MPI encoding, contrary to {@link X25519SecretBCPGKey} which uses
+ * native encoding.
* Modern implementations use dedicated key types {@link X25519SecretBCPGKey}, {@link X448SecretBCPGKey} along with
* dedicated algorithm tags {@link PublicKeyAlgorithmTags#X25519}, {@link PublicKeyAlgorithmTags#X448}.
* If you want to be compatible with legacy applications however, you should use this class instead.
@@ -17,6 +20,8 @@
* Crypto-Refresh - Algorithm-Specific Parts for ECDH Keys
* @see
* Crypto-Refresh - Algorithm-Specific Parts for ECDSA Keys
+ * @see
+ * Crypto-Refresh - Curve25519Legacy ECDH Secret Key Material (deprecated)
*/
public class ECSecretBCPGKey
extends BCPGObject
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/X25519SecretBCPGKey.java b/pg/src/main/java/org/bouncycastle/bcpg/X25519SecretBCPGKey.java
index c023d7abbc..0840663fb1 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/X25519SecretBCPGKey.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/X25519SecretBCPGKey.java
@@ -8,6 +8,8 @@
* Note however, that legacy implementations might not understand this key type yet.
* For a key type compatible with legacy v4 implementations, see {@link ECDHPublicBCPGKey} with
* {@link PublicKeyAlgorithmTags#ECDH}.
+ * Note: Contrary to {@link ECSecretBCPGKey} using {@link PublicKeyAlgorithmTags#ECDH}, which uses big-endian
+ * MPI encoding to encode the secret key material, {@link X25519SecretBCPGKey} uses native little-endian encoding.
*
* @see
* Crypto-Refresh - Algorithm-Specific Part for X25519 Keys
From 9c97d4a0c7023f8b6b23fb822af58bb88436b50a Mon Sep 17 00:00:00 2001
From: Paul Schaub
Date: Tue, 21 May 2024 15:04:43 +0200
Subject: [PATCH 0083/1644] Fix javadoc reference to ECSecretBCPGKey
---
pg/src/main/java/org/bouncycastle/bcpg/X25519SecretBCPGKey.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/X25519SecretBCPGKey.java b/pg/src/main/java/org/bouncycastle/bcpg/X25519SecretBCPGKey.java
index 0840663fb1..17043353af 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/X25519SecretBCPGKey.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/X25519SecretBCPGKey.java
@@ -6,7 +6,7 @@
* Secret key of type {@link PublicKeyAlgorithmTags#X25519}.
* This type was introduced with Crypto-Refresh and can be used with v4, v6 keys.
* Note however, that legacy implementations might not understand this key type yet.
- * For a key type compatible with legacy v4 implementations, see {@link ECDHPublicBCPGKey} with
+ * For a key type compatible with legacy v4 implementations, see {@link ECSecretBCPGKey} with
* {@link PublicKeyAlgorithmTags#ECDH}.
* Note: Contrary to {@link ECSecretBCPGKey} using {@link PublicKeyAlgorithmTags#ECDH}, which uses big-endian
* MPI encoding to encode the secret key material, {@link X25519SecretBCPGKey} uses native little-endian encoding.
From 94b91e18475572d539a7c0adbeced56bb24d60af Mon Sep 17 00:00:00 2001
From: gefeili
Date: Wed, 22 May 2024 07:38:13 +0930
Subject: [PATCH 0084/1644] Add EdECObjectIdentifiers.id_X25519 to
Bc/JcePublicKeyKeyEncryptionMethodGenerator. Remove providedKeyAlgorithm from
PGPSignatureGenerator and PGPV3SignatureGenerator.
---
.../openpgp/PGPSignatureGenerator.java | 37 ++++++++++---------
.../openpgp/PGPV3SignatureGenerator.java | 10 ++---
...PublicKeyKeyEncryptionMethodGenerator.java | 3 +-
...PublicKeyKeyEncryptionMethodGenerator.java | 3 +-
4 files changed, 28 insertions(+), 25 deletions(-)
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPSignatureGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPSignatureGenerator.java
index 3f34aaac27..3f7544515f 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/PGPSignatureGenerator.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPSignatureGenerator.java
@@ -28,7 +28,7 @@ public class PGPSignatureGenerator
private SignatureSubpacket[] hashed = new SignatureSubpacket[0];
private PGPContentSignerBuilder contentSignerBuilder;
private PGPContentSigner contentSigner;
- private int providedKeyAlgorithm = -1;
+ //private int providedKeyAlgorithm = -1;
/**
* Create a signature generator built on the passed in contentSignerBuilder.
@@ -58,10 +58,10 @@ public void init(
sigType = contentSigner.getType();
lastb = 0;
- if (providedKeyAlgorithm >= 0 && providedKeyAlgorithm != contentSigner.getKeyAlgorithm())
- {
- throw new PGPException("key algorithm mismatch");
- }
+// if (providedKeyAlgorithm >= 0 && providedKeyAlgorithm != contentSigner.getKeyAlgorithm())
+// {
+// throw new PGPException("key algorithm mismatch");
+// }
}
public void setHashedSubpackets(
@@ -167,34 +167,35 @@ public PGPSignature generate()
throw new PGPException("exception encoding hashed data.", e);
}
-
byte[] trailer = sOut.toByteArray();
blockUpdate(trailer, 0, trailer.length);
-
- if (contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.RSA_SIGN
- || contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.RSA_GENERAL) // an RSA signature
+ switch (contentSigner.getKeyAlgorithm())
+ {
+ case PublicKeyAlgorithmTags.RSA_SIGN:
+ case PublicKeyAlgorithmTags.RSA_GENERAL:
{
sigValues = new MPInteger[1];
sigValues[0] = new MPInteger(new BigInteger(1, contentSigner.getSignature()));
+ break;
}
- else if (contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.EDDSA_LEGACY)
+ case PublicKeyAlgorithmTags.EDDSA_LEGACY:
{
byte[] enc = contentSigner.getSignature();
sigValues = new MPInteger[]{
new MPInteger(new BigInteger(1, Arrays.copyOfRange(enc, 0, enc.length / 2))),
new MPInteger(new BigInteger(1, Arrays.copyOfRange(enc, enc.length / 2, enc.length)))
};
+ break;
}
- else if (contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.Ed25519 ||
- contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.Ed448)
- {
+ case PublicKeyAlgorithmTags.Ed25519:
+ case PublicKeyAlgorithmTags.Ed448:
// Contrary to EDDSA_LEGACY, the new PK algorithms Ed25519, Ed448 do not use MPI encoding
sigValues = null;
- }
- else
- {
+ break;
+ default:
sigValues = PGPUtil.dsaSigToMpi(contentSigner.getSignature());
+ break;
}
byte[] digest = contentSigner.getDigest();
@@ -206,13 +207,13 @@ else if (contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.Ed25519 ||
if (sigValues != null)
{
return new PGPSignature(new SignaturePacket(sigType, contentSigner.getKeyID(), contentSigner.getKeyAlgorithm(),
- contentSigner.getHashAlgorithm(), hPkts, unhPkts, fingerPrint, sigValues));
+ contentSigner.getHashAlgorithm(), hPkts, unhPkts, fingerPrint, sigValues));
}
else
{
// Ed25519, Ed448 use raw encoding instead of MPI
return new PGPSignature(new SignaturePacket(4, sigType, contentSigner.getKeyID(), contentSigner.getKeyAlgorithm(),
- contentSigner.getHashAlgorithm(), hPkts, unhPkts, fingerPrint, contentSigner.getSignature()));
+ contentSigner.getHashAlgorithm(), hPkts, unhPkts, fingerPrint, contentSigner.getSignature()));
}
}
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPV3SignatureGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPV3SignatureGenerator.java
index c743ce901c..cb171b9ebf 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/PGPV3SignatureGenerator.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPV3SignatureGenerator.java
@@ -19,7 +19,7 @@ public class PGPV3SignatureGenerator
{
private PGPContentSignerBuilder contentSignerBuilder;
private PGPContentSigner contentSigner;
- private int providedKeyAlgorithm = -1;
+// private int providedKeyAlgorithm = -1;
/**
* Create a signature generator built on the passed in contentSignerBuilder.
@@ -49,10 +49,10 @@ public void init(
sigType = contentSigner.getType();
lastb = 0;
- if (providedKeyAlgorithm >= 0 && providedKeyAlgorithm != contentSigner.getKeyAlgorithm())
- {
- throw new PGPException("key algorithm mismatch");
- }
+// if (providedKeyAlgorithm >= 0 && providedKeyAlgorithm != contentSigner.getKeyAlgorithm())
+// {
+// throw new PGPException("key algorithm mismatch");
+// }
}
/**
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java
index 7d8d63825d..8621c91d56 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java
@@ -32,6 +32,7 @@
import org.bouncycastle.crypto.params.X25519PublicKeyParameters;
import org.bouncycastle.crypto.params.X448KeyGenerationParameters;
import org.bouncycastle.crypto.params.X448PublicKeyParameters;
+import org.bouncycastle.internal.asn1.edec.EdECObjectIdentifiers;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.operator.PGPPad;
@@ -85,7 +86,7 @@ protected byte[] encryptSessionInfo(PGPPublicKey pubKey, byte[] sessionInfo)
{
ECDHPublicBCPGKey ecPubKey = (ECDHPublicBCPGKey)pubKeyPacket.getKey();
byte[] userKeyingMaterial = RFC6637Utils.createUserKeyingMaterial(pubKeyPacket, new BcKeyFingerprintCalculator());
- if (ecPubKey.getCurveOID().equals(CryptlibObjectIdentifiers.curvey25519))
+ if (ecPubKey.getCurveOID().equals(CryptlibObjectIdentifiers.curvey25519) || ecPubKey.getCurveOID().equals(EdECObjectIdentifiers.id_X25519))
{
AsymmetricCipherKeyPair ephKp = getAsymmetricCipherKeyPair(new X25519KeyPairGenerator(), new X25519KeyGenerationParameters(random));
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyKeyEncryptionMethodGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyKeyEncryptionMethodGenerator.java
index 72bb0cb56c..c41f6312da 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyKeyEncryptionMethodGenerator.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyKeyEncryptionMethodGenerator.java
@@ -25,6 +25,7 @@
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.bcpg.PublicKeyPacket;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
+import org.bouncycastle.internal.asn1.edec.EdECObjectIdentifiers;
import org.bouncycastle.jcajce.spec.HybridValueParameterSpec;
import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
@@ -100,7 +101,7 @@ protected byte[] encryptSessionInfo(PGPPublicKey pubKey, byte[] sessionInfo)
ECDHPublicBCPGKey ecKey = (ECDHPublicBCPGKey)pubKey.getPublicKeyPacket().getKey();
String keyEncryptionOID = RFC6637Utils.getKeyEncryptionOID(ecKey.getSymmetricKeyAlgorithm()).getId();
PublicKeyPacket pubKeyPacket = pubKey.getPublicKeyPacket();
- if (ecKey.getCurveOID().equals(CryptlibObjectIdentifiers.curvey25519))
+ if (ecKey.getCurveOID().equals(CryptlibObjectIdentifiers.curvey25519) || ecKey.getCurveOID().equals(EdECObjectIdentifiers.id_X25519))
{
return getEncryptSessionInfo(pubKeyPacket, "X25519", cryptoPublicKey, keyEncryptionOID,
ecKey.getSymmetricKeyAlgorithm(), sessionInfo, RFC6637Utils.getXDHAlgorithm(pubKeyPacket),
From 40979ea0f3b6ca6570b3b1a67a8f602d26a6271e Mon Sep 17 00:00:00 2001
From: gefeili
Date: Wed, 22 May 2024 13:17:11 +0930
Subject: [PATCH 0085/1644] Changes around StreamUtil.
---
.../bouncycastle/bcpg/ArmoredInputStream.java | 2 +-
.../org/bouncycastle/bcpg/BCPGOutputStream.java | 16 ++++++++++++++++
.../java/org/bouncycastle/bcpg/MPInteger.java | 5 +----
.../org/bouncycastle/bcpg/PublicKeyPacket.java | 9 ++-------
.../java/org/bouncycastle/bcpg/StreamUtil.java | 5 +----
.../openpgp/test/PGPKeyRingTest.java | 1 -
6 files changed, 21 insertions(+), 17 deletions(-)
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java b/pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java
index 3bf1cd3898..817829d128 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java
@@ -24,7 +24,7 @@
public class ArmoredInputStream
extends InputStream
{
- /*
+ /**
* set up the decoding table.
*/
private static final byte[] decodingTable;
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/BCPGOutputStream.java b/pg/src/main/java/org/bouncycastle/bcpg/BCPGOutputStream.java
index 6ce35b7454..a1b7f92da5 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/BCPGOutputStream.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/BCPGOutputStream.java
@@ -323,6 +323,22 @@ public void writePacket(
p.encode(this);
}
+ void writeShort(short n)
+ throws IOException
+ {
+ out.write((byte)(n >> 8));
+ out.write((byte)n);
+ }
+
+ void writeInt(int n)
+ throws IOException
+ {
+ out.write(n >> 24);
+ out.write(n >> 16);
+ out.write(n >> 8);
+ out.write(n);
+ }
+
void writePacket(
int tag,
byte[] body)
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/MPInteger.java b/pg/src/main/java/org/bouncycastle/bcpg/MPInteger.java
index df54d6c15f..74563178d6 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/MPInteger.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/MPInteger.java
@@ -43,10 +43,7 @@ public void encode(
BCPGOutputStream out)
throws IOException
{
- int length = value.bitLength();
-
- out.write(length >> 8);
- out.write(length);
+ out.writeShort((short)value.bitLength());
byte[] bytes = value.toByteArray();
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyPacket.java
index 0026365b9a..5339fb6ba0 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyPacket.java
@@ -162,19 +162,14 @@ public byte[] getEncodedContents()
if (version <= VERSION_3)
{
- pOut.write((byte)(validDays >> 8));
- pOut.write((byte)validDays);
+ pOut.writeShort((short)validDays);
}
pOut.write(algorithm);
if (version == VERSION_6)
{
- int keyOctets = key.getEncoded().length;
- pOut.write(keyOctets >> 24);
- pOut.write(keyOctets >> 16);
- pOut.write(keyOctets >> 8);
- pOut.write(keyOctets);
+ pOut.writeInt(key.getEncoded().length);
}
pOut.writeObject((BCPGObject)key);
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/StreamUtil.java b/pg/src/main/java/org/bouncycastle/bcpg/StreamUtil.java
index c91961cce0..17ae840e92 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/StreamUtil.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/StreamUtil.java
@@ -109,10 +109,7 @@ static long readKeyID(BCPGInputStream in)
static void writeTime(BCPGOutputStream pOut, long time)
throws IOException
{
- pOut.write((byte)(time >> 24));
- pOut.write((byte)(time >> 16));
- pOut.write((byte)(time >> 8));
- pOut.write((byte)time);
+ pOut.writeInt((int) time);
}
static long readTime(BCPGInputStream in)
diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/PGPKeyRingTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/PGPKeyRingTest.java
index e422b8422a..84e17ca7cd 100644
--- a/pg/src/test/java/org/bouncycastle/openpgp/test/PGPKeyRingTest.java
+++ b/pg/src/test/java/org/bouncycastle/openpgp/test/PGPKeyRingTest.java
@@ -3211,7 +3211,6 @@ private void doTestNoExportPrivateKey(PGPKeyPair keyPair)
public void testNullEncryption()
throws Exception
{
- char[] passPhrase = "fred".toCharArray();
KeyPairGenerator bareGenerator = KeyPairGenerator.getInstance("RSA", new BouncyCastleProvider());
bareGenerator.initialize(2048);
KeyPair rsaPair = bareGenerator.generateKeyPair();
From e89655e813df581e363739e3b4d4ac19a6d9b398 Mon Sep 17 00:00:00 2001
From: gefeili
Date: Wed, 22 May 2024 16:00:32 +0930
Subject: [PATCH 0086/1644] Changes around StreamUtil
---
.../bouncycastle/bcpg/BCPGInputStream.java | 8 +-
.../bouncycastle/bcpg/LiteralDataPacket.java | 4 +-
.../bouncycastle/bcpg/PublicKeyPacket.java | 4 +-
.../bouncycastle/bcpg/SignaturePacket.java | 311 +++++++++---------
.../bcpg/SignatureSubpacketInputStream.java | 4 +-
.../org/bouncycastle/bcpg/StreamUtil.java | 14 +-
.../UserAttributeSubpacketInputStream.java | 2 +-
7 files changed, 179 insertions(+), 168 deletions(-)
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java b/pg/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java
index f965ec3503..ee48ddf978 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java
@@ -216,7 +216,7 @@ else if (l <= 223)
}
else if (l == 255)
{
- bodyLen = (this.read() << 24) | (this.read() << 16) | (this.read() << 8) | this.read();
+ bodyLen = StreamUtil.read4OctetLength(this);
}
else
{
@@ -236,10 +236,10 @@ else if (l == 255)
bodyLen = this.read();
break;
case 1:
- bodyLen = (this.read() << 8) | this.read();
+ bodyLen = StreamUtil.read2OctetLength(this);
break;
case 2:
- bodyLen = (this.read() << 24) | (this.read() << 16) | (this.read() << 8) | this.read();
+ bodyLen = StreamUtil.read4OctetLength(this);
break;
case 3:
partial = true;
@@ -410,7 +410,7 @@ else if (l <= 223)
}
else if (l == 255)
{
- dataLength = (in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read();
+ dataLength = StreamUtil.read4OctetLength(in);
}
else
{
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/LiteralDataPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/LiteralDataPacket.java
index 16e64c377b..3b42ed8dfb 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/LiteralDataPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/LiteralDataPacket.java
@@ -35,7 +35,7 @@ public class LiteralDataPacket
fileName[i] = (byte)ch;
}
- modDate = ((long)in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read();
+ modDate = StreamUtil.readTime(in);
if (modDate < 0)
{
throw new IOException("literal data truncated in header");
@@ -55,7 +55,7 @@ public int getFormat()
*/
public long getModificationTime()
{
- return modDate * 1000L;
+ return modDate;
}
/**
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyPacket.java
index 5339fb6ba0..099b59838e 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyPacket.java
@@ -36,7 +36,7 @@ public class PublicKeyPacket
super(keyTag);
version = in.read();
- time = ((long)in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read();
+ time = StreamUtil.read4OctetLength(in);
if (version <= VERSION_3)
{
@@ -47,7 +47,7 @@ public class PublicKeyPacket
if (version == VERSION_6)
{
// TODO: Use keyOctets to be able to parse unknown keys
- long keyOctets = ((long)in.read() << 24) | ((long)in.read() << 16) | ((long)in.read() << 8) | in.read();
+ long keyOctets = StreamUtil.read4OctetLength(in);
}
switch (algorithm)
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java b/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java
index 52bb39e959..2ea40b4d2d 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java
@@ -8,13 +8,15 @@
import org.bouncycastle.bcpg.sig.IssuerKeyID;
import org.bouncycastle.bcpg.sig.SignatureCreationTime;
import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Pack;
import org.bouncycastle.util.io.Streams;
/**
* generic signature packet
*/
public class SignaturePacket
- extends ContainedPacket implements PublicKeyAlgorithmTags
+ extends ContainedPacket
+ implements PublicKeyAlgorithmTags
{
public static final int VERSION_2 = 2;
public static final int VERSION_3 = 3;
@@ -22,21 +24,21 @@ public class SignaturePacket
public static final int VERSION_5 = 5; // https://datatracker.ietf.org/doc/draft-koch-librepgp/
public static final int VERSION_6 = 6; // https://datatracker.ietf.org/doc/draft-ietf-openpgp-crypto-refresh/
- private int version;
- private int signatureType;
- private long creationTime;
- private long keyID;
- private int keyAlgorithm;
- private int hashAlgorithm;
- private MPInteger[] signature;
- private byte[] fingerPrint;
- private SignatureSubpacket[] hashedData;
- private SignatureSubpacket[] unhashedData;
- private byte[] signatureEncoding;
- private byte[] salt; // v6 only
+ private int version;
+ private int signatureType;
+ private long creationTime;
+ private long keyID;
+ private int keyAlgorithm;
+ private int hashAlgorithm;
+ private MPInteger[] signature;
+ private byte[] fingerPrint;
+ private SignatureSubpacket[] hashedData;
+ private SignatureSubpacket[] unhashedData;
+ private byte[] signatureEncoding;
+ private byte[] salt; // v6 only
SignaturePacket(
- BCPGInputStream in)
+ BCPGInputStream in)
throws IOException
{
super(SIGNATURE);
@@ -44,35 +46,35 @@ public class SignaturePacket
version = in.read();
switch (version)
{
- case VERSION_2:
- case VERSION_3:
- parseV2_V3(in);
- break;
- case VERSION_4:
- case VERSION_5:
- parseV4_V5(in);
- break;
- case VERSION_6:
- parseV6(in);
- break;
- default:
- Streams.drain(in);
- throw new UnsupportedPacketVersionException("unsupported version: " + version);
+ case VERSION_2:
+ case VERSION_3:
+ parseV2_V3(in);
+ break;
+ case VERSION_4:
+ case VERSION_5:
+ parseV4_V5(in);
+ break;
+ case VERSION_6:
+ parseV6(in);
+ break;
+ default:
+ Streams.drain(in);
+ throw new UnsupportedPacketVersionException("unsupported version: " + version);
}
}
/**
* Parse a version 2 or version 3 signature.
+ *
* @param in input stream which already skipped over the version number
* @throws IOException if the packet is malformed
- *
* @see
- * Version 3 packet format
+ * Version 3 packet format
*/
private void parseV2_V3(BCPGInputStream in)
throws IOException
{
- int l = in.read(); // length l MUST be 5
+ int l = in.read(); // length l MUST be 5
signatureType = in.read();
creationTime = StreamUtil.readTime(in);
@@ -91,16 +93,16 @@ private void parseV2_V3(BCPGInputStream in)
/**
* Parse a version 4 or version 5 signature.
* The difference between version 4 and 5 is that a version 5 signature contains additional metadata.
+ *
* @param in input stream which already skipped over the version number
* @throws IOException if the packet is malformed
- *
* @see
- * Version 4 packet format
+ * Version 4 packet format
* @see
- * Version 5 packet format
+ * Version 5 packet format
*/
private void parseV4_V5(BCPGInputStream in)
- throws IOException
+ throws IOException
{
signatureType = in.read();
keyAlgorithm = in.read();
@@ -119,14 +121,14 @@ private void parseV4_V5(BCPGInputStream in)
* Parse a version 6 signature.
* Version 6 signatures do use 4 octet subpacket area length descriptors and contain an additional salt value
* (which may or may not be of size 0, librepgp and crypto-refresh are in disagreement here).
+ *
* @param in input stream which already skipped over the version number
* @throws IOException if the packet is malformed
- *
* @see
- * Version 6 packet format
+ * Version 6 packet format
*/
private void parseV6(BCPGInputStream in)
- throws IOException
+ throws IOException
{
signatureType = in.read();
keyAlgorithm = in.read();
@@ -153,7 +155,7 @@ private void parseV6(BCPGInputStream in)
* @throws IOException if the packet is malformed
*/
private void parseSubpackets(BCPGInputStream in)
- throws IOException
+ throws IOException
{
int hashedLength;
if (version == 6)
@@ -164,18 +166,18 @@ private void parseSubpackets(BCPGInputStream in)
{
hashedLength = StreamUtil.read2OctetLength(in);
}
- byte[] hashed = new byte[hashedLength];
+ byte[] hashed = new byte[hashedLength];
in.readFully(hashed);
//
// read the signature sub packet data.
//
- SignatureSubpacket sub;
- SignatureSubpacketInputStream sIn = new SignatureSubpacketInputStream(
- new ByteArrayInputStream(hashed));
+ SignatureSubpacket sub;
+ SignatureSubpacketInputStream sIn = new SignatureSubpacketInputStream(
+ new ByteArrayInputStream(hashed));
- Vector vec = new Vector();
+ Vector vec = new Vector();
while ((sub = sIn.readPacket()) != null)
{
vec.addElement(sub);
@@ -185,7 +187,7 @@ private void parseSubpackets(BCPGInputStream in)
for (int i = 0; i != hashedData.length; i++)
{
- SignatureSubpacket p = vec.elementAt(i);
+ SignatureSubpacket p = vec.elementAt(i);
if (p instanceof IssuerKeyID)
{
keyID = ((IssuerKeyID)p).getKeyID();
@@ -207,12 +209,12 @@ else if (p instanceof SignatureCreationTime)
{
unhashedLength = StreamUtil.read2OctetLength(in);
}
- byte[] unhashed = new byte[unhashedLength];
+ byte[] unhashed = new byte[unhashedLength];
in.readFully(unhashed);
sIn = new SignatureSubpacketInputStream(
- new ByteArrayInputStream(unhashed));
+ new ByteArrayInputStream(unhashed));
vec.removeAllElements();
while ((sub = sIn.readPacket()) != null)
@@ -224,7 +226,7 @@ else if (p instanceof SignatureCreationTime)
for (int i = 0; i != unhashedData.length; i++)
{
- SignatureSubpacket p = vec.elementAt(i);
+ SignatureSubpacket p = vec.elementAt(i);
if (p instanceof IssuerKeyID)
{
keyID = ((IssuerKeyID)p).getKeyID();
@@ -243,64 +245,64 @@ else if (p instanceof SignatureCreationTime)
* @throws IOException if the packet is malformed
*/
private void parseSignature(BCPGInputStream in)
- throws IOException
+ throws IOException
{
switch (keyAlgorithm)
{
- case RSA_GENERAL:
- case RSA_SIGN:
- MPInteger v = new MPInteger(in);
-
- signature = new MPInteger[1];
- signature[0] = v;
- break;
- case DSA:
- MPInteger r = new MPInteger(in);
- MPInteger s = new MPInteger(in);
-
- signature = new MPInteger[2];
- signature[0] = r;
- signature[1] = s;
- break;
- case ELGAMAL_ENCRYPT: // yep, this really does happen sometimes.
- case ELGAMAL_GENERAL:
- MPInteger p = new MPInteger(in);
- MPInteger g = new MPInteger(in);
- MPInteger y = new MPInteger(in);
-
- signature = new MPInteger[3];
- signature[0] = p;
- signature[1] = g;
- signature[2] = y;
- break;
- case Ed448:
- signatureEncoding = new byte[org.bouncycastle.math.ec.rfc8032.Ed448.SIGNATURE_SIZE];
- in.readFully(signatureEncoding);
- break;
- case Ed25519:
- signatureEncoding = new byte[org.bouncycastle.math.ec.rfc8032.Ed25519.SIGNATURE_SIZE];
- in.readFully(signatureEncoding);
- break;
- case ECDSA:
- case EDDSA_LEGACY:
-
- MPInteger ecR = new MPInteger(in);
- MPInteger ecS = new MPInteger(in);
-
- signature = new MPInteger[2];
- signature[0] = ecR;
- signature[1] = ecS;
- break;
- default:
- if (keyAlgorithm >= PublicKeyAlgorithmTags.EXPERIMENTAL_1 && keyAlgorithm <= PublicKeyAlgorithmTags.EXPERIMENTAL_11)
- {
- signature = null;
- signatureEncoding = Streams.readAll(in);
- }
- else
- {
- throw new IOException("unknown signature key algorithm: " + keyAlgorithm);
- }
+ case RSA_GENERAL:
+ case RSA_SIGN:
+ MPInteger v = new MPInteger(in);
+
+ signature = new MPInteger[1];
+ signature[0] = v;
+ break;
+ case DSA:
+ MPInteger r = new MPInteger(in);
+ MPInteger s = new MPInteger(in);
+
+ signature = new MPInteger[2];
+ signature[0] = r;
+ signature[1] = s;
+ break;
+ case ELGAMAL_ENCRYPT: // yep, this really does happen sometimes.
+ case ELGAMAL_GENERAL:
+ MPInteger p = new MPInteger(in);
+ MPInteger g = new MPInteger(in);
+ MPInteger y = new MPInteger(in);
+
+ signature = new MPInteger[3];
+ signature[0] = p;
+ signature[1] = g;
+ signature[2] = y;
+ break;
+ case Ed448:
+ signatureEncoding = new byte[org.bouncycastle.math.ec.rfc8032.Ed448.SIGNATURE_SIZE];
+ in.readFully(signatureEncoding);
+ break;
+ case Ed25519:
+ signatureEncoding = new byte[org.bouncycastle.math.ec.rfc8032.Ed25519.SIGNATURE_SIZE];
+ in.readFully(signatureEncoding);
+ break;
+ case ECDSA:
+ case EDDSA_LEGACY:
+
+ MPInteger ecR = new MPInteger(in);
+ MPInteger ecS = new MPInteger(in);
+
+ signature = new MPInteger[2];
+ signature[0] = ecR;
+ signature[1] = ecS;
+ break;
+ default:
+ if (keyAlgorithm >= PublicKeyAlgorithmTags.EXPERIMENTAL_1 && keyAlgorithm <= PublicKeyAlgorithmTags.EXPERIMENTAL_11)
+ {
+ signature = null;
+ signatureEncoding = Streams.readAll(in);
+ }
+ else
+ {
+ throw new IOException("unknown signature key algorithm: " + keyAlgorithm);
+ }
}
}
@@ -316,14 +318,14 @@ private void parseSignature(BCPGInputStream in)
* @param signature
*/
public SignaturePacket(
- int signatureType,
- long keyID,
- int keyAlgorithm,
- int hashAlgorithm,
- SignatureSubpacket[] hashedData,
- SignatureSubpacket[] unhashedData,
- byte[] fingerPrint,
- MPInteger[] signature)
+ int signatureType,
+ long keyID,
+ int keyAlgorithm,
+ int hashAlgorithm,
+ SignatureSubpacket[] hashedData,
+ SignatureSubpacket[] unhashedData,
+ byte[] fingerPrint,
+ MPInteger[] signature)
{
this(4, signatureType, keyID, keyAlgorithm, hashAlgorithm, hashedData, unhashedData, fingerPrint, signature);
}
@@ -338,14 +340,14 @@ public SignaturePacket(
* @param signature
*/
public SignaturePacket(
- int version,
- int signatureType,
- long keyID,
- int keyAlgorithm,
- int hashAlgorithm,
- long creationTime,
- byte[] fingerPrint,
- MPInteger[] signature)
+ int version,
+ int signatureType,
+ long keyID,
+ int keyAlgorithm,
+ int hashAlgorithm,
+ long creationTime,
+ byte[] fingerPrint,
+ MPInteger[] signature)
{
this(version, signatureType, keyID, keyAlgorithm, hashAlgorithm, null, null, fingerPrint, signature);
@@ -353,15 +355,15 @@ public SignaturePacket(
}
public SignaturePacket(
- int version,
- int signatureType,
- long keyID,
- int keyAlgorithm,
- int hashAlgorithm,
- SignatureSubpacket[] hashedData,
- SignatureSubpacket[] unhashedData,
- byte[] fingerPrint,
- MPInteger[] signature)
+ int version,
+ int signatureType,
+ long keyID,
+ int keyAlgorithm,
+ int hashAlgorithm,
+ SignatureSubpacket[] hashedData,
+ SignatureSubpacket[] unhashedData,
+ byte[] fingerPrint,
+ MPInteger[] signature)
{
super(SIGNATURE);
@@ -382,15 +384,15 @@ public SignaturePacket(
}
public SignaturePacket(
- int version,
- int signatureType,
- long keyID,
- int keyAlgorithm,
- int hashAlgorithm,
- SignatureSubpacket[] hashedData,
- SignatureSubpacket[] unhashedData,
- byte[] fingerPrint,
- byte[] signatureEncoding)
+ int version,
+ int signatureType,
+ long keyID,
+ int keyAlgorithm,
+ int hashAlgorithm,
+ SignatureSubpacket[] hashedData,
+ SignatureSubpacket[] unhashedData,
+ byte[] fingerPrint,
+ byte[] signatureEncoding)
{
super(SIGNATURE);
@@ -427,6 +429,7 @@ public int getSignatureType()
/**
* return the keyID
+ *
* @return the keyID that created the signature.
*/
public long getKeyID()
@@ -436,6 +439,7 @@ public long getKeyID()
/**
* Return the signature's fingerprint.
+ *
* @return fingerprint (digest prefix) of the signature
*/
public byte[] getFingerPrint()
@@ -446,6 +450,7 @@ public byte[] getFingerPrint()
/**
* Return the signature's salt.
* Only for v6 signatures.
+ *
* @return salt
*/
public byte[] getSalt()
@@ -461,24 +466,21 @@ public byte[] getSalt()
*/
public byte[] getSignatureTrailer()
{
- byte[] trailer = null;
+ byte[] trailer;
if (version == 3 || version == 2)
{
trailer = new byte[5];
- long time = creationTime / 1000;
+ long time = creationTime / 1000;
trailer[0] = (byte)signatureType;
- trailer[1] = (byte)(time >> 24);
- trailer[2] = (byte)(time >> 16);
- trailer[3] = (byte)(time >> 8);
- trailer[4] = (byte)(time);
+ Pack.intToBigEndian((int)time, trailer, 1);
}
else
{
- ByteArrayOutputStream sOut = new ByteArrayOutputStream();
- SignatureSubpacket[] hashed = this.getHashedSubPackets();
+ ByteArrayOutputStream sOut = new ByteArrayOutputStream();
+ SignatureSubpacket[] hashed = this.getHashedSubPackets();
try
{
sOut.write((byte)this.getVersion());
@@ -486,7 +488,7 @@ public byte[] getSignatureTrailer()
sOut.write((byte)this.getKeyAlgorithm());
sOut.write((byte)this.getHashAlgorithm());
- ByteArrayOutputStream hOut = new ByteArrayOutputStream();
+ ByteArrayOutputStream hOut = new ByteArrayOutputStream();
for (int i = 0; i != hashed.length; i++)
@@ -494,11 +496,11 @@ public byte[] getSignatureTrailer()
hashed[i].encode(hOut);
}
- byte[] data = hOut.toByteArray();
+ byte[] data = hOut.toByteArray();
StreamUtil.write2OctetLength(sOut, data.length);
sOut.write(data);
- byte[] hData = sOut.toByteArray();
+ byte[] hData = sOut.toByteArray();
sOut.write((byte)this.getVersion());
sOut.write((byte)0xff);
@@ -544,6 +546,7 @@ public MPInteger[] getSignature()
/**
* Return the byte encoding of the signature section.
+ *
* @return uninterpreted signature bytes.
*/
public byte[] getSignatureBytes()
@@ -593,11 +596,11 @@ public long getCreationTime()
}
public void encode(
- BCPGOutputStream out)
+ BCPGOutputStream out)
throws IOException
{
- ByteArrayOutputStream bOut = new ByteArrayOutputStream();
- BCPGOutputStream pOut = new BCPGOutputStream(bOut);
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ BCPGOutputStream pOut = new BCPGOutputStream(bOut);
pOut.write(version);
@@ -605,7 +608,7 @@ public void encode(
{
pOut.write(5); // the length of the next block
- long time = creationTime / 1000;
+ long time = creationTime / 1000;
pOut.write(signatureType);
StreamUtil.writeTime(pOut, time);
@@ -621,14 +624,14 @@ else if (version == VERSION_4 || version == VERSION_5 || version == VERSION_6)
pOut.write(keyAlgorithm);
pOut.write(hashAlgorithm);
- ByteArrayOutputStream sOut = new ByteArrayOutputStream();
+ ByteArrayOutputStream sOut = new ByteArrayOutputStream();
for (int i = 0; i != hashedData.length; i++)
{
hashedData[i].encode(sOut);
}
- byte[] data = sOut.toByteArray();
+ byte[] data = sOut.toByteArray();
if (version == VERSION_6)
{
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/SignatureSubpacketInputStream.java b/pg/src/main/java/org/bouncycastle/bcpg/SignatureSubpacketInputStream.java
index dab6e423ee..544279019d 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/SignatureSubpacketInputStream.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/SignatureSubpacketInputStream.java
@@ -69,7 +69,7 @@ public SignatureSubpacket readPacket()
throws IOException
{
int l = this.read();
- int bodyLen = 0;
+ int bodyLen;
if (l < 0)
{
@@ -89,7 +89,7 @@ else if (l <= 223)
else if (l == 255)
{
isLongLength = true;
- bodyLen = (in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read();
+ bodyLen = StreamUtil.read4OctetLength(in);
}
else
{
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/StreamUtil.java b/pg/src/main/java/org/bouncycastle/bcpg/StreamUtil.java
index 17ae840e92..dc88ac5074 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/StreamUtil.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/StreamUtil.java
@@ -109,13 +109,13 @@ static long readKeyID(BCPGInputStream in)
static void writeTime(BCPGOutputStream pOut, long time)
throws IOException
{
- pOut.writeInt((int) time);
+ pOut.writeInt((int)time);
}
static long readTime(BCPGInputStream in)
throws IOException
{
- return (((long)in.read() << 24) | ((long) in.read() << 16) | ((long) in.read() << 8) | in.read()) * 1000;
+ return (long)read4OctetLength(in) * 1000L;
}
static void write2OctetLength(OutputStream pOut, int len)
@@ -132,7 +132,7 @@ static int read2OctetLength(InputStream in)
}
static void write4OctetLength(OutputStream pOut, int len)
- throws IOException
+ throws IOException
{
pOut.write(len >> 24);
pOut.write(len >> 16);
@@ -146,4 +146,12 @@ static int read4OctetLength(InputStream in)
return (in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read();
}
+ /**
+ * Note: flags is an array of three boolean values:
+ * flags[0] indicates l is negative
+ * */
+// static int readBodyLen(InputStream in, boolean[] flags){
+// flags =
+// }
+
}
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/UserAttributeSubpacketInputStream.java b/pg/src/main/java/org/bouncycastle/bcpg/UserAttributeSubpacketInputStream.java
index 2b1ed0d195..0a3e9b9d59 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/UserAttributeSubpacketInputStream.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/UserAttributeSubpacketInputStream.java
@@ -88,7 +88,7 @@ else if (l <= 223)
}
else if (l == 255)
{
- bodyLen = (in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read();
+ bodyLen = StreamUtil.read4OctetLength(in);
longLength = true;
}
else
From 6a6c0be8f72dd3a221a87c6265f4e139e4b92755 Mon Sep 17 00:00:00 2001
From: gefeili
Date: Wed, 22 May 2024 17:10:57 +0930
Subject: [PATCH 0087/1644] Add StreamUtil.readBodyLen function
---
.../bouncycastle/bcpg/BCPGInputStream.java | 112 ++++++------------
.../bcpg/SignatureSubpacketInputStream.java | 26 +---
.../org/bouncycastle/bcpg/StreamUtil.java | 41 ++++++-
.../UserAttributeSubpacketInputStream.java | 92 ++++++--------
4 files changed, 118 insertions(+), 153 deletions(-)
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java b/pg/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java
index ee48ddf978..f2c70bdb92 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java
@@ -11,7 +11,8 @@
* Stream reader for PGP objects
*/
public class BCPGInputStream
- extends InputStream implements PacketTags
+ extends InputStream
+ implements PacketTags
{
/**
* If the argument is a {@link BCPGInputStream}, return it.
@@ -29,19 +30,19 @@ public static BCPGInputStream wrap(InputStream in)
return new BCPGInputStream(in);
}
- InputStream in;
- boolean next = false;
- int nextB;
+ InputStream in;
+ boolean next = false;
+ int nextB;
- boolean mNext = false;
- int mNextB;
+ boolean mNext = false;
+ int mNextB;
public BCPGInputStream(
- InputStream in)
+ InputStream in)
{
this.in = in;
}
-
+
public int available()
throws IOException
{
@@ -113,9 +114,9 @@ public int read(
}
public void readFully(
- byte[] buf,
- int off,
- int len)
+ byte[] buf,
+ int off,
+ int len)
throws IOException
{
if (Streams.readFully(this, buf, off, len) < len)
@@ -131,7 +132,7 @@ public byte[] readAll()
}
public void readFully(
- byte[] buf)
+ byte[] buf)
throws IOException
{
readFully(buf, 0, buf.length);
@@ -141,7 +142,6 @@ public void readFully(
* Obtains the tag of the next packet in the stream.
*
* @return the {@link PacketTags tag number}.
- *
* @throws IOException if an error occurs reading the tag from the stream.
*/
public int nextPacketTag()
@@ -176,12 +176,13 @@ public int nextPacketTag()
/**
* Reads the next packet from the stream.
+ *
* @throws IOException
*/
public Packet readPacket()
throws IOException
{
- int hdr = this.read();
+ int hdr = this.read();
if (hdr < 0)
{
@@ -193,36 +194,17 @@ public Packet readPacket()
throw new IOException("invalid header encountered");
}
- boolean newPacket = (hdr & 0x40) != 0;
- int tag = 0;
- int bodyLen = 0;
- boolean partial = false;
+ boolean newPacket = (hdr & 0x40) != 0;
+ int tag = 0;
+ int bodyLen = 0;
+ boolean partial = false;
if (newPacket)
{
tag = hdr & 0x3f;
-
- int l = this.read();
-
- if (l < 192)
- {
- bodyLen = l;
- }
- else if (l <= 223)
- {
- int b = this.read();
-
- bodyLen = ((l - 192) << 8) + (b) + 192;
- }
- else if (l == 255)
- {
- bodyLen = StreamUtil.read4OctetLength(this);
- }
- else
- {
- partial = true;
- bodyLen = 1 << (l & 0x1f);
- }
+ boolean[] flags = new boolean[3];
+ bodyLen = StreamUtil.readBodyLen(this, this, flags);
+ partial = flags[2];
}
else
{
@@ -249,7 +231,7 @@ else if (l == 255)
}
}
- BCPGInputStream objStream;
+ BCPGInputStream objStream;
if (bodyLen == 0 && partial)
{
@@ -260,7 +242,7 @@ else if (l == 255)
objStream = new BCPGInputStream(
new BufferedInputStream(new PartialInputStream(this, partial, bodyLen)));
}
-
+
switch (tag)
{
case RESERVED:
@@ -314,9 +296,9 @@ else if (l == 255)
}
/**
- * @deprecated use skipMarkerAndPaddingPackets
* @return the tag for the next non-marker/padding packet
* @throws IOException on a parsing issue.
+ * @deprecated use skipMarkerAndPaddingPackets
*/
public int skipMarkerPackets()
throws IOException
@@ -326,6 +308,7 @@ public int skipMarkerPackets()
/**
* skip any marker and padding packets found in the stream.
+ *
* @return the tag for the next non-marker/padding packet
* @throws IOException on a parsing issue.
*/
@@ -334,7 +317,7 @@ public int skipMarkerAndPaddingPackets()
{
int tag;
while ((tag = nextPacketTag()) == PacketTags.MARKER
- || tag == PacketTags.PADDING)
+ || tag == PacketTags.PADDING)
{
readPacket();
}
@@ -350,20 +333,20 @@ public void close()
/**
* a stream that overlays our input stream, allowing the user to only read a segment of it.
- *
+ *
* NB: dataLength will be negative if the segment length is in the upper range above 2**31.
*/
private static class PartialInputStream
extends InputStream
{
- private BCPGInputStream in;
- private boolean partial;
- private int dataLength;
+ private BCPGInputStream in;
+ private boolean partial;
+ private int dataLength;
PartialInputStream(
- BCPGInputStream in,
- boolean partial,
- int dataLength)
+ BCPGInputStream in,
+ boolean partial,
+ int dataLength)
{
this.in = in;
this.partial = partial;
@@ -392,32 +375,13 @@ public int available()
private int loadDataLength()
throws IOException
{
- int l = in.read();
-
- if (l < 0)
+ boolean[] flags = new boolean[3];
+ dataLength = StreamUtil.readBodyLen(in, in, flags);
+ if (flags[0])
{
return -1;
}
-
- partial = false;
- if (l < 192)
- {
- dataLength = l;
- }
- else if (l <= 223)
- {
- dataLength = ((l - 192) << 8) + (in.read()) + 192;
- }
- else if (l == 255)
- {
- dataLength = StreamUtil.read4OctetLength(in);
- }
- else
- {
- partial = true;
- dataLength = 1 << (l & 0x1f);
- }
-
+ partial = flags[2];
return dataLength;
}
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/SignatureSubpacketInputStream.java b/pg/src/main/java/org/bouncycastle/bcpg/SignatureSubpacketInputStream.java
index 544279019d..a819156b6f 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/SignatureSubpacketInputStream.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/SignatureSubpacketInputStream.java
@@ -68,33 +68,17 @@ public int read()
public SignatureSubpacket readPacket()
throws IOException
{
- int l = this.read();
- int bodyLen;
-
- if (l < 0)
+ boolean[] flags = new boolean[3];
+ int bodyLen = StreamUtil.readBodyLen(this, in, flags);
+ if (flags[0])
{
return null;
}
-
- boolean isLongLength = false;
-
- if (l < 192)
- {
- bodyLen = l;
- }
- else if (l <= 223)
- {
- bodyLen = ((l - 192) << 8) + (in.read()) + 192;
- }
- else if (l == 255)
- {
- isLongLength = true;
- bodyLen = StreamUtil.read4OctetLength(in);
- }
- else
+ else if (flags[2])
{
throw new IOException("unexpected length header");
}
+ boolean isLongLength = flags[1];
int tag = in.read();
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/StreamUtil.java b/pg/src/main/java/org/bouncycastle/bcpg/StreamUtil.java
index dc88ac5074..b6df0cbf17 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/StreamUtil.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/StreamUtil.java
@@ -7,6 +7,8 @@
import java.io.OutputStream;
import java.nio.channels.FileChannel;
+import org.bouncycastle.util.Arrays;
+
class StreamUtil
{
private static final long MAX_MEMORY = Runtime.getRuntime().maxMemory();
@@ -148,10 +150,39 @@ static int read4OctetLength(InputStream in)
/**
* Note: flags is an array of three boolean values:
- * flags[0] indicates l is negative
- * */
-// static int readBodyLen(InputStream in, boolean[] flags){
-// flags =
-// }
+ * flags[0] indicates l is negative, flag for eof
+ * flags[1] indicates (is)longLength = true
+ * flags[2] indicate partial = true
+ */
+ static int readBodyLen(InputStream in, InputStream subIn, boolean[] flags)
+ throws IOException
+ {
+ Arrays.fill(flags, false);
+ int l = in.read();
+ int bodyLen = -1;
+ if (l < 0)
+ {
+ flags[0] = true;
+ }
+ if (l < 192)
+ {
+ bodyLen = l;
+ }
+ else if (l <= 223)
+ {
+ bodyLen = ((l - 192) << 8) + (subIn.read()) + 192;
+ }
+ else if (l == 255)
+ {
+ flags[1] = true;
+ bodyLen = StreamUtil.read4OctetLength(in);
+ }
+ else
+ {
+ flags[2] = true;
+ bodyLen = 1 << (l & 0x1f);
+ }
+ return bodyLen;
+ }
}
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/UserAttributeSubpacketInputStream.java b/pg/src/main/java/org/bouncycastle/bcpg/UserAttributeSubpacketInputStream.java
index 0a3e9b9d59..2864368b36 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/UserAttributeSubpacketInputStream.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/UserAttributeSubpacketInputStream.java
@@ -10,111 +10,97 @@
* reader for user attribute sub-packets
*/
public class UserAttributeSubpacketInputStream
- extends InputStream implements UserAttributeSubpacketTags
+ extends InputStream
+ implements UserAttributeSubpacketTags
{
- InputStream in;
-
+ InputStream in;
+
public UserAttributeSubpacketInputStream(
- InputStream in)
+ InputStream in)
{
this.in = in;
}
-
+
public int available()
throws IOException
{
return in.available();
}
-
+
public int read()
throws IOException
{
return in.read();
}
-
+
private void readFully(
- byte[] buf,
- int off,
- int len)
+ byte[] buf,
+ int off,
+ int len)
throws IOException
{
if (len > 0)
{
- int b = this.read();
-
+ int b = this.read();
+
if (b < 0)
{
throw new EOFException();
}
-
+
buf[off] = (byte)b;
off++;
len--;
}
-
+
while (len > 0)
{
- int l = in.read(buf, off, len);
-
+ int l = in.read(buf, off, len);
+
if (l < 0)
{
throw new EOFException();
}
-
+
off += l;
len -= l;
}
}
-
+
public UserAttributeSubpacket readPacket()
throws IOException
{
- int l = this.read();
- int bodyLen = 0;
- boolean longLength = false;
-
- if (l < 0)
+ boolean[] flags = new boolean[3];
+ int bodyLen = StreamUtil.readBodyLen(this, in, flags);
+ if (flags[0])
{
return null;
}
-
- if (l < 192)
- {
- bodyLen = l;
- }
- else if (l <= 223)
+ else if (flags[2])
{
- bodyLen = ((l - 192) << 8) + (in.read()) + 192;
- }
- else if (l == 255)
- {
- bodyLen = StreamUtil.read4OctetLength(in);
- longLength = true;
+ throw new IOException("unrecognised length reading user attribute sub packet");
}
- else
+ boolean longLength = flags[1];
+
+ int tag = in.read();
+
+ if (tag < 0)
{
- throw new IOException("unrecognised length reading user attribute sub packet");
+ throw new EOFException("unexpected EOF reading user attribute sub packet");
}
- int tag = in.read();
+ byte[] data = new byte[bodyLen - 1];
- if (tag < 0)
- {
- throw new EOFException("unexpected EOF reading user attribute sub packet");
- }
-
- byte[] data = new byte[bodyLen - 1];
+ this.readFully(data, 0, data.length);
- this.readFully(data, 0, data.length);
-
- int type = tag;
+ int type = tag;
- switch (type)
- {
- case IMAGE_ATTRIBUTE:
- return new ImageAttribute(longLength, data);
- }
+ switch (type)
+ {
+ case IMAGE_ATTRIBUTE:
+ return new ImageAttribute(longLength, data);
+ }
- return new UserAttributeSubpacket(type, longLength, data);
+ return new UserAttributeSubpacket(type, longLength, data);
}
}
From 0413250d1a3e851c00aaa7d69e47d3dca210143d Mon Sep 17 00:00:00 2001
From: gefeili
Date: Wed, 22 May 2024 17:15:03 +0930
Subject: [PATCH 0088/1644] Add StreamUtil.readBodyLen function
---
pg/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java | 4 ++--
.../org/bouncycastle/bcpg/SignatureSubpacketInputStream.java | 2 +-
pg/src/main/java/org/bouncycastle/bcpg/StreamUtil.java | 4 ++--
.../bouncycastle/bcpg/UserAttributeSubpacketInputStream.java | 2 +-
4 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java b/pg/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java
index f2c70bdb92..1f2e625c66 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java
@@ -203,7 +203,7 @@ public Packet readPacket()
{
tag = hdr & 0x3f;
boolean[] flags = new boolean[3];
- bodyLen = StreamUtil.readBodyLen(this, this, flags);
+ bodyLen = StreamUtil.readBodyLen(this, flags);
partial = flags[2];
}
else
@@ -376,7 +376,7 @@ private int loadDataLength()
throws IOException
{
boolean[] flags = new boolean[3];
- dataLength = StreamUtil.readBodyLen(in, in, flags);
+ dataLength = StreamUtil.readBodyLen(in, flags);
if (flags[0])
{
return -1;
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/SignatureSubpacketInputStream.java b/pg/src/main/java/org/bouncycastle/bcpg/SignatureSubpacketInputStream.java
index a819156b6f..d7d2eb18bc 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/SignatureSubpacketInputStream.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/SignatureSubpacketInputStream.java
@@ -69,7 +69,7 @@ public SignatureSubpacket readPacket()
throws IOException
{
boolean[] flags = new boolean[3];
- int bodyLen = StreamUtil.readBodyLen(this, in, flags);
+ int bodyLen = StreamUtil.readBodyLen(this, flags);
if (flags[0])
{
return null;
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/StreamUtil.java b/pg/src/main/java/org/bouncycastle/bcpg/StreamUtil.java
index b6df0cbf17..12acd23fec 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/StreamUtil.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/StreamUtil.java
@@ -154,7 +154,7 @@ static int read4OctetLength(InputStream in)
* flags[1] indicates (is)longLength = true
* flags[2] indicate partial = true
*/
- static int readBodyLen(InputStream in, InputStream subIn, boolean[] flags)
+ static int readBodyLen(InputStream in, boolean[] flags)
throws IOException
{
Arrays.fill(flags, false);
@@ -170,7 +170,7 @@ static int readBodyLen(InputStream in, InputStream subIn, boolean[] flags)
}
else if (l <= 223)
{
- bodyLen = ((l - 192) << 8) + (subIn.read()) + 192;
+ bodyLen = ((l - 192) << 8) + (in.read()) + 192;
}
else if (l == 255)
{
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/UserAttributeSubpacketInputStream.java b/pg/src/main/java/org/bouncycastle/bcpg/UserAttributeSubpacketInputStream.java
index 2864368b36..66112430d1 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/UserAttributeSubpacketInputStream.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/UserAttributeSubpacketInputStream.java
@@ -71,7 +71,7 @@ public UserAttributeSubpacket readPacket()
throws IOException
{
boolean[] flags = new boolean[3];
- int bodyLen = StreamUtil.readBodyLen(this, in, flags);
+ int bodyLen = StreamUtil.readBodyLen(this, flags);
if (flags[0])
{
return null;
From 5eb068a2af5541cad2440f37b153c6396e22faf1 Mon Sep 17 00:00:00 2001
From: gefeili
Date: Wed, 22 May 2024 17:25:59 +0930
Subject: [PATCH 0089/1644] Reformat PGPV3SignatureGenerator.
---
.../openpgp/PGPV3SignatureGenerator.java | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPV3SignatureGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPV3SignatureGenerator.java
index cb171b9ebf..ab74f84c4f 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/PGPV3SignatureGenerator.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPV3SignatureGenerator.java
@@ -24,23 +24,23 @@ public class PGPV3SignatureGenerator
/**
* Create a signature generator built on the passed in contentSignerBuilder.
*
- * @param contentSignerBuilder builder to produce PGPContentSigner objects for generating signatures.
+ * @param contentSignerBuilder builder to produce PGPContentSigner objects for generating signatures.
*/
public PGPV3SignatureGenerator(
PGPContentSignerBuilder contentSignerBuilder)
{
this.contentSignerBuilder = contentSignerBuilder;
}
-
+
/**
* Initialise the generator for signing.
- *
+ *
* @param signatureType
* @param key
* @throws PGPException
*/
public void init(
- int signatureType,
+ int signatureType,
PGPPrivateKey key)
throws PGPException
{
@@ -57,7 +57,7 @@ public void init(
/**
* Return the one pass header associated with the current signature.
- *
+ *
* @param isNested
* @return PGPOnePassSignature
* @throws PGPException
@@ -68,10 +68,10 @@ public PGPOnePassSignature generateOnePassVersion(
{
return new PGPOnePassSignature(new OnePassSignaturePacket(sigType, contentSigner.getHashAlgorithm(), contentSigner.getKeyAlgorithm(), contentSigner.getKeyID(), isNested));
}
-
+
/**
* Return a V3 signature object containing the current signature state.
- *
+ *
* @return PGPSignature
* @throws PGPException
*/
@@ -95,7 +95,7 @@ public PGPSignature generate()
MPInteger[] sigValues;
if (contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.RSA_SIGN
|| contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.RSA_GENERAL)
- // an RSA signature
+ // an RSA signature
{
sigValues = new MPInteger[1];
sigValues[0] = new MPInteger(new BigInteger(1, contentSigner.getSignature()));
From 53f22c2f269b6192b69d0db2f6dc8cc852b544c0 Mon Sep 17 00:00:00 2001
From: Paul Schaub
Date: Wed, 22 May 2024 11:38:27 +0200
Subject: [PATCH 0090/1644] Add error messages to
Curve25519PrivateKeyEncodingTest
---
.../Curve25519PrivateKeyEncodingTest.java | 24 ++++++++++++-------
1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/Curve25519PrivateKeyEncodingTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/Curve25519PrivateKeyEncodingTest.java
index a68fceb337..96ecc77a44 100644
--- a/pg/src/test/java/org/bouncycastle/openpgp/test/Curve25519PrivateKeyEncodingTest.java
+++ b/pg/src/test/java/org/bouncycastle/openpgp/test/Curve25519PrivateKeyEncodingTest.java
@@ -75,18 +75,22 @@ private void jca_verifySecretKeyReverseEncoding()
// Legacy key uses reversed encoding
PGPKeyPair pgpECDHKeyPair = new JcaPGPKeyPair(PublicKeyAlgorithmTags.ECDH, kp, date);
byte[] encodedECDHPrivateKey = pgpECDHKeyPair.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
- isTrue(containsSubsequence(encodedECDHPrivateKey, Arrays.reverse(rawPrivateKey)));
+ isTrue("ECDH Curve25519Legacy (X25519) key MUST encode secret key in 'reverse' (big-endian MPI encoding) (JCE implementation)",
+ containsSubsequence(encodedECDHPrivateKey, Arrays.reverse(rawPrivateKey)));
byte[] decodedECDHPrivateKey = jcaNativePrivateKey(c.getPrivateKey(pgpECDHKeyPair.getPrivateKey()));
- isEncodingEqual(decodedECDHPrivateKey, rawPrivateKey);
+ isEncodingEqual("Decoded ECDH Curve25519Legacy (X25519) key MUST match original raw key (JCE implementation)",
+ decodedECDHPrivateKey, rawPrivateKey);
// X25519 key uses native encoding
PGPKeyPair pgpX25519KeyPair = new JcaPGPKeyPair(PublicKeyAlgorithmTags.X25519, kp, date);
byte[] encodedX25519PrivateKey = pgpX25519KeyPair.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
- isTrue(containsSubsequence(encodedX25519PrivateKey, rawPrivateKey));
+ isTrue("X25519 key MUST use native encoding (little-endian) to encode the secret key material (JCE implementation)",
+ containsSubsequence(encodedX25519PrivateKey, rawPrivateKey));
byte[] decodedX25519PrivateKey = jcaNativePrivateKey(c.getPrivateKey(pgpX25519KeyPair.getPrivateKey()));
- isEncodingEqual(rawPrivateKey, decodedX25519PrivateKey);
+ isEncodingEqual("Decoded X25519 key MUST match original raw key (JCE implementation)",
+ rawPrivateKey, decodedX25519PrivateKey);
}
/**
@@ -122,18 +126,22 @@ private void bc_verifySecretKeyReverseEncoding()
// Legacy key uses reversed encoding
PGPKeyPair pgpECDHKeyPair = new BcPGPKeyPair(PublicKeyAlgorithmTags.ECDH, kp, date);
byte[] encodedECDHPrivateKey = pgpECDHKeyPair.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
- isTrue(containsSubsequence(encodedECDHPrivateKey, Arrays.reverse(rawPrivateKey)));
+ isTrue("ECDH Curve25519Legacy (X25519) key MUST encode secret key in 'reverse' (big-endian MPI encoding) (BC implementation)",
+ containsSubsequence(encodedECDHPrivateKey, Arrays.reverse(rawPrivateKey)));
byte[] decodedECDHPrivateKey = ((X25519PrivateKeyParameters) c.getPrivateKey(pgpECDHKeyPair.getPrivateKey())).getEncoded();
- isEncodingEqual(decodedECDHPrivateKey, rawPrivateKey);
+ isEncodingEqual("Decoded ECDH Curve25519Legacy (X25519) key MUST match original raw key (BC implementation)",
+ decodedECDHPrivateKey, rawPrivateKey);
// X25519 key uses native encoding
PGPKeyPair pgpX25519KeyPair = new BcPGPKeyPair(PublicKeyAlgorithmTags.X25519, kp, date);
byte[] encodedX25519PrivateKey = pgpX25519KeyPair.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
- isTrue(containsSubsequence(encodedX25519PrivateKey, rawPrivateKey));
+ isTrue("X25519 key MUST use native encoding (little-endian) to encode the secret key material (BC implementation)",
+ containsSubsequence(encodedX25519PrivateKey, rawPrivateKey));
byte[] decodedX25519PrivateKey = ((X25519PrivateKeyParameters) c.getPrivateKey(pgpX25519KeyPair.getPrivateKey())).getEncoded();
- isEncodingEqual(rawPrivateKey, decodedX25519PrivateKey);
+ isEncodingEqual("Decoded X25519 key MUST match original raw key (BC implementation)",
+ rawPrivateKey, decodedX25519PrivateKey);
}
/**
From 347c2af1820475cd8b7515530dc1fa70aa36d9a9 Mon Sep 17 00:00:00 2001
From: David Hook
Date: Wed, 22 May 2024 20:44:33 +1000
Subject: [PATCH 0091/1644] removed unnecessary import
---
pg/src/main/java/org/bouncycastle/bcpg/PublicKeyPacket.java | 2 --
1 file changed, 2 deletions(-)
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyPacket.java
index f67e546836..0fbe12c2b9 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyPacket.java
@@ -1,7 +1,5 @@
package org.bouncycastle.bcpg;
-import sun.jvm.hotspot.types.JBooleanField;
-
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Date;
From b4f8a61b8eb75a5bc58d7a2851ffe343e0ab67a3 Mon Sep 17 00:00:00 2001
From: Paul Schaub
Date: Tue, 7 May 2024 09:59:21 +0200
Subject: [PATCH 0092/1644] OpenPGP Packets: Remember packetTagFormat to allow
round-tripping packets unmodified
---
.../bouncycastle/bcpg/AEADEncDataPacket.java | 9 +-
.../bouncycastle/bcpg/BCPGInputStream.java | 44 +--
.../bouncycastle/bcpg/BCPGOutputStream.java | 66 +++-
.../bcpg/CompressedDataPacket.java | 12 +-
.../bouncycastle/bcpg/ContainedPacket.java | 21 +-
.../bouncycastle/bcpg/ExperimentalPacket.java | 22 +-
.../bouncycastle/bcpg/InputStreamPacket.java | 11 +-
.../bouncycastle/bcpg/LiteralDataPacket.java | 12 +-
.../org/bouncycastle/bcpg/MarkerPacket.java | 14 +-
.../bcpg/ModDetectionCodePacket.java | 16 +-
.../bcpg/OnePassSignaturePacket.java | 14 +-
.../java/org/bouncycastle/bcpg/Packet.java | 18 ++
.../org/bouncycastle/bcpg/PacketFormat.java | 26 ++
.../org/bouncycastle/bcpg/PaddingPacket.java | 12 +-
.../bcpg/PublicKeyEncSessionPacket.java | 14 +-
.../bouncycastle/bcpg/PublicKeyPacket.java | 28 +-
.../bouncycastle/bcpg/PublicSubkeyPacket.java | 13 +-
.../org/bouncycastle/bcpg/ReservedPacket.java | 7 +-
.../bouncycastle/bcpg/SecretKeyPacket.java | 31 +-
.../bouncycastle/bcpg/SecretSubkeyPacket.java | 9 +-
.../bouncycastle/bcpg/SignaturePacket.java | 14 +-
.../bcpg/SymmetricEncDataPacket.java | 11 +-
.../bcpg/SymmetricEncIntegrityPacket.java | 12 +-
.../bcpg/SymmetricKeyEncSessionPacket.java | 13 +-
.../org/bouncycastle/bcpg/TrustPacket.java | 16 +-
.../org/bouncycastle/bcpg/UnknownPacket.java | 10 +-
.../bcpg/UserAttributePacket.java | 16 +-
.../org/bouncycastle/bcpg/UserIDPacket.java | 16 +-
.../bcpg/test/BCPGOutputStreamTest.java | 283 ++++++++++++++++++
29 files changed, 692 insertions(+), 98 deletions(-)
create mode 100644 pg/src/main/java/org/bouncycastle/bcpg/PacketFormat.java
create mode 100644 pg/src/test/java/org/bouncycastle/bcpg/test/BCPGOutputStreamTest.java
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/AEADEncDataPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/AEADEncDataPacket.java
index 72ee63af87..57cbf24293 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/AEADEncDataPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/AEADEncDataPacket.java
@@ -23,9 +23,16 @@ public class AEADEncDataPacket
private final byte[] iv;
public AEADEncDataPacket(BCPGInputStream in)
+ throws IOException
+ {
+ this(in, false);
+ }
+
+ public AEADEncDataPacket(BCPGInputStream in,
+ boolean newPacketFormat)
throws IOException
{
- super(in, AEAD_ENC_DATA);
+ super(in, AEAD_ENC_DATA, newPacketFormat);
version = (byte)in.read();
if (version != VERSION_1)
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java b/pg/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java
index f965ec3503..7c22b8bbfd 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java
@@ -264,52 +264,52 @@ else if (l == 255)
switch (tag)
{
case RESERVED:
- return new ReservedPacket(objStream);
+ return new ReservedPacket(objStream, newPacket);
case PUBLIC_KEY_ENC_SESSION:
- return new PublicKeyEncSessionPacket(objStream);
+ return new PublicKeyEncSessionPacket(objStream, newPacket);
case SIGNATURE:
- return new SignaturePacket(objStream);
+ return new SignaturePacket(objStream, newPacket);
case SYMMETRIC_KEY_ENC_SESSION:
- return new SymmetricKeyEncSessionPacket(objStream);
+ return new SymmetricKeyEncSessionPacket(objStream, newPacket);
case ONE_PASS_SIGNATURE:
- return new OnePassSignaturePacket(objStream);
+ return new OnePassSignaturePacket(objStream, newPacket);
case SECRET_KEY:
- return new SecretKeyPacket(objStream);
+ return new SecretKeyPacket(objStream, newPacket);
case PUBLIC_KEY:
- return new PublicKeyPacket(objStream);
+ return new PublicKeyPacket(objStream, newPacket);
case SECRET_SUBKEY:
- return new SecretSubkeyPacket(objStream);
+ return new SecretSubkeyPacket(objStream, newPacket);
case COMPRESSED_DATA:
- return new CompressedDataPacket(objStream);
+ return new CompressedDataPacket(objStream, newPacket);
case SYMMETRIC_KEY_ENC:
- return new SymmetricEncDataPacket(objStream);
+ return new SymmetricEncDataPacket(objStream, newPacket);
case MARKER:
- return new MarkerPacket(objStream);
+ return new MarkerPacket(objStream, newPacket);
case LITERAL_DATA:
- return new LiteralDataPacket(objStream);
+ return new LiteralDataPacket(objStream, newPacket);
case TRUST:
- return new TrustPacket(objStream);
+ return new TrustPacket(objStream, newPacket);
case USER_ID:
- return new UserIDPacket(objStream);
+ return new UserIDPacket(objStream, newPacket);
case USER_ATTRIBUTE:
- return new UserAttributePacket(objStream);
+ return new UserAttributePacket(objStream, newPacket);
case PUBLIC_SUBKEY:
- return new PublicSubkeyPacket(objStream);
+ return new PublicSubkeyPacket(objStream, newPacket);
case SYM_ENC_INTEGRITY_PRO:
- return new SymmetricEncIntegrityPacket(objStream);
+ return new SymmetricEncIntegrityPacket(objStream, newPacket);
case MOD_DETECTION_CODE:
- return new ModDetectionCodePacket(objStream);
+ return new ModDetectionCodePacket(objStream, newPacket);
case AEAD_ENC_DATA:
- return new AEADEncDataPacket(objStream);
+ return new AEADEncDataPacket(objStream, newPacket);
case PADDING:
- return new PaddingPacket(objStream);
+ return new PaddingPacket(objStream, newPacket);
case EXPERIMENTAL_1:
case EXPERIMENTAL_2:
case EXPERIMENTAL_3:
case EXPERIMENTAL_4:
- return new ExperimentalPacket(tag, objStream);
+ return new ExperimentalPacket(tag, objStream, newPacket);
default:
- return new UnknownPacket(tag, objStream);
+ return new UnknownPacket(tag, objStream, newPacket);
}
}
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/BCPGOutputStream.java b/pg/src/main/java/org/bouncycastle/bcpg/BCPGOutputStream.java
index 6ce35b7454..63996321a8 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/BCPGOutputStream.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/BCPGOutputStream.java
@@ -29,7 +29,7 @@ public static BCPGOutputStream wrap(OutputStream out)
}
OutputStream out;
- private boolean useOldFormat;
+ private PacketFormat packetFormat;
private byte[] partialBuffer;
private int partialBufferLength;
private int partialPower;
@@ -46,11 +46,11 @@ public static BCPGOutputStream wrap(OutputStream out)
public BCPGOutputStream(
OutputStream out)
{
- this(out, false);
+ this(out, PacketFormat.ROUNDTRIP);
}
/**
- * Base constructor specifying whether or not to use packets in the new format
+ * Base constructor specifying whether to use packets in the new format
* wherever possible.
*
* @param out output stream to write encoded data to.
@@ -59,9 +59,16 @@ public BCPGOutputStream(
public BCPGOutputStream(
OutputStream out,
boolean newFormatOnly)
+ {
+ this(out, newFormatOnly ? PacketFormat.CURRENT : PacketFormat.ROUNDTRIP);
+ }
+
+ public BCPGOutputStream(
+ OutputStream out,
+ PacketFormat packetFormat)
{
this.out = out;
- this.useOldFormat = !newFormatOnly;
+ this.packetFormat = packetFormat;
}
/**
@@ -75,6 +82,7 @@ public BCPGOutputStream(
throws IOException
{
this.out = out;
+ this.packetFormat = PacketFormat.LEGACY;
this.writeHeader(tag, true, true, 0);
}
@@ -95,6 +103,7 @@ public BCPGOutputStream(
throws IOException
{
this.out = out;
+ this.packetFormat = oldFormat ? PacketFormat.LEGACY : PacketFormat.CURRENT;
if (length > 0xFFFFFFFFL)
{
@@ -122,6 +131,7 @@ public BCPGOutputStream(
throws IOException
{
this.out = out;
+ this.packetFormat = PacketFormat.CURRENT;
this.writeHeader(tag, false, false, length);
}
@@ -141,6 +151,7 @@ public BCPGOutputStream(
throws IOException
{
this.out = out;
+ this.packetFormat = PacketFormat.CURRENT;
this.writeHeader(tag, false, true, 0);
this.partialBuffer = buffer;
@@ -316,6 +327,11 @@ public void write(
}
}
+ /**
+ * Write a packet to the stream.
+ * @param p packet
+ * @throws IOException
+ */
public void writePacket(
ContainedPacket p)
throws IOException
@@ -323,15 +339,54 @@ public void writePacket(
p.encode(this);
}
+ /**
+ * Write a packet to the stream.
+ * The packet will use the old encoding format if {@link #packetFormat} is {@link PacketFormat#LEGACY}, otherwise
+ * it will be encoded using the new packet format.
+ * @param tag packet tag
+ * @param body packet body
+ * @throws IOException
+ */
void writePacket(
int tag,
byte[] body)
throws IOException
{
- this.writeHeader(tag, useOldFormat, false, body.length);
+ this.writeHeader(tag, packetFormat == PacketFormat.LEGACY, false, body.length);
this.write(body);
}
+ /**
+ * Write a packet.
+ * The packet format will be chosen primarily based on {@link #packetFormat}.
+ * If {@link #packetFormat} is {@link PacketFormat#CURRENT}, the packet will be encoded using the new format.
+ * If it is {@link PacketFormat#LEGACY}, the packet will use old encoding format.
+ * If it is {@link PacketFormat#ROUNDTRIP}, then the format will be determined by objectPrefersNewPacketFormat.
+ *
+ * @param objectPrefersNewPacketFormat whether the packet prefers to be encoded using the new packet format
+ * @param tag packet tag
+ * @param body packet body
+ * @throws IOException
+ */
+ void writePacket(
+ boolean objectPrefersNewPacketFormat,
+ int tag,
+ byte[] body)
+ throws IOException
+ {
+ boolean oldPacketFormat = packetFormat == PacketFormat.LEGACY ||
+ (packetFormat == PacketFormat.ROUNDTRIP && !objectPrefersNewPacketFormat);
+ this.writeHeader(tag, oldPacketFormat, false, body.length);
+ this.write(body);
+ }
+
+ /**
+ * Write a packet, forcing the packet format to be either old or new.
+ * @param tag packet tag
+ * @param body packet body
+ * @param oldFormat if true, old format is forced, else force new format
+ * @throws IOException
+ */
void writePacket(
int tag,
byte[] body,
@@ -379,4 +434,5 @@ public void close()
out.flush();
out.close();
}
+
}
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/CompressedDataPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/CompressedDataPacket.java
index 9dcafaa61c..accd96445c 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/CompressedDataPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/CompressedDataPacket.java
@@ -11,10 +11,18 @@ public class CompressedDataPacket
int algorithm;
CompressedDataPacket(
- BCPGInputStream in)
+ BCPGInputStream in)
+ throws IOException
+ {
+ this(in, false);
+ }
+
+ CompressedDataPacket(
+ BCPGInputStream in,
+ boolean newPacketFormat)
throws IOException
{
- super(in, COMPRESSED_DATA);
+ super(in, COMPRESSED_DATA, newPacketFormat);
algorithm = in.read();
}
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/ContainedPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/ContainedPacket.java
index 27c82a5e4d..8f87a96af7 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/ContainedPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/ContainedPacket.java
@@ -12,21 +12,30 @@ public abstract class ContainedPacket
extends Packet
implements Encodable
{
+
ContainedPacket(int packetTag)
{
- super(packetTag);
+ this(packetTag, false);
+ }
+
+ ContainedPacket(int packetTag, boolean newPacketFormat)
+ {
+ super(packetTag, newPacketFormat);
}
public byte[] getEncoded()
throws IOException
{
- ByteArrayOutputStream bOut = new ByteArrayOutputStream();
- BCPGOutputStream pOut = new BCPGOutputStream(bOut);
-
- pOut.writePacket(this);
+ return getEncoded(PacketFormat.ROUNDTRIP);
+ }
+ public byte[] getEncoded(PacketFormat format)
+ throws IOException
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ BCPGOutputStream pOut = new BCPGOutputStream(bOut, format);
+ pOut.writePacket(this);
pOut.close();
-
return bOut.toByteArray();
}
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/ExperimentalPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/ExperimentalPacket.java
index 927b1bade9..b787b02ba9 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/ExperimentalPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/ExperimentalPacket.java
@@ -11,7 +11,20 @@ public class ExperimentalPacket
extends ContainedPacket implements PublicKeyAlgorithmTags
{
private byte[] contents;
-
+
+ /**
+ *
+ * @param in
+ * @throws IOException
+ */
+ ExperimentalPacket(
+ int tag,
+ BCPGInputStream in)
+ throws IOException
+ {
+ this(tag, in, false);
+ }
+
/**
*
* @param in
@@ -19,10 +32,11 @@ public class ExperimentalPacket
*/
ExperimentalPacket(
int tag,
- BCPGInputStream in)
+ BCPGInputStream in,
+ boolean newPacketFormat)
throws IOException
{
- super(tag);
+ super(tag, newPacketFormat);
this.contents = in.readAll();
}
@@ -44,6 +58,6 @@ public void encode(
BCPGOutputStream out)
throws IOException
{
- out.writePacket(getPacketTag(), contents);
+ out.writePacket(hasNewPacketFormat(), getPacketTag(), contents);
}
}
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/InputStreamPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/InputStreamPacket.java
index f042703be2..2dfb9b87af 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/InputStreamPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/InputStreamPacket.java
@@ -22,8 +22,15 @@ public InputStreamPacket(
BCPGInputStream in,
int packetTag)
{
- super(packetTag);
-
+ this(in, packetTag, false);
+ }
+
+ InputStreamPacket(
+ BCPGInputStream in,
+ int packetTag,
+ boolean newPacketFormat)
+ {
+ super(packetTag, newPacketFormat);
this.in = in;
}
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/LiteralDataPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/LiteralDataPacket.java
index 16e64c377b..3446c27f60 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/LiteralDataPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/LiteralDataPacket.java
@@ -16,10 +16,18 @@ public class LiteralDataPacket
long modDate;
LiteralDataPacket(
- BCPGInputStream in)
+ BCPGInputStream in)
+ throws IOException
+ {
+ this(in, false);
+ }
+
+ LiteralDataPacket(
+ BCPGInputStream in,
+ boolean newPacketFormat)
throws IOException
{
- super(in, LITERAL_DATA);
+ super(in, LITERAL_DATA, newPacketFormat);
format = in.read();
int l = in.read();
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/MarkerPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/MarkerPacket.java
index 2f4a8da428..dad7658e3f 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/MarkerPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/MarkerPacket.java
@@ -13,10 +13,18 @@ public class MarkerPacket
byte[] marker = {(byte)0x50, (byte)0x47, (byte)0x50};
public MarkerPacket(
- BCPGInputStream in)
+ BCPGInputStream in)
+ throws IOException
+ {
+ this(in, false);
+ }
+
+ public MarkerPacket(
+ BCPGInputStream in,
+ boolean newPacketFormat)
throws IOException
{
- super(MARKER);
+ super(MARKER, newPacketFormat);
in.readFully(marker);
}
@@ -25,6 +33,6 @@ public void encode(
BCPGOutputStream out)
throws IOException
{
- out.writePacket(MARKER, marker);
+ out.writePacket(hasNewPacketFormat(), MARKER, marker);
}
}
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/ModDetectionCodePacket.java b/pg/src/main/java/org/bouncycastle/bcpg/ModDetectionCodePacket.java
index 7e837b510f..2fcef653da 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/ModDetectionCodePacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/ModDetectionCodePacket.java
@@ -9,12 +9,20 @@ public class ModDetectionCodePacket
extends ContainedPacket
{
private byte[] digest;
-
+
ModDetectionCodePacket(
- BCPGInputStream in)
+ BCPGInputStream in)
+ throws IOException
+ {
+ this(in, false);
+ }
+
+ ModDetectionCodePacket(
+ BCPGInputStream in,
+ boolean newPacketFormat)
throws IOException
{
- super(MOD_DETECTION_CODE);
+ super(MOD_DETECTION_CODE, newPacketFormat);
this.digest = new byte[20];
in.readFully(this.digest);
@@ -44,6 +52,6 @@ public void encode(
BCPGOutputStream out)
throws IOException
{
- out.writePacket(MOD_DETECTION_CODE, digest, false);
+ out.writePacket(hasNewPacketFormat(), MOD_DETECTION_CODE, digest);
}
}
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/OnePassSignaturePacket.java b/pg/src/main/java/org/bouncycastle/bcpg/OnePassSignaturePacket.java
index cf6189fdd4..218ffa788b 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/OnePassSignaturePacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/OnePassSignaturePacket.java
@@ -45,10 +45,18 @@ public class OnePassSignaturePacket
* @throws IOException when the end of stream is prematurely reached, or when the packet is malformed
*/
OnePassSignaturePacket(
- BCPGInputStream in)
+ BCPGInputStream in)
+ throws IOException
+ {
+ this(in, false);
+ }
+
+ OnePassSignaturePacket(
+ BCPGInputStream in,
+ boolean newPacketFormat)
throws IOException
{
- super(ONE_PASS_SIGNATURE);
+ super(ONE_PASS_SIGNATURE, newPacketFormat);
version = in.read();
sigType = in.read();
@@ -274,7 +282,7 @@ else if (version == VERSION_6)
pOut.close();
- out.writePacket(ONE_PASS_SIGNATURE, bOut.toByteArray());
+ out.writePacket(hasNewPacketFormat(), ONE_PASS_SIGNATURE, bOut.toByteArray());
}
}
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/Packet.java b/pg/src/main/java/org/bouncycastle/bcpg/Packet.java
index 3356c04650..697683c14c 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/Packet.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/Packet.java
@@ -6,6 +6,7 @@ public class Packet
implements PacketTags
{
private final int packetTag;
+ private final boolean newPacketFormat;
// for API compatibility
public Packet()
@@ -14,8 +15,14 @@ public Packet()
}
Packet(int packetTag)
+ {
+ this(packetTag, false);
+ }
+
+ Packet(int packetTag, boolean newPacketFormat)
{
this.packetTag = packetTag;
+ this.newPacketFormat = newPacketFormat;
}
/**
@@ -28,6 +35,17 @@ public final int getPacketTag()
return packetTag;
}
+ /**
+ * Return true, if this instance of a packet was encoded using the new packet format.
+ * If the packet was encoded using the old legacy format, return false instead.
+ *
+ * @return true if new packet format encoding is used
+ */
+ public boolean hasNewPacketFormat()
+ {
+ return newPacketFormat;
+ }
+
/**
* Returns whether the packet is to be considered critical for v6 implementations.
* Packets with tags less or equal to 39 are critical.
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/PacketFormat.java b/pg/src/main/java/org/bouncycastle/bcpg/PacketFormat.java
new file mode 100644
index 0000000000..61f62d0340
--- /dev/null
+++ b/pg/src/main/java/org/bouncycastle/bcpg/PacketFormat.java
@@ -0,0 +1,26 @@
+package org.bouncycastle.bcpg;
+
+/**
+ * OpenPGP Packet Header Length Format.
+ *
+ * @see
+ * OpenPGP Packet Headers
+ */
+public enum PacketFormat
+{
+ /**
+ * Always use the old (legacy) packet format.
+ */
+ LEGACY,
+
+ /**
+ * Always use the current (new) packet format.
+ */
+ CURRENT,
+
+ /**
+ * Let the individual packet decide the format (see {@link Packet#hasNewPacketFormat()}).
+ * This allows to round-trip packets without changing the packet format.
+ */
+ ROUNDTRIP
+}
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/PaddingPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/PaddingPacket.java
index e50b863eaf..bd45514606 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/PaddingPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/PaddingPacket.java
@@ -12,16 +12,22 @@ public class PaddingPacket
private final byte[] padding;
public PaddingPacket(BCPGInputStream in)
+ throws IOException
+ {
+ this(in, true);
+ }
+
+ public PaddingPacket(BCPGInputStream in, boolean newPacketFormat)
throws IOException
{
- super(PADDING);
+ super(PADDING, newPacketFormat);
padding = Streams.readAll(in);
}
public PaddingPacket(byte[] padding)
{
- super(PADDING);
+ super(PADDING, true);
this.padding = padding;
}
@@ -47,6 +53,6 @@ public byte[] getPadding()
public void encode(BCPGOutputStream pOut)
throws IOException
{
- pOut.writePacket(PacketTags.PADDING, padding);
+ pOut.writePacket(hasNewPacketFormat(), PacketTags.PADDING, padding);
}
}
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyEncSessionPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyEncSessionPacket.java
index 072bcdfdeb..b75dc5e4c3 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyEncSessionPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyEncSessionPacket.java
@@ -33,10 +33,18 @@ public class PublicKeyEncSessionPacket
private byte[] keyFingerprint; // v6
PublicKeyEncSessionPacket(
- BCPGInputStream in)
+ BCPGInputStream in)
+ throws IOException
+ {
+ this(in, false);
+ }
+
+ PublicKeyEncSessionPacket(
+ BCPGInputStream in,
+ boolean newPacketFormat)
throws IOException
{
- super(PUBLIC_KEY_ENC_SESSION);
+ super(PUBLIC_KEY_ENC_SESSION, newPacketFormat);
version = in.read();
@@ -271,6 +279,6 @@ else if (version == VERSION_6)
pOut.close();
- out.writePacket(PUBLIC_KEY_ENC_SESSION, bOut.toByteArray());
+ out.writePacket(hasNewPacketFormat(), PUBLIC_KEY_ENC_SESSION, bOut.toByteArray());
}
}
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyPacket.java
index 0026365b9a..f67e546836 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyPacket.java
@@ -1,5 +1,7 @@
package org.bouncycastle.bcpg;
+import sun.jvm.hotspot.types.JBooleanField;
+
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Date;
@@ -22,18 +24,34 @@ public class PublicKeyPacket
private BCPGKey key;
PublicKeyPacket(
- BCPGInputStream in)
+ BCPGInputStream in)
+ throws IOException
+ {
+ this(in, false);
+ }
+ PublicKeyPacket(
+ BCPGInputStream in,
+ boolean newPacketFormat)
throws IOException
{
- this(PUBLIC_KEY, in);
+ this(PUBLIC_KEY, in, newPacketFormat);
+ }
+
+ PublicKeyPacket(
+ int keyTag,
+ BCPGInputStream in)
+ throws IOException
+ {
+ this(keyTag, in, false);
}
PublicKeyPacket(
int keyTag,
- BCPGInputStream in)
+ BCPGInputStream in,
+ boolean newPacketFormat)
throws IOException
{
- super(keyTag);
+ super(keyTag, newPacketFormat);
version = in.read();
time = ((long)in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read();
@@ -188,6 +206,6 @@ public void encode(
BCPGOutputStream out)
throws IOException
{
- out.writePacket(getPacketTag(), getEncodedContents());
+ out.writePacket(hasNewPacketFormat(), getPacketTag(), getEncodedContents());
}
}
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/PublicSubkeyPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/PublicSubkeyPacket.java
index ea02f07f59..bcf62bc0a5 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/PublicSubkeyPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/PublicSubkeyPacket.java
@@ -9,11 +9,20 @@
public class PublicSubkeyPacket
extends PublicKeyPacket
{
+
+ PublicSubkeyPacket(
+ BCPGInputStream in)
+ throws IOException
+ {
+ this(in, false);
+ }
+
PublicSubkeyPacket(
- BCPGInputStream in)
+ BCPGInputStream in,
+ boolean newPacketFormat)
throws IOException
{
- super(PUBLIC_SUBKEY, in);
+ super(PUBLIC_SUBKEY, in, newPacketFormat);
}
/**
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/ReservedPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/ReservedPacket.java
index cf2f597c2f..bc766b6864 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/ReservedPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/ReservedPacket.java
@@ -5,6 +5,11 @@ public class ReservedPacket
{
public ReservedPacket(BCPGInputStream in)
{
- super(in, RESERVED);
+ this(in, false);
+ }
+
+ public ReservedPacket(BCPGInputStream in, boolean newPacketFormat)
+ {
+ super(in, RESERVED, newPacketFormat);
}
}
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/SecretKeyPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/SecretKeyPacket.java
index decb8a1214..198d3333b0 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/SecretKeyPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/SecretKeyPacket.java
@@ -69,22 +69,43 @@ public class SecretKeyPacket
private byte[] iv;
SecretKeyPacket(
- BCPGInputStream in)
- throws IOException
+ BCPGInputStream in)
+ throws IOException
{
this(SECRET_KEY, in);
}
+ SecretKeyPacket(
+ BCPGInputStream in,
+ boolean newPacketFormat)
+ throws IOException
+ {
+ this(SECRET_KEY, in, newPacketFormat);
+ }
+
+ /**
+ * @param in
+ * @throws IOException
+ */
+ SecretKeyPacket(
+ int keyTag,
+ BCPGInputStream in)
+ throws IOException
+ {
+ this(keyTag, in, false);
+ }
+
/**
* @param in
* @throws IOException
*/
SecretKeyPacket(
int keyTag,
- BCPGInputStream in)
+ BCPGInputStream in,
+ boolean newPacketFormat)
throws IOException
{
- super(keyTag);
+ super(keyTag, newPacketFormat);
if (this instanceof SecretSubkeyPacket)
{
@@ -323,6 +344,6 @@ public void encode(
BCPGOutputStream out)
throws IOException
{
- out.writePacket(getPacketTag(), getEncodedContents());
+ out.writePacket(hasNewPacketFormat(), getPacketTag(), getEncodedContents());
}
}
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/SecretSubkeyPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/SecretSubkeyPacket.java
index b7610747d9..e048aa4d16 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/SecretSubkeyPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/SecretSubkeyPacket.java
@@ -18,9 +18,16 @@ public class SecretSubkeyPacket
BCPGInputStream in)
throws IOException
{
- super(SECRET_SUBKEY, in);
+ this(in, false);
}
+ SecretSubkeyPacket(
+ BCPGInputStream in,
+ boolean newPacketFormat)
+ throws IOException
+ {
+ super(SECRET_SUBKEY, in, newPacketFormat);
+ }
/**
* Create a secret subkey packet.
* If the encryption algorithm is NOT {@link SymmetricKeyAlgorithmTags#NULL},
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java b/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java
index 52bb39e959..d716dcdae7 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java
@@ -36,10 +36,18 @@ public class SignaturePacket
private byte[] salt; // v6 only
SignaturePacket(
- BCPGInputStream in)
+ BCPGInputStream in)
+ throws IOException
+ {
+ this(in, false);
+ }
+
+ SignaturePacket(
+ BCPGInputStream in,
+ boolean newPacketFormat)
throws IOException
{
- super(SIGNATURE);
+ super(SIGNATURE, newPacketFormat);
version = in.read();
switch (version)
@@ -686,7 +694,7 @@ else if (version == VERSION_4 || version == VERSION_5 || version == VERSION_6)
pOut.close();
- out.writePacket(SIGNATURE, bOut.toByteArray());
+ out.writePacket(hasNewPacketFormat(), SIGNATURE, bOut.toByteArray());
}
private void setCreationTime()
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/SymmetricEncDataPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/SymmetricEncDataPacket.java
index eeca55b97c..90cf34ece3 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/SymmetricEncDataPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/SymmetricEncDataPacket.java
@@ -10,9 +10,16 @@ public class SymmetricEncDataPacket
implements BCPGHeaderObject
{
public SymmetricEncDataPacket(
- BCPGInputStream in)
+ BCPGInputStream in)
{
- super(in, SYMMETRIC_KEY_ENC);
+ this(in, false);
+ }
+
+ public SymmetricEncDataPacket(
+ BCPGInputStream in,
+ boolean newPacketFormat)
+ {
+ super(in, SYMMETRIC_KEY_ENC, newPacketFormat);
}
public SymmetricEncDataPacket()
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/SymmetricEncIntegrityPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/SymmetricEncIntegrityPacket.java
index d2f81746c1..79bc42357c 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/SymmetricEncIntegrityPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/SymmetricEncIntegrityPacket.java
@@ -31,10 +31,18 @@ public class SymmetricEncIntegrityPacket
byte[] salt; // V2
SymmetricEncIntegrityPacket(
- BCPGInputStream in)
+ BCPGInputStream in)
+ throws IOException
+ {
+ this(in, false);
+ }
+
+ SymmetricEncIntegrityPacket(
+ BCPGInputStream in,
+ boolean newPacketFormat)
throws IOException
{
- super(in, SYM_ENC_INTEGRITY_PRO);
+ super(in, SYM_ENC_INTEGRITY_PRO, newPacketFormat);
version = in.read();
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/SymmetricKeyEncSessionPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/SymmetricKeyEncSessionPacket.java
index 25220f10b5..c8574ca5ca 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/SymmetricKeyEncSessionPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/SymmetricKeyEncSessionPacket.java
@@ -40,10 +40,17 @@ public class SymmetricKeyEncSessionPacket
private byte[] authTag; // V5, V6
public SymmetricKeyEncSessionPacket(
- BCPGInputStream in)
+ BCPGInputStream in)
+ throws IOException
+ {
+ this(in, false);
+ }
+ public SymmetricKeyEncSessionPacket(
+ BCPGInputStream in,
+ boolean newPacketFormat)
throws IOException
{
- super(SYMMETRIC_KEY_ENC_SESSION);
+ super(SYMMETRIC_KEY_ENC_SESSION, newPacketFormat);
version = in.read();
if (version == VERSION_4)
@@ -349,6 +356,6 @@ else if (version == VERSION_5 || version == VERSION_6)
pOut.close();
- out.writePacket(SYMMETRIC_KEY_ENC_SESSION, bOut.toByteArray());
+ out.writePacket(hasNewPacketFormat(), SYMMETRIC_KEY_ENC_SESSION, bOut.toByteArray());
}
}
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/TrustPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/TrustPacket.java
index a009240504..db920ec297 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/TrustPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/TrustPacket.java
@@ -10,12 +10,20 @@ public class TrustPacket
extends ContainedPacket
{
byte[] levelAndTrustAmount;
-
+
public TrustPacket(
- BCPGInputStream in)
+ BCPGInputStream in)
+ throws IOException
+ {
+ this(in, false);
+ }
+
+ public TrustPacket(
+ BCPGInputStream in,
+ boolean newPacketFormat)
throws IOException
{
- super(TRUST);
+ super(TRUST, newPacketFormat);
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
int ch;
@@ -47,6 +55,6 @@ public void encode(
BCPGOutputStream out)
throws IOException
{
- out.writePacket(TRUST, levelAndTrustAmount);
+ out.writePacket(hasNewPacketFormat(), TRUST, levelAndTrustAmount);
}
}
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/UnknownPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/UnknownPacket.java
index 80fbfb4dff..5f11541625 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/UnknownPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/UnknownPacket.java
@@ -12,7 +12,13 @@ public class UnknownPacket
public UnknownPacket(int tag, BCPGInputStream in)
throws IOException
{
- super(tag);
+ this(tag, in, false);
+ }
+
+ public UnknownPacket(int tag, BCPGInputStream in, boolean newPacketFormat)
+ throws IOException
+ {
+ super(tag, newPacketFormat);
this.contents = in.readAll();
}
@@ -26,6 +32,6 @@ public void encode(
BCPGOutputStream out)
throws IOException
{
- out.writePacket(getPacketTag(), contents);
+ out.writePacket(hasNewPacketFormat(), getPacketTag(), contents);
}
}
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/UserAttributePacket.java b/pg/src/main/java/org/bouncycastle/bcpg/UserAttributePacket.java
index e87a36a7fe..d0b099cfb7 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/UserAttributePacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/UserAttributePacket.java
@@ -11,12 +11,20 @@ public class UserAttributePacket
extends ContainedPacket
{
private UserAttributeSubpacket[] subpackets;
-
+
public UserAttributePacket(
- BCPGInputStream in)
+ BCPGInputStream in)
+ throws IOException
+ {
+ this(in, false);
+ }
+
+ public UserAttributePacket(
+ BCPGInputStream in,
+ boolean newPacketFormat)
throws IOException
{
- super(USER_ATTRIBUTE);
+ super(USER_ATTRIBUTE, newPacketFormat);
UserAttributeSubpacketInputStream sIn = new UserAttributeSubpacketInputStream(in);
UserAttributeSubpacket sub;
@@ -59,6 +67,6 @@ public void encode(
subpackets[i].encode(bOut);
}
- out.writePacket(USER_ATTRIBUTE, bOut.toByteArray());
+ out.writePacket(hasNewPacketFormat(), USER_ATTRIBUTE, bOut.toByteArray());
}
}
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/UserIDPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/UserIDPacket.java
index 0cd550352d..7256e1466b 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/UserIDPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/UserIDPacket.java
@@ -13,12 +13,20 @@ public class UserIDPacket
implements UserDataPacket
{
private byte[] idData;
-
+
+ public UserIDPacket(
+ BCPGInputStream in)
+ throws IOException
+ {
+ this(in, false);
+ }
+
public UserIDPacket(
- BCPGInputStream in)
+ BCPGInputStream in,
+ boolean newPacketFormat)
throws IOException
{
- super(USER_ID);
+ super(USER_ID, newPacketFormat);
this.idData = in.readAll();
}
@@ -67,6 +75,6 @@ public void encode(
BCPGOutputStream out)
throws IOException
{
- out.writePacket(USER_ID, idData);
+ out.writePacket(hasNewPacketFormat(), USER_ID, idData);
}
}
diff --git a/pg/src/test/java/org/bouncycastle/bcpg/test/BCPGOutputStreamTest.java b/pg/src/test/java/org/bouncycastle/bcpg/test/BCPGOutputStreamTest.java
new file mode 100644
index 0000000000..4e02f9378f
--- /dev/null
+++ b/pg/src/test/java/org/bouncycastle/bcpg/test/BCPGOutputStreamTest.java
@@ -0,0 +1,283 @@
+package org.bouncycastle.bcpg.test;
+
+import org.bouncycastle.bcpg.*;
+import org.bouncycastle.openpgp.*;
+import org.bouncycastle.openpgp.bc.BcPGPObjectFactory;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class BCPGOutputStreamTest extends SimpleTest {
+
+ private void testForceNewPacketFormat() throws IOException {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ BCPGOutputStream pOut = new BCPGOutputStream(bOut, PacketFormat.CURRENT);
+
+ new UserIDPacket("Alice").encode(pOut);
+ new UserIDPacket("Bob").encode(pOut);
+
+ pOut.close();
+
+ ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray());
+ BCPGInputStream pIn = new BCPGInputStream(bIn);
+
+ isTrue(pIn.readPacket().hasNewPacketFormat());
+ isTrue(pIn.readPacket().hasNewPacketFormat());
+ }
+
+ private void testForceOldPacketFormat() throws IOException {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ BCPGOutputStream pOut = new BCPGOutputStream(bOut, PacketFormat.LEGACY);
+
+ new UserIDPacket("Alice").encode(pOut);
+ new UserIDPacket("Bob").encode(pOut);
+
+ pOut.close();
+
+ ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray());
+ BCPGInputStream pIn = new BCPGInputStream(bIn);
+
+ isTrue(!pIn.readPacket().hasNewPacketFormat());
+ isTrue(!pIn.readPacket().hasNewPacketFormat());
+ }
+
+ private void testRoundTripPacketFormat() throws IOException {
+ List oldPackets = new ArrayList<>();
+ ByteArrayInputStream obIn = new ByteArrayInputStream(Hex.decode("b405416c696365b403426f62"));
+ BCPGInputStream opIn = new BCPGInputStream(obIn);
+ oldPackets.add((UserIDPacket) opIn.readPacket());
+ oldPackets.add((UserIDPacket) opIn.readPacket());
+
+ List newPackets = new ArrayList<>();
+ ByteArrayInputStream nbIn = new ByteArrayInputStream(Hex.decode("cd05416c696365cd03426f62"));
+ BCPGInputStream npIn = new BCPGInputStream(nbIn);
+ newPackets.add((UserIDPacket) npIn.readPacket());
+ newPackets.add((UserIDPacket) npIn.readPacket());
+
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ BCPGOutputStream pOut = new BCPGOutputStream(bOut, PacketFormat.ROUNDTRIP);
+
+ // Write New, Old, Old, New
+ pOut.writePacket(newPackets.get(0));
+ pOut.writePacket(oldPackets.get(0));
+ pOut.writePacket(oldPackets.get(1));
+ pOut.writePacket(newPackets.get(1));
+ pOut.close();
+
+ ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray());
+ BCPGInputStream pIn = new BCPGInputStream(bIn);
+
+ // Test New, Old, Old, New
+ isTrue(pIn.readPacket().hasNewPacketFormat());
+ isTrue(!pIn.readPacket().hasNewPacketFormat());
+ isTrue(!pIn.readPacket().hasNewPacketFormat());
+ isTrue(pIn.readPacket().hasNewPacketFormat());
+ }
+
+ private void testRoundtripMixedPacketFormats() throws IOException {
+ // Certificate with mixed new and old packet formats
+ // The primary key + sigs use new format
+ // The signing subkey + sigs use old format
+ // The encryption subkey + sigs use new format again
+ String encodedCert = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
+ "Version: BCPG v@RELEASE_NAME@\n" +
+ "\n" +
+ "xcTGBGYvuZUBDACyFv3LQiubgHM4eJUFnsLEei8/l4bGKdVx8hRu6N5rfcjZt3RM\n" +
+ "UGUi+HQDnRbUvJ5B/7qDB7Ia7bpRf7BrYmho5vqNtjpxUPs3Mct1TjqCm2yLC9zH\n" +
+ "+qHmGSPX4dLtSKKpXc4iBMGtFknhXKnoUovv7XZDecIIDbJhaqoFptRfqFc30SRj\n" +
+ "ktQcyXutcIYlhaPQ/JtJlNWfmo0+NTEjfpDOZovCzAi769QnljntkiKXxQzBihzb\n" +
+ "f1Ou7NzJ17m/7pJXBOTKKXboMFDc1ct6f2s2lomEhCYRPRC6eITWpXK/G7e503xS\n" +
+ "eaMnd29BrmbEnw6QgCjafu72t8rgD6jbj3+kaRR85AevLWrfefo9ofhUMl0DDKc+\n" +
+ "bhNQAMRZY9vlRdEo0pNLL9kIMzl10HL9viRXxCwp4d1chH0qLQdy8W13WrjhS0Fw\n" +
+ "GlEkcTt2Z/4kGmYeLvBfQqfz62owIR47otX8JU+QdTmP89SyZRyuHVwB+Pgg32oC\n" +
+ "1fSJUVHRCb6f1z0AEQEAAf4JAwJgAx2GH4oiN2CgjB+JAKrjlbrfjhLDN+w14SeC\n" +
+ "vGpP40Ay/bUFYgfvkVA3CQFMYJ4fCKluhu2cHAhzCrGCAKs42ZvkKzHuDohzEjGG\n" +
+ "N9IkA4y/Gv2tTewhtMALxbHtS7CCX6IBqC292mupm7ND4aULhLM4xqXKXUny543V\n" +
+ "hPJEuKL8D5CLRqFJPtrw/791izbdr6J+rKxYwscL5NiUJQFLVsK59+7sJvSvBK9N\n" +
+ "DIiFT+hRSJjb6rBrXWZK0bUCsaCLHL0k8PLPdBdzZ3YJqcaIFRd0Sq7l7Ck7RxXh\n" +
+ "L8tf8Swcr1UafMMYbyMJW5VtJxZStV1OgdWVatrbkW5GINlZ7pp63kIOY2GbK99H\n" +
+ "Q4mAQV1EpAlItF4QqkOunyGqw6aN5x9+Hoyr0O17428604wsptZstNT0wz3qGJJ/\n" +
+ "ye3I9xveEMhRwv1ZB3MsiiBLj5kEa2l4W6O3g/6Sdu75MGhalyi5+r41SgKkxMWI\n" +
+ "OCTZMWCNZ1ZR9Ehlsg5uOtNUc9RkAn9BAfBzDwBMMa8wzHKsNiaiR2Tonpm1+miB\n" +
+ "TtBU7RUw5CEAcGbnYmLvlxVwe7CTVezYIQIroCCqY738Y5mpOSB0UhT1JAORPCJJ\n" +
+ "PCWRGQ9UDm0xK/dFbbRNTJqYM2GZKOM5esnKQlxJ1t9+VoQ+mPLOPHnkmUXU8Rq8\n" +
+ "9c3bLogW/dsCm0j8lfV0sKsAXfuD3spHPoJwQOFwZ4kDeFjJlRTusNSgXQsRF/x4\n" +
+ "Wvwt+jmxRwenkeVunigqZVOhctf8ozkncpCV8tTLp8X2VrlJNx5XoggFzOwEWxJa\n" +
+ "mMx1gvsAlsEiTCK5g6xocULxKfBfJogUrBXZk0GUJriJzB470QMnQ49a4gGFh4w7\n" +
+ "Wh2/FiRRMIGTTXPFHSQ92kaWoqO1jDhM7c6HrxEETS7NrWi6TLvfYxrCZ9GlYanK\n" +
+ "MPDa75lVpuE95M1+dRShX13LJpSlOq8Eius/kP9W54sCT4DTIvlz08QdcaNpRN/9\n" +
+ "ZZGJw6gMttshvvl1eOKOjD3iW110qJtxh/W4OxgDL7R+JKKYfjc9lWMIhfy1sOQK\n" +
+ "cmXZVP6HVK5y+xwW8h8MTqjUznYo1Lfu6icmBm7q0/P2lSkBSQ76kSL4exgtYr4k\n" +
+ "XRj483ipnlUr8ue53ALXOC46NIj8wE2+LDg1rAt3AWru1fOGFMU4bol7ytBoBKmz\n" +
+ "Cvv/6hxDZGSR43n+FLWMd40hRjneeM+0oy/6vvU0Rsa3FdL7m+/Rui8CC4JoG/Kc\n" +
+ "xl3uj0OgfHvHYPjLoXOPUdPNIptwfCQ9xvEfWWJA4hcyRdToy1gjYINSmJDIhYgL\n" +
+ "yyFlm2GlHMKDo71TEODRwNHy61Jdikx43c0ZQWxpY2UgPGFsaWNlQGV4YW1wbGUu\n" +
+ "Y29tPsLA+QQTAQoAIwUCZi+5lwKbAQIeARYhBLSFPwVS++7sNFWR0nm9t4YHzlXs\n" +
+ "AAoJEHm9t4YHzlXs7soL/jtmG5E6helkBjFLZqBfXJUxIniEtOxT0GrePTfA7lde\n" +
+ "0hKDh3Wjh0+RmfnuatopWW1DRKmhnS0uAIwIewIH7rzhHG+i9OHAwZ6R61ptEKmH\n" +
+ "WL5JeqTNq3bLD6U6VgfrFq1DNxtfTWTPwTzSIBuGVLJjRFEqq5olH4dD6xImO7Lk\n" +
+ "t3KJ9Du8IdmLsoEcw0tMhd5cSbh2gE1F1CnmSufDts2coTv7B/lQTAhOFQQedMFa\n" +
+ "N/mKJ/v+DvRjB9nV+rYqeqweTLJ2AJcmnmDTiue74CgP2o84Cf7JEAZ83vy2hHLq\n" +
+ "KGvsYbQoE1oSt7vU9otGotSutrFZww8LmnkJCQwHPrNWC21CKoo/7bGo4ToDaVOw\n" +
+ "FEC9l2pusMaN6y9ztsq2Wz3mlQppe0kizMmkA+WM34Lu0EI3DGQvqIcIMKEg242B\n" +
+ "e8gV2qN7t/zMvLM34A4sDD7L1e8dKKnru0MY73TaMAw+kbRuM/DrQpT4PI1cvD4j\n" +
+ "xN/rVB0g4JIuVElygMLA950FhgRmL7mVAQwA2WGMqveX3ssz5tdKIP6q8krGSwsF\n" +
+ "CR7qBGVac5XiYaNzg6YJX+r8CiSAT+mN55t7TN9C7kND9zlssLJidKuXs87Bgwjl\n" +
+ "gmuO0AL9VFKTx2IkEVovwwKvozJd9vt79IKY6wJ4eqbElaBfNy2uov4kuOxcEEuY\n" +
+ "n7UQttW6Pp0JFP1lb+hZ524r05wYb7LGdPyz1vNPeYEg48PkcNU6Z9DXfU80JVp8\n" +
+ "Cy0XNxHm31ML/DgLJHIVZ0dstA2KZnWxhlKNNfdmYakGEU5QISGViReybcc3kwco\n" +
+ "v5agauCUWNFvLM7NYI7S1m5A0r6hWM/CtQwUgb0PIT97nEbYmIB+su+6RTLvOkMM\n" +
+ "3465MxjBVwwiWFpZcHiUP3q5Eelr1V75rOwII3HKSC1tfZwjWxyzdBpPZDpMIxHT\n" +
+ "9ldJnlLzIvUOR2pSusqnGrCeurGqNxT/b9lEifoHcDNiyo+Qj4WsOjp+I9sBkfYP\n" +
+ "G3Hbx/OmKvkSY4a+L53iY2H1xjAfYwySI5KBABEBAAH+CQMCYAMdhh+KIjdg8Sop\n" +
+ "plGYPDvXQ3N1JgtYiGXjOuvsEIuxqmY4CwtFATtqjphFPe+6GL4zS16vcGlAPwgI\n" +
+ "h0+aZQxPHKJcUx997zm9PFrOjnSInFraAdIvpBay+5DlicGJuARZ/8ZhNZ7qxbIT\n" +
+ "vi5ZVRnrPef6SO5E1zeSFodMV5FZE1fZNTeSq2AOQ/tIkMPsjK/BpKSTLSebkwii\n" +
+ "G3IgQEB6albQsfpKTqSCgDS9o0/b5/q+KMYtHoS0XwoE46df1wfjirz6zNY6VShd\n" +
+ "3MaJ+Jb1GzsRCTTedKHHnF/fU49uWs7LNRZT1PhSMBH/scL1w2u8bmdvW0i8PJaN\n" +
+ "bbdX/Zs2sVxfACfeUSkF6GAmqnc1+6RnZSxMjSqk/1uPhtuSUa1NsJ9UlxHIQcDp\n" +
+ "NGlQCrabUdhmjfZWtXCIcINXj0JPfMTNIxLC4YBSUvV+y//UEzq+LYT0tnOXeU6O\n" +
+ "/JCzhEoGhPvboFfv8p3fPVvvLFseONZuX3d9WzbAQiCpCXlP4+Ro0OFw32JGTZLq\n" +
+ "mi6eiJceAJ4sza4V/DeaDovJ62RJHzJOtb8+cOFyo+/8m46YMF07X2AwdjE8Az69\n" +
+ "td1N79S9eqtYjV1VWrrf94fpeUGV4UD1ugt/UalGbGm4IQFTGZGb0vinImLoM7ts\n" +
+ "84BneaosoYh/62bA5OzIF9xqHjFQ9XiNjwODFpiIZl3twL7DVDWf9Paq9ki1mv2D\n" +
+ "pan8sJtsZYj0j0D+V9nDk5LSBiMnb+qaagq/Wt648eqaxGP1gb5B/w9rrYiF0/TX\n" +
+ "H5q/qzHxh9j8sPEAM3R7Y9+IL1RgF0/3VgBx52/eJxVvb9FUZOoF7cvrESAFDqSo\n" +
+ "p3/pN07kMN3fHNIFpQGbsC6ECmEatPnANJH0InDnPERTGasiCtshdzx8bTLIdh95\n" +
+ "3hTJuv6ML8+PP9Jp4eLiAkinW+leBEyFgpYMFBdSifQ5R/jU7n/6IcW/4u5t66if\n" +
+ "RFnE0N2hpMUQPTl5hZH5TY9AU55MZCLzvDbYW/cXmIuBuRNVfaLIWSKp8UxFwZfk\n" +
+ "zki8N+EUPeB1dPaFapuArvpmSAl7uLzNYAb7X4Tf29VBlz2zRhh2dMAOtzX+55YX\n" +
+ "nNE2gEiGP1FAA00l/nIKWIHf0u6zMO0jj6soSwsan9VfoyK9jc7qDObPrW0v0lsK\n" +
+ "0CknbavMkrS+DdhTWAzYhvdSUZV80H/lRUwrTyCpaiRuuDu+kZOScTH1JsFkDEix\n" +
+ "NypeMhSIh5Izzf0njazQSxzoI6XcEJLukFPONvZ0oTsXF1j0IITFboWjQpuUJ/kg\n" +
+ "ZGBuBlllk2gD7t3H8r1zRiKkHaw9Nr4Fr4r5wNJVfVKrnQkuQBJneP2JA1UEGAEK\n" +
+ "Ab8FAmYvuZcCmwIWIQS0hT8FUvvu7DRVkdJ5vbeGB85V7MDdIAQZAQoABgUCZi+5\n" +
+ "lwAKCRDVw7vf0dE2sN77DACUd5X+RFI264quxxPZlO/jmcu3PfoeGtWL4ILMZ8Lj\n" +
+ "4NyoqctmRthZzEvvyzmg/IQOPJlIru18aJKZQgrKkQzytbd+BL8GfsTXh7XwICcu\n" +
+ "xS9pzMMKi29rU8ViwK+4blAjxGcPRrniJYBn7NWAlumjpUVCzoIpjcphpiCKTZlz\n" +
+ "m2W4iWGSPzemDmOzEEWERafu3O08yS5n9zl2wrdOjClNC4Pmlyy8PH8b42mgMr4e\n" +
+ "nP8CoTpzdFQbzSg/A3pfYgK+TLtVI9KZO4V/OIK6jppKUDg0ZA+GDUYC4mtjgHgQ\n" +
+ "Qaj0OiAHxti1bYA/VgoBLI3D/AW5JNJ0XGUXO++qwR5rNa6Sgs2DATvBw6mLbiVV\n" +
+ "pYBuDTnFRtXURm1pkD8Z+jSKz5eq7fEnO8GhnW+4ftPztXpucl85jtAHTqPFaiET\n" +
+ "jDwrdmHNqvMdu0KQfd+D1bU8KSf2v/9h7LS/fyfxDxYgX15O4crtQV1Obq6yLbbA\n" +
+ "G0YwRknIaPbq9qZx8iXue+kACgkQeb23hgfOVewMmgwAnJl4g0sX89VFz1OtMLJj\n" +
+ "Ui2QvPCpMkhsrgbaLS3q+wSZIUTZWzTzcZhDajNs3f/KjL2Dm5UxkHD29DuUv++r\n" +
+ "YPsVpWkk8wtD8/Am4CF1b5ibXboBrouAuju64pqrHjrM8/1WeZatYqkjShk5DqA2\n" +
+ "PlgpHFxoRB/0QnUwp6kpu2Tr3CTrcn0tyyqbcwTr5pw5oBLWcWgc8LMIFV2zdnHa\n" +
+ "bGvsew9puss1oh5Fs58XYg0Gdf/J/qelWgxbx/b4GHy5wxvb5BkbNMz7hWquZNsc\n" +
+ "DRuCOwRwlhCY13rTDUwwonU/PMPwP9I6pU5LBx+xRt3p8CeE4f00ANdxbS6JI5iA\n" +
+ "zqUsKIlUlwH+AO9VtRqiAJsVwaJVm/GOWJVeIKiz8M/jgiW0NCVJb01RW+3WVaY5\n" +
+ "kpLKqE1V0xq4mxw16mjBguUx3HdR5rh3ZZJ0TfXGGDAhLQC7PXe3oQyN1NbbH2Vx\n" +
+ "jVKueQsGPX2022pepiJXXtAzGIBR0eUOfylpewuerFwEx8TGBGYvuZUBDADjCLaD\n" +
+ "w3L65khVSjpsu8jr9B72xbx/EIlXEKr2KXa1lvf0yadxKB5/KytXWffQ8lEMmdi9\n" +
+ "p86+LIWIh7wx+mhh2g64um2yJiuS+HRTWWA69nb6/1Tl5G2VyT81AVQ5JAcNyIIS\n" +
+ "RuWvzZoQDNf0sImT0o7dAK4KLtofGMy/rIaNebE2Qu4dks5aBjIV2/bPoIMrSuJF\n" +
+ "UK5UsUOPx5jlYk5gpgyPcl30YgLf0Bizp4RJSCpIjjDJ6WvKBxlRChdfbP52vawI\n" +
+ "IEcMGnWMVFvVd8I57v6HDtbQTgF8BepwgsWHnTGtoIkVnKc/nlM3LtNiJUY3z915\n" +
+ "TZGRbYcuqWZhMbnJoQLRgQXh6/E4FzxDxaKoXpYXuPDxCTfNxeqU3hrRZUfKOdjw\n" +
+ "+BS5rSicvbGaqgyz29518bG04hzrmWORoJExozWTOoE+kTU5+o7DmS7qtd+z63lL\n" +
+ "bjqLhFALPl9qbwVlFlFo6X6jmlo8THVkX5lLI1+Qaq2g3G1YYCoXDMoGbk8AEQEA\n" +
+ "Af4JAwJgAx2GH4oiN2BJ7FHcEtvbKapzj3N2OwxYHmWymAAjgPe10Ne2W7FFi4Qy\n" +
+ "sj0Ss9NbWV5Nw4NqnE6syOFNVeLs5t7BdTqXs3NxOTJo0iS+lpL5OgUcMSWu2hN7\n" +
+ "jDbeXEBZFSQ7epetVDAetYsKLZBHpsI19aamUEnRZicKATjVQud9pHhC9BTFp62i\n" +
+ "D4a2IuxQcweuw5D8brKH9WfXYXlNzjoZdsqvWRWy77/6s2hg9V7lo65C/p8C0DB7\n" +
+ "blJwptt9j/vTlzTyavV60rRma3VeMgQw5sn0b2lqWvmLRgpjmCv2AdD9A6rYU/4+\n" +
+ "f+sknWq5c/gaoWAMWNg+vgRpZUT9C5ZlT86QUuz0DO7ySoy0gy9Z3BID+JDcXP/b\n" +
+ "BYftC7XQut+nnWGD+Pr2E0YvrTQLw0ISCzKyCI9iZh0gvwv9bKdYOUSEhOM9zlk+\n" +
+ "gt3hFJvVXvLbFUHEbh0Oep0AcCzFKzBrTYBeJ8Z8vvgNfie9zMtY3EAV4tBU2MVB\n" +
+ "3JqgRJ9Qam/ZGJ47GIbkRnqrbCmL44U3Qvl9to4g96gmrQXfdJUAtpntewuuDguJ\n" +
+ "MgKYUTv9TupYErHoTFlV61czXEwITE6y3TuePgWp4sY+BXGWyFc4puS+KNB+y+GC\n" +
+ "hJdchAyJcVhsV2e7ElC2URVmGpDkW23qcRMFlu7QMaENI5itKEinKIPQokITO0l+\n" +
+ "I13oJ4KlMEgfofNQ4rWdoqir7AqaQ+HXTV0l8iQQJPuAwXYnSe4xjuHuBssJ92Qk\n" +
+ "B6H+7IGDvXwMikQzOkeZVrsL0f1Pg1DIPMgt5l4qleZ4aL0cDqBQHh3JLtXWQ4jp\n" +
+ "ffYWxxCXILO10WYbqAaG4eXr/vsCb/TfADiF07azQMgWrhk3NSSoVRRjQgIntCAR\n" +
+ "u9C2x6FcyeF4ND0eIciWH6+pby0xC9bg5XlKlgeMN/BoXnj/k34ZgoHbe6NmjhT2\n" +
+ "bpgXrQQl4QPBS67jr2lU6NunmOwoHwX+epwIIKW8bcjvOTs0XEGVCJleIyUf88m6\n" +
+ "bpV4WUmIk4I8ROztJRzxZpNB8HgZb9XzbOcXccUl8sjTOyMlQTIAl/qUIomJ8snH\n" +
+ "lzEhoWYWzWUrEe42CVld++xhLQkgR/V484ch+vDi9EjmKCRVWVdOnHda9fHe5o8n\n" +
+ "TQzMdG8Fvy2XrFQAeCdNkD+itwV1OIva9j9KQHadS9MVl5PYzy/ezwIrK9siXAXu\n" +
+ "4YiaSTL4TPwjWppQCJJv8mQskNP72tc8TELA225MtsPcSPiCT1aLUUKQmpQstEeQ\n" +
+ "S6Cv+uggbDXqWfow2mx5w4bJXLxpe09vm5rqBT6Scg2e4e3yRNiYGFXX2QsNVRui\n" +
+ "nLCJLAUtpUJXBcLA9gQYAQoAIAUCZi+5lwKbDBYhBLSFPwVS++7sNFWR0nm9t4YH\n" +
+ "zlXsAAoJEHm9t4YHzlXsaFsL/16ktY2/knugZ8bN1df/QzdDE30wWakcDqAZhEMb\n" +
+ "+MyazHM08ipXFkvNsz0r7Y6DXqvOTvRlzXc7csk3Np/rrFFwpkckHXz1JkrQwAtD\n" +
+ "rIMcmzqm25u7rKti0NfsacQI1mie+wFyrApvXTBF2av9Fn1ch07A4f6JTfD62KAo\n" +
+ "ccBKAr38LVBwwGJZh6WqOazgoO8B4ia1MveHgOCsf3SurigXt1iMCCqWvvpQUil9\n" +
+ "3hU8x1SNy0TajFwXSeAMTAyoWVlC7ceixVr9dPLgRuMbsfHYsBAMw9wHSSNVyqvl\n" +
+ "vhB4X/j3bIFhl3iqj1P7Km33yVbk30KtKHuPFpHMJBu8CZ4/JcPnfGK35aTgfV9N\n" +
+ "W9V5u+mtWKReL8Ii0/jQ53PGJ7I1m8uzLB83mmRYY2hoqxdzWTXB57oDJbPwZRSx\n" +
+ "5puZWZ4WbmsHSaPe0gMIQH3ItcnWuB2sxhkpXSnOtXIK44lqcQwq69ygHEP11W85\n" +
+ "3hRZb5W+1RCWcuPc/oWxMuwiBw==\n" +
+ "=7IAh\n" +
+ "-----END PGP PRIVATE KEY BLOCK-----\n";
+
+ ByteArrayInputStream bIn = new ByteArrayInputStream(encodedCert.getBytes());
+ ArmoredInputStream aIn = new ArmoredInputStream(bIn);
+ BCPGInputStream pIn = new BCPGInputStream(aIn);
+ PGPObjectFactory objectFactory = new BcPGPObjectFactory(pIn);
+ PGPSecretKeyRing secretKeys = (PGPSecretKeyRing) objectFactory.nextObject();
+
+ // ROUNDTRIP
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ArmoredOutputStream aOut = new ArmoredOutputStream(bOut);
+ BCPGOutputStream pOut = new BCPGOutputStream(aOut, PacketFormat.ROUNDTRIP);
+ secretKeys.encode(pOut);
+ pOut.close();
+ aOut.close();
+
+ isEquals(encodedCert, bOut.toString());
+
+ // NEW PACKET FORMAT
+ bOut = new ByteArrayOutputStream();
+ aOut = new ArmoredOutputStream(bOut);
+ pOut = new BCPGOutputStream(aOut, PacketFormat.CURRENT);
+ secretKeys.encode(pOut);
+ pOut.close();
+ aOut.close();
+
+ bIn = new ByteArrayInputStream(bOut.toByteArray());
+ aIn = new ArmoredInputStream(bIn);
+ pIn = new BCPGInputStream(aIn);
+ Packet packet;
+ while ((packet = pIn.readPacket()) != null) {
+ isTrue(packet.hasNewPacketFormat());
+ }
+
+ // OLD PACKET FORMAT
+ bOut = new ByteArrayOutputStream();
+ aOut = new ArmoredOutputStream(bOut);
+ pOut = new BCPGOutputStream(aOut, PacketFormat.LEGACY);
+ secretKeys.encode(pOut);
+ pOut.close();
+ aOut.close();
+
+ bIn = new ByteArrayInputStream(bOut.toByteArray());
+ aIn = new ArmoredInputStream(bIn);
+ pIn = new BCPGInputStream(aIn);
+ while ((packet = pIn.readPacket()) != null) {
+ isTrue(!packet.hasNewPacketFormat());
+ }
+ }
+
+ @Override
+ public String getName() {
+ return "BCPGOutputStreamTest";
+ }
+
+ @Override
+ public void performTest() throws Exception {
+ testForceOldPacketFormat();
+ testForceNewPacketFormat();
+ testRoundTripPacketFormat();
+ testRoundtripMixedPacketFormats();
+ }
+
+ public static void main(String[] args) {
+ runTest(new BCPGOutputStreamTest());
+ }
+}
From 1bca359fccebda29d604e45fae93cb646ef4ada9 Mon Sep 17 00:00:00 2001
From: Paul Schaub
Date: Wed, 22 May 2024 12:55:16 +0200
Subject: [PATCH 0093/1644] Fix OnePassSignaturePacketTest by only comparing
encoding if format matches
---
.../bcpg/test/OnePassSignaturePacketTest.java | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/pg/src/test/java/org/bouncycastle/bcpg/test/OnePassSignaturePacketTest.java b/pg/src/test/java/org/bouncycastle/bcpg/test/OnePassSignaturePacketTest.java
index 9f26357554..69703195ae 100644
--- a/pg/src/test/java/org/bouncycastle/bcpg/test/OnePassSignaturePacketTest.java
+++ b/pg/src/test/java/org/bouncycastle/bcpg/test/OnePassSignaturePacketTest.java
@@ -105,8 +105,10 @@ private void roundtripV3Packet()
isNull("round-tripped OPS v3 MUST NOT have salt",
after.getSalt());
- isEncodingEqual("Packet encoding mismatch",
- before, after);
+ if (before.hasNewPacketFormat() && newTypeIdFormat)
+ {
+ isEncodingEqual(before, after);
+ }
}
}
@@ -178,7 +180,10 @@ private void roundtripV6Packet()
isEncodingEqual("round-tripped OPS salt mismatch",
before.getSalt(), after.getSalt());
- isEncodingEqual(before, after);
+ if (before.hasNewPacketFormat() && newTypeIdFormat)
+ {
+ isEncodingEqual(before, after);
+ }
}
}
From 95b574ee4052ccb333d7b421b59d361cd56bae51 Mon Sep 17 00:00:00 2001
From: gefeili
Date: Thu, 23 May 2024 12:26:01 +0930
Subject: [PATCH 0094/1644] move the javadoc to the right place caused by merge
---
.../org/bouncycastle/bcpg/BCPGOutputStream.java | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/BCPGOutputStream.java b/pg/src/main/java/org/bouncycastle/bcpg/BCPGOutputStream.java
index 16cafb7ed8..965e04487e 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/BCPGOutputStream.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/BCPGOutputStream.java
@@ -339,14 +339,6 @@ public void writePacket(
p.encode(this);
}
- /**
- * Write a packet to the stream.
- * The packet will use the old encoding format if {@link #packetFormat} is {@link PacketFormat#LEGACY}, otherwise
- * it will be encoded using the new packet format.
- * @param tag packet tag
- * @param body packet body
- * @throws IOException
- */
void writeShort(short n)
throws IOException
{
@@ -363,6 +355,14 @@ void writeInt(int n)
out.write(n);
}
+ /**
+ * Write a packet to the stream.
+ * The packet will use the old encoding format if {@link #packetFormat} is {@link PacketFormat#LEGACY}, otherwise
+ * it will be encoded using the new packet format.
+ * @param tag packet tag
+ * @param body packet body
+ * @throws IOException
+ */
void writePacket(
int tag,
byte[] body)
From 7a4fc943f5a5368422f1a3b51fbdd76d612ef7ea Mon Sep 17 00:00:00 2001
From: gefeili
Date: Thu, 23 May 2024 13:31:43 +0930
Subject: [PATCH 0095/1644] refactor around streams
---
.../bouncycastle/bcpg/BCPGOutputStream.java | 19 +-
.../java/org/bouncycastle/bcpg/MPInteger.java | 6 +-
.../bouncycastle/bcpg/PublicKeyPacket.java | 6 +-
.../bouncycastle/bcpg/SignaturePacket.java | 427 +++++++++---------
.../org/bouncycastle/bcpg/StreamUtil.java | 2 +-
5 files changed, 211 insertions(+), 249 deletions(-)
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/BCPGOutputStream.java b/pg/src/main/java/org/bouncycastle/bcpg/BCPGOutputStream.java
index 965e04487e..d4432920d0 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/BCPGOutputStream.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/BCPGOutputStream.java
@@ -206,8 +206,7 @@ private void writeHeader(
else if (bodyLen <= 0xffff)
{
this.write(hdr | 0x01);
- this.write((byte)(bodyLen >> 8));
- this.write((byte)(bodyLen));
+ StreamUtil.write2OctetLength(this, (int)bodyLen);
}
else
{
@@ -339,22 +338,6 @@ public void writePacket(
p.encode(this);
}
- void writeShort(short n)
- throws IOException
- {
- out.write((byte)(n >> 8));
- out.write((byte)n);
- }
-
- void writeInt(int n)
- throws IOException
- {
- out.write(n >> 24);
- out.write(n >> 16);
- out.write(n >> 8);
- out.write(n);
- }
-
/**
* Write a packet to the stream.
* The packet will use the old encoding format if {@link #packetFormat} is {@link PacketFormat#LEGACY}, otherwise
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/MPInteger.java b/pg/src/main/java/org/bouncycastle/bcpg/MPInteger.java
index 74563178d6..286ab2ab34 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/MPInteger.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/MPInteger.java
@@ -15,7 +15,7 @@ public MPInteger(
BCPGInputStream in)
throws IOException
{
- int length = (in.read() << 8) | in.read();
+ int length = StreamUtil.read2OctetLength(in);
byte[] bytes = new byte[(length + 7) / 8];
in.readFully(bytes);
@@ -43,8 +43,8 @@ public void encode(
BCPGOutputStream out)
throws IOException
{
- out.writeShort((short)value.bitLength());
-
+ StreamUtil.write2OctetLength(out, value.bitLength());
+
byte[] bytes = value.toByteArray();
if (bytes[0] == 0)
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyPacket.java
index 8a3c721b76..7332c33662 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyPacket.java
@@ -56,7 +56,7 @@ public class PublicKeyPacket
if (version <= VERSION_3)
{
- validDays = (in.read() << 8) | in.read();
+ validDays = StreamUtil.read2OctetLength(in);
}
algorithm = (byte)in.read();
@@ -178,14 +178,14 @@ public byte[] getEncodedContents()
if (version <= VERSION_3)
{
- pOut.writeShort((short)validDays);
+ StreamUtil.write2OctetLength(pOut, validDays);
}
pOut.write(algorithm);
if (version == VERSION_6)
{
- pOut.writeInt(key.getEncoded().length);
+ StreamUtil.write4OctetLength(pOut, key.getEncoded().length);
}
pOut.writeObject((BCPGObject)key);
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java b/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java
index 680580ebb5..f24ebb2114 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java
@@ -15,7 +15,8 @@
* generic signature packet
*/
public class SignaturePacket
- extends ContainedPacket implements PublicKeyAlgorithmTags
+ extends ContainedPacket
+ implements PublicKeyAlgorithmTags
{
public static final int VERSION_2 = 2;
public static final int VERSION_3 = 3;
@@ -23,28 +24,28 @@ public class SignaturePacket
public static final int VERSION_5 = 5; // https://datatracker.ietf.org/doc/draft-koch-librepgp/
public static final int VERSION_6 = 6; // https://datatracker.ietf.org/doc/draft-ietf-openpgp-crypto-refresh/
- private int version;
- private int signatureType;
- private long creationTime;
- private long keyID;
- private int keyAlgorithm;
- private int hashAlgorithm;
- private MPInteger[] signature;
- private byte[] fingerPrint;
- private SignatureSubpacket[] hashedData;
- private SignatureSubpacket[] unhashedData;
- private byte[] signatureEncoding;
- private byte[] salt; // v6 only
+ private int version;
+ private int signatureType;
+ private long creationTime;
+ private long keyID;
+ private int keyAlgorithm;
+ private int hashAlgorithm;
+ private MPInteger[] signature;
+ private byte[] fingerPrint;
+ private SignatureSubpacket[] hashedData;
+ private SignatureSubpacket[] unhashedData;
+ private byte[] signatureEncoding;
+ private byte[] salt; // v6 only
SignaturePacket(
- BCPGInputStream in)
- throws IOException
+ BCPGInputStream in)
+ throws IOException
{
this(in, false);
}
SignaturePacket(
- BCPGInputStream in,
+ BCPGInputStream in,
boolean newPacketFormat)
throws IOException
{
@@ -53,35 +54,35 @@ public class SignaturePacket
version = in.read();
switch (version)
{
- case VERSION_2:
- case VERSION_3:
- parseV2_V3(in);
- break;
- case VERSION_4:
- case VERSION_5:
- parseV4_V5(in);
- break;
- case VERSION_6:
- parseV6(in);
- break;
- default:
- Streams.drain(in);
- throw new UnsupportedPacketVersionException("unsupported version: " + version);
+ case VERSION_2:
+ case VERSION_3:
+ parseV2_V3(in);
+ break;
+ case VERSION_4:
+ case VERSION_5:
+ parseV4_V5(in);
+ break;
+ case VERSION_6:
+ parseV6(in);
+ break;
+ default:
+ Streams.drain(in);
+ throw new UnsupportedPacketVersionException("unsupported version: " + version);
}
}
/**
* Parse a version 2 or version 3 signature.
+ *
* @param in input stream which already skipped over the version number
* @throws IOException if the packet is malformed
- *
* @see
- * Version 3 packet format
+ * Version 3 packet format
*/
private void parseV2_V3(BCPGInputStream in)
throws IOException
{
- int l = in.read(); // length l MUST be 5
+ int l = in.read(); // length l MUST be 5
signatureType = in.read();
creationTime = StreamUtil.readTime(in);
@@ -100,16 +101,16 @@ private void parseV2_V3(BCPGInputStream in)
/**
* Parse a version 4 or version 5 signature.
* The difference between version 4 and 5 is that a version 5 signature contains additional metadata.
+ *
* @param in input stream which already skipped over the version number
* @throws IOException if the packet is malformed
- *
* @see
- * Version 4 packet format
+ * Version 4 packet format
* @see
- * Version 5 packet format
+ * Version 5 packet format
*/
private void parseV4_V5(BCPGInputStream in)
- throws IOException
+ throws IOException
{
signatureType = in.read();
keyAlgorithm = in.read();
@@ -128,14 +129,14 @@ private void parseV4_V5(BCPGInputStream in)
* Parse a version 6 signature.
* Version 6 signatures do use 4 octet subpacket area length descriptors and contain an additional salt value
* (which may or may not be of size 0, librepgp and crypto-refresh are in disagreement here).
+ *
* @param in input stream which already skipped over the version number
* @throws IOException if the packet is malformed
- *
* @see
- * Version 6 packet format
+ * Version 6 packet format
*/
private void parseV6(BCPGInputStream in)
- throws IOException
+ throws IOException
{
signatureType = in.read();
keyAlgorithm = in.read();
@@ -162,39 +163,15 @@ private void parseV6(BCPGInputStream in)
* @throws IOException if the packet is malformed
*/
private void parseSubpackets(BCPGInputStream in)
- throws IOException
+ throws IOException
{
- int hashedLength;
- if (version == 6)
- {
- hashedLength = StreamUtil.read4OctetLength(in);
- }
- else
- {
- hashedLength = StreamUtil.read2OctetLength(in);
- }
- byte[] hashed = new byte[hashedLength];
-
- in.readFully(hashed);
-
- //
- // read the signature sub packet data.
- //
- SignatureSubpacket sub;
- SignatureSubpacketInputStream sIn = new SignatureSubpacketInputStream(
- new ByteArrayInputStream(hashed));
-
- Vector vec = new Vector();
- while ((sub = sIn.readPacket()) != null)
- {
- vec.addElement(sub);
- }
+ Vector vec = readSignatureSubpacketVector(in);
hashedData = new SignatureSubpacket[vec.size()];
for (int i = 0; i != hashedData.length; i++)
{
- SignatureSubpacket p = vec.elementAt(i);
+ SignatureSubpacket p = vec.elementAt(i);
if (p instanceof IssuerKeyID)
{
keyID = ((IssuerKeyID)p).getKeyID();
@@ -207,40 +184,50 @@ else if (p instanceof SignatureCreationTime)
hashedData[i] = p;
}
- int unhashedLength;
- if (version == VERSION_6)
+ vec = readSignatureSubpacketVector(in);
+ unhashedData = new SignatureSubpacket[vec.size()];
+
+ for (int i = 0; i != unhashedData.length; i++)
+ {
+ SignatureSubpacket p = vec.elementAt(i);
+ if (p instanceof IssuerKeyID)
+ {
+ keyID = ((IssuerKeyID)p).getKeyID();
+ }
+
+ unhashedData[i] = p;
+ }
+ }
+
+ private Vector readSignatureSubpacketVector(BCPGInputStream in)
+ throws IOException
+ {
+ int hashedLength;
+ if (version == 6)
{
- unhashedLength = StreamUtil.read4OctetLength(in);
+ hashedLength = StreamUtil.read4OctetLength(in);
}
else
{
- unhashedLength = StreamUtil.read2OctetLength(in);
+ hashedLength = StreamUtil.read2OctetLength(in);
}
- byte[] unhashed = new byte[unhashedLength];
+ byte[] hashed = new byte[hashedLength];
- in.readFully(unhashed);
+ in.readFully(hashed);
- sIn = new SignatureSubpacketInputStream(
- new ByteArrayInputStream(unhashed));
+ //
+ // read the signature sub packet data.
+ //
+ SignatureSubpacket sub;
+ SignatureSubpacketInputStream sIn = new SignatureSubpacketInputStream(
+ new ByteArrayInputStream(hashed));
- vec.removeAllElements();
+ Vector vec = new Vector();
while ((sub = sIn.readPacket()) != null)
{
vec.addElement(sub);
}
-
- unhashedData = new SignatureSubpacket[vec.size()];
-
- for (int i = 0; i != unhashedData.length; i++)
- {
- SignatureSubpacket p = vec.elementAt(i);
- if (p instanceof IssuerKeyID)
- {
- keyID = ((IssuerKeyID)p).getKeyID();
- }
-
- unhashedData[i] = p;
- }
+ return vec;
}
/**
@@ -252,64 +239,64 @@ else if (p instanceof SignatureCreationTime)
* @throws IOException if the packet is malformed
*/
private void parseSignature(BCPGInputStream in)
- throws IOException
+ throws IOException
{
switch (keyAlgorithm)
{
- case RSA_GENERAL:
- case RSA_SIGN:
- MPInteger v = new MPInteger(in);
-
- signature = new MPInteger[1];
- signature[0] = v;
- break;
- case DSA:
- MPInteger r = new MPInteger(in);
- MPInteger s = new MPInteger(in);
-
- signature = new MPInteger[2];
- signature[0] = r;
- signature[1] = s;
- break;
- case ELGAMAL_ENCRYPT: // yep, this really does happen sometimes.
- case ELGAMAL_GENERAL:
- MPInteger p = new MPInteger(in);
- MPInteger g = new MPInteger(in);
- MPInteger y = new MPInteger(in);
-
- signature = new MPInteger[3];
- signature[0] = p;
- signature[1] = g;
- signature[2] = y;
- break;
- case Ed448:
- signatureEncoding = new byte[org.bouncycastle.math.ec.rfc8032.Ed448.SIGNATURE_SIZE];
- in.readFully(signatureEncoding);
- break;
- case Ed25519:
- signatureEncoding = new byte[org.bouncycastle.math.ec.rfc8032.Ed25519.SIGNATURE_SIZE];
- in.readFully(signatureEncoding);
- break;
- case ECDSA:
- case EDDSA_LEGACY:
-
- MPInteger ecR = new MPInteger(in);
- MPInteger ecS = new MPInteger(in);
-
- signature = new MPInteger[2];
- signature[0] = ecR;
- signature[1] = ecS;
- break;
- default:
- if (keyAlgorithm >= PublicKeyAlgorithmTags.EXPERIMENTAL_1 && keyAlgorithm <= PublicKeyAlgorithmTags.EXPERIMENTAL_11)
- {
- signature = null;
- signatureEncoding = Streams.readAll(in);
- }
- else
- {
- throw new IOException("unknown signature key algorithm: " + keyAlgorithm);
- }
+ case RSA_GENERAL:
+ case RSA_SIGN:
+ MPInteger v = new MPInteger(in);
+
+ signature = new MPInteger[1];
+ signature[0] = v;
+ break;
+ case DSA:
+ MPInteger r = new MPInteger(in);
+ MPInteger s = new MPInteger(in);
+
+ signature = new MPInteger[2];
+ signature[0] = r;
+ signature[1] = s;
+ break;
+ case ELGAMAL_ENCRYPT: // yep, this really does happen sometimes.
+ case ELGAMAL_GENERAL:
+ MPInteger p = new MPInteger(in);
+ MPInteger g = new MPInteger(in);
+ MPInteger y = new MPInteger(in);
+
+ signature = new MPInteger[3];
+ signature[0] = p;
+ signature[1] = g;
+ signature[2] = y;
+ break;
+ case Ed448:
+ signatureEncoding = new byte[org.bouncycastle.math.ec.rfc8032.Ed448.SIGNATURE_SIZE];
+ in.readFully(signatureEncoding);
+ break;
+ case Ed25519:
+ signatureEncoding = new byte[org.bouncycastle.math.ec.rfc8032.Ed25519.SIGNATURE_SIZE];
+ in.readFully(signatureEncoding);
+ break;
+ case ECDSA:
+ case EDDSA_LEGACY:
+
+ MPInteger ecR = new MPInteger(in);
+ MPInteger ecS = new MPInteger(in);
+
+ signature = new MPInteger[2];
+ signature[0] = ecR;
+ signature[1] = ecS;
+ break;
+ default:
+ if (keyAlgorithm >= PublicKeyAlgorithmTags.EXPERIMENTAL_1 && keyAlgorithm <= PublicKeyAlgorithmTags.EXPERIMENTAL_11)
+ {
+ signature = null;
+ signatureEncoding = Streams.readAll(in);
+ }
+ else
+ {
+ throw new IOException("unknown signature key algorithm: " + keyAlgorithm);
+ }
}
}
@@ -325,14 +312,14 @@ private void parseSignature(BCPGInputStream in)
* @param signature
*/
public SignaturePacket(
- int signatureType,
- long keyID,
- int keyAlgorithm,
- int hashAlgorithm,
- SignatureSubpacket[] hashedData,
- SignatureSubpacket[] unhashedData,
- byte[] fingerPrint,
- MPInteger[] signature)
+ int signatureType,
+ long keyID,
+ int keyAlgorithm,
+ int hashAlgorithm,
+ SignatureSubpacket[] hashedData,
+ SignatureSubpacket[] unhashedData,
+ byte[] fingerPrint,
+ MPInteger[] signature)
{
this(4, signatureType, keyID, keyAlgorithm, hashAlgorithm, hashedData, unhashedData, fingerPrint, signature);
}
@@ -347,14 +334,14 @@ public SignaturePacket(
* @param signature
*/
public SignaturePacket(
- int version,
- int signatureType,
- long keyID,
- int keyAlgorithm,
- int hashAlgorithm,
- long creationTime,
- byte[] fingerPrint,
- MPInteger[] signature)
+ int version,
+ int signatureType,
+ long keyID,
+ int keyAlgorithm,
+ int hashAlgorithm,
+ long creationTime,
+ byte[] fingerPrint,
+ MPInteger[] signature)
{
this(version, signatureType, keyID, keyAlgorithm, hashAlgorithm, null, null, fingerPrint, signature);
@@ -362,15 +349,15 @@ public SignaturePacket(
}
public SignaturePacket(
- int version,
- int signatureType,
- long keyID,
- int keyAlgorithm,
- int hashAlgorithm,
- SignatureSubpacket[] hashedData,
- SignatureSubpacket[] unhashedData,
- byte[] fingerPrint,
- MPInteger[] signature)
+ int version,
+ int signatureType,
+ long keyID,
+ int keyAlgorithm,
+ int hashAlgorithm,
+ SignatureSubpacket[] hashedData,
+ SignatureSubpacket[] unhashedData,
+ byte[] fingerPrint,
+ MPInteger[] signature)
{
super(SIGNATURE);
@@ -391,15 +378,15 @@ public SignaturePacket(
}
public SignaturePacket(
- int version,
- int signatureType,
- long keyID,
- int keyAlgorithm,
- int hashAlgorithm,
- SignatureSubpacket[] hashedData,
- SignatureSubpacket[] unhashedData,
- byte[] fingerPrint,
- byte[] signatureEncoding)
+ int version,
+ int signatureType,
+ long keyID,
+ int keyAlgorithm,
+ int hashAlgorithm,
+ SignatureSubpacket[] hashedData,
+ SignatureSubpacket[] unhashedData,
+ byte[] fingerPrint,
+ byte[] signatureEncoding)
{
super(SIGNATURE);
@@ -436,6 +423,7 @@ public int getSignatureType()
/**
* return the keyID
+ *
* @return the keyID that created the signature.
*/
public long getKeyID()
@@ -445,6 +433,7 @@ public long getKeyID()
/**
* Return the signature's fingerprint.
+ *
* @return fingerprint (digest prefix) of the signature
*/
public byte[] getFingerPrint()
@@ -455,6 +444,7 @@ public byte[] getFingerPrint()
/**
* Return the signature's salt.
* Only for v6 signatures.
+ *
* @return salt
*/
public byte[] getSalt()
@@ -476,15 +466,15 @@ public byte[] getSignatureTrailer()
{
trailer = new byte[5];
- long time = creationTime / 1000;
+ long time = creationTime / 1000;
trailer[0] = (byte)signatureType;
Pack.intToBigEndian((int)time, trailer, 1);
}
else
{
- ByteArrayOutputStream sOut = new ByteArrayOutputStream();
- SignatureSubpacket[] hashed = this.getHashedSubPackets();
+ ByteArrayOutputStream sOut = new ByteArrayOutputStream();
+ SignatureSubpacket[] hashed = this.getHashedSubPackets();
try
{
sOut.write((byte)this.getVersion());
@@ -492,7 +482,7 @@ public byte[] getSignatureTrailer()
sOut.write((byte)this.getKeyAlgorithm());
sOut.write((byte)this.getHashAlgorithm());
- ByteArrayOutputStream hOut = new ByteArrayOutputStream();
+ ByteArrayOutputStream hOut = new ByteArrayOutputStream();
for (int i = 0; i != hashed.length; i++)
@@ -500,11 +490,11 @@ public byte[] getSignatureTrailer()
hashed[i].encode(hOut);
}
- byte[] data = hOut.toByteArray();
+ byte[] data = hOut.toByteArray();
StreamUtil.write2OctetLength(sOut, data.length);
sOut.write(data);
- byte[] hData = sOut.toByteArray();
+ byte[] hData = sOut.toByteArray();
sOut.write((byte)this.getVersion());
sOut.write((byte)0xff);
@@ -550,6 +540,7 @@ public MPInteger[] getSignature()
/**
* Return the byte encoding of the signature section.
+ *
* @return uninterpreted signature bytes.
*/
public byte[] getSignatureBytes()
@@ -599,11 +590,11 @@ public long getCreationTime()
}
public void encode(
- BCPGOutputStream out)
+ BCPGOutputStream out)
throws IOException
{
- ByteArrayOutputStream bOut = new ByteArrayOutputStream();
- BCPGOutputStream pOut = new BCPGOutputStream(bOut);
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ BCPGOutputStream pOut = new BCPGOutputStream(bOut);
pOut.write(version);
@@ -611,7 +602,7 @@ public void encode(
{
pOut.write(5); // the length of the next block
- long time = creationTime / 1000;
+ long time = creationTime / 1000;
pOut.write(signatureType);
StreamUtil.writeTime(pOut, time);
@@ -627,43 +618,10 @@ else if (version == VERSION_4 || version == VERSION_5 || version == VERSION_6)
pOut.write(keyAlgorithm);
pOut.write(hashAlgorithm);
- ByteArrayOutputStream sOut = new ByteArrayOutputStream();
-
- for (int i = 0; i != hashedData.length; i++)
- {
- hashedData[i].encode(sOut);
- }
-
- byte[] data = sOut.toByteArray();
-
- if (version == VERSION_6)
- {
- StreamUtil.write4OctetLength(pOut, data.length);
- }
- else
- {
- StreamUtil.write2OctetLength(pOut, data.length);
- }
- pOut.write(data);
-
+ ByteArrayOutputStream sOut = new ByteArrayOutputStream();
+ writeSignatureSubpacketArray(sOut, pOut, hashedData);
sOut.reset();
-
- for (int i = 0; i != unhashedData.length; i++)
- {
- unhashedData[i].encode(sOut);
- }
-
- data = sOut.toByteArray();
-
- if (version == VERSION_6)
- {
- StreamUtil.write4OctetLength(pOut, data.length);
- }
- else
- {
- StreamUtil.write2OctetLength(pOut, data.length);
- }
- pOut.write(data);
+ writeSignatureSubpacketArray(sOut, pOut, unhashedData);
}
else
{
@@ -695,6 +653,27 @@ else if (version == VERSION_4 || version == VERSION_5 || version == VERSION_6)
out.writePacket(hasNewPacketFormat(), SIGNATURE, bOut.toByteArray());
}
+ private void writeSignatureSubpacketArray(ByteArrayOutputStream sOut, BCPGOutputStream pOut, SignatureSubpacket[] array)
+ throws IOException
+ {
+ for (int i = 0; i != array.length; i++)
+ {
+ array[i].encode(sOut);
+ }
+
+ byte[] data = sOut.toByteArray();
+
+ if (version == VERSION_6)
+ {
+ StreamUtil.write4OctetLength(pOut, data.length);
+ }
+ else
+ {
+ StreamUtil.write2OctetLength(pOut, data.length);
+ }
+ pOut.write(data);
+ }
+
private void setCreationTime()
{
for (int i = 0; i != hashedData.length; i++)
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/StreamUtil.java b/pg/src/main/java/org/bouncycastle/bcpg/StreamUtil.java
index 12acd23fec..8ca36dd974 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/StreamUtil.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/StreamUtil.java
@@ -111,7 +111,7 @@ static long readKeyID(BCPGInputStream in)
static void writeTime(BCPGOutputStream pOut, long time)
throws IOException
{
- pOut.writeInt((int)time);
+ StreamUtil.write4OctetLength(pOut, (int) time);
}
static long readTime(BCPGInputStream in)
From 33a678b453db5ef3c6583e6b74ed99ece6bef4b8 Mon Sep 17 00:00:00 2001
From: gefeili
Date: Thu, 23 May 2024 15:38:48 +0930
Subject: [PATCH 0096/1644] refactor PGPPublicKey.join
---
.../bouncycastle/openpgp/PGPPublicKey.java | 100 +++++++-----------
1 file changed, 36 insertions(+), 64 deletions(-)
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKey.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKey.java
index b0a880dc74..f6d20df72f 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKey.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKey.java
@@ -1137,28 +1137,7 @@ public static PGPPublicKey join(
}
// key signatures
- for (Iterator it = copy.keySigs.iterator(); it.hasNext(); )
- {
- PGPSignature keySig = (PGPSignature)it.next();
- boolean found = false;
- for (int i = 0; i < keySigs.size(); i++)
- {
- PGPSignature existingKeySig = (PGPSignature)keySigs.get(i);
- if (PGPSignature.isSignatureEncodingEqual(existingKeySig, keySig))
- {
- found = true;
- // join existing sig with copy to apply modifications in unhashed subpackets
- existingKeySig = PGPSignature.join(existingKeySig, keySig);
- keySigs.set(i, existingKeySig);
- break;
- }
- }
- if (found)
- {
- break;
- }
- keySigs.add(keySig);
- }
+ joinPgpSignatureList(copy.keySigs, keySigs, true, true);
// user-ids and id sigs
for (int idIdx = 0; idIdx < copy.ids.size(); idIdx++)
@@ -1198,27 +1177,7 @@ public static PGPPublicKey join(
}
List existingIdSigs = (List)idSigs.get(existingIdIndex);
- for (Iterator it = copyIdSigs.iterator(); it.hasNext(); )
- {
- PGPSignature newSig = (PGPSignature)it.next();
- boolean found = false;
- for (int i = 0; i < existingIdSigs.size(); i++)
- {
- PGPSignature existingSig = (PGPSignature)existingIdSigs.get(i);
- if (PGPSignature.isSignatureEncodingEqual(newSig, existingSig))
- {
- found = true;
- // join existing sig with copy to apply modifications in unhashed subpackets
- existingSig = PGPSignature.join(existingSig, newSig);
- existingIdSigs.set(i, existingSig);
- break;
- }
- }
- if (!found)
- {
- existingIdSigs.add(newSig);
- }
- }
+ joinPgpSignatureList(copyIdSigs, existingIdSigs, false, true);
}
// subSigs
@@ -1230,27 +1189,7 @@ public static PGPPublicKey join(
}
else
{
- for (Iterator it = copy.subSigs.iterator(); it.hasNext(); )
- {
- PGPSignature copySubSig = (PGPSignature)it.next();
- boolean found = false;
- for (int i = 0; subSigs != null && i < subSigs.size(); i++)
- {
- PGPSignature existingSubSig = (PGPSignature)subSigs.get(i);
- if (PGPSignature.isSignatureEncodingEqual(existingSubSig, copySubSig))
- {
- found = true;
- // join existing sig with copy to apply modifications in unhashed subpackets
- existingSubSig = PGPSignature.join(existingSubSig, copySubSig);
- subSigs.set(i, existingSubSig);
- break;
- }
- }
- if (!found && subSigs != null)
- {
- subSigs.add(copySubSig);
- }
- }
+ joinPgpSignatureList(copy.subSigs, subSigs, false, subSigs != null);
}
}
@@ -1259,4 +1198,37 @@ public static PGPPublicKey join(
return merged;
}
+
+ private static void joinPgpSignatureList(List source,
+ List rlt,
+ boolean needBreak,
+ boolean isNotNull)
+ throws PGPException
+ {
+ for (Iterator it = source.iterator(); it.hasNext(); )
+ {
+ PGPSignature copySubSig = (PGPSignature)it.next();
+ boolean found = false;
+ for (int i = 0; isNotNull && i < rlt.size(); i++)
+ {
+ PGPSignature existingSubSig = (PGPSignature)rlt.get(i);
+ if (PGPSignature.isSignatureEncodingEqual(existingSubSig, copySubSig))
+ {
+ found = true;
+ // join existing sig with copy to apply modifications in unhashed subpackets
+ existingSubSig = PGPSignature.join(existingSubSig, copySubSig);
+ rlt.set(i, existingSubSig);
+ break;
+ }
+ }
+ if (found && needBreak)
+ {
+ break;
+ }
+ else if (!found && isNotNull)
+ {
+ rlt.add(copySubSig);
+ }
+ }
+ }
}
From 9cd5f989380b5792349f83e6df6a82f772e2e00a Mon Sep 17 00:00:00 2001
From: gefeili
Date: Thu, 23 May 2024 15:50:34 +0930
Subject: [PATCH 0097/1644] Add Util.encodePGPSignatures
---
.../org/bouncycastle/openpgp/PGPPublicKey.java | 15 +++------------
.../org/bouncycastle/openpgp/PGPSecretKey.java | 16 +++-------------
.../main/java/org/bouncycastle/openpgp/Util.java | 11 +++++++++++
3 files changed, 17 insertions(+), 25 deletions(-)
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKey.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKey.java
index f6d20df72f..2c788db3df 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKey.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKey.java
@@ -737,10 +737,7 @@ public void encode(OutputStream outStream, boolean forTransfer)
if (subSigs == null) // not a sub-key
{
- for (int i = 0; i != keySigs.size(); i++)
- {
- ((PGPSignature)keySigs.get(i)).encode(out);
- }
+ Util.encodePGPSignatures(out, keySigs, false);
for (int i = 0; i != ids.size(); i++)
{
@@ -763,18 +760,12 @@ public void encode(OutputStream outStream, boolean forTransfer)
}
List sigs = (List)idSigs.get(i);
- for (int j = 0; j != sigs.size(); j++)
- {
- ((PGPSignature)sigs.get(j)).encode(out, forTransfer);
- }
+ Util.encodePGPSignatures(out, sigs, forTransfer);
}
}
else
{
- for (int j = 0; j != subSigs.size(); j++)
- {
- ((PGPSignature)subSigs.get(j)).encode(out, forTransfer);
- }
+ Util.encodePGPSignatures(out, subSigs, forTransfer);
}
}
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java
index fecddf9702..96fc361663 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java
@@ -752,10 +752,7 @@ public void encode(OutputStream outStream)
if (pub.subSigs == null) // is not a sub key
{
- for (int i = 0; i != pub.keySigs.size(); i++)
- {
- ((PGPSignature)pub.keySigs.get(i)).encode(out);
- }
+ Util.encodePGPSignatures(out, pub.keySigs, false);
for (int i = 0; i != pub.ids.size(); i++)
{
@@ -778,19 +775,12 @@ public void encode(OutputStream outStream)
}
List sigs = (List)pub.idSigs.get(i);
-
- for (int j = 0; j != sigs.size(); j++)
- {
- ((PGPSignature)sigs.get(j)).encode(out);
- }
+ Util.encodePGPSignatures(out, sigs, false);
}
}
else
{
- for (int j = 0; j != pub.subSigs.size(); j++)
- {
- ((PGPSignature)pub.subSigs.get(j)).encode(out);
- }
+ Util.encodePGPSignatures(out, pub.subSigs, false);
}
}
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/Util.java b/pg/src/main/java/org/bouncycastle/openpgp/Util.java
index 76242fa45b..af4a6c68e9 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/Util.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/Util.java
@@ -2,6 +2,8 @@
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
import org.bouncycastle.bcpg.BCPGInputStream;
@@ -32,4 +34,13 @@ static BCPGInputStream createBCPGInputStream(InputStream pgIn, int tag1, int tag
throw new IOException("unexpected tag " + bcIn.nextPacketTag() + " encountered");
}
+
+ static void encodePGPSignatures(OutputStream stream, List sigs, boolean forTransfer)
+ throws IOException
+ {
+ for (int i = 0; i != sigs.size(); i++)
+ {
+ ((PGPSignature)sigs.get(i)).encode(stream, forTransfer);
+ }
+ }
}
From 8e6038e16dab7cb28f0053ffb5f335c962415268 Mon Sep 17 00:00:00 2001
From: Paul Schaub
Date: Mon, 13 May 2024 15:35:26 +0200
Subject: [PATCH 0098/1644] Add test for EC curve key conversion
---
.../openpgp/test/AbstractPgpKeyPairTest.java | 3 +-
.../openpgp/test/ECDSAKeyPairTest.java | 256 ++++++++++++++++++
2 files changed, 258 insertions(+), 1 deletion(-)
create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/test/ECDSAKeyPairTest.java
diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/AbstractPgpKeyPairTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/AbstractPgpKeyPairTest.java
index 507c85c153..b4060b2584 100644
--- a/pg/src/test/java/org/bouncycastle/openpgp/test/AbstractPgpKeyPairTest.java
+++ b/pg/src/test/java/org/bouncycastle/openpgp/test/AbstractPgpKeyPairTest.java
@@ -2,6 +2,7 @@
import org.bouncycastle.bcpg.test.AbstractPacketTest;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.operator.bc.BcPGPKeyConverter;
import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPair;
@@ -35,7 +36,7 @@ public BcPGPKeyPair toBcKeyPair(JcaPGPKeyPair keyPair)
public JcaPGPKeyPair toJcaKeyPair(BcPGPKeyPair keyPair)
throws PGPException
{
- JcaPGPKeyConverter c = new JcaPGPKeyConverter();
+ JcaPGPKeyConverter c = new JcaPGPKeyConverter().setProvider(new BouncyCastleProvider());
return new JcaPGPKeyPair(keyPair.getPublicKey().getAlgorithm(),
new KeyPair(
c.getPublicKey(keyPair.getPublicKey()),
diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/ECDSAKeyPairTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/ECDSAKeyPairTest.java
new file mode 100644
index 0000000000..4a7a725064
--- /dev/null
+++ b/pg/src/test/java/org/bouncycastle/openpgp/test/ECDSAKeyPairTest.java
@@ -0,0 +1,256 @@
+package org.bouncycastle.openpgp.test;
+
+import org.bouncycastle.bcpg.*;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec;
+import org.bouncycastle.openpgp.*;
+import org.bouncycastle.openpgp.bc.BcPGPSecretKeyRing;
+import org.bouncycastle.openpgp.jcajce.JcaPGPSecretKeyRing;
+import org.bouncycastle.openpgp.operator.bc.BcPGPKeyConverter;
+import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPair;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyConverter;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.*;
+import java.util.Date;
+
+public class ECDSAKeyPairTest extends AbstractPgpKeyPairTest
+{
+
+ private static final String PRIME256v1 = "" +
+ "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
+ "Version: BCPG v@RELEASE_NAME@\n" +
+ "\n" +
+ "lHcEZkH7VRMIKoZIzj0DAQcCAwQee5wkHVVrG7u7CcrHoZOaC+reK0wn2Y5XPJoU\n" +
+ "O6geh1j2qXHj4+f+a6lav5hzKIJZHkgBYcS0aeABgWNjKsHbAAD/b4K93MJF7c2l\n" +
+ "4Y7ojBqTuZAOOD0Dyqe8MTXXyDUWN/0R/w==\n" +
+ "=mPB9\n" +
+ "-----END PGP PRIVATE KEY BLOCK-----";
+ private static final String SECP384r1 = "" +
+ "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
+ "Version: BCPG v@RELEASE_NAME@\n" +
+ "\n" +
+ "lKQEZkH7VhMFK4EEACIDAwQgkKs+EzJaFLgMZH5Fp1S8DCXZC0OildnuQX6F7Jzt\n" +
+ "BgkYyfDZ/F2KNistCqfsmxWnwAxtdRuuY2PfehWktQBQaID0OfXUnOC2E5961b3/\n" +
+ "7xoZU26T0npmTqX0P/wuXawAAX9S2V72/xeShrcIwIwy2QvCcsW9ATBSQ6U+T7KZ\n" +
+ "zzFisUiqCgYa/9hoSNnu7iNrnrcYlQ==\n" +
+ "=SyFg\n" +
+ "-----END PGP PRIVATE KEY BLOCK-----";
+ private static final String SECP521r1 = "" +
+ "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
+ "Version: BCPG v@RELEASE_NAME@\n" +
+ "\n" +
+ "lNkEZkH7VhMFK4EEACMEIwQBxt7DenSWrjuJGR0ouSwylW3ZC6mX4S+A5Cav7nz3\n" +
+ "DninA8Rdt3Cd5sHQ1IWea+J05NUZDKbOL417lUSPkAVLot0B/Qis90wODcGnAXbc\n" +
+ "m+m7rN2/Waryj/EsxLxub4UNtyZ405C8dDo9ch2JRfHiH6R1dwyqD9+yY2lOPYO+\n" +
+ "tn5fx/4AAgIDG9+DPtDf91tBMhBKc0f++t6aV115HLlyIpnEipThSwMTgzWm0uPZ\n" +
+ "KD3CifJeUU/TMk9IGFYvRlaWBQfrB3V/Ahz4\n" +
+ "=DD95\n" +
+ "-----END PGP PRIVATE KEY BLOCK-----";
+ private static final String BRAINPOOLP256r1 = "" +
+ "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
+ "Version: BCPG v@RELEASE_NAME@\n" +
+ "\n" +
+ "lHgEZkH7VhMJKyQDAwIIAQEHAgMEj7YxVg4/2p4uuhcpRqGl2i+vDhjx8YhUUNJX\n" +
+ "RNFozBuIWJ6zkW3wRKdD/7Y7tzKNwyHmZ4FBFCcUoLliLeD4SAABAIkEm4iT1g0B\n" +
+ "Bo9vkUrUcP2b+vtOuwtmrvGrT0VzVXYlD5M=\n" +
+ "=vZRh\n" +
+ "-----END PGP PRIVATE KEY BLOCK-----";
+ private static final String BRAINPOOLP384r1 = "" +
+ "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
+ "Version: BCPG v@RELEASE_NAME@\n" +
+ "\n" +
+ "lKgEZkH7VhMJKyQDAwIIAQELAwMEYm1fhilklF53Pj91awsoO0aZsppmPk9KNESD\n" +
+ "H7/gSK86gl+yhf4/oKSxeOFDHCU2es6Iijq/TCIaAjeFH3ITEyQ4tPdnDqQSz2xq\n" +
+ "o6wtRTW3cRD9oyoOT8bAMdm+RYpJAAF5AXAfxp3VtxqVVxnR1mC3Z3nL25zmvdu1\n" +
+ "oPRvA9fenVxTOlyU6X9qCycSuxamkPO7Gic=\n" +
+ "=2eJn\n" +
+ "-----END PGP PRIVATE KEY BLOCK-----";
+ private static final String BRAINPOOLP521r1 = "" +
+ "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
+ "Version: BCPG v@RELEASE_NAME@\n" +
+ "\n" +
+ "lNgEZkH7VhMJKyQDAwIIAQENBAMEbSjn4lQKNnC50PzeUtenikvF62KR7HfOLJTA\n" +
+ "r/T17tFx3Qb6Ek/xQWIJ5nIHroOrduZjLigPOXqQ+GNhCgdNPGUqAWw1sfQ86nrx\n" +
+ "jqlr67na3F3eaTJr9ajr2V37/5uHnuryJnkyy2laFdOGD0Ad9/bQkvXYoWVm0P07\n" +
+ "uCPnexEAAgCSUoeS3c+DAZlWETdyuSDyvHK7GLO67+CgVsEyqBF/Kch/vhBZFWXA\n" +
+ "Cs9lph8la5B0faKH5XSbeReudKGh/MjfIJo=\n" +
+ "=MZeT\n" +
+ "-----END PGP PRIVATE KEY BLOCK-----";
+
+ @Override
+ public String getName()
+ {
+ return "ECDSAKeyPairTest";
+ }
+
+ @Override
+ public void performTest()
+ throws Exception
+ {
+ Security.addProvider(new BouncyCastleProvider());
+ testConversionOfParsedJcaKeyPair();
+ testConversionOfParsedBcKeyPair();
+
+ testConversionOfFreshJcaKeyPair();
+ }
+
+ private void testConversionOfParsedJcaKeyPair() throws PGPException, IOException {
+ // parseAndConvertJca(PRIME256v1);
+ // parseAndConvertJca(SECP384r1);
+ // parseAndConvertJca(SECP521r1);
+ parseAndConvertJca(BRAINPOOLP256r1);
+ parseAndConvertJca(BRAINPOOLP384r1);
+ parseAndConvertJca(BRAINPOOLP521r1);
+ }
+
+ private void parseAndConvertJca(String curve) throws IOException, PGPException {
+ JcaPGPKeyConverter c = new JcaPGPKeyConverter();
+ PGPKeyPair parsed = parseJca(curve);
+ byte[] pubEnc = parsed.getPublicKey().getEncoded();
+ byte[] privEnc = parsed.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
+
+ JcaPGPKeyPair j1 = new JcaPGPKeyPair(
+ parsed.getPublicKey().getAlgorithm(),
+ new KeyPair(c.getPublicKey(parsed.getPublicKey()),
+ c.getPrivateKey(parsed.getPrivateKey())),
+ parsed.getPublicKey().getCreationTime());
+ isEncodingEqual(pubEnc, j1.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j1.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+
+ BcPGPKeyPair b1 = toBcKeyPair(j1);
+ isEncodingEqual(pubEnc, b1.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b1.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+
+ JcaPGPKeyPair j2 = toJcaKeyPair(b1);
+ isEncodingEqual(pubEnc, j2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+
+ BcPGPKeyPair b2 = toBcKeyPair(j2);
+ isEncodingEqual(pubEnc, b2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ }
+
+ private void testConversionOfParsedBcKeyPair() throws PGPException, IOException {
+ parseAndConvertBc(PRIME256v1);
+ parseAndConvertBc(SECP384r1);
+ parseAndConvertBc(SECP521r1);
+ parseAndConvertBc(BRAINPOOLP256r1);
+ parseAndConvertBc(BRAINPOOLP384r1);
+ parseAndConvertBc(BRAINPOOLP521r1);
+ }
+
+ private void parseAndConvertBc(String curve) throws IOException, PGPException {
+ BcPGPKeyConverter c = new BcPGPKeyConverter();
+ PGPKeyPair parsed = parseBc(curve);
+ byte[] pubEnc = parsed.getPublicKey().getEncoded();
+ byte[] privEnc = parsed.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
+
+ BcPGPKeyPair b1 = new BcPGPKeyPair(
+ parsed.getPublicKey().getAlgorithm(),
+ new AsymmetricCipherKeyPair(
+ c.getPublicKey(parsed.getPublicKey()),
+ c.getPrivateKey(parsed.getPrivateKey())),
+ parsed.getPublicKey().getCreationTime());
+ isEncodingEqual(pubEnc, b1.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b1.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+
+ JcaPGPKeyPair j1 = toJcaKeyPair(b1);
+ isEncodingEqual(pubEnc, j1.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j1.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+
+ BcPGPKeyPair b2 = toBcKeyPair(j1);
+ isEncodingEqual(pubEnc, b2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+
+ JcaPGPKeyPair j2 = toJcaKeyPair(b2);
+ isEncodingEqual(pubEnc, j2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+
+ }
+
+ private PGPKeyPair parseJca(String armored) throws IOException, PGPException {
+ ByteArrayInputStream bIn = new ByteArrayInputStream(armored.getBytes(StandardCharsets.UTF_8));
+ ArmoredInputStream aIn = new ArmoredInputStream(bIn);
+ BCPGInputStream pIn = new BCPGInputStream(aIn);
+ JcaPGPSecretKeyRing ring = new JcaPGPSecretKeyRing(pIn);
+ PGPSecretKey sk = ring.getSecretKey();
+ return new PGPKeyPair(sk.getPublicKey(), sk.extractPrivateKey(null));
+ }
+
+ private PGPKeyPair parseBc(String armored) throws IOException, PGPException {
+ ByteArrayInputStream bIn = new ByteArrayInputStream(armored.getBytes(StandardCharsets.UTF_8));
+ ArmoredInputStream aIn = new ArmoredInputStream(bIn);
+ BCPGInputStream pIn = new BCPGInputStream(aIn);
+ BcPGPSecretKeyRing ring = new BcPGPSecretKeyRing(pIn);
+ PGPSecretKey sk = ring.getSecretKey();
+ return new PGPKeyPair(sk.getPublicKey(), sk.extractPrivateKey(null));
+ }
+
+ private void testConversionOfFreshJcaKeyPair()
+ throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, PGPException, IOException
+ {
+ for (String curve : new String[] {"prime256v1", "secp384r1", "secp521r1", "brainpoolP256r1", "brainpoolP384r1", "brainpoolP512r1"})
+ {
+ try {
+ testConversionOfFreshJcaKeyPair(curve);
+ } catch (Exception e) {
+
+ }
+ }
+
+ }
+
+ private void testConversionOfFreshJcaKeyPair(String curve)
+ throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException, PGPException
+ {
+ Date date = currentTimeRounded();
+ KeyPairGenerator gen = KeyPairGenerator.getInstance("ECDSA", new BouncyCastleProvider());
+ gen.initialize(new ECNamedCurveGenParameterSpec(curve));
+ KeyPair kp = gen.generateKeyPair();
+
+ JcaPGPKeyPair j1 = new JcaPGPKeyPair(PublicKeyAlgorithmTags.ECDSA, kp, date);
+ byte[] pubEnc = j1.getPublicKey().getEncoded();
+ byte[] privEnc = j1.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
+ isTrue("Legacy ECDSA public key MUST be instanceof ECDSAPublicBCPGKey",
+ j1.getPublicKey().getPublicKeyPacket().getKey() instanceof ECDSAPublicBCPGKey);
+ isTrue("Legacy ECDSA secret key MUST be instanceof ECSecretBCPGKey",
+ j1.getPrivateKey().getPrivateKeyDataPacket() instanceof ECSecretBCPGKey);
+
+ BcPGPKeyPair b1 = toBcKeyPair(j1);
+ isEncodingEqual(pubEnc, b1.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b1.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy ECDSA public key MUST be instanceof ECDSAPublicBCPGKey",
+ b1.getPublicKey().getPublicKeyPacket().getKey() instanceof ECDSAPublicBCPGKey);
+ isTrue(" Legacy ECDSA secret key MUST be instanceof ECSecretBCPGKey",
+ b1.getPrivateKey().getPrivateKeyDataPacket() instanceof ECSecretBCPGKey);
+
+ JcaPGPKeyPair j2 = toJcaKeyPair(b1);
+ isEncodingEqual(pubEnc, j2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, j2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy ECDSA public key MUST be instanceof ECDSAPublicBCPGKey",
+ j2.getPublicKey().getPublicKeyPacket().getKey() instanceof ECDSAPublicBCPGKey);
+ isTrue("Legacy ECDSA secret key MUST be instanceof ECSecretBCPGKey",
+ j2.getPrivateKey().getPrivateKeyDataPacket() instanceof ECSecretBCPGKey);
+
+ BcPGPKeyPair b2 = toBcKeyPair(j2);
+ isEncodingEqual(pubEnc, b2.getPublicKey().getEncoded());
+ isEncodingEqual(privEnc, b2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
+ isTrue("Legacy ECDSA public key MUST be instanceof ECDSAPublicBCPGKey",
+ b2.getPublicKey().getPublicKeyPacket().getKey() instanceof ECDSAPublicBCPGKey);
+ isTrue("Legacy ECDSA secret key MUST be instanceof ECSecretBCPGKey",
+ b2.getPrivateKey().getPrivateKeyDataPacket() instanceof ECSecretBCPGKey);
+
+ isEquals("Creation time is preserved",
+ date.getTime(), b2.getPublicKey().getCreationTime().getTime());
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new ECDSAKeyPairTest());
+ }
+}
From a32e9b63efcd491fe4c291038407b5b2ee1d913d Mon Sep 17 00:00:00 2001
From: Paul Schaub
Date: Mon, 20 May 2024 11:12:41 +0200
Subject: [PATCH 0099/1644] Fix conversion of ECDSA keys
---
.../operator/jcajce/JcaPGPKeyConverter.java | 89 +++++++++++++------
.../openpgp/test/ECDSAKeyPairTest.java | 61 ++++++++-----
.../openpgp/test/RegressionTest.java | 3 +-
3 files changed, 100 insertions(+), 53 deletions(-)
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java
index 64779da731..e4ed38340d 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java
@@ -29,6 +29,7 @@
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Date;
+import java.util.Enumeration;
import javax.crypto.interfaces.DHPrivateKey;
import javax.crypto.interfaces.DHPublicKey;
@@ -36,6 +37,7 @@
import javax.crypto.spec.DHPrivateKeySpec;
import javax.crypto.spec.DHPublicKeySpec;
+import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.DEROctetString;
@@ -49,6 +51,7 @@
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.asn1.x9.X9ECParametersHolder;
import org.bouncycastle.asn1.x9.X9ECPoint;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.bcpg.BCPGKey;
import org.bouncycastle.bcpg.DSAPublicBCPGKey;
import org.bouncycastle.bcpg.DSASecretBCPGKey;
@@ -72,6 +75,7 @@
import org.bouncycastle.bcpg.X25519SecretBCPGKey;
import org.bouncycastle.bcpg.X448PublicBCPGKey;
import org.bouncycastle.bcpg.X448SecretBCPGKey;
+import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
import org.bouncycastle.jcajce.util.ProviderJcaJceHelper;
@@ -549,47 +553,58 @@ private BCPGKey getPublicBCPGKey(int algorithm, PGPAlgorithmParameters algorithm
SubjectPublicKeyInfo keyInfo = SubjectPublicKeyInfo.getInstance(pubKey.getEncoded());
// TODO: should probably match curve by comparison as well
- ASN1ObjectIdentifier curveOid = ASN1ObjectIdentifier.getInstance(keyInfo.getAlgorithm().getParameters());
- if (curveOid == null)
+ ASN1Encodable enc = keyInfo.getAlgorithm().getAlgorithm();
+ ASN1ObjectIdentifier curveOid;
+ curveOid = ASN1ObjectIdentifier.getInstance(enc);
+
+ // ecPublicKey is not specific enough. Drill down to find proper OID.
+ if (X9ObjectIdentifiers.id_ecPublicKey.equals(curveOid))
{
- // Legacy XDH on Curve25519 (legacy X25519)
- // 1.3.6.1.4.1.3029.1.5.1 & 1.3.101.110
- if (pubKey.getAlgorithm().regionMatches(true, 0, "X2", 0, 2))
+ enc = getCurveOIDForBCECKey((BCECPublicKey) pubKey);
+ ASN1ObjectIdentifier nCurveOid = ASN1ObjectIdentifier.getInstance(enc);
+ if (nCurveOid != null)
{
- PGPKdfParameters kdfParams = implGetKdfParameters(CryptlibObjectIdentifiers.curvey25519, algorithmParameters);
+ curveOid = nCurveOid;
+ }
+ }
- return new ECDHPublicBCPGKey(CryptlibObjectIdentifiers.curvey25519, new BigInteger(1, getPointEncUncompressed(pubKey, X25519.SCALAR_SIZE)),
+ // Legacy XDH on Curve25519 (legacy X25519)
+ // 1.3.6.1.4.1.3029.1.5.1 & 1.3.101.110
+ if (pubKey.getAlgorithm().regionMatches(true, 0, "X2", 0, 2))
+ {
+ PGPKdfParameters kdfParams = implGetKdfParameters(CryptlibObjectIdentifiers.curvey25519, algorithmParameters);
+
+ return new ECDHPublicBCPGKey(CryptlibObjectIdentifiers.curvey25519, new BigInteger(1, getPointEncUncompressed(pubKey, X25519.SCALAR_SIZE)),
kdfParams.getHashAlgorithm(), kdfParams.getSymmetricWrapAlgorithm());
- }
- // Legacy X448 (1.3.101.111)
- if (pubKey.getAlgorithm().regionMatches(true, 0, "X4", 0, 2))
- {
+ }
+ // Legacy X448 (1.3.101.111)
+ if (pubKey.getAlgorithm().regionMatches(true, 0, "X4", 0, 2))
+ {
- PGPKdfParameters kdfParams = implGetKdfParameters(EdECObjectIdentifiers.id_X448, algorithmParameters);
+ PGPKdfParameters kdfParams = implGetKdfParameters(EdECObjectIdentifiers.id_X448, algorithmParameters);
- return new ECDHPublicBCPGKey(EdECObjectIdentifiers.id_X448, new BigInteger(1, getPointEncUncompressed(pubKey, X448.SCALAR_SIZE)),
+ return new ECDHPublicBCPGKey(EdECObjectIdentifiers.id_X448, new BigInteger(1, getPointEncUncompressed(pubKey, X448.SCALAR_SIZE)),
kdfParams.getHashAlgorithm(), kdfParams.getSymmetricWrapAlgorithm());
- }
- // sun.security.ec.XDHPublicKeyImpl returns "XDH" for getAlgorithm()
- // In this case we need to determine the curve by looking at the length of the encoding :/
- else
+ }
+ // sun.security.ec.XDHPublicKeyImpl returns "XDH" for getAlgorithm()
+ // In this case we need to determine the curve by looking at the length of the encoding :/
+ else if (pubKey.getAlgorithm().regionMatches(true, 0, "XDH", 0, 3))
+ {
+ // Legacy X25519 (1.3.6.1.4.1.3029.1.5.1 & 1.3.101.110)
+ if (X25519.SCALAR_SIZE + 12 == pubKey.getEncoded().length) // + 12 for some reason
{
- // Legacy X25519 (1.3.6.1.4.1.3029.1.5.1 & 1.3.101.110)
- if (X25519.SCALAR_SIZE + 12 == pubKey.getEncoded().length) // + 12 for some reason
- {
- PGPKdfParameters kdfParams = implGetKdfParameters(CryptlibObjectIdentifiers.curvey25519, algorithmParameters);
+ PGPKdfParameters kdfParams = implGetKdfParameters(CryptlibObjectIdentifiers.curvey25519, algorithmParameters);
- return new ECDHPublicBCPGKey(CryptlibObjectIdentifiers.curvey25519, new BigInteger(1, getPointEncUncompressed(pubKey, X25519.SCALAR_SIZE)),
+ return new ECDHPublicBCPGKey(CryptlibObjectIdentifiers.curvey25519, new BigInteger(1, getPointEncUncompressed(pubKey, X25519.SCALAR_SIZE)),
kdfParams.getHashAlgorithm(), kdfParams.getSymmetricWrapAlgorithm());
- }
- // Legacy X448 (1.3.101.111)
- else
- {
- PGPKdfParameters kdfParams = implGetKdfParameters(EdECObjectIdentifiers.id_X448, algorithmParameters);
+ }
+ // Legacy X448 (1.3.101.111)
+ else
+ {
+ PGPKdfParameters kdfParams = implGetKdfParameters(EdECObjectIdentifiers.id_X448, algorithmParameters);
- return new ECDHPublicBCPGKey(EdECObjectIdentifiers.id_X448, new BigInteger(1, getPointEncUncompressed(pubKey, X448.SCALAR_SIZE)),
+ return new ECDHPublicBCPGKey(EdECObjectIdentifiers.id_X448, new BigInteger(1, getPointEncUncompressed(pubKey, X448.SCALAR_SIZE)),
kdfParams.getHashAlgorithm(), kdfParams.getSymmetricWrapAlgorithm());
- }
}
}
@@ -670,6 +685,22 @@ private BCPGKey getPublicBCPGKey(int algorithm, PGPAlgorithmParameters algorithm
}
}
+ private ASN1Encodable getCurveOIDForBCECKey(BCECPublicKey pubKey)
+ {
+ // Iterate through all registered curves to find applicable OID
+ Enumeration names = ECNamedCurveTable.getNames();
+ while (names.hasMoreElements())
+ {
+ String name = (String) names.nextElement();
+ X9ECParameters parms = ECNamedCurveTable.getByName(name);
+ if (pubKey.getParameters().getCurve().equals(parms.getCurve()))
+ {
+ return ECNamedCurveTable.getOID(name);
+ }
+ }
+ return null;
+ }
+
@FunctionalInterface
private interface BCPGKeyOperation
{
diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/ECDSAKeyPairTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/ECDSAKeyPairTest.java
index 4a7a725064..bdefb35220 100644
--- a/pg/src/test/java/org/bouncycastle/openpgp/test/ECDSAKeyPairTest.java
+++ b/pg/src/test/java/org/bouncycastle/openpgp/test/ECDSAKeyPairTest.java
@@ -18,7 +18,8 @@
import java.security.*;
import java.util.Date;
-public class ECDSAKeyPairTest extends AbstractPgpKeyPairTest
+public class ECDSAKeyPairTest
+ extends AbstractPgpKeyPairTest
{
private static final String PRIME256v1 = "" +
@@ -93,23 +94,27 @@ public void performTest()
throws Exception
{
Security.addProvider(new BouncyCastleProvider());
+ testConversionOfFreshJcaKeyPair();
testConversionOfParsedJcaKeyPair();
testConversionOfParsedBcKeyPair();
- testConversionOfFreshJcaKeyPair();
}
- private void testConversionOfParsedJcaKeyPair() throws PGPException, IOException {
- // parseAndConvertJca(PRIME256v1);
- // parseAndConvertJca(SECP384r1);
- // parseAndConvertJca(SECP521r1);
+ private void testConversionOfParsedJcaKeyPair()
+ throws PGPException, IOException
+ {
parseAndConvertJca(BRAINPOOLP256r1);
parseAndConvertJca(BRAINPOOLP384r1);
parseAndConvertJca(BRAINPOOLP521r1);
+ parseAndConvertJca(PRIME256v1);
+ parseAndConvertJca(SECP384r1);
+ parseAndConvertJca(SECP521r1);
}
- private void parseAndConvertJca(String curve) throws IOException, PGPException {
- JcaPGPKeyConverter c = new JcaPGPKeyConverter();
+ private void parseAndConvertJca(String curve)
+ throws IOException, PGPException
+ {
+ JcaPGPKeyConverter c = new JcaPGPKeyConverter().setProvider(new BouncyCastleProvider());
PGPKeyPair parsed = parseJca(curve);
byte[] pubEnc = parsed.getPublicKey().getEncoded();
byte[] privEnc = parsed.getPrivateKey().getPrivateKeyDataPacket().getEncoded();
@@ -119,7 +124,7 @@ private void parseAndConvertJca(String curve) throws IOException, PGPException {
new KeyPair(c.getPublicKey(parsed.getPublicKey()),
c.getPrivateKey(parsed.getPrivateKey())),
parsed.getPublicKey().getCreationTime());
- isEncodingEqual(pubEnc, j1.getPublicKey().getEncoded());
+ isEncodingEqual("ECDSA Public key (" + curve + ") encoding mismatch", pubEnc, j1.getPublicKey().getEncoded());
isEncodingEqual(privEnc, j1.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
BcPGPKeyPair b1 = toBcKeyPair(j1);
@@ -135,16 +140,20 @@ private void parseAndConvertJca(String curve) throws IOException, PGPException {
isEncodingEqual(privEnc, b2.getPrivateKey().getPrivateKeyDataPacket().getEncoded());
}
- private void testConversionOfParsedBcKeyPair() throws PGPException, IOException {
- parseAndConvertBc(PRIME256v1);
- parseAndConvertBc(SECP384r1);
- parseAndConvertBc(SECP521r1);
+ private void testConversionOfParsedBcKeyPair()
+ throws PGPException, IOException
+ {
parseAndConvertBc(BRAINPOOLP256r1);
parseAndConvertBc(BRAINPOOLP384r1);
parseAndConvertBc(BRAINPOOLP521r1);
+ parseAndConvertBc(PRIME256v1);
+ parseAndConvertBc(SECP384r1);
+ parseAndConvertBc(SECP521r1);
}
- private void parseAndConvertBc(String curve) throws IOException, PGPException {
+ private void parseAndConvertBc(String curve)
+ throws IOException, PGPException
+ {
BcPGPKeyConverter c = new BcPGPKeyConverter();
PGPKeyPair parsed = parseBc(curve);
byte[] pubEnc = parsed.getPublicKey().getEncoded();
@@ -173,7 +182,9 @@ private void parseAndConvertBc(String curve) throws IOException, PGPException {
}
- private PGPKeyPair parseJca(String armored) throws IOException, PGPException {
+ private PGPKeyPair parseJca(String armored)
+ throws IOException, PGPException
+ {
ByteArrayInputStream bIn = new ByteArrayInputStream(armored.getBytes(StandardCharsets.UTF_8));
ArmoredInputStream aIn = new ArmoredInputStream(bIn);
BCPGInputStream pIn = new BCPGInputStream(aIn);
@@ -182,7 +193,9 @@ private PGPKeyPair parseJca(String armored) throws IOException, PGPException {
return new PGPKeyPair(sk.getPublicKey(), sk.extractPrivateKey(null));
}
- private PGPKeyPair parseBc(String armored) throws IOException, PGPException {
+ private PGPKeyPair parseBc(String armored)
+ throws IOException, PGPException
+ {
ByteArrayInputStream bIn = new ByteArrayInputStream(armored.getBytes(StandardCharsets.UTF_8));
ArmoredInputStream aIn = new ArmoredInputStream(bIn);
BCPGInputStream pIn = new BCPGInputStream(aIn);
@@ -194,15 +207,17 @@ private PGPKeyPair parseBc(String armored) throws IOException, PGPException {
private void testConversionOfFreshJcaKeyPair()
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, PGPException, IOException
{
- for (String curve : new String[] {"prime256v1", "secp384r1", "secp521r1", "brainpoolP256r1", "brainpoolP384r1", "brainpoolP512r1"})
+ for (String curve : new String[] {
+ "prime256v1",
+ "secp384r1",
+ "secp521r1",
+ "brainpoolP256r1",
+ "brainpoolP384r1",
+ "brainpoolP512r1"
+ })
{
- try {
- testConversionOfFreshJcaKeyPair(curve);
- } catch (Exception e) {
-
- }
+ testConversionOfFreshJcaKeyPair(curve);
}
-
}
private void testConversionOfFreshJcaKeyPair(String curve)
diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/RegressionTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/RegressionTest.java
index e9a4c11e3c..e110c16ba2 100644
--- a/pg/src/test/java/org/bouncycastle/openpgp/test/RegressionTest.java
+++ b/pg/src/test/java/org/bouncycastle/openpgp/test/RegressionTest.java
@@ -74,7 +74,8 @@ public class RegressionTest
new LegacyX25519KeyPairTest(),
new LegacyX448KeyPairTest(),
- new Curve25519PrivateKeyEncodingTest()
+ new Curve25519PrivateKeyEncodingTest(),
+ new ECDSAKeyPairTest()
};
public static void main(String[] args)
From 8004fb31210c45a95634daf20a8021e0fea09ec7 Mon Sep 17 00:00:00 2001
From: Paul Schaub
Date: Thu, 23 May 2024 09:59:53 +0200
Subject: [PATCH 0100/1644] Rename named curve lookup method
---
.../openpgp/operator/jcajce/JcaPGPKeyConverter.java | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java
index e4ed38340d..a12d9cbbb1 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java
@@ -557,10 +557,10 @@ private BCPGKey getPublicBCPGKey(int algorithm, PGPAlgorithmParameters algorithm
ASN1ObjectIdentifier curveOid;
curveOid = ASN1ObjectIdentifier.getInstance(enc);
- // ecPublicKey is not specific enough. Drill down to find proper OID.
+ // BCECPublicKey uses explicit parameter encoding, so we need to find the named curve manually
if (X9ObjectIdentifiers.id_ecPublicKey.equals(curveOid))
{
- enc = getCurveOIDForBCECKey((BCECPublicKey) pubKey);
+ enc = getNamedCurveOID((BCECPublicKey) pubKey);
ASN1ObjectIdentifier nCurveOid = ASN1ObjectIdentifier.getInstance(enc);
if (nCurveOid != null)
{
@@ -685,7 +685,7 @@ else if (pubKey.getAlgorithm().regionMatches(true, 0, "XDH", 0, 3))
}
}
- private ASN1Encodable getCurveOIDForBCECKey(BCECPublicKey pubKey)
+ private ASN1Encodable getNamedCurveOID(BCECPublicKey pubKey)
{
// Iterate through all registered curves to find applicable OID
Enumeration names = ECNamedCurveTable.getNames();
From 897caef2bcfdbd369fcd0692b923dc95df328260 Mon Sep 17 00:00:00 2001
From: Paul Schaub
Date: Thu, 23 May 2024 10:39:49 +0200
Subject: [PATCH 0101/1644] Do not register BouncyCastleProvider in
ECDSAKeyPairTest
---
.../java/org/bouncycastle/openpgp/test/ECDSAKeyPairTest.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/ECDSAKeyPairTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/ECDSAKeyPairTest.java
index bdefb35220..111f8fd853 100644
--- a/pg/src/test/java/org/bouncycastle/openpgp/test/ECDSAKeyPairTest.java
+++ b/pg/src/test/java/org/bouncycastle/openpgp/test/ECDSAKeyPairTest.java
@@ -93,7 +93,6 @@ public String getName()
public void performTest()
throws Exception
{
- Security.addProvider(new BouncyCastleProvider());
testConversionOfFreshJcaKeyPair();
testConversionOfParsedJcaKeyPair();
testConversionOfParsedBcKeyPair();
From 185ed7f149967967c259cad1a5fc7240599da8ef Mon Sep 17 00:00:00 2001
From: gefeili
Date: Fri, 24 May 2024 08:43:01 +0930
Subject: [PATCH 0102/1644] Minor change in SignaturePacket.
---
pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java b/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java
index f24ebb2114..4774e8f838 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java
@@ -494,11 +494,10 @@ public byte[] getSignatureTrailer()
StreamUtil.write2OctetLength(sOut, data.length);
sOut.write(data);
- byte[] hData = sOut.toByteArray();
-
+ int hDataSize = sOut.size();
sOut.write((byte)this.getVersion());
sOut.write((byte)0xff);
- StreamUtil.write4OctetLength(sOut, hData.length);
+ StreamUtil.write4OctetLength(sOut, hDataSize);
}
catch (IOException e)
{
From 225fcaf5d7c08ad7564f9d410b8221c387e0d944 Mon Sep 17 00:00:00 2001
From: gefeili
Date: Tue, 28 May 2024 16:55:53 +0930
Subject: [PATCH 0103/1644] create generateHKDFBytes, isX25519. Refactor:
makeKeyFromPassPhrase
---
.../bcpg/SymmetricKeyEncSessionPacket.java | 3 +++
.../bouncycastle/openpgp/operator/PGPUtil.java | 3 +--
.../openpgp/operator/bc/BcAEADUtil.java | 18 +++++++++++-------
.../operator/bc/BcPBEDataDecryptorFactory.java | 9 +--------
.../bc/BcPBEKeyEncryptionMethodGenerator.java | 13 +------------
.../openpgp/operator/bc/BcPGPKeyConverter.java | 4 ++--
.../bc/BcPublicKeyDataDecryptorFactory.java | 3 +--
...cPublicKeyKeyEncryptionMethodGenerator.java | 5 +----
.../openpgp/operator/bc/BcUtil.java | 7 +++++++
.../openpgp/operator/jcajce/JcaJcePGPUtil.java | 7 +++++++
.../operator/jcajce/JcaPGPKeyConverter.java | 4 ++--
.../openpgp/operator/jcajce/JceAEADUtil.java | 18 ++++++++++++------
.../JcePBEDataDecryptorFactoryBuilder.java | 8 +-------
.../JcePBEKeyEncryptionMethodGenerator.java | 12 +-----------
...cePublicKeyDataDecryptorFactoryBuilder.java | 4 +---
...ePublicKeyKeyEncryptionMethodGenerator.java | 4 +---
16 files changed, 53 insertions(+), 69 deletions(-)
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/SymmetricKeyEncSessionPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/SymmetricKeyEncSessionPacket.java
index c8574ca5ca..098d5ccac5 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/SymmetricKeyEncSessionPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/SymmetricKeyEncSessionPacket.java
@@ -71,6 +71,9 @@ else if (version == VERSION_5 || version == VERSION_6)
// https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#section-5.3.2-3.5
int s2kOctetCount = in.read();
+
+ //TODO: use this line to replace the following code?
+ //s2k = new S2K(in);
s2kBytes = new byte[s2kOctetCount];
in.readFully(s2kBytes);
try
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPUtil.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPUtil.java
index 8d8e23b0d5..2d77e5d9a1 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPUtil.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPUtil.java
@@ -94,6 +94,7 @@ else if (s2k.getHashAlgorithm() != digestCalculator.getAlgorithm())
try
{
+ byte[] iv = s2k != null? s2k.getIV() : null;
while (generatedBytes < keyBytes.length)
{
if (s2k != null)
@@ -103,8 +104,6 @@ else if (s2k.getHashAlgorithm() != digestCalculator.getAlgorithm())
dOut.write(0);
}
- byte[] iv = s2k.getIV();
-
switch (s2k.getType())
{
case S2K.SIMPLE:
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcAEADUtil.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcAEADUtil.java
index f2cb8c4baf..4d58cb9982 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcAEADUtil.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcAEADUtil.java
@@ -91,21 +91,25 @@ protected static long getChunkLength(int chunkSize)
* @param salt salt
* @param hkdfInfo HKDF info
* @return message key and separate IV
- * @throws PGPException
*/
static byte[][] deriveMessageKeyAndIv(int aeadAlgo, int cipherAlgo, byte[] sessionKey, byte[] salt, byte[] hkdfInfo)
- throws PGPException
+ {
+ int keyLen = SymmetricKeyUtils.getKeyLengthInOctets(cipherAlgo);
+ int ivLen = AEADUtils.getIVLength(aeadAlgo);
+ byte[] messageKeyAndIv = generateHKDFBytes(sessionKey, salt, hkdfInfo, keyLen + ivLen - 8);
+
+ return new byte[][]{Arrays.copyOfRange(messageKeyAndIv, 0, keyLen), Arrays.copyOfRange(messageKeyAndIv, keyLen, keyLen + ivLen)};
+ }
+
+ static byte[] generateHKDFBytes(byte[] sessionKey, byte[] salt, byte[] hkdfInfo, int len)
{
HKDFParameters hkdfParameters = new HKDFParameters(sessionKey, salt, hkdfInfo);
HKDFBytesGenerator hkdfGen = new HKDFBytesGenerator(new SHA256Digest());
hkdfGen.init(hkdfParameters);
- int keyLen = SymmetricKeyUtils.getKeyLengthInOctets(cipherAlgo);
- int ivLen = AEADUtils.getIVLength(aeadAlgo);
- byte[] messageKeyAndIv = new byte[keyLen + ivLen - 8];
+ byte[] messageKeyAndIv = new byte[len];
hkdfGen.generateBytes(messageKeyAndIv, 0, messageKeyAndIv.length);
-
- return new byte[][]{Arrays.copyOfRange(messageKeyAndIv, 0, keyLen), Arrays.copyOfRange(messageKeyAndIv, keyLen, keyLen + ivLen)};
+ return messageKeyAndIv;
}
public static AEADBlockCipher createAEADCipher(int encAlgorithm, int aeadAlgorithm)
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPBEDataDecryptorFactory.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPBEDataDecryptorFactory.java
index 3dca31d018..62aad81ba1 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPBEDataDecryptorFactory.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPBEDataDecryptorFactory.java
@@ -8,12 +8,8 @@
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.InvalidCipherTextException;
-import org.bouncycastle.crypto.digests.SHA256Digest;
-import org.bouncycastle.crypto.engines.CamelliaEngine;
-import org.bouncycastle.crypto.generators.HKDFBytesGenerator;
import org.bouncycastle.crypto.modes.AEADBlockCipher;
import org.bouncycastle.crypto.params.AEADParameters;
-import org.bouncycastle.crypto.params.HKDFParameters;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSessionKey;
@@ -94,13 +90,10 @@ public byte[] recoverAEADEncryptedSessionData(SymmetricKeyEncSessionPacket keyDa
byte[] hkdfInfo = keyData.getAAData(); // Between v5 and v6, these bytes differ
int kekLen = SymmetricKeyUtils.getKeyLengthInOctets(keyData.getEncAlgorithm());
- byte[] kek = new byte[kekLen];
// HKDF
// secretKey := HKDF_sha256(ikm, hkdfInfo).generate()
- HKDFBytesGenerator hkdfGen = new HKDFBytesGenerator(new SHA256Digest());
- hkdfGen.init(new HKDFParameters(ikm, null, hkdfInfo));
- hkdfGen.generateBytes(kek, 0, kek.length);
+ byte[] kek = BcAEADUtil.generateHKDFBytes(ikm, null, hkdfInfo, kekLen);
final KeyParameter secretKey = new KeyParameter(kek);
// AEAD
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPBEKeyEncryptionMethodGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPBEKeyEncryptionMethodGenerator.java
index 986fbcdfaf..5277c4d943 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPBEKeyEncryptionMethodGenerator.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPBEKeyEncryptionMethodGenerator.java
@@ -7,12 +7,8 @@
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.InvalidCipherTextException;
-import org.bouncycastle.crypto.digests.SHA256Digest;
-import org.bouncycastle.crypto.engines.CamelliaEngine;
-import org.bouncycastle.crypto.generators.HKDFBytesGenerator;
import org.bouncycastle.crypto.modes.AEADCipher;
import org.bouncycastle.crypto.params.AEADParameters;
-import org.bouncycastle.crypto.params.HKDFParameters;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.operator.PBEKeyEncryptionMethodGenerator;
@@ -113,15 +109,8 @@ protected byte[] encryptSessionInfo(int encAlgorithm, byte[] key, byte[] session
}
protected byte[] generateV6KEK(int kekAlgorithm, byte[] ikm, byte[] info)
- throws PGPException
{
- HKDFBytesGenerator hkdf = new HKDFBytesGenerator(new SHA256Digest());
- hkdf.init(new HKDFParameters(ikm, null, info));
-
- int kekLen = SymmetricKeyUtils.getKeyLengthInOctets(kekAlgorithm);
- byte[] kek = new byte[kekLen];
- hkdf.generateBytes(kek, 0, kek.length);
- return kek;
+ return BcAEADUtil.generateHKDFBytes(ikm, null, info, SymmetricKeyUtils.getKeyLengthInOctets(kekAlgorithm));
}
protected byte[] getEskAndTag(int kekAlgorithm, int aeadAlgorithm, byte[] sessionInfo, byte[] key, byte[] iv, byte[] info)
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyConverter.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyConverter.java
index 23ab52f0e4..7b22eed680 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyConverter.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyConverter.java
@@ -122,7 +122,7 @@ public AsymmetricKeyParameter getPrivateKey(PGPPrivateKey privKey)
{
ECDHPublicBCPGKey ecdhPub = (ECDHPublicBCPGKey)pubPk.getKey();
- if (CryptlibObjectIdentifiers.curvey25519.equals(ecdhPub.getCurveOID()))
+ if (BcUtil.isX25519(ecdhPub.getCurveOID()))
{
return PrivateKeyFactory.createKey(getPrivateKeyInfo(EdECObjectIdentifiers.id_X25519,
Arrays.reverseInPlace(BigIntegers.asUnsignedByteArray(((ECSecretBCPGKey)privPk).getX()))));
@@ -209,7 +209,7 @@ public AsymmetricKeyParameter getPublicKey(PGPPublicKey publicKey)
{
ECDHPublicBCPGKey ecdhK = (ECDHPublicBCPGKey)publicPk.getKey();
- if (ecdhK.getCurveOID().equals(CryptlibObjectIdentifiers.curvey25519))
+ if (BcUtil.isX25519(ecdhK.getCurveOID()))
{
byte[] pEnc = BigIntegers.asUnsignedByteArray(ecdhK.getEncodedPoint());
// skip the 0x40 header byte.
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java
index 6734eafdc0..bc4eee8ce6 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java
@@ -3,7 +3,6 @@
import java.io.IOException;
import java.math.BigInteger;
-import org.bouncycastle.asn1.cryptlib.CryptlibObjectIdentifiers;
import org.bouncycastle.bcpg.AEADEncDataPacket;
import org.bouncycastle.bcpg.ECDHPublicBCPGKey;
import org.bouncycastle.bcpg.HashAlgorithmTags;
@@ -110,7 +109,7 @@ else if (keyAlgorithm == PublicKeyAlgorithmTags.ECDH)
ECDHPublicBCPGKey ecPubKey = (ECDHPublicBCPGKey)pgpPrivKey.getPublicKeyPacket().getKey();
// XDH
- if (ecPubKey.getCurveOID().equals(CryptlibObjectIdentifiers.curvey25519))
+ if (BcUtil.isX25519(ecPubKey.getCurveOID()))
{
if (pEnc.length != 1 + X25519PublicKeyParameters.KEY_SIZE || 0x40 != pEnc[0])
{
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java
index 8621c91d56..1b0978e671 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java
@@ -4,7 +4,6 @@
import java.math.BigInteger;
import java.security.SecureRandom;
-import org.bouncycastle.asn1.cryptlib.CryptlibObjectIdentifiers;
import org.bouncycastle.bcpg.ECDHPublicBCPGKey;
import org.bouncycastle.bcpg.HashAlgorithmTags;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
@@ -13,7 +12,6 @@
import org.bouncycastle.crypto.AsymmetricBlockCipher;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
-import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.KeyGenerationParameters;
import org.bouncycastle.crypto.RawAgreement;
import org.bouncycastle.crypto.Wrapper;
@@ -32,7 +30,6 @@
import org.bouncycastle.crypto.params.X25519PublicKeyParameters;
import org.bouncycastle.crypto.params.X448KeyGenerationParameters;
import org.bouncycastle.crypto.params.X448PublicKeyParameters;
-import org.bouncycastle.internal.asn1.edec.EdECObjectIdentifiers;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.operator.PGPPad;
@@ -86,7 +83,7 @@ protected byte[] encryptSessionInfo(PGPPublicKey pubKey, byte[] sessionInfo)
{
ECDHPublicBCPGKey ecPubKey = (ECDHPublicBCPGKey)pubKeyPacket.getKey();
byte[] userKeyingMaterial = RFC6637Utils.createUserKeyingMaterial(pubKeyPacket, new BcKeyFingerprintCalculator());
- if (ecPubKey.getCurveOID().equals(CryptlibObjectIdentifiers.curvey25519) || ecPubKey.getCurveOID().equals(EdECObjectIdentifiers.id_X25519))
+ if (BcUtil.isX25519(ecPubKey.getCurveOID()))
{
AsymmetricCipherKeyPair ephKp = getAsymmetricCipherKeyPair(new X25519KeyPairGenerator(), new X25519KeyGenerationParameters(random));
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcUtil.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcUtil.java
index 9a4af7103c..d9f8fea076 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcUtil.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcUtil.java
@@ -4,6 +4,8 @@
import java.math.BigInteger;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.cryptlib.CryptlibObjectIdentifiers;
+import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.bcpg.AEADEncDataPacket;
@@ -125,4 +127,9 @@ static byte[] getSecret(RawAgreement agreement, AsymmetricKeyParameter privKey,
agreement.calculateAgreement(ephPub, secret, 0);
return secret;
}
+
+ static boolean isX25519(ASN1ObjectIdentifier curveID)
+ {
+ return curveID.equals(CryptlibObjectIdentifiers.curvey25519) || curveID.equals(EdECObjectIdentifiers.id_X25519);
+ }
}
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaJcePGPUtil.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaJcePGPUtil.java
index 8d1a75b737..d87663a715 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaJcePGPUtil.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaJcePGPUtil.java
@@ -12,6 +12,8 @@
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.cryptlib.CryptlibObjectIdentifiers;
+import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.bcpg.PublicKeyPacket;
@@ -79,4 +81,9 @@ static Key getSecret(OperatorHelper helper, PublicKey cryptoPublicKey, String ke
agreement.doPhase(cryptoPublicKey, true);
return agreement.generateSecret(keyEncryptionOID);
}
+
+ static boolean isX25519(ASN1ObjectIdentifier curveID)
+ {
+ return curveID.equals(CryptlibObjectIdentifiers.curvey25519) || curveID.equals(EdECObjectIdentifiers.id_X25519);
+ }
}
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java
index 66e0179a34..dbb4fd679d 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java
@@ -194,7 +194,7 @@ public PrivateKey getPrivateKey(PGPPrivateKey privKey)
ECDHPublicBCPGKey ecdhPub = (ECDHPublicBCPGKey)pubPk.getKey();
ECSecretBCPGKey ecdhK = (ECSecretBCPGKey)privPk;
- if (CryptlibObjectIdentifiers.curvey25519.equals(ecdhPub.getCurveOID()))
+ if (JcaJcePGPUtil.isX25519(ecdhPub.getCurveOID()))
{
// 'reverse' because the native format for X25519 private keys is little-endian
return implGeneratePrivate("XDH", () -> getPrivateKeyInfo(EdECObjectIdentifiers.id_X25519,
@@ -289,7 +289,7 @@ public PublicKey getPublicKey(PGPPublicKey publicKey)
{
ECDHPublicBCPGKey ecdhK = (ECDHPublicBCPGKey)publicPk.getKey();
- if (ecdhK.getCurveOID().equals(CryptlibObjectIdentifiers.curvey25519))
+ if (JcaJcePGPUtil.isX25519(ecdhK.getCurveOID()))
{
return get25519PublicKey(ecdhK.getEncodedPoint(), EdECObjectIdentifiers.id_X25519, "XDH", "Curve");
}
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JceAEADUtil.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JceAEADUtil.java
index 8d5024eab5..6e76199ef6 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JceAEADUtil.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JceAEADUtil.java
@@ -99,17 +99,23 @@ protected static long getChunkLength(int chunkSize)
static byte[][] deriveMessageKeyAndIv(int aeadAlgo, int cipherAlgo, byte[] sessionKey, byte[] salt, byte[] hkdfInfo)
throws PGPException
{
- // TODO: needs to be JCA based. KeyGenerator?
+ // TODO: needs to be JCA based. KeyGenerator
+ int keyLen = SymmetricKeyUtils.getKeyLengthInOctets(cipherAlgo);
+ int ivLen = AEADUtils.getIVLength(aeadAlgo);
+ byte[] messageKeyAndIv = generateHKDFBytes(sessionKey, salt, hkdfInfo, keyLen + ivLen - 8);
+
+ return new byte[][]{Arrays.copyOfRange(messageKeyAndIv, 0, keyLen), Arrays.copyOfRange(messageKeyAndIv, keyLen, keyLen + ivLen)};
+ }
+
+ static byte[] generateHKDFBytes(byte[] sessionKey, byte[] salt, byte[] hkdfInfo, int len)
+ {
HKDFParameters hkdfParameters = new HKDFParameters(sessionKey, salt, hkdfInfo);
HKDFBytesGenerator hkdfGen = new HKDFBytesGenerator(new SHA256Digest());
hkdfGen.init(hkdfParameters);
- int keyLen = SymmetricKeyUtils.getKeyLengthInOctets(cipherAlgo);
- int ivLen = AEADUtils.getIVLength(aeadAlgo);
- byte[] messageKeyAndIv = new byte[keyLen + ivLen - 8];
+ byte[] messageKeyAndIv = new byte[len];
hkdfGen.generateBytes(messageKeyAndIv, 0, messageKeyAndIv.length);
-
- return new byte[][]{Arrays.copyOfRange(messageKeyAndIv, 0, keyLen), Arrays.copyOfRange(messageKeyAndIv, keyLen, keyLen + ivLen)};
+ return messageKeyAndIv;
}
/**
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBEDataDecryptorFactoryBuilder.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBEDataDecryptorFactoryBuilder.java
index 8687678369..3a2677d8e9 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBEDataDecryptorFactoryBuilder.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBEDataDecryptorFactoryBuilder.java
@@ -12,9 +12,6 @@
import org.bouncycastle.bcpg.SymmetricEncIntegrityPacket;
import org.bouncycastle.bcpg.SymmetricKeyEncSessionPacket;
import org.bouncycastle.bcpg.SymmetricKeyUtils;
-import org.bouncycastle.crypto.digests.SHA256Digest;
-import org.bouncycastle.crypto.generators.HKDFBytesGenerator;
-import org.bouncycastle.crypto.params.HKDFParameters;
import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
import org.bouncycastle.jcajce.util.ProviderJcaJceHelper;
@@ -144,13 +141,10 @@ public byte[] recoverAEADEncryptedSessionData(SymmetricKeyEncSessionPacket keyDa
byte[] hkdfInfo = keyData.getAAData(); // between v5 and v6, these bytes differ
int kekLen = SymmetricKeyUtils.getKeyLengthInOctets(keyData.getEncAlgorithm());
- byte[] kek = new byte[kekLen];
// HKDF
// secretKey := HKDF_sha256(ikm, hkdfInfo).generate()
- HKDFBytesGenerator hkdfGen = new HKDFBytesGenerator(new SHA256Digest()); // SHA256 is fixed
- hkdfGen.init(new HKDFParameters(ikm, null, hkdfInfo));
- hkdfGen.generateBytes(kek, 0, kek.length);
+ byte[] kek = JceAEADUtil.generateHKDFBytes(ikm, null, hkdfInfo, kekLen);
final SecretKey secretKey = new SecretKeySpec(kek, PGPUtil.getSymmetricCipherName(keyData.getEncAlgorithm()));
// AEAD
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java
index 478bbc7432..69e03019b7 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java
@@ -15,11 +15,8 @@
import org.bouncycastle.bcpg.S2K;
import org.bouncycastle.bcpg.SymmetricKeyUtils;
import org.bouncycastle.crypto.InvalidCipherTextException;
-import org.bouncycastle.crypto.digests.SHA256Digest;
-import org.bouncycastle.crypto.generators.HKDFBytesGenerator;
import org.bouncycastle.crypto.modes.AEADCipher;
import org.bouncycastle.crypto.params.AEADParameters;
-import org.bouncycastle.crypto.params.HKDFParameters;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
@@ -150,15 +147,8 @@ protected byte[] encryptSessionInfo(int encAlgorithm, byte[] key, byte[] session
}
protected byte[] generateV6KEK(int kekAlgorithm, byte[] ikm, byte[] info)
- throws PGPException
{
- HKDFBytesGenerator hkdf = new HKDFBytesGenerator(new SHA256Digest());
- hkdf.init(new HKDFParameters(ikm, null, info));
-
- int kekLen = SymmetricKeyUtils.getKeyLengthInOctets(kekAlgorithm);
- byte[] kek = new byte[kekLen];
- hkdf.generateBytes(kek, 0, kek.length);
- return kek;
+ return JceAEADUtil.generateHKDFBytes(ikm, null, info, SymmetricKeyUtils.getKeyLengthInOctets(kekAlgorithm));
}
protected byte[] getEskAndTag(int kekAlgorithm, int aeadAlgorithm, byte[] sessionInfo, byte[] key, byte[] iv, byte[] info)
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java
index 691575e272..8593d28848 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java
@@ -17,7 +17,6 @@
import javax.crypto.interfaces.DHKey;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.cryptlib.CryptlibObjectIdentifiers;
import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
@@ -31,7 +30,6 @@
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.bcpg.X25519PublicBCPGKey;
import org.bouncycastle.bcpg.X448PublicBCPGKey;
-import org.bouncycastle.crypto.params.X25519PublicKeyParameters;
import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
@@ -257,7 +255,7 @@ private byte[] decryptSessionData(JcaPGPKeyConverter converter, PGPPrivateKey pr
String agreementName;
ECDHPublicBCPGKey ecKey = (ECDHPublicBCPGKey)pubKeyData.getKey();
// XDH
- if (ecKey.getCurveOID().equals(CryptlibObjectIdentifiers.curvey25519))
+ if (JcaJcePGPUtil.isX25519(ecKey.getCurveOID()))
{
agreementName = RFC6637Utils.getXDHAlgorithm(pubKeyData);
if (pEnc.length != (1 + X25519PublicBCPGKey.LENGTH) || 0x40 != pEnc[0])
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyKeyEncryptionMethodGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyKeyEncryptionMethodGenerator.java
index c41f6312da..cc8b7ac16b 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyKeyEncryptionMethodGenerator.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyKeyEncryptionMethodGenerator.java
@@ -17,7 +17,6 @@
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.SecretKeySpec;
-import org.bouncycastle.asn1.cryptlib.CryptlibObjectIdentifiers;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x9.X962Parameters;
@@ -25,7 +24,6 @@
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.bcpg.PublicKeyPacket;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
-import org.bouncycastle.internal.asn1.edec.EdECObjectIdentifiers;
import org.bouncycastle.jcajce.spec.HybridValueParameterSpec;
import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
@@ -101,7 +99,7 @@ protected byte[] encryptSessionInfo(PGPPublicKey pubKey, byte[] sessionInfo)
ECDHPublicBCPGKey ecKey = (ECDHPublicBCPGKey)pubKey.getPublicKeyPacket().getKey();
String keyEncryptionOID = RFC6637Utils.getKeyEncryptionOID(ecKey.getSymmetricKeyAlgorithm()).getId();
PublicKeyPacket pubKeyPacket = pubKey.getPublicKeyPacket();
- if (ecKey.getCurveOID().equals(CryptlibObjectIdentifiers.curvey25519) || ecKey.getCurveOID().equals(EdECObjectIdentifiers.id_X25519))
+ if (JcaJcePGPUtil.isX25519(ecKey.getCurveOID()))
{
return getEncryptSessionInfo(pubKeyPacket, "X25519", cryptoPublicKey, keyEncryptionOID,
ecKey.getSymmetricKeyAlgorithm(), sessionInfo, RFC6637Utils.getXDHAlgorithm(pubKeyPacket),
From b330d2f3895d12c69814ceb1f056c26773f0e224 Mon Sep 17 00:00:00 2001
From: gefeili
Date: Wed, 29 May 2024 12:07:38 +0930
Subject: [PATCH 0104/1644] Use prepend to replace concatenate for getting
session data
---
.../openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java | 2 +-
.../jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java
index bc4eee8ce6..a892708363 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java
@@ -244,7 +244,7 @@ private byte[] getSessionData(byte[] enc, AsymmetricKeyParameter privKey, int pL
KeyParameter key = new KeyParameter(RFC6637KDFCalculator.createKey(hashAlgorithm, symmetricKeyAlgorithm,
Arrays.concatenate(pEnc, pgpPrivKey.getPublicKeyPacket().getKey().getEncoded(), secret), "OpenPGP " + algorithmName));
- return Arrays.concatenate(new byte[]{enc[pLen + 1]}, unwrapSessionData(keyEnc, symmetricKeyAlgorithm, key));
+ return Arrays.prepend(unwrapSessionData(keyEnc, symmetricKeyAlgorithm, key), enc[pLen + 1]);
}
private static byte[] unwrapSessionData(byte[] keyEnc, int symmetricKeyAlgorithm, KeyParameter key)
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java
index 8593d28848..0b79b0fdd0 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java
@@ -305,7 +305,7 @@ private byte[] decryptSessionData(JcaPGPKeyConverter converter, PGPPrivateKey pr
Key paddedSessionKey = getSessionKey(converter, privKey, agreementAlgorithm, publicKey, symmetricKeyAlgorithm, keyEnc,
JcaJcePGPUtil.getHybridValueParameterSpecWithPrepend(pEnc, privKey.getPublicKeyPacket(), algorithmName));
symmetricKeyAlgorithm = enc[pLen + 1] & 0xff;
- return Arrays.concatenate(new byte[]{(byte)symmetricKeyAlgorithm}, paddedSessionKey.getEncoded());
+ return Arrays.prepend(paddedSessionKey.getEncoded(), (byte)symmetricKeyAlgorithm);
}
catch (Exception e)
{
From d44127498d7c5b67dc92aca8f02015c3bc1e9da0 Mon Sep 17 00:00:00 2001
From: David Hook
Date: Thu, 30 May 2024 09:26:43 +1000
Subject: [PATCH 0105/1644] added UTF8 support
---
.../org/bouncycastle/mail/smime/handlers/multipart_signed.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mail/src/main/java/org/bouncycastle/mail/smime/handlers/multipart_signed.java b/mail/src/main/java/org/bouncycastle/mail/smime/handlers/multipart_signed.java
index 3abd395f01..7fba22651c 100644
--- a/mail/src/main/java/org/bouncycastle/mail/smime/handlers/multipart_signed.java
+++ b/mail/src/main/java/org/bouncycastle/mail/smime/handlers/multipart_signed.java
@@ -207,7 +207,7 @@ public void writeln(String s)
{
try
{
- byte abyte0[] = Strings.toByteArray(s);
+ byte abyte0[] = Strings.toUTF8ByteArray(s);
super.out.write(abyte0);
super.out.write(newline);
}
From 975c2cc0961daa06430b57fe261b16c10fbb9c44 Mon Sep 17 00:00:00 2001
From: David Hook
Date: Thu, 30 May 2024 09:53:44 +1000
Subject: [PATCH 0106/1644] Generalized EC key processing for different
providers, added use of X9.62 parameters for curve OID finding (relates to
github #1671)
---
.../operator/jcajce/JcaPGPKeyConverter.java | 41 ++++++++++++-------
1 file changed, 26 insertions(+), 15 deletions(-)
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java
index a12d9cbbb1..ea93cf68a8 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java
@@ -5,8 +5,6 @@
import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
@@ -18,11 +16,9 @@
import java.security.interfaces.RSAPublicKey;
import java.security.spec.DSAPrivateKeySpec;
import java.security.spec.DSAPublicKeySpec;
-import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPrivateKeySpec;
import java.security.spec.ECPublicKeySpec;
-import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateCrtKeySpec;
@@ -48,6 +44,7 @@
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x9.ECNamedCurveTable;
+import org.bouncycastle.asn1.x9.X962Parameters;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.asn1.x9.X9ECParametersHolder;
import org.bouncycastle.asn1.x9.X9ECPoint;
@@ -75,13 +72,13 @@
import org.bouncycastle.bcpg.X25519SecretBCPGKey;
import org.bouncycastle.bcpg.X448PublicBCPGKey;
import org.bouncycastle.bcpg.X448SecretBCPGKey;
-import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
import org.bouncycastle.jcajce.util.ProviderJcaJceHelper;
import org.bouncycastle.jce.interfaces.ElGamalPublicKey;
+import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
-
import org.bouncycastle.math.ec.rfc7748.X25519;
import org.bouncycastle.math.ec.rfc7748.X448;
import org.bouncycastle.math.ec.rfc8032.Ed25519;
@@ -236,7 +233,7 @@ else if (EdECObjectIdentifiers.id_X448.equals(ecdhPub.getCurveOID()))
}
case PublicKeyAlgorithmTags.ECDSA:
{
- return implGetPrivateKeyEC("ECDSA", (ECDSAPublicBCPGKey)pubPk.getKey(), (ECSecretBCPGKey)privPk);
+ return implGetPrivateKeyEC("EC", (ECDSAPublicBCPGKey)pubPk.getKey(), (ECSecretBCPGKey)privPk);
}
// Legacy EdDSA (legacy Ed448, legacy Ed25519)
case PublicKeyAlgorithmTags.EDDSA_LEGACY:
@@ -349,7 +346,7 @@ else if (ecdhK.getCurveOID().equals(EdECObjectIdentifiers.id_X448))
}
case PublicKeyAlgorithmTags.ECDSA:
{
- return implGetPublicKeyEC("ECDSA", (ECDSAPublicBCPGKey) publicPk.getKey());
+ return implGetPublicKeyEC("EC", (ECDSAPublicBCPGKey) publicPk.getKey());
}
// Legacy EdDSA (legacy Ed448, legacy Ed25519)
case PublicKeyAlgorithmTags.EDDSA_LEGACY:
@@ -420,11 +417,11 @@ else if (ecdhK.getCurveOID().equals(EdECObjectIdentifiers.id_X448))
}
private ECParameterSpec getECParameterSpec(ASN1ObjectIdentifier curveOid, X9ECParameters x9Params)
- throws InvalidParameterSpecException, NoSuchProviderException, NoSuchAlgorithmException
+ throws IOException, GeneralSecurityException
{
AlgorithmParameters params = helper.createAlgorithmParameters("EC");
- params.init(new ECGenParameterSpec(ECNamedCurveTable.getName(curveOid)));
+ params.init(new X962Parameters(curveOid).getEncoded());
return params.getParameterSpec(ECParameterSpec.class);
}
@@ -560,7 +557,7 @@ private BCPGKey getPublicBCPGKey(int algorithm, PGPAlgorithmParameters algorithm
// BCECPublicKey uses explicit parameter encoding, so we need to find the named curve manually
if (X9ObjectIdentifiers.id_ecPublicKey.equals(curveOid))
{
- enc = getNamedCurveOID((BCECPublicKey) pubKey);
+ enc = getNamedCurveOID(X962Parameters.getInstance(keyInfo.getAlgorithm().getParameters()));
ASN1ObjectIdentifier nCurveOid = ASN1ObjectIdentifier.getInstance(enc);
if (nCurveOid != null)
{
@@ -685,15 +682,29 @@ else if (pubKey.getAlgorithm().regionMatches(true, 0, "XDH", 0, 3))
}
}
- private ASN1Encodable getNamedCurveOID(BCECPublicKey pubKey)
+ private ASN1Encodable getNamedCurveOID(X962Parameters ecParams)
{
+ ECCurve curve = null;
+ if (ecParams.isNamedCurve())
+ {
+ return ASN1ObjectIdentifier.getInstance(ecParams.getParameters());
+ }
+ else if (ecParams.isImplicitlyCA())
+ {
+ curve = ((X9ECParameters)CryptoServicesRegistrar.getProperty(CryptoServicesRegistrar.Property.EC_IMPLICITLY_CA)).getCurve();
+ }
+ else
+ {
+ curve = X9ECParameters.getInstance(ecParams.getParameters()).getCurve();
+ }
+
// Iterate through all registered curves to find applicable OID
Enumeration names = ECNamedCurveTable.getNames();
while (names.hasMoreElements())
{
- String name = (String) names.nextElement();
+ String name = (String)names.nextElement();
X9ECParameters parms = ECNamedCurveTable.getByName(name);
- if (pubKey.getParameters().getCurve().equals(parms.getCurve()))
+ if (curve.equals(parms.getCurve()))
{
return ECNamedCurveTable.getOID(name);
}
@@ -763,7 +774,7 @@ private PublicKey implGetPublicKeyX509(byte[] pEnc, int pEncOff, ASN1ObjectIdent
}
private PrivateKey implGetPrivateKeyEC(String keyAlgorithm, ECPublicBCPGKey ecPub, ECSecretBCPGKey ecPriv)
- throws GeneralSecurityException, PGPException
+ throws GeneralSecurityException, PGPException, IOException
{
ASN1ObjectIdentifier curveOid = ecPub.getCurveOID();
ECPrivateKeySpec ecPrivSpec = new ECPrivateKeySpec(ecPriv.getX(), getECParameterSpec(curveOid, JcaJcePGPUtil.getX9Parameters(curveOid)));
From 7bbee1821ba22057f83a2ab33d72d952badb8972 Mon Sep 17 00:00:00 2001
From: David Hook
Date: Thu, 30 May 2024 10:01:13 +1000
Subject: [PATCH 0107/1644] Minor cleanup of unused argument.
---
.../openpgp/operator/jcajce/JcaPGPKeyConverter.java | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java
index ea93cf68a8..311db29fb8 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java
@@ -416,7 +416,7 @@ else if (ecdhK.getCurveOID().equals(EdECObjectIdentifiers.id_X448))
}
}
- private ECParameterSpec getECParameterSpec(ASN1ObjectIdentifier curveOid, X9ECParameters x9Params)
+ private ECParameterSpec getECParameterSpec(ASN1ObjectIdentifier curveOid)
throws IOException, GeneralSecurityException
{
AlgorithmParameters params = helper.createAlgorithmParameters("EC");
@@ -777,7 +777,7 @@ private PrivateKey implGetPrivateKeyEC(String keyAlgorithm, ECPublicBCPGKey ecPu
throws GeneralSecurityException, PGPException, IOException
{
ASN1ObjectIdentifier curveOid = ecPub.getCurveOID();
- ECPrivateKeySpec ecPrivSpec = new ECPrivateKeySpec(ecPriv.getX(), getECParameterSpec(curveOid, JcaJcePGPUtil.getX9Parameters(curveOid)));
+ ECPrivateKeySpec ecPrivSpec = new ECPrivateKeySpec(ecPriv.getX(), getECParameterSpec(curveOid));
return implGeneratePrivate(keyAlgorithm, ecPrivSpec);
}
@@ -791,7 +791,7 @@ private PublicKey implGetPublicKeyEC(String keyAlgorithm, ECPublicBCPGKey ecPub)
new java.security.spec.ECPoint(
ecPubPoint.getAffineXCoord().toBigInteger(),
ecPubPoint.getAffineYCoord().toBigInteger()),
- getECParameterSpec(curveOID, x9Params));
+ getECParameterSpec(curveOID));
return implGeneratePublic(keyAlgorithm, ecPubSpec);
}
From a3df8e0d47df5f65d8577ced517179794aee725c Mon Sep 17 00:00:00 2001
From: gefeili
Date: Thu, 30 May 2024 15:39:32 +0930
Subject: [PATCH 0108/1644] Solve the bugs related EDDSA_LEGACY with Ed448
---
.../bouncycastle/openpgp/PGPSignature.java | 5 ++--
.../openpgp/operator/PGPKeyConverter.java | 2 +-
.../openpgp/operator/RFC6637Utils.java | 8 ++++---
.../openpgp/operator/bc/BcImplProvider.java | 7 ++++++
.../bc/BcPublicKeyDataDecryptorFactory.java | 10 ++++++++
...PublicKeyKeyEncryptionMethodGenerator.java | 12 ++++++++++
.../jcajce/JcaPGPContentSignerBuilder.java | 12 +++++++++-
.../JcaPGPContentVerifierBuilderProvider.java | 13 ++++++++++-
.../operator/jcajce/JcaPGPKeyConverter.java | 5 ++--
...ePublicKeyDataDecryptorFactoryBuilder.java | 11 ++++++++-
...PublicKeyKeyEncryptionMethodGenerator.java | 8 +++++++
.../operator/jcajce/OperatorHelper.java | 5 ++--
.../openpgp/test/BcImplProviderTest.java | 23 +++++++++++++++++++
.../openpgp/test/OperatorBcTest.java | 1 +
14 files changed, 108 insertions(+), 14 deletions(-)
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java
index 8428d0efd6..3e961c0361 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java
@@ -454,9 +454,8 @@ else if (getKeyAlgorithm() == PublicKeyAlgorithmTags.EDDSA_LEGACY)
{
byte[] a = BigIntegers.asUnsignedByteArray(sigValues[0].getValue());
byte[] b = BigIntegers.asUnsignedByteArray(sigValues[1].getValue());
- signature = new byte[Ed25519.SIGNATURE_SIZE];
- System.arraycopy(a, 0, signature, Ed25519.PUBLIC_KEY_SIZE - a.length, a.length);
- System.arraycopy(b, 0, signature, Ed25519.SIGNATURE_SIZE - b.length, b.length);
+ //TODO: distinguish Ed25519 and Ed448
+ signature = Arrays.concatenate(a, b);
}
else
{
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPKeyConverter.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPKeyConverter.java
index 52ca458e0b..ab73bd51d8 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPKeyConverter.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPKeyConverter.java
@@ -75,7 +75,7 @@ protected PGPKeyConverter()
* | AES-128 |
*
*
- * | Curve448 |
+ * Curve448Legacy (not in RFC Draft) |
* SHA2-512 |
* AES-256 |
*
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/RFC6637Utils.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/RFC6637Utils.java
index 4132038a39..aa65e34a76 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/RFC6637Utils.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/RFC6637Utils.java
@@ -4,6 +4,7 @@
import java.io.IOException;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
import org.bouncycastle.bcpg.ECDHPublicBCPGKey;
@@ -37,14 +38,15 @@ else if (pubKeyData.getKey() instanceof X448PublicBCPGKey)
return "X448withSHA512CKDF";
}
ECDHPublicBCPGKey ecKey = (ECDHPublicBCPGKey)pubKeyData.getKey();
+ String curve = ecKey.getCurveOID().equals(EdECObjectIdentifiers.id_X448) ? "X448" : "X25519";
switch (ecKey.getHashAlgorithm())
{
case HashAlgorithmTags.SHA256:
- return "X25519withSHA256CKDF";
+ return curve + "withSHA256CKDF";
case HashAlgorithmTags.SHA384:
- return "X25519withSHA384CKDF";
+ return curve + "withSHA384CKDF";
case HashAlgorithmTags.SHA512:
- return "X25519withSHA512CKDF";
+ return curve + "withSHA512CKDF";
default:
throw new IllegalArgumentException("Unknown hash algorithm specified: " + ecKey.getHashAlgorithm());
}
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcImplProvider.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcImplProvider.java
index 02d456ea17..ee940d1d40 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcImplProvider.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcImplProvider.java
@@ -33,6 +33,8 @@
import org.bouncycastle.crypto.engines.RFC3394WrapEngine;
import org.bouncycastle.crypto.engines.RSABlindedEngine;
import org.bouncycastle.crypto.engines.TwofishEngine;
+import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
+import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
import org.bouncycastle.crypto.signers.DSADigestSigner;
import org.bouncycastle.crypto.signers.DSASigner;
import org.bouncycastle.crypto.signers.ECDSASigner;
@@ -96,6 +98,11 @@ static Signer createSigner(int keyAlgorithm, int hashAlgorithm, CipherParameters
case PublicKeyAlgorithmTags.ECDSA:
return new DSADigestSigner(new ECDSASigner(), createDigest(hashAlgorithm));
case PublicKeyAlgorithmTags.EDDSA_LEGACY:
+ if (keyParam instanceof Ed25519PrivateKeyParameters || keyParam instanceof Ed25519PublicKeyParameters)
+ {
+ return new EdDsaSigner(new Ed25519Signer(), createDigest(hashAlgorithm));
+ }
+ return new EdDsaSigner(new Ed448Signer(new byte[0]), createDigest(hashAlgorithm));
case PublicKeyAlgorithmTags.Ed25519:
return new EdDsaSigner(new Ed25519Signer(), createDigest(hashAlgorithm));
case PublicKeyAlgorithmTags.Ed448:
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java
index a892708363..efdee52ba5 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java
@@ -3,6 +3,7 @@
import java.io.IOException;
import java.math.BigInteger;
+import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
import org.bouncycastle.bcpg.AEADEncDataPacket;
import org.bouncycastle.bcpg.ECDHPublicBCPGKey;
import org.bouncycastle.bcpg.HashAlgorithmTags;
@@ -118,6 +119,15 @@ else if (keyAlgorithm == PublicKeyAlgorithmTags.ECDH)
// skip the 0x40 header byte.
secret = BcUtil.getSecret(new X25519Agreement(), privKey, new X25519PublicKeyParameters(pEnc, 1));
}
+ else if (ecPubKey.getCurveOID().equals(EdECObjectIdentifiers.id_X448))
+ {
+ if (pEnc.length != 1 + X448PublicKeyParameters.KEY_SIZE || 0x40 != pEnc[0])
+ {
+ throw new IllegalArgumentException("Invalid Curve25519 public key");
+ }
+ // skip the 0x40 header byte.
+ secret = BcUtil.getSecret(new X448Agreement(), privKey, new X448PublicKeyParameters(pEnc, 1));
+ }
else
{
ECDomainParameters ecParameters = ((ECPrivateKeyParameters)privKey).getParameters();
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java
index 1b0978e671..60c35df653 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java
@@ -4,6 +4,7 @@
import java.math.BigInteger;
import java.security.SecureRandom;
+import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
import org.bouncycastle.bcpg.ECDHPublicBCPGKey;
import org.bouncycastle.bcpg.HashAlgorithmTags;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
@@ -94,6 +95,17 @@ protected byte[] encryptSessionInfo(PGPPublicKey pubKey, byte[] sessionInfo)
((X25519PublicKeyParameters)ephKp.getPublic()).encode(ephPubEncoding, 1);
return encryptSessionInfo(sessionInfo, secret, userKeyingMaterial, ephPubEncoding, ecPubKey.getHashAlgorithm(), ecPubKey.getSymmetricKeyAlgorithm());
}
+ else if (ecPubKey.getCurveOID().equals(EdECObjectIdentifiers.id_X448))
+ {
+ AsymmetricCipherKeyPair ephKp = getAsymmetricCipherKeyPair(new X448KeyPairGenerator(), new X448KeyGenerationParameters(random));
+
+ byte[] secret = BcUtil.getSecret(new X448Agreement(), ephKp.getPrivate(), cryptoPublicKey);
+
+ byte[] ephPubEncoding = new byte[1 + X448PublicKeyParameters.KEY_SIZE];
+ ephPubEncoding[0] = X_HDR;
+ ((X448PublicKeyParameters)ephKp.getPublic()).encode(ephPubEncoding, 1);
+ return encryptSessionInfo(sessionInfo, secret, userKeyingMaterial, ephPubEncoding, ecPubKey.getHashAlgorithm(), ecPubKey.getSymmetricKeyAlgorithm());
+ }
else
{
AsymmetricCipherKeyPair ephKp = getAsymmetricCipherKeyPair(new ECKeyPairGenerator(),
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPContentSignerBuilder.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPContentSignerBuilder.java
index de028e85cc..5a7c236b64 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPContentSignerBuilder.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPContentSignerBuilder.java
@@ -95,7 +95,17 @@ public PGPContentSigner build(final int signatureType, final long keyID, final P
final PGPDigestCalculator digestCalculator = digestCalculatorProviderBuilder.build().get(hashAlgorithm);
final PGPDigestCalculator edDigestCalculator = digestCalculatorProviderBuilder.build().get(hashAlgorithm);
final Signature signature;
- signature = helper.createSignature(keyAlgorithm, hashAlgorithm);
+
+ if (keyAlgorithm == PublicKeyAlgorithmTags.EDDSA_LEGACY && privateKey.getAlgorithm().equals("Ed448"))
+ {
+ // Try my best to solve Ed448Legacy issue
+ signature = helper.createSignature("Ed448");
+ }
+ else
+ {
+ signature = helper.createSignature(keyAlgorithm, hashAlgorithm);
+ }
+
try
{
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPContentVerifierBuilderProvider.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPContentVerifierBuilderProvider.java
index de459a43ac..9b3002ea40 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPContentVerifierBuilderProvider.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPContentVerifierBuilderProvider.java
@@ -73,7 +73,18 @@ public JcaPGPContentVerifierBuilder(int keyAlgorithm, int hashAlgorithm)
public PGPContentVerifier build(final PGPPublicKey publicKey)
throws PGPException
{
- final Signature signature = helper.createSignature(keyAlgorithm, hashAlgorithm);
+ final Signature signature;
+
+ if (keyAlgorithm == PublicKeyAlgorithmTags.EDDSA_LEGACY
+ && ((EdDSAPublicBCPGKey)publicKey.getPublicKeyPacket().getKey()).getCurveOID().equals(EdECObjectIdentifiers.id_Ed448))
+ {
+ // Try my best to solve Ed448Legacy issue
+ signature = helper.createSignature("Ed448");
+ }
+ else
+ {
+ signature = helper.createSignature(keyAlgorithm, hashAlgorithm);
+ }
final PGPDigestCalculator digestCalculator = digestCalculatorProviderBuilder.build().get(hashAlgorithm);
final PublicKey jcaKey = keyConverter.getPublicKey(publicKey);
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java
index f891ca9ab7..30854ae449 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java
@@ -728,11 +728,12 @@ private BCPGKey getPublicBCPGKey(PublicKey pubKey, int keySize, BCPGKeyOperation
private byte[] getPointEncUncompressed(PublicKey pubKey, int publicKeySize)
{
- SubjectPublicKeyInfo pubInfo = SubjectPublicKeyInfo.getInstance(pubKey.getEncoded());
+ byte[] pubInfo = SubjectPublicKeyInfo.getInstance(pubKey.getEncoded()).getPublicKeyData().getBytes();
byte[] pointEnc = new byte[1 + publicKeySize];
pointEnc[0] = 0x40;
- System.arraycopy(pubInfo.getPublicKeyData().getBytes(), 0, pointEnc, 1, pointEnc.length - 1);
+ //offset with pointEnc.length - pubInfo.length to avoid leading zero issue
+ System.arraycopy(pubInfo, 0, pointEnc, pointEnc.length - pubInfo.length , pubInfo.length);
return pointEnc;
}
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java
index 0b79b0fdd0..9c7bc8fbc1 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java
@@ -264,6 +264,15 @@ private byte[] decryptSessionData(JcaPGPKeyConverter converter, PGPPrivateKey pr
}
publicKey = getPublicKey(pEnc, EdECObjectIdentifiers.id_X25519, 1);
}
+ else if (ecKey.getCurveOID().equals(EdECObjectIdentifiers.id_X448))
+ {
+ agreementName = RFC6637Utils.getXDHAlgorithm(pubKeyData);
+ if (pEnc.length != (1 + X448PublicBCPGKey.LENGTH) || 0x40 != pEnc[0])
+ {
+ throw new IllegalArgumentException("Invalid Curve25519 public key");
+ }
+ publicKey = getPublicKey(pEnc, EdECObjectIdentifiers.id_X448, 1);
+ }
else
{
X9ECParametersHolder x9Params = ECNamedCurveTable.getByOIDLazy(ecKey.getCurveOID());
@@ -318,7 +327,7 @@ private Key getSessionKey(JcaPGPKeyConverter converter, PGPPrivateKey privKey, S
throws PGPException, GeneralSecurityException
{
PrivateKey privateKey = converter.getPrivateKey(privKey);
- Key key = JcaJcePGPUtil.getSecret(helper, publicKey, RFC6637Utils.getKeyEncryptionOID(symmetricKeyAlgorithm).getId(), agreementName, ukms, privateKey);
+ Key key = JcaJcePGPUtil.getSecret(helper, publicKey, RFC6637Utils.getKeyEncryptionOID(symmetricKeyAlgorithm).getId(), agreementName, ukms, privateKey);
Cipher c = helper.createKeyWrapper(symmetricKeyAlgorithm);
c.init(Cipher.UNWRAP_MODE, key);
return c.unwrap(keyEnc, "Session", Cipher.SECRET_KEY);
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyKeyEncryptionMethodGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyKeyEncryptionMethodGenerator.java
index cc8b7ac16b..08110ac560 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyKeyEncryptionMethodGenerator.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyKeyEncryptionMethodGenerator.java
@@ -17,6 +17,7 @@
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.SecretKeySpec;
+import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x9.X962Parameters;
@@ -106,6 +107,13 @@ protected byte[] encryptSessionInfo(PGPPublicKey pubKey, byte[] sessionInfo)
(kpGen) -> kpGen.initialize(255, random),
(ephPubEncoding) -> Arrays.prepend(ephPubEncoding, X_HDR));
}
+ else if (ecKey.getCurveOID().equals(EdECObjectIdentifiers.id_X448))
+ {
+ return getEncryptSessionInfo(pubKeyPacket, "X448", cryptoPublicKey, keyEncryptionOID,
+ ecKey.getSymmetricKeyAlgorithm(), sessionInfo, RFC6637Utils.getXDHAlgorithm(pubKeyPacket),
+ (kpGen) -> kpGen.initialize(448, random),
+ (ephPubEncoding) -> Arrays.prepend(ephPubEncoding, X_HDR));
+ }
else
{
return getEncryptSessionInfo(pubKeyPacket, "EC", cryptoPublicKey, keyEncryptionOID,
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/OperatorHelper.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/OperatorHelper.java
index 3f0ade5164..319c6164b9 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/OperatorHelper.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/OperatorHelper.java
@@ -49,7 +49,7 @@ MessageDigest createDigest(int algorithm)
catch (NoSuchAlgorithmException e)
{
if (algorithm == HashAlgorithmTags.SHA1
- || (algorithm >= HashAlgorithmTags.SHA256 && algorithm <= HashAlgorithmTags.SHA224))
+ || (algorithm >= HashAlgorithmTags.SHA256 && algorithm <= HashAlgorithmTags.SHA224))
{
dig = helper.createMessageDigest("SHA-" + digestName.substring(3));
}
@@ -203,7 +203,7 @@ Cipher createKeyWrapper(int encAlgorithm)
}
}
- private Signature createSignature(String cipherName)
+ Signature createSignature(String cipherName)
throws PGPException
{
try
@@ -249,6 +249,7 @@ public Signature createSignature(int keyAlgorithm, int hashAlgorithm)
return createSignature(PGPUtil.getDigestName(hashAlgorithm) + "with" + encAlg);
}
+
public AlgorithmParameters createAlgorithmParameters(String algorithm)
throws NoSuchProviderException, NoSuchAlgorithmException
{
diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/BcImplProviderTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/BcImplProviderTest.java
index faaf2615c8..96851850b5 100644
--- a/pg/src/test/java/org/bouncycastle/openpgp/test/BcImplProviderTest.java
+++ b/pg/src/test/java/org/bouncycastle/openpgp/test/BcImplProviderTest.java
@@ -142,6 +142,28 @@ public void operation()
});
//createSigner
+ testCreateSigner(PublicKeyAlgorithmTags.EDDSA_LEGACY, new EdDsaSigner(new Ed448Signer(new byte[0]), new SHA1Digest()), "EdDSA",
+ new PrivateKeyOperation()
+ {
+ @Override
+ public BCPGKey getPrivateBCPGKey(PGPPublicKey pub, PrivateKey privKey)
+ throws IOException
+ {
+ PrivateKeyInfo pInfo = PrivateKeyInfo.getInstance(privKey.getEncoded());
+ return new EdSecretBCPGKey(
+ new BigInteger(1, ASN1OctetString.getInstance(pInfo.parsePrivateKey()).getOctets()));
+ }
+ },
+ new KeyPairGeneratorOperation()
+ {
+ @Override
+ public void initialize(KeyPairGenerator kpGen)
+ throws InvalidAlgorithmParameterException
+ {
+ kpGen.initialize(new ECNamedCurveGenParameterSpec("Ed448"));
+ }
+ });
+
testCreateSigner(PublicKeyAlgorithmTags.DSA, new DSADigestSigner(new DSASigner(), new SHA1Digest()), "DSA",
new PrivateKeyOperation()
{
@@ -317,6 +339,7 @@ public void initialize(KeyPairGenerator kpGen)
kpGen.initialize(new ECNamedCurveGenParameterSpec("Ed25519"));
}
});
+
testCreateSigner(PublicKeyAlgorithmTags.Ed448, new EdDsaSigner(new Ed448Signer(new byte[0]), new SHA1Digest()), "EdDSA",
new PrivateKeyOperation()
{
diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/OperatorBcTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/OperatorBcTest.java
index d6332eaad8..460badd9f1 100644
--- a/pg/src/test/java/org/bouncycastle/openpgp/test/OperatorBcTest.java
+++ b/pg/src/test/java/org/bouncycastle/openpgp/test/OperatorBcTest.java
@@ -383,6 +383,7 @@ private void testCreateKeyPair(int algorithm1, int algorithm2, String name, KeyP
public void testKeyRings()
throws Exception
{
+ keyringTest("EdDSA", "Ed448", PublicKeyAlgorithmTags.EDDSA_LEGACY, "XDH", "X448", PublicKeyAlgorithmTags.ECDH, HashAlgorithmTags.SHA512, SymmetricKeyAlgorithmTags.AES_256);
keyringTest("EdDSA", "Ed448", PublicKeyAlgorithmTags.Ed448, "XDH", "X448", PublicKeyAlgorithmTags.X448, HashAlgorithmTags.SHA512, SymmetricKeyAlgorithmTags.AES_256);
keyringTest("EdDSA", "Ed25519", PublicKeyAlgorithmTags.EDDSA_LEGACY, "XDH", "X25519", PublicKeyAlgorithmTags.ECDH, HashAlgorithmTags.SHA256, SymmetricKeyAlgorithmTags.AES_128);
From 22513e73cac5206ae9f87bc00b27bea519e9080b Mon Sep 17 00:00:00 2001
From: Karsten Otto
Date: Tue, 6 Dec 2022 16:43:14 +0100
Subject: [PATCH 0109/1644] ProvTlsServer should use custom DH groups when
configured
(cherry picked from commit 2a5b8858a0789c33d70de237ef0379db30ffd6e7)
---
.../jsse/provider/ProvTlsServer.java | 30 ++++++++++++++++++-
1 file changed, 29 insertions(+), 1 deletion(-)
diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/ProvTlsServer.java b/tls/src/main/java/org/bouncycastle/jsse/provider/ProvTlsServer.java
index f39a51b78a..0e5d18fc47 100644
--- a/tls/src/main/java/org/bouncycastle/jsse/provider/ProvTlsServer.java
+++ b/tls/src/main/java/org/bouncycastle/jsse/provider/ProvTlsServer.java
@@ -7,6 +7,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.LinkedHashMap;
@@ -45,6 +46,7 @@
import org.bouncycastle.tls.TlsUtils;
import org.bouncycastle.tls.TrustedAuthority;
import org.bouncycastle.tls.crypto.DHGroup;
+import org.bouncycastle.tls.crypto.TlsDHConfig;
import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;
@@ -64,7 +66,6 @@ class ProvTlsServer
* TODO[jsse] Does this selection override the restriction from 'jdk.tls.ephemeralDHKeySize'?
* TODO[fips] Probably should be ignored in fips mode?
*/
- @SuppressWarnings("unused")
private static final DHGroup[] provServerDefaultDHEParameters = getDefaultDHEParameters();
private static final boolean provServerEnableCA = PropertyUtils
@@ -153,6 +154,14 @@ else if (!p.isProbablePrime(120))
outerComma = closeBrace + 1;
if (outerComma >= limit)
{
+ result.sort(new Comparator()
+ {
+ @Override
+ public int compare(DHGroup a, DHGroup b)
+ {
+ return a.getP().bitLength() - b.getP().bitLength();
+ }
+ });
return result.toArray(new DHGroup[result.size()]);
}
}
@@ -324,6 +333,25 @@ protected boolean selectCipherSuite(int cipherSuite) throws IOException
return result;
}
+ @Override
+ public TlsDHConfig getDHConfig() throws IOException
+ {
+ if (provServerDefaultDHEParameters != null)
+ {
+ int minimumFiniteFieldBits = Math.max(
+ TlsDHUtils.getMinimumFiniteFieldBits(selectedCipherSuite), provEphemeralDHKeySize);
+
+ for (DHGroup group: provServerDefaultDHEParameters)
+ {
+ if (group.getP().bitLength() >= minimumFiniteFieldBits)
+ {
+ return new TlsDHConfig(group);
+ }
+ }
+ }
+ return super.getDHConfig();
+ }
+
@Override
protected int selectDH(int minimumFiniteFieldBits)
{
From 3b66bb100e2f8728bb757d2d2d6d38f4e8f2688f Mon Sep 17 00:00:00 2001
From: Paul Schaub
Date: Thu, 23 May 2024 14:03:22 +0200
Subject: [PATCH 0110/1644] Add signing/verification tests for
LegacyEd25519/LegacyEd448 keys
---
.../test/LegacyEd25519KeyPairTest.java | 65 +++++++++++++++++++
.../openpgp/test/LegacyEd448KeyPairTest.java | 61 +++++++++++++++++
2 files changed, 126 insertions(+)
diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/LegacyEd25519KeyPairTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/LegacyEd25519KeyPairTest.java
index fda4d0d7d2..f3efe8bc1e 100644
--- a/pg/src/test/java/org/bouncycastle/openpgp/test/LegacyEd25519KeyPairTest.java
+++ b/pg/src/test/java/org/bouncycastle/openpgp/test/LegacyEd25519KeyPairTest.java
@@ -2,6 +2,7 @@
import org.bouncycastle.bcpg.EdDSAPublicBCPGKey;
import org.bouncycastle.bcpg.EdSecretBCPGKey;
+import org.bouncycastle.bcpg.HashAlgorithmTags;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.generators.Ed25519KeyPairGenerator;
@@ -9,10 +10,20 @@
import org.bouncycastle.jcajce.spec.EdDSAParameterSpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPKeyPair;
+import org.bouncycastle.openpgp.PGPSignature;
+import org.bouncycastle.openpgp.PGPSignatureGenerator;
+import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
+import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider;
+import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
+import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider;
import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPair;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Date;
@@ -31,6 +42,60 @@ public void performTest()
{
testConversionOfJcaKeyPair();
testConversionOfBcKeyPair();
+ testV4SigningVerificationWithJcaKey();
+ testV4SigningVerificationWithBcKey();
+ }
+
+ private void testV4SigningVerificationWithJcaKey()
+ throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, PGPException
+ {
+ Date date = currentTimeRounded();
+ KeyPairGenerator gen = KeyPairGenerator.getInstance("EDDSA", new BouncyCastleProvider());
+ gen.initialize(new EdDSAParameterSpec("Ed25519"));
+ KeyPair kp = gen.generateKeyPair();
+ PGPKeyPair keyPair = new JcaPGPKeyPair(PublicKeyAlgorithmTags.EDDSA_LEGACY, kp, date);
+
+ byte[] data = "Hello, World!\n".getBytes(StandardCharsets.UTF_8);
+
+ PGPContentSignerBuilder contSigBuilder = new JcaPGPContentSignerBuilder(
+ keyPair.getPublicKey().getAlgorithm(),
+ HashAlgorithmTags.SHA512)
+ .setProvider(new BouncyCastleProvider());
+ PGPSignatureGenerator sigGen = new PGPSignatureGenerator(contSigBuilder);
+ sigGen.init(PGPSignature.BINARY_DOCUMENT, keyPair.getPrivateKey());
+ sigGen.update(data);
+ PGPSignature signature = sigGen.generate();
+
+ PGPContentVerifierBuilderProvider contVerBuilder = new JcaPGPContentVerifierBuilderProvider()
+ .setProvider(new BouncyCastleProvider());
+ signature.init(contVerBuilder, keyPair.getPublicKey());
+ signature.update(data);
+ isTrue(signature.verify());
+ }
+
+ private void testV4SigningVerificationWithBcKey()
+ throws PGPException
+ {
+ Date date = currentTimeRounded();
+ Ed25519KeyPairGenerator gen = new Ed25519KeyPairGenerator();
+ gen.init(new Ed25519KeyGenerationParameters(new SecureRandom()));
+ AsymmetricCipherKeyPair kp = gen.generateKeyPair();
+ BcPGPKeyPair keyPair = new BcPGPKeyPair(PublicKeyAlgorithmTags.EDDSA_LEGACY, kp, date);
+
+ byte[] data = "Hello, World!\n".getBytes(StandardCharsets.UTF_8);
+
+ PGPContentSignerBuilder contSigBuilder = new BcPGPContentSignerBuilder(
+ keyPair.getPublicKey().getAlgorithm(),
+ HashAlgorithmTags.SHA512);
+ PGPSignatureGenerator sigGen = new PGPSignatureGenerator(contSigBuilder);
+ sigGen.init(PGPSignature.BINARY_DOCUMENT, keyPair.getPrivateKey());
+ sigGen.update(data);
+ PGPSignature signature = sigGen.generate();
+
+ PGPContentVerifierBuilderProvider contVerBuilder = new BcPGPContentVerifierBuilderProvider();
+ signature.init(contVerBuilder, keyPair.getPublicKey());
+ signature.update(data);
+ isTrue(signature.verify());
}
private void testConversionOfJcaKeyPair()
diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/LegacyEd448KeyPairTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/LegacyEd448KeyPairTest.java
index 07f320df3d..49c73603cb 100644
--- a/pg/src/test/java/org/bouncycastle/openpgp/test/LegacyEd448KeyPairTest.java
+++ b/pg/src/test/java/org/bouncycastle/openpgp/test/LegacyEd448KeyPairTest.java
@@ -7,10 +7,17 @@
import org.bouncycastle.jcajce.spec.EdDSAParameterSpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.*;
+import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
+import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider;
+import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
+import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider;
import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPair;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Date;
@@ -29,6 +36,60 @@ public void performTest()
{
testConversionOfJcaKeyPair();
testConversionOfBcKeyPair();
+ testV4SigningVerificationWithJcaKey();
+ testV4SigningVerificationWithBcKey();
+ }
+
+ private void testV4SigningVerificationWithJcaKey()
+ throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, PGPException
+ {
+ Date date = currentTimeRounded();
+ KeyPairGenerator gen = KeyPairGenerator.getInstance("EDDSA", new BouncyCastleProvider());
+ gen.initialize(new EdDSAParameterSpec("Ed448"));
+ KeyPair kp = gen.generateKeyPair();
+ PGPKeyPair keyPair = new JcaPGPKeyPair(PublicKeyAlgorithmTags.EDDSA_LEGACY, kp, date);
+
+ byte[] data = "Hello, World!\n".getBytes(StandardCharsets.UTF_8);
+
+ PGPContentSignerBuilder contSigBuilder = new JcaPGPContentSignerBuilder(
+ keyPair.getPublicKey().getAlgorithm(),
+ HashAlgorithmTags.SHA512)
+ .setProvider(new BouncyCastleProvider());
+ PGPSignatureGenerator sigGen = new PGPSignatureGenerator(contSigBuilder);
+ sigGen.init(PGPSignature.BINARY_DOCUMENT, keyPair.getPrivateKey());
+ sigGen.update(data);
+ PGPSignature signature = sigGen.generate();
+
+ PGPContentVerifierBuilderProvider contVerBuilder = new JcaPGPContentVerifierBuilderProvider()
+ .setProvider(new BouncyCastleProvider());
+ signature.init(contVerBuilder, keyPair.getPublicKey());
+ signature.update(data);
+ isTrue(signature.verify());
+ }
+
+ private void testV4SigningVerificationWithBcKey()
+ throws PGPException
+ {
+ Date date = currentTimeRounded();
+ Ed448KeyPairGenerator gen = new Ed448KeyPairGenerator();
+ gen.init(new Ed448KeyGenerationParameters(new SecureRandom()));
+ AsymmetricCipherKeyPair kp = gen.generateKeyPair();
+ BcPGPKeyPair keyPair = new BcPGPKeyPair(PublicKeyAlgorithmTags.EDDSA_LEGACY, kp, date);
+
+ byte[] data = "Hello, World!\n".getBytes(StandardCharsets.UTF_8);
+
+ PGPContentSignerBuilder contSigBuilder = new BcPGPContentSignerBuilder(
+ keyPair.getPublicKey().getAlgorithm(),
+ HashAlgorithmTags.SHA512);
+ PGPSignatureGenerator sigGen = new PGPSignatureGenerator(contSigBuilder);
+ sigGen.init(PGPSignature.BINARY_DOCUMENT, keyPair.getPrivateKey());
+ sigGen.update(data);
+ PGPSignature signature = sigGen.generate();
+
+ PGPContentVerifierBuilderProvider contVerBuilder = new BcPGPContentVerifierBuilderProvider();
+ signature.init(contVerBuilder, keyPair.getPublicKey());
+ signature.update(data);
+ isTrue(signature.verify());
}
private void testConversionOfJcaKeyPair()
From 105dbef01d688a963f98ba2815f1343774c42fae Mon Sep 17 00:00:00 2001
From: Paul Schaub
Date: Thu, 23 May 2024 11:40:45 +0200
Subject: [PATCH 0111/1644] Fix legacy Ed448 signature field parsing
---
.../org/bouncycastle/openpgp/PGPSignature.java | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java
index 8428d0efd6..1c2a5ecebc 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java
@@ -19,6 +19,7 @@
import org.bouncycastle.bcpg.SignatureSubpacket;
import org.bouncycastle.bcpg.TrustPacket;
import org.bouncycastle.math.ec.rfc8032.Ed25519;
+import org.bouncycastle.math.ec.rfc8032.Ed448;
import org.bouncycastle.openpgp.operator.PGPContentVerifier;
import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilder;
import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider;
@@ -454,9 +455,19 @@ else if (getKeyAlgorithm() == PublicKeyAlgorithmTags.EDDSA_LEGACY)
{
byte[] a = BigIntegers.asUnsignedByteArray(sigValues[0].getValue());
byte[] b = BigIntegers.asUnsignedByteArray(sigValues[1].getValue());
- signature = new byte[Ed25519.SIGNATURE_SIZE];
- System.arraycopy(a, 0, signature, Ed25519.PUBLIC_KEY_SIZE - a.length, a.length);
- System.arraycopy(b, 0, signature, Ed25519.SIGNATURE_SIZE - b.length, b.length);
+ if (a.length + b.length == Ed25519.SIGNATURE_SIZE)
+ {
+ signature = new byte[Ed25519.SIGNATURE_SIZE];
+ System.arraycopy(a, 0, signature, Ed25519.PUBLIC_KEY_SIZE - a.length, a.length);
+ System.arraycopy(b, 0, signature, Ed25519.SIGNATURE_SIZE - b.length, b.length);
+ }
+ else
+ {
+ signature = new byte[Ed448.SIGNATURE_SIZE];
+ System.arraycopy(a, 0, signature, Ed448.PUBLIC_KEY_SIZE - a.length, a.length);
+ System.arraycopy(b, 0, signature, Ed448.SIGNATURE_SIZE - b.length, b.length);
+ }
+
}
else
{
From d07266b2d4c10c6c22cb0945059005b12b50014a Mon Sep 17 00:00:00 2001
From: Paul Schaub
Date: Thu, 23 May 2024 13:57:38 +0200
Subject: [PATCH 0112/1644] Fix signing using legacy Ed448 keys
---
.../openpgp/operator/bc/BcImplProvider.java | 10 ++++++++++
.../jcajce/JcaPGPContentSignerBuilder.java | 9 ++++++++-
.../JcaPGPContentVerifierBuilderProvider.java | 14 ++++++++++----
3 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcImplProvider.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcImplProvider.java
index 02d456ea17..a60d61222b 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcImplProvider.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcImplProvider.java
@@ -33,6 +33,8 @@
import org.bouncycastle.crypto.engines.RFC3394WrapEngine;
import org.bouncycastle.crypto.engines.RSABlindedEngine;
import org.bouncycastle.crypto.engines.TwofishEngine;
+import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
+import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
import org.bouncycastle.crypto.signers.DSADigestSigner;
import org.bouncycastle.crypto.signers.DSASigner;
import org.bouncycastle.crypto.signers.ECDSASigner;
@@ -96,6 +98,14 @@ static Signer createSigner(int keyAlgorithm, int hashAlgorithm, CipherParameters
case PublicKeyAlgorithmTags.ECDSA:
return new DSADigestSigner(new ECDSASigner(), createDigest(hashAlgorithm));
case PublicKeyAlgorithmTags.EDDSA_LEGACY:
+ if (keyParam instanceof Ed25519PrivateKeyParameters || keyParam instanceof Ed25519PublicKeyParameters)
+ {
+ return new EdDsaSigner(new Ed25519Signer(), createDigest(hashAlgorithm));
+ }
+ else
+ {
+ return new EdDsaSigner(new Ed448Signer(new byte[0]), createDigest(hashAlgorithm));
+ }
case PublicKeyAlgorithmTags.Ed25519:
return new EdDsaSigner(new Ed25519Signer(), createDigest(hashAlgorithm));
case PublicKeyAlgorithmTags.Ed448:
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPContentSignerBuilder.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPContentSignerBuilder.java
index de028e85cc..ff437c5c6b 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPContentSignerBuilder.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPContentSignerBuilder.java
@@ -95,7 +95,14 @@ public PGPContentSigner build(final int signatureType, final long keyID, final P
final PGPDigestCalculator digestCalculator = digestCalculatorProviderBuilder.build().get(hashAlgorithm);
final PGPDigestCalculator edDigestCalculator = digestCalculatorProviderBuilder.build().get(hashAlgorithm);
final Signature signature;
- signature = helper.createSignature(keyAlgorithm, hashAlgorithm);
+ if (keyAlgorithm == PublicKeyAlgorithmTags.EDDSA_LEGACY && privateKey.getAlgorithm().equals("Ed448"))
+ {
+ signature = helper.createSignature(PublicKeyAlgorithmTags.Ed448, hashAlgorithm);
+ }
+ else
+ {
+ signature = helper.createSignature(keyAlgorithm, hashAlgorithm);
+ }
try
{
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPContentVerifierBuilderProvider.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPContentVerifierBuilderProvider.java
index de459a43ac..73f33ec310 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPContentVerifierBuilderProvider.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPContentVerifierBuilderProvider.java
@@ -8,8 +8,6 @@
import java.security.SignatureException;
import java.security.interfaces.RSAPublicKey;
-import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
-import org.bouncycastle.bcpg.EdDSAPublicBCPGKey;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.jcajce.io.OutputStreamFactory;
import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
@@ -73,11 +71,19 @@ public JcaPGPContentVerifierBuilder(int keyAlgorithm, int hashAlgorithm)
public PGPContentVerifier build(final PGPPublicKey publicKey)
throws PGPException
{
- final Signature signature = helper.createSignature(keyAlgorithm, hashAlgorithm);
-
final PGPDigestCalculator digestCalculator = digestCalculatorProviderBuilder.build().get(hashAlgorithm);
final PublicKey jcaKey = keyConverter.getPublicKey(publicKey);
+ final Signature signature;
+ if (keyAlgorithm == PublicKeyAlgorithmTags.EDDSA_LEGACY && jcaKey.getAlgorithm().equals("Ed448"))
+ {
+ signature = helper.createSignature(PublicKeyAlgorithmTags.Ed448, hashAlgorithm);
+ }
+ else
+ {
+ signature = helper.createSignature(keyAlgorithm, hashAlgorithm);
+ }
+
try
{
signature.initVerify(jcaKey);
From 80f81a5840ff57e927766ffd2475d272aaff4078 Mon Sep 17 00:00:00 2001
From: Peter Dettman
Date: Thu, 30 May 2024 22:01:20 +0700
Subject: [PATCH 0113/1644] Followup changes for custom DH groups
---
CONTRIBUTORS.html | 1 +
docs/releasenotes.html | 6 ++
.../jsse/provider/NamedGroupInfo.java | 46 ++++++++----
.../jsse/provider/ProvTlsServer.java | 72 ++++++++++++-------
4 files changed, 89 insertions(+), 36 deletions(-)
diff --git a/CONTRIBUTORS.html b/CONTRIBUTORS.html
index 12e559629f..a5d5833ece 100644
--- a/CONTRIBUTORS.html
+++ b/CONTRIBUTORS.html
@@ -547,6 +547,7 @@
Seung Yeon <https://github.com/seungyeonpark> - addition of Memoable method implementations to CertPathValidationContext and CertificatePoliciesValidation.
yuhh0328 <https://github.com/yuhh0328> - initial patch for adding ML-KEM support to TLS.
Jan Oupický <https://github.com/Honzaik> - update to draft 13 of composite PQC signatures.
+Karsten Otto <https://github.com/ottoka> - finished the support for jdk.tls.server.defaultDHEParameters.