All peerstore records encode/decode tests pass

This commit is contained in:
херетик
2023-05-31 12:46:53 +01:00
parent a81ac4658d
commit d7cf030a6d
11 changed files with 276 additions and 483 deletions

View File

@@ -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 "<hash>/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) }

View File

@@ -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()
}
}

View File

@@ -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{} }

View File

@@ -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{} }

View File

@@ -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")
}
}

View File

@@ -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,

View File

@@ -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)

View File

@@ -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)

View File

@@ -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.
//

View File

@@ -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{} }

View File

@@ -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()
}
}