-
-
Notifications
You must be signed in to change notification settings - Fork 940
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support for Ed25519 Host- and Private-Keys
- Loading branch information
Showing
5 changed files
with
404 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
using System; | ||
using Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10; | ||
|
||
namespace Renci.SshNet.Security.Chaos.NaCl | ||
{ | ||
internal static class Ed25519 | ||
{ | ||
public static readonly int PublicKeySizeInBytes = 32; | ||
public static readonly int SignatureSizeInBytes = 64; | ||
public static readonly int ExpandedPrivateKeySizeInBytes = 32 * 2; | ||
public static readonly int PrivateKeySeedSizeInBytes = 32; | ||
public static readonly int SharedKeySizeInBytes = 32; | ||
|
||
public static bool Verify(ArraySegment<byte> signature, ArraySegment<byte> message, ArraySegment<byte> publicKey) | ||
{ | ||
if (signature.Count != SignatureSizeInBytes) | ||
throw new ArgumentException(string.Format("Signature size must be {0}", SignatureSizeInBytes), "signature.Count"); | ||
if (publicKey.Count != PublicKeySizeInBytes) | ||
throw new ArgumentException(string.Format("Public key size must be {0}", PublicKeySizeInBytes), "publicKey.Count"); | ||
return Ed25519Operations.crypto_sign_verify(signature.Array, signature.Offset, message.Array, message.Offset, message.Count, publicKey.Array, publicKey.Offset); | ||
} | ||
|
||
public static bool Verify(byte[] signature, byte[] message, byte[] publicKey) | ||
{ | ||
if (signature == null) | ||
throw new ArgumentNullException("signature"); | ||
if (message == null) | ||
throw new ArgumentNullException("message"); | ||
if (publicKey == null) | ||
throw new ArgumentNullException("publicKey"); | ||
if (signature.Length != SignatureSizeInBytes) | ||
throw new ArgumentException(string.Format("Signature size must be {0}", SignatureSizeInBytes), "signature.Length"); | ||
if (publicKey.Length != PublicKeySizeInBytes) | ||
throw new ArgumentException(string.Format("Public key size must be {0}", PublicKeySizeInBytes), "publicKey.Length"); | ||
return Ed25519Operations.crypto_sign_verify(signature, 0, message, 0, message.Length, publicKey, 0); | ||
} | ||
|
||
public static void Sign(ArraySegment<byte> signature, ArraySegment<byte> message, ArraySegment<byte> expandedPrivateKey) | ||
{ | ||
if (signature.Array == null) | ||
throw new ArgumentNullException("signature.Array"); | ||
if (signature.Count != SignatureSizeInBytes) | ||
throw new ArgumentException("signature.Count"); | ||
if (expandedPrivateKey.Array == null) | ||
throw new ArgumentNullException("expandedPrivateKey.Array"); | ||
if (expandedPrivateKey.Count != ExpandedPrivateKeySizeInBytes) | ||
throw new ArgumentException("expandedPrivateKey.Count"); | ||
if (message.Array == null) | ||
throw new ArgumentNullException("message.Array"); | ||
Ed25519Operations.crypto_sign2(signature.Array, signature.Offset, message.Array, message.Offset, message.Count, expandedPrivateKey.Array, expandedPrivateKey.Offset); | ||
} | ||
|
||
public static byte[] Sign(byte[] message, byte[] expandedPrivateKey) | ||
{ | ||
var signature = new byte[SignatureSizeInBytes]; | ||
Sign(new ArraySegment<byte>(signature), new ArraySegment<byte>(message), new ArraySegment<byte>(expandedPrivateKey)); | ||
return signature; | ||
} | ||
|
||
public static byte[] PublicKeyFromSeed(byte[] privateKeySeed) | ||
{ | ||
byte[] privateKey; | ||
byte[] publicKey; | ||
KeyPairFromSeed(out publicKey, out privateKey, privateKeySeed); | ||
CryptoBytes.Wipe(privateKey); | ||
return publicKey; | ||
} | ||
|
||
public static byte[] ExpandedPrivateKeyFromSeed(byte[] privateKeySeed) | ||
{ | ||
byte[] privateKey; | ||
byte[] publicKey; | ||
KeyPairFromSeed(out publicKey, out privateKey, privateKeySeed); | ||
CryptoBytes.Wipe(publicKey); | ||
return privateKey; | ||
} | ||
|
||
public static void KeyPairFromSeed(out byte[] publicKey, out byte[] expandedPrivateKey, byte[] privateKeySeed) | ||
{ | ||
if (privateKeySeed == null) | ||
throw new ArgumentNullException("privateKeySeed"); | ||
if (privateKeySeed.Length != PrivateKeySeedSizeInBytes) | ||
throw new ArgumentException("privateKeySeed"); | ||
var pk = new byte[PublicKeySizeInBytes]; | ||
var sk = new byte[ExpandedPrivateKeySizeInBytes]; | ||
Ed25519Operations.crypto_sign_keypair(pk, 0, sk, 0, privateKeySeed, 0); | ||
publicKey = pk; | ||
expandedPrivateKey = sk; | ||
} | ||
|
||
public static void KeyPairFromSeed(ArraySegment<byte> publicKey, ArraySegment<byte> expandedPrivateKey, ArraySegment<byte> privateKeySeed) | ||
{ | ||
if (publicKey.Array == null) | ||
throw new ArgumentNullException("publicKey.Array"); | ||
if (expandedPrivateKey.Array == null) | ||
throw new ArgumentNullException("expandedPrivateKey.Array"); | ||
if (privateKeySeed.Array == null) | ||
throw new ArgumentNullException("privateKeySeed.Array"); | ||
if (publicKey.Count != PublicKeySizeInBytes) | ||
throw new ArgumentException("publicKey.Count"); | ||
if (expandedPrivateKey.Count != ExpandedPrivateKeySizeInBytes) | ||
throw new ArgumentException("expandedPrivateKey.Count"); | ||
if (privateKeySeed.Count != PrivateKeySeedSizeInBytes) | ||
throw new ArgumentException("privateKeySeed.Count"); | ||
Ed25519Operations.crypto_sign_keypair( | ||
publicKey.Array, publicKey.Offset, | ||
expandedPrivateKey.Array, expandedPrivateKey.Offset, | ||
privateKeySeed.Array, privateKeySeed.Offset); | ||
} | ||
|
||
[Obsolete("Needs more testing")] | ||
public static byte[] KeyExchange(byte[] publicKey, byte[] privateKey) | ||
{ | ||
var sharedKey = new byte[SharedKeySizeInBytes]; | ||
KeyExchange(new ArraySegment<byte>(sharedKey), new ArraySegment<byte>(publicKey), new ArraySegment<byte>(privateKey)); | ||
return sharedKey; | ||
} | ||
|
||
[Obsolete("Needs more testing")] | ||
public static void KeyExchange(ArraySegment<byte> sharedKey, ArraySegment<byte> publicKey, ArraySegment<byte> privateKey) | ||
{ | ||
if (sharedKey.Array == null) | ||
throw new ArgumentNullException("sharedKey.Array"); | ||
if (publicKey.Array == null) | ||
throw new ArgumentNullException("publicKey.Array"); | ||
if (privateKey.Array == null) | ||
throw new ArgumentNullException("privateKey"); | ||
if (sharedKey.Count != 32) | ||
throw new ArgumentException("sharedKey.Count != 32"); | ||
if (publicKey.Count != 32) | ||
throw new ArgumentException("publicKey.Count != 32"); | ||
if (privateKey.Count != 64) | ||
throw new ArgumentException("privateKey.Count != 64"); | ||
|
||
FieldElement montgomeryX, edwardsY, edwardsZ, sharedMontgomeryX; | ||
FieldOperations.fe_frombytes(out edwardsY, publicKey.Array, publicKey.Offset); | ||
FieldOperations.fe_1(out edwardsZ); | ||
MontgomeryCurve25519.EdwardsToMontgomeryX(out montgomeryX, ref edwardsY, ref edwardsZ); | ||
byte[] h = Sha512.Hash(privateKey.Array, privateKey.Offset, 32);//ToDo: Remove alloc | ||
ScalarOperations.sc_clamp(h, 0); | ||
MontgomeryOperations.scalarmult(out sharedMontgomeryX, h, 0, ref montgomeryX); | ||
CryptoBytes.Wipe(h); | ||
FieldOperations.fe_tobytes(sharedKey.Array, sharedKey.Offset, ref sharedMontgomeryX); | ||
MontgomeryCurve25519.KeyExchangeOutputHashNaCl(sharedKey.Array, sharedKey.Offset); | ||
} | ||
} | ||
} |
93 changes: 93 additions & 0 deletions
93
src/Renci.SshNet/Security/Cryptography/ED25519DigitalSignature.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
|
||
/// <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 | ||
} | ||
} |
Oops, something went wrong.