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

Add secp256k1 #1419

Merged
merged 18 commits into from
Apr 26, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
25 changes: 22 additions & 3 deletions src/neo/Cryptography/Crypto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,22 @@ public static byte[] Sign(byte[] message, byte[] prikey, byte[] pubkey)
}
}

public static bool VerifySignature(ReadOnlySpan<byte> message, ReadOnlySpan<byte> signature, ReadOnlySpan<byte> pubkey)
public static bool VerifySignature(ReadOnlySpan<byte> message, ReadOnlySpan<byte> signature, ReadOnlySpan<byte> pubkey, ECC.ECCurve.Curve curve = ECC.ECCurve.Curve.Secp256r1)
shargon marked this conversation as resolved.
Show resolved Hide resolved
{
if (pubkey.Length == 33 && (pubkey[0] == 0x02 || pubkey[0] == 0x03))
{
try
{
pubkey = ECC.ECPoint.DecodePoint(pubkey, ECC.ECCurve.Secp256r1).EncodePoint(false).AsSpan(1);
switch (curve)
{
case ECC.ECCurve.Curve.Secp256r1:
pubkey = ECC.ECPoint.DecodePoint(pubkey, ECC.ECCurve.Secp256r1).EncodePoint(false).AsSpan(1);
break;
case ECC.ECCurve.Curve.Secp256k1:
pubkey = ECC.ECPoint.DecodePoint(pubkey, ECC.ECCurve.Secp256k1).EncodePoint(false).AsSpan(1);
break;
default: return false;
}
}
catch
{
Expand All @@ -53,9 +62,19 @@ public static bool VerifySignature(ReadOnlySpan<byte> message, ReadOnlySpan<byte
{
throw new ArgumentException();
}

ECCurve ncurve;

switch (curve)
{
case ECC.ECCurve.Curve.Secp256r1: ncurve = ECCurve.NamedCurves.nistP256; break;
case ECC.ECCurve.Curve.Secp256k1: ncurve = ECCurve.CreateFromValue("1.3.132.0.10"); break;
shargon marked this conversation as resolved.
Show resolved Hide resolved
default: return false;
}

using (var ecdsa = ECDsa.Create(new ECParameters
{
Curve = ECCurve.NamedCurves.nistP256,
Curve = ncurve,
Q = new ECPoint
{
X = pubkey[..32].ToArray(),
Expand Down
6 changes: 6 additions & 0 deletions src/neo/Cryptography/ECC/ECCurve.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,11 @@ private ECCurve(BigInteger Q, BigInteger A, BigInteger B, BigInteger N, byte[] G
BigInteger.Parse("00FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", NumberStyles.AllowHexSpecifier),
("04" + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296" + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5").HexToBytes()
);

public enum Curve
{
Secp256r1 = 0x00,
Secp256k1 = 0x01
}
}
}
4 changes: 2 additions & 2 deletions src/neo/SmartContract/Contract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public static byte[] CreateMultiSigRedeemScript(int m, params ECPoint[] publicKe
}
sb.EmitPush(publicKeys.Length);
sb.Emit(OpCode.PUSHNULL);
sb.EmitSysCall(InteropService.Crypto.ECDsaCheckMultiSig);
sb.EmitSysCall(InteropService.Crypto.ECDsaSecp256r1CheckMultiSig);
return sb.ToArray();
}
}
Expand All @@ -102,7 +102,7 @@ public static byte[] CreateSignatureRedeemScript(ECPoint publicKey)
{
sb.EmitPush(publicKey.EncodePoint(true));
sb.Emit(OpCode.PUSHNULL);
sb.EmitSysCall(InteropService.Crypto.ECDsaVerify);
sb.EmitSysCall(InteropService.Crypto.ECDsaSecp256r1Verify);
return sb.ToArray();
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/neo/SmartContract/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public static bool IsMultiSigContract(this byte[] script, out int m, out int n)
if (script[i++] != (byte)OpCode.PUSHNULL) return false;
if (script[i++] != (byte)OpCode.SYSCALL) return false;
if (script.Length != i + 4) return false;
if (BitConverter.ToUInt32(script, i) != InteropService.Crypto.ECDsaCheckMultiSig)
if (BitConverter.ToUInt32(script, i) != InteropService.Crypto.ECDsaSecp256r1CheckMultiSig)
return false;
return true;
}
Expand All @@ -74,7 +74,7 @@ public static bool IsSignatureContract(this byte[] script)
|| script[1] != 33
|| script[35] != (byte)OpCode.PUSHNULL
|| script[36] != (byte)OpCode.SYSCALL
|| BitConverter.ToUInt32(script, 37) != InteropService.Crypto.ECDsaVerify)
|| BitConverter.ToUInt32(script, 37) != InteropService.Crypto.ECDsaSecp256r1Verify)
return false;
return true;
}
Expand Down
72 changes: 65 additions & 7 deletions src/neo/SmartContract/InteropService.Crypto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Neo.Network.P2P.Payloads;
using Neo.VM;
using Neo.VM.Types;
using Org.BouncyCastle.Crypto.Digests;
using System;
using System.Linq;
using Array = Neo.VM.Types.Array;
Expand All @@ -13,8 +14,13 @@ partial class InteropService
{
public static class Crypto
{
public static readonly InteropDescriptor ECDsaVerify = Register("Neo.Crypto.ECDsaVerify", Crypto_ECDsaVerify, 0_01000000, TriggerType.All, CallFlags.None);
public static readonly InteropDescriptor ECDsaCheckMultiSig = Register("Neo.Crypto.ECDsaCheckMultiSig", Crypto_ECDsaCheckMultiSig, GetECDsaCheckMultiSigPrice, TriggerType.All, CallFlags.None);
public static readonly InteropDescriptor SHA3Keccak256 = Register("Neo.Crypto.SHA3.Keccak256", Crypto_SHA3Keccak256, 0_01000000, TriggerType.All, CallFlags.None);
public static readonly InteropDescriptor SHA256 = Register("Neo.Crypto.SHA256", Crypto_SHA256, 0_01000000, TriggerType.All, CallFlags.None);
erikzhang marked this conversation as resolved.
Show resolved Hide resolved

public static readonly InteropDescriptor ECDsaSecp256r1Verify = Register("Neo.Crypto.ECDsa.Secp256r1.Verify", Crypto_ECDsaSecp256r1Verify, 0_01000000, TriggerType.All, CallFlags.None);
public static readonly InteropDescriptor ECDsaSecp256k1Verify = Register("Neo.Crypto.ECDsa.Secp256k1.Verify", Crypto_ECDsaSecp256k1Verify, 0_01000000, TriggerType.All, CallFlags.None);
public static readonly InteropDescriptor ECDsaSecp256r1CheckMultiSig = Register("Neo.Crypto.ECDsa.Secp256r1.CheckMultiSig", Crypto_ECDsaSecp256r1CheckMultiSig, GetECDsaCheckMultiSigPrice, TriggerType.All, CallFlags.None);
public static readonly InteropDescriptor ECDsaSecp256k1CheckMultiSig = Register("Neo.Crypto.ECDsa.Secp256k1.CheckMultiSig", Crypto_ECDsaSecp256k1CheckMultiSig, GetECDsaCheckMultiSigPrice, TriggerType.All, CallFlags.None);
erikzhang marked this conversation as resolved.
Show resolved Hide resolved

private static long GetECDsaCheckMultiSigPrice(EvaluationStack stack)
{
Expand All @@ -24,10 +30,52 @@ private static long GetECDsaCheckMultiSigPrice(EvaluationStack stack)
if (item is Array array) n = array.Count;
else n = (int)item.GetBigInteger();
if (n < 1) return 0;
return ECDsaVerify.Price * n;
return ECDsaSecp256r1Verify.Price * n;
}

private static bool Crypto_SHA3Keccak256(ApplicationEngine engine)
{
StackItem item0 = engine.CurrentContext.EvaluationStack.Pop();
ReadOnlySpan<byte> value = item0 switch
{
InteropInterface _interface => _interface.GetInterface<IVerifiable>().GetHashData(),
Null _ => engine.ScriptContainer.GetHashData(),
_ => item0.GetSpan()
};

var digest = new KeccakDigest(256);
var output = new byte[digest.GetDigestSize()];
digest.BlockUpdate(value.ToArray(), 0, value.Length);
digest.DoFinal(output, 0);
engine.CurrentContext.EvaluationStack.Push(output);
return true;
}

private static bool Crypto_ECDsaVerify(ApplicationEngine engine)
private static bool Crypto_SHA256(ApplicationEngine engine)
{
StackItem item0 = engine.CurrentContext.EvaluationStack.Pop();
ReadOnlySpan<byte> value = item0 switch
{
InteropInterface _interface => _interface.GetInterface<IVerifiable>().GetHashData(),
Null _ => engine.ScriptContainer.GetHashData(),
_ => item0.GetSpan()
};

engine.CurrentContext.EvaluationStack.Push(value.ToArray().Sha256());
return true;
}

private static bool Crypto_ECDsaSecp256r1Verify(ApplicationEngine engine)
{
return Crypto_ECDsaVerify(engine, Cryptography.ECC.ECCurve.Curve.Secp256r1);
}

private static bool Crypto_ECDsaSecp256k1Verify(ApplicationEngine engine)
{
return Crypto_ECDsaVerify(engine, Cryptography.ECC.ECCurve.Curve.Secp256k1);
}

private static bool Crypto_ECDsaVerify(ApplicationEngine engine, Cryptography.ECC.ECCurve.Curve curve)
{
StackItem item0 = engine.CurrentContext.EvaluationStack.Pop();
ReadOnlySpan<byte> message = item0 switch
Expand All @@ -40,7 +88,7 @@ private static bool Crypto_ECDsaVerify(ApplicationEngine engine)
ReadOnlySpan<byte> signature = engine.CurrentContext.EvaluationStack.Pop().GetSpan();
try
{
engine.CurrentContext.EvaluationStack.Push(Cryptography.Crypto.VerifySignature(message, signature, pubkey));
engine.CurrentContext.EvaluationStack.Push(Cryptography.Crypto.VerifySignature(message, signature, pubkey, curve));
}
catch (ArgumentException)
{
Expand All @@ -49,7 +97,17 @@ private static bool Crypto_ECDsaVerify(ApplicationEngine engine)
return true;
}

private static bool Crypto_ECDsaCheckMultiSig(ApplicationEngine engine)
private static bool Crypto_ECDsaSecp256r1CheckMultiSig(ApplicationEngine engine)
{
return Crypto_ECDsaCheckMultiSig(engine, Cryptography.ECC.ECCurve.Curve.Secp256r1);
}

private static bool Crypto_ECDsaSecp256k1CheckMultiSig(ApplicationEngine engine)
{
return Crypto_ECDsaCheckMultiSig(engine, Cryptography.ECC.ECCurve.Curve.Secp256k1);
}

private static bool Crypto_ECDsaCheckMultiSig(ApplicationEngine engine, Cryptography.ECC.ECCurve.Curve curve)
{
StackItem item0 = engine.CurrentContext.EvaluationStack.Pop();
ReadOnlySpan<byte> message = item0 switch
Expand Down Expand Up @@ -97,7 +155,7 @@ private static bool Crypto_ECDsaCheckMultiSig(ApplicationEngine engine)
{
for (int i = 0, j = 0; fSuccess && i < m && j < n;)
{
if (Cryptography.Crypto.VerifySignature(message, signatures[i], pubkeys[j]))
if (Cryptography.Crypto.VerifySignature(message, signatures[i], pubkeys[j], curve))
i++;
j++;
if (m - i > n - j)
Expand Down
4 changes: 2 additions & 2 deletions src/neo/Wallets/Wallet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ public static long CalculateNetworkFee(byte[] witness_script, ref int size)
if (witness_script.IsSignatureContract())
{
size += 67 + witness_script.GetVarSize();
networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Crypto.ECDsaVerify, null);
networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Crypto.ECDsaSecp256r1Verify, null);
}
else if (witness_script.IsMultiSigContract(out int m, out int n))
{
Expand All @@ -366,7 +366,7 @@ public static long CalculateNetworkFee(byte[] witness_script, ref int size)
networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * n;
using (ScriptBuilder sb = new ScriptBuilder())
networkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(n).ToArray()[0]];
networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Crypto.ECDsaVerify, null) * n;
networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Crypto.ECDsaSecp256r1Verify, null) * n;
}
else
{
Expand Down
1 change: 1 addition & 0 deletions src/neo/neo.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

<ItemGroup>
<PackageReference Include="Akka" Version="1.3.16" />
<PackageReference Include="BouncyCastle.NetCore" Version="1.8.5" />
<PackageReference Include="K4os.Compression.LZ4" Version="1.1.11" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
Expand Down
12 changes: 6 additions & 6 deletions tests/neo.UnitTests/SmartContract/UT_Contract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public void TestGetAddress()
Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33);
expectedArray[35] = (byte)OpCode.PUSHNULL;
expectedArray[36] = (byte)OpCode.SYSCALL;
Array.Copy(BitConverter.GetBytes(InteropService.Crypto.ECDsaVerify), 0, expectedArray, 37, 4);
Array.Copy(BitConverter.GetBytes(InteropService.Crypto.ECDsaSecp256r1Verify), 0, expectedArray, 37, 4);
Assert.AreEqual(expectedArray.ToScriptHash().ToAddress(), contract.Address);
}

Expand All @@ -44,7 +44,7 @@ public void TestGetScriptHash()
Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33);
expectedArray[35] = (byte)OpCode.PUSHNULL;
expectedArray[36] = (byte)OpCode.SYSCALL;
Array.Copy(BitConverter.GetBytes(InteropService.Crypto.ECDsaVerify), 0, expectedArray, 37, 4);
Array.Copy(BitConverter.GetBytes(InteropService.Crypto.ECDsaSecp256r1Verify), 0, expectedArray, 37, 4);
Assert.AreEqual(expectedArray.ToScriptHash(), contract.ScriptHash);
}

