Skip to content

Commit

Permalink
Merge 1433535 into b8dc272
Browse files Browse the repository at this point in the history
  • Loading branch information
kc1212 committed Dec 7, 2018
2 parents b8dc272 + 1433535 commit 79414a2
Show file tree
Hide file tree
Showing 38 changed files with 4,340 additions and 1,304 deletions.
10 changes: 7 additions & 3 deletions .travis.yml
Expand Up @@ -10,9 +10,11 @@ env:
- TEST_DIR=kyber

install:
- gimme 1.9
- . $HOME/.gimme/envs/go1.9.env
- gimme 1.11
- . $HOME/.gimme/envs/go1.11.env
- go get github.com/dedis/Coding || true
- go get github.com/dedis/kyber
- (cd `go env GOPATH`/src/github.com/dedis/kyber && git checkout redist2)
# Because we are using "language: node_js" the "git clone" is not in the
# GOPATH. So make a copy of it over where it is supposed to be.
- git clone . `go env GOPATH`/src/github.com/dedis/cothority
Expand All @@ -39,8 +41,10 @@ matrix:
- "1.11.x"

install:
- go get -t ./...
- go get github.com/dedis/Coding || true
- go get github.com/dedis/kyber
- (cd `go env GOPATH`/src/github.com/dedis/kyber && git checkout redist2)
- go get -t ./...

before_install:
- cd $TRAVIS_BUILD_DIR
Expand Down
32 changes: 28 additions & 4 deletions calypso/README.md
Expand Up @@ -56,10 +56,23 @@ every `blockInterval` seconds.

## CreateLTS

The CreateLTS endpoint is currently unsecured. It takes a list of nodes as
input and then asks all these nodes to create a distributed key using DKG. For
this operation, all nodes must be online. Per default, a threshold of 2/3 of
the nodes must be present for the decryption.
_The CreateLTS endpoint is currently unsecured._

The client that initiates `CreateLTS` should hold two rosters. One roster for
storing the secret shares of LTS (long term secret), the other for a ByzCoin
instance for storing the LTS roster (using the LTS contract).

If the LTS roster does not exist on ByzCoin, the client is responsible for
creating it. Which can be done by sending a ByzCoin transaction. The
transaction should spawn a new LTS instance.

After the LTS roster is on ByzCoin but before the creation of LTS shares. The
client should make a `CreateLTS` request to a node in the LTS roster. The
request should contain the instance ID that contains the LTS roster. Then,
every Calypso node should check that the instance ID that holds the LTS roster
exists before starting the DKG. For this operation, all nodes must be online.
Per default, a threshold of 2/3 of the nodes must be present for the
decryption.

The CreateLTS service endpoint returns a `LTSID` in the form of a 32 byte
slice. This ID represents the group that created the distributed key. Any node
Expand All @@ -81,3 +94,14 @@ as a target an existing instance.
The read contract verifies that the request is valid and points to the write
instance. It stores the reader's public key in the instance, so that the
secret-management cothority can re-encrypt to this reader's public key.

## Resharing LTS
It is possible that the roster might change and the LTS shares must be
re-distributed but without changing the LTS itself. We accomplish this in two
steps.

1. The authorised client(s) must update the LTS roster in the blockchain (an
instance of the LTS smart contract).
2. Then, the client instructs the calypso conodes to run the resharing
protocol. The members of the old calypso finds the new roster and then
executes the actual protocol.
61 changes: 56 additions & 5 deletions calypso/api.go
Expand Up @@ -6,7 +6,9 @@ import (
"github.com/dedis/cothority"
"github.com/dedis/cothority/byzcoin"
"github.com/dedis/cothority/darc"
"github.com/dedis/cothority/skipchain"
"github.com/dedis/onet"
"github.com/dedis/onet/network"
"github.com/dedis/protobuf"
)

Expand Down Expand Up @@ -37,20 +39,69 @@ func NewClient(byzcoin *byzcoin.Client) *Client {
cothority.Suite, ServiceName)}
}

