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 17 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
5 changes: 2 additions & 3 deletions src/neo/Consensus/ConsensusService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,7 @@ private void OnCommitReceived(ConsensusPayload payload, Commit commit)
{
existingCommitPayload = payload;
}
else if (Crypto.VerifySignature(hashData, commit.Signature,
context.Validators[payload.ValidatorIndex].EncodePoint(false)))
else if (Crypto.VerifySignature(hashData, commit.Signature, context.Validators[payload.ValidatorIndex]))
{
existingCommitPayload = payload;
CheckCommits();
Expand Down Expand Up @@ -433,7 +432,7 @@ private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest m
byte[] hashData = context.EnsureHeader().GetHashData();
for (int i = 0; i < context.CommitPayloads.Length; i++)
if (context.CommitPayloads[i]?.ConsensusMessage.ViewNumber == context.ViewNumber)
if (!Crypto.VerifySignature(hashData, context.CommitPayloads[i].GetDeserializedMessage<Commit>().Signature, context.Validators[i].EncodePoint(false)))
if (!Crypto.VerifySignature(hashData, context.CommitPayloads[i].GetDeserializedMessage<Commit>().Signature, context.Validators[i]))
context.CommitPayloads[i] = null;

if (context.TransactionHashes.Length == 0)
Expand Down
78 changes: 58 additions & 20 deletions src/neo/Cryptography/Crypto.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Numerics;
using System.Security.Cryptography;

namespace Neo.Cryptography
Expand Down Expand Up @@ -32,38 +33,75 @@ 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, ECC.ECPoint pubkey)
{
if (pubkey.Length == 33 && (pubkey[0] == 0x02 || pubkey[0] == 0x03))
if (pubkey.Curve == ECC.ECCurve.Secp256r1)
{
try
byte[] buffer = pubkey.EncodePoint(false);
using (var ecdsa = ECDsa.Create(new ECParameters
{
pubkey = ECC.ECPoint.DecodePoint(pubkey, ECC.ECCurve.Secp256r1).EncodePoint(false).AsSpan(1);
}
catch
Curve = ECCurve.NamedCurves.nistP256,
Q = new ECPoint
{
X = buffer[1..33],
Y = buffer[33..]
}
}))
{
return false;
return ecdsa.VerifyData(message, signature, HashAlgorithmName.SHA256);
}
}
else if (pubkey.Length == 65 && pubkey[0] == 0x04)
{
pubkey = pubkey[1..];
}
else if (pubkey.Length != 64)
else
{
throw new ArgumentException();
var ecdsa = new ECC.ECDsa(pubkey);
var r = new BigInteger(signature[..32], true, true);
var s = new BigInteger(signature[32..], true, true);
return ecdsa.VerifySignature(message.Sha256(), r, s);
}
using (var ecdsa = ECDsa.Create(new ECParameters
}

public static bool VerifySignature(ReadOnlySpan<byte> message, ReadOnlySpan<byte> signature, ReadOnlySpan<byte> pubkey, ECC.ECCurve curve)
{
if (curve == ECC.ECCurve.Secp256r1)
{
Curve = ECCurve.NamedCurves.nistP256,
Q = new ECPoint
if (pubkey.Length == 33 && (pubkey[0] == 0x02 || pubkey[0] == 0x03))
{
X = pubkey[..32].ToArray(),
Y = pubkey[32..].ToArray()
try
{
pubkey = ECC.ECPoint.DecodePoint(pubkey, curve).EncodePoint(false).AsSpan(1);
}
catch
{
return false;
}
}
}))
else if (pubkey.Length == 65 && pubkey[0] == 0x04)
{
pubkey = pubkey[1..];
}
else
{
throw new ArgumentException();
}
using (var ecdsa = ECDsa.Create(new ECParameters
{
Curve = ECCurve.NamedCurves.nistP256,
Q = new ECPoint
{
X = pubkey[..32].ToArray(),
Y = pubkey[32..].ToArray()
}
}))
{
return ecdsa.VerifyData(message, signature, HashAlgorithmName.SHA256);
}
}
else
{
return ecdsa.VerifyData(message, signature, HashAlgorithmName.SHA256);
var ecdsa = new ECC.ECDsa(ECC.ECPoint.DecodePoint(pubkey, curve));
var r = new BigInteger(signature[..32], true, true);
var s = new BigInteger(signature[32..], true, true);
return ecdsa.VerifySignature(message.Sha256(), r, s);
}
}
}
Expand Down
107 changes: 107 additions & 0 deletions src/neo/Cryptography/ECC/ECDsa.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
using System;
using System.Numerics;
using System.Security.Cryptography;

