Skip to content
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

EcRecover #3633

Closed
wants to merge 59 commits into from
Closed
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
bcc5fd0
add hardofork HF_Echidna
Jim8y Aug 7, 2024
e63f10b
Add entries to `Designation` event (#3397)
shargon Aug 8, 2024
9d16c53
[Neo Core StdLib] Add Base64url (#3453)
Jim8y Aug 8, 2024
f746f8d
add hardofork HF_Echidna
Jim8y Aug 7, 2024
8d7f9e8
Add entries to `Designation` event (#3397)
shargon Aug 8, 2024
3fc8077
[Neo Core StdLib] Add Base64url (#3453)
Jim8y Aug 8, 2024
620d938
Merge branch 'HF_Echidna' of github.com:neo-project/neo into HF_Echidna
Jim8y Oct 2, 2024
7e31d0c
add hardofork HF_Echidna
Jim8y Aug 7, 2024
993e3fe
Add entries to `Designation` event (#3397)
shargon Aug 8, 2024
37bf0cb
[Neo Core StdLib] Add Base64url (#3453)
Jim8y Aug 8, 2024
7dba130
format
Jim8y Nov 6, 2024
0457ccd
Merge branch 'HF_Echidna' of github.com:neo-project/neo into HF_Echidna
Jim8y Nov 6, 2024
d7a291e
Merge Master
cschuchardt88 Nov 17, 2024
6e780c0
Fixed typo
cschuchardt88 Nov 17, 2024
02d6ab9
Added back #3397
cschuchardt88 Nov 17, 2024
8fb30df
Fixed tests
cschuchardt88 Nov 17, 2024
f9244eb
fixed global.json
cschuchardt88 Nov 17, 2024
650c9e9
Merge branch 'master' into HF_Echidna
Jim8y Nov 18, 2024
331541a
Merge branch 'master' into HF_Echidna
Jim8y Nov 20, 2024
eb96d14
Merge branch 'master' into HF_Echidna
shargon Nov 20, 2024
74498e5
Merge branch 'master' into HF_Echidna
Jim8y Nov 23, 2024
577c431
Merge branch 'master' into HF_Echidna
cschuchardt88 Dec 4, 2024
1c82ed9
Update src/Neo/Neo.csproj
cschuchardt88 Dec 4, 2024
b81b127
Update src/Neo/Neo.csproj
cschuchardt88 Dec 4, 2024
a3a6fd5
Merge branch 'master' into HF_Echidna
Jim8y Dec 11, 2024
9e28008
EC Recover
shargon Dec 18, 2024
73d90c9
Fix
shargon Dec 18, 2024
2ac18ea
Fix HF
shargon Dec 18, 2024
c086661
Merge branch 'master' into HF_Echidna
Jim8y Dec 19, 2024
0f1507f
Merge branch 'master' into HF_Echidna
Jim8y Dec 19, 2024
f91b680
[`Fix`]: integer overflow in `JumpTable.SubStr ` (#3496)
nan01ab Dec 19, 2024
705f4bb
Fix NEO callstates (#3599)
shargon Dec 19, 2024
498ed91
Merge branch 'master' into HF_Echidna
Jim8y Dec 19, 2024
81bd7f6
Update src/Neo/SmartContract/Native/CryptoLib.cs
shargon Dec 19, 2024
08c3be6
Merge branch 'master' into HF_Echidna
Jim8y Dec 19, 2024
59fe8b5
fix tests error (#3636)
Jim8y Dec 19, 2024
a784c41
NeoToken: accept candidate registration via onNEP17Payment (#3597)
roman-khimov Dec 20, 2024
e2acc64
specify the argument exception information.
Jim8y Dec 20, 2024
216c39e
Fix Ut (#3635)
shargon Dec 20, 2024
2f98dae
update with two ecrevover
Jim8y Dec 26, 2024
6658ad6
fix issues.
Jim8y Dec 26, 2024
b2bbaba
remove not appliable tests
Jim8y Dec 26, 2024
e3673e8
Update src/Neo/Cryptography/Crypto.cs
shargon Dec 27, 2024
fc9aab8
Unify
shargon Dec 27, 2024
092d132
Include in try
shargon Dec 27, 2024
548bd05
NeoToken: add NEP-27 to supported standards list starting from Echidn…
AnnaShaleva Dec 27, 2024
0782042
fix bls tests and add support to compact signature for secp256k1
Jim8y Dec 28, 2024
b594d1b
Merge branch 'ec-recover' of github.com:neo-project/neo into ec-recover
Jim8y Dec 28, 2024
3365319
Update src/Neo/Cryptography/Crypto.cs
Jim8y Dec 29, 2024
fe28919
Update src/Neo/Cryptography/Crypto.cs
Jim8y Dec 29, 2024
409c3b5
Update src/Neo/Cryptography/Crypto.cs
Jim8y Dec 29, 2024
6c60137
Update src/Neo/Cryptography/Crypto.cs
cschuchardt88 Dec 29, 2024
8470de5
Merge branch 'master' into HF_Echidna
cschuchardt88 Dec 29, 2024
6fc94aa
fix eip-2098 tests
Hecate2 Dec 31, 2024
93fc37b
ut: fix HF_Echidna unit tests (#3646)
shargon Dec 31, 2024
fb28f36
Merge branch 'HF_Echidna' into ec-recover
Jim8y Dec 31, 2024
c60ba6f
add a new Recover interface
Jim8y Dec 31, 2024
7b22712
update manifest
Jim8y Dec 31, 2024
9112507
Update src/Neo/SmartContract/Native/CryptoLib.cs
Jim8y Dec 31, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/Neo.CLI/config.fs.mainnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
"HF_Aspidochelone": 3000000,
"HF_Basilisk": 4500000,
"HF_Cockatrice": 5800000,
"HF_Domovoi": 5800000
"HF_Domovoi": 5800000,
"HF_Echidna": 5800001
},
"StandbyCommittee": [
"026fa34ec057d74c2fdf1a18e336d0bd597ea401a0b2ad57340d5c220d09f44086",
Expand Down
3 changes: 2 additions & 1 deletion src/Neo.CLI/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
"HF_Aspidochelone": 1730000,
"HF_Basilisk": 4120000,
"HF_Cockatrice": 5450000,
"HF_Domovoi": 5570000
"HF_Domovoi": 5570000,
"HF_Echidna": 5570001
},
"InitialGasDistribution": 5200000000000000,
"ValidatorsCount": 7,
Expand Down
3 changes: 2 additions & 1 deletion src/Neo.CLI/config.mainnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
"HF_Aspidochelone": 1730000,
"HF_Basilisk": 4120000,
"HF_Cockatrice": 5450000,
"HF_Domovoi": 5570000
"HF_Domovoi": 5570000,
"HF_Echidna": 5570001
},
"InitialGasDistribution": 5200000000000000,
"ValidatorsCount": 7,
Expand Down
3 changes: 2 additions & 1 deletion src/Neo.CLI/config.testnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
"HF_Aspidochelone": 210000,
"HF_Basilisk": 2680000,
"HF_Cockatrice": 3967000,
"HF_Domovoi": 4144000
"HF_Domovoi": 4144000,
"HF_Echidna": 4144001
},
"InitialGasDistribution": 5200000000000000,
"ValidatorsCount": 7,
Expand Down
133 changes: 106 additions & 27 deletions src/Neo/Cryptography/Crypto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
// modifications are permitted.

using Neo.IO.Caching;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Utilities.Encoders;
using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography;

Expand All @@ -24,21 +25,18 @@ namespace Neo.Cryptography
/// </summary>
public static class Crypto
{
private static readonly ECDsaCache CacheECDsa = new();
private static readonly bool IsOSX = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
private static readonly ECCurve secP256k1 = ECCurve.CreateFromFriendlyName("secP256k1");
private static readonly X9ECParameters bouncySecp256k1 = Org.BouncyCastle.Asn1.Sec.SecNamedCurves.GetByName("secp256k1");
private static readonly X9ECParameters bouncySecp256r1 = Org.BouncyCastle.Asn1.Sec.SecNamedCurves.GetByName("secp256r1");

/// <summary>
/// Holds domain parameters for Secp256r1 elliptic curve.
/// 64 bytes ECDSA signature + 1 byte recovery id
/// </summary>
private static readonly ECDomainParameters secp256r1DomainParams = new ECDomainParameters(bouncySecp256r1.Curve, bouncySecp256r1.G, bouncySecp256r1.N, bouncySecp256r1.H);

private const int RecoverableSignatureLength = 64 + 1;
/// <summary>
/// Holds domain parameters for Secp256k1 elliptic curve.
/// 64 bytes ECDSA signature
/// </summary>
private static readonly ECDomainParameters secp256k1DomainParams = new ECDomainParameters(bouncySecp256k1.Curve, bouncySecp256k1.G, bouncySecp256k1.N, bouncySecp256k1.H);
private const int SignatureLength = 64;
private static readonly BigInteger s_prime = new(1, Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"));
private static readonly ECDsaCache CacheECDsa = new();
private static readonly bool IsOSX = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
private static readonly ECCurve secP256k1 = ECCurve.CreateFromFriendlyName("secP256k1");

/// <summary>
/// Calculates the 160-bit hash value of the specified message.
Expand Down Expand Up @@ -72,13 +70,9 @@ public static byte[] Sign(byte[] message, byte[] priKey, ECC.ECCurve ecCurve = n
{
if (hasher == Hasher.Keccak256 || (IsOSX && ecCurve == ECC.ECCurve.Secp256k1))
{
var domain =
ecCurve == null || ecCurve == ECC.ECCurve.Secp256r1 ? secp256r1DomainParams :
ecCurve == ECC.ECCurve.Secp256k1 ? secp256k1DomainParams :
throw new NotSupportedException(nameof(ecCurve));
var signer = new Org.BouncyCastle.Crypto.Signers.ECDsaSigner();
var privateKey = new BigInteger(1, priKey);
var priKeyParameters = new ECPrivateKeyParameters(privateKey, domain);
var priKeyParameters = new ECPrivateKeyParameters(privateKey, ecCurve.BouncyCastleDomainParams);
signer.Init(true, priKeyParameters);
var messageHash =
hasher == Hasher.SHA256 ? message.Sha256() :
Expand Down Expand Up @@ -112,6 +106,99 @@ public static byte[] Sign(byte[] message, byte[] priKey, ECC.ECCurve ecCurve = n
return ecdsa.SignData(message, hashAlg);
}


shargon marked this conversation as resolved.
Show resolved Hide resolved
/// <summary>
/// Recovers the public key from a signature and message hash.
/// </summary>
/// <param name="signature">65-byte signature (r[32] || s[32] || v[1])</param>
/// <param name="hash">32-byte message hash</param>
/// <returns>The recovered public key</returns>
/// <exception cref="ArgumentException">Thrown when signature or hash parameters are invalid</exception>
public static ECC.ECPoint ECRecover(byte[] signature, byte[] hash)
{
if (signature is not { Length: RecoverableSignatureLength })
throw new ArgumentException("Signature must be 65 bytes with recovery value", nameof(signature));
if (hash is not { Length: 32 })
throw new ArgumentException("Message hash must be 32 bytes", nameof(hash));

try
{
// Extract r, s components (32 bytes each)
var r = new BigInteger(1, signature.Take(32).ToArray());
var s = new BigInteger(1, signature.Skip(32).Take(32).ToArray());

// Get recovery id, allowing both 0-3 and 27-30
var v = signature[SignatureLength];
var recId = v >= 27 ? v - 27 : v;
if (recId > 3)
throw new ArgumentException("Recovery value must be 0-3 or 27-30", nameof(signature));

// Get curve parameters
var curve = ECC.ECCurve.Secp256k1.BouncyCastleCurve;
var n = curve.N;

// Validate r, s values
if (r.SignValue <= 0 || r.CompareTo(n) >= 0)
throw new ArgumentException("r must be in range [1, N-1]", nameof(signature));
if (s.SignValue <= 0 || s.CompareTo(n) >= 0)
throw new ArgumentException("s must be in range [1, N-1]", nameof(signature));

// Calculate x coordinate
var i = BigInteger.ValueOf(recId >> 1);
var x = r.Add(i.Multiply(n));

// Get curve field
var field = curve.Curve.Field;
if (x.CompareTo(field.Characteristic) >= 0)
throw new ArgumentException("Invalid x coordinate", nameof(signature));

// Convert x to field element
var xField = curve.Curve.FromBigInteger(x);

// Compute right-hand side of curve equation: y^2 = x^3 + ax + b
var rhs = xField.Square().Multiply(xField).Add(curve.Curve.A.Multiply(xField)).Add(curve.Curve.B);

// Compute y coordinate
var y = rhs.Sqrt();
if (y == null)
throw new ArgumentException("Invalid x coordinate - no square root exists", nameof(signature));

// Ensure y has correct parity
if (y.ToBigInteger().TestBit(0) != ((recId & 1) == 1))
y = y.Negate();

// Create R point
var R = curve.Curve.CreatePoint(x, y.ToBigInteger());

// Check R * n = infinity
if (!R.Multiply(n).IsInfinity)
throw new ArgumentException("Invalid R point order", nameof(signature));

// Calculate e = -hash mod n
var e = new BigInteger(1, hash).Negate().Mod(n);

// Calculate r^-1
var rInv = r.ModInverse(n);

// Calculate Q = r^-1 (sR - eG)
var Q = R.Multiply(s).Add(curve.G.Multiply(e)).Multiply(rInv);

if (Q.IsInfinity)
throw new ArgumentException("Invalid public key point at infinity", nameof(signature));

// Convert to Neo ECPoint format
return ECC.ECPoint.FromBytes(Q.GetEncoded(false), ECC.ECCurve.Secp256k1);
}
catch (ArgumentException)
{
throw;
}
catch (Exception ex)
{
throw new ArgumentException("Invalid signature parameters", nameof(signature), ex);
}
}

/// <summary>
/// Verifies that a digital signature is appropriate for the provided key, message and hash algorithm.
/// </summary>
Expand All @@ -126,18 +213,10 @@ public static bool VerifySignature(ReadOnlySpan<byte> message, ReadOnlySpan<byte

if (hasher == Hasher.Keccak256 || (IsOSX && pubkey.Curve == ECC.ECCurve.Secp256k1))
{
var domain =
pubkey.Curve == ECC.ECCurve.Secp256r1 ? secp256r1DomainParams :
pubkey.Curve == ECC.ECCurve.Secp256k1 ? secp256k1DomainParams :
throw new NotSupportedException(nameof(pubkey.Curve));
var curve =
pubkey.Curve == ECC.ECCurve.Secp256r1 ? bouncySecp256r1.Curve :
bouncySecp256k1.Curve;

var point = curve.CreatePoint(
var point = pubkey.Curve.BouncyCastleCurve.Curve.CreatePoint(
new BigInteger(pubkey.X.Value.ToString()),
new BigInteger(pubkey.Y.Value.ToString()));
var pubKey = new ECPublicKeyParameters("ECDSA", point, domain);
var pubKey = new ECPublicKeyParameters("ECDSA", point, pubkey.Curve.BouncyCastleDomainParams);
var signer = new Org.BouncyCastle.Crypto.Signers.ECDsaSigner();
signer.Init(false, pubKey);

Expand Down
16 changes: 13 additions & 3 deletions src/Neo/Cryptography/ECC/ECCurve.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
// modifications are permitted.

using Neo.Extensions;
using Org.BouncyCastle.Crypto.Parameters;
using System.Globalization;
using System.Numerics;

Expand All @@ -33,9 +34,14 @@ public class ECCurve
/// </summary>
public readonly ECPoint G;

public readonly Org.BouncyCastle.Asn1.X9.X9ECParameters BouncyCastleCurve;
/// <summary>
/// Holds domain parameters for Secp256r1 elliptic curve.
/// </summary>
public readonly ECDomainParameters BouncyCastleDomainParams;
internal readonly int ExpectedECPointLength;

private ECCurve(BigInteger Q, BigInteger A, BigInteger B, BigInteger N, byte[] G)
private ECCurve(BigInteger Q, BigInteger A, BigInteger B, BigInteger N, byte[] G, string curveName)
{
this.Q = Q;
ExpectedECPointLength = ((int)VM.Utility.GetBitLength(Q) + 7) / 8;
Expand All @@ -44,6 +50,8 @@ private ECCurve(BigInteger Q, BigInteger A, BigInteger B, BigInteger N, byte[] G
this.N = N;
Infinity = new ECPoint(null, null, this);
this.G = ECPoint.DecodePoint(G, this);
BouncyCastleCurve = Org.BouncyCastle.Asn1.Sec.SecNamedCurves.GetByName(curveName);
BouncyCastleDomainParams = new ECDomainParameters(BouncyCastleCurve.Curve, BouncyCastleCurve.G, BouncyCastleCurve.N, BouncyCastleCurve.H);
}

/// <summary>
Expand All @@ -55,7 +63,8 @@ private ECCurve(BigInteger Q, BigInteger A, BigInteger B, BigInteger N, byte[] G
BigInteger.Zero,
7,
BigInteger.Parse("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", NumberStyles.AllowHexSpecifier),
("04" + "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798" + "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8").HexToBytes()
("04" + "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798" + "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8").HexToBytes(),
"secp256k1"
);

/// <summary>
Expand All @@ -67,7 +76,8 @@ private ECCurve(BigInteger Q, BigInteger A, BigInteger B, BigInteger N, byte[] G
BigInteger.Parse("00FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", NumberStyles.AllowHexSpecifier),
BigInteger.Parse("005AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", NumberStyles.AllowHexSpecifier),
BigInteger.Parse("00FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", NumberStyles.AllowHexSpecifier),
("04" + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296" + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5").HexToBytes()
("04" + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296" + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5").HexToBytes(),
"secp256r1"
);
}
}
26 changes: 26 additions & 0 deletions src/Neo/Cryptography/SignatureFormat.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (C) 2015-2024 The Neo Project.
//
// SignatureFormat.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 SignatureFormat : byte
{
/// <summary>
/// Der
/// </summary>
Der = 0,

/// <summary>
/// Fixed 32 bytes per BigInteger
/// </summary>
Fixed32 = 1
}
}
3 changes: 2 additions & 1 deletion src/Neo/Hardfork.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public enum Hardfork : byte
HF_Aspidochelone,
HF_Basilisk,
HF_Cockatrice,
HF_Domovoi
HF_Domovoi,
HF_Echidna
}
}
2 changes: 2 additions & 0 deletions src/Neo/Neo.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.2.1" />
<PackageReference Include="System.IO.Hashing" Version="9.0.0" />
</ItemGroup>

<ItemGroup>
Expand Down
Loading
Loading