added signature to introduction for spoofing protection

This commit is contained in:
херетик
2023-02-26 12:04:15 +00:00
parent 859bbecfe2
commit 2d91a0e7c0
10 changed files with 98 additions and 33 deletions

View File

@@ -2,9 +2,9 @@ package hiddenservice
import ( import (
"git-indra.lan/indra-labs/indra" "git-indra.lan/indra-labs/indra"
"git-indra.lan/indra-labs/indra/pkg/crypto/key/pub"
"git-indra.lan/indra-labs/indra/pkg/crypto/nonce" "git-indra.lan/indra-labs/indra/pkg/crypto/nonce"
"git-indra.lan/indra-labs/indra/pkg/crypto/sha256" "git-indra.lan/indra-labs/indra/pkg/crypto/sha256"
"git-indra.lan/indra-labs/indra/pkg/messages/intro"
"git-indra.lan/indra-labs/indra/pkg/messages/magicbytes" "git-indra.lan/indra-labs/indra/pkg/messages/magicbytes"
log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" log2 "git-indra.lan/indra-labs/indra/pkg/proc/log"
"git-indra.lan/indra-labs/indra/pkg/splice" "git-indra.lan/indra-labs/indra/pkg/splice"
@@ -14,7 +14,7 @@ import (
const ( const (
MagicString = "hs" MagicString = "hs"
Len = magicbytes.Len + nonce.IDLen + pub.KeyLen + Len = magicbytes.Len + nonce.IDLen + intro.Len +
3*sha256.Len + nonce.IVLen*3 3*sha256.Len + nonce.IVLen*3
) )
@@ -30,11 +30,7 @@ var (
// header for any client that requests it. // header for any client that requests it.
type Layer struct { type Layer struct {
nonce.ID nonce.ID
// Identity is a public key identifying the hidden service. It is encoded intro.Layer
// into Bech32 encoding to function like an IP address, with a 2 byte
// truncated hash check suffix to eliminate possible human input errors and
// ending in ".indra" to indicate it is an indra hidden service.
Identity *pub.Key
// Ciphers is a set of 3 symmetric ciphers that are to be used in their // Ciphers is a set of 3 symmetric ciphers that are to be used in their
// given order over the reply message from the service. // given order over the reply message from the service.
Ciphers [3]sha256.Hash Ciphers [3]sha256.Hash
@@ -56,7 +52,9 @@ func (x *Layer) Encode(b slice.Bytes, c *slice.Cursor) {
splice.Splice(b, c). splice.Splice(b, c).
Magic(Magic). Magic(Magic).
ID(x.ID). ID(x.ID).
Pubkey(x.Identity). Pubkey(x.Key).
AddrPort(x.AddrPort).
Signature(x.Bytes).
Hash(x.Ciphers[0]).Hash(x.Ciphers[1]).Hash(x.Ciphers[2]). Hash(x.Ciphers[0]).Hash(x.Ciphers[1]).Hash(x.Ciphers[2]).
IV(x.Nonces[0]).IV(x.Nonces[1]).IV(x.Nonces[2]) IV(x.Nonces[0]).IV(x.Nonces[1]).IV(x.Nonces[2])
} }
@@ -67,7 +65,9 @@ func (x *Layer) Decode(b slice.Bytes, c *slice.Cursor) (e error) {
} }
splice.Splice(b, c). splice.Splice(b, c).
ReadID(&x.ID). ReadID(&x.ID).
ReadPubkey(&x.Identity). ReadPubkey(&x.Key).
ReadAddrPort(&x.AddrPort).
ReadSignature(x.Bytes).
ReadHash(&x.Ciphers[0]).ReadHash(&x.Ciphers[1]).ReadHash(&x.Ciphers[2]). ReadHash(&x.Ciphers[0]).ReadHash(&x.Ciphers[1]).ReadHash(&x.Ciphers[2]).
ReadIV(&x.Nonces[0]).ReadIV(&x.Nonces[1]).ReadIV(&x.Nonces[2]) ReadIV(&x.Nonces[0]).ReadIV(&x.Nonces[1]).ReadIV(&x.Nonces[2])
return return

View File

@@ -4,17 +4,27 @@ import (
"net" "net"
"net/netip" "net/netip"
"git-indra.lan/indra-labs/indra"
"git-indra.lan/indra-labs/indra/pkg/crypto/key/prv"
"git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub"
"git-indra.lan/indra-labs/indra/pkg/crypto/key/sig"
"git-indra.lan/indra-labs/indra/pkg/crypto/sha256"
"git-indra.lan/indra-labs/indra/pkg/messages/magicbytes" "git-indra.lan/indra-labs/indra/pkg/messages/magicbytes"
log2 "git-indra.lan/indra-labs/indra/pkg/proc/log"
"git-indra.lan/indra-labs/indra/pkg/splice" "git-indra.lan/indra-labs/indra/pkg/splice"
"git-indra.lan/indra-labs/indra/pkg/types" "git-indra.lan/indra-labs/indra/pkg/types"
"git-indra.lan/indra-labs/indra/pkg/util/slice" "git-indra.lan/indra-labs/indra/pkg/util/slice"
) )
var (
log = log2.GetLogger(indra.PathBase)
check = log.E.Chk
)
const ( const (
MagicString = "in" MagicString = "in"
AddrLen = net.IPv6len + 3 AddrLen = net.IPv6len + 3
Len = magicbytes.Len + pub.KeyLen + AddrLen Len = magicbytes.Len + pub.KeyLen + AddrLen + sig.Len
) )
var ( var (
@@ -24,17 +34,57 @@ var (
type Layer struct { type Layer struct {
*pub.Key *pub.Key
*netip.AddrPort *netip.AddrPort
sig.Bytes
}
func New(key *prv.Key, ap *netip.AddrPort) (im *Layer) {
pk := pub.Derive(key)
bap, _ := ap.MarshalBinary()
pkb := pk.ToBytes()
hash := sha256.Single(append(pkb[:], bap...))
var e error
var sign sig.Bytes
if sign, e = sig.Sign(key, hash); check(e) {
return nil
}
im = &Layer{
Key: pk,
AddrPort: ap,
Bytes: sign,
}
return
}
func (im *Layer) Validate() bool {
bap, _ := im.AddrPort.MarshalBinary()
pkb := im.Key.ToBytes()
hash := sha256.Single(append(pkb[:], bap...))
key, e := im.Bytes.Recover(hash)
if check(e) {
return false
}
kb := key.ToBytes()
if kb.Equals(pkb) {
return true
}
return false
} }
func (im *Layer) Insert(o types.Onion) {} func (im *Layer) Insert(o types.Onion) {}
func (im *Layer) Len() int { return Len } func (im *Layer) Len() int { return Len }
func (im *Layer) Encode(b slice.Bytes, c *slice.Cursor) { func (im *Layer) Encode(b slice.Bytes, c *slice.Cursor) {
splice.Splice(b, c).Magic(Magic).Pubkey(im.Key).AddrPort(im.AddrPort) splice.Splice(b, c).
Magic(Magic).
Pubkey(im.Key).
AddrPort(im.AddrPort).
Signature(im.Bytes)
return return
} }
func (im *Layer) Decode(b slice.Bytes, c *slice.Cursor) (e error) { func (im *Layer) Decode(b slice.Bytes, c *slice.Cursor) (e error) {
splice.Splice(b, c).ReadPubkey(&im.Key).ReadAddrPort(&im.AddrPort) splice.Splice(b, c).
ReadPubkey(&im.Key).
ReadAddrPort(&im.AddrPort).ReadSignature(im.Bytes)
return return
} }

View File

@@ -44,7 +44,7 @@ var (
// is concealed to the hops except for the encryption crypt they decrypt using // is concealed to the hops except for the encryption crypt they decrypt using
// their Payload key, delivered in this message. // their Payload key, delivered in this message.
type Layer struct { type Layer struct {
nonce.ID nonce.ID // only used by a node
Hop byte // only used by a node Hop byte // only used by a node
Header, Payload *prv.Key Header, Payload *prv.Key
types.Onion types.Onion

View File

@@ -10,9 +10,9 @@ import (
"go.uber.org/atomic" "go.uber.org/atomic"
"git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv"
"git-indra.lan/indra-labs/indra/pkg/crypto/key/pub"
"git-indra.lan/indra-labs/indra/pkg/crypto/nonce" "git-indra.lan/indra-labs/indra/pkg/crypto/nonce"
"git-indra.lan/indra-labs/indra/pkg/crypto/sha256" "git-indra.lan/indra-labs/indra/pkg/crypto/sha256"
"git-indra.lan/indra-labs/indra/pkg/messages/intro"
log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" log2 "git-indra.lan/indra-labs/indra/pkg/proc/log"
"git-indra.lan/indra-labs/indra/pkg/service" "git-indra.lan/indra-labs/indra/pkg/service"
"git-indra.lan/indra-labs/indra/pkg/transport" "git-indra.lan/indra-labs/indra/pkg/transport"
@@ -290,10 +290,10 @@ func TestClient_HiddenService(t *testing.T) {
t.Error(e) t.Error(e)
t.FailNow() t.FailNow()
} }
identPub := pub.Derive(identPrv)
id := nonce.NewID() id := nonce.NewID()
il := intro.New(identPrv, clients[0].GetLocalNodeAddress())
clients[0].SendIntro(id, clients[0].Sessions[i+returns], clients[0].SendIntro(id, clients[0].Sessions[i+returns],
identPub, func(id nonce.ID, b slice.Bytes) { il, func(id nonce.ID, b slice.Bytes) {
log.I.Ln("success") log.I.Ln("success")
}) })
} }
@@ -303,7 +303,7 @@ func TestClient_HiddenService(t *testing.T) {
} }
} }
func TestClient_HiddenServiceBroadcast(t *testing.T) { func TestClient_HiddenServiceBroadcast(t *testing.T) {
log2.SetLogLevel(log2.Trace) log2.SetLogLevel(log2.Info)
var clients []*Engine var clients []*Engine
var e error var e error
const returns = 2 const returns = 2
@@ -340,10 +340,12 @@ func TestClient_HiddenServiceBroadcast(t *testing.T) {
t.Error(e) t.Error(e)
t.FailNow() t.FailNow()
} }
identPub := pub.Derive(identPrv) log2.SetLogLevel(log2.Trace)
// identPub := pub.Derive(identPrv)
id := nonce.NewID() id := nonce.NewID()
il := intro.New(identPrv, clients[0].GetLocalNodeAddress())
clients[0].SendIntro(id, clients[0].Sessions[returns], clients[0].SendIntro(id, clients[0].Sessions[returns],
identPub, func(id nonce.ID, b slice.Bytes) { il, func(id nonce.ID, b slice.Bytes) {
log.I.Ln("success") log.I.Ln("success")
}) })
time.Sleep(time.Second * 5) time.Sleep(time.Second * 5)

View File

@@ -10,8 +10,8 @@ func (eng *Engine) hiddenservice(hs *hiddenservice.Layer, b slice.Bytes,
c *slice.Cursor, prev types.Onion) { c *slice.Cursor, prev types.Onion) {
log.D.F("%s adding introduction for key %s", eng.GetLocalNodeAddress(), log.D.F("%s adding introduction for key %s", eng.GetLocalNodeAddress(),
hs.Identity.ToBase32()) hs.Layer.Key.ToBase32())
eng.Introductions.AddIntro(hs.Identity, b[*c:]) eng.Introductions.AddIntro(hs.Layer.Key, b[*c:])
log.I.Ln("stored new introduction, starting broadcast") log.I.Ln("stored new introduction, starting broadcast")
go eng.hiddenserviceBroadcaster(hs.Identity) go eng.hiddenserviceBroadcaster(hs.Layer.Key)
} }

View File

@@ -10,7 +10,7 @@ func (eng *Engine) session(on *session.Layer, b slice.Bytes,
c *slice.Cursor, prev types.Onion) { c *slice.Cursor, prev types.Onion) {
log.D.Ln(prev == nil) log.D.Ln(prev == nil)
log.T.F("incoming session %s", on.ID) log.T.F("incoming session %x", on.PreimageHash())
pi := eng.FindPendingPreimage(on.PreimageHash()) pi := eng.FindPendingPreimage(on.PreimageHash())
if pi != nil { if pi != nil {
// We need to delete this first in case somehow two such messages arrive // We need to delete this first in case somehow two such messages arrive

View File

@@ -1,12 +1,12 @@
package relay package relay
import ( import (
"git-indra.lan/indra-labs/indra/pkg/crypto/key/pub"
"git-indra.lan/indra-labs/indra/pkg/crypto/nonce" "git-indra.lan/indra-labs/indra/pkg/crypto/nonce"
"git-indra.lan/indra-labs/indra/pkg/messages/intro"
"git-indra.lan/indra-labs/indra/pkg/util/slice" "git-indra.lan/indra-labs/indra/pkg/util/slice"
) )
func (eng *Engine) SendIntro(id nonce.ID, target *Session, ident *pub.Key, func (eng *Engine) SendIntro(id nonce.ID, target *Session, intr *intro.Layer,
hook func(id nonce.ID, b slice.Bytes)) { hook func(id nonce.ID, b slice.Bytes)) {
log.I.Ln(target.Hop) log.I.Ln(target.Hop)
@@ -16,7 +16,7 @@ func (eng *Engine) SendIntro(id nonce.ID, target *Session, ident *pub.Key,
se := eng.SelectHops(hops, s) se := eng.SelectHops(hops, s)
var c Circuit var c Circuit
copy(c[:], se) copy(c[:], se)
o := HiddenService(id, ident, se[len(se)-1], c, eng.KeySet) o := HiddenService(id, intr, se[len(se)-1], c, eng.KeySet)
log.D.Ln("sending out intro onion") log.D.Ln("sending out intro onion")
res := eng.PostAcctOnion(o) res := eng.PostAcctOnion(o)
eng.SendWithOneHook(c[0].AddrPort, res, hook) eng.SendWithOneHook(c[0].AddrPort, res, hook)

View File

@@ -5,9 +5,10 @@ import (
"git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub"
"git-indra.lan/indra-labs/indra/pkg/crypto/key/signer" "git-indra.lan/indra-labs/indra/pkg/crypto/key/signer"
"git-indra.lan/indra-labs/indra/pkg/crypto/nonce" "git-indra.lan/indra-labs/indra/pkg/crypto/nonce"
"git-indra.lan/indra-labs/indra/pkg/messages/intro"
) )
func HiddenService(id nonce.ID, ident *pub.Key, client *Session, s Circuit, func HiddenService(id nonce.ID, il *intro.Layer, client *Session, s Circuit,
ks *signer.KeySet) Skins { ks *signer.KeySet) Skins {
var prvs [3]*prv.Key var prvs [3]*prv.Key
@@ -25,7 +26,7 @@ func HiddenService(id nonce.ID, ident *pub.Key, client *Session, s Circuit,
ReverseCrypt(s[0], ks.Next(), n[0], 3). ReverseCrypt(s[0], ks.Next(), n[0], 3).
ReverseCrypt(s[1], ks.Next(), n[1], 2). ReverseCrypt(s[1], ks.Next(), n[1], 2).
ReverseCrypt(s[2], ks.Next(), n[2], 1). ReverseCrypt(s[2], ks.Next(), n[2], 1).
HiddenService(id, ident, prvs, pubs, returnNonces). HiddenService(id, il, prvs, pubs, returnNonces).
ReverseCrypt(s[3], prvs[0], n[3], 3). ReverseCrypt(s[3], prvs[0], n[3], 3).
ReverseCrypt(s[4], prvs[1], n[4], 2). ReverseCrypt(s[4], prvs[1], n[4], 2).
ReverseCrypt(client, prvs[2], n[5], 1) ReverseCrypt(client, prvs[2], n[5], 1)

View File

@@ -17,6 +17,7 @@ import (
"git-indra.lan/indra-labs/indra/pkg/messages/forward" "git-indra.lan/indra-labs/indra/pkg/messages/forward"
"git-indra.lan/indra-labs/indra/pkg/messages/getbalance" "git-indra.lan/indra-labs/indra/pkg/messages/getbalance"
"git-indra.lan/indra-labs/indra/pkg/messages/hiddenservice" "git-indra.lan/indra-labs/indra/pkg/messages/hiddenservice"
"git-indra.lan/indra-labs/indra/pkg/messages/intro"
"git-indra.lan/indra-labs/indra/pkg/messages/noop" "git-indra.lan/indra-labs/indra/pkg/messages/noop"
"git-indra.lan/indra-labs/indra/pkg/messages/response" "git-indra.lan/indra-labs/indra/pkg/messages/response"
"git-indra.lan/indra-labs/indra/pkg/messages/reverse" "git-indra.lan/indra-labs/indra/pkg/messages/reverse"
@@ -113,13 +114,13 @@ func (o Skins) GetBalance(id, confID nonce.ID, prvs [3]*prv.Key,
}) })
} }
func (o Skins) HiddenService(id nonce.ID, addr *pub.Key, prvs [3]*prv.Key, func (o Skins) HiddenService(id nonce.ID, intr *intro.Layer, prvs [3]*prv.Key,
pubs [3]*pub.Key, nonces [3]nonce.IV) Skins { pubs [3]*pub.Key, nonces [3]nonce.IV) Skins {
return append(o, &hiddenservice.Layer{ return append(o, &hiddenservice.Layer{
ID: id, ID: id,
Identity: addr, Layer: *intr,
Ciphers: GenCiphers(prvs, pubs), Ciphers: GenCiphers(prvs, pubs),
Nonces: nonces, Nonces: nonces,
}) })
} }

View File

@@ -11,6 +11,7 @@ import (
"git-indra.lan/indra-labs/indra/pkg/crypto/key/cloak" "git-indra.lan/indra-labs/indra/pkg/crypto/key/cloak"
"git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv"
"git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub"
"git-indra.lan/indra-labs/indra/pkg/crypto/key/sig"
"git-indra.lan/indra-labs/indra/pkg/crypto/nonce" "git-indra.lan/indra-labs/indra/pkg/crypto/nonce"
"git-indra.lan/indra-labs/indra/pkg/crypto/sha256" "git-indra.lan/indra-labs/indra/pkg/crypto/sha256"
"git-indra.lan/indra-labs/indra/pkg/messages/magicbytes" "git-indra.lan/indra-labs/indra/pkg/messages/magicbytes"
@@ -177,4 +178,14 @@ func (s *Splicer) Bytes(b []byte) *Splicer {
return s return s
} }
func (s *Splicer) Signature(sb sig.Bytes) *Splicer {
copy(s.b[*s.c:s.c.Inc(sig.Len)], sb[:])
return s
}
func (s *Splicer) ReadSignature(sb sig.Bytes) *Splicer {
copy(sb[:], s.b[*s.c:s.c.Inc(sig.Len)])
return s
}
func (s *Splicer) Done() {} func (s *Splicer) Done() {}