Skip to content

Commit

Permalink
Merge pull request #1778 from dedis/work-be1-arnauds5-check-if-organizer
Browse files Browse the repository at this point in the history
Verify the sender of Rollcall create/open/close
  • Loading branch information
arnauds5 authored Apr 10, 2024
2 parents 26f61d4 + 6dcaf08 commit af80986
Show file tree
Hide file tree
Showing 2 changed files with 249 additions and 20 deletions.
60 changes: 46 additions & 14 deletions be1-go/channel/lao/lao.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,12 @@ func (c *Channel) processRollCallCreate(msg message.Message, msgData interface{}
return xerrors.Errorf("invalid roll_call#create message: %v", err)
}

// check that the message was from an organizer
err = c.checkIsFromOrganizer(msg)
if err != nil {
return err
}

// Check that the ProposedEnd is greater than the ProposedStart
if data.ProposedStart > data.ProposedEnd {
return answer.NewErrorf(-4, "The field `proposed_start` is greater than the field "+
Expand Down Expand Up @@ -391,6 +397,12 @@ func (c *Channel) processRollCallOpen(msg message.Message, msgData interface{},
return xerrors.Errorf("invalid roll_call#open message: %v", err)
}

// check that the message was from an organizer
err = c.checkIsFromOrganizer(msg)
if err != nil {
return err
}

if !c.rollCall.checkPrevID([]byte(rollCallOpen.Opens)) {
return answer.NewError(-1, "The field `opens` does not correspond to the id of "+
"the previous roll call message")
Expand All @@ -417,6 +429,12 @@ func (c *Channel) processRollCallClose(msg message.Message, msgData interface{},
return xerrors.Errorf("invalid roll_call#close message: %v", err)
}

// check that the message was from an organizer
err = c.checkIsFromOrganizer(msg)
if err != nil {
return err
}

if c.rollCall.state != Open {
return answer.NewError(-1, "The roll call cannot be closed since it's not open")
}
Expand Down Expand Up @@ -452,21 +470,10 @@ func (c *Channel) processElectionObject(msg message.Message, msgData interface{}
return xerrors.Errorf("message %v isn't a election#setup message", msgData)
}

senderBuf, err := base64.URLEncoding.DecodeString(msg.Sender)
if err != nil {
return xerrors.Errorf(keyDecodeError, err)
}

// Check if the sender of election creation message is the organizer
senderPoint := crypto.Suite.Point()
err = senderPoint.UnmarshalBinary(senderBuf)
// check that the message was from an organizer
err := c.checkIsFromOrganizer(msg)
if err != nil {
return answer.NewErrorf(-4, keyUnmarshalError, err)
}

if !c.organizerPubKey.Equal(senderPoint) {
return answer.NewErrorf(-5, "Sender key does not match the "+
"organizer's one: %s != %s", senderPoint, c.organizerPubKey)
return err
}

var electionSetup messagedata.ElectionSetup
Expand Down Expand Up @@ -769,3 +776,28 @@ func (c *Channel) extractLaoID() string {
func (r *rollCall) checkPrevID(prevID []byte) bool {
return string(prevID) == r.id
}

// checkIsFromOrganizer is a helper method which validates that the message's
// sender is the organizer. Return an error if it failed or if it's false,
// return nil if it was from the organizer.
func (c *Channel) checkIsFromOrganizer(msg message.Message) error {
senderBuf, err := base64.URLEncoding.DecodeString(msg.Sender)
if err != nil {
return answer.NewInvalidMessageFieldError(keyDecodeError, err)
}

senderPoint := crypto.Suite.Point()

err = senderPoint.UnmarshalBinary(senderBuf)
if err != nil {
return answer.NewInvalidMessageFieldError(keyUnmarshalError, senderBuf)
}

if !c.organizerPubKey.Equal(senderPoint) {
return answer.NewAccessDeniedError(
"sender key %v does not match organizer key %v",
senderPoint, c.organizerPubKey)
}

return nil
}
209 changes: 203 additions & 6 deletions be1-go/channel/lao/lao_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"go.dedis.ch/kyber/v3/sign/schnorr"
"io"
"os"
"path/filepath"
Expand All @@ -16,6 +17,7 @@ import (
"popstellar/message/query/method/message"
"popstellar/network/socket"
"popstellar/validation"
"strconv"
"sync"
"testing"
"time"
Expand Down Expand Up @@ -488,6 +490,82 @@ func TestBaseChannel_SimulateRollCall(t *testing.T) {
require.NoError(t, channel.Publish(messageClosePub, nil))
}

func TestLAOChannel_Rollcall_Creation_Not_Organizer(t *testing.T) {
keypair := generateKeyPair(t)

fakeHub, err := NewFakeHub("", keypair.public, nolog, nil)
require.NoError(t, err)

m := message.Message{MessageID: "0"}
channel, err := NewChannel(sampleLao, fakeHub, m, nolog, keypair.public, nil)
require.NoError(t, err)

// Publish a rollcall create message with a different key than the
// organizer, an error is expected
err = channel.Publish(sampleRollCallCreatePublish, nil)
require.Error(t, err)
}

func TestLAOChannel_Rollcall_Open_Not_Organizer(t *testing.T) {
keypairOrg := generateKeyPair(t)
keypairOther := generateKeyPair(t)

fakeHub, err := NewFakeHub("", keypairOrg.public, nolog, nil)
require.NoError(t, err)

laoId := messagedata.Hash(base64.URLEncoding.EncodeToString(keypairOrg.
publicBuf), strconv.FormatInt(time.Now().Unix(), 10), "Lao 1")
laoChannel := "/root/" + laoId

m := message.Message{MessageID: "0"}
channel, err := NewChannel(laoChannel, fakeHub, m, nolog, keypairOrg.public, nil)
require.NoError(t, err)

rollcallCreate, rollcallId := createRollCallCreate(t, keypairOrg, laoId)

err = channel.Publish(rollcallCreate, nil)
require.NoError(t, err)

rollcallOpen := createRollCallOpen(t, keypairOther, laoId, rollcallId)

// Publish a rollcall open message with a different key than the
// organizer, an error is expected
err = channel.Publish(rollcallOpen, nil)
require.Error(t, err)
}

func TestLAOChannel_Rollcall_Close_Not_Organizer(t *testing.T) {
keypairOrg := generateKeyPair(t)
keypairOther := generateKeyPair(t)

fakeHub, err := NewFakeHub("", keypairOrg.public, nolog, nil)
require.NoError(t, err)

laoId := messagedata.Hash(base64.URLEncoding.EncodeToString(keypairOrg.
publicBuf), strconv.FormatInt(time.Now().Unix(), 10), "Lao 1")
laoChannel := "/root/" + laoId

m := message.Message{MessageID: "0"}
channel, err := NewChannel(laoChannel, fakeHub, m, nolog, keypairOrg.public, nil)
require.NoError(t, err)

rollcallCreate, rollcallId := createRollCallCreate(t, keypairOrg, laoId)

err = channel.Publish(rollcallCreate, nil)
require.NoError(t, err)

rollcallOpen := createRollCallOpen(t, keypairOrg, laoId, rollcallId)
err = channel.Publish(rollcallOpen, nil)
require.NoError(t, err)

rollcallClose := createRollCallClose(t, keypairOther, laoId, rollcallId)

// Publish a rollcall close message with a different key than the
// organizer, an error is expected
err = channel.Publish(rollcallClose, nil)
require.Error(t, err)
}

func TestLAOChannel_Election_Creation(t *testing.T) {
keypair := generateKeyPair(t)
publicKey64 := base64.URLEncoding.EncodeToString(keypair.publicBuf)
Expand Down Expand Up @@ -572,13 +650,14 @@ func TestLAOChannel_Sends_Greeting(t *testing.T) {
}

func Test_LAOChannel_Witness_Message(t *testing.T) {
keypair := generateKeyPair(t)
fakeHub, err := NewFakeHub("", keypair.public, nolog, nil)
organizerPk := getPublicKeyPoint(t, organizerPublicKey)

fakeHub, err := NewFakeHub("", organizerPk, nolog, nil)
require.NoError(t, err)

// Create new Lao channel
m := message.Message{MessageID: "0"}
channel, err := NewChannel(sampleLao, fakeHub, m, nolog, keypair.public, nil)
channel, err := NewChannel(sampleLao, fakeHub, m, nolog, organizerPk, nil)
require.NoError(t, err)

// Publish roll_call_create message
Expand All @@ -593,13 +672,14 @@ func Test_LAOChannel_Witness_Message(t *testing.T) {
}

func Test_LAOChannel_Witness_Message_Not_Received_Yet(t *testing.T) {
keypair := generateKeyPair(t)
fakeHub, err := NewFakeHub("", keypair.public, nolog, nil)
organizerPk := getPublicKeyPoint(t, organizerPublicKey)

fakeHub, err := NewFakeHub("", organizerPk, nolog, nil)
require.NoError(t, err)

// Create new Lao channel
m := message.Message{MessageID: "0"}
channel, err := NewChannel(sampleLao, fakeHub, m, nolog, keypair.public, nil)
channel, err := NewChannel(sampleLao, fakeHub, m, nolog, organizerPk, nil)
require.NoError(t, err)

// Publish witness message and catchup on channel to get the message back
Expand Down Expand Up @@ -803,6 +883,19 @@ func (f *fakeSocket) ID() string {
var sampleLao = "/root/QNNTcGQk-rnehNjgizdzi9IT1nIlmXsOXy1BCWsNaVE="
var organizerPublicKey = "A2nPAZfsvBRPb5uOb1_hUVuAKt5YKPRZdiFq1g0TLr0="

// getPublicKeyPoint convert a base64 encoded public key to a Kyber.Point
func getPublicKeyPoint(t *testing.T, publicKeyBase64 string) kyber.Point {
keyBuf, err := base64.URLEncoding.DecodeString(publicKeyBase64)
require.NoError(t, err)

senderPk := crypto.Suite.Point()

err = senderPk.UnmarshalBinary(keyBuf)
require.NoError(t, err)

return senderPk
}

var sampleRollCallCreate = message.Message{
Data: "eyJjcmVhdGlvbiI6MTY4NDI1OTU4MSwiZGVzY3JpcHRpb24iOiIiLCJpZCI6IktxLV9CbUJUZTFEWnFjSXEzU2pOcklzdHAzTFdCM0N6VFhoOVpBaHctUUU9IiwibG9jYXRpb24iOiJ0ZSI" +
"sIm5hbWUiOiJ0ZSIsInByb3Bvc2VkX2VuZCI6MTY4NDI2MzEyMCwicHJvcG9zZWRfc3RhcnQiOjE2ODQyNTk1ODEsIm9iamVjdCI6InJvbGxfY2FsbCIsImFjdGlvbiI6ImNyZWF0ZSJ9",
Expand Down Expand Up @@ -851,3 +944,107 @@ var sampleWitnessMessagePublish = method.Publish{
Message: sampleWitnessMessage,
},
}

// createPublish is a helper function that create a Publish message
// containing a message data with valid signature and ids
func createPublish(t *testing.T, sender keypair, laoId string,
data []byte) method.Publish {

data64 := base64.URLEncoding.EncodeToString(data)
senderPk := base64.URLEncoding.EncodeToString(sender.publicBuf)
signature, err := schnorr.Sign(suite, sender.private, data)
require.NoError(t, err)

msg := message.Message{
Data: data64,
Sender: senderPk,
Signature: base64.URLEncoding.EncodeToString(signature),
MessageID: messagedata.Hash(data64, senderPk),
WitnessSignatures: nil,
}

publishMsg := method.Publish{
Base: query.Base{
JSONRPCBase: jsonrpc.JSONRPCBase{
JSONRPC: "2.0",
},
Method: "publish",
},

Params: struct {
Channel string `json:"channel"`
Message message.Message `json:"message"`
}{
Channel: "/root/" + laoId,
Message: msg,
},
}

return publishMsg
}

func createRollCallCreate(t *testing.T, sender keypair,
laoId string) (method.Publish, string) {

now := time.Now().Unix()
rollcallName := "Roll Call"
rollcallId := messagedata.Hash("R", laoId, strconv.FormatInt(now, 10), rollcallName)
rollcallCreate, err := json.Marshal(messagedata.RollCallCreate{
Object: "roll_call",
Action: "create",
ID: rollcallId,
Name: rollcallName,
Creation: now,
ProposedStart: now,
ProposedEnd: now + 1000,
Location: "EPFL",
Description: "",
})
require.NoError(t, err)

rollcallCreatePublish := createPublish(t, sender, laoId, rollcallCreate)

return rollcallCreatePublish, rollcallId
}

func createRollCallOpen(t *testing.T, sender keypair,
laoId string, rollcallId string) method.Publish {

openAt := time.Now().Unix()
updateId := messagedata.Hash("R", laoId, rollcallId, strconv.FormatInt(openAt, 10))

rollcallOpen, err := json.Marshal(messagedata.RollCallOpen{
Object: "roll_call",
Action: "open",
UpdateID: updateId,
Opens: rollcallId,
OpenedAt: openAt,
})
require.NoError(t, err)

rollcallOpenPublish := createPublish(t, sender, laoId, rollcallOpen)

return rollcallOpenPublish
}

func createRollCallClose(t *testing.T, sender keypair,
laoId string, openId string) method.Publish {

closeAt := time.Now().Unix()
updateId := messagedata.Hash("R", laoId, openId, strconv.FormatInt(closeAt, 10))
attendees := []string{base64.URLEncoding.EncodeToString(sender.publicBuf)}

rollcallClose, err := json.Marshal(messagedata.RollCallClose{
Object: "roll_call",
Action: "close",
UpdateID: updateId,
Closes: openId,
ClosedAt: closeAt,
Attendees: attendees,
})
require.NoError(t, err)

rollcallClosePublish := createPublish(t, sender, laoId, rollcallClose)

return rollcallClosePublish
}

0 comments on commit af80986

Please sign in to comment.