Skip to content

Commit 696f86a

Browse files
committed
Support for Ed25519 Host- and Private-Keys
1 parent 56d2c9e commit 696f86a

File tree

6 files changed

+554
-1
lines changed

6 files changed

+554
-1
lines changed

src/Renci.SshNet/ConnectionInfo.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy
378378

379379
HostKeyAlgorithms = new Dictionary<string, Func<byte[], KeyHostAlgorithm>>
380380
{
381+
{"ssh-ed25519", data => new KeyHostAlgorithm("ssh-ed25519", new ED25519Key(), data)},
381382
#if FEATURE_ECDSA
382383
{"ecdsa-sha2-nistp256", data => new KeyHostAlgorithm("ecdsa-sha2-nistp256", new EcdsaKey(), data)},
383384
{"ecdsa-sha2-nistp384", data => new KeyHostAlgorithm("ecdsa-sha2-nistp384", new EcdsaKey(), data)},

src/Renci.SshNet/Renci.SshNet.csproj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@
376376
<Compile Include="Security\BouncyCastle\src\util\Strings.cs" />
377377
<Compile Include="Security\BouncyCastle\src\util\Times.cs" />
378378
<Compile Include="Security\Chaos.NaCl\CryptoBytes.cs" />
379+
<Compile Include="Security\Chaos.NaCl\Ed25519.cs" />
379380
<Compile Include="Security\Chaos.NaCl\Internal\Array16.cs" />
380381
<Compile Include="Security\Chaos.NaCl\Internal\Array8.cs" />
381382
<Compile Include="Security\Chaos.NaCl\Internal\ByteIntegerConverter.cs" />
@@ -435,8 +436,11 @@
435436
<Compile Include="Security\Chaos.NaCl\Internal\Sha512Internal.cs" />
436437
<Compile Include="Security\Chaos.NaCl\MontgomeryCurve25519.cs" />
437438
<Compile Include="Security\Chaos.NaCl\Sha512.cs" />
439+
<Compile Include="Security\Cryptography\Ciphers\Ed255129.cs" />
440+
<Compile Include="Security\Cryptography\ED25519DigitalSignature.cs" />
438441
<Compile Include="Security\Cryptography\EcdsaDigitalSignature.cs" />
439442
<Compile Include="Security\Cryptography\EcdsaKey.cs" />
443+
<Compile Include="Security\Cryptography\ED25519Key.cs" />
440444
<Compile Include="Security\Cryptography\HMACMD5.cs" />
441445
<Compile Include="Security\Cryptography\HMACSHA1.cs" />
442446
<Compile Include="Security\Cryptography\HMACSHA256.cs" />
@@ -753,4 +757,4 @@
753757
<Target Name="AfterBuild">
754758
</Target>
755759
-->
756-
</Project>
760+
</Project>
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
using System;
2+
using Chaos.NaCl.Internal.Ed25519Ref10;
3+
4+
namespace Chaos.NaCl
5+
{
6+
public static class Ed25519
7+
{
8+
public static readonly int PublicKeySizeInBytes = 32;
9+
public static readonly int SignatureSizeInBytes = 64;
10+
public static readonly int ExpandedPrivateKeySizeInBytes = 32 * 2;
11+
public static readonly int PrivateKeySeedSizeInBytes = 32;
12+
public static readonly int SharedKeySizeInBytes = 32;
13+
14+
public static bool Verify(ArraySegment<byte> signature, ArraySegment<byte> message, ArraySegment<byte> publicKey)
15+
{
16+
if (signature.Count != SignatureSizeInBytes)
17+
throw new ArgumentException(string.Format("Signature size must be {0}", SignatureSizeInBytes), "signature.Count");
18+
if (publicKey.Count != PublicKeySizeInBytes)
19+
throw new ArgumentException(string.Format("Public key size must be {0}", PublicKeySizeInBytes), "publicKey.Count");
20+
return Ed25519Operations.crypto_sign_verify(signature.Array, signature.Offset, message.Array, message.Offset, message.Count, publicKey.Array, publicKey.Offset);
21+
}
22+
23+
public static bool Verify(byte[] signature, byte[] message, byte[] publicKey)
24+
{
25+
if (signature == null)
26+
throw new ArgumentNullException("signature");
27+
if (message == null)
28+
throw new ArgumentNullException("message");
29+
if (publicKey == null)
30+
throw new ArgumentNullException("publicKey");
31+
if (signature.Length != SignatureSizeInBytes)
32+
throw new ArgumentException(string.Format("Signature size must be {0}", SignatureSizeInBytes), "signature.Length");
33+
if (publicKey.Length != PublicKeySizeInBytes)
34+
throw new ArgumentException(string.Format("Public key size must be {0}", PublicKeySizeInBytes), "publicKey.Length");
35+
return Ed25519Operations.crypto_sign_verify(signature, 0, message, 0, message.Length, publicKey, 0);
36+
}
37+
38+
public static void Sign(ArraySegment<byte> signature, ArraySegment<byte> message, ArraySegment<byte> expandedPrivateKey)
39+
{
40+
if (signature.Array == null)
41+
throw new ArgumentNullException("signature.Array");
42+
if (signature.Count != SignatureSizeInBytes)
43+
throw new ArgumentException("signature.Count");
44+
if (expandedPrivateKey.Array == null)
45+
throw new ArgumentNullException("expandedPrivateKey.Array");
46+
if (expandedPrivateKey.Count != ExpandedPrivateKeySizeInBytes)
47+
throw new ArgumentException("expandedPrivateKey.Count");
48+
if (message.Array == null)
49+
throw new ArgumentNullException("message.Array");
50+
Ed25519Operations.crypto_sign2(signature.Array, signature.Offset, message.Array, message.Offset, message.Count, expandedPrivateKey.Array, expandedPrivateKey.Offset);
51+
}
52+
53+
public static byte[] Sign(byte[] message, byte[] expandedPrivateKey)
54+
{
55+
var signature = new byte[SignatureSizeInBytes];
56+
Sign(new ArraySegment<byte>(signature), new ArraySegment<byte>(message), new ArraySegment<byte>(expandedPrivateKey));
57+
return signature;
58+
}
59+
60+
public static byte[] PublicKeyFromSeed(byte[] privateKeySeed)
61+
{
62+
byte[] privateKey;
63+
byte[] publicKey;
64+
KeyPairFromSeed(out publicKey, out privateKey, privateKeySeed);
65+
CryptoBytes.Wipe(privateKey);
66+
return publicKey;
67+
}
68+
69+
public static byte[] ExpandedPrivateKeyFromSeed(byte[] privateKeySeed)
70+
{
71+
byte[] privateKey;
72+
byte[] publicKey;
73+
KeyPairFromSeed(out publicKey, out privateKey, privateKeySeed);
74+
CryptoBytes.Wipe(publicKey);
75+
return privateKey;
76+
}
77+
78+
public static void KeyPairFromSeed(out byte[] publicKey, out byte[] expandedPrivateKey, byte[] privateKeySeed)
79+
{
80+
if (privateKeySeed == null)
81+
throw new ArgumentNullException("privateKeySeed");
82+
if (privateKeySeed.Length != PrivateKeySeedSizeInBytes)
83+
throw new ArgumentException("privateKeySeed");
84+
var pk = new byte[PublicKeySizeInBytes];
85+
var sk = new byte[ExpandedPrivateKeySizeInBytes];
86+
Ed25519Operations.crypto_sign_keypair(pk, 0, sk, 0, privateKeySeed, 0);
87+
publicKey = pk;
88+
expandedPrivateKey = sk;
89+
}
90+
91+
public static void KeyPairFromSeed(ArraySegment<byte> publicKey, ArraySegment<byte> expandedPrivateKey, ArraySegment<byte> privateKeySeed)
92+
{
93+
if (publicKey.Array == null)
94+
throw new ArgumentNullException("publicKey.Array");
95+
if (expandedPrivateKey.Array == null)
96+
throw new ArgumentNullException("expandedPrivateKey.Array");
97+
if (privateKeySeed.Array == null)
98+
throw new ArgumentNullException("privateKeySeed.Array");
99+
if (publicKey.Count != PublicKeySizeInBytes)
100+
throw new ArgumentException("publicKey.Count");
101+
if (expandedPrivateKey.Count != ExpandedPrivateKeySizeInBytes)
102+
throw new ArgumentException("expandedPrivateKey.Count");
103+
if (privateKeySeed.Count != PrivateKeySeedSizeInBytes)
104+
throw new ArgumentException("privateKeySeed.Count");
105+
Ed25519Operations.crypto_sign_keypair(
106+
publicKey.Array, publicKey.Offset,
107+
expandedPrivateKey.Array, expandedPrivateKey.Offset,
108+
privateKeySeed.Array, privateKeySeed.Offset);
109+
}
110+
111+
[Obsolete("Needs more testing")]
112+
public static byte[] KeyExchange(byte[] publicKey, byte[] privateKey)
113+
{
114+
var sharedKey = new byte[SharedKeySizeInBytes];
115+
KeyExchange(new ArraySegment<byte>(sharedKey), new ArraySegment<byte>(publicKey), new ArraySegment<byte>(privateKey));
116+
return sharedKey;
117+
}
118+
119+
[Obsolete("Needs more testing")]
120+
public static void KeyExchange(ArraySegment<byte> sharedKey, ArraySegment<byte> publicKey, ArraySegment<byte> privateKey)
121+
{
122+
if (sharedKey.Array == null)
123+
throw new ArgumentNullException("sharedKey.Array");
124+
if (publicKey.Array == null)
125+
throw new ArgumentNullException("publicKey.Array");
126+
if (privateKey.Array == null)
127+
throw new ArgumentNullException("privateKey");
128+
if (sharedKey.Count != 32)
129+
throw new ArgumentException("sharedKey.Count != 32");
130+
if (publicKey.Count != 32)
131+
throw new ArgumentException("publicKey.Count != 32");
132+
if (privateKey.Count != 64)
133+
throw new ArgumentException("privateKey.Count != 64");
134+
135+
FieldElement montgomeryX, edwardsY, edwardsZ, sharedMontgomeryX;
136+
FieldOperations.fe_frombytes(out edwardsY, publicKey.Array, publicKey.Offset);
137+
FieldOperations.fe_1(out edwardsZ);
138+
MontgomeryCurve25519.EdwardsToMontgomeryX(out montgomeryX, ref edwardsY, ref edwardsZ);
139+
byte[] h = Sha512.Hash(privateKey.Array, privateKey.Offset, 32);//ToDo: Remove alloc
140+
ScalarOperations.sc_clamp(h, 0);
141+
MontgomeryOperations.scalarmult(out sharedMontgomeryX, h, 0, ref montgomeryX);
142+
CryptoBytes.Wipe(h);
143+
FieldOperations.fe_tobytes(sharedKey.Array, sharedKey.Offset, ref sharedMontgomeryX);
144+
MontgomeryCurve25519.KeyExchangeOutputHashNaCl(sharedKey.Array, sharedKey.Offset);
145+
}
146+
}
147+
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
using Chaos.NaCl;
2+
using Chaos.NaCl.Internal.Ed25519Ref10;
3+
using System;
4+
#pragma warning disable CS1591 // Fehledes XML-Kommentar für öffentlich sichtbaren Typ oder Element
5+
namespace Renci.SshNet.Security.Cryptography.Ciphers
6+
{
7+
public class Ed25519
8+
{
9+
public static readonly int PublicKeySizeInBytes = 32;
10+
public static readonly int SignatureSizeInBytes = 64;
11+
public static readonly int ExpandedPrivateKeySizeInBytes = 32 * 2;
12+
public static readonly int PrivateKeySeedSizeInBytes = 32;
13+
public static readonly int SharedKeySizeInBytes = 32;
14+
15+
public static bool Verify(ArraySegment<byte> signature, ArraySegment<byte> message, ArraySegment<byte> publicKey)
16+
{
17+
if (signature.Count != SignatureSizeInBytes)
18+
throw new ArgumentException(string.Format("Signature size must be {0}", SignatureSizeInBytes), "signature.Count");
19+
if (publicKey.Count != PublicKeySizeInBytes)
20+
throw new ArgumentException(string.Format("Public key size must be {0}", PublicKeySizeInBytes), "publicKey.Count");
21+
return Ed25519Operations.crypto_sign_verify(signature.Array, signature.Offset, message.Array, message.Offset, message.Count, publicKey.Array, publicKey.Offset);
22+
}
23+
24+
public static bool Verify(byte[] signature, byte[] message, byte[] publicKey)
25+
{
26+
if (signature == null)
27+
throw new ArgumentNullException("signature");
28+
if (message == null)
29+
throw new ArgumentNullException("message");
30+
if (publicKey == null)
31+
throw new ArgumentNullException("publicKey");
32+
if (signature.Length != SignatureSizeInBytes)
33+
throw new ArgumentException(string.Format("Signature size must be {0}", SignatureSizeInBytes), "signature.Length");
34+
if (publicKey.Length != PublicKeySizeInBytes)
35+
throw new ArgumentException(string.Format("Public key size must be {0}", PublicKeySizeInBytes), "publicKey.Length");
36+
return Ed25519Operations.crypto_sign_verify(signature, 0, message, 0, message.Length, publicKey, 0);
37+
}
38+
39+
public static void Sign(ArraySegment<byte> signature, ArraySegment<byte> message, ArraySegment<byte> expandedPrivateKey)
40+
{
41+
if (signature.Array == null)
42+
throw new ArgumentNullException("signature.Array");
43+
if (signature.Count != SignatureSizeInBytes)
44+
throw new ArgumentException("signature.Count");
45+
if (expandedPrivateKey.Array == null)
46+
throw new ArgumentNullException("expandedPrivateKey.Array");
47+
if (expandedPrivateKey.Count != ExpandedPrivateKeySizeInBytes)
48+
throw new ArgumentException("expandedPrivateKey.Count");
49+
if (message.Array == null)
50+
throw new ArgumentNullException("message.Array");
51+
Ed25519Operations.crypto_sign2(signature.Array, signature.Offset, message.Array, message.Offset, message.Count, expandedPrivateKey.Array, expandedPrivateKey.Offset);
52+
}
53+
54+
public static byte[] Sign(byte[] message, byte[] expandedPrivateKey)
55+
{
56+
var signature = new byte[SignatureSizeInBytes];
57+
Sign(new ArraySegment<byte>(signature), new ArraySegment<byte>(message), new ArraySegment<byte>(expandedPrivateKey));
58+
return signature;
59+
}
60+
61+
public static byte[] PublicKeyFromSeed(byte[] privateKeySeed)
62+
{
63+
byte[] privateKey;
64+
byte[] publicKey;
65+
KeyPairFromSeed(out publicKey, out privateKey, privateKeySeed);
66+
CryptoBytes.Wipe(privateKey);
67+
return publicKey;
68+
}
69+
70+
public static byte[] ExpandedPrivateKeyFromSeed(byte[] privateKeySeed)
71+
{
72+
byte[] privateKey;
73+
byte[] publicKey;
74+
KeyPairFromSeed(out publicKey, out privateKey, privateKeySeed);
75+
CryptoBytes.Wipe(publicKey);
76+
return privateKey;
77+
}
78+
79+
public static void KeyPairFromSeed(out byte[] publicKey, out byte[] expandedPrivateKey, byte[] privateKeySeed)
80+
{
81+
if (privateKeySeed == null)
82+
throw new ArgumentNullException("privateKeySeed");
83+
if (privateKeySeed.Length != PrivateKeySeedSizeInBytes)
84+
throw new ArgumentException("privateKeySeed");
85+
var pk = new byte[PublicKeySizeInBytes];
86+
var sk = new byte[ExpandedPrivateKeySizeInBytes];
87+
Ed25519Operations.crypto_sign_keypair(pk, 0, sk, 0, privateKeySeed, 0);
88+
publicKey = pk;
89+
expandedPrivateKey = sk;
90+
}
91+
92+
public static void KeyPairFromSeed(ArraySegment<byte> publicKey, ArraySegment<byte> expandedPrivateKey, ArraySegment<byte> privateKeySeed)
93+
{
94+
if (publicKey.Array == null)
95+
throw new ArgumentNullException("publicKey.Array");
96+
if (expandedPrivateKey.Array == null)
97+
throw new ArgumentNullException("expandedPrivateKey.Array");
98+
if (privateKeySeed.Array == null)
99+
throw new ArgumentNullException("privateKeySeed.Array");
100+
if (publicKey.Count != PublicKeySizeInBytes)
101+
throw new ArgumentException("publicKey.Count");
102+
if (expandedPrivateKey.Count != ExpandedPrivateKeySizeInBytes)
103+
throw new ArgumentException("expandedPrivateKey.Count");
104+
if (privateKeySeed.Count != PrivateKeySeedSizeInBytes)
105+
throw new ArgumentException("privateKeySeed.Count");
106+
Ed25519Operations.crypto_sign_keypair(
107+
publicKey.Array, publicKey.Offset,
108+
expandedPrivateKey.Array, expandedPrivateKey.Offset,
109+
privateKeySeed.Array, privateKeySeed.Offset);
110+
}
111+
112+
[Obsolete("Needs more testing")]
113+
public static byte[] KeyExchange(byte[] publicKey, byte[] privateKey)
114+
{
115+
var sharedKey = new byte[SharedKeySizeInBytes];
116+
KeyExchange(new ArraySegment<byte>(sharedKey), new ArraySegment<byte>(publicKey), new ArraySegment<byte>(privateKey));
117+
return sharedKey;
118+
}
119+
120+
[Obsolete("Needs more testing")]
121+
public static void KeyExchange(ArraySegment<byte> sharedKey, ArraySegment<byte> publicKey, ArraySegment<byte> privateKey)
122+
{
123+
if (sharedKey.Array == null)
124+
throw new ArgumentNullException("sharedKey.Array");
125+
if (publicKey.Array == null)
126+
throw new ArgumentNullException("publicKey.Array");
127+
if (privateKey.Array == null)
128+
throw new ArgumentNullException("privateKey");
129+
if (sharedKey.Count != 32)
130+
throw new ArgumentException("sharedKey.Count != 32");
131+
if (publicKey.Count != 32)
132+
throw new ArgumentException("publicKey.Count != 32");
133+
if (privateKey.Count != 64)
134+
throw new ArgumentException("privateKey.Count != 64");
135+
136+
FieldElement montgomeryX, edwardsY, edwardsZ, sharedMontgomeryX;
137+
FieldOperations.fe_frombytes(out edwardsY, publicKey.Array, publicKey.Offset);
138+
FieldOperations.fe_1(out edwardsZ);
139+
MontgomeryCurve25519.EdwardsToMontgomeryX(out montgomeryX, ref edwardsY, ref edwardsZ);
140+
byte[] h = Sha512.Hash(privateKey.Array, privateKey.Offset, 32);//ToDo: Remove alloc
141+
ScalarOperations.sc_clamp(h, 0);
142+
MontgomeryOperations.scalarmult(out sharedMontgomeryX, h, 0, ref montgomeryX);
143+
CryptoBytes.Wipe(h);
144+
FieldOperations.fe_tobytes(sharedKey.Array, sharedKey.Offset, ref sharedMontgomeryX);
145+
MontgomeryCurve25519.KeyExchangeOutputHashNaCl(sharedKey.Array, sharedKey.Offset);
146+
}
147+
}
148+
}
149+
#pragma warning restore CS1591 // Fehledes XML-Kommentar für öffentlich sichtbaren Typ oder Element

0 commit comments

Comments
 (0)