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

Eip1459 #3707

Merged
merged 30 commits into from
Dec 19, 2021
Merged

Eip1459 #3707

Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
5def8e1
added ENR project and tests and updated workflow files
tkstanczak Dec 17, 2021
9f4d299
first draft, before testing - EIP778 implementation
tkstanczak Dec 17, 2021
4c41e49
eip-778 passing test vector
tkstanczak Dec 18, 2021
4fbf879
make test a bit more readable
tkstanczak Dec 18, 2021
050fc7f
cleanup / modernize / dewarningize discovery
tkstanczak Dec 18, 2021
a294c08
better invalid signature handling
tkstanczak Dec 18, 2021
eb68682
discovery config bootnodes moved to network config
tkstanczak Dec 18, 2021
607d889
removed all nullability warnings from tests
tkstanczak Dec 18, 2021
41abcd2
peer loader test fix
tkstanczak Dec 18, 2021
f06bab1
fixed test
tkstanczak Dec 18, 2021
4e0056e
removed more warnings
tkstanczak Dec 18, 2021
3c4d56a
basic implementation of EIP-868
tkstanczak Dec 18, 2021
929a3dd
TODOs
tkstanczak Dec 18, 2021
f039381
improve API and add verification and compressed keys
tkstanczak Dec 18, 2021
61f5ec5
Merge branch 'eip778' into eip868
tkstanczak Dec 18, 2021
3d369dd
validation of ENR signatures and compressed keys
tkstanczak Dec 18, 2021
6317529
sending back ENR messages + better discovery notifications
tkstanczak Dec 18, 2021
fb1f3d3
fixed discovery test post ENR
tkstanczak Dec 18, 2021
60fd409
more tests for EIP-868, serializers and flow
tkstanczak Dec 18, 2021
1a95a6c
fix of one test - Ping serializer will not include sequence when ENR …
tkstanczak Dec 18, 2021
1dda0dd
ENR tests for lifecycle manager
tkstanczak Dec 18, 2021
bd6914c
link to eth entry
tkstanczak Dec 18, 2021
7adc57e
Merge branch 'master' into eip868
tkstanczak Dec 19, 2021
3322509
fix to last ENR
tkstanczak Dec 19, 2021
e961734
Merge branch 'master' into eip868
tkstanczak Dec 19, 2021
51195c7
DNS project placeholders
tkstanczak Dec 18, 2021
30a262c
100% coverage on ENR
tkstanczak Dec 18, 2021
703d427
my first DNS query
tkstanczak Dec 18, 2021
a0634d9
DNS discovery basic implementation
tkstanczak Dec 19, 2021
f56cb33
removed the heavy test
tkstanczak Dec 19, 2021
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
Prev Previous commit
Next Next commit
validation of ENR signatures and compressed keys
  • Loading branch information
