Commit 66b01652 authored by David Stainton's avatar David Stainton

Teach wire handler to distinguish between client types

fixes a rather critical security vulnerability
parent 65669f0f
......@@ -37,6 +37,7 @@ import (
"github.com/katzenpost/authority/voting/client"
"github.com/katzenpost/authority/voting/server/config"
"github.com/katzenpost/core/crypto/cert"
"github.com/katzenpost/core/crypto/ecdh"
"github.com/katzenpost/core/crypto/eddsa"
"github.com/katzenpost/core/crypto/rand"
"github.com/katzenpost/core/epochtime"
......@@ -90,6 +91,7 @@ type state struct {
authorizedMixes map[[eddsa.PublicKeySize]byte]bool
authorizedProviders map[[eddsa.PublicKeySize]byte]string
authorizedAuthorities map[[eddsa.PublicKeySize]byte]bool
authorityLinkKeys map[[eddsa.PublicKeySize]byte]*ecdh.PublicKey
documents map[uint64]*document
descriptors map[uint64]map[[eddsa.PublicKeySize]byte]*descriptor
......@@ -479,7 +481,7 @@ func (s *state) sendRevealToPeer(peer *config.AuthorityPeer, reveal []byte, epoc
defer s.s.Done()
cfg := &wire.SessionConfig{
Authenticator: s,
AdditionalData: []byte(""),
AdditionalData: s.s.identityKey.PublicKey().Bytes(),
AuthenticationKey: s.s.linkKey,
RandomReader: rand.Reader,
}
......@@ -539,7 +541,7 @@ func (s *state) sendVoteToPeer(peer *config.AuthorityPeer, vote []byte, epoch ui
defer s.s.Done()
cfg := &wire.SessionConfig{
Authenticator: s,
AdditionalData: []byte(""),
AdditionalData: s.s.identityKey.PublicKey().Bytes(),
AuthenticationKey: s.s.linkKey,
RandomReader: rand.Reader,
}
......@@ -1379,6 +1381,11 @@ func newState(s *Server) (*state, error) {
pk := v.IdentityPublicKey.ByteArray()
st.authorizedAuthorities[pk] = true
}
st.authorityLinkKeys = make(map[[eddsa.PublicKeySize]byte]*ecdh.PublicKey)
for _, v := range st.s.cfg.Authorities {
pk := v.IdentityPublicKey.ByteArray()
st.authorityLinkKeys[pk] = v.LinkPublicKey
}
st.documents = make(map[uint64]*document)
st.descriptors = make(map[uint64]map[[eddsa.PublicKeySize]byte]*descriptor)
......
......@@ -74,39 +74,74 @@ func (s *Server) onConn(conn net.Conn) {
// Parse the command, and craft the response.
var resp commands.Command
if auth.isClient {
resp = s.onClient(rAddr, cmd)
} else if auth.isMix {
resp = s.onMix(rAddr, cmd, auth.peerIdentityKey)
} else if auth.isAuthority {
resp = s.onAuthority(rAddr, cmd)
} else {
panic("wtf") // should only happen if there is a bug in wireAuthenticator
}
// Send the response, if any.
if resp != nil {
conn.SetDeadline(time.Now().Add(responseDeadline))
if err = wireConn.SendCommand(resp); err != nil {
s.log.Debugf("Peer %v: Failed to send response: %v", rAddr, err)
}
}
}
func (s *Server) onClient(rAddr net.Addr, cmd commands.Command) commands.Command {
s.log.Debug("onClient")
var resp commands.Command
switch c := cmd.(type) {
case *commands.GetConsensus:
resp = s.onGetConsensus(rAddr, c)
default:
s.log.Debugf("Peer %v: Invalid request: %T", rAddr, c)
return nil
}
return resp
}
func (s *Server) onMix(rAddr net.Addr, cmd commands.Command, peerIdentityKey *eddsa.PublicKey) commands.Command {
s.log.Debug("onMix")
var resp commands.Command
switch c := cmd.(type) {
case *commands.GetConsensus:
resp = s.onGetConsensus(rAddr, c)
case *commands.PostDescriptor:
resp = s.onPostDescriptor(rAddr, c, peerIdentityKey)
default:
s.log.Debugf("Peer %v: Invalid request: %T", rAddr, c)
return nil
}
return resp
}
func (s *Server) onAuthority(rAddr net.Addr, cmd commands.Command) commands.Command {
s.log.Debug("onAuthority")
var resp commands.Command
switch c := cmd.(type) {
case *commands.GetConsensus:
resp = s.onGetConsensus(rAddr, c)
case *commands.Vote:
resp = s.onVote(c)
case *commands.VoteStatus:
s.log.Error("VoteStatus command is not allowed on Authority wire service listener.")
return
return nil
case *commands.Reveal:
resp = s.onReveal(c)
case *commands.RevealStatus:
s.log.Error("RevealStatus command is not allowed on Authority wire service listener.")
return
case *commands.GetConsensus:
resp = s.onGetConsensus(rAddr, c)
case *commands.PostDescriptor:
if auth.peerIdentityKey == nil {
// A client trying to post is actively evil, don't even dignify
// it with a response.
s.log.Errorf("Peer %v: Not allowed to post.", rAddr)
return
}
resp = s.onPostDescriptor(rAddr, c, auth.peerIdentityKey)
return nil
default:
s.log.Debugf("Peer %v: Invalid request: %T", rAddr, c)
return
}
// Send the response, if any.
if resp != nil {
conn.SetDeadline(time.Now().Add(responseDeadline))
if err = wireConn.SendCommand(resp); err != nil {
s.log.Debugf("Peer %v: Failed to send response: %v", rAddr, err)
}
return nil
}
return resp
}
func (s *Server) onVote(cmd *commands.Vote) commands.Command {
......@@ -201,36 +236,57 @@ func (s *Server) onPostDescriptor(rAddr net.Addr, cmd *commands.PostDescriptor,
type wireAuthenticator struct {
s *Server
peerIdentityKey *eddsa.PublicKey
isClient bool
isMix bool
isAuthority bool
}
func (a *wireAuthenticator) IsPeerValid(creds *wire.PeerCredentials) bool {
// Just allow clients to connect with fetch access.
switch len(creds.AdditionalData) {
case 0:
a.isClient = true
return true
case eddsa.PublicKeySize:
default:
a.s.log.Debugf("Rejecting authentication, invalid AD size.")
a.s.log.Warning("Rejecting authentication, invalid AD size.")
return false
}
a.peerIdentityKey = new(eddsa.PublicKey)
if err := a.peerIdentityKey.FromBytes(creds.AdditionalData); err != nil {
a.s.log.Debugf("Rejecting authentication, invalid AD: %v", err)
a.s.log.Warningf("Rejecting authentication, invalid AD: %v", err)
return false
}
pk := a.peerIdentityKey.ByteArray()
if !(a.s.state.authorizedMixes[pk] || a.s.state.authorizedProviders[pk] != "") {
a.s.log.Debugf("Rejecting authentication, not a valid mix/provider.")
return false
}
_, isMix := a.s.state.authorizedMixes[pk]
_, isProvider := a.s.state.authorizedProviders[pk]
_, isAuthority := a.s.state.authorizedAuthorities[pk]
linkPk := a.peerIdentityKey.ToECDH()
if !linkPk.Equal(creds.PublicKey) {
a.s.log.Debugf("Rejecting authentication, public key mismatch.")
if isMix || isProvider {
linkPk := a.peerIdentityKey.ToECDH()
if !linkPk.Equal(creds.PublicKey) {
a.s.log.Warning("Rejecting mix authentication, public key mismatch.")
return false
}
a.isMix = true // Providers and mixes are both mixes. :)
return true
} else if isAuthority {
linkKey, ok := a.s.state.authorityLinkKeys[pk]
if !ok {
a.s.log.Warning("Rejecting authority authentication, no link key entry.")
return false
}
if !linkKey.Equal(creds.PublicKey) {
a.s.log.Warning("Rejecting authority authentication, public key mismatch.")
return false
}
a.isAuthority = true
return true
} else {
a.s.log.Warning("Rejecting authority authentication, public key mismatch.")
return false
}
return true
return false // Not reached.
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment