Skip to content

Commit

Permalink
Break cipher batches up into individual transactions
Browse files Browse the repository at this point in the history
Before, the whole batch was a single blob. Now it's a sequence of
transactions, encrypted individually.
  • Loading branch information
jannikluhn committed Sep 24, 2021
1 parent d2efb96 commit f5f1e98
Show file tree
Hide file tree
Showing 11 changed files with 100 additions and 62 deletions.
50 changes: 31 additions & 19 deletions rolling-shutter/decryptor/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,18 @@ import (
type decryptionSigningData struct {
instanceID uint64
epochID uint64
cipherBatch []byte
decryptedBatch []byte
cipherBatch [][]byte
decryptedBatch [][]byte
}

// hashChain computes a hash over the given slice. The empty slice is mapped to the zero hash. A
// non-empty slice is mapped to `keccak256(data[-1], hashChain(data[:-1]))``.
func hashChain(data [][]byte) common.Hash {
h := make([]byte, 32)
for _, d := range data {
h = crypto.Keccak256(d, h)
}
return common.BytesToHash(h)
}

// hash computes the hash over the whole struct, which is the data that should be signed.
Expand All @@ -28,13 +38,8 @@ func (d decryptionSigningData) hash() common.Hash {
binary.BigEndian.PutUint64(b, d.epochID)
s.Write(b)

binary.BigEndian.PutUint64(b, uint64(len(d.cipherBatch)))
s.Write(b)
s.Write(d.cipherBatch)

binary.BigEndian.PutUint64(b, uint64(len(d.decryptedBatch)))
s.Write(b)
s.Write(d.decryptedBatch)
s.Write(hashChain(d.cipherBatch).Bytes())
s.Write(hashChain(d.decryptedBatch).Bytes())

h := s.Sum([]byte{})
return common.BytesToHash(h)
Expand All @@ -50,17 +55,24 @@ func (d decryptionSigningData) verify(signature *shbls.Signature, publicKey *shb
return shbls.Verify(signature, publicKey, d.hash().Bytes())
}

func decryptCipherBatch(cipherBatch []byte, key *shcrypto.EpochSecretKey) []byte {
// TODO: cipher batches contain many txs that should be decrypted individually. For now, we
// just pretend it's a single one.
encryptedMessage := shcrypto.EncryptedMessage{}
if err := encryptedMessage.Unmarshal(cipherBatch); err != nil {
return []byte{}
}
func decryptCipherBatch(cipherBatch [][]byte, key *shcrypto.EpochSecretKey) [][]byte {
decryptedBatch := make([][]byte, len(cipherBatch))

for i, tx := range cipherBatch {
encryptedMessage := shcrypto.EncryptedMessage{}
if err := encryptedMessage.Unmarshal(tx); err != nil {
decryptedBatch[i] = []byte{}
continue
}

decryptedBatch, err := encryptedMessage.Decrypt(key)
if err != nil {
return []byte{}
decryptedTx, err := encryptedMessage.Decrypt(key)
if err != nil {
decryptedBatch[i] = []byte{}
continue
}

decryptedBatch[i] = decryptedTx
}

return decryptedBatch
}
30 changes: 26 additions & 4 deletions rolling-shutter/decryptor/crypto_test.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,42 @@
package decryptor

import (
"bytes"
"crypto/rand"
"testing"

"github.com/ethereum/go-ethereum/crypto"
"gotest.tools/v3/assert"

"github.com/shutter-network/shutter/shlib/shcrypto/shbls"
)

func TestHashChain(t *testing.T) {
inputs := [][][]byte{
{},
{[]byte("value")},
{[]byte("v1"), []byte("v2")},
{[]byte("v1"), []byte("v2"), []byte("v3")},
}
expectedOutputs := [][]byte{
make([]byte, 32),
crypto.Keccak256([]byte("value"), make([]byte, 32)),
crypto.Keccak256([]byte("v2"), crypto.Keccak256([]byte("v1"), make([]byte, 32))),
crypto.Keccak256([]byte("v3"), crypto.Keccak256([]byte("v2"), crypto.Keccak256([]byte("v1"), make([]byte, 32)))),
}

for i := 0; i < len(inputs); i++ {
output := hashChain(inputs[i])
assert.Check(t, bytes.Equal(output.Bytes(), expectedOutputs[i]))
}
}

func TestSigning(t *testing.T) {
d := decryptionSigningData{
instanceID: 1,
epochID: 2,
cipherBatch: []byte("cipher"),
decryptedBatch: []byte("decrypted"),
cipherBatch: [][]byte{[]byte("ctx1"), []byte("ctx2")},
decryptedBatch: [][]byte{[]byte("dtx1"), []byte("dtx2")},
}
secretKey, publicKey, err := shbls.RandomKeyPair(rand.Reader)
assert.NilError(t, err)
Expand All @@ -25,8 +47,8 @@ func TestSigning(t *testing.T) {
modD := decryptionSigningData{
instanceID: 1,
epochID: 2,
cipherBatch: []byte("cipher"),
decryptedBatch: []byte("different"),
cipherBatch: [][]byte{},
decryptedBatch: [][]byte{},
}
assert.Check(t, d.hash() != modD.hash())

Expand Down
4 changes: 2 additions & 2 deletions rolling-shutter/decryptor/dcrdb/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion rolling-shutter/decryptor/dcrdb/query.sql
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ WHERE epoch_id = $1;

-- name: InsertCipherBatch :execresult
INSERT INTO decryptor.cipher_batch (
epoch_id, data
epoch_id, transactions
) VALUES (
$1, $2
)
Expand Down
12 changes: 6 additions & 6 deletions rolling-shutter/decryptor/dcrdb/query.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions rolling-shutter/decryptor/dcrdb/schema.sql
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
-- schema-version: 2 --
-- schema-version: 3 --
-- Please change the version above if you make incompatible changes to
-- the schema. We'll use this to check we're using the right schema.

Expand All @@ -7,7 +7,7 @@ CREATE SCHEMA IF NOT EXISTS decryptor;

CREATE TABLE IF NOT EXISTS decryptor.cipher_batch (
epoch_id bytea PRIMARY KEY,
data bytea
transactions bytea[]
);
CREATE TABLE IF NOT EXISTS decryptor.decryption_key (
epoch_id bytea PRIMARY KEY,
Expand Down
8 changes: 4 additions & 4 deletions rolling-shutter/decryptor/inputhandling.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ func handleCipherBatchInput(
cipherBatch *shmsg.CipherBatch,
) ([]shmsg.P2PMessage, error) {
tag, err := db.InsertCipherBatch(ctx, dcrdb.InsertCipherBatchParams{
EpochID: medley.Uint64EpochIDToBytes(cipherBatch.EpochID),
Data: cipherBatch.Data,
EpochID: medley.Uint64EpochIDToBytes(cipherBatch.EpochID),
Transactions: cipherBatch.Transactions,
})
if err != nil {
return nil, err
Expand Down Expand Up @@ -83,11 +83,11 @@ func handleEpoch(
return nil, errors.Wrapf(err, "invalid decryption key for epoch %d in db", epochID)
}

decryptedBatch := decryptCipherBatch(cipherBatch.Data, decryptionKey)
decryptedBatch := decryptCipherBatch(cipherBatch.Transactions, decryptionKey)
signingData := decryptionSigningData{
instanceID: 0,
epochID: epochID,
cipherBatch: cipherBatch.Data,
cipherBatch: cipherBatch.Transactions,
decryptedBatch: decryptedBatch,
}
signedHash := signingData.hash().Bytes()
Expand Down
17 changes: 8 additions & 9 deletions rolling-shutter/decryptor/inputhandling_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,29 +84,28 @@ func TestInsertCipherBatchIntegration(t *testing.T) {
config := newTestConfig(t)

m := &shmsg.CipherBatch{
EpochID: 100,
Data: []byte("hello"),
EpochID: 100,
Transactions: [][]byte{[]byte("tx1"), []byte("tx2")},
}
msgs, err := handleCipherBatchInput(ctx, config, db, m)
assert.NilError(t, err)

mStored, err := db.GetCipherBatch(ctx, medley.Uint64EpochIDToBytes(m.EpochID))
assert.NilError(t, err)
assert.Check(t, medley.BytesEpochIDToUint64(mStored.EpochID) == m.EpochID)
assert.Check(t, bytes.Equal(mStored.Data, m.Data))

assert.DeepEqual(t, mStored.Transactions, m.Transactions)
assert.Check(t, len(msgs) == 0)

m2 := &shmsg.CipherBatch{
EpochID: 100,
Data: []byte("hello2"),
EpochID: 100,
Transactions: [][]byte{[]byte("tx3")},
}
msgs, err = handleCipherBatchInput(ctx, config, db, m2)
assert.NilError(t, err)

m2Stored, err := db.GetCipherBatch(ctx, medley.Uint64EpochIDToBytes(m.EpochID))
assert.NilError(t, err)
assert.Check(t, bytes.Equal(m2Stored.Data, m.Data))
assert.DeepEqual(t, m2Stored.Transactions, m.Transactions)

assert.Check(t, len(msgs) == 0)
}
Expand All @@ -122,8 +121,8 @@ func TestHandleEpochIntegration(t *testing.T) {
config := newTestConfig(t)

cipherBatchMsg := &shmsg.CipherBatch{
EpochID: 123,
Data: []byte("hello"),
EpochID: 123,
Transactions: [][]byte{[]byte("tx1")},
}
msgs, err := handleCipherBatchInput(ctx, config, db, cipherBatchMsg)
assert.NilError(t, err)
Expand Down
16 changes: 10 additions & 6 deletions rolling-shutter/mocknode/mocknode.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,15 +192,19 @@ func (m *MockNode) sendDecryptionTrigger(ctx context.Context, epochID uint64) er
func (m *MockNode) sendCipherBatchMessage(ctx context.Context, epochID uint64, eonPublicKey *shcrypto.EonPublicKey) error {
log.Printf("sending cipher batch for epoch %d", epochID)

cipherBatch, err := encryptRandomMessage(epochID, eonPublicKey)
if err != nil {
return err
txs := [][]byte{}
for i := 0; i < 3; i++ {
tx, err := encryptRandomMessage(epochID, eonPublicKey)
if err != nil {
return err
}
txs = append(txs, tx)
}

msg := &shmsg.CipherBatch{
InstanceID: m.Config.InstanceID,
EpochID: epochID,
Data: cipherBatch,
InstanceID: m.Config.InstanceID,
EpochID: epochID,
Transactions: txs,
}
msgBytes, err := proto.Marshal(msg)
if err != nil {
Expand Down
17 changes: 9 additions & 8 deletions rolling-shutter/shmsg/gossip.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion rolling-shutter/shmsg/gossip.proto
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ message DecryptionKey {
message CipherBatch {
uint64 instanceID = 1;
uint64 epochID = 2;
bytes data = 3;
repeated bytes transactions = 3;
}

message AggregatedDecryptionSignature {
Expand Down

0 comments on commit f5f1e98

Please sign in to comment.