tkstanczak committed Dec 18, 2021

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit 3d369dddaa7b276c488b1224b1510d4342568087
4 changes: 2 additions & 2 deletions src/Nethermind/Nethermind.Crypto/BlockHeaderExtensions.cs
Original file line number Diff line number Diff line change
@@ -22,13 +22,13 @@ namespace Nethermind.Crypto
{
public static class BlockHeaderExtensions
{
private static HeaderDecoder _headerDecoder = new();
private static readonly HeaderDecoder _headerDecoder = new();

public static Keccak CalculateHash(this BlockHeader header, RlpBehaviors behaviors = RlpBehaviors.None)
{
KeccakRlpStream stream = new();
_headerDecoder.Encode(stream, header, behaviors);
return new Keccak(stream.GetHash());
return stream.GetHash();
}

public static Keccak CalculateHash(this Block block, RlpBehaviors behaviors = RlpBehaviors.None)
6 changes: 3 additions & 3 deletions src/Nethermind/Nethermind.Crypto/KeccakRlpStream.cs
Original file line number Diff line number Diff line change
@@ -26,9 +26,9 @@ public class KeccakRlpStream : RlpStream
{
private readonly KeccakHash _keccakHash;

public byte[] GetHash()
public Keccak GetHash()
{
return _keccakHash.Hash;
return new Keccak(_keccakHash.Hash);
}

public KeccakRlpStream()
@@ -58,7 +58,7 @@ public override byte ReadByte()
throw new NotSupportedException("Cannot read form Keccak");
}

protected override Span<byte> Read(int length)
public override Span<byte> Read(int length)
{
throw new NotSupportedException("Cannot read form Keccak");
}
Original file line number Diff line number Diff line change
@@ -96,10 +96,6 @@ private void RequestEnr()

public void ProcessEnrResponseMsg(EnrResponseMsg enrResponseMsg)
{
// TODO: 1) compressed sig validation
// TODO: 2) compressed public key publication
// TODO: 3) use test vector from ENR to check private key -> compressed public key -> sig
// TODO: 4) test for NodeRecord validation
// TODO: 5) tests for the whole req resp flow
// TODO: 6) use the fork ID knowledge to mark each node with info on the forkhash

Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@ namespace Nethermind.Network.Discovery.Serializers;
public abstract class DiscoveryMsgSerializerBase
{
private readonly PrivateKey _privateKey;
private readonly IEcdsa _ecdsa;
protected readonly IEcdsa _ecdsa;

private readonly INodeIdResolver _nodeIdResolver;

Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@

using System.Net;
using Nethermind.Core.Crypto;
using Nethermind.Core.Extensions;
using Nethermind.Crypto;
using Nethermind.Network.Discovery.Messages;
using Nethermind.Network.Enr;
@@ -25,8 +26,13 @@ namespace Nethermind.Network.Discovery.Serializers;

public class EnrResponseMsgSerializer : DiscoveryMsgSerializerBase, IMessageSerializer<EnrResponseMsg>
{
private readonly NodeRecordSigner _nodeRecordSigner;

public EnrResponseMsgSerializer(IEcdsa ecdsa, IPrivateKeyGenerator nodeKey, INodeIdResolver nodeIdResolver)
: base(ecdsa, nodeKey, nodeIdResolver) { }
: base(ecdsa, nodeKey, nodeIdResolver)
{
_nodeRecordSigner = new NodeRecordSigner(ecdsa, nodeKey.Generate());
}

public byte[] Serialize(EnrResponseMsg msg)
{
@@ -35,70 +41,25 @@ public byte[] Serialize(EnrResponseMsg msg)
Rlp.Encode(msg.ExpirationTime)
).Bytes;

byte[] serializedMsg = Serialize((byte) msg.MsgType, data);
byte[] serializedMsg = Serialize((byte)msg.MsgType, data);
return serializedMsg;
}

public EnrResponseMsg Deserialize(byte[] msgBytes)
{
(PublicKey FarPublicKey, byte[] Mdc, byte[] Data) results = PrepareForDeserialization(msgBytes);
RlpStream rlpStream = results.Data.AsRlpStream();

rlpStream.ReadSequenceLength();
rlpStream.DecodeKeccak(); // do I need to check it?
int currentPosition = rlpStream.Position;
int recordRlpLength = rlpStream.ReadSequenceLength();

NodeRecord nodeRecord = new();
rlpStream.DecodeKeccak(); // skip (not sure if needed to verify)

// TODO: may want to move this deserialization logic to something reusable

ReadOnlySpan<byte> sigBytes = rlpStream.DecodeByteArraySpan();
// TODO: The recipient of the packet should verify that the node record is signed by node who sent ENRResponse.
Signature signature = new(sigBytes, 0);
int enrSequence = rlpStream.DecodeInt();
while (rlpStream.Position < currentPosition + recordRlpLength)
string resHex = results.Data.Slice(rlpStream.Position).ToHexString();
NodeRecord nodeRecord = _nodeRecordSigner.Deserialize(rlpStream);
if (!_nodeRecordSigner.Verify(nodeRecord))
{
string key = rlpStream.DecodeString();
switch (key)
{
case EnrContentKey.Eth:
_ = rlpStream.ReadSequenceLength();
_ = rlpStream.ReadSequenceLength();
byte[] forkHash = rlpStream.DecodeByteArray();
int nextBlock = rlpStream.DecodeInt();
nodeRecord.SetEntry(new EthEntry(forkHash, nextBlock));
break;
case EnrContentKey.Id:
rlpStream.SkipItem();
nodeRecord.SetEntry(IdEntry.Instance);
break;
case EnrContentKey.Ip:
ReadOnlySpan<byte> ipBytes = rlpStream.DecodeByteArraySpan();
IPAddress address = new(ipBytes);
nodeRecord.SetEntry(new IpEntry(address));
break;
case EnrContentKey.Tcp:
int tcpPort = rlpStream.DecodeInt();
nodeRecord.SetEntry(new TcpEntry(tcpPort));
break;
case EnrContentKey.Udp:
int udpPort = rlpStream.DecodeInt();
nodeRecord.SetEntry(new UdpEntry(udpPort));
break;
case EnrContentKey.Secp256K1:
ReadOnlySpan<byte> keyBytes = rlpStream.DecodeByteArraySpan();
CompressedPublicKey compressedPublicKey = new(keyBytes);
nodeRecord.SetEntry(new Secp256K1Entry(compressedPublicKey));
break;
default:
rlpStream.SkipItem();
break;
}
throw new NetworkingException("Invalid ENR signature", NetworkExceptionType.Discovery);
}

nodeRecord.Sequence = enrSequence;
EnrResponseMsg msg = new (results.FarPublicKey, nodeRecord);

EnrResponseMsg msg = new(results.FarPublicKey, nodeRecord);
return msg;
}
}
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
using Nethermind.Core.Crypto;
using Nethermind.Core.Extensions;
using Nethermind.Crypto;
using Nethermind.Serialization.Rlp;
using NUnit.Framework;

namespace Nethermind.Network.Enr.Test;
@@ -71,7 +72,6 @@ public void Can_verify_signature()

Ecdsa ecdsa = new();
PrivateKey privateKey = new("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291");
PublicKey expectedPublicKey = privateKey.PublicKey;
NodeRecordSigner signer = new(ecdsa, privateKey);
NodeRecord nodeRecord = new();
nodeRecord.SetEntry(new IpEntry(
@@ -88,6 +88,22 @@ public void Can_verify_signature()
nodeRecord.Sequence = 1; // override

signer.Sign(nodeRecord);
Assert.AreEqual(signer.Verify(nodeRecord), compressedPublicKey);
Assert.IsTrue(signer.Verify(nodeRecord));
}

[TestCase("f897b840421561b4ed5de28a7100e0a5005ecc0ba6ba6cc18528061e811704c8794fec965cba63831051d134bdc801c0c90d31a30d241074095311ffe6628d5545478b770a83657468c7c68496516d06808269648276348269708436ed0a0a89736563703235366b31a103f5c110132b0374805d4453f55577cc9c58bb1a08f822b9b3722132e3095f69728374637082765f8375647082765f")]
[TestCase("f897b8406fb9316953b51793ee43316fe14f2d0ac0b356b86815175c6d231840bd6f24e504bfa6492ccc1f4b0853b02ae44fbee861f52044dd08e4a23edf6187ea5e46e71583657468c7c68420c327fc80826964827634826970847f00000189736563703235366b31a102ba4be3a4095b23fe90a850709394476bf23c9788ad124325a6163f342e05a7308374637082765f8375647082765f")]
[TestCase("f89fb8401d2ab9d1937f7d3524feec8edb45e3abc4e4a01ca227615502bcad2cd68eaf804fc5865f6a5551bd5c39f56ee4d4c005c69be3efc44f2a9ff312d71de13a62de8207ab83657468c7c6843de1adaf808269648276348269708467e4b73289736563703235366b31a102bb8f962e961a1d82dac4bc32b71e491da35bcd69e18bec31aba9b9fadd0e1a1184736e6170c08374637082765f8375647082765f")]
public void Can_deserialize_and_verify_real_world_cases(string testCase)
{
Ecdsa ecdsa = new();
PrivateKey privateKey = new("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291");
NodeRecordSigner signer = new(ecdsa, privateKey);
RlpStream rlpStream = Bytes.FromHexString(testCase).AsRlpStream();
NodeRecord nodeRecord = signer.Deserialize(rlpStream);
string hex = nodeRecord.GetHex();
Console.WriteLine(testCase);
Console.WriteLine(hex);
Assert.IsTrue(signer.Verify(nodeRecord));
}
}
6 changes: 3 additions & 3 deletions src/Nethermind/Nethermind.Network.Enr/EthEntry.cs
Original file line number Diff line number Diff line change
@@ -30,9 +30,9 @@ public EthEntry(byte[] forkHash, long nextBlock) : base(new ForkId(forkHash, nex

protected override int GetRlpLengthOfValue()
{
return Rlp.LengthOf(
Rlp.LengthOfSequence(
Rlp.LengthOfSequence(5 + Rlp.LengthOf(Value.NextBlock))));
return Rlp.LengthOfSequence(
Rlp.LengthOfSequence(
5 + Rlp.LengthOf(Value.NextBlock)));
}

protected override void EncodeValue(RlpStream rlpStream)
36 changes: 34 additions & 2 deletions src/Nethermind/Nethermind.Network.Enr/NodeRecord.cs
Original file line number Diff line number Diff line change
@@ -16,6 +16,8 @@
//

using Nethermind.Core.Crypto;
using Nethermind.Core.Extensions;
using Nethermind.Crypto;
using Nethermind.Serialization.Rlp;

namespace Nethermind.Network.Enr;
@@ -25,9 +27,13 @@ public class NodeRecord
private int _sequence;

private string? _enrString;

private Keccak? _contentHash;

private SortedDictionary<string, EnrContentEntry> Entries { get; } = new();

public byte[]? OriginalContentRlp { get; set; }

public int Sequence
{
get => _sequence;
@@ -36,6 +42,8 @@ public int Sequence
if (_sequence != value)
{
_sequence = value;
_enrString = null;
_contentHash = null;
Signature = null;
}
}
@@ -48,6 +56,21 @@ public string EnrString
return _enrString ??= CreateEnrString();
}
}

public Keccak ContentHash
{
get
{
return _contentHash ??= CalculateContentHash();
}
}

private Keccak CalculateContentHash()
{
KeccakRlpStream rlpStream = new();
EncodeContent(rlpStream);
return rlpStream.GetHash();
}

public Signature? Signature { get; private set; }

@@ -116,7 +139,7 @@ private int GetRlpLengthWithSignature()
GetContentLengthWithSignature());
}

public void EncodeContent(RlpStream rlpStream)
private void EncodeContent(RlpStream rlpStream)
{
int contentLength = GetContentLengthWithoutSignature();
rlpStream.StartSequence(contentLength);
@@ -127,14 +150,23 @@ public void EncodeContent(RlpStream rlpStream)
}
}

public string GetHex()
{
int contentLength = GetContentLengthWithSignature();
int totalLength = Rlp.LengthOfSequence(contentLength);
RlpStream rlpStream = new(totalLength);
Encode(rlpStream);
return rlpStream.Data!.ToHexString();
}

private void Encode(RlpStream rlpStream)
{
RequireSignature();

int contentLength = GetContentLengthWithSignature();
rlpStream.StartSequence(contentLength);
rlpStream.Encode(Signature!.Bytes);
rlpStream.Encode(Sequence);
rlpStream.Encode(Sequence); // a different sequence here (not RLP sequence)
foreach ((_, EnrContentEntry contentEntry) in Entries.OrderBy(e => e.Key))
{
contentEntry.Encode(rlpStream);
Loading