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);
+ }
}
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..96ecc77a44
--- /dev/null
+++ b/pg/src/test/java/org/bouncycastle/openpgp/test/Curve25519PrivateKeyEncodingTest.java
@@ -0,0 +1,195 @@
+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("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("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("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("Decoded X25519 key MUST match original raw key (JCE implementation)",
+ 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("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("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("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("Decoded X25519 key MUST match original raw key (BC implementation)",
+ 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)