-
Notifications
You must be signed in to change notification settings - Fork 1k
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
add secp256k1 + keccak256 to signature verification. #3205
Changes from all commits
361f7cb
2f9bd00
ac68c5c
59f30de
0a6fb53
f4908c6
5dc20cc
d8f64a2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// Copyright (C) 2015-2024 The Neo Project. | ||
// | ||
// Hasher.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 Hasher : byte | ||
{ | ||
SHA256 = 0x00, | ||
Keccak256 = 0x01, | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -12,6 +12,7 @@ | |||||
using Neo.IO; | ||||||
using Neo.Network.P2P.Payloads; | ||||||
using Neo.Wallets; | ||||||
using Org.BouncyCastle.Crypto.Digests; | ||||||
using Org.BouncyCastle.Crypto.Engines; | ||||||
using Org.BouncyCastle.Crypto.Modes; | ||||||
using Org.BouncyCastle.Crypto.Parameters; | ||||||
|
@@ -153,6 +154,25 @@ public static byte[] Sha256(this Span<byte> value) | |||||
return Sha256((ReadOnlySpan<byte>)value); | ||||||
} | ||||||
|
||||||
public static byte[] Keccak256(this byte[] value) | ||||||
{ | ||||||
KeccakDigest keccak = new(256); | ||||||
keccak.BlockUpdate(value, 0, value.Length); | ||||||
byte[] result = new byte[keccak.GetDigestSize()]; | ||||||
keccak.DoFinal(result, 0); | ||||||
return result; | ||||||
} | ||||||
|
||||||
public static byte[] Keccak256(this ReadOnlySpan<byte> value) | ||||||
{ | ||||||
return Keccak256(value.ToArray()); | ||||||
} | ||||||
|
||||||
public static byte[] Keccak256(this Span<byte> value) | ||||||
{ | ||||||
return Keccak256(value.ToArray()); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
|
||||||
public static byte[] AES256Encrypt(this byte[] plainData, byte[] key, byte[] nonce, byte[] associatedData = null) | ||||||
{ | ||||||
if (nonce.Length != 12) throw new ArgumentOutOfRangeException(nameof(nonce)); | ||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -379,11 +379,11 @@ | |||||
uint execFeeFactor = NativeContract.Policy.GetExecFeeFactor(snapshot); | ||||||
for (int i = 0; i < hashes.Length; i++) | ||||||
{ | ||||||
if (IsSignatureContract(witnesses[i].VerificationScript.Span)) | ||||||
net_fee -= execFeeFactor * SignatureContractCost(); | ||||||
else if (IsMultiSigContract(witnesses[i].VerificationScript.Span, out int m, out int n)) | ||||||
if (IsSignatureContract(witnesses[i].VerificationScript.Span, out bool? isV2, out _, out _)) | ||||||
net_fee -= execFeeFactor * SignatureContractCost(isV2.Value); | ||||||
else if (IsMultiSigContract(witnesses[i].VerificationScript.Span, out int m, out int n, out isV2)) | ||||||
{ | ||||||
net_fee -= execFeeFactor * MultiSignatureContractCost(m, n); | ||||||
net_fee -= execFeeFactor * MultiSignatureContractCost(m, n, isV2.Value); | ||||||
} | ||||||
else | ||||||
{ | ||||||
|
@@ -415,21 +415,21 @@ | |||||
UInt160[] hashes = GetScriptHashesForVerifying(null); | ||||||
for (int i = 0; i < hashes.Length; i++) | ||||||
{ | ||||||
if (IsSignatureContract(witnesses[i].VerificationScript.Span)) | ||||||
if (IsSignatureContract(witnesses[i].VerificationScript.Span, out bool? isV2, out ECCurve? curve, out Hasher? hasher)) | ||||||
Check warning on line 418 in src/Neo/Network/P2P/Payloads/Transaction.cs
|
||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dont add |
||||||
{ | ||||||
if (hashes[i] != witnesses[i].ScriptHash) return VerifyResult.Invalid; | ||||||
var pubkey = witnesses[i].VerificationScript.Span[2..35]; | ||||||
var pubkey = isV2.Value ? witnesses[i].VerificationScript.Span[4..37] : witnesses[i].VerificationScript.Span[2..35]; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
try | ||||||
{ | ||||||
if (!Crypto.VerifySignature(this.GetSignData(settings.Network), witnesses[i].InvocationScript.Span[2..], pubkey, ECCurve.Secp256r1)) | ||||||
if (!Crypto.VerifySignature(this.GetSignData(settings.Network), witnesses[i].InvocationScript.Span[2..], pubkey, curve, hasher.Value)) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
return VerifyResult.InvalidSignature; | ||||||
} | ||||||
catch | ||||||
{ | ||||||
return VerifyResult.Invalid; | ||||||
} | ||||||
} | ||||||
else if (IsMultiSigContract(witnesses[i].VerificationScript.Span, out var m, out ECPoint[] points)) | ||||||
else if (IsMultiSigContract(witnesses[i].VerificationScript.Span, out var m, out ECPoint[] points, out isV2, out curve, out hasher)) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
{ | ||||||
if (hashes[i] != witnesses[i].ScriptHash) return VerifyResult.Invalid; | ||||||
var signatures = GetMultiSignatures(witnesses[i].InvocationScript); | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,11 +13,17 @@ | |
using Neo.Cryptography.ECC; | ||
using Neo.Network.P2P; | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
namespace Neo.SmartContract | ||
{ | ||
partial class ApplicationEngine | ||
{ | ||
protected internal readonly static Dictionary<byte, ECCurve> ECCurveSelection = new(){ | ||
{ 0x00, ECCurve.Secp256r1 }, | ||
{ 0x01, ECCurve.Secp256k1 }, | ||
}; | ||
|
||
Comment on lines
+22
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (This may not be the best place to put this dictionary) |
||
/// <summary> | ||
/// The price of System.Crypto.CheckSig. | ||
/// </summary> | ||
|
@@ -29,12 +35,24 @@ partial class ApplicationEngine | |
/// </summary> | ||
public static readonly InteropDescriptor System_Crypto_CheckSig = Register("System.Crypto.CheckSig", nameof(CheckSig), CheckSigPrice, CallFlags.None); | ||
|
||
/// <summary> | ||
/// The <see cref="InteropDescriptor"/> of System.Crypto.CheckSigV2. | ||
/// Checks the signature (secp256k1, secp256r1, or other curve) for the current script container. | ||
/// </summary> | ||
public static readonly InteropDescriptor System_Crypto_CheckSigV2 = Register("System.Crypto.CheckSigV2", nameof(CheckSigV2), CheckSigPrice, CallFlags.None); | ||
|
||
/// <summary> | ||
/// The <see cref="InteropDescriptor"/> of System.Crypto.CheckMultisig. | ||
/// Checks the signatures for the current script container. | ||
/// </summary> | ||
public static readonly InteropDescriptor System_Crypto_CheckMultisig = Register("System.Crypto.CheckMultisig", nameof(CheckMultisig), 0, CallFlags.None); | ||
|
||
/// <summary> | ||
/// The <see cref="InteropDescriptor"/> of System.Crypto.CheckMultisigV2. | ||
/// Checks the signatures (secp256k1, secp256r1, or other curve) for the current script container. | ||
/// </summary> | ||
public static readonly InteropDescriptor System_Crypto_CheckMultisigV2 = Register("System.Crypto.CheckMultisigV2", nameof(CheckMultisigV2), 0, CallFlags.None); | ||
|
||
/// <summary> | ||
/// The implementation of System.Crypto.CheckSig. | ||
/// Checks the signature for the current script container. | ||
|
@@ -54,6 +72,32 @@ protected internal bool CheckSig(byte[] pubkey, byte[] signature) | |
} | ||
} | ||
|
||
/// <summary> | ||
/// This is a new version check signature method that allows for different curves and hashers. | ||
/// </summary> | ||
/// <param name="eccurve">The ec curve</param> | ||
/// <param name="hash">The hash algorithm to get the hash of the message.</param> | ||
/// <param name="pubkey">The public key.</param> | ||
/// <param name="signature">The signature of the message.</param> | ||
/// <returns>The signature is valid or not.</returns> | ||
/// <exception cref="ArgumentOutOfRangeException">Throw if the given hash algorithm or ec curve is not defined.</exception> | ||
protected internal bool CheckSigV2(byte eccurve, byte hash, byte[] pubkey, byte[] signature) | ||
{ | ||
try | ||
{ | ||
if (!Enum.IsDefined(typeof(Hasher), hash)) | ||
throw new ArgumentOutOfRangeException("Invalid hasher"); | ||
Hasher hasher = (Hasher)hash; | ||
if (!ECCurveSelection.TryGetValue(eccurve, out ECCurve curve)) | ||
throw new ArgumentOutOfRangeException("Invalid EC curve"); | ||
return Crypto.VerifySignature(ScriptContainer.GetSignData(ProtocolSettings.Network), signature, pubkey, curve, hasher); | ||
} | ||
catch (Exception) | ||
{ | ||
return false; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// The implementation of System.Crypto.CheckMultisig. | ||
/// Checks the signatures for the current script container. | ||
|
@@ -84,5 +128,44 @@ protected internal bool CheckMultisig(byte[][] pubkeys, byte[][] signatures) | |
} | ||
return true; | ||
} | ||
|
||
/// <summary> | ||
/// This is a new version check multisig method that allows for different curves and hashers. | ||
/// </summary> | ||
/// <param name="eccurve">The ec curve</param> | ||
/// <param name="hash">The hash algorithm to get the hash of the message.</param> | ||
/// <param name="pubkeys">The public keys.</param> | ||
/// <param name="signatures">The signatures of the message.</param> | ||
/// <returns>The signature is valid or not.</returns> | ||
/// <exception cref="ArgumentOutOfRangeException">Throw if the given hash algorithm or ec curve is not defined.</exception> | ||
protected internal bool CheckMultisigV2(byte eccurve, byte hash, byte[][] pubkeys, byte[][] signatures) | ||
{ | ||
byte[] message = ScriptContainer.GetSignData(ProtocolSettings.Network); | ||
int m = signatures.Length, n = pubkeys.Length; | ||
if (n == 0 || m == 0 || m > n) throw new ArgumentException(); | ||
AddGas(CheckSigPrice * n * ExecFeeFactor); | ||
try | ||
{ | ||
if (!Enum.IsDefined(typeof(Hasher), hash)) | ||
throw new ArgumentOutOfRangeException("Invalid hasher"); | ||
Hasher hasher = (Hasher)hash; | ||
if (!ECCurveSelection.TryGetValue(eccurve, out ECCurve curve)) | ||
throw new ArgumentOutOfRangeException("Invalid EC curve"); | ||
|
||
for (int i = 0, j = 0; i < m && j < n;) | ||
{ | ||
if (Crypto.VerifySignature(message, signatures[i], pubkeys[j], curve, hasher)) | ||
i++; | ||
j++; | ||
if (m - i > n - j) | ||
return false; | ||
} | ||
} | ||
catch (ArgumentException) | ||
{ | ||
return false; | ||
} | ||
return true; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.