From 1df77062356ac1366265a4f99d14a2e29f65d4f4 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 30 May 2024 13:28:51 +0200 Subject: [PATCH 1/3] Add reproducer for Ed448 key generation anomaly --- .../openpgp/test/Ed448AnomalyReproducer.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/test/Ed448AnomalyReproducer.java diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/Ed448AnomalyReproducer.java b/pg/src/test/java/org/bouncycastle/openpgp/test/Ed448AnomalyReproducer.java new file mode 100644 index 0000000000..ca516656c3 --- /dev/null +++ b/pg/src/test/java/org/bouncycastle/openpgp/test/Ed448AnomalyReproducer.java @@ -0,0 +1,55 @@ +package org.bouncycastle.openpgp.test; + +import org.bouncycastle.bcpg.PublicKeyAlgorithmTags; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPKeyPair; +import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyConverter; +import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair; +import org.bouncycastle.util.test.SimpleTest; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PublicKey; +import java.security.Security; +import java.util.Date; + +public class Ed448AnomalyReproducer + extends SimpleTest +{ + @Override + public String getName() { + return "Ed448AnomalyReproducer"; + } + + @Override + public void performTest() + throws Exception + { + JcaPGPKeyConverter jcaPGPKeyConverter = new JcaPGPKeyConverter().setProvider(new BouncyCastleProvider()); + int failure = 0; + for (int total = 0; total < 2000; total++) + { + Date creationDate = new Date(); + KeyPairGenerator gen = KeyPairGenerator.getInstance("Ed448", "BC"); + KeyPair keyPair = gen.generateKeyPair(); + PGPKeyPair jcaPgpPair = new JcaPGPKeyPair(PublicKeyAlgorithmTags.Ed448, keyPair, creationDate); + + try + { + PublicKey pubKey = jcaPGPKeyConverter.getPublicKey(jcaPgpPair.getPublicKey()); // fails in ~1/200 of times + } + catch (PGPException e) + { + failure++; + System.out.println(failure + "/" + total + " (" + (((double)(failure)) / total) * 100 + "%)"); + } + } + } + + public static void main(String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + runTest(new Ed448AnomalyReproducer()); + } +} From d9c1644e43ad0c3cf7663553bd462f8c1d5b0c33 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 30 May 2024 13:29:32 +0200 Subject: [PATCH 2/3] Add debug info for prefix mismatch to BCEdDSAPublicKey constructor --- .../jcajce/provider/asymmetric/edec/BCEdDSAPublicKey.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/BCEdDSAPublicKey.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/BCEdDSAPublicKey.java index dc19a4a000..da91980564 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/BCEdDSAPublicKey.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/BCEdDSAPublicKey.java @@ -14,6 +14,7 @@ import org.bouncycastle.jcajce.interfaces.EdDSAPublicKey; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Properties; +import org.bouncycastle.util.encoders.Hex; public class BCEdDSAPublicKey implements EdDSAPublicKey @@ -54,7 +55,9 @@ else if ((rawData.length - prefixLength) == Ed25519PublicKeyParameters.KEY_SIZE) } else { - throw new InvalidKeySpecException("raw key data not recognised"); + throw new InvalidKeySpecException("raw key data not recognised (prefix mismatch:\n" + + "Expected: " + Hex.toHexString(prefix) + "\n" + + "Got: " + Hex.toHexString(java.util.Arrays.copyOf(rawData, prefixLength)) + ")"); } } From 0ac533a8a266630d89b9f14db43080ebb2f2877e Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 31 May 2024 14:42:27 +0200 Subject: [PATCH 3/3] Fix conversion of Ed25519 and Ed448 keys with leading zeros --- .../openpgp/operator/jcajce/JcaPGPKeyConverter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 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 311db29fb8..c68bc13817 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 @@ -370,7 +370,7 @@ else if (ecdhK.getCurveOID().equals(EdECObjectIdentifiers.id_X448)) BCPGKey key = publicPk.getKey(); if (key instanceof Ed25519PublicBCPGKey) { - return implGetPublicKeyX509(BigIntegers.asUnsignedByteArray(new BigInteger(1, publicPk.getKey().getEncoded())), + return implGetPublicKeyX509(publicPk.getKey().getEncoded(), 0, EdECObjectIdentifiers.id_Ed25519, "EdDSA"); } else @@ -382,7 +382,7 @@ else if (ecdhK.getCurveOID().equals(EdECObjectIdentifiers.id_X448)) // Modern Ed448 (1.3.101.113) case PublicKeyAlgorithmTags.Ed448: { - return implGetPublicKeyX509(BigIntegers.asUnsignedByteArray(new BigInteger(1, publicPk.getKey().getEncoded())), + return implGetPublicKeyX509(publicPk.getKey().getEncoded(), 0, EdECObjectIdentifiers.id_Ed448, "EdDSA"); } case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: