Commit 9dcabdef authored by David Stainton's avatar David Stainton

Update all components to use new pki doc fields

parent 23816327
......@@ -82,10 +82,10 @@ func TestDocument(t *testing.T) {
Epoch: debugTestEpoch,
SendRatePerMinute: testSendRate,
Topology: make([][][]byte, 3),
MixLambda: 0.42,
MixMaxDelay: 23,
SendLambda: 0.69,
SendMaxInterval: 17,
Mu: 0.42,
MuMaxDelay: 23,
LambdaP: 0.69,
LambdaPMaxDelay: 17,
SharedRandomCommit: sharedRandomCommit,
SharedRandomValue: make([]byte, SharedRandomValueLength),
}
......@@ -116,10 +116,16 @@ func TestDocument(t *testing.T) {
require.NoError(err, "VerifyAndParseDocument()")
require.Equal(doc.Epoch, ddoc.Epoch, "VerifyAndParseDocument(): Epoch")
require.Equal(doc.SendRatePerMinute, testSendRate, "VerifyAndParseDocument(): SendRatePerMinute")
require.Equal(doc.MixLambda, ddoc.MixLambda, "VerifyAndParseDocument(): MixLambda")
require.Equal(doc.MixMaxDelay, ddoc.MixMaxDelay, "VerifyAndParseDocument(): MixMaxDelay")
require.Equal(doc.SendLambda, ddoc.SendLambda, "VerifyAndParseDocument(): SendLambda")
require.Equal(doc.SendMaxInterval, ddoc.SendMaxInterval, "VerifyAndParseDocument(): SendMaxInterval")
require.Equal(doc.Mu, ddoc.Mu, "VerifyAndParseDocument(): Mu")
require.Equal(doc.MuMaxDelay, ddoc.MuMaxDelay, "VerifyAndParseDocument(): MuMaxDelay")
require.Equal(doc.LambdaP, ddoc.LambdaP, "VerifyAndParseDocument(): LambdaP")
require.Equal(doc.LambdaPMaxDelay, ddoc.LambdaPMaxDelay, "VerifyAndParseDocument(): LambdaPMaxDelay")
require.Equal(doc.LambdaL, ddoc.LambdaL, "VerifyAndParseDocument(): LambdaL")
require.Equal(doc.LambdaLMaxDelay, ddoc.LambdaLMaxDelay, "VerifyAndParseDocument(): LambdaLMaxDelay")
require.Equal(doc.LambdaD, ddoc.LambdaD, "VerifyAndParseDocument(): LambdaD")
require.Equal(doc.LambdaDMaxDelay, ddoc.LambdaDMaxDelay, "VerifyAndParseDocument(): LambdaDMaxDelay")
require.Equal(doc.LambdaM, ddoc.LambdaM, "VerifyAndParseDocument(): LambdaM")
require.Equal(doc.LambdaMMaxDelay, ddoc.LambdaMMaxDelay, "VerifyAndParseDocument(): LambdaMMaxDelay")
t.Logf("Deserialized document: '%v'", ddoc)
......
......@@ -45,12 +45,16 @@ const (
// Note: These values are picked primarily for debugging and need to
// be changed to something more suitable for a production deployment
// at some point.
defaultMixLambda = 0.00025
defaultMixMaxPercentile = 0.99999
defaultSendLambda = 0.00006
defaultSendMaxPercentile = 0.95
defaultMixLoopLambda = 0.00006
defaultMixLoopMaxPercentile = 0.95
defaultMu = 0.00025
defaultMuMaxPercentile = 0.99999
defaultLambdaP = 0.00025
defaultLambdaPMaxPercentile = 0.99999
defaultLambdaL = 0.00025
defaultLambdaLMaxPercentile = 0.99999
defaultLambdaD = 0.00025
defaultLambdaDMaxPercentile = 0.99999
defaultLambdaM = 0.00025
defaultLambdaMMaxPercentile = 0.99999
)
var defaultLogging = Logging{
......@@ -122,40 +126,78 @@ type Parameters struct {
// SendRatePerMinute is the rate per minute.
SendRatePerMinute uint64
// MixLambda is the inverse of the mean of the exponential distribution
// that the Sphinx packet per-hop mixing delay will be sampled from.
MixLambda float64
// Mu is the inverse of the mean of the exponential distribution
// that is used to select the delay for each hop.
Mu float64
// MixMaxDelay is the maximum Sphinx packet per-hop mixing delay in
// milliseconds.
MixMaxDelay uint64
// MuMaxDelay sets the maximum delay for Mu.
MuMaxDelay uint64
// SendLambda is the inverse of the mean of the exponential distribution
// that clients will sample to determine send timing legit forward messages
// or drop decoy messages.
SendLambda float64
// LambdaP is the inverse of the mean of the exponential distribution
// that is used to select the delay between clients sending from their egress
// FIFO queue or drop decoy message.
LambdaP float64
// SendMaxInterval is the maximum send interval in milliseconds.
SendMaxInterval uint64
// LambdaPMaxDelay sets the maximum delay for LambdaP.
LambdaPMaxDelay uint64
// MixLoopLambda is the inverse of the mean of the exponential distribution
// that clients will sample to determine send timing of loop decoy messages.
MixLoopLambda float64
// LambdaL is the inverse of the mean of the exponential distribution
// that is used to select the delay between clients sending from their egress
// FIFO queue or drop decoy message.
LambdaL float64
// MixLoopMaxInterval is the maximum send interval in milliseconds.
MixLoopMaxInterval uint64
// LambdaLMaxDelay sets the maximum delay for LambdaP.
LambdaLMaxDelay uint64
// LambdaD is the inverse of the mean of the exponential distribution
// that is used to select the delay between clients sending from their egress
// FIFO queue or drop decoy message.
LambdaD float64
// LambdaDMaxDelay sets the maximum delay for LambdaP.
LambdaDMaxDelay uint64
// LambdaM is the inverse of the mean of the exponential distribution
// that is used to select the delay between clients sending from their egress
// FIFO queue or drop decoy message.
LambdaM float64
// LambdaMMaxDelay sets the maximum delay for LambdaP.
LambdaMMaxDelay uint64
}
func (pCfg *Parameters) validate() error {
if pCfg.MixLambda < 0 {
return fmt.Errorf("config: Parameters: MixLambda %v is invalid", pCfg.MixLambda)
if pCfg.Mu < 0 {
return fmt.Errorf("config: Parameters: Mu %v is invalid", pCfg.Mu)
}
if pCfg.MuMaxDelay > absoluteMaxDelay {
return fmt.Errorf("config: Parameters: MuMaxDelay %v is out of range", pCfg.MuMaxDelay)
}
if pCfg.LambdaP < 0 {
return fmt.Errorf("config: Parameters: LambdaP %v is invalid", pCfg.LambdaP)
}
if pCfg.LambdaPMaxDelay > absoluteMaxDelay {
return fmt.Errorf("config: Parameters: LambdaPMaxDelay %v is out of range", pCfg.LambdaPMaxDelay)
}
if pCfg.LambdaL < 0 {
return fmt.Errorf("config: Parameters: LambdaL %v is invalid", pCfg.LambdaP)
}
if pCfg.MixMaxDelay > absoluteMaxDelay {
return fmt.Errorf("config: Parameters: MixMaxDelay %v is out of range", pCfg.MixMaxDelay)
if pCfg.LambdaLMaxDelay > absoluteMaxDelay {
return fmt.Errorf("config: Parameters: LambdaLMaxDelay %v is out of range", pCfg.LambdaPMaxDelay)
}
if pCfg.SendLambda < 0 {
return fmt.Errorf("config: Parameters: SendLambda %v is invalid", pCfg.SendLambda)
if pCfg.LambdaD < 0 {
return fmt.Errorf("config: Parameters: LambdaD %v is invalid", pCfg.LambdaP)
}
if pCfg.LambdaDMaxDelay > absoluteMaxDelay {
return fmt.Errorf("config: Parameters: LambdaDMaxDelay %v is out of range", pCfg.LambdaPMaxDelay)
}
if pCfg.LambdaM < 0 {
return fmt.Errorf("config: Parameters: LambdaM %v is invalid", pCfg.LambdaP)
}
if pCfg.LambdaMMaxDelay > absoluteMaxDelay {
return fmt.Errorf("config: Parameters: LambdaMMaxDelay %v is out of range", pCfg.LambdaPMaxDelay)
}
return nil
}
......@@ -163,26 +205,38 @@ func (pCfg *Parameters) applyDefaults() {
if pCfg.SendRatePerMinute == 0 {
pCfg.SendRatePerMinute = defaultSendRatePerMinute
}
if pCfg.MixLambda == 0 {
pCfg.MixLambda = defaultMixLambda
if pCfg.Mu == 0 {
pCfg.Mu = defaultMu
}
if pCfg.MixMaxDelay == 0 {
pCfg.MixMaxDelay = uint64(rand.ExpQuantile(pCfg.MixLambda, defaultMixMaxPercentile))
if pCfg.MixMaxDelay > absoluteMaxDelay {
pCfg.MixMaxDelay = absoluteMaxDelay
if pCfg.MuMaxDelay == 0 {
pCfg.MuMaxDelay = uint64(rand.ExpQuantile(pCfg.Mu, defaultMuMaxPercentile))
if pCfg.MuMaxDelay > absoluteMaxDelay {
pCfg.MuMaxDelay = absoluteMaxDelay
}
}
if pCfg.SendLambda == 0 {
pCfg.SendLambda = defaultSendLambda
if pCfg.LambdaP == 0 {
pCfg.LambdaP = defaultLambdaP
}
if pCfg.LambdaPMaxDelay == 0 {
pCfg.LambdaPMaxDelay = uint64(rand.ExpQuantile(pCfg.LambdaP, defaultLambdaPMaxPercentile))
}
if pCfg.LambdaL == 0 {
pCfg.LambdaL = defaultLambdaL
}
if pCfg.LambdaLMaxDelay == 0 {
pCfg.LambdaLMaxDelay = uint64(rand.ExpQuantile(pCfg.LambdaL, defaultLambdaLMaxPercentile))
}
if pCfg.LambdaD == 0 {
pCfg.LambdaD = defaultLambdaD
}
if pCfg.SendMaxInterval == 0 {
pCfg.SendMaxInterval = uint64(rand.ExpQuantile(pCfg.SendLambda, defaultSendMaxPercentile))
if pCfg.LambdaDMaxDelay == 0 {
pCfg.LambdaDMaxDelay = uint64(rand.ExpQuantile(pCfg.LambdaD, defaultLambdaDMaxPercentile))
}
if pCfg.MixLoopLambda == 0 {
pCfg.MixLoopLambda = defaultMixLoopLambda
if pCfg.LambdaM == 0 {
pCfg.LambdaM = defaultLambdaM
}
if pCfg.MixLoopMaxInterval == 0 {
pCfg.MixLoopMaxInterval = uint64(rand.ExpQuantile(pCfg.MixLoopLambda, defaultMixLoopMaxPercentile))
if pCfg.LambdaMMaxDelay == 0 {
pCfg.LambdaMMaxDelay = uint64(rand.ExpQuantile(pCfg.LambdaM, defaultLambdaMMaxPercentile))
}
}
......
......@@ -193,16 +193,20 @@ func (s *state) generateDocument(epoch uint64) {
// Build the Document.
doc := &s11n.Document{
Epoch: epoch,
SendRatePerMinute: s.s.cfg.Parameters.SendRatePerMinute,
MixLambda: s.s.cfg.Parameters.MixLambda,
MixMaxDelay: s.s.cfg.Parameters.MixMaxDelay,
SendLambda: s.s.cfg.Parameters.SendLambda,
SendMaxInterval: s.s.cfg.Parameters.SendMaxInterval,
MixLoopLambda: s.s.cfg.Parameters.MixLoopLambda,
MixLoopMaxInterval: s.s.cfg.Parameters.MixLoopMaxInterval,
Topology: topology,
Providers: providers,
Epoch: epoch,
SendRatePerMinute: s.s.cfg.Parameters.SendRatePerMinute,
Mu: s.s.cfg.Parameters.Mu,
MuMaxDelay: s.s.cfg.Parameters.MuMaxDelay,
LambdaP: s.s.cfg.Parameters.LambdaP,
LambdaPMaxDelay: s.s.cfg.Parameters.LambdaPMaxDelay,
LambdaL: s.s.cfg.Parameters.LambdaL,
LambdaLMaxDelay: s.s.cfg.Parameters.LambdaLMaxDelay,
LambdaD: s.s.cfg.Parameters.LambdaD,
LambdaDMaxDelay: s.s.cfg.Parameters.LambdaDMaxDelay,
LambdaM: s.s.cfg.Parameters.LambdaM,
LambdaMMaxDelay: s.s.cfg.Parameters.LambdaMMaxDelay,
Topology: topology,
Providers: providers,
}
// For compatibliity with shared s11n implementation between voting
// and non-voting authority, add SharedRandomValue.
......
// voting_authority_tests.go - Katzenpost voting authority tests
// Copyright (C) 2018 Yawning Angel, David Stainton, Masala.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package tests
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"io/ioutil"
"log"
mrand "math/rand"
"net/textproto"
"os"
"path/filepath"
"reflect"
"sync"
"testing"
//"testing/quick"
"time"
"github.com/hpcloud/tail"
vServer "github.com/katzenpost/authority/voting/server"
vConfig "github.com/katzenpost/authority/voting/server/config"
"github.com/katzenpost/client"
cConfig "github.com/katzenpost/client/config"
"github.com/katzenpost/core/crypto/ecdh"
"github.com/katzenpost/core/crypto/eddsa"
"github.com/katzenpost/core/crypto/rand"
"github.com/katzenpost/core/epochtime"
"github.com/katzenpost/core/thwack"
"github.com/katzenpost/core/utils"
nServer "github.com/katzenpost/server"
sConfig "github.com/katzenpost/server/config"
"github.com/stretchr/testify/assert"
)
const (
pingService = "loop"
logFile = "kimchi.log"
basePort = 30000
nrNodes = 3
nrProviders = 1
)
var tailConfig = tail.Config{
Poll: true,
Follow: true,
Logger: tail.DiscardingLogger,
}
type serverInterface interface {
Shutdown()
Wait()
}
type kimchi struct {
sync.Mutex
sync.WaitGroup
baseDir string
logWriter io.Writer
votingAuthConfigs []*vConfig.Config
nodeConfigs []*sConfig.Config
lastPort uint16
nodeIdx int
providerIdx int
servers []serverInterface
tails []*tail.Tail
}
func newKimchi(basePort int) *kimchi {
//[]*sConfig.Config
k := &kimchi{
lastPort: uint16(basePort + 1),
nodeConfigs: make([]*sConfig.Config, 0),
votingAuthConfigs: make([]*vConfig.Config, 0),
}
return k
}
func (s *kimchi) initLogging() error {
logFilePath := filepath.Join(s.baseDir, logFile)
f, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
if err != nil {
return err
}
// Log to both stdout *and* the log file.
s.logWriter = io.MultiWriter(f, os.Stdout)
log.SetOutput(s.logWriter)
return nil
}
func (s *kimchi) genGoodVotingAuthoritiesCfg(numAuthorities int) error {
parameters := &vConfig.Parameters{
MixLambda: 1,
MixMaxDelay: 10000,
SendLambda: 123,
SendMaxInterval: 123456,
}
configs := []*vConfig.Config{}
// initial generation of key material for each authority
peersMap := make(map[[eddsa.PublicKeySize]byte]*vConfig.AuthorityPeer)
for i := 0; i < numAuthorities; i++ {
cfg := new(vConfig.Config)
cfg.Logging = &vConfig.Logging{
Disable: false,
File: "katzenpost.log",
Level: "DEBUG",
}
cfg.Parameters = parameters
cfg.Authority = &vConfig.Authority{
Identifier: fmt.Sprintf("authority-%v.example.org", i),
Addresses: []string{fmt.Sprintf("127.0.0.1:%d", s.lastPort)},
DataDir: filepath.Join(s.baseDir, fmt.Sprintf("authority%d", i)),
}
s.lastPort++
privateIdentityKey, err := eddsa.NewKeypair(rand.Reader)
if err != nil {
return err
}
cfg.Debug = &vConfig.Debug{
IdentityKey: privateIdentityKey,
Layers: 3,
MinNodesPerLayer: 1,
GenerateOnly: false,
}
configs = append(configs, cfg)
authorityPeer := &vConfig.AuthorityPeer{
IdentityPublicKey: cfg.Debug.IdentityKey.PublicKey(),
LinkPublicKey: cfg.Debug.IdentityKey.PublicKey().ToECDH(),
Addresses: cfg.Authority.Addresses,
}
peersMap[cfg.Debug.IdentityKey.PublicKey().ByteArray()] = authorityPeer
}
// tell each authority about it's peers
for i := 0; i < numAuthorities; i++ {
peers := []*vConfig.AuthorityPeer{}
for id, peer := range peersMap {
if !bytes.Equal(id[:], configs[i].Debug.IdentityKey.PublicKey().Bytes()) {
peers = append(peers, peer)
}
}
configs[i].Authorities = peers
}
s.votingAuthConfigs = append(s.votingAuthConfigs, configs...)
return nil
}
func (s *kimchi) genBadVotingAuthoritiesCfg(numAuthorities int) error {
parameters := &vConfig.Parameters{} // XXX all nil params means bad votes
configs := []*vConfig.Config{}
// initial generation of key material for each authority
peersMap := make(map[[eddsa.PublicKeySize]byte]*vConfig.AuthorityPeer)
for i := 0; i < numAuthorities; i++ {
cfg := new(vConfig.Config)
cfg.Logging = &vConfig.Logging{
Disable: false,
File: "katzenpost.log",
Level: "DEBUG",
}
cfg.Parameters = parameters
cfg.Authority = &vConfig.Authority{
Identifier: fmt.Sprintf("authority-%v.example.org", i),
Addresses: []string{fmt.Sprintf("127.0.0.1:%d", s.lastPort)},
DataDir: filepath.Join(s.baseDir, fmt.Sprintf("authority%d", i)),
}
s.lastPort++
privateIdentityKey, err := eddsa.NewKeypair(rand.Reader)
if err != nil {
return err
}
cfg.Debug = &vConfig.Debug{
IdentityKey: privateIdentityKey,
Layers: 3,
MinNodesPerLayer: 1,
GenerateOnly: false,
}
configs = append(configs, cfg)
authorityPeer := &vConfig.AuthorityPeer{
IdentityPublicKey: cfg.Debug.IdentityKey.PublicKey(),
LinkPublicKey: cfg.Debug.IdentityKey.PublicKey().ToECDH(),
Addresses: cfg.Authority.Addresses,
}
peersMap[cfg.Debug.IdentityKey.PublicKey().ByteArray()] = authorityPeer
}
// tell each authority about it's peers
for i := 0; i < numAuthorities; i++ {
peers := []*vConfig.AuthorityPeer{}
for id, peer := range peersMap {
if !bytes.Equal(id[:], configs[i].Debug.IdentityKey.PublicKey().Bytes()) {
peers = append(peers, peer)
}
}
configs[i].Authorities = peers
}
s.votingAuthConfigs = append(s.votingAuthConfigs, configs...)
return nil
}
func (s *kimchi) genNodeConfig(isProvider bool, isVoting bool) error {
const serverLogFile = "katzenpost.log"
n := fmt.Sprintf("node-%d", s.nodeIdx)
if isProvider {
n = fmt.Sprintf("provider-%d", s.providerIdx)
}
cfg := new(sConfig.Config)
// Server section.
cfg.Server = new(sConfig.Server)
cfg.Server.Identifier = fmt.Sprintf("%s.eXaMpLe.org", n)
cfg.Server.Addresses = []string{fmt.Sprintf("127.0.0.1:%d", s.lastPort)}
cfg.Server.DataDir = filepath.Join(s.baseDir, n)
cfg.Server.IsProvider = isProvider
// Logging section.
cfg.Logging = new(sConfig.Logging)
cfg.Logging.File = serverLogFile
cfg.Logging.Level = "DEBUG"
// Debug section.
cfg.Debug = new(sConfig.Debug)
cfg.Debug.NumSphinxWorkers = 1
identity, err := eddsa.NewKeypair(rand.Reader)
if err != nil {
return err
}
cfg.Debug.IdentityKey = identity
if isVoting {
peers := []*sConfig.Peer{}
for _, peer := range s.votingAuthConfigs {
idKey, err := peer.Debug.IdentityKey.PublicKey().MarshalText()
if err != nil {
return err
}
linkKey, err := peer.Debug.IdentityKey.PublicKey().ToECDH().MarshalText()
if err != nil {
return err
}
p := &sConfig.Peer{
Addresses: peer.Authority.Addresses,
IdentityPublicKey: string(idKey),
LinkPublicKey: string(linkKey),
}
if len(peer.Authority.Addresses) == 0 {
panic("wtf")
}
peers = append(peers, p)
}
cfg.PKI = &sConfig.PKI{
Voting: &sConfig.Voting{
Peers: peers,
},
}
} else {
panic("wtf")
}
if isProvider {
// Enable the thwack interface.
cfg.Management = new(sConfig.Management)
cfg.Management.Enable = true
s.providerIdx++
cfg.Provider = new(sConfig.Provider)
cfg.Server.AltAddresses = map[string][]string{
"TCP": []string{fmt.Sprintf("localhost:%d", s.lastPort)},
"torv2": []string{"onedaythiswillbea.onion:2323"},
}
loopCfg := new(sConfig.Kaetzchen)
loopCfg.Capability = "loop"
loopCfg.Endpoint = "+loop"
cfg.Provider.Kaetzchen = append(cfg.Provider.Kaetzchen, loopCfg)
} else {
s.nodeIdx++
}
s.nodeConfigs = append(s.nodeConfigs, cfg)
s.lastPort++
err = cfg.FixupAndValidate()
if err != nil {
return errors.New("genNodeConfig failure on fixupandvalidate")
}
return nil
}
// generateWhitelist returns providers, mixes, error
func (s *kimchi) generateVotingWhitelist() ([]*vConfig.Node, []*vConfig.Node, error) {
mixes := []*vConfig.Node{}
providers := []*vConfig.Node{}
for _, nodeCfg := range s.nodeConfigs {
if nodeCfg.Server.IsProvider {
provider := &vConfig.Node{
Identifier: nodeCfg.Server.Identifier,
IdentityKey: nodeCfg.Debug.IdentityKey.PublicKey(),
}
providers = append(providers, provider)
continue
}
mix := &vConfig.Node{
IdentityKey: nodeCfg.Debug.IdentityKey.PublicKey(),
}
mixes = append(mixes, mix)
}
return providers, mixes, nil
}
func (s *kimchi) runVotingAuthorities() error {
for _, vCfg := range s.votingAuthConfigs {
vCfg.FixupAndValidate()
server, err := vServer.New(vCfg)
if err != nil {
return err
}
go s.logTailer(vCfg.Authority.Identifier, filepath.Join(vCfg.Authority.DataDir, vCfg.Logging.File))
s.servers = append(s.servers, server)
}
return nil
}
func (s *kimchi) thwackUser(provider *sConfig.Config, user string, pubKey *ecdh.PublicKey) error {
log.Printf("Attempting to add user: %v@%v", user, provider.Server.Identifier)
sockFn := filepath.Join(provider.Server.DataDir, "management_sock")
c, err := textproto.Dial("unix", sockFn)
if err != nil {
return err
}
defer c.Close()
if _, _, err = c.ReadResponse(int(thwack.StatusServiceReady)); err != nil {
return err
}
for _, v := range []string{
fmt.Sprintf("ADD_USER %v %v", user, pubKey),
"QUIT",
} {
if err = c.PrintfLine("%v", v); err != nil {
return err
}
if _, _, err = c.ReadResponse(int(thwack.StatusOk)); err != nil {
return err
}
}
return nil
}