Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Core] rename hasher #3651

Merged
merged 6 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 62 additions & 18 deletions src/Neo/Cryptography/Crypto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,31 @@ public static byte[] Hash256(ReadOnlySpan<byte> message)
return message.Sha256().Sha256();
}

/// <summary>
/// Signs the specified message using the ECDSA algorithm and specified hash algorithm.
/// </summary>
/// <param name="message">The message to be signed.</param>
/// <param name="priKey">The private key to be used.</param>
/// <param name="ecCurve">The <see cref="ECC.ECCurve"/> curve of the signature.</param>
/// <param name="hasher">The hash algorithm to hash the message.</param>
/// <returns>The ECDSA signature for the specified message.</returns>
[Obsolete("Use Sign(byte[], byte[], ECC.ECCurve, HashAlgorithm) instead")]
public static byte[] Sign(byte[] message, byte[] priKey, ECC.ECCurve ecCurve, Hasher hasher)
{
return Sign(message, priKey, ecCurve, (HashAlgorithm)hasher);
}

/// <summary>
/// Signs the specified message using the ECDSA algorithm and specified hash algorithm.
/// </summary>
/// <param name="message">The message to be signed.</param>
/// <param name="priKey">The private key to be used.</param>
/// <param name="ecCurve">The <see cref="ECC.ECCurve"/> curve of the signature, default is <see cref="ECC.ECCurve.Secp256r1"/>.</param>
/// <param name="hasher">The hash algorithm to hash the message, default is SHA256.</param>
/// <param name="hashAlgorithm">The hash algorithm to hash the message, default is SHA256.</param>
/// <returns>The ECDSA signature for the specified message.</returns>
public static byte[] Sign(byte[] message, byte[] priKey, ECC.ECCurve ecCurve = null, Hasher hasher = Hasher.SHA256)
public static byte[] Sign(byte[] message, byte[] priKey, ECC.ECCurve ecCurve = null, HashAlgorithm hashAlgorithm = HashAlgorithm.SHA256)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only change the argument name for obsolete?

