From d7cf030a6ddca7929f239c8b36d794a52d064b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D1=85=D0=B5=D1=80=D0=B5=D1=82=D0=B8=D0=BA?= <херетик@indra.org> Date: Wed, 31 May 2023 12:46:53 +0100 Subject: [PATCH] All peerstore records encode/decode tests pass --- pkg/engine/onions/adaddress.go | 175 ++++++++++++++++++-------- pkg/engine/onions/adaddress_test.go | 62 +++++++++ pkg/engine/onions/adintro.go | 52 +------- pkg/engine/onions/adpeer.go | 143 +++++++++------------ pkg/engine/onions/adpeer_test.go | 44 +++---- pkg/engine/onions/adservice.go | 21 +--- pkg/engine/onions/interfaces.go | 2 +- pkg/engine/onions/introquery.go | 4 +- pkg/engine/onions/onion_skins.go | 15 --- pkg/engine/onions/peer.go | 187 ---------------------------- pkg/engine/onions/peer_test.go | 54 -------- 11 files changed, 276 insertions(+), 483 deletions(-) create mode 100644 pkg/engine/onions/adaddress_test.go delete mode 100644 pkg/engine/onions/peer.go delete mode 100644 pkg/engine/onions/peer_test.go diff --git a/pkg/engine/onions/adaddress.go b/pkg/engine/onions/adaddress.go index d6775d3f..d02c4c51 100644 --- a/pkg/engine/onions/adaddress.go +++ b/pkg/engine/onions/adaddress.go @@ -5,21 +5,23 @@ import ( "github.com/indra-labs/indra/pkg/crypto" "github.com/indra-labs/indra/pkg/crypto/nonce" "github.com/indra-labs/indra/pkg/crypto/sha256" + "github.com/indra-labs/indra/pkg/engine/coding" + "github.com/indra-labs/indra/pkg/engine/magic" "github.com/indra-labs/indra/pkg/engine/sess" - "github.com/indra-labs/indra/pkg/engine/sessions" "github.com/indra-labs/indra/pkg/util/qu" - "github.com/indra-labs/indra/pkg/util/slice" "github.com/indra-labs/indra/pkg/util/splice" + "github.com/libp2p/go-libp2p/core/peer" "github.com/multiformats/go-multiaddr" "net/netip" - "time" + "reflect" ) const ( AddressAdMagic = "adad" - AddressAdLen = nonce.IDLen + + AddressAdLen = magic.Len + + nonce.IDLen + + crypto.PubKeyLen + splice.AddrLen + 1 + - slice.Uint64Len + crypto.SigLen ) @@ -28,80 +30,143 @@ const ( // address. This means hidden service introducers for values over zero. // Hidden services have no value in the zero index, which is "/address/0". type AddressAd struct { - ID nonce.ID // To ensure no repeating message - Multiaddr multiaddr.Multiaddr // We only use a netip.AddrPort though. - Index byte // This is the index in the slice from Peer. - Expiry time.Time // zero for relay's public address (32 bit). - Sig crypto.SigBytes + ID nonce.ID // To ensure no repeating message + Key *crypto.Pub + Addr multiaddr.Multiaddr + Sig crypto.SigBytes } -func (x *AddressAd) Account(res *sess.Data, sm *sess.Manager, s *sessions.Data, - last bool) (skip bool, sd *sessions.Data) { - - return false, nil +func AddKeyToMultiaddr(in multiaddr.Multiaddr, pub *crypto.Pub) (ma multiaddr.Multiaddr) { + var pid peer.ID + var e error + if pid, e = peer.IDFromPublicKey(pub); fails(e) { + return + } + var k multiaddr.Multiaddr + if k, e = multiaddr.NewMultiaddr("/p2p/" + pid.String()); fails(e) { + return + } + ma = in.Encapsulate(k) + return } func (x *AddressAd) Decode(s *splice.Splice) (e error) { - var addr *netip.AddrPort - s.ReadID(&x.ID).ReadAddrPort(&addr).ReadByte(&x.Index).ReadTime(&x.Expiry) + if e = magic.TooShort(s.Remaining(), AddressAdLen-magic.Len, + PeerMagic); fails(e) { + + return + } + addr := &netip.AddrPort{} + s.ReadID(&x.ID). + ReadPubkey(&x.Key). + ReadAddrPort(&addr). + ReadSignature(&x.Sig) + //cid.Parse(x.Key.ToBytes()) + var ap multiaddr.Multiaddr + proto := "ip4" + if addr.Addr().Is6() { + proto = "ip6" + } + if ap, e = multiaddr.NewMultiaddr( + "/" + proto + "/" + addr.Addr().String() + + "/tcp/" + fmt.Sprint(addr.Port()), + ); fails(e) { + return + } + x.Addr = AddKeyToMultiaddr(ap, x.Key) return } func (x *AddressAd) Encode(s *splice.Splice) (e error) { - x.Splice(s.Magic(AddressAdMagic)) + log.T.S("encoding", reflect.TypeOf(x), + x.ID, x.Sig, + ) + x.Splice(s) return } -func (x *AddressAd) GetOnion() interface{} { return nil } -func (x *AddressAd) Gossip(sm *sess.Manager, c qu.C) {} -func (x *AddressAd) Handle(s *splice.Splice, p Onion, ni Ngin) (e error) { return nil } -func (x *AddressAd) Len() int { return AddressAdLen } -func (x *AddressAd) Magic() string { return "" } +func (x *AddressAd) GetOnion() interface{} { return x } -func (x *AddressAd) Sign(prv *crypto.Prv) (e error) { - s := splice.New(x.Len()) - if e = x.Encode(s); fails(e) { - return - } - var b []byte - if b, e = prv.Sign(s.GetUntil(s.GetCursor())); fails(e) { - return - } - if len(b) != crypto.SigLen { - return fmt.Errorf("signature incorrect length, got %d expected %d", - len(b), crypto.SigLen) - } - copy(x.Sig[:], b) - return nil +func (x *AddressAd) Gossip(sm *sess.Manager, c qu.C) { + log.D.F("propagating peer info for %s", + x.Key.ToBased32Abbreviated()) + Gossip(x, sm, c) + log.T.Ln("finished broadcasting peer info") } +func (x *AddressAd) Len() int { return AddressAdLen } + +func (x *AddressAd) Magic() string { return AddressAdMagic } + func (x *AddressAd) Splice(s *splice.Splice) { + x.SpliceWithoutSig(s) + s.Signature(x.Sig) +} + +func (x *AddressAd) SpliceWithoutSig(s *splice.Splice) { var e error - var ip, port string - if ip, e = x.Multiaddr.ValueForProtocol(multiaddr.P_IP4); fails(e) { + var ap netip.AddrPort + if ap, e = MultiaddrToAddrPort(x.Addr); fails(e) { + return } - if ip == "" { - if ip, e = x.Multiaddr.ValueForProtocol(multiaddr.P_IP6); fails(e) { + s.Magic(AddressAdMagic). + ID(x.ID). + Pubkey(x.Key). + AddrPort(&ap) +} + +func (x *AddressAd) Validate() bool { + s := splice.New(AddressAdLen - magic.Len) + x.SpliceWithoutSig(s) + hash := sha256.Single(s.GetUntil(s.GetCursor())) + key, e := x.Sig.Recover(hash) + if fails(e) { + return false + } + if key.Equals(x.Key) { + return true + } + return false +} + +func MultiaddrToAddrPort(ma multiaddr.Multiaddr) (ap netip.AddrPort, e error) { + var addrStr string + if addrStr, e = ma.ValueForProtocol(multiaddr.P_IP4); fails(e) { + if addrStr, e = ma.ValueForProtocol(multiaddr.P_IP6); fails(e) { return } } - // There is really no alternative to TCP so, TCP it is. - if port, e = x.Multiaddr.ValueForProtocol(multiaddr.P_TCP); fails(e) { + var portStr string + if portStr, e = ma.ValueForProtocol(multiaddr.P_TCP); fails(e) { return } - var addr netip.AddrPort - if addr, e = netip.ParseAddrPort(ip + ":" + port); fails(e) { - } - s.ID(x.ID).AddrPort(&addr).Byte(x.Index).Time(x.Expiry) -} - -func (x *AddressAd) Validate(s *splice.Splice) (pub *crypto.Pub) { - h := sha256.Single(s.GetRange(0, nonce.IDLen+splice.AddrLen+1+ - slice.Uint64Len)) - var e error - if pub, e = x.Sig.Recover(h); fails(e) { + if ap, e = netip.ParseAddrPort(addrStr + ":" + portStr); fails(e) { + return } return } -func (x *AddressAd) Wrap(inner Onion) {} +func NewAddressAd(id nonce.ID, key *crypto.Prv, + ma multiaddr.Multiaddr) (peerAd *AddressAd) { + + pub := crypto.DerivePub(key) + ma = AddKeyToMultiaddr(ma, pub) + log.D.Ln("ma", ma) + peerAd = &AddressAd{ + ID: id, + Key: pub, + Addr: ma, + } + s := splice.New(IntroLen - magic.Len) + peerAd.SpliceWithoutSig(s) + hash := sha256.Single(s.GetUntil(s.GetCursor())) + var e error + if peerAd.Sig, e = crypto.Sign(key, hash); fails(e) { + return nil + } + return +} + +func addrGen() coding.Codec { return &AddressAd{} } + +func init() { Register(AddressAdMagic, addrGen) } diff --git a/pkg/engine/onions/adaddress_test.go b/pkg/engine/onions/adaddress_test.go new file mode 100644 index 00000000..b0dca746 --- /dev/null +++ b/pkg/engine/onions/adaddress_test.go @@ -0,0 +1,62 @@ +package onions + +import ( + "github.com/indra-labs/indra/pkg/crypto" + "github.com/indra-labs/indra/pkg/crypto/nonce" + "github.com/indra-labs/indra/pkg/engine/coding" + log2 "github.com/indra-labs/indra/pkg/proc/log" + "github.com/indra-labs/indra/pkg/util/splice" + "github.com/multiformats/go-multiaddr" + "testing" +) + +func TestAdAddress(t *testing.T) { + log2.SetLogLevel(log2.Trace) + var e error + pr, _, _ := crypto.NewSigner() + id := nonce.NewID() + var ma multiaddr.Multiaddr + if ma, e = multiaddr.NewMultiaddr("/ip4/127.0.0.1/tcp/4242"); fails(e) { + t.FailNow() + } + aa := NewAddressAd(id, pr, ma) + log.D.S("ad", aa) + s := splice.New(aa.Len()) + if e = aa.Encode(s); fails(e) { + t.FailNow() + } + s.SetCursor(0) + var onc coding.Codec + if onc = Recognise(s); onc == nil { + t.Error("did not unwrap") + t.FailNow() + } + if e = onc.Decode(s); fails(e) { + t.Error("did not decode") + t.FailNow() + } + log.D.S(onc) + var ad *AddressAd + var ok bool + if ad, ok = onc.(*AddressAd); !ok { + t.Error("did not unwrap expected type") + t.FailNow() + } + if ad.ID != aa.ID { + t.Errorf("ID did not decode correctly") + t.FailNow() + } + if ad.Addr.String() != aa.Addr.String() { + t.Errorf("address did not decode correctly") + t.FailNow() + } + + if !ad.Key.Equals(crypto.DerivePub(pr)) { + t.Errorf("public key did not decode correctly") + t.FailNow() + } + if !ad.Validate() { + t.Errorf("received ad did not validate") + t.FailNow() + } +} diff --git a/pkg/engine/onions/adintro.go b/pkg/engine/onions/adintro.go index 3bf203a7..4e79c545 100644 --- a/pkg/engine/onions/adintro.go +++ b/pkg/engine/onions/adintro.go @@ -11,7 +11,6 @@ import ( "github.com/indra-labs/indra/pkg/engine/coding" "github.com/indra-labs/indra/pkg/engine/magic" "github.com/indra-labs/indra/pkg/engine/sess" - "github.com/indra-labs/indra/pkg/engine/sessions" "github.com/indra-labs/indra/pkg/util/qu" "github.com/indra-labs/indra/pkg/util/slice" "github.com/indra-labs/indra/pkg/util/splice" @@ -39,16 +38,7 @@ type IntroAd struct { Sig crypto.SigBytes } -func (x *IntroAd) Account( - res *sess.Data, - sm *sess.Manager, - s *sessions.Data, - last bool, -) (skip bool, sd *sessions.Data) { - - res.ID = x.ID - return -} +var _ coding.Codec = &IntroAd{} func (x *IntroAd) Decode(s *splice.Splice) (e error) { if e = magic.TooShort(s.Remaining(), IntroLen-magic.Len, @@ -84,41 +74,7 @@ func (x *IntroAd) Gossip(sm *sess.Manager, c qu.C) { x.Key.ToBased32Abbreviated()) } -func (x *IntroAd) Handle(s *splice.Splice, p Onion, ng Ngin) (e error) { - log.D.Ln("handling intro") - ng.GetHidden().Lock() - valid := x.Validate() - if valid { - - // Add to our current peer state advertisements. - _ = valid - - log.D.Ln(ng.Mgr().GetLocalNodeAddressString(), "validated intro", x.ID) - kb := x.Key.ToBytes() - if _, ok := ng.GetHidden().KnownIntros[x.Key.ToBytes()]; ok { - log.D.Ln(ng.Mgr().GetLocalNodeAddressString(), "already have intro") - ng.Pending().ProcessAndDelete(x.ID, &kb, s.GetAll()) - ng.GetHidden().Unlock() - return - } - log.D.F("%s storing intro for %s %s", - ng.Mgr().GetLocalNodeAddressString(), x.Key.ToBased32Abbreviated(), - x.ID) - ng.GetHidden().KnownIntros[x.Key.ToBytes()] = x - var ok bool - if ok, e = ng.Pending().ProcessAndDelete(x.ID, &kb, - s.GetAll()); ok || !fails(e) { - - ng.GetHidden().Unlock() - log.D.Ln("deleted pending response", x.ID) - return - } - } - ng.GetHidden().Unlock() - return -} - -func (x *IntroAd) Len() int { return IntroLen } +func (x *IntroAd) Len() int { return IntroLen } func (x *IntroAd) Magic() string { return IntroMagic } @@ -145,8 +101,6 @@ func (x *IntroAd) Validate() bool { return false } -func (x *IntroAd) Wrap(inner Onion) {} - func IntroSplice( s *splice.Splice, id nonce.ID, @@ -196,6 +150,6 @@ func NewIntroAd( return } -func init() { Register(IntroMagic, introGen) } +func init() { Register(IntroMagic, introGen) } func introGen() coding.Codec { return &IntroAd{} } diff --git a/pkg/engine/onions/adpeer.go b/pkg/engine/onions/adpeer.go index b5d6ee67..1226d387 100644 --- a/pkg/engine/onions/adpeer.go +++ b/pkg/engine/onions/adpeer.go @@ -1,133 +1,114 @@ package onions import ( - "fmt" "github.com/indra-labs/indra/pkg/crypto" "github.com/indra-labs/indra/pkg/crypto/nonce" "github.com/indra-labs/indra/pkg/crypto/sha256" "github.com/indra-labs/indra/pkg/engine/coding" "github.com/indra-labs/indra/pkg/engine/magic" "github.com/indra-labs/indra/pkg/engine/sess" - "github.com/indra-labs/indra/pkg/engine/sessions" "github.com/indra-labs/indra/pkg/util/qu" "github.com/indra-labs/indra/pkg/util/slice" "github.com/indra-labs/indra/pkg/util/splice" + "reflect" ) const ( - PeerAdMagic = "prad" - PeerAdLen = magic.Len + + PeerMagic = "peer" + PeerLen = magic.Len + nonce.IDLen + - crypto.PubKeyLen + + crypto.PubKeyLen + 1 + slice.Uint32Len + crypto.SigLen ) -var _ Ad = &PeerAd{} - -// PeerAd is the root identity document for an Indra peer. It is indexed by the -// Identity field, its public key. The slices found below it are derived via -// concatenation of strings with the keys and hashing to generate a derived -// field index, used to search the DHT for matches. -// -// The data stored for Peer must be signed with the key claimed by the Identity. -// For hidden services the address fields are signed in the DHT by the hidden -// service from their introduction solicitation, and the index from the current -// set is given by the hidden service. type PeerAd struct { - nonce.ID // To ensure no repeating message - Identity *crypto.Pub // Must match signature. - RelayRate uint32 // Zero means not relaying. + ID nonce.ID // This ensures never a repeated signed message. + Key *crypto.Pub // Identity key. + RelayRate uint32 Sig crypto.SigBytes } -func NewPeerAd( - id nonce.ID, - key *crypto.Prv, - relayRate uint32, -) (pa *PeerAd) { +func NewPeer(id nonce.ID, key *crypto.Prv, + relayRate uint32) (peerAd *PeerAd) { - pa = &PeerAd{ - ID: id, - Identity: crypto.DerivePub(key), - RelayRate: relayRate, - } + pk := crypto.DerivePub(key) + s := splice.New(IntroLen - magic.Len) + s.ID(id). + Pubkey(pk). + Uint32(relayRate) + hash := sha256.Single(s.GetUntil(s.GetCursor())) var e error - if e = pa.Sign(key); fails(e) { - return + var sign crypto.SigBytes + if sign, e = crypto.Sign(key, hash); fails(e) { + return nil + } + peerAd = &PeerAd{ + ID: id, + Key: pk, + RelayRate: relayRate, + Sig: sign, } return } -func (x *PeerAd) Account(res *sess.Data, sm *sess.Manager, s *sessions.Data, last bool) (skip bool, sd *sessions.Data) { - //TODO implement me - panic("implement me") -} - func (x *PeerAd) Decode(s *splice.Splice) (e error) { + if e = magic.TooShort(s.Remaining(), PeerLen-magic.Len, + PeerMagic); fails(e) { + + return + } s.ReadID(&x.ID). - ReadPubkey(&x.Identity). + ReadPubkey(&x.Key). ReadUint32(&x.RelayRate). ReadSignature(&x.Sig) - return nil + return } func (x *PeerAd) Encode(s *splice.Splice) (e error) { - s.Magic(PeerAdMagic) - x.Splice(s) - return nil + log.T.S("encoding", reflect.TypeOf(x), + x.ID, x.Sig, + ) + x.Splice(s.Magic(PeerMagic)) + return } -func (x *PeerAd) GetOnion() interface{} { return nil } -func (x *PeerAd) Gossip(sm *sess.Manager, c qu.C) {} -func (x *PeerAd) Handle(s *splice.Splice, p Onion, ni Ngin) (e error) { - return nil -} -func (x *PeerAd) Len() int { return PeerAdLen } -func (x *PeerAd) Magic() string { return PeerAdMagic } +func (x *PeerAd) GetOnion() interface{} { return x } -func (x *PeerAd) Sign(prv *crypto.Prv) (e error) { - s := splice.New(x.Len()) - if e = x.Encode(s); fails(e) { - return - } - var b []byte - if b, e = prv.Sign(s.GetUntil(s.GetCursor())); fails(e) { - return - } - if len(b) != crypto.SigLen { - return fmt.Errorf("signature incorrect length, got %d expected %d", - len(b), crypto.SigLen) - } - copy(x.Sig[:], b) - return nil +func (x *PeerAd) Gossip(sm *sess.Manager, c qu.C) { + log.D.F("propagating peer info for %s", + x.Key.ToBased32Abbreviated()) + Gossip(x, sm, c) + log.T.Ln("finished broadcasting peer info") } +func (x *PeerAd) Len() int { return PeerLen } + +func (x *PeerAd) Magic() string { return PeerMagic } + func (x *PeerAd) Splice(s *splice.Splice) { s.ID(x.ID). - Pubkey(x.Identity). + Pubkey(x.Key). Uint32(x.RelayRate). Signature(x.Sig) } -func (x *PeerAd) SpliceNoSig(s *splice.Splice) { +func (x *PeerAd) Validate() bool { + s := splice.New(PeerLen - magic.Len) s.ID(x.ID). - Pubkey(x.Identity). - Uint32(x.RelayRate). - Signature(x.Sig) -} - -func (x *PeerAd) Validate() (valid bool) { - s := splice.New(x.Len()) - x.SpliceNoSig(s) - h := sha256.Single(s.GetUntil(s.GetCursor())) - var e error - var pk *crypto.Pub - if pk, e = x.Sig.Recover(h); fails(e) { + Pubkey(x.Key). + Uint32(x.RelayRate) + hash := sha256.Single(s.GetUntil(s.GetCursor())) + key, e := x.Sig.Recover(hash) + if fails(e) { + return false } - return pk != nil + if key.Equals(x.Key) { + return true + } + return false } -func (x *PeerAd) Wrap(inner Onion) {} -func init() { Register(PeerAdMagic, peerAdGen) } -func peerAdGen() coding.Codec { return &PeerAd{} } +func init() { Register(PeerMagic, peerGen) } + +func peerGen() coding.Codec { return &PeerAd{} } diff --git a/pkg/engine/onions/adpeer_test.go b/pkg/engine/onions/adpeer_test.go index d9bf37d2..7d12515f 100644 --- a/pkg/engine/onions/adpeer_test.go +++ b/pkg/engine/onions/adpeer_test.go @@ -9,39 +9,41 @@ import ( "testing" ) -func TestOnionSkins_PeerAd(t *testing.T) { +func TestPeerAd(t *testing.T) { log2.SetLogLevel(log2.Trace) var e error - pr, _, _ := crypto.NewSigner() + pr, ks, _ := crypto.NewSigner() id := nonce.NewID() - peerAd := NewPeerAd(id, pr, 20000) - s := splice.New(peerAd.Len()) - if e = peerAd.Encode(s); fails(e) { - t.FailNow() + // in := NewPeer(id, pr, time.Now().Add(time.Hour)) + var prvs crypto.Privs + for i := range prvs { + prvs[i] = ks.Next() } + var pubs crypto.Pubs + for i := range pubs { + pubs[i] = crypto.DerivePub(prvs[i]) + } + pa := NewPeer(id, pr, 20000) + s := splice.New(pa.Len()) + if e = pa.Encode(s); fails(e) { + t.Fatalf("did not encode") + } + log.D.S(s.GetAll().ToBytes()) s.SetCursor(0) var onc coding.Codec if onc = Recognise(s); onc == nil { - t.Error("did not unwrap") - t.FailNow() + t.Fatalf("did not unwrap") } if e = onc.Decode(s); fails(e) { - t.Error("did not decode") - t.FailNow() + t.Fatalf("did not decode") } log.D.S(onc) - var pa *PeerAd + var peer *PeerAd var ok bool - if pa, ok = onc.(*PeerAd); !ok { - t.Error("did not unwrap expected type") - t.FailNow() + if peer, ok = onc.(*PeerAd); !ok { + t.Fatal("did not unwrap expected type") } - if pa.RelayRate != peerAd.RelayRate { - t.Errorf("relay rate did not decode correctly") - t.FailNow() - } - if !pa.Validate() { - t.Errorf("received intro did not validate") - t.FailNow() + if !peer.Validate() { + t.Fatalf("received PeerAd did not validate") } } diff --git a/pkg/engine/onions/adservice.go b/pkg/engine/onions/adservice.go index 84f395a7..a31d8541 100644 --- a/pkg/engine/onions/adservice.go +++ b/pkg/engine/onions/adservice.go @@ -8,7 +8,6 @@ import ( "github.com/indra-labs/indra/pkg/engine/coding" "github.com/indra-labs/indra/pkg/engine/magic" "github.com/indra-labs/indra/pkg/engine/sess" - "github.com/indra-labs/indra/pkg/engine/sessions" "github.com/indra-labs/indra/pkg/util/qu" "github.com/indra-labs/indra/pkg/util/slice" "github.com/indra-labs/indra/pkg/util/splice" @@ -26,7 +25,7 @@ const ( // ServiceAd stores a specification for the fee rate and the service port, which // must be a well known port to match with a type of service, eg 80 for web, 53 -// for DNS, etc. These are also attached to the Peer entry via concatenating +// for DNS, etc. These are also attached to the PeerAd entry via concatenating // "/service/N" where N is the index of the entry. A zero value at an index // signals to stop scanning for more subsequent values. type ServiceAd struct { @@ -37,6 +36,8 @@ type ServiceAd struct { Sig crypto.SigBytes } +var _ coding.Codec = &ServiceAd{} + func NewServiceAd( id nonce.ID, key *crypto.Prv, @@ -63,16 +64,6 @@ func NewServiceAd( return } -func (x *ServiceAd) Account( - res *sess.Data, - sm *sess.Manager, - s *sessions.Data, - last bool, -) (skip bool, sd *sessions.Data) { - - return false, nil -} - func (x *ServiceAd) Decode(s *splice.Splice) (e error) { s.ReadID(&x.ID). ReadPubkey(&x.Key). @@ -92,10 +83,6 @@ func (x *ServiceAd) GetOnion() interface{} { return nil } func (x *ServiceAd) Gossip(sm *sess.Manager, c qu.C) { } -func (x *ServiceAd) Handle(s *splice.Splice, p Onion, ni Ngin) (e error) { - return nil -} - func (x *ServiceAd) Len() int { return ServiceAdLen } func (x *ServiceAd) Magic() string { return "" } @@ -140,8 +127,6 @@ func (x *ServiceAd) Validate() (valid bool) { return false } -func (x *ServiceAd) Wrap(inner Onion) {} - func ServiceSplice( s *splice.Splice, id nonce.ID, diff --git a/pkg/engine/onions/interfaces.go b/pkg/engine/onions/interfaces.go index cdc95c1e..0861969c 100644 --- a/pkg/engine/onions/interfaces.go +++ b/pkg/engine/onions/interfaces.go @@ -32,7 +32,7 @@ type Onion interface { last bool) (skip bool, sd *sessions.Data) } type Ad interface { - Onion + coding.Codec Splice(s *splice.Splice) Validate() bool Gossip(sm *sess.Manager, c qu.C) diff --git a/pkg/engine/onions/introquery.go b/pkg/engine/onions/introquery.go index d220c379..326d61e3 100644 --- a/pkg/engine/onions/introquery.go +++ b/pkg/engine/onions/introquery.go @@ -83,9 +83,9 @@ func (x *IntroQuery) Handle(s *splice.Splice, p Onion, ng Ngin) (e error) { return } ng.GetHidden().Unlock() - iqr := Encode(il) + e = il.Encode(s) rb := FormatReply(GetRoutingHeaderFromCursor(s), x.Ciphers, x.Nonces, - iqr.GetAll()) + s.GetAll()) switch on1 := p.(type) { case *Crypt: sess := ng.Mgr().FindSessionByHeader(on1.ToPriv) diff --git a/pkg/engine/onions/onion_skins.go b/pkg/engine/onions/onion_skins.go index 82487fd7..f2c50246 100644 --- a/pkg/engine/onions/onion_skins.go +++ b/pkg/engine/onions/onion_skins.go @@ -222,17 +222,6 @@ func (o Skins) HiddenService(in *IntroAd, point *ExitPoint) Skins { }) } -func (o Skins) Intro(id nonce.ID, key *crypto.Prv, ap *netip.AddrPort, - expires time.Time) (sk Skins) { - return append(o, NewIntroAd(id, key, ap, 0, 0, expires)) -} - -func (o Skins) PeerAd(id nonce.ID, key *crypto.Prv, ap *netip.AddrPort, - relayRate uint32, port uint16, expires time.Time) (sk Skins) { - - return append(o, NewPeerAd(id, key, relayRate)) -} - func (o Skins) IntroQuery(id nonce.ID, hsk *crypto.Pub, exit *ExitPoint) Skins { return append(o, &IntroQuery{ ID: id, @@ -328,10 +317,6 @@ func (o Skins) Message(msg *Message, ks *crypto.KeySet) Skins { msg) } -func (o Skins) Peer(id nonce.ID, key *crypto.Prv, relayRate uint32, expires time.Time) (sk Skins) { - return append(o, NewPeer(id, key, expires, relayRate)) -} - // Ping is a message which checks the liveness of relays by ensuring they are // correctly relaying messages. // diff --git a/pkg/engine/onions/peer.go b/pkg/engine/onions/peer.go deleted file mode 100644 index 49ff43a6..00000000 --- a/pkg/engine/onions/peer.go +++ /dev/null @@ -1,187 +0,0 @@ -package onions - -import ( - "net/netip" - "reflect" - "time" - - "github.com/gookit/color" - - "github.com/indra-labs/indra/pkg/crypto" - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/crypto/sha256" - "github.com/indra-labs/indra/pkg/engine/coding" - "github.com/indra-labs/indra/pkg/engine/magic" - "github.com/indra-labs/indra/pkg/engine/node" - "github.com/indra-labs/indra/pkg/engine/sess" - "github.com/indra-labs/indra/pkg/engine/sessions" - "github.com/indra-labs/indra/pkg/util/qu" - "github.com/indra-labs/indra/pkg/util/slice" - "github.com/indra-labs/indra/pkg/util/splice" -) - -const ( - PeerMagic = "peer" - PeerLen = magic.Len + - nonce.IDLen + - crypto.PubKeyLen + 1 + - splice.AddrLen + - slice.Uint32Len + - slice.Uint64Len + - crypto.SigLen -) - -type Peer struct { - ID nonce.ID // This ensures never a repeated signed message. - Key *crypto.Pub // Identity key. - AddrPort *netip.AddrPort - RelayRate uint32 - Expiry time.Time - Sig crypto.SigBytes -} - -func NewPeer(id nonce.ID, key *crypto.Prv, expires time.Time, - relayRate uint32) (in *Peer) { - - pk := crypto.DerivePub(key) - s := splice.New(IntroLen - magic.Len) - s.ID(id). - Pubkey(pk). - Uint32(relayRate). - Uint64(uint64(expires.UnixNano())) - hash := sha256.Single(s.GetUntil(s.GetCursor())) - var e error - var sign crypto.SigBytes - if sign, e = crypto.Sign(key, hash); fails(e) { - return nil - } - in = &Peer{ - ID: id, - Key: pk, - RelayRate: relayRate, - Expiry: expires, - Sig: sign, - } - return -} - -func (x *Peer) Account(res *sess.Data, sm *sess.Manager, - s *sessions.Data, last bool) (skip bool, sd *sessions.Data) { - - res.ID = x.ID - return -} - -func (x *Peer) Decode(s *splice.Splice) (e error) { - if e = magic.TooShort(s.Remaining(), PeerLen-magic.Len, - PeerMagic); fails(e) { - - return - } - s.ReadID(&x.ID). - ReadPubkey(&x.Key). - ReadUint32(&x.RelayRate). - ReadTime(&x.Expiry). - ReadSignature(&x.Sig) - return -} - -func (x *Peer) Encode(s *splice.Splice) (e error) { - log.T.S("encoding", reflect.TypeOf(x), - x.ID, x.Expiry, x.Sig, - ) - x.Splice(s.Magic(PeerMagic)) - return -} - -func (x *Peer) GetOnion() interface{} { return x } - -func (x *Peer) Gossip(sm *sess.Manager, c qu.C) { - log.D.F("propagating peer info for %s", - x.Key.ToBased32Abbreviated()) - Gossip(x, sm, c) - log.T.Ln("finished broadcasting peer info") -} - -func (x *Peer) Handle(s *splice.Splice, p Onion, ng Ngin) (e error) { - ng.GetHidden().Lock() - valid := x.Validate() - if valid { - log.T.Ln(ng.Mgr().GetLocalNodeAddressString(), "validated intro", x.ID) - kb := x.Key.ToBytes() - if _, ok := ng.GetHidden().KnownIntros[x.Key.ToBytes()]; ok { - log.D.Ln(ng.Mgr().GetLocalNodeAddressString(), "already have intro") - ng.Pending().ProcessAndDelete(x.ID, &kb, s.GetAll()) - ng.GetHidden().Unlock() - return - } - log.D.F("%s storing intro for %s %s", - ng.Mgr().GetLocalNodeAddressString(), x.Key.ToBased32Abbreviated(), - x.ID) - // ng.GetHidden().KnownIntros[x.Key.ToBytes()] = x - var ok bool - if ok, e = ng.Pending().ProcessAndDelete(x.ID, &kb, - s.GetAll()); ok || fails(e) { - - ng.GetHidden().Unlock() - log.D.Ln("deleted pending response", x.ID) - return - } - log.D.F("%s sending out intro to %s to all known peers", - ng.Mgr().GetLocalNodeAddressString(), x.Key.ToBased32Abbreviated()) - sender := ng.Mgr().FindNodeByIdentity(x.Key) - nn := make(map[nonce.ID]*node.Node) - ng.Mgr().ForEachNode(func(n *node.Node) bool { - if n.ID != sender.ID { - nn[n.ID] = n - return true - } - return false - }) - counter := 0 - for i := range nn { - log.T.F("sending intro to %s", - color.Yellow.Sprint(nn[i].AddrPort.String())) - nn[i].Transport.Send(s.GetAll()) - counter++ - if counter < 2 { - continue - } - break - } - } - ng.GetHidden().Unlock() - return -} - -func (x *Peer) Len() int { return PeerLen } -func (x *Peer) Magic() string { return PeerMagic } - -func (x *Peer) Splice(s *splice.Splice) { - s.ID(x.ID). - Pubkey(x.Key). - Uint32(x.RelayRate). - Uint64(uint64(x.Expiry.UnixNano())). - Signature(x.Sig) -} - -func (x *Peer) Validate() bool { - s := splice.New(PeerLen - magic.Len) - s.ID(x.ID). - Pubkey(x.Key). - Uint32(x.RelayRate). - Uint64(uint64(x.Expiry.UnixNano())) - hash := sha256.Single(s.GetUntil(s.GetCursor())) - key, e := x.Sig.Recover(hash) - if fails(e) { - return false - } - if key.Equals(x.Key) && x.Expiry.After(time.Now()) { - return true - } - return false -} - -func (x *Peer) Wrap(inner Onion) {} -func init() { Register(PeerMagic, peerGen) } -func peerGen() coding.Codec { return &Peer{} } diff --git a/pkg/engine/onions/peer_test.go b/pkg/engine/onions/peer_test.go deleted file mode 100644 index 8f6fef27..00000000 --- a/pkg/engine/onions/peer_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package onions - -import ( - "testing" - "time" - - "github.com/indra-labs/indra/pkg/crypto" - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/engine/coding" - log2 "github.com/indra-labs/indra/pkg/proc/log" -) - -func TestOnionSkins_Peer(t *testing.T) { - log2.SetLogLevel(log2.Debug) - var e error - pr, ks, _ := crypto.NewSigner() - id := nonce.NewID() - // in := NewPeer(id, pr, time.Now().Add(time.Hour)) - var prvs crypto.Privs - for i := range prvs { - prvs[i] = ks.Next() - } - var pubs crypto.Pubs - for i := range pubs { - pubs[i] = crypto.DerivePub(prvs[i]) - } - on1 := Skins{}. - Peer(id, pr, 20000, time.Now().Add(time.Hour)) - on1 = append(on1, &End{}) - on := on1.Assemble() - s := Encode(on) - log.D.S(s.GetAll().ToBytes()) - s.SetCursor(0) - var onc coding.Codec - if onc = Recognise(s); onc == nil { - t.Error("did not unwrap") - t.FailNow() - } - if e = onc.Decode(s); fails(e) { - t.Error("did not decode") - t.FailNow() - } - log.D.S(onc) - var peer *Peer - var ok bool - if peer, ok = onc.(*Peer); !ok { - t.Error("did not unwrap expected type") - t.FailNow() - } - if !peer.Validate() { - t.Errorf("received Peer did not validate") - t.FailNow() - } -}