Expand Down Expand Up @@ -86,7 +86,7 @@ public void TestCreateMultiSigContract()
expectedArray[71] = (byte)OpCode.PUSH2;
expectedArray[72] = (byte)OpCode.PUSHNULL;
expectedArray[73] = (byte)OpCode.SYSCALL;
Array.Copy(BitConverter.GetBytes(InteropService.Crypto.ECDsaCheckMultiSig), 0, expectedArray, 74, 4);
Array.Copy(BitConverter.GetBytes(InteropService.Crypto.ECDsaSecp256r1CheckMultiSig), 0, expectedArray, 74, 4);
CollectionAssert.AreEqual(expectedArray, contract.Script);
Assert.AreEqual(2, contract.ParameterList.Length);
Assert.AreEqual(ContractParameterType.Signature, contract.ParameterList[0]);
Expand Down Expand Up @@ -122,7 +122,7 @@ public void TestCreateMultiSigRedeemScript()
expectedArray[71] = (byte)OpCode.PUSH2;
expectedArray[72] = (byte)OpCode.PUSHNULL;
expectedArray[73] = (byte)OpCode.SYSCALL;
Array.Copy(BitConverter.GetBytes(InteropService.Crypto.ECDsaCheckMultiSig), 0, expectedArray, 74, 4);
Array.Copy(BitConverter.GetBytes(InteropService.Crypto.ECDsaSecp256r1CheckMultiSig), 0, expectedArray, 74, 4);
CollectionAssert.AreEqual(expectedArray, script);
}