{
if (hasher == Hasher.Keccak256 || (IsOSX && ecCurve == ECC.ECCurve.Secp256k1))
if (hashAlgorithm == HashAlgorithm.Keccak256 || (IsOSX && ecCurve == ECC.ECCurve.Secp256k1))
{
var domain =
ecCurve == null || ecCurve == ECC.ECCurve.Secp256r1 ? secp256r1DomainParams :
Expand All @@ -81,9 +95,9 @@ public static byte[] Sign(byte[] message, byte[] priKey, ECC.ECCurve ecCurve = n
var priKeyParameters = new ECPrivateKeyParameters(privateKey, domain);
signer.Init(true, priKeyParameters);
var messageHash =
hasher == Hasher.SHA256 ? message.Sha256() :
hasher == Hasher.Keccak256 ? message.Keccak256() :
throw new NotSupportedException(nameof(hasher));
hashAlgorithm == HashAlgorithm.SHA256 ? message.Sha256() :
hashAlgorithm == HashAlgorithm.Keccak256 ? message.Keccak256() :
throw new NotSupportedException(nameof(hashAlgorithm));
var signature = signer.GenerateSignature(messageHash);

var signatureBytes = new byte[64];
Expand All @@ -107,8 +121,8 @@ public static byte[] Sign(byte[] message, byte[] priKey, ECC.ECCurve ecCurve = n
D = priKey,
});
var hashAlg =
hasher == Hasher.SHA256 ? HashAlgorithmName.SHA256 :
throw new NotSupportedException(nameof(hasher));
hashAlgorithm == HashAlgorithm.SHA256 ? HashAlgorithmName.SHA256 :
throw new NotSupportedException($"The hash algorithm {nameof(hashAlgorithm)} is not supported.");
return ecdsa.SignData(message, hashAlg);
}

Expand All @@ -118,13 +132,28 @@ public static byte[] Sign(byte[] message, byte[] priKey, ECC.ECCurve ecCurve = n
/// <param name="message">The signed message.</param>
/// <param name="signature">The signature to be verified.</param>
/// <param name="pubkey">The public key to be used.</param>
/// <param name="hasher">The hash algorithm to be used to hash the message, the default is SHA256.</param>
/// <param name="hasher">The hash algorithm to be used to hash the message.</param>
/// <returns><see langword="true"/> if the signature is valid; otherwise, <see langword="false"/>.</returns>
public static bool VerifySignature(ReadOnlySpan<byte> message, ReadOnlySpan<byte> signature, ECC.ECPoint pubkey, Hasher hasher = Hasher.SHA256)
[Obsolete("Use VerifySignature(ReadOnlySpan<byte>, ReadOnlySpan<byte>, ECC.ECPoint, HashAlgorithm) instead")]
public static bool VerifySignature(ReadOnlySpan<byte> message, ReadOnlySpan<byte> signature, ECC.ECPoint pubkey, Hasher hasher)
{
return VerifySignature(message, signature, pubkey, (HashAlgorithm)hasher);
}


/// <summary>
shargon marked this conversation as resolved.
Show resolved Hide resolved
/// Verifies that a digital signature is appropriate for the provided key, message and hash algorithm.
/// </summary>
/// <param name="message">The signed message.</param>
/// <param name="signature">The signature to be verified.</param>
/// <param name="pubkey">The public key to be used.</param>
/// <param name="hashAlgorithm">The hash algorithm to be used to hash the message, the default is SHA256.</param>
/// <returns><see langword="true"/> if the signature is valid; otherwise, <see langword="false"/>.</returns>
public static bool VerifySignature(ReadOnlySpan<byte> message, ReadOnlySpan<byte> signature, ECC.ECPoint pubkey, HashAlgorithm hashAlgorithm = HashAlgorithm.SHA256)
{
if (signature.Length != 64) return false;

if (hasher == Hasher.Keccak256 || (IsOSX && pubkey.Curve == ECC.ECCurve.Secp256k1))
if (hashAlgorithm == HashAlgorithm.Keccak256 || (IsOSX && pubkey.Curve == ECC.ECCurve.Secp256k1))
{
var domain =
pubkey.Curve == ECC.ECCurve.Secp256r1 ? secp256r1DomainParams :
Expand All @@ -146,17 +175,17 @@ public static bool VerifySignature(ReadOnlySpan<byte> message, ReadOnlySpan<byte
var s = new BigInteger(1, sig, 32, 32);

var messageHash =
hasher == Hasher.SHA256 ? message.Sha256() :
hasher == Hasher.Keccak256 ? message.Keccak256() :
throw new NotSupportedException(nameof(hasher));
hashAlgorithm == HashAlgorithm.SHA256 ? message.Sha256() :
hashAlgorithm == HashAlgorithm.Keccak256 ? message.Keccak256() :
throw new NotSupportedException(nameof(hashAlgorithm));

return signer.VerifySignature(messageHash, r, s);
}

var ecdsa = CreateECDsa(pubkey);
var hashAlg =
hasher == Hasher.SHA256 ? HashAlgorithmName.SHA256 :
throw new NotSupportedException(nameof(hasher));
hashAlgorithm == HashAlgorithm.SHA256 ? HashAlgorithmName.SHA256 :
throw new NotSupportedException($"The hash algorithm {nameof(hashAlgorithm)} is not supported.");
return ecdsa.VerifyData(message, signature, hashAlg);
}

Expand Down Expand Up @@ -199,9 +228,24 @@ public static ECDsa CreateECDsa(ECC.ECPoint pubkey)
/// <param name="curve">The curve to be used by the ECDSA algorithm.</param>
/// <param name="hasher">The hash algorithm to be used hash the message, the default is SHA256.</param>
/// <returns><see langword="true"/> if the signature is valid; otherwise, <see langword="false"/>.</returns>
public static bool VerifySignature(ReadOnlySpan<byte> message, ReadOnlySpan<byte> signature, ReadOnlySpan<byte> pubkey, ECC.ECCurve curve, Hasher hasher = Hasher.SHA256)
[Obsolete("Use VerifySignature(ReadOnlySpan<byte>, ReadOnlySpan<byte>, ReadOnlySpan<byte>, ECC.ECCurve, HashAlgorithm) instead")]
public static bool VerifySignature(ReadOnlySpan<byte> message, ReadOnlySpan<byte> signature, ReadOnlySpan<byte> pubkey, ECC.ECCurve curve, Hasher hasher)
{
return VerifySignature(message, signature, ECC.ECPoint.DecodePoint(pubkey, curve), (HashAlgorithm)hasher);
}

/// <summary>
/// Verifies that a digital signature is appropriate for the provided key, curve, message and hasher.
/// </summary>
/// <param name="message">The signed message.</param>
/// <param name="signature">The signature to be verified.</param>
/// <param name="pubkey">The public key to be used.</param>
/// <param name="curve">The curve to be used by the ECDSA algorithm.</param>
/// <param name="hashAlgorithm">The hash algorithm to be used hash the message, the default is SHA256.</param>
/// <returns><see langword="true"/> if the signature is valid; otherwise, <see langword="false"/>.</returns>
public static bool VerifySignature(ReadOnlySpan<byte> message, ReadOnlySpan<byte> signature, ReadOnlySpan<byte> pubkey, ECC.ECCurve curve, HashAlgorithm hashAlgorithm = HashAlgorithm.SHA256)
{
return VerifySignature(message, signature, ECC.ECPoint.DecodePoint(pubkey, curve), hasher);
return VerifySignature(message, signature, ECC.ECPoint.DecodePoint(pubkey, curve), hashAlgorithm);
}
}
}
26 changes: 26 additions & 0 deletions src/Neo/Cryptography/HashAlgorithm.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (C) 2015-2024 The Neo Project.
//
// HashAlgorithm.cs file belongs to the neo project and is free
// software distributed under the MIT software license, see the
// accompanying file LICENSE in the main directory of the
// repository or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

namespace Neo.Cryptography
{
public enum HashAlgorithm : byte
Copy link
Contributor

@nan01ab nan01ab Jan 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add an implicit operator here(it converts Hasher to HashAlgorithm)?

{
/// <summary>
/// The SHA256 hash algorithm.
/// </summary>
SHA256 = 0x00,

/// <summary>
/// The Keccak256 hash algorithm.
/// </summary>
Keccak256 = 0x01,
}
}
3 changes: 3 additions & 0 deletions src/Neo/Cryptography/Hasher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using System;

namespace Neo.Cryptography
{
/// <summary>
/// Represents hash function identifiers supported by ECDSA message signature and verification.
/// </summary>
[Obsolete("Use HashAlgorithm instead")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer rename the enum instead of duplicate all the methods

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have to, those methods are public, previously our principle is duplicate a new one and obsolete old one before remove it, at least previouly we deal with public methods that way.

public enum Hasher : byte
{
/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Neo/Cryptography/Murmur128.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace Neo.Cryptography
/// <summary>
/// Computes the 128 bits murmur hash for the input data.
/// </summary>
public sealed class Murmur128 : HashAlgorithm
public sealed class Murmur128 : System.Security.Cryptography.HashAlgorithm
{
private const ulong c1 = 0x87c37b91114253d5;
private const ulong c2 = 0x4cf5ad432745937f;
Expand Down
2 changes: 1 addition & 1 deletion src/Neo/Cryptography/Murmur32.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace Neo.Cryptography
/// <summary>
/// Computes the murmur hash for the input data.
/// </summary>
public sealed class Murmur32 : HashAlgorithm
public sealed class Murmur32 : System.Security.Cryptography.HashAlgorithm
{
private const uint c1 = 0xcc9e2d51;
private const uint c2 = 0x1b873593;
Expand Down
2 changes: 1 addition & 1 deletion src/Neo/Cryptography/RIPEMD160Managed.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace Neo.Cryptography
/// Computes the ripemd160 hash for the input data.
/// </summary>
[ComVisible(true)]
public class RIPEMD160Managed : HashAlgorithm
public class RIPEMD160Managed : System.Security.Cryptography.HashAlgorithm
{
private readonly byte[] _buffer;
private long _count; // Number of bytes in the hashed message
Expand Down
12 changes: 6 additions & 6 deletions src/Neo/SmartContract/Native/CryptoLib.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ namespace Neo.SmartContract.Native
/// </summary>
public sealed partial class CryptoLib : NativeContract
{
private static readonly Dictionary<NamedCurveHash, (ECCurve Curve, Hasher Hasher)> s_curves = new()
private static readonly Dictionary<NamedCurveHash, (ECCurve Curve, HashAlgorithm HashAlgorithm)> s_curves = new()
{
[NamedCurveHash.secp256k1SHA256] = (ECCurve.Secp256k1, Hasher.SHA256),
[NamedCurveHash.secp256r1SHA256] = (ECCurve.Secp256r1, Hasher.SHA256),
[NamedCurveHash.secp256k1Keccak256] = (ECCurve.Secp256k1, Hasher.Keccak256),
[NamedCurveHash.secp256r1Keccak256] = (ECCurve.Secp256r1, Hasher.Keccak256),
[NamedCurveHash.secp256k1SHA256] = (ECCurve.Secp256k1, HashAlgorithm.SHA256),
[NamedCurveHash.secp256r1SHA256] = (ECCurve.Secp256r1, HashAlgorithm.SHA256),
[NamedCurveHash.secp256k1Keccak256] = (ECCurve.Secp256k1, HashAlgorithm.Keccak256),
[NamedCurveHash.secp256r1Keccak256] = (ECCurve.Secp256r1, HashAlgorithm.Keccak256),
};

internal CryptoLib() : base() { }
Expand Down Expand Up @@ -91,7 +91,7 @@ public static bool VerifyWithECDsa(byte[] message, byte[] pubkey, byte[] signatu
try
{
var ch = s_curves[curveHash];
return Crypto.VerifySignature(message, signature, pubkey, ch.Curve, ch.Hasher);
return Crypto.VerifySignature(message, signature, pubkey, ch.Curve, ch.HashAlgorithm);
}
catch (ArgumentException)
{
Expand Down
16 changes: 8 additions & 8 deletions tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ public void TestVerifyWithECDsa_CustomTxWitness_SingleSig()
Version = 0,
Witnesses = []
};
var tx_signature = Crypto.Sign(tx.GetSignData(TestBlockchain.TheNeoSystem.Settings.Network), privkey, ECCurve.Secp256k1, Hasher.Keccak256);
var tx_signature = Crypto.Sign(tx.GetSignData(TestBlockchain.TheNeoSystem.Settings.Network), privkey, ECCurve.Secp256k1, HashAlgorithm.Keccak256);

// inv is a builder of witness invocation script corresponding to the public key.
using ScriptBuilder inv = new();
Expand Down Expand Up @@ -737,7 +737,7 @@ public void TestVerifyWithECDsa_CustomTxWitness_MultiSig()
{
if (i == 1) // Skip one key since we need only 3 signatures.
continue;
var sig = Crypto.Sign(tx.GetSignData(TestBlockchain.TheNeoSystem.Settings.Network), keys[i].Item1, ECCurve.Secp256k1, Hasher.Keccak256);
var sig = Crypto.Sign(tx.GetSignData(TestBlockchain.TheNeoSystem.Settings.Network), keys[i].Item1, ECCurve.Secp256k1, HashAlgorithm.Keccak256);
inv.EmitPush(sig);
}

Expand Down Expand Up @@ -868,23 +868,23 @@ public void TestVerifyWithECDsa()
byte[] message = System.Text.Encoding.Default.GetBytes("HelloWorld");

// secp256r1 + SHA256
byte[] signature = Crypto.Sign(message, privR1, ECCurve.Secp256r1, Hasher.SHA256);
byte[] signature = Crypto.Sign(message, privR1, ECCurve.Secp256r1, HashAlgorithm.SHA256);
Crypto.VerifySignature(message, signature, pubR1).Should().BeTrue(); // SHA256 hash is used by default.
CallVerifyWithECDsa(message, pubR1, signature, NamedCurveHash.secp256r1SHA256).Should().Be(true);

// secp256r1 + Keccak256
signature = Crypto.Sign(message, privR1, ECCurve.Secp256r1, Hasher.Keccak256);
Crypto.VerifySignature(message, signature, pubR1, Hasher.Keccak256).Should().BeTrue();
signature = Crypto.Sign(message, privR1, ECCurve.Secp256r1, HashAlgorithm.Keccak256);
Crypto.VerifySignature(message, signature, pubR1, HashAlgorithm.Keccak256).Should().BeTrue();
CallVerifyWithECDsa(message, pubR1, signature, NamedCurveHash.secp256r1Keccak256).Should().Be(true);

// secp256k1 + SHA256
signature = Crypto.Sign(message, privK1, ECCurve.Secp256k1, Hasher.SHA256);
signature = Crypto.Sign(message, privK1, ECCurve.Secp256k1, HashAlgorithm.SHA256);
Crypto.VerifySignature(message, signature, pubK1).Should().BeTrue(); // SHA256 hash is used by default.
CallVerifyWithECDsa(message, pubK1, signature, NamedCurveHash.secp256k1SHA256).Should().Be(true);

// secp256k1 + Keccak256
signature = Crypto.Sign(message, privK1, ECCurve.Secp256k1, Hasher.Keccak256);
Crypto.VerifySignature(message, signature, pubK1, Hasher.Keccak256).Should().BeTrue();
signature = Crypto.Sign(message, privK1, ECCurve.Secp256k1, HashAlgorithm.Keccak256);
Crypto.VerifySignature(message, signature, pubK1, HashAlgorithm.Keccak256).Should().BeTrue();
CallVerifyWithECDsa(message, pubK1, signature, NamedCurveHash.secp256k1Keccak256).Should().Be(true);
}

Expand Down
Loading