namespace Neo.Cryptography.ECC
{
public class ECDsa
{
private readonly byte[] privateKey;
private readonly ECPoint publicKey;
private readonly ECCurve curve;

public ECDsa(byte[] privateKey, ECCurve curve)
: this(curve.G * privateKey)
{
this.privateKey = privateKey;
}

public ECDsa(ECPoint publicKey)
{
this.publicKey = publicKey;
this.curve = publicKey.Curve;
}

private BigInteger CalculateE(BigInteger n, ReadOnlySpan<byte> message)
{
int messageBitLength = message.Length * 8;
BigInteger trunc = new BigInteger(message, isUnsigned: true, isBigEndian: true);
if (n.GetBitLength() < messageBitLength)
{
trunc >>= messageBitLength - n.GetBitLength();
}
return trunc;
}

public BigInteger[] GenerateSignature(ReadOnlySpan<byte> message)
{
if (privateKey == null) throw new InvalidOperationException();
BigInteger e = CalculateE(curve.N, message);
BigInteger d = new BigInteger(privateKey, isUnsigned: true, isBigEndian: true);
BigInteger r, s;
using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
{
do
{
BigInteger k;
do
{
do
{
k = rng.NextBigInteger(curve.N.GetBitLength());
}
while (k.Sign == 0 || k.CompareTo(curve.N) >= 0);
ECPoint p = ECPoint.Multiply(curve.G, k);
BigInteger x = p.X.Value;
r = x.Mod(curve.N);
}
while (r.Sign == 0);
s = (k.ModInverse(curve.N) * (e + d * r)).Mod(curve.N);
if (s > curve.N / 2)
{
s = curve.N - s;
}
}
while (s.Sign == 0);
}
return new BigInteger[] { r, s };
}

private static ECPoint SumOfTwoMultiplies(ECPoint P, BigInteger k, ECPoint Q, BigInteger l)
{
int m = Math.Max(k.GetBitLength(), l.GetBitLength());
ECPoint Z = P + Q;
ECPoint R = P.Curve.Infinity;
for (int i = m - 1; i >= 0; --i)
{
R = R.Twice();
if (k.TestBit(i))
{
if (l.TestBit(i))
R = R + Z;
else
R = R + P;
}
else
{
if (l.TestBit(i))
R = R + Q;
}
}
return R;
}

public bool VerifySignature(ReadOnlySpan<byte> message, BigInteger r, BigInteger s)
{
if (r.Sign < 1 || s.Sign < 1 || r.CompareTo(curve.N) >= 0 || s.CompareTo(curve.N) >= 0)
return false;
BigInteger e = CalculateE(curve.N, message);
BigInteger c = s.ModInverse(curve.N);
BigInteger u1 = (e * c).Mod(curve.N);
BigInteger u2 = (r * c).Mod(curve.N);
ECPoint point = SumOfTwoMultiplies(curve.G, u1, publicKey, u2);
BigInteger v = point.X.Value.Mod(curve.N);
return v.Equals(r);
}
}
}
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.CheckMultisigWithECDsaSecp256r1);
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.VerifyWithECDsaSecp256r1);
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 @@ -92,7 +92,7 @@ private static bool IsMultiSigContract(byte[] script, out int m, out int n, List
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.CheckMultisigWithECDsaSecp256r1)
return false;
return true;
}
Expand All @@ -104,7 +104,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.VerifyWithECDsaSecp256r1)
return false;
return true;
}
Expand Down
52 changes: 45 additions & 7 deletions src/neo/SmartContract/InteropService.Crypto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@ 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 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 VerifyWithECDsaSecp256r1 = Register("Neo.Crypto.ECDsa.Secp256r1.Verify", Crypto_ECDsaSecp256r1Verify, 0_01000000, TriggerType.All, CallFlags.None);
public static readonly InteropDescriptor VerifyWithECDsaSecp256k1 = Register("Neo.Crypto.ECDsa.Secp256k1.Verify", Crypto_ECDsaSecp256k1Verify, 0_01000000, TriggerType.All, CallFlags.None);
public static readonly InteropDescriptor CheckMultisigWithECDsaSecp256r1 = Register("Neo.Crypto.ECDsa.Secp256r1.CheckMultiSig", Crypto_ECDsaSecp256r1CheckMultiSig, GetECDsaCheckMultiSigPrice, TriggerType.All, CallFlags.None);
public static readonly InteropDescriptor CheckMultisigWithECDsaSecp256k1 = Register("Neo.Crypto.ECDsa.Secp256k1.CheckMultiSig", Crypto_ECDsaSecp256k1CheckMultiSig, GetECDsaCheckMultiSigPrice, TriggerType.All, CallFlags.None);

private static long GetECDsaCheckMultiSigPrice(EvaluationStack stack, StoreView snapshot)
{
Expand All @@ -25,10 +29,34 @@ private static long GetECDsaCheckMultiSigPrice(EvaluationStack stack, StoreView
if (item is Array array) n = array.Count;
else n = (int)item.GetBigInteger();
if (n < 1) return 0;
return ECDsaVerify.Price * n;
return VerifyWithECDsaSecp256r1.Price * n;
}

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_ECDsaVerify(ApplicationEngine engine)
private static bool Crypto_ECDsaSecp256r1Verify(ApplicationEngine engine)
{
return Crypto_ECDsaVerify(engine, Cryptography.ECC.ECCurve.Secp256r1);
}

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

private static bool Crypto_ECDsaVerify(ApplicationEngine engine, Cryptography.ECC.ECCurve curve)
{
StackItem item0 = engine.CurrentContext.EvaluationStack.Pop();
ReadOnlySpan<byte> message = item0 switch
Expand All @@ -41,7 +69,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 @@ -50,7 +78,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.Secp256r1);
}

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

private static bool Crypto_ECDsaCheckMultiSig(ApplicationEngine engine, Cryptography.ECC.ECCurve curve)
{
StackItem item0 = engine.CurrentContext.EvaluationStack.Pop();
ReadOnlySpan<byte> message = item0 switch
Expand Down Expand Up @@ -98,7 +136,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
2 changes: 1 addition & 1 deletion src/neo/SmartContract/Manifest/ContractGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public static ContractGroup FromJson(JObject json)
/// <returns>Return true or false</returns>
public bool IsValid(UInt160 hash)
{
return Crypto.VerifySignature(hash.ToArray(), Signature, PubKey.EncodePoint(false));
return Crypto.VerifySignature(hash.ToArray(), Signature, PubKey);
}

public virtual JObject ToJson()
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 @@ -346,7 +346,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, null);
networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Crypto.VerifyWithECDsaSecp256r1, null, null);
}
else if (witness_script.IsMultiSigContract(out int m, out int n))
{
Expand All @@ -358,7 +358,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, null) * n;
networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Crypto.VerifyWithECDsaSecp256r1, null, null) * n;
}
else
{
Expand Down
Loading