Skip to content

Commit

Permalink
add EdDSA support to bcpg
Browse files Browse the repository at this point in the history
  • Loading branch information
Valodim committed Apr 29, 2017
1 parent eac72a1 commit 1c44d1e
Show file tree
Hide file tree
Showing 12 changed files with 265 additions and 11 deletions.
63 changes: 63 additions & 0 deletions pg/src/main/java/org/bouncycastle/bcpg/EdDSAPublicBCPGKey.java
@@ -0,0 +1,63 @@
package org.bouncycastle.bcpg;

import java.io.IOException;
import java.math.BigInteger;

import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.BigIntegers;


/**
* base class for an ECDSA Public Key.
*/
public class EdDSAPublicBCPGKey
extends ECPublicBCPGKey
{
private static final ASN1ObjectIdentifier OID_Ed25519 = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.15.1");

/**
* @param in the stream to read the packet from.
*/
protected EdDSAPublicBCPGKey(
BCPGInputStream in)
throws IOException
{
super(in);

if (!OID_Ed25519.equals(oid))
{
throw new IOException("Invalid curve oid for EdDSA key!");
}
}

public EdDSAPublicBCPGKey(
BigInteger encodedPoint)
throws IOException
{
super(OID_Ed25519, encodedPoint);
}

public static EdDSAPublicBCPGKey fromEdDSAEncodedPoint(
byte[] eddsaEncodedPoint)
throws IOException
{
byte[] openpgpEncodedPoint = new byte[eddsaEncodedPoint.length + 1];
openpgpEncodedPoint[0] = 0x40;
System.arraycopy(eddsaEncodedPoint, 0, openpgpEncodedPoint, 1, eddsaEncodedPoint.length);
return new EdDSAPublicBCPGKey(BigIntegers.fromUnsignedByteArray(openpgpEncodedPoint));
}

public byte[] getEdDSAEncodedPoint()
{
BigInteger encodedPoint = getEncodedPoint();
byte[] pointData = BigIntegers.asUnsignedByteArray(encodedPoint);
if (pointData[0] != 0x40)
{
throw new IllegalStateException("Invalid point format in EdDSA key!");
}
return Arrays.copyOfRange(pointData, 1, pointData.length);
}

}
94 changes: 94 additions & 0 deletions pg/src/main/java/org/bouncycastle/bcpg/EdDSASecretBCPGKey.java
@@ -0,0 +1,94 @@
package org.bouncycastle.bcpg;

import java.io.IOException;
import java.math.BigInteger;

import org.bouncycastle.util.BigIntegers;


/**
* base class for an EC Secret Key.
*/
public class EdDSASecretBCPGKey
extends BCPGObject
implements BCPGKey
{
MPInteger x;

/**
* @param in
* @throws IOException
*/
public EdDSASecretBCPGKey(
BCPGInputStream in)
throws IOException
{
this.x = new MPInteger(in);
}

/**
* @param x
*/
public EdDSASecretBCPGKey(
BigInteger x)
{
this.x = new MPInteger(x);
}

/**
* @param seed
*/
public EdDSASecretBCPGKey(
byte[] seed)
{
BigInteger x = BigIntegers.fromUnsignedByteArray(seed);
this.x = new MPInteger(x);
}

/**
* return "PGP"
*
* @see BCPGKey#getFormat()
*/
public String getFormat()
{
return "PGP";
}

/**
* return the standard PGP encoding of the key.
*
* @see BCPGKey#getEncoded()
*/
public byte[] getEncoded()
{
try
{
return super.getEncoded();
}
catch (IOException e)
{
return null;
}
}

public void encode(
BCPGOutputStream out)
throws IOException
{
out.writeObject(x);
}

/**
* @return x
*/
public BigInteger getX()
{
return x.getValue();
}

public byte[] getSeed()
{
return BigIntegers.asUnsignedByteArray(x.getValue());
}
}
Expand Up @@ -18,6 +18,7 @@ public interface PublicKeyAlgorithmTags
public static final int ECDSA = 19; // Reserved for ECDSA
public static final int ELGAMAL_GENERAL = 20; // Elgamal (Encrypt or Sign)
public static final int DIFFIE_HELLMAN = 21; // Reserved for Diffie-Hellman (X9.42, as defined for IETF-S/MIME)
public static final int EDDSA = 22; // EdDSA https://tools.ietf.org/html/draft-koch-eddsa-for-openpgp-04

public static final int EXPERIMENTAL_1 = 100;
public static final int EXPERIMENTAL_2 = 101;
Expand Down
3 changes: 3 additions & 0 deletions pg/src/main/java/org/bouncycastle/bcpg/PublicKeyPacket.java
Expand Up @@ -70,6 +70,9 @@ public class PublicKeyPacket

break;
}
case EDDSA:
key = new EdDSAPublicBCPGKey(in);
break;
default:
key = new OpaquePublicBCPGKey(in);
break;
Expand Down
Expand Up @@ -159,6 +159,7 @@ else if (p instanceof SignatureCreationTime)
signature[2] = y;
break;
case ECDSA:
case EDDSA:
MPInteger ecR = new MPInteger(in);
MPInteger ecS = new MPInteger(in);

