Skip to content

Commit

Permalink
[Core] rename hasher (neo-project#3651)
Browse files Browse the repository at this point in the history
* rename

* revert unrelated change

* HashAlgorithm instead

* Update src/Neo/Cryptography/Crypto.cs

---------

Co-authored-by: Shargon <[email protected]>
Co-authored-by: NGD Admin <[email protected]>
  • Loading branch information
3 people authored Jan 7, 2025
1 parent 110abc5 commit 02c5f3a
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 35 deletions.
79 changes: 61 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)
{
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,27 @@ 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>
[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>
/// 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, Hasher hasher = Hasher.SHA256)
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 +174,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 +227,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
{
/// <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")]
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

0 comments on commit 02c5f3a

Please sign in to comment.