Skip to content

Commit 9f98858

Browse files
committed
Initial implementation of OpenPGP EdDSA and usage of the Curve25519 in ECDH.
1 parent eee5d90 commit 9f98858

13 files changed

+156
-31
lines changed

crypto/src/asn1/gnu/GNUObjectIdentifiers.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ public abstract class GnuObjectIdentifiers
2828
public static readonly DerObjectIdentifier Crc = new DerObjectIdentifier("1.3.6.1.4.1.11591.14"); // CRC algorithms
2929
public static readonly DerObjectIdentifier Crc32 = new DerObjectIdentifier("1.3.6.1.4.1.11591.14.1"); // CRC 32
3030

31-
/** 1.3.6.1.4.1.11591.15 - ellipticCurve */
32-
public static readonly DerObjectIdentifier EllipticCurve = new DerObjectIdentifier("1.3.6.1.4.1.11591.15");
31+
/** 1.3.6.1.4.1.11591.15 - ellipticCurve */
32+
public static readonly DerObjectIdentifier EllipticCurve = new DerObjectIdentifier("1.3.6.1.4.1.11591.15");
3333

34-
public static readonly DerObjectIdentifier Ed25519 = EllipticCurve.Branch("1");
34+
public static readonly DerObjectIdentifier Ed25519 = EllipticCurve.Branch("1");
3535
}
3636
}

crypto/src/asn1/misc/MiscObjectIdentifiers.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ public abstract class MiscObjectIdentifiers
7474
public static readonly DerObjectIdentifier cryptlib_algorithm_blowfish_CFB = cryptlib_algorithm.Branch("1.3");
7575
public static readonly DerObjectIdentifier cryptlib_algorithm_blowfish_OFB = cryptlib_algorithm.Branch("1.4");
7676

77+
// OpenPGP (draft-ietf-openpgp-rfc4880bis-10)
78+
public static readonly DerObjectIdentifier Curve25519 = cryptlib_algorithm.Branch("5.1");
79+
7780
//
7881
// Blake2b
7982
//