Expand Down
15 changes: 14 additions & 1 deletion pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKey.java
Expand Up @@ -15,6 +15,7 @@
import org.bouncycastle.bcpg.ContainedPacket;
import org.bouncycastle.bcpg.DSAPublicBCPGKey;
import org.bouncycastle.bcpg.ECPublicBCPGKey;
import org.bouncycastle.bcpg.EdDSAPublicBCPGKey;
import org.bouncycastle.bcpg.ElGamalPublicBCPGKey;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.bcpg.PublicKeyPacket;
Expand All @@ -23,6 +24,8 @@
import org.bouncycastle.bcpg.TrustPacket;
import org.bouncycastle.bcpg.UserAttributePacket;
import org.bouncycastle.bcpg.UserIDPacket;
import org.bouncycastle.jcajce.provider.asymmetric.eddsa.spec.EdDSANamedCurveSpec;
import org.bouncycastle.jcajce.provider.asymmetric.eddsa.spec.EdDSANamedCurveTable;
import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
import org.bouncycastle.util.Arrays;

Expand Down Expand Up @@ -86,7 +89,17 @@ else if (key instanceof ElGamalPublicBCPGKey)
}
else if (key instanceof ECPublicBCPGKey)
{
this.keyStrength = ECNamedCurveTable.getByOID(((ECPublicBCPGKey)key).getCurveOID()).getCurve().getFieldSize();
if (key instanceof EdDSAPublicBCPGKey)
{
EdDSANamedCurveSpec eddsaSpec =
EdDSANamedCurveTable.getByName("ed25519");
this.keyStrength = eddsaSpec.getCurve().getField().getb();
}
else
{
this.keyStrength =
ECNamedCurveTable.getByOID(((ECPublicBCPGKey) key).getCurveOID()).getCurve().getFieldSize();
}
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions pg/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java
Expand Up @@ -19,6 +19,7 @@
import org.bouncycastle.bcpg.DSASecretBCPGKey;
import org.bouncycastle.bcpg.ECDSAPublicBCPGKey;
import org.bouncycastle.bcpg.ECSecretBCPGKey;
import org.bouncycastle.bcpg.EdDSASecretBCPGKey;
import org.bouncycastle.bcpg.ElGamalSecretBCPGKey;
import org.bouncycastle.bcpg.HashAlgorithmTags;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
Expand Down Expand Up @@ -528,6 +529,10 @@ public PGPPrivateKey extractPrivateKey(
ECSecretBCPGKey ecPriv = new ECSecretBCPGKey(in);

return new PGPPrivateKey(this.getKeyID(), pubPk, ecPriv);
case PGPPublicKey.EDDSA:
EdDSASecretBCPGKey edPriv = new EdDSASecretBCPGKey(in);

return new PGPPrivateKey(this.getKeyID(), pubPk, edPriv);
default:
throw new PGPException("unknown public key algorithm encountered");
}
Expand Down
28 changes: 21 additions & 7 deletions pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java
Expand Up @@ -11,6 +11,7 @@
import org.bouncycastle.bcpg.BCPGInputStream;
import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.bcpg.MPInteger;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.bcpg.SignaturePacket;
import org.bouncycastle.bcpg.SignatureSubpacket;
import org.bouncycastle.bcpg.TrustPacket;
Expand Down Expand Up @@ -485,17 +486,30 @@ public byte[] getSignature()
}
else
{
try
if (sigPck.getKeyAlgorithm() == PublicKeyAlgorithmTags.EDDSA)
{
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(new ASN1Integer(sigValues[0].getValue()));
v.add(new ASN1Integer(sigValues[1].getValue()));
signature = new byte[64];
byte[] bytes;
bytes = BigIntegers.asUnsignedByteArray(sigValues[0].getValue());
System.arraycopy(bytes, 0, signature, 0, bytes.length);

signature = new DERSequence(v).getEncoded();
bytes = BigIntegers.asUnsignedByteArray(sigValues[1].getValue());
System.arraycopy(bytes, 0, signature, 32, bytes.length);
}
catch (IOException e)
else
{
throw new PGPException("exception encoding DSA sig.", e);
try
{
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(new ASN1Integer(sigValues[0].getValue()));
v.add(new ASN1Integer(sigValues[1].getValue()));

signature = new DERSequence(v).getEncoded();
}
catch (IOException e)
{
throw new PGPException("exception encoding DSA sig.", e);
}
}
}
}
Expand Down
Expand Up @@ -17,6 +17,7 @@
import org.bouncycastle.bcpg.sig.SignatureCreationTime;
import org.bouncycastle.openpgp.operator.PGPContentSigner;
import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.util.Strings;

/**
Expand Down Expand Up @@ -262,11 +263,20 @@ public PGPSignature generate()
sigValues = new MPInteger[1];
sigValues[0] = new MPInteger(new BigInteger(1, contentSigner.getSignature()));
}
else if (contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.EDDSA)
{
byte[] sig = contentSigner.getSignature();

sigValues = new MPInteger[2];

sigValues[0] = new MPInteger(BigIntegers.fromUnsignedByteArray(sig, 0, 32));
sigValues[1] = new MPInteger(BigIntegers.fromUnsignedByteArray(sig, 32, 32));
}
else
{
{
sigValues = PGPUtil.dsaSigToMpi(contentSigner.getSignature());
}

byte[] digest = contentSigner.getDigest();
byte[] fingerPrint = new byte[2];

Expand Down
Expand Up @@ -43,6 +43,8 @@ protected PublicKeyKeyEncryptionMethodGenerator(
throw new IllegalArgumentException("Can't use DSA for encryption.");
case PGPPublicKey.ECDSA:
throw new IllegalArgumentException("Can't use ECDSA for encryption.");
case PGPPublicKey.EDDSA:
throw new IllegalArgumentException("Can't use EdDSA for encryption.");
default:
throw new IllegalArgumentException("unknown asymmetric algorithm: " + pubKey.getAlgorithm());
}
Expand Down

0 comments on commit 1c44d1e

Please sign in to comment.