using System;
using System.Globalization;
#if NETFRAMEWORK
using System.Security.Cryptography;
#endif // NETFRAMEWORK
using Renci.SshNet.Common;
namespace Renci.SshNet.Security.Cryptography
{
///
/// Implements ECDSA digital signature algorithm.
///
public class EcdsaDigitalSignature : DigitalSignature, IDisposable
{
private readonly EcdsaKey _key;
private bool _isDisposed;
///
/// Initializes a new instance of the class.
///
/// The ECDSA key.
/// is null.
public EcdsaDigitalSignature(EcdsaKey key)
{
if (key is null)
{
throw new ArgumentNullException(nameof(key));
}
_key = key;
}
///
/// Verifies the signature.
///
/// The input.
/// The signature.
///
/// true if signature was successfully verified; otherwise false.
///
public override bool Verify(byte[] input, byte[] signature)
{
// for 521 sig_size is 132
var sig_size = _key.KeyLength == 521 ? 132 : _key.KeyLength / 4;
var ssh_data = new SshDataSignature(signature, sig_size);
#if NETFRAMEWORK
var ecdsa = (ECDsaCng)_key.Ecdsa;
ecdsa.HashAlgorithm = _key.HashAlgorithm;
return ecdsa.VerifyData(input, ssh_data.Signature);
#else
return _key.Ecdsa.VerifyData(input, ssh_data.Signature, _key.HashAlgorithm);
#endif
}
///
/// Creates the signature.
///
/// The input.
///
/// Signed input data.
///
public override byte[] Sign(byte[] input)
{
#if NETFRAMEWORK
var ecdsa = (ECDsaCng)_key.Ecdsa;
ecdsa.HashAlgorithm = _key.HashAlgorithm;
var signed = ecdsa.SignData(input);
#else
var signed = _key.Ecdsa.SignData(input, _key.HashAlgorithm);
#endif
var ssh_data = new SshDataSignature(signed.Length) { Signature = signed };
return ssh_data.GetBytes();
}
///
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
///
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
///
/// Releases unmanaged and - optionally - managed resources
///
/// true to release both managed and unmanaged resources; false to release only unmanaged resources.
protected virtual void Dispose(bool disposing)
{
if (_isDisposed)
{
return;
}
if (disposing)
{
_isDisposed = true;
}
}
///
/// Finalizes an instance of the class.
///
~EcdsaDigitalSignature()
{
Dispose(disposing: false);
}
}
internal sealed class SshDataSignature : SshData
{
private readonly int _signature_size;
private byte[] _signature_r;
private byte[] _signature_s;
public byte[] Signature
{
get
{
var signature = new byte[_signature_size];
Buffer.BlockCopy(_signature_r, 0, signature, 0, _signature_r.Length);
Buffer.BlockCopy(_signature_s, 0, signature, _signature_r.Length, _signature_s.Length);
return signature;
}
set
{
var signed_r = new byte[_signature_size / 2];
Buffer.BlockCopy(value, 0, signed_r, 0, signed_r.Length);
_signature_r = signed_r.ToBigInteger2().ToByteArray().Reverse();
var signed_s = new byte[_signature_size / 2];
Buffer.BlockCopy(value, signed_r.Length, signed_s, 0, signed_s.Length);
_signature_s = signed_s.ToBigInteger2().ToByteArray().Reverse();
}
}
public SshDataSignature(int sig_size)
{
_signature_size = sig_size;
}
public SshDataSignature(byte[] data, int sig_size)
{
_signature_size = sig_size;
Load(data);
}
protected override void LoadData()
{
_signature_r = ReadBinary().TrimLeadingZeros().Pad(_signature_size / 2);
_signature_s = ReadBinary().TrimLeadingZeros().Pad(_signature_size / 2);
}
protected override void SaveData()
{
WriteBinaryString(_signature_r.ToBigInteger2().ToByteArray().Reverse());
WriteBinaryString(_signature_s.ToBigInteger2().ToByteArray().Reverse());
}
public new byte[] ReadBinary()
{
var length = ReadUInt32();
if (length > int.MaxValue)
{
throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Strings longer than {0} is not supported.", int.MaxValue));
}
return ReadBytes((int)length);
}
protected override int BufferCapacity
{
get
{
var capacity = base.BufferCapacity;
capacity += 4; // r length
capacity += _signature_r.Length; // signature r
capacity += 4; // s length
capacity += _signature_s.Length; // signature s
return capacity;
}
}
}
}