diff --git a/src/Neo/Cryptography/Crypto.cs b/src/Neo/Cryptography/Crypto.cs index a2e89dc787..99da988042 100644 --- a/src/Neo/Cryptography/Crypto.cs +++ b/src/Neo/Cryptography/Crypto.cs @@ -60,17 +60,31 @@ public static byte[] Hash256(ReadOnlySpan message) return message.Sha256().Sha256(); } + /// + /// Signs the specified message using the ECDSA algorithm and specified hash algorithm. + /// + /// The message to be signed. + /// The private key to be used. + /// The curve of the signature. + /// The hash algorithm to hash the message. + /// The ECDSA signature for the specified message. + [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); + } + /// /// Signs the specified message using the ECDSA algorithm and specified hash algorithm. /// /// The message to be signed. /// The private key to be used. /// The curve of the signature, default is . - /// The hash algorithm to hash the message, default is SHA256. + /// The hash algorithm to hash the message, default is SHA256. /// The ECDSA signature for the specified message. - 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 : @@ -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]; @@ -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); } @@ -118,13 +132,27 @@ public static byte[] Sign(byte[] message, byte[] priKey, ECC.ECCurve ecCurve = n /// The signed message. /// The signature to be verified. /// The public key to be used. - /// The hash algorithm to be used to hash the message, the default is SHA256. + /// The hash algorithm to be used to hash the message. + /// if the signature is valid; otherwise, . + [Obsolete("Use VerifySignature(ReadOnlySpan, ReadOnlySpan, ECC.ECPoint, HashAlgorithm) instead")] + public static bool VerifySignature(ReadOnlySpan message, ReadOnlySpan signature, ECC.ECPoint pubkey, Hasher hasher) + { + return VerifySignature(message, signature, pubkey, (HashAlgorithm)hasher); + } + + /// + /// Verifies that a digital signature is appropriate for the provided key, message and hash algorithm. + /// + /// The signed message. + /// The signature to be verified. + /// The public key to be used. + /// The hash algorithm to be used to hash the message, the default is SHA256. /// if the signature is valid; otherwise, . - public static bool VerifySignature(ReadOnlySpan message, ReadOnlySpan signature, ECC.ECPoint pubkey, Hasher hasher = Hasher.SHA256) + public static bool VerifySignature(ReadOnlySpan message, ReadOnlySpan 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 : @@ -146,17 +174,17 @@ public static bool VerifySignature(ReadOnlySpan message, ReadOnlySpanThe curve to be used by the ECDSA algorithm. /// The hash algorithm to be used hash the message, the default is SHA256. /// if the signature is valid; otherwise, . - public static bool VerifySignature(ReadOnlySpan message, ReadOnlySpan signature, ReadOnlySpan pubkey, ECC.ECCurve curve, Hasher hasher = Hasher.SHA256) + [Obsolete("Use VerifySignature(ReadOnlySpan, ReadOnlySpan, ReadOnlySpan, ECC.ECCurve, HashAlgorithm) instead")] + public static bool VerifySignature(ReadOnlySpan message, ReadOnlySpan signature, ReadOnlySpan pubkey, ECC.ECCurve curve, Hasher hasher) + { + return VerifySignature(message, signature, ECC.ECPoint.DecodePoint(pubkey, curve), (HashAlgorithm)hasher); + } + + /// + /// Verifies that a digital signature is appropriate for the provided key, curve, message and hasher. + /// + /// The signed message. + /// The signature to be verified. + /// The public key to be used. + /// The curve to be used by the ECDSA algorithm. + /// The hash algorithm to be used hash the message, the default is SHA256. + /// if the signature is valid; otherwise, . + public static bool VerifySignature(ReadOnlySpan message, ReadOnlySpan signature, ReadOnlySpan 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); } } } diff --git a/src/Neo/Cryptography/HashAlgorithm.cs b/src/Neo/Cryptography/HashAlgorithm.cs new file mode 100644 index 0000000000..dfd8b2280d --- /dev/null +++ b/src/Neo/Cryptography/HashAlgorithm.cs @@ -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 + { + /// + /// The SHA256 hash algorithm. + /// + SHA256 = 0x00, + + /// + /// The Keccak256 hash algorithm. + /// + Keccak256 = 0x01, + } +} diff --git a/src/Neo/Cryptography/Hasher.cs b/src/Neo/Cryptography/Hasher.cs index 21c5fef6a8..9b085aa587 100644 --- a/src/Neo/Cryptography/Hasher.cs +++ b/src/Neo/Cryptography/Hasher.cs @@ -9,11 +9,14 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using System; + namespace Neo.Cryptography { /// /// Represents hash function identifiers supported by ECDSA message signature and verification. /// + [Obsolete("Use HashAlgorithm instead")] public enum Hasher : byte { /// diff --git a/src/Neo/Cryptography/Murmur128.cs b/src/Neo/Cryptography/Murmur128.cs index 15eca775cd..cd8e54f664 100644 --- a/src/Neo/Cryptography/Murmur128.cs +++ b/src/Neo/Cryptography/Murmur128.cs @@ -19,7 +19,7 @@ namespace Neo.Cryptography /// /// Computes the 128 bits murmur hash for the input data. /// - public sealed class Murmur128 : HashAlgorithm + public sealed class Murmur128 : System.Security.Cryptography.HashAlgorithm { private const ulong c1 = 0x87c37b91114253d5; private const ulong c2 = 0x4cf5ad432745937f; diff --git a/src/Neo/Cryptography/Murmur32.cs b/src/Neo/Cryptography/Murmur32.cs index 0f2c1b828d..cccff68d76 100644 --- a/src/Neo/Cryptography/Murmur32.cs +++ b/src/Neo/Cryptography/Murmur32.cs @@ -18,7 +18,7 @@ namespace Neo.Cryptography /// /// Computes the murmur hash for the input data. /// - public sealed class Murmur32 : HashAlgorithm + public sealed class Murmur32 : System.Security.Cryptography.HashAlgorithm { private const uint c1 = 0xcc9e2d51; private const uint c2 = 0x1b873593; diff --git a/src/Neo/Cryptography/RIPEMD160Managed.cs b/src/Neo/Cryptography/RIPEMD160Managed.cs index 932c1cfea6..19a406d20f 100644 --- a/src/Neo/Cryptography/RIPEMD160Managed.cs +++ b/src/Neo/Cryptography/RIPEMD160Managed.cs @@ -20,7 +20,7 @@ namespace Neo.Cryptography /// Computes the ripemd160 hash for the input data. /// [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 diff --git a/src/Neo/SmartContract/Native/CryptoLib.cs b/src/Neo/SmartContract/Native/CryptoLib.cs index 9027298752..23c76aa158 100644 --- a/src/Neo/SmartContract/Native/CryptoLib.cs +++ b/src/Neo/SmartContract/Native/CryptoLib.cs @@ -21,12 +21,12 @@ namespace Neo.SmartContract.Native /// public sealed partial class CryptoLib : NativeContract { - private static readonly Dictionary s_curves = new() + private static readonly Dictionary 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() { } @@ -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) { diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs index daf1922644..457bd56ca0 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs @@ -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(); @@ -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); } @@ -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); }