diff --git a/src/Neo.SmartContract.Framework/Native/CryptoLib.cs b/src/Neo.SmartContract.Framework/Native/CryptoLib.cs index 43dc85d16..149830c00 100644 --- a/src/Neo.SmartContract.Framework/Native/CryptoLib.cs +++ b/src/Neo.SmartContract.Framework/Native/CryptoLib.cs @@ -10,6 +10,7 @@ #pragma warning disable CS0626 +using System; using Neo.SmartContract.Framework.Attributes; namespace Neo.SmartContract.Framework.Native @@ -28,6 +29,9 @@ public static partial class CryptoLib public static extern ByteString Murmur32(ByteString value, uint seed); + [Obsolete("VerifyWithECDsa has changed its signature. Please, use a compatible version of VerifyWithECDsa with NamedCurveHash curveHash argument instead.")] public static extern bool VerifyWithECDsa(ByteString message, ECPoint pubkey, ByteString signature, NamedCurve curve); + + public static extern bool VerifyWithECDsa(ByteString message, ECPoint pubkey, ByteString signature, NamedCurveHash curveHash); } } diff --git a/src/Neo.SmartContract.Framework/Native/NamedCurveHash.cs b/src/Neo.SmartContract.Framework/Native/NamedCurveHash.cs new file mode 100644 index 000000000..2f499b82e --- /dev/null +++ b/src/Neo.SmartContract.Framework/Native/NamedCurveHash.cs @@ -0,0 +1,44 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// The Neo.SmartContract.Framework is free software distributed under the MIT +// software license, see the accompanying file LICENSE in the main directory +// of the project 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. + +using System; + +namespace Neo.SmartContract.Framework.Native +{ + /// + /// Represents a pair of the named curve used in ECDSA and a hash algorithm used to hash message. + /// This is a compatible extension of an obsolete enum. + /// + /// + /// https://tools.ietf.org/html/rfc4492#section-5.1.1 + /// + public enum NamedCurveHash : byte + { + /// + /// The secp256k1 curve and SHA256 hash algorithm. + /// + secp256k1SHA256 = 22, + + /// + /// The secp256r1 curve, which known as prime256v1 or nistP-256, and SHA256 hash algorithm. + /// + secp256r1SHA256 = 23, + + /// + /// The secp256k1 curve and Keccak256 hash algorithm. + /// + secp256k1Keccak256 = 24, + + /// + /// The secp256r1 curve, which known as prime256v1 or nistP-256, and Keccak256 hash algorithm. + /// + secp256r1Keccak256 = 25 + } +} diff --git a/tests/Neo.SmartContract.Framework.TestContracts/Contract_Crypto.cs b/tests/Neo.SmartContract.Framework.TestContracts/Contract_Crypto.cs index 4106125d8..5e505f9aa 100644 --- a/tests/Neo.SmartContract.Framework.TestContracts/Contract_Crypto.cs +++ b/tests/Neo.SmartContract.Framework.TestContracts/Contract_Crypto.cs @@ -24,12 +24,22 @@ public static byte[] Murmur32(byte[] value, uint seed) public static bool Secp256r1VerifySignatureWithMessage(byte[] message, ECPoint pubkey, byte[] signature) { - return CryptoLib.VerifyWithECDsa((ByteString)message, pubkey, (ByteString)signature, NamedCurve.secp256r1); + return CryptoLib.VerifyWithECDsa((ByteString)message, pubkey, (ByteString)signature, NamedCurveHash.secp256r1SHA256); + } + + public static bool Secp256r1VerifyKeccakSignatureWithMessage(byte[] message, ECPoint pubkey, byte[] signature) + { + return CryptoLib.VerifyWithECDsa((ByteString)message, pubkey, (ByteString)signature, NamedCurveHash.secp256r1Keccak256); } public static bool Secp256k1VerifySignatureWithMessage(byte[] message, ECPoint pubkey, byte[] signature) { - return CryptoLib.VerifyWithECDsa((ByteString)message, pubkey, (ByteString)signature, NamedCurve.secp256k1); + return CryptoLib.VerifyWithECDsa((ByteString)message, pubkey, (ByteString)signature, NamedCurveHash.secp256k1SHA256); + } + + public static bool Secp256k1VerifyKeccakSignatureWithMessage(byte[] message, ECPoint pubkey, byte[] signature) + { + return CryptoLib.VerifyWithECDsa((ByteString)message, pubkey, (ByteString)signature, NamedCurveHash.secp256k1Keccak256); } public static byte[] Bls12381Serialize(object data) diff --git a/tests/Neo.SmartContract.Framework.UnitTests/Services/CryptoTest.cs b/tests/Neo.SmartContract.Framework.UnitTests/Services/CryptoTest.cs index 494038d32..f2dd3db7d 100644 --- a/tests/Neo.SmartContract.Framework.UnitTests/Services/CryptoTest.cs +++ b/tests/Neo.SmartContract.Framework.UnitTests/Services/CryptoTest.cs @@ -52,7 +52,7 @@ public void Test_RIPEMD160() [TestMethod] public void Test_VerifySignatureWithMessage() { - // secp256r1 + // secp256r1 with SHA256 hash var key = GenerateKey(32); var data = Engine.Transaction.GetSignData(ProtocolSettings.Default.Network); @@ -63,13 +63,23 @@ public void Test_VerifySignatureWithMessage() Assert.IsFalse(Contract.Secp256r1VerifySignatureWithMessage(System.Array.Empty(), key.PublicKey, signature)); Assert.IsTrue(Contract.Secp256r1VerifySignatureWithMessage(data, key.PublicKey, signature)); - // secp256k1 + // secp256r1 with Keccak hash + + var signatureKeccak = Crypto.Sign(data, key.PrivateKey, hasher: Hasher.Keccak256); + + // Check + + Assert.IsFalse(Contract.Secp256r1VerifyKeccakSignatureWithMessage(System.Array.Empty(), key.PublicKey, signatureKeccak)); + Assert.IsTrue(Contract.Secp256r1VerifyKeccakSignatureWithMessage(data, key.PublicKey, signatureKeccak)); + + // secp256k1 with SHA256 hash var pubkey = Cryptography.ECC.ECCurve.Secp256k1.G * key.PrivateKey; var pubKeyData = pubkey.EncodePoint(false).Skip(1).ToArray(); + var curve = ECCurve.CreateFromFriendlyName("secP256k1"); var ecdsa = ECDsa.Create(new ECParameters { - Curve = ECCurve.CreateFromFriendlyName("secP256k1"), + Curve = curve, D = key.PrivateKey, Q = new ECPoint { @@ -83,6 +93,15 @@ public void Test_VerifySignatureWithMessage() Assert.IsFalse(Contract.Secp256k1VerifySignatureWithMessage(System.Array.Empty(), pubkey, signature)); Assert.IsTrue(Contract.Secp256k1VerifySignatureWithMessage(data, pubkey, signature)); + + // secp256k1 with Keccak hash + + signature = Crypto.Sign(data, key.PrivateKey, ecCurve: curve, hasher: Hasher.Keccak256); + + // Check + + Assert.IsFalse(Contract.Secp256k1VerifyKeccakSignatureWithMessage(System.Array.Empty(), pubkey, signature)); + Assert.IsTrue(Contract.Secp256k1VerifyKeccakSignatureWithMessage(data, pubkey, signature)); } [TestMethod]