Skip to content

add secp256k1 + keccak256 to signature verification.#3205

Closed
Jim8y wants to merge 8 commits intoneo-project:masterfrom
Jim8y:support-secp256k1
Closed

add secp256k1 + keccak256 to signature verification.#3205
Jim8y wants to merge 8 commits intoneo-project:masterfrom
Jim8y:support-secp256k1

Conversation

@Jim8y
Copy link
Contributor

@Jim8y Jim8y commented Apr 25, 2024

Description

This pr focus on adding support to secp256k1 signed transactions and hashed with Keccak256.

Fixes # (issue)

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Test Configuration:

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

@Jim8y Jim8y marked this pull request as draft April 25, 2024 03:41
Jimmy and others added 4 commits April 25, 2024 11:43
Comment on lines +22 to +26
protected internal readonly static Dictionary<byte, ECCurve> ECCurveSelection = new(){
{ 0x00, ECCurve.Secp256r1 },
{ 0x01, ECCurve.Secp256k1 },
};

Copy link
Contributor

Choose a reason for hiding this comment

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

(This may not be the best place to put this dictionary)

Copy link
Contributor

@roman-khimov roman-khimov left a comment

Choose a reason for hiding this comment

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

Why?

@Jim8y
Copy link
Contributor Author

Jim8y commented Apr 25, 2024

Why?

Hey @roman-khimov, this feature is required by some exchanges that only support secp256k1 to sign transactions, its hardware restrained and can not be updated. Thus, we need to add support to verify secp256k1 signed transactions. But we only verify it, we dont construct it.

@roman-khimov
Copy link
Contributor

How come it wasn't a problem for years of Neo existence?

This raises so many compatibility questions that I don't even know where to begin. Addresses/wallets/script parsers/NeoFS accounts/NeoX accounts.

Likely the issue can be solved with crypto.verifyWithECDsa that has support for k1 (proxy contract that does actions based on incoming payloads signed with k1 key).

@Jim8y
Copy link
Contributor Author

Jim8y commented Apr 27, 2024

New syscall:

System.Crypto.CheckSigV2

System.Crypto.CheckMultisigV2

Data Type:

InvocationScript: Old format
VerificationScript: 1 byte curve| 1 byte hash | Old format

Curve:

0x00 r1,
0x01 k1,

Hash:

0x00 SHA256,
0x01 Keccak256

Exampe:

            using ScriptBuilder verificationScriptBuilder = new();
            verificationScriptBuilder.EmitRaw([0x01]); // Push curve secp256K1
            verificationScriptBuilder.EmitRaw([0x01]); // Push Keccak256 hasher
            verificationScriptBuilder.EmitPush(pubKey); // Push pubkey
            verificationScriptBuilder.EmitSysCall(ApplicationEngine.System_Crypto_CheckSigV2);

Price:

SignatureContractCostV2:
SignatureContractCost() + (isV2 ? ApplicationEngine.OpCodePriceTable[(byte)OpCode.PUSH1] * 2 : 0);

MultiSignatureContractCostV2:
MultiSignatureContractCost() + (isV2 ? ApplicationEngine.OpCodePriceTable[(byte)OpCode.PUSH1] * 2 : 0);

How to check if a verification script is V2:

       private static ReadOnlySpan<byte> ParseScriptV2(ReadOnlySpan<byte> script, out bool? isV2, out ECCurve? curve, out Hasher? hasher)
        {
            if (script.Length < 40) throw new FormatException("The verification script is too short.");
            isV2 = BinaryPrimitives.ReadUInt32LittleEndian(script[^4..]) == ApplicationEngine.System_Crypto_CheckSigV2 ||
                BinaryPrimitives.ReadUInt32LittleEndian(script[^4..]) == ApplicationEngine.System_Crypto_CheckMultisigV2;
            curve = ECCurve.Secp256r1;
            hasher = Hasher.SHA256;
            if (!isV2.Value)
            {
                return script;
            }

            curve = script[0] == 0x01 ? ECCurve.Secp256k1 : ECCurve.Secp256r1;
            hasher = script[1] == 0x01 ? Hasher.Keccak256 : Hasher.SHA256;
            return script[2..];
        }

@Jim8y
Copy link
Contributor Author

Jim8y commented Apr 27, 2024

Note: Still need to add it to the hard fork.


public static byte[] Keccak256(this ReadOnlySpan<byte> value)
{
return Keccak256(value.ToArray());
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
return Keccak256(value.ToArray());
return Keccak256([.. value]);


public static byte[] Keccak256(this Span<byte> value)
{
return Keccak256(value.ToArray());
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
return Keccak256(value.ToArray());
return Keccak256([.. value]);

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))
Copy link
Member

Choose a reason for hiding this comment

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

Dont add ? nullable until project is convert to nullable project.

{
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];
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
var pubkey = isV2.Value ? witnesses[i].VerificationScript.Span[4..37] : witnesses[i].VerificationScript.Span[2..35];
var pubkey = isV2.Value ? witnesses[i].VerificationScript[4..37] : witnesses[i].VerificationScript[2..35];

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))
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (!Crypto.VerifySignature(this.GetSignData(settings.Network), witnesses[i].InvocationScript.Span[2..], pubkey, curve, hasher.Value))
if (!Crypto.VerifySignature(this.GetSignData(settings.Network), witnesses[i].InvocationScript[2..], pubkey, curve, hasher.Value))

}
}
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))
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
else if (IsMultiSigContract(witnesses[i].VerificationScript.Span, out var m, out ECPoint[] points, out isV2, out curve, out hasher))
else if (IsMultiSigContract(witnesses[i].VerificationScript, out var m, out ECPoint[] points, out isV2, out curve, out hasher))

@AnnaShaleva
Copy link
Member

This raises so many compatibility questions that I don't even know where to begin. Addresses/wallets/script parsers/NeoFS accounts/NeoX accounts.

Just to be in sync with Discord: we've discussed it with @roman-khimov and prepared an alternative concept that do not require core protocol modifications and do not require custom verification contract deployment. The solution is rather simple: use custom witness verification script with a call to native CryptoLib's verifyWithECDsa. See the nspcc-dev/neo-go#3425 (it is an updated version) and don't hesitate to comment, I'll port it to the core in the nearest future.

@cschuchardt88
Copy link
Member

cschuchardt88 commented Apr 28, 2024

Can't they use NeoX to bridge to N3? Isnt that the point of NeoX?

@shargon
Copy link
Member

shargon commented Apr 29, 2024

It's possible to use his verifyWithECDsa as verification script without store the contract in the blockchain, isn't it?

@roman-khimov roman-khimov mentioned this pull request May 2, 2024
AnnaShaleva added a commit that referenced this pull request May 3, 2024
A port of
nspcc-dev/neo-go@1e2b438.

This commit contains minor protocol extension needed for custom
Koblitz-based verification scripts (an alternative to
#3205).

Replace native CryptoLib's verifyWithECDsa `curve` parameter by
`curveHash` parameter which is a enum over supported pairs of named
curves and hash functions.

Even though this change is a compatible extension of the protocol, it
changes the genesis state due to parameter renaming (CryptoLib's
manifest is changed). But we're going to resync chain in 3.7 release
anyway, so it's not a big deal.

Also, we need to check mainnet and testnet compatibility in case if
anyone has ever called verifyWithECDsa with 24 or 25 `curve` value.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
AnnaShaleva added a commit that referenced this pull request May 3, 2024
Koblitz-based and Keccak-based transaction witness verification for
single signature and multisignature ported from
nspcc-dev/neo-go#3425.

An alternative to #3205.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
AnnaShaleva added a commit that referenced this pull request May 6, 2024
A port of
nspcc-dev/neo-go@1e2b438.

This commit contains minor protocol extension needed for custom
Koblitz-based verification scripts (an alternative to
#3205).

Replace native CryptoLib's verifyWithECDsa `curve` parameter by
`curveHash` parameter which is a enum over supported pairs of named
curves and hash functions. NamedCurve enum mark as deprecated and
replaced by NamedCurveHash with compatible behaviour.

Even though this change is a compatible extension of the protocol, it
changes the genesis state due to parameter renaming (CryptoLib's
manifest is changed). But we're going to resync chain in 3.7 release
anyway, so it's not a big deal.

Also, we need to check mainnet and testnet compatibility in case if
anyone has ever called verifyWithECDsa with 24 or 25 `curve` value.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
AnnaShaleva added a commit that referenced this pull request May 6, 2024
Koblitz-based and Keccak-based transaction witness verification for
single signature and multisignature ported from
nspcc-dev/neo-go#3425.

An alternative to #3205.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
NGDAdmin pushed a commit that referenced this pull request May 10, 2024
…#3209)

* Native: extend CryptoLib's verifyWithECDsa with hasher parameter

A port of
nspcc-dev/neo-go@1e2b438.

This commit contains minor protocol extension needed for custom
Koblitz-based verification scripts (an alternative to
#3205).

Replace native CryptoLib's verifyWithECDsa `curve` parameter by
`curveHash` parameter which is a enum over supported pairs of named
curves and hash functions. NamedCurve enum mark as deprecated and
replaced by NamedCurveHash with compatible behaviour.

Even though this change is a compatible extension of the protocol, it
changes the genesis state due to parameter renaming (CryptoLib's
manifest is changed). But we're going to resync chain in 3.7 release
anyway, so it's not a big deal.

Also, we need to check mainnet and testnet compatibility in case if
anyone has ever called verifyWithECDsa with 24 or 25 `curve` value.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>

* SmartContract: add extension to ScriptBuilder for System.Contract.Call

Group the set of common operations required to emit
System.Contract.Call appcall.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>

* Native: add an example of custom Koblitz signature verification

Koblitz-based and Keccak-based transaction witness verification for
single signature and multisignature ported from
nspcc-dev/neo-go#3425.

An alternative to #3205.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>

* SmartContract: make multisig koblitz easier to parse

1. Make prologue be exactly the same as regular CheckMultisig.
2. But instead of "SYSCALL System.Crypto.CheckMultisig" do INITSLOT and K check.
3. This makes all of the code from INITSLOT below be independent of N/M, so
   one can parse the script beginning in the same way CheckMultisig is parsed and
   then just compare the rest of it with some known-good blob.
4. The script becomes a tiny bit larger now, but properties above are too good.

Ported from
nspcc-dev/neo-go@34ee294.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>

* SmartContract: use ABORT in Koblitz multisig

Make the script a bit shorter. ABORTMSG would cost a bit more.

Ported from
nspcc-dev/neo-go@fb16891.
Ref.
nspcc-dev/neo-go#3425 (comment).

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>

* SmartContract: reduce callflag scope for Koblitz verification scripts

All flag is too wide. A port of
nspcc-dev/neo-go@fe292f3.

Ref.
nspcc-dev/neo-go#3425 (comment).

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>

* Native: add tests for CryptoLib's verifyWithECDsa

No functional changes, just add more unit-tests.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>

* Native: update NamedCurveHash values for Keccak256 hasher

Use 122 and 123 respectively for secp256k1Keccak256 and
secp256r1Keccak256.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>

* SmartContract: move EmitAppCallNoArgs to the testing code

We're not going to implement custom Koblitz witness generation at the
core, and thus, the only user of this API is testing code.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>

* Apply suggestions from code review

clean ut lines

* fix names

* Cryptography: cache ECDomainParameters for Secp256r1 and Secp256k1

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>

* Update tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs

* Update tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs

* Update tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs

* Update tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs

* Update tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs

* Update tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs

* Update tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs

* Update tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs

* Update tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs

* Update tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs

* Update tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs

* Update tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs

* Update tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs

* Update tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs

* Update tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs

---------

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
Co-authored-by: Shargon <shargon@gmail.com>
Co-authored-by: Jimmy <jinghui@wayne.edu>
@NGDAdmin
Copy link
Collaborator

Close because of #3209

@NGDAdmin NGDAdmin closed this May 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants