Skip to content

Commit

Permalink
[neox-2.x] add interop services (#1597)
Browse files Browse the repository at this point in the history
* add interop services: Neo.Cryptography.Keccak256 and Neo.Cryptography.Ecrecover

* Add KeyRecover function Unit Test

* add wrong key case

* fix format

* Add keccak256 Hash Function test

* Add hexadecimal value check in keccak256
Add recovery id in generate signature

* remove unnecessary blank

* fix whitespace formatting

* fix some problems

* clean

* refactor code

* Remove keccak256 hash algorithm

* Remove keccak256 UT

* remove unnecessary reference

* Remove unused usings

* Add exception type Judgement

* Add signature validate part

* fix format

* remove docheck

Co-authored-by: 晨 黎 <lichen@neo.org>
Co-authored-by: Luchuan <luchuan@neo.org>
  • Loading branch information
3 people authored May 6, 2020
1 parent a8756d5 commit 1f7da51
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 8 deletions.
62 changes: 62 additions & 0 deletions neo.UnitTests/UT_ECDsa.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Security.Cryptography;
using System.Numerics;
using System;

namespace Neo.Cryptography.ECC.Tests
{
[TestClass()]
public class UT_ECDsa
{
public static byte[] generatekey(int privateKeyLength)
{
byte[] privateKey = new byte[privateKeyLength];
using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
{
rng.GetBytes(privateKey);
}
return privateKey;
}

[TestMethod()]
public void KeyRecoverTest()
{
KeyRecover(ECCurve.Secp256k1);
KeyRecover(ECCurve.Secp256r1);
}

public static void KeyRecover(ECCurve Curve)
{
byte[] privateKey = generatekey(32);
ECPoint publicKey = Curve.G * privateKey;
ECDsa ecdsa = new ECDsa(privateKey, Curve);
byte[] message = System.Text.Encoding.Default.GetBytes("HelloWorld");
BigInteger[] signatures = ecdsa.GenerateSignature(message);
BigInteger r = signatures[0];
BigInteger s = signatures[1];
bool v;
if (signatures[2] == 0)
{
v = true;
}
else
{
v = false;
}
ECPoint recoverKey = ECDsa.KeyRecover(Curve, r, s, message, v);
Assert.IsTrue(recoverKey.Equals(publicKey));
//wrong r part
r = new BigInteger(generatekey(32));
s = new BigInteger(generatekey(32));
try
{
recoverKey = ECDsa.KeyRecover(Curve, r, s, message, v);
Assert.IsFalse(recoverKey.Equals(publicKey));
}
catch (Exception e)
{
Assert.IsTrue(e.GetType() == typeof(ArithmeticException));
}
}
}
}
5 changes: 2 additions & 3 deletions neo.UnitTests/UT_Helper.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.Network.P2P;
using Neo.SmartContract;
using Neo.Wallets;
using FluentAssertions;

namespace Neo.UnitTests
{
Expand Down Expand Up @@ -37,6 +37,5 @@ public void ToScriptHash()
UInt160 res = testByteArray.ToScriptHash();
res.Should().Be(UInt160.Parse("2d3b96ae1bcc5a585e075e3b81920210dec16302"));
}

}
}
53 changes: 51 additions & 2 deletions neo/Cryptography/ECC/ECDsa.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public BigInteger[] GenerateSignature(byte[] message)
if (privateKey == null) throw new InvalidOperationException();
BigInteger e = CalculateE(curve.N, message);
BigInteger d = new BigInteger(privateKey.Reverse().Concat(new byte[1]).ToArray());
BigInteger r, s;
BigInteger r, s, isEven;
using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
{
do
Expand All @@ -53,6 +53,7 @@ public BigInteger[] GenerateSignature(byte[] message)
}
while (k.Sign == 0 || k.CompareTo(curve.N) >= 0);
ECPoint p = ECPoint.Multiply(curve.G, k);
isEven = p.Y.Value & 1;
BigInteger x = p.X.Value;
r = x.Mod(curve.N);
}
Expand All @@ -61,11 +62,12 @@ public BigInteger[] GenerateSignature(byte[] message)
if (s > curve.N / 2)
{
s = curve.N - s;
isEven = isEven ^ 1;
}
}
while (s.Sign == 0);
}
return new BigInteger[] { r, s };
return new BigInteger[] { r, s, isEven };
}

private static ECPoint SumOfTwoMultiplies(ECPoint P, BigInteger k, ECPoint Q, BigInteger l)
Expand Down Expand Up @@ -104,5 +106,52 @@ public bool VerifySignature(byte[] message, BigInteger r, BigInteger s)
BigInteger v = point.X.Value.Mod(curve.N);
return v.Equals(r);
}

