Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Darc identity test #2482

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 128 additions & 7 deletions calypso/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package calypso

import (
"encoding/binary"
"go.dedis.ch/kyber/v3/util/key"
"testing"
"time"

Expand Down Expand Up @@ -137,8 +138,6 @@ func TestClient_Calypso(t *testing.T) {
ltsReply, err := calypsoClient.CreateLTS(roster, gDarc.GetBaseID(), []darc.Signer{admin}, []uint64{adminCt})
adminCt++
require.NoError(t, err)
//If no error, assign it
calypsoClient.ltsReply = ltsReply

//Create a signer, darc for data point #1
darc1 := darc.NewDarc(darc.InitRules([]darc.Identity{provider1.Identity()},
Expand Down Expand Up @@ -166,11 +165,12 @@ func TestClient_Calypso(t *testing.T) {
_, err = calypsoClient.SpawnDarc(admin, adminCt, gDarc, *darc2, 10)
adminCt++
require.NoError(t, err)

//Create a secret key
key1 := []byte("secret key 1")
//Create a Write instance
write1 := NewWrite(cothority.Suite, calypsoClient.ltsReply.InstanceID,
darc1.GetBaseID(), calypsoClient.ltsReply.X, key1)
write1 := NewWrite(cothority.Suite, ltsReply.InstanceID,
darc1.GetBaseID(), ltsReply.X, key1)
//Write it to calypso
wr1, err := calypsoClient.AddWrite(write1, provider1, 1, *darc1, 10)
require.NoError(t, err)
Expand All @@ -188,8 +188,8 @@ func TestClient_Calypso(t *testing.T) {

key2 := []byte("secret key 2")
//Create a Write instance
write2 := NewWrite(cothority.Suite, calypsoClient.ltsReply.InstanceID,
darc2.GetBaseID(), calypsoClient.ltsReply.X, key2)
write2 := NewWrite(cothority.Suite, ltsReply.InstanceID,
darc2.GetBaseID(), ltsReply.X, key2)
wr2, err := calypsoClient.AddWrite(write2, provider2, 1, *darc2, 10)
require.NoError(t, err)
prWr2, err := calypsoClient.WaitProof(wr2.InstanceID, time.Second, nil)
Expand All @@ -210,10 +210,131 @@ func TestClient_Calypso(t *testing.T) {
// Make sure you can actually decrypt
dk1, err := calypsoClient.DecryptKey(&DecryptKey{Read: *prRe1, Write: *prWr1})
require.NoError(t, err)
require.True(t, dk1.X.Equal(calypsoClient.ltsReply.X))
require.True(t, dk1.X.Equal(ltsReply.X))
keyCopy1, err := dk1.RecoverKey(reader1.Ed25519.Secret)
require.NoError(t, err)
require.Equal(t, key1, keyCopy1)

// use keyCopy to unlock the stuff in writeInstance.Data
}

// Tests the Calypso system with a simple write/read scenario.
// But it does the signing outside of the `Read` and `Write` methods for
// integration of the MPC signing by OneKey.
func TestClient_Calypso_Simple(t *testing.T) {
l := onet.NewTCPTest(cothority.Suite)
_, roster, _ := l.GenTree(3, true)
defer l.CloseAll()

admin := darc.NewSignerEd25519(nil, nil)
adminCt := uint64(1)
user := 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:" + ContractLongTermSecretID},
admin.Identity())

msg.BlockInterval = 500 * time.Millisecond
require.NoError(t, err)
// The darc inside it should be valid.
gDarc := msg.GenesisDarc
require.Nil(t, gDarc.Verify(true))
//Create Ledger
c, _, err := byzcoin.NewLedger(msg, false)
require.NoError(t, err)
//Create a Calypso Client (Byzcoin + Onet)
calypsoClient := NewClient(c)

//Create the LTS
for _, who := range roster.List {
err := calypsoClient.Authorize(who, c.ID)
require.NoError(t, err)
}
ltsReply, err := calypsoClient.CreateLTS(roster, gDarc.GetBaseID(), []darc.Signer{admin}, []uint64{adminCt})
adminCt++
require.NoError(t, err)

//Create a signer darc
userDarc := darc.NewDarc(darc.InitRules([]darc.Identity{user.Identity()},
[]darc.Identity{user.Identity()}), []byte("Provider1"))
// user can read and write.
// This can be changed to two different public keys.
err = userDarc.Rules.AddRule(darc.Action("spawn:"+ContractWriteID),
expression.InitOrExpr(user.Identity().String()))
require.NoError(t, err)
err = userDarc.Rules.AddRule(darc.Action("spawn:"+ContractReadID),
expression.InitOrExpr(user.Identity().String()))
require.NoError(t, err)
require.NotNil(t, userDarc)
_, err = calypsoClient.SpawnDarc(admin, adminCt, gDarc, *userDarc, 10)
adminCt++
require.NoError(t, err)

data := []byte("Some secret data - or the user's private key")
// Create a Write structure
write1, err := NewWriteData(cothority.Suite,
ltsReply.InstanceID,
userDarc.GetBaseID(), ltsReply.X, data)
require.NoError(t, err)

// Create a write-instance and send it to Byzcoin - here
// the instruction and the transaction is created manually,
// so that an external signer can sign the hash of the instruction.
wrInst, err := ContractWriteSpawnInstruction(write1, userDarc)
require.NoError(t, err)
wrInst.SignerCounter = []uint64{1}
wrInst.SignerIdentities = []darc.Identity{user.Identity()}
wrTx, err := calypsoClient.bcClient.CreateTransaction(*wrInst)
require.NoError(t, err)
digest := wrTx.Instructions.Hash()

// This signature can be replaced by an external signature.
signature, err := user.Sign(digest)
require.NoError(t, err)
wrTx.Instructions[0].Signatures = [][]byte{signature}

// Send the transaction to ByzCoin
_, err = calypsoClient.bcClient.AddTransactionAndWait(wrTx, 10)
require.NoError(t, err)
wrID := wrTx.Instructions[0].DeriveID("")
proofWr, err := calypsoClient.WaitProof(wrID, time.Second, nil)
require.NoError(t, err)

// Create a read-instance and send it to ByzCoin.
ephemeral := key.NewKeyPair(cothority.Suite)
readInst, err := ContractReadSpawnInstruction(wrID, ephemeral.Public)
require.NoError(t, err)
readInst.SignerCounter = []uint64{2}
readInst.SignerIdentities = []darc.Identity{user.Identity()}
readTx, err := calypsoClient.bcClient.CreateTransaction(*readInst)
require.NoError(t, err)
digest = readTx.Instructions.Hash()

// This signature can be replaced by an external signature
signature, err = user.Sign(digest)
require.NoError(t, err)
readTx.Instructions[0].Signatures = [][]byte{signature}
readID := readTx.Instructions[0].DeriveID("")

// Send the transaction to ByzCoin
_, err = calypsoClient.bcClient.AddTransactionAndWait(readTx, 10)
require.NoError(t, err)
proofRd, err := calypsoClient.WaitProof(readID, time.Second,
nil)
require.NoError(t, err)

// Make sure you can actually decrypt
dk, err := calypsoClient.DecryptKey(&DecryptKey{Read: *proofRd,
Write: *proofWr})
require.NoError(t, err)
require.True(t, dk.X.Equal(ltsReply.X))
keyCopy, err := dk.RecoverKey(ephemeral.Private)
require.NoError(t, err)
var wrCopy Write
require.NoError(t, proofWr.VerifyAndDecode(cothority.Suite, ContractWriteID,
&wrCopy))
dataDecrypt, err := wrCopy.Decrypt(keyCopy)
require.NoError(t, err)
require.Equal(t, data, dataDecrypt)
}
46 changes: 46 additions & 0 deletions calypso/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package calypso

import (
"fmt"
"go.dedis.ch/kyber/v3"
"strings"

"go.dedis.ch/cothority/v3"
Expand Down Expand Up @@ -278,3 +279,48 @@ func (c ContractWrite) VerifyInstruction(rst byzcoin.ReadOnlyStateTrie, inst byz
}
return inst.VerifyWithOption(rst, ctxHash, nil)
}

// ContractWriteSpawnInstruction returns the spawn instruction for a Write
// contract.
func ContractWriteSpawnInstruction(wr *Write,
d *darc.Darc) (*byzcoin.Instruction, error) {
writeBuf, err := protobuf.Encode(wr)
if err != nil {
return nil, xerrors.Errorf("couldn't encode write: %v", err)
}
return &byzcoin.Instruction{
InstanceID: byzcoin.NewInstanceID(d.GetBaseID()),
Spawn: &byzcoin.Spawn{
ContractID: ContractWriteID,
Args: byzcoin.Arguments{
{
Name: "write",
Value: writeBuf,
},
},
},
}, nil
}

// ContractReadSpawnInstruction returns the spawn instruction for a Read
// contract.
func ContractReadSpawnInstruction(wrID byzcoin.InstanceID,
xc kyber.Point) (*byzcoin.Instruction, error) {
var readBuf []byte
read := &Read{
Write: wrID,
Xc: xc,
}
readBuf, err := protobuf.Encode(read)
if err != nil {
return nil, xerrors.Errorf("encoding Read message: %v", err)
}

return &byzcoin.Instruction{
InstanceID: wrID,
Spawn: &byzcoin.Spawn{
ContractID: ContractReadID,
Args: byzcoin.Arguments{{Name: "read", Value: readBuf}},
},
}, nil
}
76 changes: 75 additions & 1 deletion calypso/struct.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
package calypso

import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"fmt"

"go.dedis.ch/cothority/v3"
"go.dedis.ch/cothority/v3/byzcoin"
"go.dedis.ch/cothority/v3/darc"
"go.dedis.ch/kyber/v3"
"go.dedis.ch/kyber/v3/suites"
"go.dedis.ch/kyber/v3/util/random"
"go.dedis.ch/kyber/v3/xof/keccak"
"go.dedis.ch/onet/v3/network"
"golang.org/x/xerrors"
"io"
)

func init() {
Expand Down Expand Up @@ -68,6 +74,20 @@ func NewWrite(suite suites.Suite, ltsid byzcoin.InstanceID, writeDarc darc.ID, X
return wr
}

// NewWriteData is like NewWrite,
// but it encrypts the data with a random key and adds the data to the Write
// structure.
func NewWriteData(suite suites.Suite, ltsid byzcoin.InstanceID,
writeDarc darc.ID, X kyber.Point, data []byte) (wr *Write, err error) {
key := random.Bits(192, true, random.New())
wr = NewWrite(suite, ltsid, writeDarc, X, key)
wr.Data, err = AeadSeal(key, data)
if err != nil {
return nil, xerrors.Errorf("couldn't seal data: %v", err)
}
return
}

// CheckProof verifies that the write-request has actually been created with
// somebody having access to the secret key.
func (wr *Write) CheckProof(suite suite, writeID darc.ID) error {
Expand Down Expand Up @@ -97,6 +117,60 @@ func (wr *Write) CheckProof(suite suite, writeID darc.ID) error {
"%s\n%s", e.String(), wr.E.String())
}

// Decrypt calls AeadOpen to decrypt the data with the given key.
func (wr *Write) Decrypt(key []byte) ([]byte, error) {
return AeadOpen(key, wr.Data)
}

// This suggested length is from https://godoc.org/crypto/cipher#NewGCM example
const nonceLen = 12

// AeadSeal encrypts the given plaintext with the given key.
// It adds a 12-byte nonce to the ciphertext.
func AeadSeal(symKey, data []byte) ([]byte, error) {
block, err := aes.NewCipher(symKey)
if err != nil {
return nil,
xerrors.Errorf("creating aes cipher block instance: %v", err)
}

// Never use more than 2^32 random nonces with a given key because of the risk of a repeat.
nonce := make([]byte, nonceLen)
_, err = io.ReadFull(rand.Reader, nonce)
if err != nil {
return nil, xerrors.Errorf("reading nonce: %v", err)
}

aesgcm, err := cipher.NewGCM(block)
if err != nil {
return nil, xerrors.Errorf("creating aesgcm instance: %v", err)
}
encData := aesgcm.Seal(nil, nonce, data, nil)
encData = append(encData, nonce...)
return encData, nil
}

// AeadOpen decrypts a given ciphertext with the given key.
func AeadOpen(key, ciphertext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil,
xerrors.Errorf("creating aes cipher block instance: %v", err)
}

aesgcm, err := cipher.NewGCM(block)
if err != nil {
return nil, xerrors.Errorf("creating aesgcm instance: %v", err)
}

if len(ciphertext) < 12 {
return nil, xerrors.New("ciphertext too short")
}
nonce := ciphertext[len(ciphertext)-nonceLen:]
out, err := aesgcm.Open(nil, nonce, ciphertext[0:len(ciphertext)-nonceLen], nil)
return out, cothority.ErrorOrNil(err, "decrypting ciphertext")
}

type newLtsConfig struct {
byzcoin.Proof
}
Expand Down