Skip to content

Commit

Permalink
Support for Ed25519 Host- and Private-Keys
Browse files Browse the repository at this point in the history
  • Loading branch information
darinkes committed Dec 5, 2018
1 parent c1b4376 commit 5145afb
Show file tree
Hide file tree
Showing 5 changed files with 256 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/Renci.SshNet/ConnectionInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy

HostKeyAlgorithms = new Dictionary<string, Func<byte[], KeyHostAlgorithm>>
{
{"ssh-ed25519", data => new KeyHostAlgorithm("ssh-ed25519", new ED25519Key(), data)},
#if FEATURE_ECDSA
{"ecdsa-sha2-nistp256", data => new KeyHostAlgorithm("ecdsa-sha2-nistp256", new EcdsaKey(), data)},
{"ecdsa-sha2-nistp384", data => new KeyHostAlgorithm("ecdsa-sha2-nistp384", new EcdsaKey(), data)},
Expand Down
2 changes: 2 additions & 0 deletions src/Renci.SshNet/Renci.SshNet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,10 @@
<Compile Include="Security\Chaos.NaCl\Internal\Sha512Internal.cs" />
<Compile Include="Security\Chaos.NaCl\MontgomeryCurve25519.cs" />
<Compile Include="Security\Chaos.NaCl\Sha512.cs" />
<Compile Include="Security\Cryptography\ED25519DigitalSignature.cs" />
<Compile Include="Security\Cryptography\EcdsaDigitalSignature.cs" />
<Compile Include="Security\Cryptography\EcdsaKey.cs" />
<Compile Include="Security\Cryptography\ED25519Key.cs" />
<Compile Include="Security\Cryptography\HMACMD5.cs" />
<Compile Include="Security\Cryptography\HMACSHA1.cs" />
<Compile Include="Security\Cryptography\HMACSHA256.cs" />
Expand Down
93 changes: 93 additions & 0 deletions src/Renci.SshNet/Security/Cryptography/ED25519DigitalSignature.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using System;
using Renci.SshNet.Common;
using Renci.SshNet.Security.Chaos.NaCl;

namespace Renci.SshNet.Security.Cryptography
{
/// <summary>
/// Implements ECDSA digital signature algorithm.
/// </summary>
public class ED25519DigitalSignature : DigitalSignature, IDisposable
{
private readonly ED25519Key _key;

/// <summary>
/// Initializes a new instance of the <see cref="ED25519DigitalSignature" /> class.
/// </summary>
/// <param name="key">The ED25519Key key.</param>
/// <exception cref="ArgumentNullException"><paramref name="key"/> is <c>null</c>.</exception>
public ED25519DigitalSignature(ED25519Key key)
{
if (key == null)
throw new ArgumentNullException("key");

_key = key;
}

/// <summary>
/// Verifies the signature.
/// </summary>
/// <param name="input">The input.</param>
/// <param name="signature">The signature.</param>
/// <returns>
/// <c>true</c> if signature was successfully verified; otherwise <c>false</c>.
/// </returns>
/// <exception cref="InvalidOperationException">Invalid signature.</exception>
public override bool Verify(byte[] input, byte[] signature)
{
return Ed25519.Verify(signature, input, _key.PublicKey.TrimLeadingZeros().Pad(Ed25519.PublicKeySizeInBytes));
}

/// <summary>
/// Creates the signature.
/// </summary>
/// <param name="input">The input.</param>
/// <returns>
/// Signed input data.
/// </returns>
/// <exception cref="SshException">Invalid ED25519Key key.</exception>
public override byte[] Sign(byte[] input)
{
return Ed25519.Sign(input, _key.PrivateKey);
}

#region IDisposable Members

private bool _isDisposed;

/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing)
{
if (_isDisposed)
return;

if (disposing)
{
_isDisposed = true;
}
}

/// <summary>
/// Releases unmanaged resources and performs other cleanup operations before the
/// <see cref="ED25519DigitalSignature"/> is reclaimed by garbage collection.
/// </summary>
~ED25519DigitalSignature()
{
Dispose(false);
}

#endregion
}
}
159 changes: 159 additions & 0 deletions src/Renci.SshNet/Security/Cryptography/ED25519Key.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
using System;
using Renci.SshNet.Common;
using Renci.SshNet.Security.Cryptography;
using Renci.SshNet.Security.Chaos.NaCl;

namespace Renci.SshNet.Security
{
/// <summary>
/// Contains ED25519 private and public key
/// </summary>
public class ED25519Key : Key, IDisposable
{
private ED25519DigitalSignature _digitalSignature;

private byte[] SK;
private byte[] PK;
private byte[] publicKey = new byte[Ed25519.PublicKeySizeInBytes];
private byte[] privateKey = new byte[Ed25519.ExpandedPrivateKeySizeInBytes];

/// <summary>
/// Gets the Key String.
/// </summary>
public override string ToString()
{
return "ssh-ed25519";
}

/// <summary>
/// Gets or sets the public.
/// </summary>
/// <value>
/// The public.
/// </value>
public override BigInteger[] Public
{
get
{
if (PK != null)
return new BigInteger[] { new BigInteger(PK) };
return new BigInteger[] { new BigInteger(publicKey) };
}
set
{
publicKey = value[0].ToByteArray().Reverse();
}
}

/// <summary>
/// Gets the length of the key.
/// </summary>
/// <value>
/// The length of the key.
/// </value>
public override int KeyLength
{
get
{
return publicKey.Length;
}
}

/// <summary>
/// Gets the digital signature.
/// </summary>
protected override DigitalSignature DigitalSignature
{
get
{
if (_digitalSignature == null)
{
_digitalSignature = new ED25519DigitalSignature(this);
}
return _digitalSignature;
}
}

/// <summary>
/// Gets the PublicKey Bytes
/// </summary>
public byte[] PublicKey
{
get
{
return publicKey;
}
}

/// <summary>
/// Gets the PrivateKey Bytes
/// </summary>
public byte[] PrivateKey
{
get
{
return privateKey;
}
}

/// <summary>
/// Initializes a new instance of the <see cref="ED25519Key"/> class.
/// </summary>
public ED25519Key()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="ED25519Key"/> class.
/// </summary>
/// <param name="pk">pk data.</param>
/// <param name="sk">sk data.</param>
public ED25519Key(byte[] pk, byte[] sk)
{
PK = pk;
SK = sk;
var seed = new byte[Ed25519.PrivateKeySeedSizeInBytes];
Buffer.BlockCopy(sk, 0, seed, 0, seed.Length);
Ed25519.KeyPairFromSeed(out publicKey, out privateKey, seed);
}

#region IDisposable Members

private bool _isDisposed;

/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing)
{
if (_isDisposed)
return;

if (disposing)
{
_isDisposed = true;
}
}

/// <summary>
/// Releases unmanaged resources and performs other cleanup operations before the
/// <see cref="DsaKey"/> is reclaimed by garbage collection.
/// </summary>
~ED25519Key()
{
Dispose(false);
}

#endregion
}
}
2 changes: 1 addition & 1 deletion src/Renci.SshNet/Security/KeyHostAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public BigInteger[] Keys
for (var i = 0; i < _keys.Count; i++)
{
var key = _keys[i];
keys[i] = key.ToBigInteger();
keys[i] = BigInteger.SshFormatBignum2(key);
}
return keys;
}
Expand Down

0 comments on commit 5145afb

Please sign in to comment.