├── coco ├── doc.go ├── stamp │ ├── constants.go │ ├── data │ │ ├── exconf1.json │ │ └── exconf2.json │ └── messg.go ├── test │ ├── data │ │ ├── exconf1.json │ │ ├── exconf.json │ │ ├── extcpconf.json │ │ ├── gen.py │ │ ├── exconf_wkeys.json │ │ ├── zoo.json │ │ └── exwax.json │ ├── deploy │ │ └── pl_deploy.sh │ ├── graphs │ │ ├── sort.go │ │ ├── quickselect.go │ │ ├── tree.go │ │ └── graph_test.go │ ├── oldconfig │ │ ├── graphs_test.go │ │ ├── config_test.go │ │ └── graphs.go │ ├── timeclient │ │ ├── timeclient.go │ │ └── stampclient │ │ │ └── stampclient.go │ ├── config │ │ └── config.go │ ├── deploy2deter │ │ ├── hosts.txt │ │ └── run_tests │ │ │ ├── monitor.go │ │ │ └── stats.go │ ├── cliutils │ │ └── utils.go │ ├── logutils │ │ └── logutils.go │ ├── latency_test │ │ └── latency.go │ ├── logserver │ │ └── home.html │ ├── forkexec │ │ └── forkexec.go │ └── exec │ │ ├── exec.go │ │ └── timestamper │ │ └── timestamper.go ├── sign │ ├── constants.go │ ├── errors.go │ ├── pubkey.go │ ├── signBenchmark_test.go │ ├── snlog.go │ ├── basicSig.go │ ├── interfaces.go │ ├── vote_test.go │ ├── round.go │ ├── snviewchange.go │ ├── signingMessages_test.go │ ├── voteMessages.go │ ├── snvoting.go │ └── signingMessages.go ├── coconet │ ├── networkMessg.go │ ├── faultyHost.go │ ├── conn.go │ ├── host.go │ └── tcpconn.go ├── proof │ └── proof_test.go └── hashid │ └── hashid.go ├── tree ├── data │ ├── .gitignore │ ├── NOTES │ └── gen.py ├── hash.go ├── intq.go ├── merkle.go ├── tree_test.go └── tree.go ├── deter ├── config.sh ├── stop.sh ├── setup.sh ├── run.sh └── topo.ns ├── .gitignore ├── python ├── utils.py ├── cells │ └── null.py ├── certify │ ├── null.py │ ├── signature.py │ └── encrypted_exchange.py ├── TODO ├── pseudonym_config.py ├── relay.py ├── elgamal.py ├── trustee.py ├── session_config.py ├── system_config.py ├── client.py ├── supervisor.py ├── dcnet_local.py ├── schnorr.py └── verdict.py ├── dcnet ├── dcnet.go ├── dcnet_test.go ├── cell.go └── simple.go ├── net ├── net.go └── view.go ├── connMan ├── interface.go └── chanConnManager.go ├── insure ├── policy.go └── doc.go ├── dissent └── config.go ├── time ├── path_test.go ├── stampd │ └── main.go ├── hash.go ├── seq.go ├── proto.go ├── NOTES └── proof.go ├── README └── util └── replace.go /coco/doc.go: -------------------------------------------------------------------------------- 1 | package coco 2 | -------------------------------------------------------------------------------- /tree/data/.gitignore: -------------------------------------------------------------------------------- 1 | *.dat 2 | -------------------------------------------------------------------------------- /deter/config.sh: -------------------------------------------------------------------------------- 1 | maxclient=4 2 | maxtrustee=2 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.pyc 3 | *.swp 4 | *~ 5 | moc_* 6 | Makefile 7 | ext/ 8 | dissent/dissent 9 | docs/html 10 | test.log 11 | libdissent.so* 12 | keygen 13 | *.moc 14 | entry_tunnel 15 | exit_tunnel 16 | -------------------------------------------------------------------------------- /coco/stamp/constants.go: -------------------------------------------------------------------------------- 1 | package stamp 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/dedis/prifi/coco/sign" 7 | ) 8 | 9 | // time we wait between rounds 10 | var ROUND_TIME time.Duration = sign.ROUND_TIME 11 | -------------------------------------------------------------------------------- /python/utils.py: -------------------------------------------------------------------------------- 1 | import binascii 2 | 3 | from Crypto.Util.number import bytes_to_long 4 | 5 | def string_to_long(s): 6 | s = bytes("".join(s.split()), "UTF-8") 7 | s = binascii.a2b_hex(s) 8 | return bytes_to_long(s) 9 | -------------------------------------------------------------------------------- /dcnet/dcnet.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package dcnet provides an abstract interface 3 | for Dining Cryptographers (DC-nets) anonymous coding schemes, 4 | as well as implementations of multiple specific DC-nets coding schemes. 5 | */ 6 | package dcnet 7 | -------------------------------------------------------------------------------- /net/net.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package net defines an abstract interface for a network access viewpoint, 3 | and implementations of this interface for both 4 | the system's "native network viewpoint 5 | and useful virtual networking viewpoints. 6 | */ 7 | package net 8 | -------------------------------------------------------------------------------- /coco/test/data/exconf1.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosts": ["host0", "host1", "host2"], 3 | "tree": {"name": "host0", 4 | "children": [ 5 | {"name": "host1","children":[]}, 6 | {"name": "host2","children":[]}]} 7 | } 8 | -------------------------------------------------------------------------------- /coco/stamp/data/exconf1.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosts": ["host0", "host1", "host2"], 3 | "tree": {"name": "host0", 4 | "children": [ 5 | {"name": "host1", 6 | "children": []}, 7 | {"name": "host2", 8 | "children": []}]} 9 | } 10 | -------------------------------------------------------------------------------- /coco/test/deploy/pl_deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -v 4 | set -e 5 | 6 | go build 7 | ./deploy -mode pl -arch 386 -u yale_dissent -config ../data/zoo.json -hosts planetlab2.cs.unc.edu:9012,pl1.6test.edu.cn:9012,planetlab1.cs.du.edu:9012,planetlab02.cs.washington.edu:9012,planetlab-2.cse.ohio-state.edu:9012,planetlab2.cs.ubc.ca:9012 -------------------------------------------------------------------------------- /coco/sign/constants.go: -------------------------------------------------------------------------------- 1 | package sign 2 | 3 | import "time" 4 | 5 | // Constants we expect might be used by other packages 6 | // TODO: can this be replaced by the application using the signer? 7 | var ROUND_TIME time.Duration = 1 * time.Second 8 | var HEARTBEAT = ROUND_TIME + ROUND_TIME/2 9 | 10 | var GOSSIP_TIME time.Duration = 3 * ROUND_TIME 11 | -------------------------------------------------------------------------------- /dcnet/dcnet_test.go: -------------------------------------------------------------------------------- 1 | package dcnet 2 | 3 | import ( 4 | "github.com/dedis/crypto/nist" 5 | "testing" 6 | ) 7 | 8 | func TestSimple(t *testing.T) { 9 | TestCellCoder(t, nist.NewAES128SHA256P256(), SimpleCoderFactory) 10 | } 11 | 12 | func TestOwned(t *testing.T) { 13 | TestCellCoder(t, nist.NewAES128SHA256P256(), OwnedCoderFactory) 14 | } 15 | -------------------------------------------------------------------------------- /python/cells/null.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | class NullEncoder: 4 | def encode(self, cell): 5 | return cell 6 | 7 | def decoded_size(self, size): 8 | return size 9 | 10 | def encoded_size(self, size): 11 | return size 12 | 13 | class NullDecoder: 14 | def decode(self, cell): 15 | return cell 16 | 17 | -------------------------------------------------------------------------------- /coco/stamp/data/exconf2.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosts": ["host0", "host1", "host2", "host3", "host4", "host5"], 3 | "tree": {"name": "host0", 4 | "children": [ 5 | {"name": "host1", 6 | "children": [{"name": "host2"}, {"name": "host3"}]}, 7 | {"name": "host4", 8 | "children": [{"name": "host5"}]}]} 9 | } 10 | -------------------------------------------------------------------------------- /coco/test/data/exconf.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosts": ["host0", "host1", "host2", "host3", "host4", "host5"], 3 | "tree": {"name": "host0", 4 | "children": [ 5 | {"name": "host1", 6 | "children": [{"name": "host2"}, {"name": "host3"}]}, 7 | {"name": "host4", 8 | "children": [{"name": "host5"}]}]} 9 | } 10 | -------------------------------------------------------------------------------- /coco/sign/errors.go: -------------------------------------------------------------------------------- 1 | package sign 2 | 3 | import "errors" 4 | 5 | var ErrUnknownMessageType error = errors.New("received message of unknown type") 6 | 7 | var ErrViewRejected error = errors.New("view Rejected: not all nodes accepted view") 8 | 9 | var ErrImposedFailure error = errors.New("failure imposed") 10 | 11 | var ErrPastRound error = errors.New("round number already passed") 12 | -------------------------------------------------------------------------------- /deter/stop.sh: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/bash 2 | # setup nodes after swapin 3 | 4 | source config.sh 5 | 6 | # Start relay first 7 | ssh relay.lld.safer pkill dissent 8 | 9 | # Start clients and trustees 10 | #for i in $(seq 0 $maxclient); do 11 | # ssh client-$i.lld.safer pkill dissent 12 | #done 13 | #for i in $(seq 0 $maxtrustee); do 14 | # ssh trustee-$i.lld.safer pkill dissent 15 | #done 16 | 17 | -------------------------------------------------------------------------------- /python/certify/null.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | class NullAccumulator: 4 | def before(self, ciphertexts): 5 | return ciphertexts 6 | 7 | def after(self, cleartexts): 8 | return cleartexts 9 | 10 | class NullCertifier: 11 | def certify(self, ciphertexts): 12 | return ciphertexts 13 | 14 | def verify(self, cleartexts): 15 | return cleartexts 16 | 17 | -------------------------------------------------------------------------------- /coco/test/data/extcpconf.json: -------------------------------------------------------------------------------- 1 | { 2 | "conn": "tcp", 3 | "hosts": ["host0", "host1", "host2", "host3", "host4", "host5"], 4 | "tree": {"name": "host0", 5 | "children": [ 6 | {"name": "host1", 7 | "children": [{"name": "host2"}, {"name": "host3"}]}, 8 | {"name": "host4", 9 | "children": [{"name": "host5"}]}]} 10 | } 11 | -------------------------------------------------------------------------------- /tree/data/NOTES: -------------------------------------------------------------------------------- 1 | 2 | 3 | /* 4 | Some sources of relevant graphs: 5 | NetworkX: http://networkx.lanl.gov/reference/generators.html 6 | Of most interest: 7 | - random_regular_graph 8 | - watts_strogatz_graph 9 | - barabasi_albert_graph 10 | - powerlaw_cluster_graph 11 | - navigable_small_world_graph (planar) 12 | - waxman_graph (planar) 13 | 14 | Internet-like: BRITE, Inet-3.0 15 | IGen: http://informatique.umons.ac.be/networks/igen/ 16 | */ 17 | 18 | -------------------------------------------------------------------------------- /coco/coconet/networkMessg.go: -------------------------------------------------------------------------------- 1 | package coconet 2 | 3 | import ( 4 | "github.com/dedis/protobuf" 5 | ) 6 | 7 | type NetworkMessg struct { 8 | Data BinaryUnmarshaler // data 9 | From string // name of server data came from 10 | Err error 11 | } 12 | 13 | func (nm *NetworkMessg) MarshalBinary() ([]byte, error) { 14 | return protobuf.Encode(nm) 15 | } 16 | 17 | func (nm *NetworkMessg) UnmarshalBinary(data []byte) error { 18 | return protobuf.Decode(data, nm) 19 | } 20 | -------------------------------------------------------------------------------- /deter/setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/bash 2 | # setup nodes after swapin 3 | 4 | packages="golang git" 5 | 6 | source config.sh 7 | 8 | nodes="relay" 9 | for i in $(seq 0 $maxclient); do 10 | nodes="$nodes client-$i" 11 | done 12 | for i in $(seq 0 $maxtrustee); do 13 | nodes="$nodes trustee-$i" 14 | done 15 | 16 | for n in $nodes; do 17 | # install packages we need 18 | ssh -o "StrictHostKeyChecking no" $n.lld.safer \ 19 | sudo apt-get -y install $packages & 20 | done 21 | wait 22 | 23 | -------------------------------------------------------------------------------- /connMan/interface.go: -------------------------------------------------------------------------------- 1 | package connMan 2 | 3 | import ( 4 | "github.com/dedis/crypto/abstract" 5 | "github.com/dedis/prifi/coconet" 6 | ) 7 | 8 | /* The ConnManager is responsible for managing multiple connections. It allows 9 | * servers to send/receive messages to other servers by specifying the public 10 | * key of the desired server. 11 | */ 12 | type ConnManager interface { 13 | // Sends a message to a specific peer. 14 | Put(abstract.Point, coconet.BinaryMarshaler) error 15 | 16 | // Receive a message from the desired peer. 17 | Get(abstract.Point, coconet.BinaryUnmarshaler) error 18 | } 19 | -------------------------------------------------------------------------------- /deter/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/bash 2 | # setup nodes after swapin 3 | 4 | packages="golang git" 5 | 6 | source config.sh 7 | 8 | # Start relay first 9 | ssh relay.lld.safer pkill dissent 10 | ssh relay.lld.safer go/src/dissent/main/dissent -relay & 11 | sleep 2 12 | 13 | # Start clients and trustees 14 | for i in $(seq 0 $maxclient); do 15 | ssh client-$i.lld.safer pkill dissent 16 | ssh client-$i.lld.safer go/src/dissent/main/dissent -client=$i >client-$i.log & 17 | done 18 | for i in $(seq 0 $maxtrustee); do 19 | ssh trustee-$i.lld.safer pkill dissent 20 | ssh trustee-$i.lld.safer go/src/dissent/main/dissent -trustee=$i >trustee-$i.log & 21 | done 22 | 23 | -------------------------------------------------------------------------------- /coco/sign/pubkey.go: -------------------------------------------------------------------------------- 1 | package sign 2 | 3 | import "github.com/dedis/prifi/coco/coconet" 4 | 5 | // Functions used in collective signing 6 | // That are direclty related to the generation/ verification/ sending 7 | // of the Simple Combined Public Key Signature 8 | 9 | // Send children challenges 10 | func (sn *Node) SendChildrenChallenges(view int, chm *ChallengeMessage) error { 11 | for _, child := range sn.Children(view) { 12 | var messg coconet.BinaryMarshaler 13 | messg = &SigningMessage{View: view, Type: Challenge, Chm: chm} 14 | 15 | // fmt.Println(sn.Name(), "send to", i, child, "on view", view) 16 | if err := child.Put(messg); err != nil { 17 | return err 18 | } 19 | } 20 | 21 | return nil 22 | } 23 | -------------------------------------------------------------------------------- /insure/policy.go: -------------------------------------------------------------------------------- 1 | package insure 2 | 3 | import ( 4 | "github.com/dedis/crypto/abstract" 5 | "github.com/dedis/crypto/edwards" 6 | "github.com/dedis/crypto/nist" 7 | ) 8 | 9 | const ( 10 | // The minimum number of private shares needed in order to reconstruct 11 | // the private secret. This parameter must be known in order to properly 12 | // decode public polynomial commits. 13 | TSHARES int = 10 14 | ) 15 | 16 | // The group to be used for all shares and should be constant. 17 | var INSURE_GROUP abstract.Group = new(edwards.ExtendedCurve).Init( 18 | edwards.Param25519(), false) 19 | 20 | // The group to be used for all public/private key pairs and should be constant. 21 | var KEY_SUITE abstract.Suite = nist.NewAES128SHA256P256() 22 | -------------------------------------------------------------------------------- /python/TODO: -------------------------------------------------------------------------------- 1 | - Configuration files should produce classes and not rely on hash tables (try to be a little more type safe) 2 | - The encoder interface is probably inappropriate and would needs to be better fleshed out 3 | - There are actually two models for generating trap bits ... one that adds noise at the expense of clients and trustees doing a ciphertext xor per trustee and one that adds no noise and does a single ciphertext generation. The current method in the design doc only discusses the latter. 4 | - Import in Lining's abstract groups interface 5 | - Import in Eleanor's encoding 6 | - Flesh out the distributed system 7 | - Add an http proxy 8 | - Add accountability / replay on trustees 9 | - Don't use main, let's use unit tests and so we need a main unittest 10 | -------------------------------------------------------------------------------- /dissent/config.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/dedis/crypto/config" 5 | "github.com/dedis/crypto/suites" 6 | ) 7 | 8 | var configFile config.File 9 | 10 | // Dissent config file format 11 | type ConfigData struct { 12 | Keys config.Keys // Info on configured key-pairs 13 | } 14 | 15 | var configData ConfigData 16 | var keyPairs []config.KeyPair 17 | 18 | func readConfig() error { 19 | 20 | // Load the configuration file 21 | configFile.Load("dissent", &configData) 22 | 23 | // Read or create our public/private keypairs 24 | pairs, err := configFile.Keys(&configData.Keys, suites.All(), 25 | defaultSuite) 26 | if err != nil { 27 | return err 28 | } 29 | keyPairs = pairs 30 | println("Loaded", len(pairs), "key-pairs") 31 | 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /time/path_test.go: -------------------------------------------------------------------------------- 1 | package time 2 | 3 | import ( 4 | "crypto/sha256" 5 | "encoding/hex" 6 | "testing" 7 | ) 8 | 9 | func TestPath(t *testing.T) { 10 | 11 | newHash := sha256.New 12 | hash := newHash() 13 | n := 100 14 | 15 | leaves := make([]HashId, n) 16 | for i := range leaves { 17 | leaves[i] = make([]byte, hash.Size()) 18 | for j := range leaves[i] { 19 | leaves[i][j] = byte(i) 20 | } 21 | //println("leaf",i,":",hex.EncodeToString(leaves[i])) 22 | } 23 | 24 | root, proofs := ProofTree(newHash, leaves) 25 | println("root:", hex.EncodeToString(root)) 26 | for i := range proofs { 27 | println("leaf", i, hex.EncodeToString(leaves[i])) 28 | proofs[i].Check(newHash, root, leaves[i]) 29 | for j := range proofs[i] { 30 | println(" ", j, hex.EncodeToString(proofs[i][j])) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /time/stampd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/dedis/crypto/config" 5 | "github.com/dedis/crypto/suites" 6 | "github.com/dedis/crypto/nist" 7 | ) 8 | 9 | type ConfigData struct { 10 | Keys config.Keys // Configured key-pairs for this timestap server 11 | } 12 | 13 | var keyPairs []config.KeyPair 14 | var configData ConfigData 15 | var configFile config.File 16 | 17 | var defaultSuite = nist.NewAES128SHA256P256() 18 | var cryptoSuites = suites.All() 19 | 20 | func readConfig() error { 21 | 22 | // Load the configuration file 23 | configFile.Load("stampd", &configData) 24 | 25 | // Read or create our public/private keypairs 26 | pairs,err := configFile.Keys(&configData.Keys, cryptoSuites, 27 | defaultSuite) 28 | if err != nil { 29 | return err 30 | } 31 | keyPairs = pairs 32 | 33 | return nil 34 | } 35 | 36 | func main() { 37 | readConfig() 38 | } 39 | 40 | -------------------------------------------------------------------------------- /coco/test/graphs/sort.go: -------------------------------------------------------------------------------- 1 | package graphs 2 | 3 | import "sort" 4 | 5 | type Float64Slice struct { 6 | A []float64 7 | I []int // indices 8 | } 9 | 10 | func (p Float64Slice) Len() int { return len(p.A) } 11 | func (p Float64Slice) Less(i, j int) bool { return p.A[i] < p.A[j] || isNaN(p.A[i]) && !isNaN(p.A[j]) } 12 | func (p Float64Slice) Swap(i, j int) { p.A[i], p.A[j], p.I[i], p.I[j] = p.A[j], p.A[i], p.I[j], p.I[i] } 13 | 14 | func (p Float64Slice) Sort() { sort.Sort(p) } 15 | 16 | func isNaN(f float64) bool { 17 | return f != f 18 | } 19 | 20 | func MakeFloat64Slice(A []float64) Float64Slice { 21 | ACopy := make([]float64, len(A)) 22 | I := make([]int, len(A)) 23 | for i := range ACopy { 24 | I[i] = i 25 | } 26 | copy(ACopy, A) 27 | var fs Float64Slice 28 | fs.A = ACopy 29 | fs.I = I 30 | return fs 31 | } 32 | 33 | func sortFloats(A []float64) Float64Slice { 34 | fs := MakeFloat64Slice(A) 35 | fs.Sort() 36 | return fs 37 | } 38 | -------------------------------------------------------------------------------- /tree/data/gen.py: -------------------------------------------------------------------------------- 1 | # Generate some relevant model graphs to use in simulation experiments 2 | 3 | import math 4 | import networkx as nx 5 | 6 | n = 100 7 | 8 | 9 | 10 | # Generic random topologies 11 | 12 | # Simple 3-regular random graphs: unrealistic but useful baseline 13 | nx.write_weighted_edgelist(nx.random_regular_graph(3,n), "3reg.dat") 14 | 15 | # Watts-Strogatz small-world graph 16 | nx.write_weighted_edgelist(nx.watts_strogatz_graph(n,2,0.5), "ws.dat") 17 | 18 | # Barabasi-Albert preferential attachment model 19 | nx.write_weighted_edgelist(nx.barabasi_albert_graph(n,1), "ba.dat") 20 | 21 | # Powerlaw cluster graph 22 | nx.write_weighted_edgelist(nx.powerlaw_cluster_graph(n,1,0.1), "pc.dat") 23 | 24 | 25 | # Planar geographic topologies 26 | 27 | # Navigable small-world planar graph. 28 | # Note that this produces nodes named by grid coords rather than integers. 29 | #gridn = math.ceil(math.sqrt(n)) 30 | #nx.write_weighted_edgelist(nx.navigable_small_world_graph(gridn), "nsw.dat") 31 | 32 | # Waxman planar graph 33 | nx.write_weighted_edgelist(nx.waxman_graph(n), "wax.dat") 34 | 35 | -------------------------------------------------------------------------------- /coco/test/data/gen.py: -------------------------------------------------------------------------------- 1 | # Generate some relevant model graphs to use in simulation experiments 2 | 3 | import math 4 | import networkx as nx 5 | 6 | n = 1000 7 | 8 | 9 | 10 | # Generic random topologies 11 | 12 | # Simple 3-regular random graphs: unrealistic but useful baseline 13 | nx.write_weighted_edgelist(nx.random_regular_graph(3,n), "3reg.dat") 14 | 15 | # Watts-Strogatz small-world graph 16 | nx.write_weighted_edgelist(nx.watts_strogatz_graph(n,2,0.5), "ws.dat") 17 | 18 | # Barabasi-Albert preferential attachment model 19 | nx.write_weighted_edgelist(nx.barabasi_albert_graph(n,1), "ba.dat") 20 | 21 | # Powerlaw cluster graph 22 | nx.write_weighted_edgelist(nx.powerlaw_cluster_graph(n,1,0.1), "pc.dat") 23 | 24 | 25 | # Planar geographic topologies 26 | 27 | # Navigable small-world planar graph. 28 | # Note that this produces nodes named by grid coords rather than integers. 29 | #gridn = math.ceil(math.sqrt(n)) 30 | #nx.write_weighted_edgelist(nx.navigable_small_world_graph(gridn), "nsw.dat") 31 | 32 | # Waxman planar graph 33 | nx.write_weighted_edgelist(nx.waxman_graph(n), "wax.dat") 34 | 35 | -------------------------------------------------------------------------------- /coco/sign/signBenchmark_test.go: -------------------------------------------------------------------------------- 1 | package sign_test 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | 7 | "github.com/dedis/prifi/coco/sign" 8 | "github.com/dedis/prifi/coco/test/oldconfig" 9 | ) 10 | 11 | // func init() { 12 | // log.SetOutput(ioutil.Discard) 13 | // } 14 | 15 | // one after the other by the root (one signature per message created) 16 | func SimpleRoundsThroughput(N int, b *testing.B) { 17 | hc, _ := oldconfig.LoadConfig("../test/data/extcpconf.json", oldconfig.ConfigOptions{ConnType: "tcp", GenHosts: true}) 18 | hc.Run(false, sign.PubKey) 19 | 20 | for n := 0; n < b.N; n++ { 21 | for i := 0; i < N; i++ { 22 | hc.SNodes[0].LogTest = []byte("hello world" + strconv.Itoa(i)) 23 | hc.SNodes[0].Announce(DefaultView, &sign.AnnouncementMessage{LogTest: hc.SNodes[0].LogTest, Round: 0}) 24 | 25 | } 26 | for _, sn := range hc.SNodes { 27 | sn.Close() 28 | } 29 | 30 | } 31 | } 32 | 33 | func BenchmarkSimpleRoundsThroughput100(b *testing.B) { 34 | SimpleRoundsThroughput(100, b) 35 | } 36 | 37 | func BenchmarkSimpleRoundsThroughput200(b *testing.B) { 38 | SimpleRoundsThroughput(200, b) 39 | } 40 | -------------------------------------------------------------------------------- /python/pseudonym_config.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import json 3 | import os 4 | import random 5 | 6 | def main(): 7 | p = argparse.ArgumentParser(description="Generate post-shuffle config from session config") 8 | p.add_argument("output_dir") 9 | opts = p.parse_args() 10 | 11 | # load in the session config 12 | with open(os.path.join(opts.output_dir, "session.json"), "r", encoding="utf-8") as fp: 13 | data = json.load(fp) 14 | clients = data["clients"] 15 | 16 | # shuffle session public keys 17 | session_pub_keys = [client["dhkey"] for client in clients] 18 | random.shuffle(session_pub_keys) 19 | 20 | # XXX trustee signatures 21 | group_id = data["group-id"] 22 | session_id = data["session-id"] 23 | shuffle = { 24 | "group-id" : group_id, 25 | "session-id" : session_id, 26 | "slots" : session_pub_keys, 27 | } 28 | 29 | with open(os.path.join(opts.output_dir, "shuffle.json"), "w", encoding="utf-8") as fp: 30 | json.dump(shuffle, fp) 31 | print("Generated post-shuffle config to {}".format(os.path.join(opts.output_dir, "shuffle.json"))) 32 | 33 | if __name__ == "__main__": 34 | main() 35 | -------------------------------------------------------------------------------- /python/certify/signature.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from Crypto.Hash import SHA256 3 | 4 | class SignatureAccumulator: 5 | def before(self, ciphertexts): 6 | self.signatures = [cph[1] for cph in ciphertexts] 7 | return [cph[0] for cph in ciphertexts] 8 | 9 | def after(self, cleartexts): 10 | return (cleartexts, self.signatures) 11 | 12 | class SignatureCertifier: 13 | def __init__(self, key, client_keys): 14 | self.key = key 15 | self.client_keys = client_keys 16 | h = SHA256.new() 17 | self.hdata = h.digest() 18 | 19 | def certify(self, ciphertexts): 20 | return (ciphertexts, self.key.sign(self.hdata)) 21 | 22 | def verify(self, cleartexts): 23 | signatures = cleartexts[1] 24 | assert len(signatures) == len(self.client_keys) 25 | for idx in len(signatures): 26 | sign = signatures[idx] 27 | assert self.client_keys[idx].verify(self.hdata, signatures[idx]) 28 | 29 | h = SHA256.new() 30 | for slot in cleartexts[0]: 31 | for cell in slot: 32 | h.update(cell) 33 | self.hdata = h.digest() 34 | 35 | return cleartexts[0] 36 | 37 | -------------------------------------------------------------------------------- /coco/test/graphs/quickselect.go: -------------------------------------------------------------------------------- 1 | package graphs 2 | 3 | func kRecSmallest(A []float64, ind []int, k int) int { 4 | pos := partition(A, ind) 5 | // if we have selected k elements return this 6 | if k == pos+1 { 7 | return pos 8 | } 9 | // if k is on the left hand side of the partition 10 | if k < pos+1 { 11 | return kRecSmallest(A[:pos], ind[:pos], k) 12 | } 13 | // look on the right half of the array 14 | // subtract the number of elements on the left hand side 15 | // from k and the element in the center 16 | return kRecSmallest(A[pos+1:], ind[pos+1:], k-pos-1) 17 | } 18 | 19 | // returns an array of indices of the smallest 20 | // it does not alter the input array 21 | func kSmallest(A []float64, k int) []int { 22 | 23 | ind := make([]int, len(A)) 24 | for i := range ind { 25 | ind[i] = i 26 | } 27 | if k >= len(A) { 28 | return ind 29 | } 30 | ACopy := make([]float64, len(A)) 31 | copy(ACopy, A) 32 | kRecSmallest(ACopy, ind, k) 33 | return ind[:k] 34 | } 35 | 36 | func partition(A []float64, ind []int) int { 37 | p := A[len(A)-1] // pivot is last element 38 | i := 0 39 | for j := range A { 40 | if A[i] <= p { 41 | A[i], A[j] = A[j], A[i] 42 | } 43 | } 44 | A[i], A[len(A)-1] = A[len(A)-1], A[i] 45 | return i 46 | } 47 | -------------------------------------------------------------------------------- /time/hash.go: -------------------------------------------------------------------------------- 1 | package time 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | 8 | type HashId []byte // Cryptographic hash content-IDs 9 | 10 | func (id HashId) Bit(i uint) int { 11 | return int(id[i>>3] >> (i&7)) 12 | } 13 | 14 | // Find the skip-chain level of an ID 15 | func (id *HashId) Level() int { 16 | var level uint 17 | for id.Bit(level) == 0 { 18 | level++ 19 | } 20 | return int(level) 21 | } 22 | 23 | 24 | // Context for looking up content blobs by self-certifying HashId. 25 | // Implementations can be either local (same-node) or remote (cross-node). 26 | type HashGet interface { 27 | 28 | // Lookup and return the binary blob for a given content HashId. 29 | // Checks and returns an error if the hash doesn't match the content; 30 | // the caller doesn't need to check this correspondence. 31 | Get(id HashId) ([]byte,error) 32 | } 33 | 34 | 35 | // Simple local-only, map-based implementation of HashGet interface 36 | type HashMap map[string][]byte 37 | 38 | func (m HashMap) Put(id HashId, data []byte) { 39 | m[string(id)] = data 40 | } 41 | 42 | func (m HashMap) Get(id HashId) ([]byte,error) { 43 | blob,ok := m[string(id)] 44 | if !ok { 45 | return nil,errors.New("HashId not found") 46 | } 47 | return blob,nil 48 | } 49 | 50 | 51 | -------------------------------------------------------------------------------- /tree/hash.go: -------------------------------------------------------------------------------- 1 | package tree 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | 8 | type HashId []byte // Cryptographic hash content-IDs 9 | 10 | func (id HashId) Bit(i uint) int { 11 | return int(id[i>>3] >> (i&7)) 12 | } 13 | 14 | // Find the skip-chain level of an ID 15 | func (id *HashId) Level() int { 16 | var level uint 17 | for id.Bit(level) == 0 { 18 | level++ 19 | } 20 | return int(level) 21 | } 22 | 23 | 24 | // Context for looking up content blobs by self-certifying HashId. 25 | // Implementations can be either local (same-node) or remote (cross-node). 26 | type HashGet interface { 27 | 28 | // Lookup and return the binary blob for a given content HashId. 29 | // Checks and returns an error if the hash doesn't match the content; 30 | // the caller doesn't need to check this correspondence. 31 | Get(id HashId) ([]byte,error) 32 | } 33 | 34 | 35 | // Simple local-only, map-based implementation of HashGet interface 36 | type HashMap map[string][]byte 37 | 38 | func (m HashMap) Put(id HashId, data []byte) { 39 | m[string(id)] = data 40 | } 41 | 42 | func (m HashMap) Get(id HashId) ([]byte,error) { 43 | blob,ok := m[string(id)] 44 | if !ok { 45 | return nil,errors.New("HashId not found") 46 | } 47 | return blob,nil 48 | } 49 | 50 | 51 | -------------------------------------------------------------------------------- /tree/intq.go: -------------------------------------------------------------------------------- 1 | package tree 2 | 3 | import ( 4 | "container/heap" 5 | ) 6 | 7 | type intElt struct { 8 | pri int 9 | obj interface{} 10 | } 11 | 12 | // Internal Heap implementation struct 13 | type intHeap struct { 14 | elts []intElt 15 | } 16 | 17 | func (h *intHeap) Len() int { 18 | return len(h.elts) 19 | } 20 | 21 | func (h *intHeap) Less(i,j int) bool { 22 | return h.elts[i].pri < h.elts[j].pri 23 | } 24 | 25 | func (h *intHeap) Swap(i,j int) { 26 | h.elts[i],h.elts[j] = h.elts[j],h.elts[i] 27 | } 28 | 29 | func (h *intHeap) Push(x interface{}) { 30 | h.elts = append(h.elts, x.(intElt)) 31 | } 32 | 33 | func (h *intHeap) Pop() interface{} { 34 | last := len(h.elts)-1 35 | elt := h.elts[last] 36 | h.elts = h.elts[:last] 37 | return elt 38 | } 39 | 40 | // Integer-indexed priority queues. 41 | // Probably horribly unoptimized way to implement an int-priority queue; 42 | // we can consider improving if it ever becomes performance-critical. 43 | type IntQ struct { 44 | heap intHeap 45 | } 46 | 47 | func (q *IntQ) Len() int { 48 | return q.heap.Len() 49 | } 50 | 51 | func (q *IntQ) Push(pri int, obj interface{}) { 52 | heap.Push(&q.heap, intElt{pri,obj}) 53 | } 54 | 55 | func (q *IntQ) Pop() (int,interface{}) { 56 | elt := heap.Pop(&q.heap).(intElt) 57 | return elt.pri,elt.obj 58 | } 59 | 60 | -------------------------------------------------------------------------------- /coco/coconet/faultyHost.go: -------------------------------------------------------------------------------- 1 | package coconet 2 | 3 | type HostState int 4 | 5 | const ( 6 | ALIVE HostState = iota 7 | DEAD 8 | ) 9 | 10 | const DefaultState HostState = ALIVE 11 | 12 | type FaultyHost struct { 13 | Host 14 | 15 | State HostState 16 | 17 | // DeadFor[x]=true is Host muslt play dead when x occurs 18 | // ex: DeadFor["commit"] is set true if host must fail on commit 19 | DeadFor map[string]bool 20 | } 21 | 22 | func NewFaultyHost(host Host, state ...HostState) *FaultyHost { 23 | fh := &FaultyHost{} 24 | 25 | fh.Host = host 26 | if len(state) > 0 { 27 | fh.State = state[0] 28 | } else { 29 | fh.State = DefaultState 30 | } 31 | fh.DeadFor = make(map[string]bool) 32 | 33 | return fh 34 | } 35 | 36 | func (fh *FaultyHost) Die() { 37 | fh.State = DEAD 38 | } 39 | 40 | // returns true is host has failed (for simulating failures) 41 | func (fh *FaultyHost) IsDead() bool { 42 | return fh.State == DEAD 43 | } 44 | 45 | // returns true is host should play dead in a specific case 46 | // ex: DeadFor["commit"] is set true if host must fail on commit 47 | func (fh *FaultyHost) IsDeadFor(x string) bool { 48 | return fh.DeadFor[x] 49 | } 50 | 51 | // ex: DeadFor["commit"] is set true if host must fail on commit 52 | func (fh *FaultyHost) SetDeadFor(x string, val bool) { 53 | fh.DeadFor[x] = val 54 | } 55 | -------------------------------------------------------------------------------- /coco/test/oldconfig/graphs_test.go: -------------------------------------------------------------------------------- 1 | package oldconfig 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/dedis/crypto/nist" 8 | "github.com/dedis/crypto/random" 9 | "github.com/dedis/prifi/coco/sign" 10 | ) 11 | 12 | func TestTreeFromRandomGraph(t *testing.T) { 13 | //defer profile.Start(profile.CPUProfile, profile.ProfilePath(".")).Stop() 14 | hc, err := loadGraph("../data/wax.dat", nist.NewAES128SHA256P256(), random.Stream) 15 | if err != nil || hc == nil { 16 | fmt.Println("run data/gen.py to generate graphs") 17 | return 18 | } 19 | // if err := ioutil.WriteFile("data/wax.json", []byte(hc.String()), 0666); err != nil { 20 | // fmt.Println(err) 21 | // } 22 | //fmt.Println(hc.String()) 23 | 24 | // Have root node initiate the signing protocol 25 | // via a simple annoucement 26 | hc.SNodes[0].LogTest = []byte("Hello World") 27 | //fmt.Println(hc.SNodes[0].NChildren()) 28 | //fmt.Println(hc.SNodes[0].Peers()) 29 | hc.SNodes[0].Announce(0, &sign.AnnouncementMessage{LogTest: hc.SNodes[0].LogTest}) 30 | } 31 | 32 | func Benchmark1000Nodes(b *testing.B) { 33 | hc, _ := LoadConfig("../data/wax.json") 34 | hc.SNodes[0].LogTest = []byte("Hello World") 35 | b.ResetTimer() 36 | for i := 0; i < b.N; i++ { 37 | hc.SNodes[0].Announce(0, &sign.AnnouncementMessage{LogTest: hc.SNodes[0].LogTest, Round: i}) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /coco/test/timeclient/timeclient.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | 6 | log "github.com/Sirupsen/logrus" 7 | 8 | "github.com/dedis/prifi/coco/test/logutils" 9 | "github.com/dedis/prifi/coco/test/oldconfig" 10 | "github.com/dedis/prifi/coco/test/timeclient/stampclient" 11 | ) 12 | 13 | var server string 14 | var nmsgs int 15 | var name string 16 | var logger string 17 | var rate int 18 | var debug bool 19 | 20 | func init() { 21 | addr, _ := oldconfig.GetAddress() 22 | // TODO: change to take in list of servers: comma separated no spaces 23 | // -server=s1,s2,s3,... 24 | flag.StringVar(&server, "server", "", "the timestamping servers to contact") 25 | flag.IntVar(&nmsgs, "nmsgs", 100, "messages per round") 26 | flag.StringVar(&name, "name", addr, "name for the client") 27 | flag.StringVar(&logger, "logger", "", "remote logger") 28 | flag.IntVar(&rate, "rate", -1, "milliseconds between timestamp requests") 29 | flag.BoolVar(&debug, "debug", false, "set debug mode") 30 | //log.SetFormatter(&log.JSONFormatter{}) 31 | } 32 | 33 | func main() { 34 | flag.Parse() 35 | if logger != "" { 36 | // blocks until we can connect to the logger 37 | lh, err := logutils.NewLoggerHook(logger, name, "timeclient") 38 | if err != nil { 39 | log.Fatal(err) 40 | } 41 | log.AddHook(lh) 42 | } 43 | stampclient.Run(server, nmsgs, name, rate, debug) 44 | } 45 | -------------------------------------------------------------------------------- /coco/test/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "net" 5 | 6 | log "github.com/Sirupsen/logrus" 7 | 8 | "github.com/dedis/prifi/coco/test/graphs" 9 | ) 10 | 11 | type ConfigFile struct { 12 | Hosts []string `json:"hosts"` 13 | Tree *graphs.Tree `json:"tree"` 14 | } 15 | 16 | func ConfigFromTree(t *graphs.Tree, hosts []string) *ConfigFile { 17 | cf := &ConfigFile{} 18 | cf.Hosts = make([]string, len(hosts)) 19 | copy(cf.Hosts, hosts) 20 | cf.Tree = t 21 | return cf 22 | } 23 | 24 | func (cf *ConfigFile) AddPorts(ports ...string) { 25 | if len(ports) == 0 { 26 | // default port 9001 27 | log.Println("ports is empty") 28 | ports = append(ports, "9001") 29 | } 30 | if len(ports) == 1 { 31 | port := ports[0] 32 | for i := 1; i < len(cf.Hosts); i++ { 33 | ports = append(ports, port) 34 | } 35 | } 36 | if len(ports) != len(cf.Hosts) { 37 | log.Fatal("len ports != len cf.Hosts") 38 | } 39 | // mcreate a mapping of oldhostnames to new hostnames 40 | hostmap := make(map[string]string) 41 | for i, hp := range cf.Hosts { 42 | h, _, err := net.SplitHostPort(hp) 43 | if err != nil { 44 | h = hp 45 | } 46 | hostmap[hp] = net.JoinHostPort(h, ports[i]) 47 | cf.Hosts[i] = net.JoinHostPort(h, ports[i]) 48 | } 49 | log.Println("add ports hostmap:", hostmap) 50 | cf.Tree.TraverseTree(func(t *graphs.Tree) { 51 | t.Name = hostmap[t.Name] 52 | }) 53 | } 54 | -------------------------------------------------------------------------------- /insure/doc.go: -------------------------------------------------------------------------------- 1 | package insure 2 | 3 | /* 4 | * This package will implement an insurance policy for the coco collective 5 | * consensus signing protocol. 6 | * 7 | * In order to achieve a more scalable Dissent, it is important that the 8 | * protocol is able to make progress even in the midst of server failure. 9 | * 10 | * To accomplish this, servers will have to take out an "insurance policy". 11 | * Servers will use Shamir Secret Sharing to give shares of their private keys 12 | * to n other servers who will act as insurers. Once the server has done so, 13 | * it can fully participate in the system and perform work for clients. 14 | * 15 | * In the event that a server becomes unresponsive, clients currently relying on 16 | * the server can contact insurers. Each insurer will then attempt to contact 17 | * the server. If the server responds with the desired work for the client, the 18 | * insurer will simply forward this to the client. Otherwise, it will give its 19 | * piece of the secret to the client. If the client is able to receive t of n 20 | * shares from the insurers, the client can recreate the private key of the 21 | * server and carry out the work itself. 22 | * 23 | * This prelimary documentation will be updated as progress is made. 24 | * 25 | * DISCLAIMER: Life Policy is a work in progress. It's interface will change. 26 | * Please contact WEB3-GForce before using this code. 27 | */ 28 | -------------------------------------------------------------------------------- /net/view.go: -------------------------------------------------------------------------------- 1 | package net 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | // A View represents an abstract network access viewpoint. 8 | type View interface { 9 | 10 | // Connect to a destination on a named network through this view. 11 | // The optional net.Dialer specifies timeout and other parameters 12 | // to be used in the connection attempt. 13 | Dial(network, address string, options *net.Dialer) (net.Conn, error) 14 | 15 | // Listen for stream-oriented connections at a network address. 16 | Listen(network, address string) (net.Listener, error) 17 | 18 | // Listen for packet-oriented connections at a network address. 19 | ListenPacket(network, address string) (net.PacketConn, error) 20 | } 21 | 22 | 23 | type sysView struct { 24 | } 25 | 26 | func (*sysView) Dial(network, address string, 27 | dialer *net.Dialer) (net.Conn, error) { 28 | if dialer != nil { 29 | return dialer.Dial(network, address) 30 | } else { 31 | return net.Dial(network, address) 32 | } 33 | } 34 | 35 | func (*sysView) Listen(network, address string) (net.Listener, error) { 36 | return net.Listen(network, address) 37 | } 38 | 39 | func (*sysView) ListenPacket(network, address string) (net.PacketConn, error) { 40 | return net.ListenPacket(network, address) 41 | } 42 | 43 | 44 | // Base implementation of View interface represent the system's 45 | // "native" network viewpoint. 46 | var SystemView View = new(sysView) 47 | 48 | -------------------------------------------------------------------------------- /coco/test/graphs/tree.go: -------------------------------------------------------------------------------- 1 | package graphs 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | 7 | "github.com/dedis/crypto/abstract" 8 | ) 9 | 10 | // tree easy to deal with 11 | // default json encoding can be read as the 12 | // Tree section of a config file 13 | type Tree struct { 14 | Name string `json:"name"` 15 | // hex encoded public and private keys 16 | PriKey string `json:"prikey,omitempty"` 17 | PubKey string `json:"pubkey,omitempty"` 18 | Children []*Tree `json:"children,omitempty"` 19 | } 20 | 21 | func (t *Tree) TraverseTree(f func(*Tree)) { 22 | f(t) 23 | for _, c := range t.Children { 24 | c.TraverseTree(f) 25 | } 26 | } 27 | 28 | // generate keys for the tree 29 | func (t *Tree) GenKeys(suite abstract.Suite, rand abstract.Cipher) { 30 | t.TraverseTree(func(t *Tree) { 31 | PrivKey := suite.Secret().Pick(rand) 32 | PubKey := suite.Point().Mul(nil, PrivKey) 33 | prk, _ := PrivKey.MarshalBinary() 34 | pbk, _ := PubKey.MarshalBinary() 35 | t.PriKey = string(hex.EncodeToString(prk)) 36 | t.PubKey = string(hex.EncodeToString(pbk)) 37 | }) 38 | } 39 | 40 | func PrintTreeNode(t *Tree) { 41 | fmt.Println(t.Name) 42 | 43 | for _, c := range t.Children { 44 | fmt.Println("\t", c.Name) 45 | } 46 | } 47 | 48 | func Depth(t *Tree) int { 49 | md := 0 50 | for _, c := range t.Children { 51 | dc := Depth(c) 52 | if dc > md { 53 | md = dc 54 | } 55 | } 56 | return md + 1 57 | } 58 | -------------------------------------------------------------------------------- /coco/proof/proof_test.go: -------------------------------------------------------------------------------- 1 | package proof 2 | 3 | import ( 4 | "crypto/sha256" 5 | "testing" 6 | 7 | "github.com/dedis/prifi/coco/hashid" 8 | ) 9 | 10 | func TestPath(t *testing.T) { 11 | 12 | newHash := sha256.New 13 | hash := newHash() 14 | n := 13 15 | 16 | leaves := make([]hashid.HashId, n) 17 | for i := range leaves { 18 | leaves[i] = make([]byte, hash.Size()) 19 | for j := range leaves[i] { 20 | leaves[i][j] = byte(i) 21 | } 22 | // println("leaf", i, ":", hex.EncodeToString(leaves[i])) 23 | // fmt.Println("leaf", i, ":", leaves[i]) 24 | } 25 | 26 | root, proofs := ProofTree(newHash, leaves) 27 | for i := range proofs { 28 | if proofs[i].Check(newHash, root, leaves[i]) == false { 29 | t.Error("check failed at leaf", i) 30 | } 31 | } 32 | } 33 | 34 | func TestPathLong(t *testing.T) { 35 | if testing.Short() { 36 | t.Skip("skipping test in short mode.") 37 | } 38 | 39 | newHash := sha256.New 40 | hash := newHash() 41 | n := 100 // takes 6 secons 42 | for k := 0; k < n; k++ { 43 | leaves := make([]hashid.HashId, k) 44 | for i := range leaves { 45 | leaves[i] = make([]byte, hash.Size()) 46 | for j := range leaves[i] { 47 | leaves[i][j] = byte(i) 48 | } 49 | } 50 | 51 | root, proofs := ProofTree(newHash, leaves) 52 | for i := range proofs { 53 | if proofs[i].Check(newHash, root, leaves[i]) == false { 54 | t.Error("check failed at leaf", i) 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /coco/sign/snlog.go: -------------------------------------------------------------------------------- 1 | package sign 2 | 3 | import ( 4 | "bytes" 5 | "encoding/gob" 6 | 7 | "github.com/dedis/crypto/abstract" 8 | "github.com/dedis/prifi/coco/hashid" 9 | ) 10 | 11 | // Signing Node Log for a round 12 | // For Marshaling and Unrmarshaling to work smoothly 13 | // crypto fields must appear first in the structure 14 | type SNLog struct { 15 | v abstract.Secret // round lasting secret 16 | V abstract.Point // round lasting commitment point 17 | V_hat abstract.Point // aggregate of commit points 18 | 19 | // merkle tree roots of children in strict order 20 | CMTRoots hashid.HashId // concatenated hash ids of children 21 | Suite abstract.Suite 22 | } 23 | 24 | func (snLog SNLog) MarshalBinary() ([]byte, error) { 25 | // abstract.Write used to encode/ marshal crypto types 26 | b := bytes.Buffer{} 27 | abstract.Write(&b, &snLog.v, snLog.Suite) 28 | abstract.Write(&b, &snLog.V, snLog.Suite) 29 | abstract.Write(&b, &snLog.V_hat, snLog.Suite) 30 | // gob is used to encode non-crypto types 31 | enc := gob.NewEncoder(&b) 32 | err := enc.Encode(snLog.CMTRoots) 33 | return b.Bytes(), err 34 | } 35 | 36 | func (snLog *SNLog) UnmarshalBinary(data []byte) error { 37 | // abstract.Read used to decode/ unmarshal crypto types 38 | b := bytes.NewBuffer(data) 39 | err := abstract.Read(b, snLog, snLog.Suite) 40 | // gob is used to decode non-crypto types 41 | rem, _ := snLog.MarshalBinary() 42 | snLog.CMTRoots = data[len(rem):] 43 | return err 44 | } 45 | -------------------------------------------------------------------------------- /coco/sign/basicSig.go: -------------------------------------------------------------------------------- 1 | package sign 2 | 3 | import ( 4 | "crypto/cipher" 5 | "errors" 6 | 7 | "github.com/dedis/crypto/abstract" 8 | ) 9 | 10 | // A basic, verifiable signature 11 | type BasicSig struct { 12 | C abstract.Secret // challenge 13 | R abstract.Secret // response 14 | } 15 | 16 | // This simplified implementation of ElGamal Signatures is based on 17 | // crypto/anon/sig.go 18 | func ElGamalSign(suite abstract.Suite, random cipher.Stream, message []byte, 19 | privateKey abstract.Secret) BasicSig { 20 | 21 | // Create random secret v and public point commitment T 22 | v := suite.Secret().Pick(random) 23 | T := suite.Point().Mul(nil, v) 24 | 25 | // Create challenge c based on message and T 26 | c := hashElGamal(suite, message, T) 27 | 28 | // Compute response r = v - x*c 29 | r := suite.Secret() 30 | r.Mul(privateKey, c).Sub(v, r) 31 | 32 | // Return verifiable signature {c, r} 33 | sig := BasicSig{c, r} 34 | return sig 35 | } 36 | 37 | func ElGamalVerify(suite abstract.Suite, message []byte, publicKey abstract.Point, 38 | sig BasicSig) error { 39 | r := sig.R 40 | c := sig.C 41 | 42 | // Compute base**(r + x*c) == T 43 | var P, T abstract.Point 44 | P = suite.Point() 45 | T = suite.Point() 46 | T.Add(T.Mul(nil, r), P.Mul(publicKey, c)) 47 | 48 | // Verify that the hash based on the message and T 49 | // matches the challange c from the signature 50 | c = hashElGamal(suite, message, T) 51 | if !c.Equal(sig.C) { 52 | return errors.New("invalid signature") 53 | } 54 | 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /coco/sign/interfaces.go: -------------------------------------------------------------------------------- 1 | package sign 2 | 3 | import ( 4 | "github.com/dedis/crypto/abstract" 5 | "github.com/dedis/prifi/coco/hashid" 6 | "github.com/dedis/prifi/coco/proof" 7 | ) 8 | 9 | var DEBUG bool // to avoid veryfing paths and signatures all the time 10 | 11 | // Returns commitment contribution for a round 12 | type CommitFunc func(view int) []byte 13 | 14 | // Called at the end of a round 15 | // Allows client of Signer to receive signature, proof, and error via RPC 16 | type DoneFunc func(view int, SNRoot hashid.HashId, LogHash hashid.HashId, p proof.Proof) 17 | 18 | // todo: see where Signer should be located 19 | type Signer interface { 20 | Name() string 21 | IsRoot(view int) bool 22 | Suite() abstract.Suite 23 | StartSigningRound() error 24 | StartVotingRound(v *Vote) error 25 | 26 | LastRound() int // last round number seen by Signer 27 | SetLastSeenRound(int) // impose change in round numbering 28 | 29 | Hostlist() []string 30 | 31 | // // proof can be nil for simple non Merkle Tree signatures 32 | // // could add option field for Sign 33 | // Sign([]byte) (hashid.HashId, proof.Proof, error) 34 | 35 | // registers a commitment function to be called 36 | // at the start of every round 37 | RegisterAnnounceFunc(cf CommitFunc) 38 | 39 | RegisterDoneFunc(df DoneFunc) 40 | 41 | // Allows user of Signer to inform Signer to run with simulated failures 42 | // As to test robustness of Signer 43 | SetFailureRate(val int) 44 | 45 | ViewChangeCh() chan string 46 | 47 | Close() 48 | Listen() error 49 | 50 | AddSelf(host string) error 51 | RemoveSelf() error 52 | } 53 | -------------------------------------------------------------------------------- /coco/sign/vote_test.go: -------------------------------------------------------------------------------- 1 | package sign_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | _ "github.com/dedis/prifi/coco" 8 | "github.com/dedis/prifi/coco/sign" 9 | "github.com/dedis/prifi/coco/test/oldconfig" 10 | ) 11 | 12 | // Configuration file data/exconf.json 13 | // 0 14 | // / \ 15 | // 1 4 16 | // / \ \ 17 | // 2 3 5 18 | func TestTreeSmallConfigVote(t *testing.T) { 19 | hc, err := oldconfig.LoadConfig("../test/data/exconf.json") 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | 24 | err = hc.Run(false, sign.Voter) 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | 29 | // Achieve consensus on removing a node 30 | vote := &sign.Vote{Type: sign.AddVT, Av: &sign.AddVote{Name: "host5", Parent: "host4"}} 31 | err = hc.SNodes[0].StartVotingRound(vote) 32 | 33 | if err != nil { 34 | t.Error(err) 35 | } 36 | 37 | } 38 | 39 | func TestTCPStaticConfigVote(t *testing.T) { 40 | hc, err := oldconfig.LoadConfig("../test/data/extcpconf.json", oldconfig.ConfigOptions{ConnType: "tcp", GenHosts: true}) 41 | if err != nil { 42 | t.Error(err) 43 | } 44 | defer func() { 45 | for _, n := range hc.SNodes { 46 | n.Close() 47 | } 48 | time.Sleep(1 * time.Second) 49 | }() 50 | 51 | err = hc.Run(false, sign.Voter) 52 | if err != nil { 53 | t.Fatal(err) 54 | } 55 | 56 | // give it some time to set up 57 | time.Sleep(2 * time.Second) 58 | 59 | hc.SNodes[0].LogTest = []byte("Hello Voting") 60 | vote := &sign.Vote{Type: sign.RemoveVT, Rv: &sign.RemoveVote{Name: "host5", Parent: "host4"}} 61 | err = hc.SNodes[0].StartVotingRound(vote) 62 | 63 | if err != nil { 64 | t.Error(err) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /python/relay.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import json 3 | import os 4 | import random 5 | import requests 6 | 7 | from bottle import request, route, run 8 | 9 | import dcnet 10 | 11 | @route("/client_ciphertext", method="POST") 12 | def client_ciphertext(): 13 | return _client_ciphertext(request.json) 14 | 15 | def _client_ciphertext(client_data): 16 | client_id = client_data["client_id"] 17 | data = client_data["data"] 18 | relay.decode_client(client_id, data) 19 | # XXX hardcoded for now 20 | if relay.client_received == 10 and relay.trustee_received == 3: 21 | print(relay.decode_final().decode("utf-8")) 22 | relay.decode_start() 23 | return None 24 | 25 | @route("/trustee_ciphertext", method="POST") 26 | def trustee_ciphertext(): 27 | return _trustee_ciphertext(request.json) 28 | 29 | def _trustee_ciphertext(trustee_data): 30 | trustee_id = trustee_data["trustee_id"] 31 | data = trustee_data["data"] 32 | relay.decode_trustee(trustee_id, data) 33 | # XXX hardcoded for now 34 | if relay.client_received == 10 and relay.trustee_received == 3: 35 | print(relay.decode_final().decode("utf-8")) 36 | relay.decode_start() 37 | return None 38 | 39 | def main(): 40 | global relay 41 | def main(): 42 | global relay 43 | 44 | p = argparse.ArgumentParser(description="Basic DC-net trustee") 45 | p.add_argument("-p", "--port", type=int, metavar="N", default=8888, dest="port") 46 | opts = p.parse_args() 47 | 48 | relay = dcnet.Relay() 49 | relay.decode_start() 50 | 51 | # start the http server 52 | run(port=opts.port) 53 | 54 | if __name__ == "__main__": 55 | main() 56 | -------------------------------------------------------------------------------- /coco/coconet/conn.go: -------------------------------------------------------------------------------- 1 | package coconet 2 | 3 | import ( 4 | "github.com/dedis/crypto/abstract" 5 | ) 6 | 7 | // Conn is an abstract bidirectonal connection. 8 | // It abstracts away the network layer as well as the data-format for communication. 9 | type Conn interface { 10 | // Name returns the name of the "to" end of the connectio. 11 | Name() string 12 | 13 | // PubKey returns the public key associated with the peer. 14 | PubKey() abstract.Point 15 | SetPubKey(abstract.Point) 16 | 17 | // Put puts data to the connection, calling the MarshalBinary method as needed. 18 | Put(data BinaryMarshaler) error 19 | // Get gets data from the connection, calling the UnmarshalBinary method as needed. 20 | // It blocks until it successfully receives data or there was a network error. 21 | // It returns io.EOF if the channel has been closed. 22 | Get(data BinaryUnmarshaler) error 23 | 24 | // Connect establishes the connection. Before using the Put and Get 25 | // methods of a Conn, Connect must first be called. 26 | Connect() error 27 | 28 | // Close closes the connection. Any blocked Put or Get operations will 29 | // be unblocked and return errors. 30 | Close() 31 | 32 | // Indicates whether the connection has been closed 33 | Closed() bool 34 | } 35 | 36 | // Taken from: http://golang.org/pkg/encoding/#BinaryMarshaler 37 | // All messages passing through our conn must implement their own BinaryMarshaler 38 | type BinaryMarshaler interface { 39 | MarshalBinary() (data []byte, err error) 40 | } 41 | 42 | // Taken from: http://golang.org/pkg/encoding/#BinaryMarshaler 43 | // All messages passing through our conn must implement their own BinaryUnmarshaler 44 | type BinaryUnmarshaler interface { 45 | UnmarshalBinary(data []byte) error 46 | } 47 | -------------------------------------------------------------------------------- /coco/hashid/hashid.go: -------------------------------------------------------------------------------- 1 | package hashid 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | ) 7 | 8 | const Size int = 32 // TODO: change the way this is known 9 | 10 | type HashId []byte // Cryptographic hash content-IDs 11 | 12 | // for sorting arrays of HashIds 13 | type ByHashId []HashId 14 | 15 | func (h ByHashId) Len() int { return len(h) } 16 | func (h ByHashId) Swap(i, j int) { h[i], h[j] = h[j], h[i] } 17 | func (h ByHashId) Less(i, j int) bool { return bytes.Compare(h[i], h[j]) < 0 } 18 | 19 | // takes in slice of hashIds and returns all of them but the ith 20 | func AllButI(mtroots []HashId, i int) []HashId { 21 | return append(mtroots[:i], mtroots[i+1:]...) 22 | } 23 | 24 | func (id HashId) Bit(i uint) int { 25 | return int(id[i>>3] >> (i & 7)) 26 | } 27 | 28 | // Find the skip-chain level of an ID 29 | func (id *HashId) Level() int { 30 | var level uint 31 | for id.Bit(level) == 0 { 32 | level++ 33 | } 34 | return int(level) 35 | } 36 | 37 | // Context for looking up content blobs by self-certifying HashId. 38 | // Implementations can be either local (same-node) or remote (cross-node). 39 | type HashGet interface { 40 | 41 | // Lookup and return the binary blob for a given content HashId. 42 | // Checks and returns an error if the hash doesn't match the content; 43 | // the caller doesn't need to check this correspondence. 44 | Get(id HashId) ([]byte, error) 45 | } 46 | 47 | // Simple local-only, map-based implementation of HashGet interface 48 | type HashMap map[string][]byte 49 | 50 | func (m HashMap) Put(id HashId, data []byte) { 51 | m[string(id)] = data 52 | } 53 | 54 | func (m HashMap) Get(id HashId) ([]byte, error) { 55 | blob, ok := m[string(id)] 56 | if !ok { 57 | return nil, errors.New("HashId not found") 58 | } 59 | return blob, nil 60 | } 61 | -------------------------------------------------------------------------------- /coco/test/oldconfig/config_test.go: -------------------------------------------------------------------------------- 1 | package oldconfig 2 | 3 | import ( 4 | "sync" 5 | "testing" 6 | 7 | "github.com/dedis/prifi/coco/sign" 8 | ) 9 | 10 | func TestLoadConfig(t *testing.T) { 11 | _, err := LoadConfig("../data/exconf.json") 12 | if err != nil { 13 | t.Error("error parsing json file:", err) 14 | } 15 | } 16 | 17 | func TestPubKeysConfig(t *testing.T) { 18 | _, err := LoadConfig("../data/exconf.json", ConfigOptions{ConnType: "tcp", GenHosts: true}) 19 | if err != nil { 20 | t.Fatal("error parsing json file:", err) 21 | } 22 | // if err := ioutil.WriteFile("data/exconf_wkeys.json", []byte(hc.String()), 0666); err != nil { 23 | // t.Fatal(err) 24 | // } 25 | } 26 | 27 | func TestPubKeysOneNode(t *testing.T) { 28 | // has hosts 8089 - 9094 @ 172.27.187.80 29 | done := make(chan bool) 30 | hosts := []string{ 31 | ":6095", 32 | ":6096", 33 | ":6097", 34 | ":6098", 35 | ":6099", 36 | ":6100"} 37 | nodes := make(map[string]*sign.Node) 38 | var mu sync.Mutex 39 | var wg sync.WaitGroup 40 | for _, host := range hosts { 41 | wg.Add(1) 42 | go func(host string) { 43 | hc, err := LoadConfig("../data/exconf_wkeys.json", ConfigOptions{ConnType: "tcp", Host: host, Hostnames: hosts}) 44 | if err != nil { 45 | done <- true 46 | t.Fatal(err) 47 | } 48 | 49 | err = hc.Run(false, sign.MerkleTree, host) 50 | if err != nil { 51 | done <- true 52 | t.Fatal(err) 53 | } 54 | 55 | mu.Lock() 56 | nodes[host] = hc.SNodes[0] 57 | mu.Unlock() 58 | 59 | if hc.SNodes[0].IsRoot(0) { 60 | hc.SNodes[0].LogTest = []byte("Hello World") 61 | err = hc.SNodes[0].Announce(0, &sign.AnnouncementMessage{LogTest: hc.SNodes[0].LogTest}) 62 | if err != nil { 63 | t.Fatal(err) 64 | } 65 | done <- true 66 | hc.SNodes[0].Close() 67 | } 68 | wg.Done() 69 | }(host) 70 | } 71 | <-done 72 | wg.Wait() 73 | for _, sn := range nodes { 74 | sn.Close() 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /time/seq.go: -------------------------------------------------------------------------------- 1 | package time 2 | 3 | import ( 4 | "io" 5 | "errors" 6 | "encoding/binary" 7 | ) 8 | 9 | const ( 10 | MaxSeqLen = binary.MaxVarintLen64 11 | ) 12 | 13 | var overflow = errors.New("sequence number overflows a 64-bit integer") 14 | 15 | // putSeq encodes a 64-bit sequence number into buf, 16 | // compressed based on sequence number ref, 17 | // and returns the number of bytes written. 18 | func putSeq(buf []byte, x, ref uint64) int { 19 | i := 0 20 | for x^ref >= 0x80 { 21 | buf[i] = byte(x) | 0x80 22 | x >>= 7 23 | ref >>= 7 24 | i++ 25 | } 26 | buf[i] = byte(x) & 0x7f 27 | return i + 1 28 | } 29 | 30 | // getSeq decodes a sequence number from buf based on sequence number ref, 31 | // and returns that value and the number of bytes read (> 0). 32 | // If an error occurred, the value is 0 and the number of bytes n 33 | // is <= 0 meaning: 34 | // 35 | // n == 0: buf too small 36 | // n < 0: value larger than 64 bits (overflow) 37 | // and -n is the number of bytes read 38 | // 39 | func getSeq(buf []byte, ref uint64) (uint64, int) { 40 | x := ref 41 | var s uint 42 | for i, b := range buf { 43 | x &^= 0x7f << s // clear out the next 7 bits 44 | if b < 0x80 { 45 | if i > 9 || i == 9 && b > 1 { 46 | return 0, -(i + 1) // overflow 47 | } 48 | return x | uint64(b)< 9 || i == 9 && b > 1 { 69 | return x, overflow 70 | } 71 | return x | uint64(b)< /dev/null 2>/dev/null < /dev/null &' > /dev/null 2>/dev/null < /dev/null &") 75 | return cmd.Run() 76 | 77 | } 78 | 79 | func Build(path, goarch, goos string) error { 80 | var cmd *exec.Cmd 81 | cmd = exec.Command("go", "build", "-v", path) 82 | cmd.Stdout = os.Stdout 83 | cmd.Stderr = os.Stderr 84 | cmd.Env = append([]string{"GOOS=" + goos, "GOARCH=" + goarch}, os.Environ()...) 85 | return cmd.Run() 86 | } 87 | 88 | func TimeoutRun(d time.Duration, f func() error) error { 89 | echan := make(chan error) 90 | go func() { 91 | echan <- f() 92 | }() 93 | var e error 94 | select { 95 | case e = <-echan: 96 | case <-time.After(d): 97 | e = errors.New("function timed out") 98 | } 99 | return e 100 | } 101 | -------------------------------------------------------------------------------- /python/client.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import json 3 | import os 4 | import random 5 | import requests 6 | 7 | from bottle import request, route, run 8 | 9 | import dcnet 10 | 11 | @route("/interval_conclusion", method="POST") 12 | def interval_conclusion(): 13 | return _interval_conclusion(request.json) 14 | 15 | def _interval_conclusion(interval_data): 16 | # run for one cell (first client gets ownership) 17 | # XXX hack to get payload_len for now 18 | message = "This is client-0's message.".encode("utf-8") 19 | payload_len = len(message) 20 | client_id = client.id 21 | # XXX recalculating public key is bad 22 | # but this code will be replaced shortly anyway 23 | if pow(dcnet.G, client.private_key, dcnet.P) != slots[0]: 24 | message = None 25 | 26 | cell = client.encode(payload_len, message) 27 | d = { 28 | "client_id" : client_id, 29 | "data" : cell, 30 | } 31 | r = relay_call("client_ciphertext", d) 32 | return None 33 | 34 | def relay_call(name, data): 35 | return requests.post("http://{}/{}".format(relay_address, name), 36 | headers={"content-type" : "application/json"}, 37 | data=json.dumps(data)) 38 | 39 | def main(): 40 | global client 41 | global relay_address 42 | global slots 43 | 44 | p = argparse.ArgumentParser(description="Basic DC-net client") 45 | p.add_argument("-p", "--port", type=int, metavar="N", default=8888, dest="port") 46 | p.add_argument("data_dir") 47 | p.add_argument("private_data") 48 | opts = p.parse_args() 49 | 50 | # start new client using id and key from per-session private_data 51 | with open(opts.private_data, "r", encoding="utf-8") as fp: 52 | data = json.load(fp) 53 | client_id = data["id"] 54 | private_key = data["private_key"] 55 | client = dcnet.Client(client_id, private_key) 56 | # load addresses from system config 57 | with open(os.path.join(opts.data_dir, "system.json"), "r", encoding="utf-8") as fp: 58 | data = json.load(fp) 59 | # XXX only using first relay for now 60 | relay_address = data["relays"][0]["ip"] 61 | # and public keys from session config 62 | with open(os.path.join(opts.data_dir, "session.json"), "r", encoding="utf-8") as fp: 63 | data = json.load(fp) 64 | trustee_keys = [t["dhkey"] for t in data["servers"]] 65 | client.compute_secrets(trustee_keys) 66 | # and slots from the post-shuffle config 67 | with open(os.path.join(opts.data_dir, "shuffle.json"), "r", encoding="utf-8") as fp: 68 | data = json.load(fp) 69 | slots = data["slots"] 70 | 71 | # start the http server 72 | run(port=opts.port) 73 | 74 | if __name__ == "__main__": 75 | main() 76 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Dissent - Dining-Cryptographers, Shuffled-Send Network 2 | 3 | July 2018: renamed to prifi_archive, the new version is available at https://github.com/dedis/prifi 4 | 5 | Overview 6 | =============================================================================== 7 | This library will eventually provide a framework for constructing provably 8 | anonymous overlays with the following key features. 9 | 10 | Contact 11 | =============================================================================== 12 | TBA 13 | 14 | Workflow 15 | =============================================================================== 16 | More specifically, these are requirements that should be followed prior to 17 | asking for a code review / pull. A code review / pull in this sense refers to 18 | the submission of a pull request not an academic discussion on design choices. 19 | 20 | API expectations: 21 | - Doxygen with comments 22 | - Follows existing patterns in the code or accompanies patches that improve 23 | those interfaces 24 | 25 | Internals: 26 | - Minimizes dependencies, if A can work without knowing about B, A should not 27 | know about B, make a C that knows about both to handle interoperation 28 | - Emphasize Qt features while minimizing additional dependencies on external 29 | libraries 30 | - Avoid blocking calls 31 | 32 | Commits: 33 | - The comments should be clear and only code chunks relevant to the commit 34 | message should be included 35 | - Merges should be rebased onto the current master and tested working before 36 | merging (git checkout -b merge master && git merge --squash dev) 37 | - Incomplete work should not be committed nor merged 38 | 39 | Also note, when you submit code for inclusion in this library, you implicitly 40 | release your personal license to the software to Yale Dedis, see below. 41 | 42 | License 43 | =============================================================================== 44 | While the code linked relies on a variety of licenses, which must be respected, 45 | the code contained herein shall use the following license: 46 | 47 | Copyright (C) 2014 Yale Dedis 48 | 49 | This program is free software; you can redistribute it and/or modify it under 50 | the terms of the GNU General Public License as published by the Free Software 51 | Foundation; either version 2 of the License, or (at your option) any later 52 | version. 53 | 54 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 55 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 56 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 57 | 58 | You should have received a copy of the GNU General Public License along with 59 | this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 60 | Street, Fifth Floor, Boston, MA 02110-1301, USA. 61 | -------------------------------------------------------------------------------- /python/supervisor.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import json 3 | import os 4 | import requests 5 | import time 6 | import signal 7 | import subprocess 8 | import sys 9 | 10 | def main(): 11 | p = argparse.ArgumentParser(description="Start up a number of clients on localhost") 12 | p.add_argument("config_dir") 13 | opts = p.parse_args() 14 | 15 | # find the ids and addresses of participants to start 16 | with open(os.path.join(opts.config_dir, "system.json"), "r", encoding="utf-8") as fp: 17 | data = json.load(fp) 18 | relay_ids, relay_ips = zip(*[(r["id"], r["ip"]) for r in data["relays"]]) 19 | client_ids, client_ips = zip(*[(c["id"], c["ip"]) for c in data["clients"]]) 20 | trustee_ids, trustee_ips = zip(*[(t["id"], t["ip"]) for t in data["servers"]]) 21 | 22 | with open(os.path.join(opts.config_dir, "session.json"), "r", encoding="utf-8") as fp: 23 | data = json.load(fp) 24 | session_id = data["session-id"] 25 | 26 | # XXX assuming that relay is already running on addresses from config 27 | # so we don't start them up here - this makes debugging easier for now 28 | 29 | try: 30 | # spawn n client processes 31 | print("Launching {} clients".format(len(client_ids))) 32 | procs = [] 33 | for iden, ip in zip(client_ids, client_ips): 34 | private_data = os.path.join(opts.config_dir, "{}-{}.json".format(iden, session_id)) 35 | p = subprocess.Popen([sys.executable, "dcnet_client.py", opts.config_dir, 36 | private_data, "-p", ip.split(":")[1]], 37 | stdout=subprocess.PIPE, stderr=subprocess.PIPE) 38 | procs.append(p) 39 | 40 | # same with trustees 41 | print("Launching {} trustees".format(len(trustee_ids))) 42 | for iden, ip in zip(trustee_ids, trustee_ips): 43 | private_data = os.path.join(opts.config_dir, "{}-{}.json".format(iden, session_id)) 44 | p = subprocess.Popen([sys.executable, "dcnet_trustee.py", opts.config_dir, 45 | private_data, "-p", ip.split(":")[1]], 46 | stdout=subprocess.PIPE, stderr=subprocess.PIPE) 47 | procs.append(p) 48 | 49 | # initiate an interval on all clients and trustees 50 | time.sleep(1) 51 | print("Starting dc-net") 52 | for ip in client_ips + trustee_ips: 53 | r = requests.post("http://{}/interval_conclusion".format(ip)) 54 | 55 | while True: 56 | time.sleep(1) 57 | except KeyboardInterrupt: 58 | pass 59 | print("Cleaning up") 60 | for i, p in enumerate(procs): 61 | p.wait() 62 | print("Client {}".format(i)) 63 | print("-"*20) 64 | print(p.stderr.read().decode("utf-8")) 65 | print("-"*20) 66 | 67 | 68 | if __name__ == "__main__": 69 | main() 70 | -------------------------------------------------------------------------------- /tree/merkle.go: -------------------------------------------------------------------------------- 1 | package tree 2 | 3 | import ( 4 | "bytes" 5 | "crypto/subtle" 6 | "errors" 7 | "hash" 8 | 9 | "github.com/dedis/crypto/abstract" 10 | ) 11 | 12 | // MerklePath represents a downward path from a (root) node in a Merkle tree 13 | // to a given (interior or leaf) descendant node, 14 | // including all the data necessary to validate and extract the descendant. 15 | // It is assumed the caller has a valid hash-pointer to the root/starting node, 16 | // and that all nodes in the path can be retrieved via self-certifying hash-ID. 17 | type MerklePath struct { 18 | Ptr []int // Offsets of hash-pointers at each intermediate level 19 | Ofs int // Offset of relevant object in last-level blob 20 | Len int // Length of relevant object in last-level blob 21 | } 22 | 23 | // Retrieve an object in a Merkle tree, 24 | // validating the entire path in the process. 25 | // Returns a slice of a buffer obtained from HashGet.Get(), 26 | // which might be shared and should be considered read-only. 27 | func MerkleGet(suite abstract.Suite, root []byte, path MerklePath, 28 | ctx HashGet) ([]byte, error) { 29 | 30 | // Follow pointers through intermediate levels 31 | blob := root 32 | for i := range path.Ptr { 33 | beg := path.Ptr[i] 34 | end := beg + suite.HashLen() 35 | if end > len(blob) { 36 | return nil, errors.New("bad Merkle tree pointer offset") 37 | } 38 | id := HashId(blob[beg:end]) 39 | b, e := ctx.Get(id) // Lookup the next-level blob 40 | if e != nil { 41 | return nil, e 42 | } 43 | blob = b 44 | } 45 | 46 | // Validate and extract the actual object 47 | beg := path.Ofs 48 | end := beg + path.Len 49 | if end > len(blob) { 50 | return nil, errors.New("bad Merkle tree object offset/length") 51 | } 52 | return blob[beg:end], nil 53 | } 54 | 55 | // Proof-of-beforeness: 56 | // a list of offsets of peer-hash-pointers at each level below the root. 57 | type MerkleProof []HashId 58 | 59 | // Given a MerkleProof and the hash of the leaf, compute the hash of the root. 60 | // If the MerkleProof is of length 0, simply returns leaf. 61 | func (p MerkleProof) Calc(newHash func() hash.Hash, leaf []byte) []byte { 62 | var buf []byte 63 | var h hash.Hash 64 | for i := len(p) - 1; i >= 0; i-- { 65 | peer := p[i] 66 | if bytes.Compare(leaf, peer) > 0 { // sort so leaf < peer 67 | leaf, peer = peer, leaf 68 | } 69 | 70 | // Hash the sorted leaf/peer pair to yield the next-higher node 71 | if h == nil { 72 | h = newHash() 73 | } else { 74 | h.Reset() 75 | } 76 | h.Write(leaf) 77 | h.Write(peer) 78 | buf = h.Sum(buf[:0]) 79 | leaf = buf 80 | } 81 | return leaf 82 | } 83 | 84 | // Check a purported MerkleProof against given root and leaf hashes. 85 | func (p MerkleProof) Check(newHash func() hash.Hash, root, leaf []byte) bool { 86 | chk := p.Calc(newHash, leaf) 87 | return subtle.ConstantTimeCompare(chk, root) != 0 88 | } 89 | 90 | //type MerkleLog struct { 91 | //} 92 | -------------------------------------------------------------------------------- /coco/test/logutils/logutils.go: -------------------------------------------------------------------------------- 1 | package logutils 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "math/rand" 8 | "runtime" 9 | "strconv" 10 | "time" 11 | 12 | "github.com/Sirupsen/logrus" 13 | "golang.org/x/net/websocket" 14 | ) 15 | 16 | func init() { 17 | rand.Seed(time.Now().UnixNano()) 18 | } 19 | 20 | type LoggerHook struct { 21 | HostPort string 22 | Conn *websocket.Conn 23 | f logrus.Formatter 24 | } 25 | 26 | func File() string { 27 | _, file, line, ok := runtime.Caller(1) 28 | if !ok { 29 | file = "???" 30 | line = 0 31 | } 32 | short := file 33 | for i := len(file) - 1; i > 0; i-- { 34 | if file[i] == '/' { 35 | short = file[i+1:] 36 | break 37 | } 38 | } 39 | file = short 40 | return file + ":" + strconv.Itoa(line) 41 | } 42 | 43 | func (lh *LoggerHook) Connect() { 44 | hostport := lh.HostPort 45 | retry: 46 | addr := "ws://" + hostport + "/_log" 47 | ws, err := websocket.Dial(addr, "", "http://localhost/") 48 | if err != nil { 49 | log.Println("failed to connect to logger:", addr) 50 | time.Sleep(time.Second) 51 | goto retry 52 | } 53 | lh.Conn = ws 54 | } 55 | 56 | // host is my host: what machine I am running on 57 | // hostport is the address of the logging server 58 | // host 59 | func NewLoggerHook(hostport, host, app string) (*LoggerHook, error) { 60 | retry: 61 | addr := "ws://" + hostport + "/_log" 62 | ws, err := websocket.Dial(addr, "", "http://localhost/") 63 | if err != nil { 64 | time.Sleep(time.Second) 65 | goto retry 66 | } 67 | return &LoggerHook{hostport, ws, &JSONFormatter{host, app}}, err 68 | } 69 | 70 | // Fire is called when a log event is fired. 71 | func (hook *LoggerHook) Fire(entry *logrus.Entry) error { 72 | serialized, err := hook.f.Format(entry) 73 | if err != nil { 74 | return fmt.Errorf("Failed to fields to format, %v", err) 75 | } 76 | _, err = hook.Conn.Write(serialized) 77 | if err != nil { 78 | return err 79 | } 80 | 81 | return nil 82 | } 83 | 84 | // Levels returns the available logging levels. 85 | func (hook *LoggerHook) Levels() []logrus.Level { 86 | return []logrus.Level{ 87 | logrus.PanicLevel, 88 | logrus.FatalLevel, 89 | logrus.ErrorLevel, 90 | logrus.WarnLevel, 91 | logrus.InfoLevel, 92 | logrus.DebugLevel, 93 | } 94 | } 95 | 96 | func (hook *LoggerHook) Close() { 97 | hook.Conn.Close() 98 | } 99 | 100 | type JSONFormatter struct { 101 | Host string 102 | App string 103 | } 104 | 105 | func (f *JSONFormatter) Format(entry *logrus.Entry) ([]byte, error) { 106 | data := make(logrus.Fields, len(entry.Data)+5) 107 | for k, v := range entry.Data { 108 | data[k] = v 109 | } 110 | data["ehost"] = f.Host // the host that this is running on 111 | data["eapp"] = f.App // what app we are running (timeclient, timestamper, signer) 112 | data["etime"] = entry.Time.Format(time.RFC3339) 113 | data["emsg"] = entry.Message 114 | data["elevel"] = entry.Level.String() 115 | 116 | serialized, err := json.Marshal(data) 117 | if err != nil { 118 | return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) 119 | } 120 | return append(serialized, '\n'), nil 121 | } 122 | -------------------------------------------------------------------------------- /connMan/chanConnManager.go: -------------------------------------------------------------------------------- 1 | package connMan 2 | 3 | import ( 4 | "github.com/dedis/crypto/abstract" 5 | "github.com/dedis/prifi/coconet" 6 | ) 7 | 8 | /* This class serves as the connection manager for the GoConn connection 9 | * type. It functions as a wrapper around goConn keeping track of which public 10 | * keys map to which connections. It also keeps track of the GoDirectory for 11 | * testing purposes. 12 | */ 13 | type ChanConnManager struct { 14 | // Tracks the connections to various peers 15 | peerMap map[string]*coconet.GoConn 16 | // This directory facilitates using go channels for testing purposes. 17 | dir *coconet.GoDirectory 18 | // The public key of the server that owns this manager. 19 | pubKey abstract.Point 20 | } 21 | 22 | // TODO: Prototypes to consider for the future 23 | // 24 | //type ConnManager struct { 25 | // peerMap map[string]coconet.Conn 26 | //} 27 | 28 | //func (cm *ConnManager) AddConn(top abstract.Point, conn coconet.Conn) { 29 | // to := top.String() 30 | // peerMap[to] = conn 31 | //} 32 | 33 | /* Initializes a new ChanConnManager 34 | * 35 | * Arguments: 36 | * goDir = the GoDirectory to use for creating new connections. Enter nil 37 | * to create a new one. 38 | * key = the public key of the owner of this manager 39 | * 40 | * Returns: 41 | * An initialized ChanConnManager 42 | */ 43 | func (gcm *ChanConnManager) Init(key abstract.Point, goDir *coconet.GoDirectory) *ChanConnManager { 44 | gcm.pubKey = key 45 | gcm.peerMap = make(map[string]*coconet.GoConn) 46 | if goDir == nil { 47 | gcm.dir = coconet.NewGoDirectory() 48 | } else { 49 | gcm.dir = goDir 50 | } 51 | return gcm 52 | } 53 | 54 | /* Adds a new connection to the connection manager 55 | * 56 | * Arguments: 57 | * theirkey = the key of the peer that this server wishes to connect to 58 | * 59 | * Returns: 60 | * An error denoting whether creating the new connection was successful. 61 | */ 62 | func (gcm *ChanConnManager) AddConn(theirKey abstract.Point) error { 63 | newConn, err := coconet.NewGoConn(gcm.dir, gcm.pubKey.String(), 64 | theirKey.String()) 65 | if err != nil { 66 | return err 67 | } 68 | gcm.peerMap[theirKey.String()] = newConn 69 | return nil 70 | } 71 | 72 | // Returns the GoDirectory of the manager. 73 | func (gcm *ChanConnManager) GetDir() *coconet.GoDirectory { 74 | return gcm.dir 75 | } 76 | 77 | /* Put a message to a given peer. 78 | * 79 | * Arguments: 80 | * p = the public key of the destination 81 | * data = the message to send 82 | * 83 | * Returns: 84 | * An error denoting whether the put was successfull 85 | */ 86 | func (gcm *ChanConnManager) Put(p abstract.Point, data coconet.BinaryMarshaler) error { 87 | return gcm.peerMap[p.String()].Put(data) 88 | } 89 | 90 | /* Get a message from a given peer. 91 | * 92 | * Arguments: 93 | * p = the public key of the origin 94 | * bum = a buffer for receiving the message 95 | * 96 | * Returns: 97 | * An error denoting whether the get to the buffer was successfull 98 | */ 99 | func (gcm ChanConnManager) Get(p abstract.Point, bum coconet.BinaryUnmarshaler) error { 100 | return gcm.peerMap[p.String()].Get(bum) 101 | } 102 | -------------------------------------------------------------------------------- /coco/test/oldconfig/graphs.go: -------------------------------------------------------------------------------- 1 | package oldconfig 2 | 3 | import ( 4 | "bufio" 5 | "container/list" 6 | "crypto/cipher" 7 | "errors" 8 | "fmt" 9 | "os" 10 | 11 | "github.com/dedis/crypto/abstract" 12 | "github.com/dedis/prifi/coco/coconet" 13 | "github.com/dedis/prifi/coco/sign" 14 | ) 15 | 16 | // var testSuite = openssl.NewAES128SHA256P256() 17 | // var testRand = random.Stream 18 | 19 | // dijkstra is actually implemented as BFS right now because it is equivalent 20 | // when edge weights are all 1. 21 | func dijkstra(m map[string]*sign.Node, root *sign.Node) { 22 | l := list.New() 23 | visited := make(map[string]bool) 24 | l.PushFront(root) 25 | visited[root.Name()] = true 26 | for e := l.Front(); e != nil; e = l.Front() { 27 | l.Remove(e) 28 | sn := e.Value.(*sign.Node) 29 | // make all unvisited peers children 30 | // and mark them as visited 31 | for name, conn := range sn.Peers() { 32 | // visited means it is already on the tree. 33 | if visited[name] { 34 | continue 35 | } 36 | visited[name] = true 37 | // add the associated peer/connection as a child 38 | sn.AddChildren(0, conn.Name()) 39 | cn, ok := m[name] 40 | if !ok { 41 | panic("error getting connection from map") 42 | } 43 | peers := cn.Peers() 44 | pconn, ok := peers[sn.Name()] 45 | if !ok { 46 | panic("parent connection doesn't exist: not bi-directional") 47 | } 48 | cn.AddParent(0, pconn.Name()) 49 | l.PushFront(cn) 50 | } 51 | } 52 | } 53 | 54 | func loadHost(hostname string, m map[string]*sign.Node, testSuite abstract.Suite, testRand cipher.Stream, hc *HostConfig) *sign.Node { 55 | if h, ok := m[hostname]; ok { 56 | return h 57 | } 58 | host := coconet.NewGoHost(hostname, coconet.NewGoDirectory()) 59 | h := sign.NewNode(host, testSuite, testRand) 60 | hc.Hosts[hostname] = h 61 | m[hostname] = h 62 | return h 63 | } 64 | 65 | // loadGraph reads in an edge list data file of the form. 66 | // from1 to1 67 | // from1 to2 68 | // from2 to2 69 | // ... 70 | func loadGraph(name string, testSuite abstract.Suite, testRand cipher.Stream) (*HostConfig, error) { 71 | f, err := os.Open(name) 72 | if err != nil { 73 | return nil, err 74 | } 75 | s := bufio.NewScanner(f) 76 | // generate the list of hosts 77 | hosts := make(map[string]*sign.Node) 78 | hc := NewHostConfig() 79 | var root *sign.Node 80 | for s.Scan() { 81 | var host1, host2 string 82 | n, err := fmt.Sscan(s.Text(), &host1, &host2) 83 | if err != nil { 84 | return nil, err 85 | } 86 | if n != 2 { 87 | return nil, errors.New("improperly formatted file") 88 | } 89 | h1 := loadHost(host1, hosts, testSuite, testRand, hc) 90 | h2 := loadHost(host2, hosts, testSuite, testRand, hc) 91 | h1.AddPeer(h2.Name(), h2.PubKey) 92 | h2.AddPeer(h1.Name(), h1.PubKey) 93 | if root == nil { 94 | root = h1 95 | } 96 | hc.SNodes = append(hc.SNodes, h1, h2) 97 | } 98 | dijkstra(hosts, root) 99 | for _, sn := range hc.SNodes { 100 | go func(sn *sign.Node) { 101 | // start listening for messages from within the tree 102 | sn.Listen() 103 | }(sn) 104 | } 105 | jhc, err := LoadJSON([]byte(hc.String())) 106 | return jhc, err 107 | } 108 | -------------------------------------------------------------------------------- /python/certify/encrypted_exchange.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import random 3 | 4 | from Crypto.Cipher import AES 5 | from Crypto.Hash import SHA256 6 | from Crypto.Util import Counter 7 | from Crypto.Util.number import long_to_bytes, bytes_to_long 8 | 9 | import verdict 10 | 11 | class EncryptedBase: 12 | def __init__(self): 13 | h = SHA256.new() 14 | self.counter = 0 15 | self.cleartext_to_generator([[]]) 16 | 17 | def cleartext_to_generator(self, cleartext): 18 | h = SHA256.new() 19 | 20 | h.update(long_to_bytes(self.counter)) 21 | for layer1 in cleartext: 22 | for layer2 in layer1: 23 | h.update(layer2) 24 | 25 | self.hdata = bytes_to_long(h.digest()) 26 | self.counter += 1 27 | 28 | class EncryptedAccumulator(EncryptedBase): 29 | def __init__(self, verifier): 30 | EncryptedBase.__init__(self) 31 | self.verifier = verifier 32 | 33 | def before(self, ciphertexts): 34 | generator = self.verifier.group.multiply( \ 35 | self.verifier.group.generator(), \ 36 | self.hdata % self.verifier.group.order()) 37 | 38 | base = self.verifier.generate_ciphertext(generator) 39 | 40 | nciphertexts = [ciphertext[0] for ciphertext in ciphertexts] 41 | for sdx in range(len(nciphertexts)): 42 | shared = base 43 | for idx in range(len(ciphertexts)): 44 | if idx == sdx: 45 | continue 46 | shared = self.verifier.group.add(shared, ciphertexts[idx][1][0]) 47 | pseed = self.verifier.group.add(shared, ciphertexts[sdx][1][1]) 48 | seed = self.verifier.group.decode(pseed) 49 | aes = AES.new(seed, AES.MODE_CTR, counter = Counter.new(128)) 50 | for ndx in range(len(nciphertexts[sdx])): 51 | for cdx in range(len(nciphertexts[sdx][ndx])): 52 | nciphertexts[sdx][ndx][cdx] = aes.decrypt(nciphertexts[sdx][ndx][cdx]) 53 | 54 | return nciphertexts 55 | 56 | def after(self, cleartexts): 57 | self.cleartext_to_generator(cleartexts) 58 | return cleartexts 59 | 60 | class EncryptedCertifier(EncryptedBase): 61 | def __init__(self, verifier): 62 | EncryptedBase.__init__(self) 63 | self.verifier = verifier 64 | self.seed_min = 2**121 65 | self.seed_max = 2**128 66 | 67 | def certify(self, ciphertexts): 68 | generator = self.verifier.group.multiply( \ 69 | self.verifier.group.generator(), \ 70 | self.hdata % self.verifier.group.order()) 71 | 72 | other = self.verifier.generate_ciphertext(generator) 73 | seed = random.randrange(self.seed_min, self.seed_max) 74 | own = self.verifier.generate_ciphertext(generator, seed) 75 | 76 | aes = AES.new(long_to_bytes(seed), AES.MODE_CTR, counter = Counter.new(128)) 77 | for ndx in range(len(ciphertexts)): 78 | for cdx in range(len(ciphertexts[ndx])): 79 | ciphertexts[ndx][cdx] = aes.encrypt(ciphertexts[ndx][cdx]) 80 | return (ciphertexts, (other, own)) 81 | 82 | def verify(self, cleartexts): 83 | self.cleartext_to_generator(cleartexts) 84 | return cleartexts 85 | -------------------------------------------------------------------------------- /coco/test/latency_test/latency.go: -------------------------------------------------------------------------------- 1 | // pings servers in the hostfile that are not this hostname 2 | // 3 | // host peer1 avgtime1 4 | // host peer2 avgtime2 5 | // ... 6 | 7 | package main 8 | 9 | import ( 10 | "bytes" 11 | "flag" 12 | "fmt" 13 | "io/ioutil" 14 | "log" 15 | "net" 16 | "os" 17 | "os/exec" 18 | "regexp" 19 | "strconv" 20 | "sync" 21 | ) 22 | 23 | var fname string 24 | var hostname string 25 | var threshold int 26 | var pingthresh int 27 | 28 | func init() { 29 | log.SetFlags(log.Lshortfile) 30 | log.SetOutput(os.Stderr) 31 | flag.StringVar(&hostname, "hostname", "", "the hostname of this machine") 32 | flag.StringVar(&fname, "hostfile", "hosts.txt", "a file of hostnames. one per line.") 33 | flag.IntVar(&pingthresh, "pingthresh", 100, "the maximum avg ping time allowed") 34 | flag.IntVar(&threshold, "threshold", 90, "the threshold") 35 | } 36 | 37 | var pingLoss *regexp.Regexp = regexp.MustCompile(`[0-9]+ packets transmitted, [0-9]+ received, ([0-9]*\.?[0-9]*)% packet loss`) 38 | var pingStats *regexp.Regexp = regexp.MustCompile(`min/avg/max/mdev = ([0-9]*\.?[0-9]*)/([0-9]*\.?[0-9]*)/([0-9]*\.?[0-9]*)/([0-9]*\.?[0-9]*) ms`) 39 | 40 | func main() { 41 | flag.Parse() 42 | bs, err := ioutil.ReadFile(fname) 43 | if err != nil { 44 | log.Fatal("FAILED TO READ FILE!") 45 | os.Exit(1) 46 | } 47 | lines := bytes.Split(bs, []byte{'\n'}) 48 | var wg sync.WaitGroup 49 | for _, bhostport := range lines { 50 | wg.Add(1) 51 | go func(bhostport []byte) { 52 | defer wg.Done() 53 | hostport := string(bhostport) 54 | if hostport == hostname { 55 | return 56 | } 57 | 58 | host, _, err := net.SplitHostPort(hostport) 59 | 60 | if err != nil { 61 | // if there was an error assume that it is that a hostname was given rather than host:port 62 | host = hostport 63 | } 64 | 65 | if host == hostname { 66 | return 67 | } 68 | 69 | ping := exec.Command("ping", host, "-c", "20") 70 | ping.Env = os.Environ() 71 | output, err := ping.CombinedOutput() 72 | if err != nil { 73 | log.Println("error pinging: ", hostname, host, string(output), err) 74 | return 75 | } 76 | //output := string(boutput) 77 | //log.Println(string(output)) 78 | smatch := pingLoss.FindSubmatch(output) 79 | if smatch == nil { 80 | log.Println("no ping loss") 81 | return 82 | } 83 | 84 | success, err := strconv.ParseFloat(string(smatch[1]), 64) 85 | if err != nil { 86 | log.Println("smatch err:", err, smatch[1], smatch[0]) 87 | return 88 | } 89 | 90 | if success > 100-float64(threshold) { 91 | log.Println("below thresh") 92 | // if a host has less than a 90% success rate don't use it 93 | return 94 | } 95 | 96 | matches := pingStats.FindSubmatch(output) 97 | if matches == nil { 98 | log.Println("FAILED:", host) 99 | return 100 | } 101 | //log.Println(matches) 102 | //log.Println(string(matches[2])) 103 | //min, _ := strconv.Atoi(string(matches[1])) 104 | avg, _ := strconv.ParseFloat(string(matches[2]), 64) 105 | //max, _ := strconv.Atoi(string(matches[3])) 106 | //stddev, _ := strconv.Atoi(string(matches[4])) 107 | if avg > float64(pingthresh) { 108 | log.Println("FAILED:", host) 109 | return 110 | } 111 | log.Println(hostname, host, avg) 112 | fmt.Println(hostname, host, avg) 113 | 114 | }(bhostport) 115 | } 116 | wg.Wait() 117 | } 118 | -------------------------------------------------------------------------------- /coco/sign/snviewchange.go: -------------------------------------------------------------------------------- 1 | package sign 2 | 3 | import "log" 4 | 5 | func (sn *Node) ChangeView(vcv *ViewChangeVote) { 6 | // log.Println(sn.Name(), " in CHANGE VIEW") 7 | // at this point actions have already been applied 8 | // all we need to do is switch our default view 9 | sn.viewmu.Lock() 10 | sn.ViewNo = vcv.View 11 | sn.viewmu.Unlock() 12 | if sn.RootFor(vcv.View) == sn.Name() { 13 | log.Println(sn.Name(), "CHANGE VIEW TO ROOT", "children", sn.Children(vcv.View)) 14 | sn.viewChangeCh <- "root" 15 | } else { 16 | log.Println(sn.Name(), "CHANGE VIEW TO REGULAR") 17 | sn.viewChangeCh <- "regular" 18 | } 19 | 20 | sn.viewmu.Lock() 21 | sn.ChangingView = false 22 | sn.viewmu.Unlock() 23 | log.Println("VIEW CHANGED") 24 | // TODO: garbage collect old connections 25 | } 26 | 27 | /* 28 | func (sn *Node) ViewChange(view int, parent string, vcm *ViewChangeMessage) error { 29 | sn.ChangingView = true 30 | 31 | log.Println(sn.Name(), "VIEW CHANGE MESSAGE: new Round == , oldlsr == , view == ", vcm.Round, sn.LastSeenRound, view) 32 | sn.LastSeenRound = max(vcm.Round, sn.LastSeenRound) 33 | 34 | iAmNextRoot := false 35 | if sn.RootFor(vcm.ViewNo) == sn.Name() { 36 | iAmNextRoot = true 37 | } 38 | 39 | sn.Views().Lock() 40 | _, exists := sn.Views().Views[vcm.ViewNo] 41 | sn.Views().Unlock() 42 | if !exists { 43 | log.Println("PEERS:", sn.Peers()) 44 | children := sn.childrenForNewView(parent) 45 | log.Println("CREATING NEW VIEW with ", len(sn.HostListOn(view-1)), "hosts", "on view", view) 46 | sn.NewView(vcm.ViewNo, parent, children, sn.HostListOn(view-1)) 47 | } 48 | 49 | log.Println(sn.Name(), ":multiplexing onto children:", sn.Children(view)) 50 | sn.multiplexOnChildren(vcm.ViewNo, &SigningMessage{View: view, Type: ViewChange, Vcm: vcm}) 51 | 52 | log.Println(sn.Name(), "waiting on view accept messages from children:", sn.Children(view)) 53 | 54 | votes := len(sn.Children(view)) 55 | 56 | log.Println(sn.Name(), "received view accept messages from children:", votes) 57 | 58 | var err error 59 | if iAmNextRoot { 60 | 61 | if votes > len(sn.HostListOn(view))*2/3 { 62 | 63 | log.Println(sn.Name(), "quorum", votes, "of", len(sn.HostListOn(view)), "confirmed me as new root") 64 | vcfm := &ViewConfirmedMessage{ViewNo: vcm.ViewNo} 65 | sm := &SigningMessage{Type: ViewConfirmed, Vcfm: vcfm, From: sn.Name(), View: vcm.ViewNo} 66 | sn.multiplexOnChildren(vcm.ViewNo, sm) 67 | 68 | sn.ChangingView = false 69 | sn.ViewNo = vcm.ViewNo 70 | sn.viewChangeCh <- "root" 71 | } else { 72 | log.Errorln(sn.Name(), " (ROOT) DID NOT RECEIVE quorum", votes, "of", len(sn.HostList)) 73 | return ErrViewRejected 74 | } 75 | } else { 76 | sn.RoundsAsRoot = 0 77 | 78 | vam := &ViewAcceptedMessage{ViewNo: vcm.ViewNo, Votes: votes} 79 | 80 | log.Println(sn.Name(), "putting up on view", view, "accept for view", vcm.ViewNo) 81 | err = sn.PutUp(context.TODO(), vcm.ViewNo, &SigningMessage{ 82 | View: view, 83 | From: sn.Name(), 84 | Type: ViewAccepted, 85 | Vam: vam}) 86 | 87 | return err 88 | } 89 | 90 | return err 91 | } 92 | 93 | func (sn *Node) ViewChanged(view int, sm *SigningMessage) { 94 | log.Println(sn.Name(), "view CHANGED to", view) 95 | 96 | sn.ChangingView = false 97 | 98 | sn.viewChangeCh <- "regular" 99 | 100 | log.Println("in view change, children for view", view, sn.Children(view)) 101 | sn.multiplexOnChildren(view, sm) 102 | log.Println(sn.Name(), " exited view CHANGE to", view) 103 | } 104 | */ 105 | -------------------------------------------------------------------------------- /coco/test/logserver/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Dissent: Coco: Logging Server 6 | 7 | 8 | 13 | 14 | 15 | 16 |
Number of Hosts: {{.Hosts}}
17 |
Depth: {{.Depth}}
18 |
Branching Factor: {{.BranchingFactor}}
19 |
Hosts Per Node: {{.HostsPerNode}}
20 |
Message Rate: {{.Rate}}
21 |
Minimum Time: s
22 |
Maximum Time: s
23 |
Average Time: s
24 |
Standard Deviation: s
25 |
26 | 27 |
28 |
29 | 30 | 106 | 107 | -------------------------------------------------------------------------------- /time/NOTES: -------------------------------------------------------------------------------- 1 | Time-Server and Merkletime reasoning support 2 | 3 | Functionality: 4 | - Timestamping requests: submit appropriate hash; 5 | at next time-step signed log-entry and Merkleproof of beforeness 6 | - Proof of beforeness, betweenness 7 | 8 | With sorted ledgers: 9 | - Proof of nothing-betweenness (proof of absence on log between two time-steps) 10 | (or maybe not necessary, if each ledger entry has a validated chain?) 11 | 12 | --- 13 | 14 | Import question, possibly for configuration option: 15 | does time server remember entire history of its log, or only to a point? 16 | For another node that watches it "constantly enough", 17 | e.g., updates at least once within timeserver's log-retention window, 18 | the node can ensure that it can always build a proof-chain 19 | from the timerserver's latest entry to a given value the node timestamped. 20 | However, if the node stops watching the server's log-head 21 | for longer than the log-retention window, 22 | then it's possible that the log-server (and no one else) 23 | will still remember the Merkle path links from the current log-head 24 | to some historical value the node timestamped, 25 | even though that path is guaranteed to exist and to be "short". 26 | (But a large number of such paths can build up over time, 27 | hence the potential need for log-limiting.) 28 | 29 | This property is probably fine, 30 | since the main purpose is for the node to be able to prove 31 | that its timestamped value happened before 32 | a given timestamp server's signed log-entry with a given sequence number, 33 | and everyone who trusts the timestamp server 34 | can simply compare log-entry sequence numbers 35 | without reconstructing the entire chain between two log-entries. 36 | 37 | --- 38 | 39 | Proving time-position: e.g., that a node's value was committed 40 | "between" time-server log entries A and B: 41 | We can in principle create two such types of proof, 42 | one purely cryptographic, the other based on trust in the timeserver. 43 | 44 | The purely cryptographic approach is to produce two Merkle paths: 45 | one from the earlier time-server entry A to a root whose hash is 46 | contained somewhere (anywhere) in the committed value; 47 | and a second Merkle path from the committed value 48 | to the later time-server log-entry B. 49 | Any node that tracks the time-server's log "continuously enough", 50 | leaving no gaps longer than the server's retention window, 51 | during the entire period from time A to time B, 52 | will be able to produce a compact cryptographic proof 53 | that the committed value was chosen between times A and B. 54 | Constructing such a proof is not guaranteed to be possible, however, 55 | if the node loses track of the time server's log for "too long". 56 | 57 | The attestation-based approach is simply to rely on 58 | the timeserver's attestation: 59 | e.g., just trust that the time server operates correctly, 60 | in particular that it always signs log-entries in sequence order 61 | and never produces two correctly-signed log entries 62 | with the same sequence number. 63 | If we trust the timeserver in this way, 64 | then the node who committed the value in question 65 | need not have tracked the log continuously between A 66 | and some much-later time C. 67 | The node gets a compact proof that the committed value 68 | happened after time A simply by embedding a suitable Merkle proof 69 | in the content of the value it commits; 70 | the node gets back a timestamp B 71 | proving that B happened after the committed value; 72 | and anyone can verify that C happened after B 73 | simply by checking their signatures and checking that C.Seq >= B.Seq. 74 | 75 | --- 76 | 77 | Good security/crypto discussion of the NIST randomness beacon: 78 | http://crypto.stackexchange.com/questions/15225/how-useful-is-nists-randomness-beacon-for-cryptographic-use 79 | 80 | -------------------------------------------------------------------------------- /coco/test/data/exwax.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosts": [ 3 | "0", 4 | "32", 5 | "0", 6 | "26", 7 | "0", 8 | "14", 9 | "0", 10 | "31", 11 | "1", 12 | "16", 13 | "1", 14 | "9", 15 | "1", 16 | "6", 17 | "1", 18 | "14", 19 | "1", 20 | "30", 21 | "2", 22 | "48", 23 | "3", 24 | "46", 25 | "3", 26 | "15", 27 | "4", 28 | "42", 29 | "5", 30 | "33", 31 | "5", 32 | "36", 33 | "5", 34 | "21", 35 | "6", 36 | "16", 37 | "7", 38 | "16", 39 | "7", 40 | "35", 41 | "7", 42 | "20", 43 | "9", 44 | "49", 45 | "9", 46 | "12", 47 | "10", 48 | "34", 49 | "11", 50 | "25", 51 | "13", 52 | "33", 53 | "13", 54 | "39", 55 | "14", 56 | "16", 57 | "14", 58 | "30", 59 | "15", 60 | "44", 61 | "17", 62 | "40", 63 | "17", 64 | "29", 65 | "17", 66 | "31", 67 | "20", 68 | "35", 69 | "21", 70 | "41", 71 | "21", 72 | "26", 73 | "25", 74 | "42", 75 | "27", 76 | "48", 77 | "31", 78 | "43", 79 | "38", 80 | "41", 81 | "41", 82 | "49" 83 | ], 84 | "tree": { 85 | "name": "0", 86 | "children": [ 87 | { 88 | "name": "26", 89 | "children": [] 90 | }, 91 | { 92 | "name": "14", 93 | "children": [ 94 | { 95 | "name": "1", 96 | "children": [ 97 | { 98 | "name": "9", 99 | "children": [ 100 | { 101 | "name": "49", 102 | "children": [ 103 | { 104 | "name": "41", 105 | "children": [ 106 | { 107 | "name": "21", 108 | "children": [ 109 | { 110 | "name": "5", 111 | "children": [ 112 | { 113 | "name": "36", 114 | "children": [] 115 | }, 116 | { 117 | "name": "33", 118 | "children": [ 119 | { 120 | "name": "13", 121 | "children": [ 122 | { 123 | "name": "39", 124 | "children": [] 125 | } 126 | ] 127 | } 128 | ] 129 | } 130 | ] 131 | } 132 | ] 133 | }, 134 | { 135 | "name": "38", 136 | "children": [] 137 | } 138 | ] 139 | } 140 | ] 141 | }, 142 | { 143 | "name": "12", 144 | "children": [] 145 | } 146 | ] 147 | } 148 | ] 149 | }, 150 | { 151 | "name": "16", 152 | "children": [ 153 | { 154 | "name": "6", 155 | "children": [] 156 | }, 157 | { 158 | "name": "7", 159 | "children": [ 160 | { 161 | "name": "35", 162 | "children": [] 163 | }, 164 | { 165 | "name": "20", 166 | "children": [] 167 | } 168 | ] 169 | } 170 | ] 171 | }, 172 | { 173 | "name": "30", 174 | "children": [] 175 | } 176 | ] 177 | }, 178 | { 179 | "name": "31", 180 | "children": [ 181 | { 182 | "name": "17", 183 | "children": [ 184 | { 185 | "name": "40", 186 | "children": [] 187 | }, 188 | { 189 | "name": "29", 190 | "children": [] 191 | } 192 | ] 193 | }, 194 | { 195 | "name": "43", 196 | "children": [] 197 | } 198 | ] 199 | }, 200 | { 201 | "name": "32", 202 | "children": [] 203 | } 204 | ] 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /coco/test/forkexec/forkexec.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "os/exec" 6 | "strconv" 7 | 8 | log "github.com/Sirupsen/logrus" 9 | "github.com/dedis/prifi/coco/test/logutils" 10 | ) 11 | 12 | // Wrapper around exec.go to enable measuring of cpu time 13 | 14 | var hostname string 15 | var configFile string 16 | var logger string 17 | var app string 18 | var pprofaddr string 19 | var physaddr string 20 | var rootwait int 21 | var debug bool 22 | var failures int 23 | var rFail int 24 | var fFail int 25 | var rounds int 26 | var amroot bool 27 | var testConnect bool 28 | var suite string 29 | 30 | // TODO: add debug flag for more debugging information (memprofilerate...) 31 | func init() { 32 | flag.StringVar(&hostname, "hostname", "", "the hostname of this node") 33 | flag.StringVar(&configFile, "config", "cfg.json", "the json configuration file") 34 | flag.StringVar(&logger, "logger", "", "remote logger") 35 | flag.StringVar(&app, "app", "stamp", "application to run [sign|stamp|vote]") 36 | flag.IntVar(&rounds, "rounds", 100, "number of rounds to run") 37 | flag.StringVar(&pprofaddr, "pprof", ":10000", "the address to run the pprof server at") 38 | flag.StringVar(&physaddr, "physaddr", "", "the physical address of the noded [for deterlab]") 39 | flag.IntVar(&rootwait, "rootwait", 30, "the amount of time the root should wait") 40 | flag.BoolVar(&debug, "debug", false, "set debugging") 41 | flag.IntVar(&failures, "failures", 0, "percent showing per node probability of failure") 42 | flag.IntVar(&rFail, "rfail", 0, "number of consecutive rounds each root runs before it fails") 43 | flag.IntVar(&fFail, "ffail", 0, "number of consecutive rounds each follower runs before it fails") 44 | flag.BoolVar(&amroot, "amroot", false, "am I root") 45 | flag.BoolVar(&testConnect, "test_connect", false, "test connecting and disconnecting") 46 | flag.StringVar(&suite, "suite", "nist256", "abstract suite to use [nist256, nist512, ed25519]") 47 | } 48 | 49 | func main() { 50 | flag.Parse() 51 | // connect with the logging server 52 | if logger != "" && (amroot || debug) { 53 | // blocks until we can connect to the logger 54 | lh, err := logutils.NewLoggerHook(logger, hostname, app) 55 | if err != nil { 56 | log.WithFields(log.Fields{ 57 | "file": logutils.File(), 58 | }).Fatalln("ERROR SETTING UP LOGGING SERVER:", err) 59 | } 60 | log.AddHook(lh) 61 | } 62 | //log.Println("IN FORKEXEC") 63 | ////log.SetOutput(ioutil.Discard) 64 | ////log.Println("Log Test") 65 | ////fmt.Println("exiting logger block") 66 | //} 67 | // log.Println("IN FORK EXEC") 68 | // recombine the flags for exec to use 69 | args := []string{ 70 | "-failures=" + strconv.Itoa(failures), 71 | "-rfail=" + strconv.Itoa(rFail), 72 | "-ffail=" + strconv.Itoa(fFail), 73 | "-hostname=" + hostname, 74 | "-config=" + configFile, 75 | "-logger=" + logger, 76 | "-app=" + app, 77 | "-pprof=" + pprofaddr, 78 | "-physaddr=" + physaddr, 79 | "-rootwait=" + strconv.Itoa(rootwait), 80 | "-debug=" + strconv.FormatBool(debug), 81 | "-rounds=" + strconv.Itoa(rounds), 82 | "-amroot=" + strconv.FormatBool(amroot), 83 | "-test_connect=" + strconv.FormatBool(testConnect), 84 | "-suite=" + suite, 85 | } 86 | cmd := exec.Command("./exec", args...) 87 | cmd.Stdout = log.StandardLogger().Writer() 88 | cmd.Stderr = log.StandardLogger().Writer() 89 | // log.Println("running command:", cmd) 90 | err := cmd.Run() 91 | if err != nil { 92 | log.Errorln("cmd run:", err) 93 | } 94 | 95 | // get CPU usage stats 96 | st := cmd.ProcessState.SystemTime() 97 | ut := cmd.ProcessState.UserTime() 98 | log.WithFields(log.Fields{ 99 | "file": logutils.File(), 100 | "type": "forkexec", 101 | "systime": st, 102 | "usertime": ut, 103 | }).Info("") 104 | 105 | } 106 | -------------------------------------------------------------------------------- /coco/sign/signingMessages_test.go: -------------------------------------------------------------------------------- 1 | package sign_test 2 | 3 | import ( 4 | "bytes" 5 | "reflect" 6 | "testing" 7 | 8 | "log" 9 | 10 | "github.com/dedis/crypto/nist" 11 | "github.com/dedis/prifi/coco/hashid" 12 | "github.com/dedis/prifi/coco/proof" 13 | "github.com/dedis/prifi/coco/sign" 14 | ) 15 | 16 | func init() { 17 | log.SetFlags(log.Lshortfile) 18 | } 19 | 20 | func TestErrorMessage(t *testing.T) { 21 | sm := &sign.SigningMessage{Type: sign.Error, Err: &sign.ErrorMessage{Err: "random error"}} 22 | b, e := sm.MarshalBinary() 23 | if e != nil { 24 | t.Fatal(e) 25 | } 26 | sm2 := &sign.SigningMessage{} 27 | e = sm2.UnmarshalBinary(b) 28 | if e != nil { 29 | t.Fatal(e) 30 | } 31 | if !reflect.DeepEqual(sm, sm2) { 32 | t.Fatal("sm != sm2: ", sm, sm2, sm.Am, sm2.Am) 33 | } 34 | } 35 | 36 | // test marshalling and unmarshalling for 37 | // the various types of signing messages 38 | 39 | func TestMUAnnouncement(t *testing.T) { 40 | logTest := []byte("Hello World") 41 | sm := &sign.SigningMessage{Type: sign.Announcement, Am: &sign.AnnouncementMessage{LogTest: logTest}} 42 | dataBytes, err := sm.MarshalBinary() 43 | if err != nil { 44 | t.Error("Marshaling didn't work") 45 | } 46 | 47 | sm2 := &sign.SigningMessage{} 48 | sm2.UnmarshalBinary(dataBytes) 49 | if err != nil { 50 | t.Error("Unmarshaling didn't work") 51 | } 52 | if !reflect.DeepEqual(sm, sm2) { 53 | t.Fatal("sm != sm2: ", sm, sm2, sm.Am, sm2.Am) 54 | } 55 | } 56 | 57 | // Test for Marshalling and Unmarshalling Challenge Messages 58 | // Important: when making empty HashIds len should be set to HASH_SIZE 59 | func TestMUChallenge(t *testing.T) { 60 | nHashIds := 3 61 | 62 | var err error 63 | suite := nist.NewAES128SHA256P256() 64 | rand := suite.Cipher([]byte("example")) 65 | 66 | cm := &sign.ChallengeMessage{} 67 | cm.C = suite.Secret().Pick(rand) 68 | cm.MTRoot = make([]byte, hashid.Size) 69 | cm.Proof = proof.Proof(make([]hashid.HashId, nHashIds)) 70 | for i := 0; i < nHashIds; i++ { 71 | cm.Proof[i] = make([]byte, hashid.Size) 72 | } 73 | sm := &sign.SigningMessage{Type: sign.Challenge, Chm: cm} 74 | smBytes, err := sm.MarshalBinary() 75 | if err != nil { 76 | t.Error(err) 77 | } 78 | 79 | messg := &sign.SigningMessage{} 80 | err = messg.UnmarshalBinary(smBytes) 81 | cm2 := messg.Chm 82 | 83 | // test for equality after marshal and unmarshal 84 | if !cm2.C.Equal(cm.C) || 85 | bytes.Compare(cm2.MTRoot, cm.MTRoot) != 0 || 86 | !byteArrayEqual(cm2.Proof, cm.Proof) { 87 | t.Error("challenge message MU failed") 88 | } 89 | } 90 | 91 | // Test for Marshalling and Unmarshalling Comit Messages 92 | // Important: when making empty HashIds len should be set to HASH_SIZE 93 | func TestMUCommit(t *testing.T) { 94 | var err error 95 | suite := nist.NewAES128SHA256P256() 96 | rand := suite.Cipher([]byte("exampfsdjkhujgkjsgfjgle")) 97 | rand2 := suite.Cipher([]byte("examplsfhsjedgjhsge2")) 98 | 99 | cm := &sign.CommitmentMessage{} 100 | cm.V, _ = suite.Point().Pick(nil, rand) 101 | cm.V_hat, _ = suite.Point().Pick(nil, rand2) 102 | 103 | cm.MTRoot = make([]byte, hashid.Size) 104 | sm := sign.SigningMessage{Type: sign.Commitment, Com: cm} 105 | smBytes, err := sm.MarshalBinary() 106 | if err != nil { 107 | t.Error(err) 108 | } 109 | 110 | messg := &sign.SigningMessage{} 111 | err = messg.UnmarshalBinary(smBytes) 112 | cm2 := messg.Com 113 | 114 | // test for equality after marshal and unmarshal 115 | if !cm2.V.Equal(cm.V) || 116 | !cm2.V_hat.Equal(cm.V_hat) || 117 | bytes.Compare(cm2.MTRoot, cm.MTRoot) != 0 { 118 | t.Error("commit message MU failed") 119 | } 120 | 121 | } 122 | 123 | func byteArrayEqual(a proof.Proof, b proof.Proof) bool { 124 | n := len(a) 125 | if n != len(b) { 126 | return false 127 | } 128 | 129 | for i := 0; i < n; i++ { 130 | if bytes.Compare(a[i], b[i]) != 0 { 131 | return false 132 | } 133 | } 134 | 135 | return true 136 | } 137 | -------------------------------------------------------------------------------- /python/dcnet_local.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import json 3 | import os 4 | import time 5 | 6 | import dcnet 7 | 8 | from cells.null import NullDecoder, NullEncoder 9 | from certify.null import NullAccumulator, NullCertifier 10 | from dh import PublicKey, PrivateKey 11 | 12 | def main(): 13 | t0 = time.time() 14 | 15 | p = argparse.ArgumentParser(description="Local, insecure DC-net test") 16 | p.add_argument("config_dir") 17 | opts = p.parse_args() 18 | 19 | # load the public system data 20 | with open(os.path.join(opts.config_dir, "system.json"), "r", encoding="utf-8") as fp: 21 | data = json.load(fp) 22 | clients = data["clients"] 23 | client_ids = [c["id"] for c in clients] 24 | client_keys = [PublicKey(c["key"]) for c in clients] 25 | trustees = data["servers"] 26 | trustee_ids = [t["id"] for t in trustees] 27 | trustee_keys = [PublicKey(t["key"]) for t in trustees] 28 | 29 | # and session data 30 | with open(os.path.join(opts.config_dir, "session.json"), "r", encoding="utf-8") as fp: 31 | data = json.load(fp) 32 | session_id = data["session-id"] 33 | nym_keys = [PublicKey(c["dhkey"]) for c in data["clients"]] 34 | 35 | # load the post-shuffle slots 36 | with open(os.path.join(opts.config_dir, "shuffle.json"), "r", encoding="utf-8") as fp: 37 | data = json.load(fp) 38 | slot_keys = [PublicKey(x) for x in data["slots"]] 39 | 40 | # start multiple clients in the same process 41 | # load private keys from individualfiles 42 | clients = [] 43 | for iden in client_ids: 44 | with open(os.path.join(opts.config_dir, "{}.json".format(iden)), "r", encoding="utf-8") as fp: 45 | data = json.load(fp) 46 | private_key = PrivateKey(x = data["private_key"]) 47 | with open(os.path.join(opts.config_dir, "{}-{}.json".format(iden, session_id)), "r", encoding="utf-8") as fp: 48 | data = json.load(fp) 49 | nym_private_key = PrivateKey(x = data["private_key"]) 50 | client = dcnet.Client(private_key, trustee_keys, NullCertifier(), NullEncoder()) 51 | client.add_own_nym(nym_private_key) 52 | client.add_nyms(slot_keys) 53 | clients.append(client) 54 | 55 | # same with trustees 56 | trustees = [] 57 | for iden in trustee_ids: 58 | with open(os.path.join(opts.config_dir, "{}.json".format(iden)), "r", encoding="utf-8") as fp: 59 | data = json.load(fp) 60 | private_key = PrivateKey(x = data["private_key"]) 61 | trustee = dcnet.Trustee(private_key, client_keys) 62 | trustee.add_nyms(slot_keys) 63 | trustees.append(trustee) 64 | 65 | # start a single relay 66 | relay = dcnet.Relay(len(trustees), NullAccumulator(), NullDecoder()) 67 | relay.add_nyms(len(clients)) 68 | relay.sync(None) 69 | 70 | trap_keys = [] 71 | for trustee in trustees: 72 | trustee.sync(None) 73 | trap_keys.append(trustee.trap_keys[-1].pubkey) 74 | 75 | for client in clients: 76 | client.sync(None, trap_keys) 77 | 78 | for idx in range(len(trustees)): 79 | trustee = trustees[idx] 80 | ciphertext = trustee.produce_interval_ciphertext() 81 | relay.store_trustee_ciphertext(idx, ciphertext) 82 | 83 | client_ciphertexts = [] 84 | for client in clients: 85 | client_ciphertexts.append(client.produce_ciphertexts()) 86 | print(relay.process_ciphertext(client_ciphertexts)) 87 | 88 | print(time.time() - t0) 89 | t0 = time.time() 90 | 91 | client_ciphertexts = [] 92 | for client in clients: 93 | client.send(client.own_nym_keys[0][1], bytes("Hello", "UTF-8")) 94 | client_ciphertexts.append(client.produce_ciphertexts()) 95 | print(relay.process_ciphertext(client_ciphertexts)) 96 | 97 | print(time.time() - t0) 98 | t0 = time.time() 99 | 100 | if __name__ == "__main__": 101 | main() 102 | -------------------------------------------------------------------------------- /coco/test/exec/exec.go: -------------------------------------------------------------------------------- 1 | // usage exec: 2 | // 3 | // exec -name "hostname" -config "cfg.json" 4 | // 5 | // -name indicates the name of the node in the cfg.json 6 | // 7 | // -config points to the file that holds the configuration. 8 | // This configuration must be in terms of the final hostnames. 9 | // 10 | // pprof runs on the physical address space [if there is a virtual and physical network layer] 11 | // and if one is specified. 12 | 13 | package main 14 | 15 | import ( 16 | "flag" 17 | "net" 18 | "net/http" 19 | _ "net/http/pprof" 20 | "strconv" 21 | 22 | _ "expvar" 23 | 24 | log "github.com/Sirupsen/logrus" 25 | 26 | "github.com/dedis/prifi/coco/test/exec/timestamper" 27 | "github.com/dedis/prifi/coco/test/logutils" 28 | ) 29 | 30 | var hostname string 31 | var cfg string 32 | var logger string 33 | var app string 34 | var rounds int 35 | var pprofaddr string 36 | var physaddr string 37 | var rootwait int 38 | var debug bool 39 | var failures int 40 | var rFail int 41 | var fFail int 42 | var amroot bool 43 | var testConnect bool 44 | var suite string 45 | 46 | // TODO: add debug flag for more debugging information (memprofilerate...) 47 | func init() { 48 | flag.StringVar(&hostname, "hostname", "", "the hostname of this node") 49 | flag.StringVar(&cfg, "config", "cfg.json", "the json configuration file") 50 | flag.StringVar(&logger, "logger", "", "remote logger") 51 | flag.StringVar(&app, "app", "stamp", "application to run [sign|time]") 52 | flag.IntVar(&rounds, "rounds", 100, "number of rounds to run") 53 | flag.StringVar(&pprofaddr, "pprof", ":10000", "the address to run the pprof server at") 54 | flag.StringVar(&physaddr, "physaddr", "", "the physical address of the noded [for deterlab]") 55 | flag.IntVar(&rootwait, "rootwait", 30, "the amount of time the root should wait") 56 | flag.BoolVar(&debug, "debug", false, "set debugging") 57 | flag.IntVar(&failures, "failures", 0, "percent showing per node probability of failure") 58 | flag.IntVar(&rFail, "rfail", 0, "number of consecutive rounds each root runs before it fails") 59 | flag.IntVar(&fFail, "ffail", 0, "number of consecutive rounds each follower runs before it fails") 60 | flag.BoolVar(&amroot, "amroot", false, "am I root node") 61 | flag.BoolVar(&testConnect, "test_connect", false, "test connecting and disconnecting") 62 | flag.StringVar(&suite, "suite", "nist256", "abstract suite to use [nist256, nist512, ed25519]") 63 | } 64 | 65 | func main() { 66 | flag.Parse() 67 | log.Println("Running Timestamper:", logger) 68 | defer func() { 69 | log.Errorln("TERMINATING HOST") 70 | }() 71 | 72 | // connect with the logging server 73 | if logger != "" && (amroot || debug) { 74 | // blocks until we can connect to the logger 75 | log.Println("Connecting to Logger") 76 | lh, err := logutils.NewLoggerHook(logger, hostname, app) 77 | log.Println("Connected to Logger") 78 | if err != nil { 79 | log.WithFields(log.Fields{ 80 | "file": logutils.File(), 81 | }).Fatalln("ERROR SETTING UP LOGGING SERVER:", err) 82 | } 83 | log.AddHook(lh) 84 | //log.SetOutput(ioutil.Discard) 85 | //fmt.Println("exiting logger block") 86 | } 87 | if physaddr == "" { 88 | h, _, err := net.SplitHostPort(hostname) 89 | if err != nil { 90 | log.Fatal("improperly formatted hostname") 91 | } 92 | physaddr = h 93 | } 94 | 95 | // run an http server to serve the cpu and memory profiles 96 | go func() { 97 | _, port, err := net.SplitHostPort(hostname) 98 | if err != nil { 99 | log.Fatal("improperly formatted hostname: should be host:port") 100 | } 101 | p, _ := strconv.Atoi(port) 102 | // uncomment if more fine grained memory debuggin is needed 103 | //runtime.MemProfileRate = 1 104 | log.Println(http.ListenAndServe(net.JoinHostPort(physaddr, strconv.Itoa(p+2)), nil)) 105 | }() 106 | 107 | // log.Println("!!!!!!!!!!!!!!!Running timestamp with rFail and fFail: ", rFail, fFail) 108 | timestamper.Run(hostname, cfg, app, rounds, rootwait, debug, testConnect, failures, rFail, fFail, logger, suite) 109 | } 110 | -------------------------------------------------------------------------------- /coco/test/graphs/graph_test.go: -------------------------------------------------------------------------------- 1 | package graphs 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "strconv" 7 | "strings" 8 | "testing" 9 | ) 10 | 11 | func TestTree(t *testing.T) { 12 | g := &Graph{Names: []string{"planetlab2.cs.unc.edu", "pl1.6test.edu.cn", "planetlab1.cs.du.edu", "planetlab02.cs.washington.edu", "planetlab-2.cse.ohio-state.edu", "planetlab2.cs.ubc.ca"}, mem: []float64{0, 213.949, 51.86, 76.716, 2754.531, 81.301, 214.143, 0, 169.744, 171.515, 557.526, 189.186, 51.601, 170.191, 0, 41.418, 2444.206, 31.475, 76.731, 171.43, 41.394, 0, 2470.722, 5.741, 349.881, 520.028, 374.362, 407.282, 0, 392.211, 81.381, 189.386, 31.582, 5.78, 141.273, 0}, Weights: [][]float64{[]float64{0, 213.949, 51.86, 76.716, 2754.531, 81.301}, []float64{214.143, 0, 169.744, 171.515, 557.526, 189.186}, []float64{51.601, 170.191, 0, 41.418, 2444.206, 31.475}, []float64{76.731, 171.43, 41.394, 0, 2470.722, 5.741}, []float64{349.881, 520.028, 374.362, 407.282, 0, 392.211}, []float64{81.381, 189.386, 31.582, 5.78, 141.273, 0}}} 13 | tree := g.Tree(2) 14 | log.Println(tree) 15 | } 16 | 17 | func TestTreeFromList(t *testing.T) { 18 | nodeNames := make([]string, 0) 19 | nodeNames = append(nodeNames, "machine0", "machine1", "machine2") 20 | hostsPerNode := 2 21 | bf := 2 22 | 23 | root, usedHosts, _, err := TreeFromList(nodeNames, hostsPerNode, bf) 24 | if err != nil { 25 | panic(err) 26 | } 27 | 28 | // JSON format 29 | // b, err := json.Marshal(root) 30 | // if err != nil { 31 | // t.Error(err) 32 | // } 33 | // fmt.Println(string(b)) 34 | 35 | // if len(usedHosts) != len(nodeNames)*hostsPerNode { 36 | // t.Error("Should have been able to use all hosts") 37 | // } 38 | fmt.Println("used hosts", usedHosts) 39 | root.TraverseTree(PrintTreeNode) 40 | 41 | // Output: 42 | // used hosts [machine0:32600 machine1:32600 machine1:32610 machine0:32610 machine2:32600 machine2:32610] 43 | // machine0:32600 44 | // machine1:32600 45 | // machine1:32610 46 | // machine1:32600 47 | // machine0:32610 48 | // machine2:32600 49 | // machine0:32610 50 | // machine2:32600 51 | // machine1:32610 52 | // machine2:32610 53 | // machine2:32610 54 | } 55 | 56 | // 1 57 | // / \ 58 | // 2 2 59 | // / \ / \ 60 | // 1 1 1 61 | // two 2s not used to avoid akward tree 62 | func TestTreeFromList2(t *testing.T) { 63 | // 2 machines with 4 hosts each and branching factor of 2 64 | // this means only 6 of the 8 hosts should be used, depth will be 2 65 | nodeNames := make([]string, 0) 66 | nodeNames = append(nodeNames, "machine0", "machine1") 67 | hostsPerNode := 4 68 | bf := 2 69 | 70 | root, usedHosts, _, err := TreeFromList(nodeNames, hostsPerNode, bf) 71 | if err != nil { 72 | panic(err) 73 | } 74 | 75 | if len(usedHosts) != 6 { 76 | t.Error("Should have been able to use only 6 hosts") 77 | } 78 | fmt.Println("used hosts", usedHosts) 79 | root.TraverseTree(PrintTreeNode) 80 | } 81 | 82 | func checkColoring(t *Tree) bool { 83 | p := strings.Split(t.Name, ":")[0] 84 | for _, c := range t.Children { 85 | h := strings.Split(c.Name, ":")[0] 86 | if h == p { 87 | return false 88 | } 89 | if !checkColoring(c) { 90 | return false 91 | } 92 | } 93 | return true 94 | } 95 | 96 | func TestTreeFromListColoring(t *testing.T) { 97 | nodes := make([]string, 0) 98 | for i := 0; i < 20; i++ { 99 | nodes = append(nodes, "host"+strconv.Itoa(i)) 100 | } 101 | for hpn := 1; hpn < 10; hpn++ { 102 | for bf := 1; bf <= hpn*len(nodes); bf++ { 103 | t.Log("generating tree:", hpn, bf) 104 | root, hosts, retDepth, err := TreeFromList(nodes, hpn, bf) 105 | if err != nil { 106 | panic(err) 107 | } 108 | if !checkColoring(root) { 109 | t.Fatal("failed to properly color:", nodes, hpn, bf) 110 | } 111 | t.Log("able to use:", len(hosts), " of: ", hpn*len(nodes)) 112 | 113 | depth := Depth(root) 114 | if depth != retDepth { 115 | panic("Returned tree depth != actual treedepth") 116 | } 117 | t.Log("depth:", depth) 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /tree/tree_test.go: -------------------------------------------------------------------------------- 1 | package tree 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "math" 7 | "math/rand" 8 | "os" 9 | "testing" 10 | 11 | "github.com/dedis/crypto/nist" 12 | "github.com/dedis/crypto/random" 13 | ) 14 | 15 | var testSuite = nist.NewAES128SHA256P256() 16 | var testRand = random.Stream 17 | 18 | /* 19 | func build(suite abstract.Suite, rand cipher.Stream, 20 | parent *node, depth, arity int) { 21 | 22 | for i := 0; i < arity; i++ { 23 | n := newNode(suite, rand, parent.pub) 24 | parent.addChild(n.pub) 25 | 26 | if depth > 0 { 27 | build(suite, rand, n, depth-1, arity) 28 | } 29 | } 30 | } 31 | */ 32 | 33 | func testLevels() (int, int) { 34 | n := 1000 35 | //l := suite.HashLen() 36 | //ids := make([]float64, n) 37 | var c1, c2 int 38 | b := float64(1.01) 39 | max := float64(0.0) 40 | maxlev := 0 41 | for i := 0; i < n; i++ { 42 | f := rand.Float64() 43 | if f > max { 44 | c1++ 45 | max = f 46 | } 47 | 48 | lev := 0 49 | for { 50 | if f < 0 || f > 1 { 51 | panic("XXX") 52 | } 53 | f := rand.Float64() * b 54 | if f >= 1.0 { 55 | break 56 | } 57 | lev++ 58 | } 59 | 60 | if lev >= maxlev { 61 | c2++ 62 | } 63 | if lev > maxlev { 64 | maxlev = lev 65 | } 66 | } 67 | println("expected", math.Log(float64(n)), "c1", c1, "c2", c2) 68 | return c1, c2 69 | } 70 | 71 | func loadHost(hostname string, m *map[string]*host) *host { 72 | if h := (*m)[hostname]; h != nil { 73 | return h 74 | } 75 | h := newHost(testSuite, testRand, hostname) 76 | (*m)[hostname] = h 77 | return h 78 | } 79 | 80 | // Form a shortest-path spanning tree over all hosts from the given root. 81 | func dijkstra(hosts map[string]*host, root *host) { 82 | 83 | rootid := string(root.id) 84 | q := IntQ{} 85 | idmap := make(map[string]*host) 86 | 87 | // Prepare treeNodes on all hosts participating in this tree 88 | for _, host := range hosts { 89 | idmap[string(host.id)] = host 90 | 91 | tn := &treeNode{} 92 | host.trees[rootid] = tn 93 | 94 | tn.dist = math.MaxInt32 95 | if host == root { 96 | tn.dist = 0 97 | tn.path = []HashId{root.id} 98 | } 99 | q.Push(tn.dist, host) 100 | } 101 | 102 | //println("qlen",q.Len(),"hosts",len(hosts)) 103 | 104 | for q.Len() > 0 { 105 | _, obj := q.Pop() 106 | host := obj.(*host) 107 | tn := host.trees[rootid] 108 | //println("dist",pri,"host",host.name,"qlen",q.Len()) 109 | if len(tn.path) != tn.dist+1 { 110 | panic("dijkstra oops!") 111 | } 112 | 113 | for peerid, _ := range host.peers { 114 | peer := idmap[peerid] 115 | if peer == nil { 116 | panic("peer oops") 117 | } 118 | //println(" peer",peer.name) 119 | ptn := peer.trees[rootid] 120 | dist := tn.dist + 1 121 | if dist >= ptn.dist { 122 | continue // no better, so do nothing 123 | } 124 | 125 | // Form the new, shorter path to this peer 126 | ptn.dist = dist 127 | ptn.path = make([]HashId, dist+1) 128 | copy(ptn.path, tn.path) 129 | ptn.path[dist] = HashId(peerid) 130 | 131 | q.Push(ptn.dist, peer) 132 | //println(" dist",dist,"qlen",q.Len()) 133 | } 134 | } 135 | } 136 | 137 | func loadGraph(name string) { 138 | f, e := os.Open(name) 139 | if e != nil { 140 | panic(e.Error()) 141 | } 142 | 143 | s := bufio.NewScanner(f) 144 | hosts := make(map[string]*host) 145 | var root *host 146 | for s.Scan() { 147 | var host1, host2 string 148 | n, e := fmt.Sscan(s.Text(), &host1, &host2) 149 | if n != 2 { 150 | panic(e.Error()) 151 | } 152 | h1 := loadHost(host1, &hosts) 153 | h2 := loadHost(host2, &hosts) 154 | h1.addPeer(h2.pub, h2.id) 155 | h2.addPeer(h1.pub, h1.id) 156 | if root == nil { 157 | root = h1 158 | } 159 | } 160 | 161 | dijkstra(hosts, root) 162 | } 163 | 164 | func TestTree(t *testing.T) { 165 | 166 | // Create a tree 167 | /* 168 | arity := 3 169 | depth := 3 170 | root := newNode(suite, rand, nil) 171 | build(suite, rand, root, depth, arity) 172 | */ 173 | 174 | /* 175 | niter := 1000 176 | var sum1,sum2 int 177 | for i := 0; i < niter; i++ { 178 | c1,c2 := testLevels() 179 | sum1 += c1 180 | sum2 += c2 181 | } 182 | println("avg",float64(sum1)/float64(niter),float64(sum2)/float64(niter)) 183 | */ 184 | 185 | loadGraph("data/wax.dat") 186 | } 187 | -------------------------------------------------------------------------------- /coco/coconet/host.go: -------------------------------------------------------------------------------- 1 | package coconet 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/dedis/crypto/abstract" 7 | "golang.org/x/net/context" 8 | ) 9 | 10 | // Host is an abstract node on the Host tree. 11 | // Representing this tree, it has the ability to PutUp and PutDown 12 | // to its parent and children respectively. 13 | // It also can Get from all its connections. 14 | // It is up to the caller to de-multiplex the messages sent back from Get. 15 | // 16 | // A Host also implements multiple views of this tree. 17 | // Each view is a unique tree (parent, children) configuration. 18 | // Applications using Hosts can specify which view to use for operations. 19 | type Host interface { 20 | // Name returns the name of this host. 21 | Name() string 22 | 23 | Parent(view int) string 24 | 25 | // Peers returns a mapping from peer names to Connections. 26 | Peers() map[string]Conn 27 | 28 | // AddPeers adds new peers to the host, but does not make it either child or parent. 29 | AddPeers(hostnames ...string) 30 | 31 | // NewView creates a NewView to operate on. 32 | // It creates a view with the given view number, which corresponds to the tree 33 | // with the specified parent and children. 34 | NewView(view int, parent string, children []string, hostlist []string) 35 | 36 | NewViewFromPrev(view int, parent string) 37 | 38 | // Returns map of this host's views 39 | Views() *Views 40 | 41 | // AddParent adds a parent to the view specified. 42 | // Used for building a view incrementally. 43 | AddParent(view int, hostname string) 44 | // AddChildren adds children to the view specified. 45 | // Used for building a view incrementally. 46 | AddChildren(view int, hostname ...string) 47 | 48 | // NChildren returns the number of children for the given view. 49 | NChildren(view int) int 50 | // Children returns the children for a given view. 51 | Children(view int) map[string]Conn 52 | // Returns list of hosts available on a specific view 53 | HostListOn(view int) []string 54 | // Set the hoslist on a specific view 55 | SetHostList(view int, hostlist []string) 56 | 57 | // IsRoot returns true if this host is the root for the given view. 58 | IsRoot(view int) bool 59 | // IsParent returns true if the given peer is the parent for a specified view. 60 | IsParent(view int, peer string) bool 61 | // IsChild returns true if the given peer is the child for a specified view. 62 | IsChild(view int, peer string) bool 63 | 64 | // PutUp puts the given data up to the parent in the specified view. 65 | // The context is used to timeout the request. 66 | PutUp(ctx context.Context, view int, data BinaryMarshaler) error 67 | // PutDown puts the given data down to the children in the specified view. 68 | // The context is used to timeout the request. 69 | PutDown(ctx context.Context, view int, data []BinaryMarshaler) error 70 | 71 | PutTo(ctx context.Context, host string, data BinaryMarshaler) error 72 | 73 | // Get returns a channel on which all received messages will be put. 74 | // It always returns a reference to the same channel. 75 | // Multiple listeners will receive disjoint sets of messages. 76 | // When receiving from the channels always recieve from both the network 77 | // messages channel as well as the error channel. 78 | Get() chan NetworkMessg 79 | 80 | // Connect connects to the parent in the given view. 81 | Connect(view int) error 82 | ConnectTo(host string) error 83 | // Listen listens for incoming connections. 84 | Listen() error 85 | 86 | // Close closes all the connections in the Host. 87 | Close() 88 | 89 | // SetSuite sets the suite to use for the Host. 90 | SetSuite(abstract.Suite) 91 | // PubKey returns the public key of the Host. 92 | PubKey() abstract.Point 93 | // SetPubKey sets the public key of the Host. 94 | SetPubKey(abstract.Point) 95 | 96 | // Pool is a pool of BinaryUnmarshallers to use when generating NetworkMessg's. 97 | Pool() *sync.Pool 98 | // SetPool sets the pool of the Host. 99 | SetPool(*sync.Pool) 100 | 101 | // Functions to allow group evolution 102 | AddPeerToPending(h string) 103 | AddPendingPeer(view int, name string) error 104 | RemovePeer(view int, name string) bool 105 | Pending() map[string]bool 106 | 107 | AddPeerToHostlist(view int, name string) 108 | RemovePeerFromHostlist(view int, name string) 109 | } 110 | -------------------------------------------------------------------------------- /coco/sign/voteMessages.go: -------------------------------------------------------------------------------- 1 | package sign 2 | 3 | import ( 4 | "reflect" 5 | "sync" 6 | "time" 7 | 8 | "github.com/dedis/crypto/abstract" 9 | "github.com/dedis/crypto/nist" 10 | "github.com/dedis/protobuf" 11 | ) 12 | 13 | type VoteType int 14 | 15 | const ( 16 | DefaultVT VoteType = iota 17 | ViewChangeVT 18 | AddVT 19 | RemoveVT 20 | ShutdownVT 21 | NoOpVT 22 | ) 23 | 24 | // Multi-Purpose Vote embeds Action to be voted on, aggregated votes, and decison 25 | // when embedded in Announce it equals Vote Request (propose) 26 | // when embedded in Commit it equals Vote Response (promise) 27 | // when embedded in Challenge it equals Vote Confirmed (accept) 28 | // when embedded in Response it equals Vote Ack/ Nack (ack/ nack) 29 | type Vote struct { 30 | Index int 31 | View int 32 | Round int 33 | 34 | Type VoteType 35 | Av *AddVote 36 | Rv *RemoveVote 37 | Vcv *ViewChangeVote 38 | 39 | Count *Count 40 | Confirmed bool 41 | } 42 | 43 | type ViewChangeVote struct { 44 | View int // view number we want to switch to 45 | Parent string // our parent currently 46 | Root string // the root for the new view 47 | // TODO: potentially have signature of new root on proposing this view 48 | } 49 | 50 | type AddVote struct { 51 | View int // view number when we want add to take place 52 | Name string // who we want to add 53 | Parent string // our parent currently 54 | } 55 | 56 | type RemoveVote struct { 57 | View int // view number when we want add to take place 58 | Name string // who we want to remove 59 | Parent string // our parent currently 60 | } 61 | 62 | type VoteResponse struct { 63 | Name string // name of the responder 64 | Accepted bool 65 | // signature proves ownership of vote and 66 | // shows that it was emitted during a specifc Round 67 | Sig BasicSig 68 | } 69 | 70 | // for sorting arrays of VoteResponse 71 | type ByVoteResponse []*VoteResponse 72 | 73 | func (vr ByVoteResponse) Len() int { return len(vr) } 74 | func (vr ByVoteResponse) Swap(i, j int) { vr[i], vr[j] = vr[j], vr[i] } 75 | func (vr ByVoteResponse) Less(i, j int) bool { return (vr[i].Name < vr[j].Name) } 76 | 77 | // When sent up in a Committment Message CountedVotes contains a subtree's votes 78 | // When sent down in a Challenge Message CountedVotes contains the whole tree's votes 79 | type Count struct { 80 | Responses []*VoteResponse // vote responses from descendants 81 | For int // number of votes for 82 | Against int // number of votes against 83 | } 84 | 85 | type CatchUpRequest struct { 86 | Index int // index of requested vote 87 | } 88 | 89 | type CatchUpResponse struct { 90 | Vote *Vote 91 | } 92 | 93 | func (v *Vote) MarshalBinary() ([]byte, error) { 94 | return protobuf.Encode(v) 95 | } 96 | 97 | func (v *Vote) UnmarshalBinary(data []byte) error { 98 | var cons = make(protobuf.Constructors) 99 | var point abstract.Point 100 | var secret abstract.Secret 101 | var suite = nist.NewAES128SHA256P256() 102 | cons[reflect.TypeOf(&point).Elem()] = func() interface{} { return suite.Point() } 103 | cons[reflect.TypeOf(&secret).Elem()] = func() interface{} { return suite.Secret() } 104 | return protobuf.DecodeWithConstructors(data, v, cons) 105 | } 106 | 107 | type VoteLog struct { 108 | Entries []*Vote 109 | Last int // last set entry 110 | 111 | mu sync.Mutex 112 | } 113 | 114 | func (vl *VoteLog) Put(index int, v *Vote) { 115 | vl.mu.Lock() 116 | defer vl.mu.Unlock() 117 | 118 | for index >= len(vl.Entries) { 119 | buf := make([]*Vote, len(vl.Entries)+1) 120 | vl.Entries = append(vl.Entries, buf...) 121 | } 122 | vl.Entries[index] = v 123 | 124 | vl.Last = max(vl.Last, index) 125 | } 126 | 127 | func (vl *VoteLog) Get(index int) *Vote { 128 | if index >= len(vl.Entries) { 129 | return nil 130 | } 131 | 132 | return vl.Entries[index] 133 | } 134 | 135 | func NewVoteLog() *VoteLog { 136 | return &VoteLog{Last: -1} 137 | } 138 | 139 | func (vl *VoteLog) Stream() chan *Vote { 140 | ch := make(chan *Vote, 0) 141 | go func() { 142 | 143 | i := 1 144 | for { 145 | vl.mu.Lock() 146 | v := vl.Get(i) 147 | vl.mu.Unlock() 148 | if v != nil { 149 | ch <- v 150 | i++ 151 | } else { 152 | time.Sleep(500 * time.Millisecond) 153 | } 154 | } 155 | }() 156 | return ch 157 | } 158 | -------------------------------------------------------------------------------- /util/replace.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "os" 5 | "errors" 6 | "io/ioutil" 7 | "path/filepath" 8 | ) 9 | 10 | type Replacer struct { 11 | Name string // Name of the file being replaced 12 | Info os.FileInfo // Timestamp of target file before replacement 13 | File *os.File // Temporary file used for writing 14 | } 15 | 16 | var raceErr = errors.New("File was concurrently modified") 17 | 18 | // Open a file to replace an existing file as safely as possible, 19 | // attempting to avoid corruption on crash or concurrent writers. 20 | // 21 | // Creates a new temporary file in the same directory as the target, 22 | // but does not actually touch the target until Close() is called. 23 | // The caller can also call Abort() to close and delete the temporary file 24 | // without replacing the original file. 25 | // 26 | // If r.Info is nil, sets it to the status of the target file 27 | // at the time of this Open() call, for later race detection on Close(). 28 | // The caller can pre-set r.Info to a different timestamp 29 | // to define the race-detection window explicitly: 30 | // e.g., using an earlier timestamp obtained 31 | // when the original file was first read. 32 | // 33 | // Something like this functionality might be useful to have in os.ioutil. 34 | func (r *Replacer) Open(filename string) error { 35 | r.Name = filename 36 | 37 | // Save a time-stamp of the file we're replacing 38 | // so we can detect concurrent modifications. 39 | if r.Info == nil { 40 | info,err := os.Stat(filename) 41 | if err != nil { 42 | if !os.IsNotExist(err) { 43 | return err 44 | } 45 | } 46 | r.Info = info 47 | } 48 | 49 | // Create a temporary file in the same directory for the replacement 50 | dir := filepath.Dir(filename) 51 | pfx := filepath.Base(filename) 52 | file,err := ioutil.TempFile(dir,pfx) 53 | if err != nil { 54 | return err 55 | } 56 | r.File = file 57 | 58 | return nil 59 | } 60 | 61 | // Attempt to commit the temporary replacement file to the target. 62 | // On success, Close()s the temporary file and atomically renames it 63 | // to the target filename saved in the Name field. 64 | // 65 | // Returns an error if the target was modified by someone else 66 | // in the time since the temporary file was created. 67 | // In this case, the caller might for example call Abort() 68 | // and create a new Replacer after accounting for the new modifications. 69 | // Alternatively the caller might call ForceCommit() instead after 70 | // warning the user that state might be lost and prompting for confirmation. 71 | func (r *Replacer) Commit() error { 72 | 73 | // Make sure the file hasn't been concurrently modified. 74 | // Since this check is not atomic with the Rename below, 75 | // a race can still happen; this is only a heuristic. 76 | // OS-specific facilities would be needed to make it truly atomic. 77 | if r.Info != nil { 78 | info,err := os.Stat(r.Name) 79 | if err != nil { 80 | return err 81 | } 82 | if info.Name() != r.Info.Name() || 83 | info.Size() != r.Info.Size() || 84 | info.ModTime() != r.Info.ModTime() { 85 | return raceErr 86 | } 87 | } 88 | 89 | // Close the tmpfile 90 | tmpname := r.File.Name() 91 | if err := r.File.Close(); err != nil { 92 | return err 93 | } 94 | 95 | // Atomically update the target filename 96 | if err := os.Rename(tmpname, r.Name); err != nil { 97 | return err 98 | } 99 | 100 | r.File = nil 101 | return nil 102 | } 103 | 104 | // Commit the temporary replacement file 105 | // without checking for concurrent modifications in the meantime. 106 | func (r *Replacer) ForceCommit() error { 107 | r.Info = nil 108 | return r.Commit() 109 | } 110 | 111 | // Abort replacement by closing and deleting the temporary file. 112 | // It is harmless to call Abort() after the Replacer 113 | // has already committed or aborted: 114 | // thus, the caller may wish to 'defer r.Abort()' immediately after Open(). 115 | func (r *Replacer) Abort() { 116 | if r.File != nil { 117 | tmpname := r.File.Name() 118 | r.File.Close() 119 | r.File = nil 120 | os.Remove(tmpname) 121 | } 122 | } 123 | 124 | // Returns true if an error returned by Commit() indicates 125 | // that the commit failed because a concurrent write was detected 126 | // and the force flag was not specified. 127 | func IsRace(err error) bool { 128 | return err == raceErr 129 | } 130 | 131 | -------------------------------------------------------------------------------- /coco/stamp/messg.go: -------------------------------------------------------------------------------- 1 | package stamp 2 | 3 | import ( 4 | "bytes" 5 | "encoding/gob" 6 | 7 | "github.com/dedis/prifi/coco/hashid" 8 | "github.com/dedis/prifi/coco/proof" 9 | ) 10 | 11 | type SeqNo byte 12 | 13 | // struct to ease keeping track of who requires a reply after 14 | // tsm is processed/ aggregated by the TSServer 15 | type MustReplyMessage struct { 16 | Tsm TimeStampMessage 17 | To string // name of reply destination 18 | } 19 | 20 | type LogEntry struct { 21 | Seq SeqNo // Consecutively-incrementing log entry sequence number 22 | Root hashid.HashId // Merkle root of values committed this time-step 23 | Time *int64 // Optional wall-clock time this entry was created 24 | } 25 | 26 | type SignedEntry struct { 27 | Ent []byte // Encoded LogEntry to which the signature applies 28 | Sig []byte // Digital signature on the LogEntry 29 | } 30 | 31 | type StampRequest struct { 32 | Val []byte // Hash-size value to timestamp 33 | } 34 | type StampReply struct { 35 | Sig []byte // Signature on the root 36 | Prf proof.Proof // Merkle proof of value 37 | } 38 | 39 | // Request to obtain an old log-entry and, optionally, 40 | // a cryptographic proof that it happened before a given newer entry. 41 | // The TSServer may be unable to process if Seq is beyond the retention window. 42 | type EntryRequest struct { 43 | Seq SeqNo // Sequence number of old entry requested 44 | } 45 | type EntryReply struct { 46 | Log SignedEntry // Signed log entry 47 | } 48 | 49 | // Request a cryptographic Merkle proof that log-entry Old happened before New. 50 | // Produces a path to a Merkle tree node containing a hash of the node itself 51 | // and the root of the history values committed within the node. 52 | // The TSServer may be unable to process if Old is beyond the retention window. 53 | type ProofRequest struct { 54 | Old, New SeqNo // Sequence number of old and new log records 55 | } 56 | type ProofReply struct { 57 | Prf proof.Proof // Requested Merkle proof 58 | } 59 | 60 | // XXX not sure we need block requests? 61 | type BlockRequest struct { 62 | Ids []hashid.HashId // Hash of block(s) requested 63 | } 64 | 65 | type BlockReply struct { 66 | Dat [][]byte // Content of block(s) requested 67 | } 68 | 69 | type ErrorReply struct { 70 | Msg string // Human-readable error message 71 | } 72 | 73 | type MessageType int 74 | 75 | const ( 76 | Error MessageType = iota 77 | StampRequestType 78 | StampReplyType 79 | ) 80 | 81 | type TimeStampMessage struct { 82 | ReqNo SeqNo // Request sequence number 83 | // ErrorReply *ErrorReply // Generic error reply to any request 84 | Type MessageType 85 | Sreq *StampRequest 86 | Srep *StampReply 87 | } 88 | 89 | func (tsm TimeStampMessage) MarshalBinary() ([]byte, error) { 90 | var b bytes.Buffer 91 | var sub []byte 92 | var err error 93 | b.WriteByte(byte(tsm.Type)) 94 | b.WriteByte(byte(tsm.ReqNo)) 95 | // marshal sub message based on its Type 96 | switch tsm.Type { 97 | case StampRequestType: 98 | sub, err = tsm.Sreq.MarshalBinary() 99 | case StampReplyType: 100 | sub, err = tsm.Srep.MarshalBinary() 101 | } 102 | if err == nil { 103 | b.Write(sub) 104 | } 105 | return b.Bytes(), err 106 | } 107 | 108 | func (sm *TimeStampMessage) UnmarshalBinary(data []byte) error { 109 | sm.Type = MessageType(data[0]) 110 | sm.ReqNo = SeqNo(data[1]) 111 | msgBytes := data[2:] 112 | var err error 113 | switch sm.Type { 114 | case StampRequestType: 115 | sm.Sreq = &StampRequest{} 116 | err = sm.Sreq.UnmarshalBinary(msgBytes) 117 | case StampReplyType: 118 | sm.Srep = &StampReply{} 119 | err = sm.Srep.UnmarshalBinary(msgBytes) 120 | 121 | } 122 | return err 123 | } 124 | 125 | func (Sreq StampRequest) MarshalBinary() ([]byte, error) { 126 | var b bytes.Buffer 127 | enc := gob.NewEncoder(&b) 128 | err := enc.Encode(Sreq.Val) 129 | return b.Bytes(), err 130 | } 131 | 132 | func (Sreq *StampRequest) UnmarshalBinary(data []byte) error { 133 | b := bytes.NewBuffer(data) 134 | dec := gob.NewDecoder(b) 135 | err := dec.Decode(&Sreq.Val) 136 | return err 137 | } 138 | 139 | func (Srep StampReply) MarshalBinary() ([]byte, error) { 140 | var b bytes.Buffer 141 | enc := gob.NewEncoder(&b) 142 | err := enc.Encode(Srep.Sig) 143 | return b.Bytes(), err 144 | } 145 | 146 | func (Srep *StampReply) UnmarshalBinary(data []byte) error { 147 | b := bytes.NewBuffer(data) 148 | dec := gob.NewDecoder(b) 149 | err := dec.Decode(&Srep.Sig) 150 | return err 151 | } 152 | -------------------------------------------------------------------------------- /coco/sign/snvoting.go: -------------------------------------------------------------------------------- 1 | package sign 2 | 3 | import ( 4 | "sync/atomic" 5 | "time" 6 | 7 | log "github.com/Sirupsen/logrus" 8 | "golang.org/x/net/context" 9 | ) 10 | 11 | func (sn *Node) ApplyVotes(ch chan *Vote) { 12 | go func() { 13 | for v := range ch { 14 | if sn.RoundTypes[v.Round] == EmptyRT { 15 | sn.RoundTypes[v.Round] = RoundType(v.Type) 16 | } 17 | sn.ApplyVote(v) 18 | } 19 | }() 20 | } 21 | 22 | // HERE: after we change to the new view, we could send our parent 23 | // a notification that we are ready to use the new view 24 | 25 | func (sn *Node) ApplyVote(v *Vote) { 26 | atomic.StoreInt64(&sn.LastAppliedVote, int64(v.Index)) 27 | lav := atomic.LoadInt64(&sn.LastAppliedVote) 28 | lsv := atomic.LoadInt64(&sn.LastSeenVote) 29 | atomic.StoreInt64(&sn.LastSeenVote, maxint64(lav, lsv)) 30 | 31 | switch v.Type { 32 | case ViewChangeVT: 33 | sn.ChangeView(v.Vcv) 34 | case AddVT: 35 | sn.AddAction(v.Av.View, v) 36 | case RemoveVT: 37 | sn.AddAction(v.Rv.View, v) 38 | case ShutdownVT: 39 | sn.Close() 40 | default: 41 | log.Errorln("applyvote: unkown vote type") 42 | } 43 | } 44 | 45 | func (sn *Node) AddAction(view int, v *Vote) { 46 | sn.Actions[view] = append(sn.Actions[view], v) 47 | } 48 | 49 | func (sn *Node) ApplyAction(view int, v *Vote) { 50 | log.Println(sn.Name(), "APPLYING ACTION") 51 | switch v.Type { 52 | case AddVT: 53 | sn.AddPeerToHostlist(view, v.Av.Name) 54 | if sn.Name() == v.Av.Parent { 55 | sn.AddChildren(view, v.Av.Name) 56 | } 57 | case RemoveVT: 58 | // removes node from Hostlist, and from children list 59 | sn.RemovePeer(view, v.Rv.Name) 60 | // not closing TCP connection on remove because if view 61 | // does not go through, connection essential to old/ current view closed 62 | default: 63 | log.Errorln("applyvote: unkown action type") 64 | } 65 | } 66 | 67 | func (sn *Node) NotifyOfAction(view int, v *Vote) { 68 | log.Println(sn.Name(), "Notifying node to be added/removed of action") 69 | gcm := &SigningMessage{ 70 | Type: GroupChanged, 71 | From: sn.Name(), 72 | View: view, 73 | LastSeenVote: int(sn.LastSeenVote), 74 | Gcm: &GroupChangedMessage{ 75 | V: v, 76 | HostList: sn.HostListOn(view)}} 77 | 78 | switch v.Type { 79 | case AddVT: 80 | if sn.Name() == v.Av.Parent { 81 | sn.PutTo(context.TODO(), v.Av.Name, gcm) 82 | } 83 | case RemoveVT: 84 | if sn.Name() == v.Rv.Parent { 85 | sn.PutTo(context.TODO(), v.Rv.Name, gcm) 86 | } 87 | default: 88 | log.Errorln("notifyofaction: unkown action type") 89 | } 90 | } 91 | 92 | func (sn *Node) AddSelf(parent string) error { 93 | log.Println("AddSelf: connecting to:", parent) 94 | err := sn.ConnectTo(parent) 95 | if err != nil { 96 | return err 97 | } 98 | 99 | log.Println("AddSelf: putting group change message to:", parent) 100 | return sn.PutTo( 101 | context.TODO(), 102 | parent, 103 | &SigningMessage{ 104 | Type: GroupChange, 105 | View: -1, 106 | Vrm: &VoteRequestMessage{ 107 | Vote: &Vote{ 108 | Type: AddVT, 109 | Av: &AddVote{ 110 | Name: sn.Name(), 111 | Parent: parent}}}}) 112 | } 113 | 114 | func (sn *Node) RemoveSelf() error { 115 | return sn.PutUp( 116 | context.TODO(), 117 | int(sn.ViewNo), 118 | &SigningMessage{ 119 | Type: GroupChange, 120 | View: -1, 121 | Vrm: &VoteRequestMessage{ 122 | Vote: &Vote{ 123 | Type: RemoveVT, 124 | Rv: &RemoveVote{ 125 | Name: sn.Name(), 126 | Parent: sn.Parent(sn.ViewNo)}}}}) 127 | } 128 | 129 | func (sn *Node) CatchUp(vi int, from string) { 130 | log.Println(sn.Name(), "attempting to catch up vote", vi) 131 | 132 | ctx := context.TODO() 133 | sn.PutTo(ctx, from, 134 | &SigningMessage{ 135 | From: sn.Name(), 136 | Type: CatchUpReq, 137 | Cureq: &CatchUpRequest{Index: vi}}) 138 | } 139 | 140 | func (sn *Node) StartGossip() { 141 | go func() { 142 | t := time.Tick(GOSSIP_TIME) 143 | for { 144 | select { 145 | case <-t: 146 | sn.viewmu.Lock() 147 | c := sn.HostListOn(sn.ViewNo) 148 | sn.viewmu.Unlock() 149 | if len(c) == 0 { 150 | log.Errorln(sn.Name(), "StartGossip: none in hostlist for view: ", sn.ViewNo, len(c)) 151 | continue 152 | } 153 | sn.randmu.Lock() 154 | from := c[sn.Rand.Int()%len(c)] 155 | sn.randmu.Unlock() 156 | log.Errorln("Gossiping with: ", from) 157 | sn.CatchUp(int(atomic.LoadInt64(&sn.LastAppliedVote)+1), from) 158 | case <-sn.closed: 159 | log.Warnln("stopping gossip: closed") 160 | return 161 | } 162 | } 163 | }() 164 | } 165 | -------------------------------------------------------------------------------- /coco/test/deploy2deter/run_tests/monitor.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "log" 9 | "math" 10 | "strconv" 11 | "time" 12 | 13 | "github.com/PuerkitoBio/goquery" 14 | "github.com/pkg/browser" 15 | "golang.org/x/net/websocket" 16 | ) 17 | 18 | // Monitor monitors log aggregates results into RunStats 19 | func Monitor(bf int) RunStats { 20 | log.Println("MONITORING") 21 | defer fmt.Println("DONE MONITORING") 22 | retry_dial: 23 | ws, err := websocket.Dial("ws://localhost:8080/log", "", "http://localhost/") 24 | if err != nil { 25 | time.Sleep(1 * time.Second) 26 | goto retry_dial 27 | } 28 | retry: 29 | // Get HTML of webpage for data (NHosts, Depth, ...) 30 | doc, err := goquery.NewDocument("http://localhost:8080/") 31 | if err != nil { 32 | log.Println("unable to get log data: retrying:", err) 33 | time.Sleep(10 * time.Second) 34 | goto retry 35 | } 36 | if view { 37 | browser.OpenURL("http://localhost:8080/") 38 | } 39 | nhosts := doc.Find("#numhosts").First().Text() 40 | log.Println("hosts:", nhosts) 41 | depth := doc.Find("#depth").First().Text() 42 | log.Println("depth:", depth) 43 | nh, err := strconv.Atoi(nhosts) 44 | if err != nil { 45 | log.Fatal("unable to convert hosts to be a number:", nhosts) 46 | } 47 | d, err := strconv.Atoi(depth) 48 | if err != nil { 49 | log.Fatal("unable to convert depth to be a number:", depth) 50 | } 51 | clientDone := false 52 | rootDone := false 53 | var rs RunStats 54 | rs.NHosts = nh 55 | rs.Depth = d 56 | rs.BF = bf 57 | 58 | var M, S float64 59 | k := float64(1) 60 | first := true 61 | for { 62 | var data []byte 63 | err := websocket.Message.Receive(ws, &data) 64 | if err != nil { 65 | // if it is an eof error than stop reading 66 | if err == io.EOF { 67 | log.Println("websocket terminated before emitting EOF or terminating string") 68 | break 69 | } 70 | continue 71 | } 72 | if bytes.Contains(data, []byte("EOF")) || bytes.Contains(data, []byte("terminating")) { 73 | log.Printf( 74 | "EOF/terminating Detected: need forkexec to report and clients: rootDone(%t) clientDone(%t)", 75 | rootDone, clientDone) 76 | } 77 | if bytes.Contains(data, []byte("root_round")) { 78 | if clientDone || rootDone { 79 | // ignore after we have received our first EOF 80 | continue 81 | } 82 | var entry StatsEntry 83 | err := json.Unmarshal(data, &entry) 84 | if err != nil { 85 | log.Fatal("json unmarshalled improperly:", err) 86 | } 87 | log.Println("root_round:", entry) 88 | if first { 89 | first = false 90 | rs.MinTime = entry.Time 91 | rs.MaxTime = entry.Time 92 | } 93 | if entry.Time < rs.MinTime { 94 | rs.MinTime = entry.Time 95 | } else if entry.Time > rs.MaxTime { 96 | rs.MaxTime = entry.Time 97 | } 98 | 99 | rs.AvgTime = ((rs.AvgTime * (k - 1)) + entry.Time) / k 100 | 101 | var tM = M 102 | M += (entry.Time - tM) / k 103 | S += (entry.Time - tM) * (entry.Time - M) 104 | k++ 105 | rs.StdDev = math.Sqrt(S / (k - 1)) 106 | } else if bytes.Contains(data, []byte("forkexec")) { 107 | if rootDone { 108 | continue 109 | } 110 | var ss SysStats 111 | err := json.Unmarshal(data, &ss) 112 | if err != nil { 113 | log.Fatal("unable to unmarshal forkexec:", ss) 114 | } 115 | rs.SysTime = ss.SysTime 116 | rs.UserTime = ss.UserTime 117 | log.Println("FORKEXEC:", ss) 118 | if clientDone { 119 | break 120 | } 121 | rootDone = true 122 | } else if bytes.Contains(data, []byte("client_msg_stats")) { 123 | if clientDone { 124 | continue 125 | } 126 | var cms ClientMsgStats 127 | err := json.Unmarshal(data, &cms) 128 | if err != nil { 129 | log.Fatal("unable to unmarshal client_msg_stats:", string(data)) 130 | } 131 | // what do I want to keep out of the Client Message States 132 | // cms.Buckets stores how many were processed at time T 133 | // cms.RoundsAfter stores how many rounds delayed it was 134 | // 135 | // get the average delay (roundsAfter), max and min 136 | // get the total number of messages timestamped 137 | // get the average number of messages timestamped per second? 138 | avg, _, _, _ := ArrStats(cms.Buckets) 139 | // get the observed rate of processed messages 140 | // avg is how many messages per second, we want how many milliseconds between messages 141 | observed := avg / 1000 // set avg to messages per milliseconds 142 | observed = 1 / observed 143 | rs.Rate = observed 144 | rs.Times = cms.Times 145 | if rootDone { 146 | break 147 | } 148 | clientDone = true 149 | } 150 | } 151 | return rs 152 | } 153 | -------------------------------------------------------------------------------- /coco/test/exec/timestamper/timestamper.go: -------------------------------------------------------------------------------- 1 | package timestamper 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | log "github.com/Sirupsen/logrus" 8 | 9 | "github.com/dedis/crypto/abstract" 10 | "github.com/dedis/crypto/edwards/ed25519" 11 | "github.com/dedis/crypto/nist" 12 | "github.com/dedis/prifi/coco" 13 | "github.com/dedis/prifi/coco/sign" 14 | "github.com/dedis/prifi/coco/test/logutils" 15 | "github.com/dedis/prifi/coco/test/oldconfig" 16 | ) 17 | 18 | func GetSuite(suite string) abstract.Suite { 19 | var s abstract.Suite 20 | switch { 21 | case suite == "nist256": 22 | s = nist.NewAES128SHA256P256() 23 | case suite == "nist512": 24 | s = nist.NewAES128SHA256QR512() 25 | case suite == "ed25519": 26 | s = ed25519.NewAES128SHA256Ed25519(true) 27 | default: 28 | s = nist.NewAES128SHA256P256() 29 | } 30 | return s 31 | } 32 | 33 | func Run(hostname, cfg, app string, rounds int, rootwait int, debug, testConnect bool, failureRate, rFail, fFail int, logger, suite string) { 34 | if debug { 35 | coco.DEBUG = true 36 | } 37 | 38 | // fmt.Println("EXEC TIMESTAMPER: " + hostname) 39 | if hostname == "" { 40 | fmt.Println("hostname is empty") 41 | log.Fatal("no hostname given") 42 | } 43 | 44 | // load the configuration 45 | //log.Println("loading configuration") 46 | var hc *oldconfig.HostConfig 47 | var err error 48 | s := GetSuite(suite) 49 | opts := oldconfig.ConfigOptions{ConnType: "tcp", Host: hostname, Suite: s} 50 | if failureRate > 0 || fFail > 0 { 51 | opts.Faulty = true 52 | } 53 | hc, err = oldconfig.LoadConfig(cfg, opts) 54 | if err != nil { 55 | fmt.Println(err) 56 | log.Fatal(err) 57 | } 58 | 59 | // set FailureRates 60 | if failureRate > 0 { 61 | for i := range hc.SNodes { 62 | hc.SNodes[i].FailureRate = failureRate 63 | } 64 | } 65 | 66 | // set root failures 67 | if rFail > 0 { 68 | for i := range hc.SNodes { 69 | hc.SNodes[i].FailAsRootEvery = rFail 70 | 71 | } 72 | } 73 | // set follower failures 74 | // a follower fails on %ffail round with failureRate probability 75 | for i := range hc.SNodes { 76 | hc.SNodes[i].FailAsFollowerEvery = fFail 77 | } 78 | 79 | // run this specific host 80 | // log.Println("RUNNING HOST CONFIG") 81 | err = hc.Run(app != "sign", sign.MerkleTree, hostname) 82 | if err != nil { 83 | log.Fatal(err) 84 | } 85 | 86 | defer func(sn *sign.Node) { 87 | log.Panicln("program has terminated:", hostname) 88 | sn.Close() 89 | }(hc.SNodes[0]) 90 | 91 | if app == "sign" { 92 | //log.Println("RUNNING Node") 93 | // if I am root do the announcement message 94 | if hc.SNodes[0].IsRoot(0) { 95 | time.Sleep(3 * time.Second) 96 | start := time.Now() 97 | iters := 10 98 | 99 | for i := 0; i < iters; i++ { 100 | start = time.Now() 101 | //fmt.Println("ANNOUNCING") 102 | hc.SNodes[0].LogTest = []byte("Hello World") 103 | err = hc.SNodes[0].Announce(0, 104 | &sign.AnnouncementMessage{ 105 | LogTest: hc.SNodes[0].LogTest, 106 | Round: i}) 107 | if err != nil { 108 | log.Println(err) 109 | } 110 | elapsed := time.Since(start) 111 | log.WithFields(log.Fields{ 112 | "file": logutils.File(), 113 | "type": "root_announce", 114 | "round": i, 115 | "time": elapsed, 116 | }).Info("") 117 | } 118 | 119 | } else { 120 | // otherwise wait a little bit (hopefully it finishes by the end of this) 121 | time.Sleep(30 * time.Second) 122 | } 123 | } else if app == "stamp" || app == "vote" { 124 | // log.Println("RUNNING TIMESTAMPER") 125 | stampers, _, err := hc.RunTimestamper(0, hostname) 126 | // get rid of the hc information so it can be GC'ed 127 | hc = nil 128 | if err != nil { 129 | log.Fatal(err) 130 | } 131 | for _, s := range stampers { 132 | // only listen if this is the hostname specified 133 | if s.Name() == hostname { 134 | s.Logger = logger 135 | s.Hostname = hostname 136 | s.App = app 137 | if s.IsRoot(0) { 138 | log.Println("RUNNING ROOT SERVER AT:", hostname, rounds) 139 | log.Printf("Waiting: %d s\n", rootwait) 140 | // wait for the other nodes to get set up 141 | time.Sleep(time.Duration(rootwait) * time.Second) 142 | 143 | log.Println("STARTING ROOT ROUND") 144 | s.Run("root", rounds) 145 | // log.Println("\n\nROOT DONE\n\n") 146 | 147 | } else if !testConnect { 148 | log.Println("RUNNING REGULAR AT:", hostname) 149 | s.Run("regular", rounds) 150 | // log.Println("\n\nREGULAR DONE\n\n") 151 | } else { 152 | // testing connection 153 | log.Println("RUNNING TEST_CONNNECT AT:", hostname) 154 | s.Run("test_connect", rounds) 155 | } 156 | } 157 | } 158 | } 159 | 160 | } 161 | -------------------------------------------------------------------------------- /time/proof.go: -------------------------------------------------------------------------------- 1 | package time 2 | 3 | import ( 4 | "bytes" 5 | "crypto/subtle" 6 | "errors" 7 | "hash" 8 | 9 | "github.com/dedis/crypto/abstract" 10 | ) 11 | 12 | type hashContext struct { 13 | newHash func() hash.Hash 14 | hash hash.Hash 15 | } 16 | 17 | func (c *hashContext) hashNode(buf []byte, left, right []byte) []byte { 18 | if bytes.Compare(left, right) > 0 { // sort so left < right 19 | left, right = right, left 20 | } 21 | if c.hash == nil { 22 | c.hash = c.newHash() 23 | } else { 24 | c.hash.Reset() 25 | } 26 | h := c.hash 27 | h.Write(left) 28 | h.Write(right) 29 | return h.Sum(buf) 30 | } 31 | 32 | // Proof-of-beforeness: 33 | // a list of offsets of peer-hash-pointers at each level below the root. 34 | type Proof []HashId 35 | 36 | // Given a Proof and the hash of the leaf, compute the hash of the root. 37 | // If the Proof is of length 0, simply returns leaf. 38 | func (p Proof) Calc(newHash func() hash.Hash, leaf []byte) []byte { 39 | c := hashContext{newHash: newHash} 40 | var buf []byte 41 | for i := len(p) - 1; i >= 0; i-- { 42 | leaf = c.hashNode(buf[:0], leaf, p[i]) 43 | buf = leaf 44 | } 45 | return leaf 46 | } 47 | 48 | // Check a purported Proof against given root and leaf hashes. 49 | func (p Proof) Check(newHash func() hash.Hash, root, leaf []byte) bool { 50 | chk := p.Calc(newHash, leaf) 51 | return subtle.ConstantTimeCompare(chk, root) != 0 52 | } 53 | 54 | // Generate a Merkle proof tree for the given list of leaves, 55 | // yielding one output proof per leaf. 56 | func ProofTree(newHash func() hash.Hash, leaves []HashId) (HashId, []Proof) { 57 | 58 | // Determine the required tree depth 59 | nleaves := len(leaves) 60 | depth := 0 61 | for n := 1; n < nleaves; n <<= 1 { 62 | depth++ 63 | } 64 | 65 | // Build the Merkle tree 66 | c := hashContext{newHash: newHash} 67 | tree := make([][]HashId, depth+1) 68 | tree[depth] = leaves 69 | nprev := nleaves 70 | tprev := tree[depth] 71 | for d := depth - 1; d >= 0; d-- { 72 | nnext := (nprev + 1) >> 1 // # hashes total at level i 73 | nnode := nprev >> 1 // # new nodes at level i 74 | println("nprev", nprev, "nnext", nnext, "nnode", nnode) 75 | tree[d] = make([]HashId, nnext) 76 | tnext := tree[d] 77 | for i := 0; i < nnode; i++ { 78 | tnext[i] = c.hashNode(nil, tprev[i*2], tprev[i*2+1]) 79 | } 80 | // If nnode < nhash, just leave the odd one nil. 81 | nprev = nnext 82 | tprev = tnext 83 | } 84 | if nprev != 1 { 85 | panic("oops") 86 | } 87 | root := tprev[0] 88 | 89 | // Build all the individual proofs from the tree. 90 | // Some towards the end may end up being shorter than depth. 91 | proofs := make([]Proof, nleaves) 92 | for i := 0; i < nleaves; i++ { 93 | p := make([]HashId, depth)[:0] 94 | for d := 0; d < depth; d++ { 95 | h := tree[d][i>>uint(depth-d)] 96 | if h != nil { 97 | p = append(p, h) 98 | } 99 | } 100 | proofs[i] = Proof(p) 101 | } 102 | return root, proofs 103 | } 104 | 105 | // MerklePath represents a downward path from a (root) node in a Merkle tree 106 | // to a given (interior or leaf) descendant node, 107 | // including all the data necessary to validate and extract the descendant. 108 | // It is assumed the caller has a valid hash-pointer to the root/starting node, 109 | // and that all nodes in the path can be retrieved via self-certifying hash-ID. 110 | type MerklePath struct { 111 | Ptr []int // Offsets of hash-pointers at each intermediate level 112 | Ofs int // Offset of relevant object in last-level blob 113 | Len int // Length of relevant object in last-level blob 114 | } 115 | 116 | // Retrieve an object in a Merkle tree, 117 | // validating the entire path in the process. 118 | // Returns a slice of a buffer obtained from HashGet.Get(), 119 | // which might be shared and should be considered read-only. 120 | func MerkleGet(suite abstract.Suite, root []byte, path MerklePath, 121 | ctx HashGet) ([]byte, error) { 122 | 123 | // Follow pointers through intermediate levels 124 | blob := root 125 | for i := range path.Ptr { 126 | beg := path.Ptr[i] 127 | // end := beg + suite.HashLen() 128 | end := beg + 256 // change me: find hash len 129 | if end > len(blob) { 130 | return nil, errors.New("bad Merkle tree pointer offset") 131 | } 132 | id := HashId(blob[beg:end]) 133 | b, e := ctx.Get(id) // Lookup the next-level blob 134 | if e != nil { 135 | return nil, e 136 | } 137 | blob = b 138 | } 139 | 140 | // Validate and extract the actual object 141 | beg := path.Ofs 142 | end := beg + path.Len 143 | if end > len(blob) { 144 | return nil, errors.New("bad Merkle tree object offset/length") 145 | } 146 | return blob[beg:end], nil 147 | } 148 | 149 | //type MerkleLog struct { 150 | //} 151 | -------------------------------------------------------------------------------- /python/schnorr.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import random 4 | 5 | from elgamal import ElGamal 6 | from Crypto.Util.number import bytes_to_long, GCD, inverse, long_to_bytes 7 | 8 | from utils import string_to_long 9 | 10 | def verdict_1024(): 11 | p = string_to_long("fd8a16fc2afdaeb2ea62b66b355f73e6c2fc4349bf4551793" 12 | "36ca1b45f75d68da0101cba63c22efd5f72e5c81dc30cf709da" 13 | "aef2323e950160926e11ef8cbf40a26496668749218b5620276" 14 | "697c2d1536b31042ad846e1e5758d79b3e4e0b5bc4c5d3a4e95" 15 | "da4502e9058ea3beade156d8234e35d5164783c57e6135139db" 16 | "097") 17 | g = 2 18 | q = (p - 1) // 2 19 | r = 2 20 | return SchnorrGroup(p, g, q, r) 21 | 22 | def diffiehellman_1024(): 23 | g = string_to_long("A4D1CBD5 C3FD3412 6765A442 EFB99905 F8104DD2 58AC507F" 24 | "D6406CFF 14266D31 266FEA1E 5C41564B 777E690F 5504F213" 25 | "160217B4 B01B886A 5E91547F 9E2749F4 D7FBD7D3 B9A92EE1" 26 | "909D0D22 63F80A76 A6A24C08 7A091F53 1DBF0A01 69B6A28A" 27 | "D662A4D1 8E73AFA3 2D779D59 18D08BC8 858F4DCE F97C2A24" 28 | "855E6EEB 22B3B2E5") 29 | p = string_to_long("B10B8F96 A080E01D DE92DE5E AE5D54EC 52C99FBC FB06A3C6" 30 | "9A6A9DCA 52D23B61 6073E286 75A23D18 9838EF1E 2EE652C0" 31 | "13ECB4AE A9061123 24975C3C D49B83BF ACCBDD7D 90C4BD70" 32 | "98488E9C 219A7372 4EFFD6FA E5644738 FAA31A4F F55BCCC0" 33 | "A151AF5F 0DC8B4BD 45BF37DF 365C1A65 E68CFDA7 6D4DA708" 34 | "DF1FB2BC 2E4A4371") 35 | q = string_to_long("F518AA87 81A8DF27 8ABA4E7D 64B7CB9D 49462353") 36 | r = 2 37 | return SchnorrGroup(p, g, q, r) 38 | 39 | class SchnorrGroup(ElGamal): 40 | def __init__(self, p, g, q, r): 41 | self.p = p 42 | self.q = q 43 | self.g = g 44 | self.r = r 45 | 46 | def generator(self): 47 | return self.g 48 | 49 | def order(self): 50 | return self.q 51 | 52 | def zero(self): 53 | return 0 54 | 55 | def identity(self): 56 | return 1 57 | 58 | def is_element(self, a): 59 | return pow(a, self.q, self.p) == 1 60 | 61 | def is_generator(self, a): 62 | return self.is_element(a) and \ 63 | (pow(a, self.r, self.p) == 1) 64 | 65 | def random_secret(self): 66 | return random.randrange(1 << (self.q.bit_length() - 1), self.q - 1) 67 | 68 | def random_element(self): 69 | return self.multiply(self.generator(), self.random_secret()) 70 | 71 | def add(self, a, b): 72 | return (a * b) % self.p 73 | 74 | def multiply(self, a, b): 75 | return pow(a, b, self.p) 76 | 77 | def inverse(self, a): 78 | return inverse(a, self.p) 79 | 80 | def bytes(self, a): 81 | return long_to_bytes(a) 82 | 83 | def element(self, a): 84 | return bytes_to_long(a) 85 | 86 | def encode(self, data): 87 | if isinstance(data, int): 88 | data = long_to_bytes(data) 89 | tmp_data = bytearray(b'\xff' + data + b'0\xff') 90 | 91 | pad = 0 92 | while pad < 256: 93 | element = bytes_to_long(tmp_data) 94 | if self.is_element(element): 95 | break 96 | pad += 1 97 | tmp_data[-2] = pad 98 | 99 | assert pad != 256 100 | 101 | return element 102 | 103 | def decode(self, a): 104 | data = long_to_bytes(a) 105 | assert data[0] == 0xff and data[-1] == 0xff 106 | return data[1:-2] 107 | 108 | def sign(self, secret, data): 109 | p1 = self.p - 1 110 | while True: 111 | k = random.randrange(1 << (p1.bit_length() - 1), p1) 112 | if GCD(k, p1) == 1: 113 | break 114 | r = self.multiply(self.generator(), k) 115 | k_inv = inverse(k, p1) 116 | s = ((self._hash(data, p1) - secret * r) * k_inv) % p1 117 | return (r, s) 118 | 119 | def verify(self, element, data, sign): 120 | if sign[0] < 1 or sign[0] > self.p - 1: 121 | return False 122 | if sign[1] < 1 or sign[1] > self.p - 1: 123 | return False 124 | v1 = self.add(self.multiply(element, sign[0]), 125 | self.multiply(sign[0], sign[1])) 126 | v2 = self.multiply(self.generator(), self._hash(data, self.p - 1)) 127 | return v1 == v2 128 | 129 | def main(): 130 | group = verdict_1024() 131 | from elgamal import PrivateKey, PublicKey 132 | x0 = PrivateKey(group) 133 | y0 = x0.public_key() 134 | x1 = PrivateKey(group) 135 | y1 = x1.public_key() 136 | 137 | assert x0.exchange(y1) == y1.exchange(x0) 138 | 139 | data = b"hello" 140 | encrypted = y0.encrypt(data) 141 | assert x0.decrypt(encrypted) == data 142 | 143 | assert y0.verify(data, x0.sign(data)) 144 | 145 | if __name__ == "__main__": 146 | main() 147 | -------------------------------------------------------------------------------- /coco/sign/signingMessages.go: -------------------------------------------------------------------------------- 1 | package sign 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/dedis/crypto/abstract" 7 | "github.com/dedis/crypto/nist" 8 | "github.com/dedis/prifi/coco/hashid" 9 | "github.com/dedis/prifi/coco/proof" 10 | "github.com/dedis/protobuf" 11 | ) 12 | 13 | // All message structures defined in this package are used in the 14 | // Collective Signing Protocol 15 | // Over the network they are sent as byte slices, so each message 16 | // has its own MarshlBinary and UnmarshalBinary method 17 | 18 | type MessageType int 19 | 20 | const ( 21 | Unset MessageType = iota 22 | Announcement 23 | Commitment 24 | Challenge 25 | Response 26 | CatchUpReq 27 | CatchUpResp 28 | GroupChange 29 | GroupChanged 30 | Default // for internal use 31 | Error 32 | ) 33 | 34 | func (m MessageType) String() string { 35 | switch m { 36 | case Unset: 37 | return "Unset" 38 | case Announcement: 39 | return "Announcement" 40 | case Commitment: 41 | return "Commitment" 42 | case Challenge: 43 | return "Challenge" 44 | case Response: 45 | return "Response" 46 | case CatchUpReq: 47 | return "CatchUpRequest" 48 | case CatchUpResp: 49 | return "CatchUpResponse" 50 | case GroupChange: 51 | return "GroupChange" 52 | case GroupChanged: 53 | return "GroupChanged" 54 | case Default: // for internal use 55 | return "Default" 56 | case Error: 57 | return "Error" 58 | } 59 | return "INVALID TYPE" 60 | } 61 | 62 | // Signing Messages are used for all comunications between servers 63 | // It is imporant for encoding/ decoding for type to be kept as first field 64 | type SigningMessage struct { 65 | Type MessageType 66 | Am *AnnouncementMessage 67 | Com *CommitmentMessage 68 | Chm *ChallengeMessage 69 | Rm *ResponseMessage 70 | Cureq *CatchUpRequest 71 | Curesp *CatchUpResponse 72 | Vrm *VoteRequestMessage 73 | Gcm *GroupChangedMessage 74 | Err *ErrorMessage 75 | From string 76 | View int 77 | LastSeenVote int // highest vote ever seen and commited in log, used for catch-up 78 | } 79 | 80 | var msgSuite abstract.Suite = nist.NewAES128SHA256P256() 81 | 82 | func NewSigningMessage() interface{} { 83 | return &SigningMessage{} 84 | } 85 | 86 | func (sm *SigningMessage) MarshalBinary() ([]byte, error) { 87 | return protobuf.Encode(sm) 88 | } 89 | 90 | func (sm *SigningMessage) UnmarshalBinary(data []byte) error { 91 | var cons = make(protobuf.Constructors) 92 | var point abstract.Point 93 | var secret abstract.Secret 94 | cons[reflect.TypeOf(&point).Elem()] = func() interface{} { return msgSuite.Point() } 95 | cons[reflect.TypeOf(&secret).Elem()] = func() interface{} { return msgSuite.Secret() } 96 | return protobuf.DecodeWithConstructors(data, sm, cons) 97 | } 98 | 99 | // Broadcasted message initiated and signed by proposer 100 | type AnnouncementMessage struct { 101 | LogTest []byte // TODO: change LogTest to Messg 102 | Round int 103 | 104 | // VoteRequest *VoteRequest 105 | Vote *Vote // Vote Request (propose) 106 | } 107 | 108 | type CommitmentMessage struct { 109 | V abstract.Point // commitment Point 110 | V_hat abstract.Point // product of subtree participating nodes' commitment points 111 | X_hat abstract.Point // product of subtree participating nodes' public keys 112 | 113 | MTRoot hashid.HashId // root of Merkle (sub)Tree 114 | 115 | // public keys of children servers that did not respond to 116 | // annoucement from root 117 | ExceptionList []abstract.Point 118 | 119 | // CountedVotes *CountedVotes // CountedVotes contains a subtree's votes 120 | Vote *Vote // Vote Response (promise) 121 | 122 | Round int 123 | } 124 | 125 | type ChallengeMessage struct { 126 | C abstract.Secret // challenge 127 | 128 | // Depth byte 129 | MTRoot hashid.HashId // the very root of the big Merkle Tree 130 | Proof proof.Proof // Merkle Path of Proofs from root to us 131 | 132 | // CountedVotes *CountedVotes // CountedVotes contains the whole tree's votes 133 | Vote *Vote // Vote Confirmerd/ Rejected (accept) 134 | 135 | Round int 136 | } 137 | 138 | type ResponseMessage struct { 139 | R_hat abstract.Secret // response 140 | 141 | // public keys of children servers that did not respond to 142 | // challenge from root 143 | ExceptionList []abstract.Point 144 | // cummulative point commits of nodes that failed after commit 145 | ExceptionV_hat abstract.Point 146 | // cummulative public keys of nodes that failed after commit 147 | ExceptionX_hat abstract.Point 148 | 149 | Vote *Vote // Vote Ack/Nack in thr log (ack/nack) 150 | 151 | Round int 152 | } 153 | 154 | type ErrorMessage struct { 155 | Err string 156 | } 157 | 158 | type VoteRequestMessage struct { 159 | Vote *Vote 160 | } 161 | 162 | type GroupChangedMessage struct { 163 | V *Vote 164 | // if vote not accepted rest of fields are nil 165 | HostList []string 166 | } 167 | -------------------------------------------------------------------------------- /coco/test/deploy2deter/run_tests/stats.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "io/ioutil" 8 | "log" 9 | "math" 10 | "net/http" 11 | "runtime" 12 | "strconv" 13 | "time" 14 | ) 15 | 16 | /* 17 | { 18 | "eapp":"time", 19 | "ehost":"10.255.0.13:2000", 20 | "elevel":"info", 21 | "emsg":"root round", 22 | "etime":"2015-02-27T09:50:45-08:00", 23 | "file":"server.go:195", 24 | "round":59, 25 | "time":893709029, 26 | "type":"root_round" 27 | } 28 | */ 29 | type StatsEntry struct { 30 | App string `json:"eapp"` 31 | Host string `json:"ehost"` 32 | Level string `json:"elevel"` 33 | Msg string `json:"emsg"` 34 | MsgTime string `json:"etime"` 35 | File string `json:"file"` 36 | Round int `json:"round"` 37 | Time float64 `json:"time"` 38 | Type string `json:"type"` 39 | } 40 | 41 | type SysStats struct { 42 | File string `json:"file"` 43 | Type string `json:"type"` 44 | SysTime float64 `json:"systime"` 45 | UserTime float64 `json:"usertime"` 46 | } 47 | 48 | type ClientMsgStats struct { 49 | File string `json:"file"` 50 | Type string `json:"type"` 51 | Buckets []float64 `json:"buck,omitempty"` 52 | RoundsAfter []float64 `json:"roundsAfter,omitempty"` 53 | Times []float64 `json:"times,omitempty"` 54 | } 55 | 56 | type RunStats struct { 57 | NHosts int 58 | Depth int 59 | 60 | BF int 61 | 62 | MinTime float64 63 | MaxTime float64 64 | AvgTime float64 65 | StdDev float64 66 | 67 | SysTime float64 68 | UserTime float64 69 | 70 | Rate float64 71 | Times []float64 72 | } 73 | 74 | func (s RunStats) CSVHeader() []byte { 75 | var buf bytes.Buffer 76 | buf.WriteString("hosts, depth, bf, min, max, avg, stddev, systime, usertime, rate\n") 77 | return buf.Bytes() 78 | } 79 | func (s RunStats) CSV() []byte { 80 | var buf bytes.Buffer 81 | fmt.Fprintf(&buf, "%d, %d, %d, %f, %f, %f, %f, %f, %f, %f\n", 82 | s.NHosts, 83 | s.Depth, 84 | s.BF, 85 | s.MinTime, 86 | s.MaxTime, 87 | s.AvgTime, 88 | s.StdDev, 89 | s.SysTime, 90 | s.UserTime, 91 | s.Rate) 92 | return buf.Bytes() 93 | } 94 | 95 | func (s RunStats) TimesCSV() []byte { 96 | times := bytes.Buffer{} 97 | times.WriteString("client_times\n") 98 | for _, t := range s.Times { 99 | times.WriteString(strconv.FormatFloat(t, 'f', 15, 64)) 100 | times.WriteRune('\n') 101 | } 102 | return times.Bytes() 103 | } 104 | 105 | func RunStatsAvg(rs []RunStats) RunStats { 106 | if len(rs) == 0 { 107 | return RunStats{} 108 | } 109 | r := RunStats{} 110 | r.NHosts = rs[0].NHosts 111 | r.Depth = rs[0].Depth 112 | r.BF = rs[0].BF 113 | r.Times = make([]float64, len(rs[0].Times)) 114 | 115 | for _, a := range rs { 116 | r.MinTime += a.MinTime 117 | r.MaxTime += a.MaxTime 118 | r.AvgTime += a.AvgTime 119 | r.StdDev += a.StdDev 120 | r.SysTime += a.SysTime 121 | r.UserTime += a.UserTime 122 | r.Rate += a.Rate 123 | r.Times = append(r.Times, a.Times...) 124 | } 125 | l := float64(len(rs)) 126 | r.MinTime /= l 127 | r.MaxTime /= l 128 | r.AvgTime /= l 129 | r.StdDev /= l 130 | r.SysTime /= l 131 | r.UserTime /= l 132 | r.Rate /= l 133 | return r 134 | } 135 | 136 | type ExpVar struct { 137 | Cmdline []string `json:"cmdline"` 138 | Memstats runtime.MemStats `json:"memstats"` 139 | } 140 | 141 | func Memstats(server string) (*ExpVar, error) { 142 | url := "localhost:8080/d/" + server + "/debug/vars" 143 | resp, err := http.Get(url) 144 | if err != nil { 145 | return nil, err 146 | } 147 | b, err := ioutil.ReadAll(resp.Body) 148 | resp.Body.Close() 149 | if err != nil { 150 | return nil, err 151 | } 152 | var evar ExpVar 153 | err = json.Unmarshal(b, &evar) 154 | if err != nil { 155 | log.Println("failed to unmarshal expvar:", string(b)) 156 | return nil, err 157 | } 158 | return &evar, nil 159 | } 160 | 161 | func MonitorMemStats(server string, poll int, done chan struct{}, stats *[]*ExpVar) { 162 | go func() { 163 | ticker := time.NewTicker(time.Duration(poll) * time.Millisecond) 164 | for { 165 | select { 166 | case <-ticker.C: 167 | evar, err := Memstats(server) 168 | if err != nil { 169 | continue 170 | } 171 | *stats = append(*stats, evar) 172 | case <-done: 173 | return 174 | } 175 | } 176 | }() 177 | } 178 | 179 | func ArrStats(stream []float64) (avg float64, min float64, max float64, stddev float64) { 180 | // truncate trailing 0s 181 | i := len(stream) - 1 182 | for ; i >= 0; i-- { 183 | if math.Abs(stream[i]) > 0.01 { 184 | break 185 | } 186 | } 187 | stream = stream[:i+1] 188 | 189 | k := float64(1) 190 | first := true 191 | var M, S float64 192 | for _, e := range stream { 193 | if first { 194 | first = false 195 | min = e 196 | max = e 197 | } 198 | if e < min { 199 | min = e 200 | } else if max < e { 201 | max = e 202 | } 203 | avg = ((avg * (k - 1)) + e) / k 204 | var tM = M 205 | M += (e - tM) / k 206 | S += (e - tM) * (e - M) 207 | k++ 208 | stddev = math.Sqrt(S / (k - 1)) 209 | } 210 | return avg, min, max, stddev 211 | } 212 | -------------------------------------------------------------------------------- /coco/coconet/tcpconn.go: -------------------------------------------------------------------------------- 1 | package coconet 2 | 3 | import ( 4 | "encoding/gob" 5 | "errors" 6 | "math/rand" 7 | "net" 8 | "sync" 9 | "time" 10 | 11 | log "github.com/Sirupsen/logrus" 12 | 13 | "github.com/dedis/crypto/abstract" 14 | ) 15 | 16 | var Latency = 100 17 | 18 | // TCPConn is an implementation of the Conn interface for TCP network connections. 19 | type TCPConn struct { 20 | // encLock guards the encoder and decoder and underlying conn. 21 | encLock sync.Mutex 22 | name string 23 | conn net.Conn 24 | enc *gob.Encoder 25 | dec *gob.Decoder 26 | 27 | // pkLock guards the public key 28 | pkLock sync.Mutex 29 | pubkey abstract.Point 30 | 31 | closed bool 32 | } 33 | 34 | // NewTCPConnFromNet wraps a net.Conn creating a new TCPConn using conn as the 35 | // underlying connection. 36 | // After creating a TCPConn in this fashion, it might be necessary to call SetName, 37 | // in order to give it an understandable name. 38 | func NewTCPConnFromNet(conn net.Conn) *TCPConn { 39 | return &TCPConn{ 40 | name: conn.RemoteAddr().String(), 41 | conn: conn, 42 | enc: gob.NewEncoder(conn), 43 | dec: gob.NewDecoder(conn)} 44 | 45 | } 46 | 47 | // NewTCPConn takes a hostname and creates TCPConn. 48 | // Before calling Get or Put Connect must first be called to establish the connection. 49 | func NewTCPConn(hostname string) *TCPConn { 50 | tp := &TCPConn{} 51 | tp.name = hostname 52 | return tp 53 | } 54 | 55 | func (tc *TCPConn) Closed() bool { 56 | tc.encLock.Lock() 57 | closed := tc.closed 58 | tc.encLock.Unlock() 59 | return closed 60 | } 61 | 62 | // Connect connects to the endpoint specified. 63 | func (tc *TCPConn) Connect() error { 64 | conn, err := net.Dial("tcp", tc.name) 65 | if err != nil { 66 | return err 67 | } 68 | tc.encLock.Lock() 69 | tc.conn = conn 70 | tc.enc = gob.NewEncoder(conn) 71 | tc.dec = gob.NewDecoder(conn) 72 | tc.encLock.Unlock() 73 | return nil 74 | } 75 | 76 | // SetName sets the name of the connection. 77 | func (tc *TCPConn) SetName(name string) { 78 | tc.name = name 79 | } 80 | 81 | // Name returns the name of the connection. 82 | func (tc *TCPConn) Name() string { 83 | return tc.name 84 | } 85 | 86 | // SetPubKey sets the public key. 87 | func (tc *TCPConn) SetPubKey(pk abstract.Point) { 88 | tc.pkLock.Lock() 89 | tc.pubkey = pk 90 | tc.pkLock.Unlock() 91 | } 92 | 93 | // PubKey returns the public key of this peer. 94 | func (tc *TCPConn) PubKey() abstract.Point { 95 | tc.pkLock.Lock() 96 | pl := tc.pubkey 97 | tc.pkLock.Unlock() 98 | return pl 99 | } 100 | 101 | // ErrNotEstablished indicates that the connection has not been successfully established 102 | // through a call to Connect yet. It does not indicate whether the failure was permanent or 103 | // temporary. 104 | var ErrNotEstablished = errors.New("connection not established") 105 | 106 | type temporary interface { 107 | Temporary() bool 108 | } 109 | 110 | // IsTemporary returns true if it is a temporary error. 111 | func IsTemporary(err error) bool { 112 | t, ok := err.(temporary) 113 | return ok && t.Temporary() 114 | } 115 | 116 | // Put puts data to the connection. 117 | // Returns io.EOF on an irrecoverable error. 118 | // Returns actual error if it is Temporary. 119 | func (tc *TCPConn) Put(bm BinaryMarshaler) error { 120 | if tc.Closed() { 121 | log.Errorln("tcpconn: put: connection closed") 122 | return ErrClosed 123 | } 124 | tc.encLock.Lock() 125 | if tc.enc == nil { 126 | tc.encLock.Unlock() 127 | return ErrNotEstablished 128 | } 129 | enc := tc.enc 130 | tc.encLock.Unlock() 131 | 132 | err := enc.Encode(bm) 133 | if err != nil { 134 | if IsTemporary(err) { 135 | return err 136 | } 137 | tc.Close() 138 | return ErrClosed 139 | } 140 | return err 141 | } 142 | 143 | // Get gets data from the connection. 144 | // Returns io.EOF on an irrecoveralbe error. 145 | // Returns given error if it is Temporary. 146 | func (tc *TCPConn) Get(bum BinaryUnmarshaler) error { 147 | if tc.Closed() { 148 | log.Errorln("tcpconn: get: connection closed") 149 | return ErrClosed 150 | } 151 | tc.encLock.Lock() 152 | for tc.dec == nil { 153 | tc.encLock.Unlock() 154 | return ErrNotEstablished 155 | } 156 | dec := tc.dec 157 | tc.encLock.Unlock() 158 | 159 | if Latency != 0 { 160 | time.Sleep(time.Duration(rand.Intn(Latency)) * time.Millisecond) 161 | } 162 | err := dec.Decode(bum) 163 | if err != nil { 164 | if IsTemporary(err) { 165 | return err 166 | } 167 | // if it is an irrecoverable error 168 | // close the channel and return that it has been closed 169 | tc.Close() 170 | return ErrClosed 171 | } 172 | return err 173 | } 174 | 175 | // Close closes the connection. 176 | func (tc *TCPConn) Close() { 177 | log.Errorln("tcpconn: closing connection") 178 | tc.encLock.Lock() 179 | defer tc.encLock.Unlock() 180 | if tc.conn != nil { 181 | // ignore error becuase only other possibility was an invalid 182 | // connection. but we don't care if we close a connection twice. 183 | tc.conn.Close() 184 | } 185 | tc.closed = true 186 | tc.conn = nil 187 | tc.enc = nil 188 | tc.dec = nil 189 | } 190 | -------------------------------------------------------------------------------- /python/verdict.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import random 4 | import unittest 5 | 6 | from Crypto.Hash import SHA256 7 | from Crypto.Util.number import long_to_bytes, bytes_to_long 8 | 9 | import schnorr 10 | from elgamal import PublicKey, PrivateKey 11 | 12 | class BaseVerdict: 13 | def __init__(self, client_keys, trustee_keys): 14 | self.group = trustee_keys[0].group 15 | self.client_keys = client_keys 16 | self.trustee_keys = trustee_keys 17 | 18 | def generate_ciphertext(self, generator, data = None): 19 | encrypted = self.group.multiply(generator, self._shared_secret) 20 | if data != None: 21 | data = self.group.encode(data) 22 | encrypted = self.group.add(data, encrypted) 23 | return encrypted 24 | 25 | def set_commitments(self, client_commitments, trustee_commitments): 26 | assert len(client_commitments) == len(self.client_keys) 27 | assert len(trustee_commitments) == len(self.trustee_keys) 28 | self.ccommit = client_commitments 29 | self.tcommit = trustee_commitments 30 | 31 | def commitment(self): 32 | return self.secret_commit 33 | 34 | class TrusteeVerdict(BaseVerdict): 35 | def __init__(self, ss_or_key, client_keys, trustee_keys, ss = False): 36 | BaseVerdict.__init__(self, client_keys, trustee_keys) 37 | 38 | if ss: 39 | self._shared_secret = ss_or_key 40 | else: 41 | self._shared_secret = self.group.zero() 42 | for idx in range(len(client_keys)): 43 | self._shared_secret = (self._shared_secret - ss_or_key.exchange(client_keys[idx])) % self.group.q 44 | self.secret_commit = self.group.multiply(self.group.generator(), self._shared_secret) 45 | 46 | def shared_secret(self): 47 | return self._shared_secret 48 | 49 | 50 | class ClientVerdict(BaseVerdict): 51 | def __init__(self, key, client_keys, trustee_keys): 52 | BaseVerdict.__init__(self, client_keys, trustee_keys) 53 | 54 | self._shared_secret = self.group.zero() 55 | index = -1 56 | for idx in range(len(trustee_keys)): 57 | ks = trustee_keys[idx] 58 | if index == -1 and ks == key.element: 59 | index = idx 60 | continue 61 | self._shared_secret = (self._shared_secret + key.exchange(trustee_keys[idx])) % self.group.q 62 | self.secret_commit = self.group.multiply(self.group.generator(), self._shared_secret) 63 | 64 | class Test(unittest.TestCase): 65 | def test_basic(self): 66 | group = schnorr.verdict_1024() 67 | self.basic(group) 68 | 69 | def gen_keys(self, group, count): 70 | keys = [] 71 | pkeys = [] 72 | 73 | for idx in range(count): 74 | k = PrivateKey(group) 75 | keys.append(k) 76 | pkeys.append(k.public_key()) 77 | 78 | return (keys, pkeys) 79 | 80 | def setup(self, group, clients, trustees): 81 | ckeys, cpkeys = self.gen_keys(group, clients) 82 | tkeys, tpkeys = self.gen_keys(group, trustees) 83 | 84 | cverdicts = [] 85 | ccommitments = [] 86 | 87 | for idx in range(clients): 88 | cverdicts.append(ClientVerdict(ckeys[idx], cpkeys, tpkeys)) 89 | ccommitments.append(cverdicts[-1].commitment()) 90 | 91 | tverdicts = [] 92 | tcommitments = [] 93 | 94 | for idx in range(trustees): 95 | tverdicts.append(TrusteeVerdict(tkeys[idx], cpkeys, tpkeys)) 96 | tcommitments.append(tverdicts[-1].commitment()) 97 | 98 | for verdict in cverdicts: 99 | verdict.set_commitments(ccommitments, tcommitments) 100 | 101 | for verdict in tverdicts: 102 | verdict.set_commitments(ccommitments, tcommitments) 103 | 104 | return (cverdicts, tverdicts) 105 | 106 | def basic(self, group): 107 | trustees = 3 108 | clients = 10 109 | owner_idx = random.randrange(0, clients) 110 | 111 | cverdicts, tverdicts = self.setup(group, clients, trustees) 112 | 113 | msg = bytes("hello world", "UTF-8") 114 | h = SHA256.new() 115 | h.update(msg) 116 | generator = pow(group.g, bytes_to_long(h.digest()), group.p) 117 | 118 | cciphertexts = [] 119 | cproofs = [] 120 | 121 | for idx in range(clients): 122 | if idx == owner_idx: 123 | cciphertexts.append(cverdicts[idx].generate_ciphertext(generator, msg)) 124 | else: 125 | cciphertexts.append(cverdicts[idx].generate_ciphertext(generator)) 126 | 127 | tciphertexts = [] 128 | for idx in range(trustees): 129 | tciphertexts.append(tverdicts[idx].generate_ciphertext(generator)) 130 | 131 | cleartext = group.identity() 132 | for ciphertext in tciphertexts: 133 | cleartext = group.add(cleartext, ciphertext) 134 | 135 | for ciphertext in cciphertexts: 136 | cleartext = group.add(cleartext, ciphertext) 137 | 138 | self.assertEqual(msg, group.decode(cleartext)) 139 | 140 | if __name__ == "__main__": 141 | unittest.main() 142 | -------------------------------------------------------------------------------- /coco/test/timeclient/stampclient/stampclient.go: -------------------------------------------------------------------------------- 1 | package stampclient 2 | 3 | import ( 4 | "crypto/rand" 5 | "io" 6 | "net" 7 | "strconv" 8 | "strings" 9 | "sync" 10 | "sync/atomic" 11 | "time" 12 | 13 | log "github.com/Sirupsen/logrus" 14 | 15 | "github.com/dedis/prifi/coco/coconet" 16 | "github.com/dedis/prifi/coco/hashid" 17 | "github.com/dedis/prifi/coco/stamp" 18 | "github.com/dedis/prifi/coco/test/logutils" 19 | ) 20 | 21 | func genRandomMessages(n int) [][]byte { 22 | msgs := make([][]byte, n) 23 | for i := range msgs { 24 | msgs[i] = make([]byte, hashid.Size) 25 | _, err := rand.Read(msgs[i]) 26 | if err != nil { 27 | log.Fatal("failed to generate random commit:", err) 28 | } 29 | } 30 | return msgs 31 | } 32 | 33 | func removeTrailingZeroes(a []int64) []int64 { 34 | i := len(a) - 1 35 | for ; i >= 0; i-- { 36 | if a[i] != 0 { 37 | break 38 | } 39 | } 40 | return a[:i+1] 41 | } 42 | 43 | var muStats sync.Mutex 44 | 45 | func AggregateStats(buck, roundsAfter, times []int64) string { 46 | muStats.Lock() 47 | log.WithFields(log.Fields{ 48 | "file": logutils.File(), 49 | "type": "client_msg_stats", 50 | "buck": removeTrailingZeroes(buck), 51 | "roundsAfter": removeTrailingZeroes(roundsAfter), 52 | "times": removeTrailingZeroes(times), 53 | }).Info("") 54 | muStats.Unlock() 55 | return "Client Finished Aggregating Statistics" 56 | } 57 | 58 | func streamMessgs(c *stamp.Client, servers []string, rate int) { 59 | //log.Println("STREAMING: GIVEN RATE") 60 | // buck[i] = # of timestamp responses received in second i 61 | buck := make([]int64, MAX_N_SECONDS) 62 | // roundsAfter[i] = # of timestamp requests that were processed i rounds late 63 | roundsAfter := make([]int64, MAX_N_ROUNDS) 64 | times := make([]int64, MAX_N_SECONDS*1000) // maximum number of milliseconds (maximum rate > 1 per millisecond) 65 | ticker := time.Tick(time.Duration(rate) * time.Millisecond) 66 | msg := genRandomMessages(1)[0] 67 | i := 0 68 | nServers := len(servers) 69 | 70 | retry: 71 | err := c.TimeStamp(msg, servers[0]) 72 | if err == io.EOF || err == coconet.ErrClosed { 73 | log.Fatal(AggregateStats(buck, roundsAfter, times)) 74 | } else if err == stamp.ErrClientToTSTimeout { 75 | log.Errorln(err) 76 | } else if err != nil { 77 | time.Sleep(500 * time.Millisecond) 78 | goto retry 79 | } 80 | 81 | tFirst := time.Now() 82 | 83 | // every tick send a time stamp request to every server specified 84 | // this will stream until we get an EOF 85 | tick := 0 86 | for _ = range ticker { 87 | tick += 1 88 | go func(msg []byte, s string, tick int) { 89 | t0 := time.Now() 90 | err := c.TimeStamp(msg, s) 91 | t := time.Since(t0) 92 | 93 | if err == io.EOF || err == coconet.ErrClosed { 94 | log.Printf("CLIENT DONE: terminating") 95 | log.Fatal(AggregateStats(buck, roundsAfter, times)) 96 | } else if err != nil { 97 | // ignore errors 98 | return 99 | } 100 | 101 | // TODO: we might want to subtract a buffer from secToTimeStamp 102 | // to account for computation time 103 | secToTimeStamp := t.Seconds() 104 | secSinceFirst := time.Since(tFirst).Seconds() 105 | atomic.AddInt64(&buck[int(secSinceFirst)], 1) 106 | index := int(secToTimeStamp) / int(stamp.ROUND_TIME/time.Second) 107 | atomic.AddInt64(&roundsAfter[index], 1) 108 | atomic.AddInt64(×[tick], t.Nanoseconds()) 109 | 110 | }(msg, servers[i], tick) 111 | 112 | i = (i + 1) % nServers 113 | } 114 | 115 | } 116 | 117 | var MAX_N_SECONDS int = 1 * 60 * 60 // 1 hours' worth of seconds 118 | var MAX_N_ROUNDS int = MAX_N_SECONDS / int(stamp.ROUND_TIME/time.Second) 119 | 120 | func Run(server string, nmsgs int, name string, rate int, debug bool) { 121 | c := stamp.NewClient(name) 122 | msgs := genRandomMessages(nmsgs + 20) 123 | servers := strings.Split(server, ",") 124 | 125 | // connect to all the servers listed 126 | for _, s := range servers { 127 | h, p, err := net.SplitHostPort(s) 128 | if err != nil { 129 | log.Fatal("improperly formatted host") 130 | } 131 | pn, _ := strconv.Atoi(p) 132 | c.AddServer(s, coconet.NewTCPConn(net.JoinHostPort(h, strconv.Itoa(pn+1)))) 133 | } 134 | 135 | // if rate specified send out one message every rate milliseconds 136 | if rate > 0 { 137 | // Stream time stamp requests 138 | streamMessgs(c, servers, rate) 139 | return 140 | } 141 | 142 | // rounds based messaging 143 | r := 0 144 | s := 0 145 | 146 | // ROUNDS BASED IS DEPRECATED 147 | log.Fatal("ROUNDS BASED RATE LIMITING DEPRECATED") 148 | for { 149 | //start := time.Now() 150 | var wg sync.WaitGroup 151 | for i := 0; i < nmsgs; i++ { 152 | wg.Add(1) 153 | go func(i, s int) { 154 | defer wg.Done() 155 | err := c.TimeStamp(msgs[i], servers[s]) 156 | if err == io.EOF { 157 | log.WithFields(log.Fields{ 158 | "file": logutils.File(), 159 | "type": "client_msg_stats", 160 | "buck": make([]int64, 0), 161 | "roundsAfter": make([]int64, 0), 162 | }).Info("") 163 | 164 | log.Fatal("EOF: terminating time client") 165 | } 166 | }(i, s) 167 | s = (s + 1) % len(servers) 168 | } 169 | wg.Wait() 170 | //elapsed := time.Since(start) 171 | log.Println("client done with round") 172 | //log.WithFields(log.Fields{ 173 | //"file": logutils.File(), 174 | //"type": "client_round", 175 | //"round": r, 176 | //"time": elapsed, 177 | //}).Info("client round") 178 | r++ 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /tree/tree.go: -------------------------------------------------------------------------------- 1 | // Scalable tree protocols. 2 | // All interaction is between immediate parent and children in a tree. 3 | // One "round-trip" through the tree starts at (is initiated by) the root, 4 | // propagates downward to the leaves, then back up to the root. 5 | // Each node can tolerate a limited number of failures among its descendants, 6 | // before it must "fail" itself (or simply pause or halt if it's the root). 7 | package tree 8 | 9 | import ( 10 | "bytes" 11 | "crypto/cipher" 12 | "encoding/binary" 13 | "github.com/dedis/crypto/abstract" 14 | ) 15 | 16 | 17 | 18 | 19 | 20 | // Each node maintains _one_ tamper-evident log of events it generates. 21 | // Each node must ensure that its event history remains linear 22 | // (i.e., does not fork or roll back). 23 | // One event history is shared among all trees the node participates in. 24 | // 25 | // Each tree requires the node to produce new events 26 | // within certain time-windows determined by the node's ancestor(s) in the tree. 27 | // If the next-event time windows for multiple trees overlap, 28 | // the node can produce one event to "satisfy" several overlapping trees. 29 | // If a node fails to produce an event for a tree in the given time window, 30 | // the node is considered to have "failed" during the corresponding tree step. 31 | // 32 | // Each node, when it receives a new event from a peer, 33 | // verifies that the new event is a strict successor to the last one, 34 | // and does not "fork" the peer's event history for example. 35 | // Each node's metadata included in a new event includes skip-chain info, 36 | // allowing other nodes to verify small or large steps forward in history 37 | // without having to store or traverse many intervening nodes in the chain. 38 | 39 | type event struct { 40 | seq uint64 41 | id HashId 42 | pred []HashId 43 | data bytes.Buffer 44 | } 45 | 46 | 47 | type log struct { 48 | suite abstract.Suite 49 | cur *event // current event under construction 50 | } 51 | 52 | func (l *log) init(suite abstract.Suite) { 53 | l.suite = suite 54 | } 55 | 56 | // Creates a new, un-finalized event, closing out the last current one if any. 57 | // The caller can write bytes to the data buffer before finalizing. 58 | func (l *log) newEvent() *event { 59 | e := &event{} 60 | 61 | // Close out the immediate predecessor event 62 | ip := l.cur // immediate predecessor 63 | var pred []HashId 64 | if ip != nil { 65 | 66 | // Compute the immediate predecessor's event ID 67 | h := l.suite.Hash() 68 | h.Write(ip.data.Bytes()) 69 | ip.id = HashId(h.Sum(nil)) 70 | 71 | // Start with a copy of our predecessor's predecessor list 72 | pred = make([]HashId, len(ip.pred)) 73 | copy(pred, ip.pred) 74 | 75 | // Incorporate immediate predecessor into predecessor list 76 | iplev := ip.id.Level() 77 | for i := 0; i <= iplev; i++ { 78 | if i < len(pred) { 79 | pred[i] = ip.id 80 | } else { 81 | pred = append(pred, ip.id) 82 | } 83 | } 84 | 85 | // Initialize the new event appropriately 86 | e.seq = ip.seq + 1 87 | } 88 | e.pred = pred 89 | 90 | // Write the new event's header 91 | binary.Write(&e.data, binary.LittleEndian, e.seq) 92 | binary.Write(&e.data, binary.LittleEndian, e.pred) 93 | 94 | l.cur = e 95 | return e 96 | } 97 | 98 | 99 | 100 | // treeNode represents a host's participation on a particular tree 101 | type treeNode struct { 102 | suite abstract.Suite 103 | 104 | // Host identities of ancestor hosts forming a path 105 | // from the root (path[0]) down to but not including us. 106 | // len(path) is our depth in the tree, 0 if we are the root. 107 | path []HashId 108 | 109 | // peers[0] is our parent, the rest are our children. 110 | // peers[0] is nil if we're the root of the tree. 111 | peers []*peer 112 | 113 | // This node's distance from the root of the tree. 114 | dist int 115 | } 116 | 117 | 118 | /* 119 | func newNode(suite abstract.Suite, rand cipher.Stream, 120 | parpub abstract.Point) *treeNode { 121 | 122 | n := &treeNode{} 123 | n.suite = suite 124 | 125 | parent := (*peer)(nil) 126 | if parpub != nil { 127 | parent = &peer{parpub} 128 | } 129 | n.peers = []*peer{parent} 130 | 131 | return n 132 | } 133 | 134 | func (n *node) addChild(childpub abstract.Point) { 135 | n.peers = append(n.peers, &peer{childpub}) 136 | } 137 | 138 | func (n *node) downStep() { 139 | } 140 | 141 | func (n *node) upStep() { 142 | } 143 | */ 144 | 145 | 146 | 147 | // peer represents the information a host maintains about 148 | // any peer host with whom it maintains direct communication. 149 | type peer struct { 150 | pub abstract.Point // peer's public key 151 | id HashId // hash of peer's public key 152 | } 153 | 154 | // host embodies the local state of a single host in the network 155 | type host struct { 156 | name string // our human-readable hostname 157 | log // our tamper-evident log 158 | 159 | pri abstract.Secret // our private key 160 | pub abstract.Point // our public key 161 | id HashId // hash of our public key 162 | 163 | peers map[string]*peer // peers indexed by string(HashId) 164 | trees map[string]*treeNode // trees indexed by string(HashId) 165 | } 166 | 167 | func newHost(suite abstract.Suite, rand cipher.Stream, hostname string) *host { 168 | h := &host{} 169 | h.name = hostname 170 | h.log.init(suite) 171 | 172 | h.pri = suite.Secret().Pick(rand) 173 | h.pub = suite.Point().Mul(nil, h.pri) 174 | h.id = abstract.HashBytes(suite, h.pub.Encode()) 175 | 176 | h.peers = make(map[string]*peer) 177 | h.trees = make(map[string]*treeNode) 178 | 179 | return h 180 | } 181 | 182 | func (h *host) addPeer(pub abstract.Point, id HashId) *peer { 183 | sid := string(id) 184 | if p := h.peers[sid]; p != nil { 185 | return p // already added 186 | } 187 | p := &peer{pub,id} 188 | h.peers[sid] = p 189 | return p 190 | } 191 | 192 | --------------------------------------------------------------------------------