// CreateLTS creates a random LTSID that can be used to reference
// the LTS group created.
func (c *Client) CreateLTS() (reply *CreateLTSReply, err error) {
// CreateLTS creates a random LTSID that can be used to reference the LTS group
// created. It first sends a transaction to ByzCoin to spawn a LTS instance,
// then it asks the Calypso cothority to start the DKG.
func (c *Client) CreateLTS(ltsRoster *onet.Roster, darcID darc.ID, signers []darc.Signer, counters []uint64) (reply *CreateLTSReply, err error) {
// Make the transaction and get its proof
buf, err := protobuf.Encode(&LtsInstanceInfo{*ltsRoster})
if err != nil {
return nil, err
}
inst := byzcoin.Instruction{
InstanceID: byzcoin.NewInstanceID(darcID),
Spawn: &byzcoin.Spawn{
ContractID: ContractLongTermSecretID,
Args: []byzcoin.Argument{
{
Name: "lts_instance_info",
Value: buf,
},
},
},
SignerCounter: counters,
}
tx := byzcoin.ClientTransaction{
Instructions: []byzcoin.Instruction{inst},
}
if err := tx.SignWith(signers...); err != nil {
return nil, err
}
if _, err := c.bcClient.AddTransactionAndWait(tx, 4); err != nil {
return nil, err
}
resp, err := c.bcClient.GetProof(tx.Instructions[0].DeriveID("").Slice())
if err != nil {
return nil, err
}

// Start the DKG
reply = &CreateLTSReply{}
err = c.c.SendProtobuf(c.bcClient.Roster.List[0], &CreateLTS{
Roster: c.bcClient.Roster,
BCID: c.bcClient.ID,
Proof: resp.Proof,
}, reply)
if err != nil {
return nil, err
}
return reply, nil
}

// Authorise adds a ByzCoinID to the list of authorized IDs for each
// server in the roster. The AuthoriseByzcoinID service refuses requests
// that do not come from localhost.
//
// It should be called by the administrator at the beginning, before any other
// API calls are made. A ByzCoinID that is not authorised will not be allowed to
// call the other APIs.
func (c *Client) Authorise(who *network.ServerIdentity, what skipchain.SkipBlockID) error {
reply := &AuthoriseReply{}
err := c.c.SendProtobuf(who, &Authorise{ByzCoinID: what}, reply)
if err != nil {
return err
}
return nil
}

// DecryptKey takes as input Read- and Write- Proofs. It verifies that
// the read/write requests match and then re-encrypts the secret
// given the public key information of the reader.
Expand Down
50 changes: 32 additions & 18 deletions calypso/api_test.go
Expand Up @@ -15,47 +15,54 @@ import (
// Tests the client function CreateLTS
func TestClient_CreateLTS(t *testing.T) {
l := onet.NewTCPTest(cothority.Suite)
servers, roster, _ := l.GenTree(3, true)
l.GetServices(servers, calypsoID)
_, roster, _ := l.GenTree(3, true)
defer l.CloseAll()

// Initialise the genesis message and send it to the service.
signer := darc.NewSignerEd25519(nil, nil)
msg, err := byzcoin.DefaultGenesisMsg(byzcoin.CurrentVersion, roster, []string{"spawn:dummy"}, signer.Identity())
msg, err := byzcoin.DefaultGenesisMsg(byzcoin.CurrentVersion, roster,
[]string{"spawn:dummy", "spawn:" + ContractLongTermSecretID},
signer.Identity())
msg.BlockInterval = 100 * time.Millisecond
require.Nil(t, err)

// The darc inside it should be valid.
d := msg.GenesisDarc
require.Nil(t, d.Verify(true))
//Create Ledger

// Create the clients
c, _, err := byzcoin.NewLedger(msg, false)
require.Nil(t, err)
//Create a Calypso Client (Byzcoin + Onet)
calypsoClient := NewClient(c)
//Invoke CreateLTS
ltsReply, err := calypsoClient.CreateLTS()
for _, who := range roster.List {
err := calypsoClient.Authorise(who, c.ID)
require.NoError(t, err)
}

// Invoke CreateLTS
ltsReply, err := calypsoClient.CreateLTS(roster, d.GetBaseID(), []darc.Signer{signer}, []uint64{1})
require.Nil(t, err)
require.NotNil(t, ltsReply.LTSID)
require.NotNil(t, ltsReply.ByzCoinID)
require.NotNil(t, ltsReply.InstanceID)
require.NotNil(t, ltsReply.X)
}

// TODO(jallen): Write TestClient_Reshare (and add api.go part too, I guess)

// Tests the client api's AddRead, AddWrite, DecryptKey
func TestClient_Calypso(t *testing.T) {
l := onet.NewTCPTest(cothority.Suite)
servers, roster, _ := l.GenTree(3, true)
l.GetServices(servers, calypsoID)
_, roster, _ := l.GenTree(3, true)
defer l.CloseAll()

admin := darc.NewSignerEd25519(nil, nil)
adminCt := uint64(1)
provider1 := darc.NewSignerEd25519(nil, nil)
reader1 := darc.NewSignerEd25519(nil, nil)
provider2 := darc.NewSignerEd25519(nil, nil)
reader2 := darc.NewSignerEd25519(nil, nil)
// Initialise the genesis message and send it to the service.
// The admin has the privilege to spawn darcs
msg, err := byzcoin.DefaultGenesisMsg(byzcoin.CurrentVersion, roster,
[]string{"spawn:" + byzcoin.ContractDarcID},
[]string{"spawn:darc", "spawn:" + ContractLongTermSecretID},
admin.Identity())

msg.BlockInterval = 100 * time.Millisecond
Expand All @@ -70,7 +77,12 @@ func TestClient_Calypso(t *testing.T) {
calypsoClient := NewClient(c)

//Create the LTS
ltsReply, err := calypsoClient.CreateLTS()
for _, who := range roster.List {
err := calypsoClient.Authorise(who, c.ID)
require.NoError(t, err)
}
ltsReply, err := calypsoClient.CreateLTS(roster, gDarc.GetBaseID(), []darc.Signer{admin}, []uint64{adminCt})
adminCt++
require.Nil(t, err)
//If no error, assign it
calypsoClient.ltsReply = ltsReply
Expand All @@ -85,7 +97,8 @@ func TestClient_Calypso(t *testing.T) {
expression.InitOrExpr(reader1.Identity().String()))
require.NotNil(t, darc1)
require.Nil(t, err)
_, err = calypsoClient.SpawnDarc(admin, 1, gDarc, *darc1, 10)
_, err = calypsoClient.SpawnDarc(admin, adminCt, gDarc, *darc1, 10)
adminCt++
require.Nil(t, err)

//Create a similar darc for provider2, reader2
Expand All @@ -97,12 +110,13 @@ func TestClient_Calypso(t *testing.T) {
darc2.Rules.AddRule(darc.Action("spawn:"+ContractReadID),
expression.InitOrExpr(reader2.Identity().String()))
//Spawn it
_, err = calypsoClient.SpawnDarc(admin, 2, gDarc, *darc2, 10)
_, err = calypsoClient.SpawnDarc(admin, adminCt, gDarc, *darc2, 10)
adminCt++
require.Nil(t, err)
//Create a secret key
key1 := []byte("secret key 1")
//Create a Write instance
write1 := NewWrite(cothority.Suite, calypsoClient.ltsReply.LTSID,
write1 := NewWrite(cothority.Suite, calypsoClient.ltsReply.InstanceID,
darc1.GetBaseID(), calypsoClient.ltsReply.X, key1)
//Write it to calypso
wr1, err := calypsoClient.AddWrite(write1, provider1, 1, *darc1, 10)
Expand All @@ -121,7 +135,7 @@ func TestClient_Calypso(t *testing.T) {

key2 := []byte("secret key 2")
//Create a Write instance
write2 := NewWrite(cothority.Suite, calypsoClient.ltsReply.LTSID,
write2 := NewWrite(cothority.Suite, calypsoClient.ltsReply.InstanceID,
darc2.GetBaseID(), calypsoClient.ltsReply.X, key2)
wr2, err := calypsoClient.AddWrite(write2, provider2, 1, *darc2, 10)
require.Nil(t, err)
Expand Down
89 changes: 88 additions & 1 deletion calypso/contracts.go
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/dedis/cothority"
"github.com/dedis/cothority/byzcoin"
"github.com/dedis/cothority/darc"
"github.com/dedis/onet"
"github.com/dedis/onet/log"
"github.com/dedis/onet/network"
"github.com/dedis/protobuf"
Expand Down Expand Up @@ -57,7 +58,6 @@ func (c *contractWr) Spawn(rst byzcoin.ReadOnlyStateTrie, inst byzcoin.Instructi
instID := inst.DeriveID("")
log.Lvlf3("Successfully verified write request and will store in %x", instID)
sc = append(sc, byzcoin.NewStateChange(byzcoin.Create, instID, ContractWriteID, w, darcID))

case ContractReadID:
var rd Read
r := inst.Spawn.Args.Search("read")
Expand Down Expand Up @@ -93,3 +93,90 @@ type contractRe struct {
func contractReadFromBytes(in []byte) (byzcoin.Contract, error) {
return nil, errors.New("calypso read instances are never instantiated")
}

// ContractLongTermSecretID is the contract ID for updating the LTS roster.
var ContractLongTermSecretID = "longTermSecret"

type contractLTS struct {
byzcoin.BasicContract
LtsInstanceInfo LtsInstanceInfo
}

func contractLTSFromBytes(in []byte) (byzcoin.Contract, error) {
c := &contractLTS{}

err := protobuf.DecodeWithConstructors(in, &c.LtsInstanceInfo, network.DefaultConstructors(cothority.Suite))
if err != nil {
return nil, errors.New("couldn't unmarshal LtsInfo: " + err.Error())
}
return c, nil
}

func (c *contractLTS) Spawn(rst byzcoin.ReadOnlyStateTrie, inst byzcoin.Instruction, coins []byzcoin.Coin) ([]byzcoin.StateChange, []byzcoin.Coin, error) {
var darcID darc.ID
_, _, _, darcID, err := rst.GetValues(inst.InstanceID.Slice())
if err != nil {
return nil, nil, err
}

if inst.Spawn.ContractID != ContractLongTermSecretID {
return nil, nil, errors.New("can only spawn long-term-secret instances")
}
infoBuf := inst.Spawn.Args.Search("lts_instance_info")
if infoBuf == nil || len(infoBuf) == 0 {
return nil, nil, errors.New("need a lts_instance_info argument")
}
var info LtsInstanceInfo
err = protobuf.DecodeWithConstructors(infoBuf, &info, network.DefaultConstructors(cothority.Suite))
if err != nil {
return nil, nil, errors.New("passed lts_instance_info argument is invalid: " + err.Error())
}
return byzcoin.StateChanges{byzcoin.NewStateChange(byzcoin.Create, inst.DeriveID(""), ContractLongTermSecretID, infoBuf, darcID)}, coins, nil
}

func (c *contractLTS) Invoke(rst byzcoin.ReadOnlyStateTrie, inst byzcoin.Instruction, coins []byzcoin.Coin) ([]byzcoin.StateChange, []byzcoin.Coin, error) {
var darcID darc.ID
curBuf, _, _, darcID, err := rst.GetValues(inst.InstanceID.Slice())
if err != nil {
return nil, nil, err
}

if inst.Invoke.Command != "reshare" {
return nil, nil, errors.New("can only reshare long-term secrets")
}
infoBuf := inst.Invoke.Args.Search("lts_instance_info")
if infoBuf == nil || len(infoBuf) == 0 {
return nil, nil, errors.New("need a lts_instance_info argument")
}

var curInfo, newInfo LtsInstanceInfo
err = protobuf.DecodeWithConstructors(infoBuf, &newInfo, network.DefaultConstructors(cothority.Suite))
if err != nil {
return nil, nil, errors.New("passed lts_instance_info argument is invalid: " + err.Error())
}
err = protobuf.DecodeWithConstructors(curBuf, &curInfo, network.DefaultConstructors(cothority.Suite))
if err != nil {
return nil, nil, errors.New("current info is invalid: " + err.Error())
}

// Verify the intersection between new roster and the old one. There must be
// at least a threshold of nodes in the intersection.
n := len(curInfo.Roster.List)
overlap := intersectRosters(&curInfo.Roster, &newInfo.Roster)
thr := n - (n-1)/3
if overlap < thr {
return nil, nil, errors.New("new roster does not overlap enough with current roster")
}

return byzcoin.StateChanges{byzcoin.NewStateChange(byzcoin.Update, inst.InstanceID, ContractLongTermSecretID, infoBuf, darcID)}, coins, nil
}

func intersectRosters(r1, r2 *onet.Roster) int {
res := 0
for _, x := range r2.List {
if i, _ := r1.Search(x.ID); i != -1 {
res++
}
}
return res
}

0 comments on commit 79414a2

Please sign in to comment.