Expand All @@ -140,7 +140,7 @@ public void TestCreateSignatureContract()
Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33);
expectedArray[35] = (byte)OpCode.PUSHNULL;
expectedArray[36] = (byte)OpCode.SYSCALL;
Array.Copy(BitConverter.GetBytes(InteropService.Crypto.ECDsaVerify), 0, expectedArray, 37, 4);
Array.Copy(BitConverter.GetBytes(InteropService.Crypto.ECDsaSecp256r1Verify), 0, expectedArray, 37, 4);
CollectionAssert.AreEqual(expectedArray, contract.Script);
Assert.AreEqual(1, contract.ParameterList.Length);
Assert.AreEqual(ContractParameterType.Signature, contract.ParameterList[0]);
Expand All @@ -160,7 +160,7 @@ public void TestCreateSignatureRedeemScript()
Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33);
expectedArray[35] = (byte)OpCode.PUSHNULL;
expectedArray[36] = (byte)OpCode.SYSCALL;
Array.Copy(BitConverter.GetBytes(InteropService.Crypto.ECDsaVerify), 0, expectedArray, 37, 4);
Array.Copy(BitConverter.GetBytes(InteropService.Crypto.ECDsaSecp256r1Verify), 0, expectedArray, 37, 4);
CollectionAssert.AreEqual(expectedArray, script);
}
}
Expand Down
Loading