diff --git a/config/config.yml b/config/config.yml index 620779e..72340c6 100755 --- a/config/config.yml +++ b/config/config.yml @@ -2,7 +2,7 @@ server_load: 7 heartbeat_interval: 5 min_nodes: 2 min_total_messages: 1 -num_rounds: 4 +num_rounds: 5 max_bruises: 2 epsilon: 0.1 delta: 1e-5 diff --git a/internal/api/clientStatus.go b/internal/api/clientStatus.go index ec544d5..419e0ec 100644 --- a/internal/api/clientStatus.go +++ b/internal/api/clientStatus.go @@ -37,7 +37,7 @@ func (cs *ClientStatus) AddSent(clientReceiver PublicNodeApi, routingPath []Publ TimeSent: time.Now(), }) - slog.Info(PrettyLogger.GetFuncName(), "message", message) + // slog.Info(PrettyLogger.GetFuncName(), "message", message) } func (cs *ClientStatus) AddReceived(message Message) { diff --git a/internal/api/message.go b/internal/api/message.go index a2ac771..70f6b0e 100644 --- a/internal/api/message.go +++ b/internal/api/message.go @@ -2,7 +2,6 @@ package api import ( "github.com/HannahMarsh/pi_t-experiment/pkg/utils" - "golang.org/x/exp/slog" ) type Message struct { @@ -13,11 +12,7 @@ type Message struct { } func NewMessage(from, to, msg string) Message { - h, err := utils.GenerateUniqueHash() - if err != nil { - slog.Error("failed to generate unique hash", err) - h = "" - } + h := utils.GenerateUniqueHash() return Message{ From: from, To: to, diff --git a/internal/api/nodeStatus.go b/internal/api/nodeStatus.go index e17ddbb..a732358 100644 --- a/internal/api/nodeStatus.go +++ b/internal/api/nodeStatus.go @@ -8,9 +8,11 @@ import ( ) type NodeStatus struct { - Received []OnionStatus - Node PublicNodeApi - mu sync.RWMutex + Received []OnionStatus + Node PublicNodeApi + CheckpointOnionsReceived map[int]int + ExpectedCheckpoints map[int]int + mu sync.RWMutex } type OnionStatus struct { @@ -22,9 +24,38 @@ type OnionStatus struct { TimeReceived time.Time Bruises int Dropped bool + NonceVerification bool + ExpectCheckPoint bool } -func (ns *NodeStatus) AddOnion(lastHop, thisAddress, nextHop string, layer int, isCheckPointOnion bool, bruises int, dropped bool) { +func NewNodeStatus(id int, address, publicKey string, isMixer bool) *NodeStatus { + return &NodeStatus{ + Received: make([]OnionStatus, 0), + Node: PublicNodeApi{ + ID: id, + Address: address, + PublicKey: publicKey, + Time: time.Now(), + IsMixer: isMixer, + }, + CheckpointOnionsReceived: make(map[int]int), + ExpectedCheckpoints: make(map[int]int), + } +} + +func (ns *NodeStatus) AddCheckpointOnion(layer int) { + ns.mu.Lock() + defer ns.mu.Unlock() + ns.CheckpointOnionsReceived[layer]++ +} + +func (ns *NodeStatus) AddExpectedCheckpoint(layer int) { + ns.mu.Lock() + defer ns.mu.Unlock() + ns.ExpectedCheckpoints[layer]++ +} + +func (ns *NodeStatus) AddOnion(lastHop, thisAddress, nextHop string, layer int, isCheckPointOnion bool, bruises int, dropped bool, nonceVerification bool, expectCheckPoint bool) { ns.mu.Lock() defer ns.mu.Unlock() ns.Received = append(ns.Received, OnionStatus{ @@ -36,6 +67,8 @@ func (ns *NodeStatus) AddOnion(lastHop, thisAddress, nextHop string, layer int, TimeReceived: time.Now(), Bruises: bruises, Dropped: dropped, + NonceVerification: nonceVerification, + ExpectCheckPoint: expectCheckPoint, }) } diff --git a/internal/model/client/client.go b/internal/model/client/client.go index 0d48b67..8900694 100644 --- a/internal/model/client/client.go +++ b/internal/model/client/client.go @@ -176,6 +176,16 @@ func DetermineRoutingPath(pathLength int, participants []api.PublicNodeApi) ([]a return selectedNodes, nil } +func DetermineCheckpointRoutingPath(pathLength int, nodes []api.PublicNodeApi, participatingClients []api.PublicNodeApi, checkpointReceiver api.PublicNodeApi, round int) ([]api.PublicNodeApi, error) { + path, err := DetermineRoutingPath(pathLength-1, utils.Remove(nodes, func(p api.PublicNodeApi) bool { + return p.Address == checkpointReceiver.Address + })) + if err != nil { + return nil, pl.WrapError(err, "failed to determine routing path") + } + return append(utils.InsertAtIndex(path, round, checkpointReceiver), utils.RandomElement(participatingClients)), nil +} + func (c *Client) formOnions(start api.StartRunApi) (map[string][]api.OnionApi, error) { onions := make(map[string][]api.OnionApi) @@ -187,26 +197,26 @@ func (c *Client) formOnions(start api.StartRunApi) (map[string][]api.OnionApi, e return node.Address != c.Adddress && node.Address != "" }) - numMessagesToSend := make(map[string]int) - - for _, msg := range c.Messages { - if _, found := numMessagesToSend[msg.To]; !found { - numMessagesToSend[msg.To] = 0 - } - numMessagesToSend[msg.To]++ - } - - dummyNum := 0 - - for addr, numMessages := range numMessagesToSend { - if numMessages < start.NumMessagesPerClient { - numDummyNeeded := start.NumMessagesPerClient - numMessages - for i := 0; i < numDummyNeeded; i++ { - c.Messages = append(c.Messages, api.NewMessage(c.Adddress, addr, fmt.Sprintf("dummy%d", dummyNum))) - dummyNum++ - } - } - } + //numMessagesToSend := make(map[string]int) + // + //for _, msg := range c.Messages { + // if _, found := numMessagesToSend[msg.To]; !found { + // numMessagesToSend[msg.To] = 0 + // } + // numMessagesToSend[msg.To]++ + //} + // + //dummyNum := 0 + // + //for addr, numMessages := range numMessagesToSend { + // if numMessages < start.NumMessagesPerClient { + // numDummyNeeded := start.NumMessagesPerClient - numMessages + // for i := 0; i < numDummyNeeded; i++ { + // c.Messages = append(c.Messages, api.NewMessage(c.Adddress, addr, fmt.Sprintf("dummy%d", dummyNum))) + // dummyNum++ + // } + // } + //} for _, msg := range c.Messages { if destination, found := utils.Find(start.ParticipatingClients, api.PublicNodeApi{}, func(client api.PublicNodeApi) bool { @@ -228,13 +238,56 @@ func (c *Client) formOnions(start api.StartRunApi) (map[string][]api.OnionApi, e return node.Address }) slog.Info("routing path", "path", addresses) - if addr, onion, err3 := pi_t.FormOnion(c.PrivateKey, c.PublicKey, msgString, publicKeys, addresses, -1); err3 != nil { + if addr, onion, checkpoints, err3 := pi_t.FormOnion(c.PrivateKey, c.PublicKey, msgString, publicKeys, addresses, -1); err3 != nil { return nil, pl.WrapError(err3, "failed to create onion") } else { + // generate checkpoint onions + for i, node := range routingPath { + if checkpoints[i] { + path, err5 := DetermineCheckpointRoutingPath(config.GlobalConfig.Rounds, nodes, utils.Filter(start.ParticipatingClients, func(publicNodeApi api.PublicNodeApi) bool { + return publicNodeApi.Address != c.Adddress && publicNodeApi.Address != "" + }), node, i) + if err5 != nil { + return nil, pl.WrapError(err5, "failed to determine checkpoint routing path") + } + + checkPointPublicKeys := utils.Map(path, func(node api.PublicNodeApi) string { + return node.PublicKey + }) + checkPointAddresses := utils.Map(path, func(node api.PublicNodeApi) string { + return node.Address + }) + + dummyMsg := api.Message{ + From: c.Adddress, + To: utils.GetLast(path).Address, + Msg: "checkpoint onion", + Hash: utils.GenerateUniqueHash(), + } + dummyPayload, err6 := json.Marshal(dummyMsg) + if err6 != nil { + return nil, pl.WrapError(err6, "failed to marshal dummy message") + } + if firstHop, checkpointOnion, _, err4 := pi_t.FormOnion(c.PrivateKey, c.PublicKey, dummyPayload, checkPointPublicKeys, checkPointAddresses, -i); err4 != nil { + return nil, pl.WrapError(err4, "failed to create onion") + } else { + if _, present := onions[firstHop]; !present { + onions[firstHop] = make([]api.OnionApi, 0) + } + onions[firstHop] = append(onions[firstHop], api.OnionApi{ + Onion: checkpointOnion, + From: c.Adddress, + To: firstHop, + }) + c.status.AddSent(utils.GetLast(path), routingPath, dummyMsg) + } + } + } + if _, present := onions[addr]; !present { onions[addr] = make([]api.OnionApi, 0) } - onions[addr] = append(onions[msg.To], api.OnionApi{ + onions[addr] = append(onions[addr], api.OnionApi{ Onion: onion, From: c.Adddress, To: addr, @@ -300,7 +353,7 @@ func (c *Client) startRun(start api.StartRunApi) (bool, error) { } func (c *Client) Receive(o string) error { - if peeled, bruises, err := pi_t.PeelOnion(o, c.PrivateKey); err != nil { + if peeled, bruises, _, _, err := pi_t.PeelOnion(o, c.PrivateKey); err != nil { return pl.WrapError(err, "node.Receive(): failed to remove layer") } else { slog.Info("Client received onion", "bruises", bruises, "from", peeled.LastHop, "to", peeled.NextHop, "layer", peeled.Layer, "is_checkpoint_onion", peeled.IsCheckpointOnion) diff --git a/internal/model/node/node.go b/internal/model/node/node.go index 243897e..1ad7266 100644 --- a/internal/model/node/node.go +++ b/internal/model/node/node.go @@ -19,16 +19,18 @@ import ( // Node represents a node in the onion routing network type Node struct { - ID int - Host string - Port int - PrivateKey string - PublicKey string - isMixer bool - mu sync.RWMutex - BulletinBoardUrl string - lastUpdate time.Time - status *api.NodeStatus + ID int + Host string + Port int + PrivateKey string + PublicKey string + isMixer bool + mu sync.RWMutex + BulletinBoardUrl string + lastUpdate time.Time + status *api.NodeStatus + checkpoints map[int]int + expectedCheckpoints map[int]int } // NewNode creates a new node @@ -37,23 +39,16 @@ func NewNode(id int, host string, port int, bulletinBoardUrl string, isMixer boo return nil, pl.WrapError(err, "node.NewClient(): failed to generate key pair") } else { n := &Node{ - ID: id, - Host: host, - Port: port, - PublicKey: publicKey, - PrivateKey: privateKey, - BulletinBoardUrl: bulletinBoardUrl, - isMixer: isMixer, - status: &api.NodeStatus{ - Received: make([]api.OnionStatus, 0), - Node: api.PublicNodeApi{ - ID: id, - Address: fmt.Sprintf("http://%s:%d", host, port), - PublicKey: publicKey, - Time: time.Now(), - IsMixer: isMixer, - }, - }, + ID: id, + Host: host, + Port: port, + PublicKey: publicKey, + PrivateKey: privateKey, + BulletinBoardUrl: bulletinBoardUrl, + isMixer: isMixer, + status: api.NewNodeStatus(id, fmt.Sprintf("http://%s:%d", host, port), publicKey, isMixer), + checkpoints: make(map[int]int), + expectedCheckpoints: make(map[int]int), } if err2 := n.RegisterWithBulletinBoard(); err2 != nil { return nil, pl.WrapError(err2, "node.NewNode(): failed to register with bulletin board") @@ -103,42 +98,42 @@ func (n *Node) startRun(start api.StartRunApi) (didParticipate bool, e error) { } func (n *Node) Receive(o string) error { - if peeled, bruises, err := pi_t.PeelOnion(o, n.PrivateKey); err != nil { + if peeled, bruises, nonceVerification, expectCheckpoint, err := pi_t.PeelOnion(o, n.PrivateKey); err != nil { return pl.WrapError(err, "node.Receive(): failed to remove layer") } else { + n.mu.Lock() + defer n.mu.Unlock() - if peeled.NextHop == "" { - n.status.AddOnion(peeled.LastHop, fmt.Sprintf("http://%s:%d", n.Host, n.Port), peeled.NextHop, peeled.Layer, peeled.IsCheckpointOnion, bruises, true) - var msg api.Message - if err2 := json.Unmarshal([]byte(peeled.Payload), &msg); err2 != nil { - return pl.WrapError(err2, "node.Receive(): failed to unmarshal message") - } - slog.Info("Received message", "from", msg.From, "to", msg.To, "msg", msg.Msg) + if expectCheckpoint { + n.expectedCheckpoints[peeled.Layer]++ + n.status.AddExpectedCheckpoint(peeled.Layer) + } + if peeled.IsCheckpointOnion { + n.checkpoints[peeled.Layer]++ + n.status.AddCheckpointOnion(peeled.Layer) + } + slog.Info("Received onion", "layer", peeled.Layer, "destination", peeled.NextHop, "nonceVerification", nonceVerification, "expectCheckpoint", expectCheckpoint) - } else { - if !n.isMixer && bruises > config.GlobalConfig.MaxBruises { - n.status.AddOnion(peeled.LastHop, fmt.Sprintf("http://%s:%d", n.Host, n.Port), peeled.NextHop, peeled.Layer, peeled.IsCheckpointOnion, bruises, true) - slog.Info("Dropping onion", "layer", peeled.Layer, "destination", peeled.NextHop) - return nil - } else { - n.status.AddOnion(peeled.LastHop, fmt.Sprintf("http://%s:%d", n.Host, n.Port), peeled.NextHop, peeled.Layer, peeled.IsCheckpointOnion, bruises, false) + if !nonceVerification { + bruises += 1 + } - if peeled.IsCheckpointOnion { - slog.Info("Received checkpoint onion", "layer", peeled.Layer, "destination", peeled.NextHop) - } else { - slog.Info("Received onion", "layer", peeled.Layer, "destination", peeled.NextHop) - } - //bruised, err2 := pi_t.BruiseOnion(payload) - //if err2 != nil { - // return pl.WrapError(err2, "node.Receive(): failed to bruise onion") - //} - addedHeader, err4 := pi_t.AddHeader(peeled, bruises, n.PrivateKey, n.PublicKey) - if err4 != nil { - return pl.WrapError(err4, "node.Receive(): failed to add header") - } - if err3 := sendToNode(peeled.NextHop, addedHeader); err != nil { - return pl.WrapError(err3, "node.Receive(): failed to send to next node") - } + if !n.isMixer && bruises > config.GlobalConfig.MaxBruises { + + n.status.AddOnion(peeled.LastHop, fmt.Sprintf("http://%s:%d", n.Host, n.Port), peeled.NextHop, peeled.Layer, peeled.IsCheckpointOnion, bruises, true, nonceVerification, expectCheckpoint) + slog.Info("Dropping onion", "layer", peeled.Layer, "destination", peeled.NextHop) + return nil + + } else if !peeled.IsCheckpointOnion { + + n.status.AddOnion(peeled.LastHop, fmt.Sprintf("http://%s:%d", n.Host, n.Port), peeled.NextHop, peeled.Layer, peeled.IsCheckpointOnion, bruises, false, nonceVerification, expectCheckpoint) + + addedHeader, err4 := pi_t.AddHeader(peeled, bruises, n.PrivateKey, n.PublicKey) + if err4 != nil { + return pl.WrapError(err4, "node.Receive(): failed to add header") + } + if err3 := sendToNode(peeled.NextHop, addedHeader); err != nil { + return pl.WrapError(err3, "node.Receive(): failed to send to next node") } } } diff --git a/internal/pi_t/README.md b/internal/pi_t/README.md index 1ee0ae5..6f05851 100644 --- a/internal/pi_t/README.md +++ b/internal/pi_t/README.md @@ -1,10 +1,10 @@ # Directory: `/internal/pi_t` -- This directory contains the implementation of the Onion Routing protocol, `pi_t`. The key components are: +- This directory contains the implementation of several Onion Routing functions from Pi_t. The key components are: - [`pi_t_functions.go`](pi_t_functions.go): Functions to form and peel onion layers. - - `keys`: HELPER package for key generation and encryption/decryption. - - `prf`: Package for Pseudo-Random Functions (PRF) used in the protocol. + - [`keys`](keys): Helper package for key generation and encryption/decryption. + - [`prf`](prf): Package for Pseudo-Random Functions (PRF) used in the protocol. ## Usage diff --git a/internal/pi_t/pi_t_functions.go b/internal/pi_t/pi_t_functions.go index 079aa47..71b6730 100644 --- a/internal/pi_t/pi_t_functions.go +++ b/internal/pi_t/pi_t_functions.go @@ -53,7 +53,15 @@ type Header struct { // - The destination node identifier. // - The base64-encoded onion with the added header. // - An error object if an error occurred, otherwise nil. -func FormOnion(privateKeyPEM string, publicKeyPEM string, payload []byte, publicKeys []string, routingPath []string, checkpoint int) (string, string, error) { +func FormOnion(privateKeyPEM string, publicKeyPEM string, payload []byte, publicKeys []string, routingPath []string, checkpoint int) (string, string, []bool, error) { + var sendCheckPoints []bool + if checkpoint <= 0 { + sendCheckPoints = make([]bool, len(publicKeys)) + for i := range sendCheckPoints { + sendCheckPoints[i] = false + } + } + for i := len(publicKeys) - 1; i >= 0; i-- { var layerBytes []byte var err error @@ -74,16 +82,22 @@ func FormOnion(privateKeyPEM string, publicKeyPEM string, payload []byte, public NextHopPubKey: publicKeys[i+1], } - // Use PRF_F1 to determine if a checkpoint onion is expected - checkpointExpected := prf.PRF_F1(privateKeyPEM, publicKeys[i], i) var nonce []byte - if checkpointExpected == 0 { - nonce = prf.PRF_F2(privateKeyPEM, publicKeys[i], i) - } else { + if checkpoint <= 0 { + // Use PRF_F1 to determine if a checkpoint onion is expected + checkpointExpected := prf.PRF_F1(privateKeyPEM, publicKeys[i], i) + if checkpointExpected == 0 { + sendCheckPoints[i] = true + } + if checkpointExpected == 0 { + nonce = prf.PRF_F2(privateKeyPEM, publicKeys[i], i) + } + } + if nonce == nil { nonce = make([]byte, 16) _, err = rand.Read(nonce) if err != nil { - return "", "", pl.WrapError(err, "failed to generate random nonce") + return "", "", nil, pl.WrapError(err, "failed to generate random nonce") } } @@ -91,28 +105,28 @@ func FormOnion(privateKeyPEM string, publicKeyPEM string, payload []byte, public layerBytes, err = json.Marshal(layer) if err != nil { - return "", "", pl.WrapError(err, "failed to marshal onion layer") + return "", "", nil, pl.WrapError(err, "failed to marshal onion layer") } } symmetricKey, err := keys.GenerateSymmetricKey() if err != nil { - return "", "", pl.WrapError(err, "failed to generate symmetric key") + return "", "", nil, pl.WrapError(err, "failed to generate symmetric key") } encryptedPayload, err := keys.EncryptWithAES(symmetricKey, layerBytes) if err != nil { - return "", "", pl.WrapError(err, "failed to encrypt payload") + return "", "", nil, pl.WrapError(err, "failed to encrypt payload") } sharedKey, err := keys.ComputeSharedKey(privateKeyPEM, publicKeys[i]) if err != nil { - return "", "", pl.WrapError(err, "failed to compute shared key") + return "", "", nil, pl.WrapError(err, "failed to compute shared key") } encryptedKey, err := keys.EncryptWithAES(sharedKey, symmetricKey) if err != nil { - return "", "", pl.WrapError(err, "failed to encrypt key") + return "", "", nil, pl.WrapError(err, "failed to encrypt key") } combinedPayload := CombinedPayload{ @@ -123,16 +137,16 @@ func FormOnion(privateKeyPEM string, publicKeyPEM string, payload []byte, public payload, err = json.Marshal(combinedPayload) if err != nil { - return "", "", pl.WrapError(err, "failed to marshal combined payload") + return "", "", nil, pl.WrapError(err, "failed to marshal combined payload") } } onionWithHeader, err := addHeaderAfterPeeling(base64.StdEncoding.EncodeToString(payload), privateKeyPEM, publicKeyPEM, publicKeys[0], 0) if err != nil { - return "", "", pl.WrapError(err, "failed to add header") + return "", "", nil, pl.WrapError(err, "failed to add header") } - return routingPath[0], onionWithHeader, nil + return routingPath[0], onionWithHeader, sendCheckPoints, nil } // AddHeader adds a header to the peeled onion payload. @@ -212,19 +226,20 @@ func removeHeader(onion string, privateKeyPEM string) (string, int, error) { // - The peeled onion payload. // - The bruise counter. // - A boolean indicating if the nonce verification passed. +// - A boolean indicating if we should expect a checkpoint onion this round // - An error object if an error occurred, otherwise nil. -func PeelOnion(onion string, privateKeyPEM string) (*OnionPayload, int, bool, error) { +func PeelOnion(onion string, privateKeyPEM string) (*OnionPayload, int, bool, bool, error) { headerRemoved, bruises, err := removeHeader(onion, privateKeyPEM) if err != nil { - return nil, -1, true, err + return nil, -1, true, false, err } - peeled, nonceVerification, err := peelOnionAfterRemovingPayload(headerRemoved, privateKeyPEM) + peeled, nonceVerification, expectCheckpoint, err := peelOnionAfterRemovingPayload(headerRemoved, privateKeyPEM) if err != nil { - return nil, -1, nonceVerification, err + return nil, -1, nonceVerification, expectCheckpoint, err } - return peeled, bruises, nonceVerification, nil + return peeled, bruises, nonceVerification, expectCheckpoint, nil } // peelOnionAfterRemovingPayload removes the outermost layer of the onion after removing the header. @@ -235,36 +250,36 @@ func PeelOnion(onion string, privateKeyPEM string) (*OnionPayload, int, bool, er // - The peeled onion payload. // - A boolean indicating if the nonce verification passed. // - An error object if an error occurred, otherwise nil. -func peelOnionAfterRemovingPayload(onion string, privateKeyPEM string) (*OnionPayload, bool, error) { +func peelOnionAfterRemovingPayload(onion string, privateKeyPEM string) (*OnionPayload, bool, bool, error) { onionBytes, err := base64.StdEncoding.DecodeString(onion) if err != nil { - return nil, true, err + return nil, true, false, err } var combinedPayload CombinedPayload if err = json.Unmarshal(onionBytes, &combinedPayload); err != nil { - return nil, true, err + return nil, true, false, err } encryptedKey, err := base64.StdEncoding.DecodeString(combinedPayload.EncryptedSharedKey) if err != nil { - return nil, true, err + return nil, true, false, err } sharedKey, err := keys.ComputeSharedKey(privateKeyPEM, combinedPayload.OriginalSenderPubKey) if err != nil { - return nil, true, err + return nil, true, false, err } symmetricKey, err := keys.DecryptWithAES(sharedKey, string(encryptedKey)) if err != nil { - return nil, true, err + return nil, true, false, err } decryptedBytes, err := keys.DecryptWithAES(symmetricKey, combinedPayload.EncryptedPayload) if err != nil { - return nil, true, err + return nil, true, false, err } if !strings.HasPrefix(string(decryptedBytes), "{\"IsCheckpointOnion\":") { @@ -274,18 +289,18 @@ func peelOnionAfterRemovingPayload(onion string, privateKeyPEM string) (*OnionPa NextHop: "", LastHop: "", Payload: string(decryptedBytes), - }, true, nil + }, true, false, nil } var layer OnionPayload err = json.Unmarshal(decryptedBytes, &layer) if err != nil { - return nil, true, err + return nil, true, false, err } nonce, err := base64.StdEncoding.DecodeString(layer.Nonce) if err != nil { - return nil, true, pl.WrapError(err, "failed to decode nonce") + return nil, true, false, pl.WrapError(err, "failed to decode nonce") } checkpointExpected := prf.PRF_F1(privateKeyPEM, combinedPayload.OriginalSenderPubKey, layer.Layer) @@ -293,9 +308,9 @@ func peelOnionAfterRemovingPayload(onion string, privateKeyPEM string) (*OnionPa expectedNonce := prf.PRF_F2(privateKeyPEM, combinedPayload.OriginalSenderPubKey, layer.Layer) if !hmac.Equal(nonce, expectedNonce) { slog.Warn("nonce verification failed") - return &layer, false, nil + return &layer, false, true, nil } } - return &layer, true, nil + return &layer, true, checkpointExpected == 0, nil } diff --git a/internal/pi_t/pi_t_functions_test.go b/internal/pi_t/pi_t_functions_test.go index 1acc91e..26fba53 100644 --- a/internal/pi_t/pi_t_functions_test.go +++ b/internal/pi_t/pi_t_functions_test.go @@ -15,7 +15,7 @@ func TestFormOnion(t *testing.T) { publicKeys := []string{publicKeyPEM, publicKeyPEM} routingPath := []string{"node1", "node2"} - addr, onion, err := FormOnion(privateKeyPEM, publicKeyPEM, payload, publicKeys, routingPath, -1) + addr, onion, _, err := FormOnion(privateKeyPEM, publicKeyPEM, payload, publicKeys, routingPath, -1) if err != nil { t.Fatalf("FormOnion() error: %v", err) } @@ -49,7 +49,7 @@ func TestPeelOnion(t *testing.T) { publicKeys := []string{publicKeyPEM1, publicKeyPEM2} routingPath := []string{"node1", "node2"} - destination, onion, err := FormOnion(privateKeyPEM, publicKeyPEM, payload, publicKeys, routingPath, -1) + destination, onion, _, err := FormOnion(privateKeyPEM, publicKeyPEM, payload, publicKeys, routingPath, -1) if err != nil { t.Fatalf("FormOnion() error: %v", err) } @@ -60,7 +60,7 @@ func TestPeelOnion(t *testing.T) { // first hop processing - peeled, bruises, _, err := PeelOnion(onion, privateKeyPEM1) + peeled, bruises, _, _, err := PeelOnion(onion, privateKeyPEM1) if err != nil { t.Fatalf("PeelOnion() error: %v", err) } @@ -76,7 +76,7 @@ func TestPeelOnion(t *testing.T) { // second hop processing - peeled2, bruises2, _, err := PeelOnion(headerAdded, privateKeyPEM2) + peeled2, bruises2, _, _, err := PeelOnion(headerAdded, privateKeyPEM2) if err != nil { t.Fatalf("PeelOnion() error: %v", err) } @@ -113,7 +113,7 @@ func TestNonceVerification(t *testing.T) { publicKeys := []string{publicKeyPEM1, publicKeyPEM2} routingPath := []string{"node1", "node2"} - destination, onion, err := FormOnion(privateKeyPEM, publicKeyPEM, payload, publicKeys, routingPath, -1) + destination, onion, _, err := FormOnion(privateKeyPEM, publicKeyPEM, payload, publicKeys, routingPath, -1) if err != nil { t.Fatalf("FormOnion() error: %v", err) } @@ -123,7 +123,7 @@ func TestNonceVerification(t *testing.T) { } // First hop processing with nonce verification - peeled, bruises, nonceVerification, err := PeelOnion(onion, privateKeyPEM1) + peeled, bruises, nonceVerification, _, err := PeelOnion(onion, privateKeyPEM1) if err != nil { t.Fatalf("PeelOnion() error: %v", err) } @@ -146,7 +146,7 @@ func TestNonceVerification(t *testing.T) { } // Second hop processing with nonce verification - peeled2, bruises2, nonceVerification2, err := PeelOnion(headerAdded, privateKeyPEM2) + peeled2, bruises2, nonceVerification2, _, err := PeelOnion(headerAdded, privateKeyPEM2) if err != nil { t.Fatalf("PeelOnion() error: %v", err) } diff --git a/pkg/utils/stream.go b/pkg/utils/stream.go index 4864956..b0e6204 100644 --- a/pkg/utils/stream.go +++ b/pkg/utils/stream.go @@ -175,6 +175,32 @@ func FilterMap[K comparable, V any](m map[K]V, condition func(K, V) bool) map[K] return filteredMap } +func Remove[T any](items []T, condition func(T) bool) []T { + filteredItems := make([]T, 0) + for _, item := range items { + if !condition(item) { + filteredItems = append(filteredItems, item) + } + } + return filteredItems +} + +func InsertAtIndex[T any](items []T, index int, value T) []T { + if index == 0 { + return append([]T{value}, items...) + } + if index == len(items) { + return append(items, value) + } + temp := append(items[:index], value) + items = append(temp, items[index:]...) + return items +} + +func GetLast[T any](items []T) T { + return items[len(items)-1] +} + func Filter[V any](values []V, condition func(V) bool) []V { filteredValues := make([]V, 0) for _, v := range values { diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index bcd82e6..01fa924 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -4,8 +4,11 @@ import ( "crypto/rand" "crypto/sha256" "encoding/hex" + "golang.org/x/exp/slog" "io" + rng "math/rand" "os" + "time" ) // ref: https://www.thorsten-hans.com/check-if-application-is-running-in-docker-container/ @@ -17,13 +20,14 @@ func IsRunningInContainer() bool { return true } -func GenerateUniqueHash() (string, error) { +func GenerateUniqueHash() string { // Create a buffer for random data randomData := make([]byte, 32) // 32 bytes for a strong unique value // Fill the buffer with random data if _, err := io.ReadFull(rand.Reader, randomData); err != nil { - return "", err + slog.Error("Failed to generate random data", err) + return "" } // Create a new SHA256 hash @@ -38,5 +42,11 @@ func GenerateUniqueHash() (string, error) { // Encode the hash to a hexadecimal string hashString := hex.EncodeToString(hashBytes) - return hashString, nil + return hashString +} + +var r = rng.New(rng.NewSource(time.Now().UnixNano())) + +func RandomElement[T any](elements []T) T { + return elements[r.Intn(len(elements))] } diff --git a/static/client/index.html b/static/client/index.html index 9197728..d5b424d 100644 --- a/static/client/index.html +++ b/static/client/index.html @@ -59,6 +59,7 @@