public static ECPoint KeyRecover(ECCurve curve, BigInteger r, BigInteger s, byte[] msg, bool isEven)
{
if (r < BigInteger.One || s < BigInteger.One)
{
throw new ArithmeticException("Invalid signature");
}
// calculate h
BigInteger h = (curve.Q + 1 + 2 * (BigInteger)Math.Sqrt((double)curve.Q)) / curve.N;
BigInteger e;
ECPoint Q = new ECPoint();
int messageBitLength;

for (int i = 0; i <= h; i++)
{
// step 1.1 x = (n * i) + r
BigInteger Rx = curve.N * i + r;
if (Rx > curve.Q) break;

// step 1.2 and 1.3 get point R
ECPoint R;
if (isEven)
{
R = ECPoint.DecompressPoint(0, Rx, curve);
}
else
{
R = ECPoint.DecompressPoint(1, Rx, curve);
}
if (ECPoint.Multiply(R, curve.N) != curve.Infinity)
continue;

// step 1.5 compute e
messageBitLength = msg.Length * 8;
e = new BigInteger(msg.Reverse().Concat(new byte[1]).ToArray());
if (curve.N.GetBitLength() < messageBitLength)
{
e >>= messageBitLength - curve.N.GetBitLength();
}

// step 1.6 Q = r^-1 (sR-eG)
BigInteger invr = r.ModInverse(curve.N);
ECPoint t0 = ECPoint.Multiply(R, s) - ECPoint.Multiply(curve.G, e);
Q = ECPoint.Multiply(t0, invr);
}
return Q;
}
}
}
2 changes: 1 addition & 1 deletion neo/Cryptography/ECC/ECPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public static ECPoint DecodePoint(byte[] encoded, ECCurve curve)
return p;
}

private static ECPoint DecompressPoint(int yTilde, BigInteger X1, ECCurve curve)
public static ECPoint DecompressPoint(int yTilde, BigInteger X1, ECCurve curve)
{
ECFieldElement x = new ECFieldElement(X1, curve);
ECFieldElement alpha = x * (x.Square() + curve.A) + curve.B;
Expand Down
34 changes: 33 additions & 1 deletion neo/SmartContract/NeoService.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using Neo.Cryptography.ECC;
using Neo.Cryptography;
using Neo.Cryptography.ECC;
using Neo.Ledger;
using Neo.Network.P2P;
using Neo.Network.P2P.Payloads;
using Neo.Persistence;
using Neo.SmartContract.Enumerators;
Expand Down Expand Up @@ -99,6 +101,8 @@ public NeoService(TriggerType trigger, Snapshot snapshot)
Register("Neo.Iterator.Keys", Iterator_Keys, 1);
Register("Neo.Iterator.Values", Iterator_Values, 1);
Register("Neo.Iterator.Concat", Iterator_Concat, 1);
Register("Neo.Cryptography.Secp256k1Recover", Secp256k1Recover, 100);
Register("Neo.Cryptography.Secp256r1Recover", Secp256r1Recover, 100);

#region Aliases
Register("Neo.Iterator.Next", Enumerator_Next, 1);
Expand Down Expand Up @@ -165,6 +169,34 @@ public NeoService(TriggerType trigger, Snapshot snapshot)
#endregion
}

private bool Secp256k1Recover(ExecutionEngine engine)
{
return EccRecover(ECCurve.Secp256k1, engine);
}

private bool Secp256r1Recover(ExecutionEngine engine)
{
return EccRecover(ECCurve.Secp256r1, engine);
}

private bool EccRecover(ECCurve curve, ExecutionEngine engine)
{
var r = new System.Numerics.BigInteger(engine.CurrentContext.EvaluationStack.Pop().GetByteArray().Reverse().Concat(new byte[1]).ToArray());
var s = new System.Numerics.BigInteger(engine.CurrentContext.EvaluationStack.Pop().GetByteArray().Reverse().Concat(new byte[1]).ToArray());
bool v = engine.CurrentContext.EvaluationStack.Pop().GetBoolean();
byte[] messageHash = engine.CurrentContext.EvaluationStack.Pop().GetByteArray();
try
{
ECPoint point = ECDsa.KeyRecover(curve, r, s, messageHash, v);
engine.CurrentContext.EvaluationStack.Push(point.EncodePoint(false).Skip(1).ToArray());
}
catch
{
engine.CurrentContext.EvaluationStack.Push(new byte[0]);
}
return true;
}

private bool Blockchain_GetAccount(ExecutionEngine engine)
{
UInt160 hash = new UInt160(engine.CurrentContext.EvaluationStack.Pop().GetByteArray());
Expand Down
3 changes: 2 additions & 1 deletion neo/SmartContract/StandardService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Neo.Cryptography.ECC;
using Neo.Cryptography;
using Neo.Cryptography.ECC;
using Neo.IO;
using Neo.Ledger;
using Neo.Network.P2P.Payloads;
Expand Down

0 comments on commit 1f7da51

Please sign in to comment.