crypto/src/bcpg/PublicKeyPacket.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ internal PublicKeyPacket(
4848
key = new ECDHPublicBcpgKey(bcpgIn);
4949
break;
5050
case PublicKeyAlgorithmTag.ECDsa:
51+
case PublicKeyAlgorithmTag.EdDsa:
5152
key = new ECDsaPublicBcpgKey(bcpgIn);
5253
break;
5354
default:

crypto/src/bcpg/SignaturePacket.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ internal SignaturePacket(
147147
signature = new MPInteger[]{ p, g, y };
148148
break;
149149
case PublicKeyAlgorithmTag.ECDsa:
150+
case PublicKeyAlgorithmTag.EdDsa:
150151
MPInteger ecR = new MPInteger(bcpgIn);
151152
MPInteger ecS = new MPInteger(bcpgIn);
152153
signature = new MPInteger[]{ ecR, ecS };

crypto/src/crypto/ec/CustomNamedCurves.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
using Org.BouncyCastle.Asn1;
55
using Org.BouncyCastle.Asn1.GM;
6+
using Org.BouncyCastle.Asn1.Gnu;
7+
using Org.BouncyCastle.Asn1.Misc;
68
using Org.BouncyCastle.Asn1.Sec;
79
using Org.BouncyCastle.Asn1.X9;
810
using Org.BouncyCastle.Math;
@@ -789,7 +791,8 @@ private static void DefineCurveAlias(string name, DerObjectIdentifier oid)
789791

790792
static CustomNamedCurves()
791793
{
792-
DefineCurve("curve25519", Curve25519Holder.Instance);
794+
DefineCurveWithOid("ed25519", GnuObjectIdentifiers.Ed25519, Curve25519Holder.Instance);
795+
DefineCurveWithOid("curve25519", MiscObjectIdentifiers.Curve25519, Curve25519Holder.Instance);
793796

794797
//DefineCurveWithOid("secp112r1", SecObjectIdentifiers.SecP112r1, SecP112R1Holder.Instance);
795798
//DefineCurveWithOid("secp112r2", SecObjectIdentifiers.SecP112r2, SecP112R2Holder.Instance);
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using System;
2+
3+
using Org.BouncyCastle.Math;
4+
5+
namespace Org.BouncyCastle.Crypto.Signers
6+
{
7+
public class EdDsa22519Signer : IDsa
8+
{
9+
private Ed25519Signer signer;
10+
11+
public virtual string AlgorithmName => "EDDSA";
12+
13+
public EdDsa22519Signer()
14+
{
15+
signer = new Ed25519Signer();
16+
}
17+
18+
public virtual void Init(bool forSigning, ICipherParameters parameters)
19+
{
20+
signer.Init(forSigning, parameters);
21+
}
22+
23+
public virtual BigInteger[] GenerateSignature(byte[] message)
24+
{
25+
signer.BlockUpdate(message, 0, message.Length);
26+
byte[] sigBytes = signer.GenerateSignature();
27+
byte[] rBytes = new byte[32];
28+
Array.Copy(sigBytes, rBytes, 32);
29+
byte[] sBytes = new byte[sigBytes.Length - 32];
30+
Array.Copy(sigBytes, 32, sBytes, 0, sigBytes.Length - 32);
31+
BigInteger r = new BigInteger(1, rBytes);
32+
BigInteger s = new BigInteger(1, sBytes);
33+
return new BigInteger[2] { r, s };
34+
}
35+
36+
public virtual bool VerifySignature(byte[] message, BigInteger r, BigInteger s)
37+
{
38+
signer.BlockUpdate(message, 0, message.Length);
39+
byte[] rBytes = r.ToByteArrayUnsigned();
40+
byte[] sBytes = s.ToByteArrayUnsigned();
41+
byte[] sigBytes = new byte[rBytes.Length + sBytes.Length];
42+
Array.Copy(rBytes, sigBytes, rBytes.Length);
43+
Array.Copy(sBytes, 0, sigBytes, rBytes.Length, sBytes.Length);
44+
return signer.VerifySignature(sigBytes);
45+
}
46+
}
47+
}

crypto/src/openpgp/PgpEncryptedDataGenerator.cs

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
using System.Diagnostics;
44
using System.IO;
55

6+
using Org.BouncyCastle.Asn1.Misc;
67
using Org.BouncyCastle.Asn1.X9;
78
using Org.BouncyCastle.Crypto;
89
using Org.BouncyCastle.Crypto.IO;
910
using Org.BouncyCastle.Crypto.Generators;
1011
using Org.BouncyCastle.Crypto.Parameters;
1112
using Org.BouncyCastle.Math;
1213
using Org.BouncyCastle.Math.EC;
14+
using Org.BouncyCastle.Math.EC.Rfc7748;
1315
using Org.BouncyCastle.Security;
1416
using Org.BouncyCastle.Utilities;
1517

@@ -105,7 +107,7 @@ private byte[] EncryptSessionInfo(byte[] sessionInfo, SecureRandom random)
105107
if (pubKey.Algorithm != PublicKeyAlgorithmTag.ECDH)
106108
{
107109
IBufferedCipher c;
108-
switch (pubKey.Algorithm)
110+
switch (pubKey.Algorithm)
109111
{
110112
case PublicKeyAlgorithmTag.RsaEncrypt:
111113
case PublicKeyAlgorithmTag.RsaGeneral:
@@ -119,37 +121,57 @@ private byte[] EncryptSessionInfo(byte[] sessionInfo, SecureRandom random)
119121
throw new PgpException("Can't use DSA for encryption.");
120122
case PublicKeyAlgorithmTag.ECDsa:
121123
throw new PgpException("Can't use ECDSA for encryption.");
124+
case PublicKeyAlgorithmTag.EdDsa:
125+
throw new PgpException("Can't use EdDSA for encryption.");
122126
default:
123127
throw new PgpException("unknown asymmetric algorithm: " + pubKey.Algorithm);
124128
}
125129

126130
AsymmetricKeyParameter akp = pubKey.GetKey();
127-
c.Init(true, new ParametersWithRandom(akp, random));
131+
c.Init(true, new ParametersWithRandom(akp, random));
128132
return c.DoFinal(sessionInfo);
129133
}
130134

131135
ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)pubKey.PublicKeyPacket.Key;
136+
KeyParameter key;
137+
byte[] encodedPublicKey;
132138

133-
// Generate the ephemeral key pair
134-
IAsymmetricCipherKeyPairGenerator gen = GeneratorUtilities.GetKeyPairGenerator("ECDH");
135-
gen.Init(new ECKeyGenerationParameters(ecKey.CurveOid, random));
139+
if (ecKey.CurveOid.Id.Equals(MiscObjectIdentifiers.Curve25519.Id))
140+
{
141+
byte[] privateKey = new byte[X25519.PointSize];
142+
X25519.GeneratePrivateKey(random, privateKey);
143+
byte[] sharedKey = new byte[32];
144+
X25519.CalculateAgreement(privateKey, 0, BigIntegers.AsUnsignedByteArray(ecKey.EncodedPoint), 1, sharedKey, 0);
145+
byte[] publicKey = new byte[X25519.PointSize + 1];
146+
publicKey[0] = 0x40; // compressed point
147+
X25519.GeneratePublicKey(privateKey, 0, publicKey, 1);
148+
encodedPublicKey = publicKey;
149+
key = new KeyParameter(Rfc6637Utilities.CreateKey(pubKey.PublicKeyPacket, sharedKey));
150+
}
151+
else
152+
{
153+
// Generate the ephemeral key pair
154+
IAsymmetricCipherKeyPairGenerator gen = GeneratorUtilities.GetKeyPairGenerator("ECDH");
155+
gen.Init(new ECKeyGenerationParameters(ecKey.CurveOid, random));
136156

137-
AsymmetricCipherKeyPair ephKp = gen.GenerateKeyPair();
138-
ECPrivateKeyParameters ephPriv = (ECPrivateKeyParameters)ephKp.Private;
139-
ECPublicKeyParameters ephPub = (ECPublicKeyParameters)ephKp.Public;
157+
AsymmetricCipherKeyPair ephKp = gen.GenerateKeyPair();
158+
ECPrivateKeyParameters ephPriv = (ECPrivateKeyParameters)ephKp.Private;
159+
ECPublicKeyParameters ephPub = (ECPublicKeyParameters)ephKp.Public;
140160

141-
ECPublicKeyParameters pub = (ECPublicKeyParameters)pubKey.GetKey();
142-
ECPoint S = pub.Q.Multiply(ephPriv.D).Normalize();
161+
ECPublicKeyParameters pub = (ECPublicKeyParameters)pubKey.GetKey();
162+
ECPoint S = pub.Q.Multiply(ephPriv.D).Normalize();
143163

144-
KeyParameter key = new KeyParameter(Rfc6637Utilities.CreateKey(pubKey.PublicKeyPacket, S));
164+
key = new KeyParameter(Rfc6637Utilities.CreateKey(pubKey.PublicKeyPacket, S));
165+
encodedPublicKey = ephPub.Q.GetEncoded(false);
166+
}
145167

146168
IWrapper w = PgpUtilities.CreateWrapper(ecKey.SymmetricKeyAlgorithm);
147169
w.Init(true, new ParametersWithRandom(key, random));
148170

149171
byte[] paddedSessionData = PgpPad.PadSessionData(sessionInfo, sessionKeyObfuscation);
150172

151173
byte[] C = w.Wrap(paddedSessionData, 0, paddedSessionData.Length);
152-
byte[] VB = new MPInteger(new BigInteger(1, ephPub.Q.GetEncoded(false))).GetEncoded();
174+
byte[] VB = new MPInteger(new BigInteger(1, encodedPublicKey)).GetEncoded();
153175

154176
byte[] rv = new byte[VB.Length + 1 + C.Length];
155177

crypto/src/openpgp/PgpPublicKey.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System;
22
using System.Collections;
33
using System.IO;
4-
4+
using Org.BouncyCastle.Asn1.Misc;
55
using Org.BouncyCastle.Asn1.Sec;
66
using Org.BouncyCastle.Asn1.X9;
77
using Org.BouncyCastle.Crypto;
@@ -177,6 +177,10 @@ public PgpPublicKey(
177177
{
178178
bcpgKey = new ECDsaPublicBcpgKey(ecK.PublicKeyParamSet, ecK.Q);
179179
}
180+
else if (algorithm == PublicKeyAlgorithmTag.EdDsa)
181+
{
182+
bcpgKey = new ECDsaPublicBcpgKey(ecK.PublicKeyParamSet, ecK.Q);
183+
}
180184
else
181185
{
182186
throw new PgpException("unknown EC algorithm");
@@ -495,7 +499,12 @@ public AsymmetricKeyParameter GetKey()
495499
case PublicKeyAlgorithmTag.ECDsa:
496500
return GetECKey("ECDSA");
497501
case PublicKeyAlgorithmTag.ECDH:
498-
return GetECKey("ECDH");
502+
if (((ECPublicBcpgKey)publicPk.Key).CurveOid.Id.Equals(MiscObjectIdentifiers.Curve25519.Id))
503+
return new X25519PublicKeyParameters(((ECPublicBcpgKey)publicPk.Key).EncodedPoint.ToByteArrayUnsigned(), 0);
504+
else
505+
return GetECKey("ECDH");
506+
case PublicKeyAlgorithmTag.EdDsa:
507+
return new Ed25519PublicKeyParameters(((ECPublicBcpgKey)publicPk.Key).EncodedPoint.ToByteArrayUnsigned(), 1);
499508
case PublicKeyAlgorithmTag.ElGamalEncrypt:
500509
case PublicKeyAlgorithmTag.ElGamalGeneral:
501510
ElGamalPublicBcpgKey elK = (ElGamalPublicBcpgKey)publicPk.Key;

crypto/src/openpgp/PgpPublicKeyEncryptedData.cs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88
using Org.BouncyCastle.Crypto.Parameters;
99
using Org.BouncyCastle.Math;
1010
using Org.BouncyCastle.Math.EC;
11+
using Org.BouncyCastle.Math.EC.Custom.Djb;
12+
using Org.BouncyCastle.Math.EC.Rfc7748;
1113
using Org.BouncyCastle.Security;
1214
using Org.BouncyCastle.Utilities.IO;
15+
using Org.BouncyCastle.Utilities;
1316

1417
namespace Org.BouncyCastle.Bcpg.OpenPgp
1518
{
@@ -210,12 +213,24 @@ private byte[] RecoverSessionData(PgpPrivateKey privKey)
210213
byte[] keyEnc = new byte[keyLen];
211214
Array.Copy(enc, 2 + pLen + 1, keyEnc, 0, keyEnc.Length);
212215

213-
ECPoint publicPoint = x9Params.Curve.DecodePoint(pEnc);
216+
KeyParameter key;
214217

215-
ECPrivateKeyParameters privKeyParams = (ECPrivateKeyParameters)privKey.Key;
216-
ECPoint S = publicPoint.Multiply(privKeyParams.D).Normalize();
217-
218-
KeyParameter key = new KeyParameter(Rfc6637Utilities.CreateKey(privKey.PublicKeyPacket, S));
218+
if (privKey.Key is X25519PrivateKeyParameters x25519privKeyParams)
219+
{
220+
byte[] sharedKey = new byte[32];
221+
byte[] reversedPrivateKey = new byte[32];
222+
x25519privKeyParams.Encode(reversedPrivateKey, 0);
223+
Array.Reverse((Array)reversedPrivateKey);
224+
X25519.ScalarMult(reversedPrivateKey, 0, pEnc, 1, sharedKey, 0);
225+
key = new KeyParameter(Rfc6637Utilities.CreateKey(privKey.PublicKeyPacket, sharedKey));
226+
}
227+
else
228+
{
229+
ECPoint publicPoint = x9Params.Curve.DecodePoint(pEnc);
230+
ECPrivateKeyParameters privKeyParams = (ECPrivateKeyParameters)privKey.Key;
231+
ECPoint S = publicPoint.Multiply(privKeyParams.D).Normalize();
232+
key = new KeyParameter(Rfc6637Utilities.CreateKey(privKey.PublicKeyPacket, S));
233+
}
219234

220235
IWrapper w = PgpUtilities.CreateWrapper(ecKey.SymmetricKeyAlgorithm);
221236
w.Init(false, key);

crypto/src/openpgp/PgpSecretKey.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System;
22
using System.Collections;
33
using System.IO;
4-
4+
using Org.BouncyCastle.Asn1.Misc;
55
using Org.BouncyCastle.Asn1.X9;
66
using Org.BouncyCastle.Crypto;
77
using Org.BouncyCastle.Crypto.Generators;
@@ -674,11 +674,17 @@ internal PgpPrivateKey DoExtractPrivateKey(byte[] rawPassPhrase, bool clearPassP
674674
privateKey = new DsaPrivateKeyParameters(dsaPriv.X, dsaParams);
675675
break;
676676
case PublicKeyAlgorithmTag.ECDH:
677-
privateKey = GetECKey("ECDH", bcpgIn);
677+
if (((ECPublicBcpgKey)secret.PublicKeyPacket.Key).CurveOid.Id.Equals(MiscObjectIdentifiers.Curve25519.Id))
678+
privateKey = new X25519PrivateKeyParameters(new ECSecretBcpgKey(bcpgIn).X.ToByteArrayUnsigned(), 0);
679+
else
680+
privateKey = GetECKey("ECDH", bcpgIn);
678681
break;
679682
case PublicKeyAlgorithmTag.ECDsa:
680683
privateKey = GetECKey("ECDSA", bcpgIn);
681684
break;
685+
case PublicKeyAlgorithmTag.EdDsa:
686+
privateKey = new Ed25519PrivateKeyParameters(new ECSecretBcpgKey(bcpgIn).X.ToByteArrayUnsigned(), 0);
687+
break;
682688
case PublicKeyAlgorithmTag.ElGamalEncrypt:
683689
case PublicKeyAlgorithmTag.ElGamalGeneral:
684690
ElGamalPublicBcpgKey elPub = (ElGamalPublicBcpgKey)pubPk.Key;

0 commit comments

Comments
 (0)