Pi_t

Clients Nodes Rounds + Checkpoint Onions
Loading...
diff --git a/static/index.html b/static/index.html index faadeee..9a68521 100644 --- a/static/index.html +++ b/static/index.html @@ -58,6 +58,7 @@

Pi_t

Clients Nodes Rounds + Checkpoint Onions
Loading...
+ + diff --git a/static/nodes/index.html b/static/nodes/index.html index 3d143ee..2c01797 100644 --- a/static/nodes/index.html +++ b/static/nodes/index.html @@ -59,6 +59,7 @@

Pi_t

Clients Nodes Rounds + Checkpoint Onions
Loading...
@@ -91,13 +92,17 @@

Pi_t

const onionTable = document.createElement('table'); onionTable.innerHTML = ` - Onions + Onions - From - To + Last Hop + Next Hop Time Received + Bruise Counter + Was Dropped? Is Checkpoint Onion? + Was Nonce Verified? + Expect a Checkpoint onion? Layer `; @@ -108,7 +113,11 @@

Pi_t

${onion.LastHop} ${onion.NextHop} ${onion.TimeReceived} + ${onion.Bruises} + ${onion.Dropped} ${onion.IsCheckPointOnion} + ${onion.NonceVerification} + ${onion.ExpectCheckPoint} ${onion.Layer} `; }); diff --git a/static/nodes/rounds/index.html b/static/nodes/rounds/index.html index 50a1d75..f65eec1 100644 --- a/static/nodes/rounds/index.html +++ b/static/nodes/rounds/index.html @@ -23,6 +23,12 @@ th { background-color: #f2f2f2; } + .true { + background-color: #eaffe0; + } + .false { + background-color: #ffe0eb; + } .node { color: green; } @@ -59,6 +65,7 @@

Pi_t

Clients Nodes Rounds + Checkpoint Onions
Loading...
@@ -110,6 +117,8 @@

Pi_t

Bruise Counter Was Dropped? Is Checkpoint Onion? + Was Nonce Verified? + Expect a Checkpoint onion? `; @@ -121,8 +130,10 @@

Pi_t

${onion.NextHop} ${onion.TimeReceived} ${onion.Bruises} - ${onion.Dropped} - ${onion.IsCheckPointOnion} + ${onion.Dropped} + ${onion.IsCheckPointOnion} + ${onion.NonceVerification} + ${onion.ExpectCheckPoint} `; });