From ac1d2cc9a0c15cfcc319f6851705cb3a20266261 Mon Sep 17 00:00:00 2001 From: Gustavo Frederico Date: Tue, 26 Oct 2021 23:39:42 -0400 Subject: [PATCH] Identification of aborts with unit test. --- common/logger.go | 13 +++ crypto/utils.go | 6 ++ ecdsa/signing/ecdsa-signing.pb.go | 109 ++++++++++++++++------ ecdsa/signing/identification_6.go | 4 +- ecdsa/signing/identification_7.go | 6 +- ecdsa/signing/identification_prep.go | 31 +++++++ ecdsa/signing/local_party.go | 24 +++-- ecdsa/signing/local_party_test.go | 133 +++++++++++++++++++++++++++ ecdsa/signing/messages.go | 24 ++++- ecdsa/signing/presign_2.go | 3 +- ecdsa/signing/presign_3.go | 16 ++-- ecdsa/signing/rounds.go | 15 ++- ecdsa/signing/sign_4.go | 54 ++++++++--- ecdsa/signing/sign_out.go | 7 +- protob/ecdsa-signing.proto | 2 + tss/message.pb.go | 2 +- tss/party.go | 54 +++++------ 17 files changed, 400 insertions(+), 103 deletions(-) create mode 100644 ecdsa/signing/identification_prep.go diff --git a/common/logger.go b/common/logger.go index e76abd5..6ad317b 100644 --- a/common/logger.go +++ b/common/logger.go @@ -7,7 +7,20 @@ package common import ( + "math/big" + "github.com/ipfs/go-log" ) var Logger = log.Logger("tss-lib") + +func FormatBigInt(a *big.Int) string { + if a == nil { + return "" + } + var aux = new(big.Int).SetInt64(0xFFFFFFFF) + return func(i *big.Int) string { + return new(big.Int).And(i, aux).Text(16) + }(a) +} + diff --git a/crypto/utils.go b/crypto/utils.go index 71ee519..24628a8 100644 --- a/crypto/utils.go +++ b/crypto/utils.go @@ -25,3 +25,9 @@ func GenerateNTildei(safePrimes [2]*big.Int) (NTildei, h1i, h2i *big.Int, err er h2 := common.GetRandomGeneratorOfTheQuadraticResidue(NTildei) return NTildei, h1, h2, nil } + +func FormatECPoint(p *ECPoint) string { + x := common.FormatBigInt(p.X()) + y := common.FormatBigInt(p.Y()) + return "(" + x + "," + y + ")" +} diff --git a/ecdsa/signing/ecdsa-signing.pb.go b/ecdsa/signing/ecdsa-signing.pb.go index d374702..04cd8f2 100644 --- a/ecdsa/signing/ecdsa-signing.pb.go +++ b/ecdsa/signing/ecdsa-signing.pb.go @@ -7,7 +7,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.17.3 +// protoc v3.18.1 // source: protob/ecdsa-signing.proto package signing @@ -310,6 +310,44 @@ func (x *SignRound4Message) GetSigmaShare() []byte { return nil } +type SignRound4AbortingMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *SignRound4AbortingMessage) Reset() { + *x = SignRound4AbortingMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_protob_ecdsa_signing_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SignRound4AbortingMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SignRound4AbortingMessage) ProtoMessage() {} + +func (x *SignRound4AbortingMessage) ProtoReflect() protoreflect.Message { + mi := &file_protob_ecdsa_signing_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SignRound4AbortingMessage.ProtoReflect.Descriptor instead. +func (*SignRound4AbortingMessage) Descriptor() ([]byte, []int) { + return file_protob_ecdsa_signing_proto_rawDescGZIP(), []int{4} +} + type IdentificationRound6Message struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -324,7 +362,7 @@ type IdentificationRound6Message struct { func (x *IdentificationRound6Message) Reset() { *x = IdentificationRound6Message{} if protoimpl.UnsafeEnabled { - mi := &file_protob_ecdsa_signing_proto_msgTypes[4] + mi := &file_protob_ecdsa_signing_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -337,7 +375,7 @@ func (x *IdentificationRound6Message) String() string { func (*IdentificationRound6Message) ProtoMessage() {} func (x *IdentificationRound6Message) ProtoReflect() protoreflect.Message { - mi := &file_protob_ecdsa_signing_proto_msgTypes[4] + mi := &file_protob_ecdsa_signing_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -350,7 +388,7 @@ func (x *IdentificationRound6Message) ProtoReflect() protoreflect.Message { // Deprecated: Use IdentificationRound6Message.ProtoReflect.Descriptor instead. func (*IdentificationRound6Message) Descriptor() ([]byte, []int) { - return file_protob_ecdsa_signing_proto_rawDescGZIP(), []int{4} + return file_protob_ecdsa_signing_proto_rawDescGZIP(), []int{5} } func (x *IdentificationRound6Message) GetH() []byte { @@ -393,7 +431,7 @@ type TempDataDumpMessage struct { func (x *TempDataDumpMessage) Reset() { *x = TempDataDumpMessage{} if protoimpl.UnsafeEnabled { - mi := &file_protob_ecdsa_signing_proto_msgTypes[5] + mi := &file_protob_ecdsa_signing_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -406,7 +444,7 @@ func (x *TempDataDumpMessage) String() string { func (*TempDataDumpMessage) ProtoMessage() {} func (x *TempDataDumpMessage) ProtoReflect() protoreflect.Message { - mi := &file_protob_ecdsa_signing_proto_msgTypes[5] + mi := &file_protob_ecdsa_signing_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -419,7 +457,7 @@ func (x *TempDataDumpMessage) ProtoReflect() protoreflect.Message { // Deprecated: Use TempDataDumpMessage.ProtoReflect.Descriptor instead. func (*TempDataDumpMessage) Descriptor() ([]byte, []int) { - return file_protob_ecdsa_signing_proto_rawDescGZIP(), []int{5} + return file_protob_ecdsa_signing_proto_rawDescGZIP(), []int{6} } func (x *TempDataDumpMessage) GetDataDump() []byte { @@ -476,22 +514,24 @@ var file_protob_ecdsa_signing_proto_rawDesc = []byte{ 0x73, 0x74, 0x61, 0x72, 0x22, 0x33, 0x0a, 0x11, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x34, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x53, 0x69, 0x67, 0x6d, 0x61, 0x53, 0x68, 0x61, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x53, - 0x69, 0x67, 0x6d, 0x61, 0x53, 0x68, 0x61, 0x72, 0x65, 0x22, 0x89, 0x01, 0x0a, 0x1b, 0x49, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x6e, - 0x64, 0x36, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x0c, 0x0a, 0x01, 0x48, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x01, 0x48, 0x12, 0x1a, 0x0a, 0x08, 0x4d, 0x75, 0x6c, 0x50, 0x72, - 0x6f, 0x6f, 0x66, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x08, 0x4d, 0x75, 0x6c, 0x50, 0x72, - 0x6f, 0x6f, 0x66, 0x12, 0x24, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x53, 0x68, 0x61, 0x72, - 0x65, 0x45, 0x6e, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x44, 0x65, 0x6c, 0x74, - 0x61, 0x53, 0x68, 0x61, 0x72, 0x65, 0x45, 0x6e, 0x63, 0x12, 0x1a, 0x0a, 0x08, 0x44, 0x65, 0x63, - 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x08, 0x44, 0x65, 0x63, - 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0x4d, 0x0a, 0x13, 0x54, 0x65, 0x6d, 0x70, 0x44, 0x61, 0x74, - 0x61, 0x44, 0x75, 0x6d, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, - 0x44, 0x61, 0x74, 0x61, 0x44, 0x75, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, - 0x44, 0x61, 0x74, 0x61, 0x44, 0x75, 0x6d, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x52, 0x6f, 0x75, 0x6e, - 0x64, 0x4e, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x52, 0x6f, 0x75, 0x6e, - 0x64, 0x4e, 0x75, 0x6d, 0x42, 0x0f, 0x5a, 0x0d, 0x65, 0x63, 0x64, 0x73, 0x61, 0x2f, 0x73, 0x69, - 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x69, 0x67, 0x6d, 0x61, 0x53, 0x68, 0x61, 0x72, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x53, 0x69, 0x67, + 0x6e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x34, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x89, 0x01, 0x0a, 0x1b, 0x49, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x36, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x0c, 0x0a, 0x01, 0x48, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x01, 0x48, 0x12, 0x1a, 0x0a, 0x08, 0x4d, 0x75, 0x6c, 0x50, 0x72, 0x6f, 0x6f, 0x66, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x08, 0x4d, 0x75, 0x6c, 0x50, 0x72, 0x6f, 0x6f, 0x66, + 0x12, 0x24, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x53, 0x68, 0x61, 0x72, 0x65, 0x45, 0x6e, + 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x53, 0x68, + 0x61, 0x72, 0x65, 0x45, 0x6e, 0x63, 0x12, 0x1a, 0x0a, 0x08, 0x44, 0x65, 0x63, 0x50, 0x72, 0x6f, + 0x6f, 0x66, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x08, 0x44, 0x65, 0x63, 0x50, 0x72, 0x6f, + 0x6f, 0x66, 0x22, 0x4d, 0x0a, 0x13, 0x54, 0x65, 0x6d, 0x70, 0x44, 0x61, 0x74, 0x61, 0x44, 0x75, + 0x6d, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x44, 0x61, 0x74, + 0x61, 0x44, 0x75, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x44, 0x61, 0x74, + 0x61, 0x44, 0x75, 0x6d, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x4e, 0x75, + 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x4e, 0x75, + 0x6d, 0x42, 0x0f, 0x5a, 0x0d, 0x65, 0x63, 0x64, 0x73, 0x61, 0x2f, 0x73, 0x69, 0x67, 0x6e, 0x69, + 0x6e, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -506,14 +546,15 @@ func file_protob_ecdsa_signing_proto_rawDescGZIP() []byte { return file_protob_ecdsa_signing_proto_rawDescData } -var file_protob_ecdsa_signing_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_protob_ecdsa_signing_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_protob_ecdsa_signing_proto_goTypes = []interface{}{ (*PreSignRound1Message)(nil), // 0: binance.tsslib.ecdsa.signing.PreSignRound1Message (*PreSignRound2Message)(nil), // 1: binance.tsslib.ecdsa.signing.PreSignRound2Message (*PreSignRound3Message)(nil), // 2: binance.tsslib.ecdsa.signing.PreSignRound3Message (*SignRound4Message)(nil), // 3: binance.tsslib.ecdsa.signing.SignRound4Message - (*IdentificationRound6Message)(nil), // 4: binance.tsslib.ecdsa.signing.IdentificationRound6Message - (*TempDataDumpMessage)(nil), // 5: binance.tsslib.ecdsa.signing.TempDataDumpMessage + (*SignRound4AbortingMessage)(nil), // 4: binance.tsslib.ecdsa.signing.SignRound4AbortingMessage + (*IdentificationRound6Message)(nil), // 5: binance.tsslib.ecdsa.signing.IdentificationRound6Message + (*TempDataDumpMessage)(nil), // 6: binance.tsslib.ecdsa.signing.TempDataDumpMessage } var file_protob_ecdsa_signing_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type @@ -578,7 +619,7 @@ func file_protob_ecdsa_signing_proto_init() { } } file_protob_ecdsa_signing_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*IdentificationRound6Message); i { + switch v := v.(*SignRound4AbortingMessage); i { case 0: return &v.state case 1: @@ -590,6 +631,18 @@ func file_protob_ecdsa_signing_proto_init() { } } file_protob_ecdsa_signing_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IdentificationRound6Message); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protob_ecdsa_signing_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TempDataDumpMessage); i { case 0: return &v.state @@ -608,7 +661,7 @@ func file_protob_ecdsa_signing_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_protob_ecdsa_signing_proto_rawDesc, NumEnums: 0, - NumMessages: 6, + NumMessages: 7, NumExtensions: 0, NumServices: 0, }, diff --git a/ecdsa/signing/identification_6.go b/ecdsa/signing/identification_6.go index 049e025..dc219da 100644 --- a/ecdsa/signing/identification_6.go +++ b/ecdsa/signing/identification_6.go @@ -17,8 +17,8 @@ import ( ) func newRound6(params *tss.Parameters, key *keygen.LocalPartySaveData, data *common.SignatureData, temp *localTempData, out chan<- tss.Message, end chan<- common.SignatureData) tss.Round { - return &identification6{&sign4{&presign3{&presign2{&presign1{ - &base{params, key, data, temp, out, end, make([]bool, len(params.Parties().IDs())), false, 3}}}}}} + return &identification6{&identificationPrep{&sign4{&presign3{&presign2{&presign1{ + &base{params, key, data, temp, out, end, make([]bool, len(params.Parties().IDs())), false, 3}}}}, false}}} } func (round *identification6) Start() *tss.Error { diff --git a/ecdsa/signing/identification_7.go b/ecdsa/signing/identification_7.go index 657248d..56a20ef 100644 --- a/ecdsa/signing/identification_7.go +++ b/ecdsa/signing/identification_7.go @@ -16,8 +16,8 @@ import ( ) func newRound7(params *tss.Parameters, key *keygen.LocalPartySaveData, data *common.SignatureData, temp *localTempData, out chan<- tss.Message, end chan<- common.SignatureData) tss.Round { - return &identification7{&identification6{&sign4{&presign3{&presign2{&presign1{ - &base{params, key, data, temp, out, end, make([]bool, len(params.Parties().IDs())), false, 3}}}}}}} + return &identification7{&identification6{&identificationPrep{&sign4{&presign3{&presign2{&presign1{ + &base{params, key, data, temp, out, end, make([]bool, len(params.Parties().IDs())), false, 3}}}}, false}}}} } func (round *identification7) Start() *tss.Error { @@ -50,7 +50,7 @@ func (round *identification7) Start() *tss.Error { } proofDec := round.temp.r6msgProofDec[j] - ok = proofDec.Verify(round.EC(), round.key.PaillierPKs[j], round.temp.r6msgDeltaShareEnc[j], round.temp.r3msgDeltaShare[j], round.key.NTildei, round.key.H1i, round.key.H2i) + ok = proofDec.Verify(round.EC(), round.key.PaillierPKs[j], round.temp.r6msgDeltaShareEnc[j], round.temp.r3msg𝛿j[j], round.key.NTildei, round.key.H1i, round.key.H2i) if !ok { errChs <- round.WrapError(errors.New("round7: proofdec verify failed"), Pj) return diff --git a/ecdsa/signing/identification_prep.go b/ecdsa/signing/identification_prep.go new file mode 100644 index 0000000..8a24693 --- /dev/null +++ b/ecdsa/signing/identification_prep.go @@ -0,0 +1,31 @@ +// Copyright © 2021 Swingby + +package signing + +import ( + "errors" + + "github.com/binance-chain/tss-lib/tss" +) + +func (round *identificationPrep) Start() *tss.Error { + if round.started { + return round.WrapError(errors.New("round already started")) + } + round.number = 5 + round.started = true + round.AbortingSigning = true + round.setOK() + return nil +} + +func (round *identificationPrep) NextRound() tss.Round { + round.started = false + return &identification6{round} +} + +func (round *identificationPrep) setOK() { + for j := range round.ok { + round.ok[j] = true + } +} \ No newline at end of file diff --git a/ecdsa/signing/local_party.go b/ecdsa/signing/local_party.go index fd9b327..f99fab9 100644 --- a/ecdsa/signing/local_party.go +++ b/ecdsa/signing/local_party.go @@ -40,6 +40,7 @@ type ( out chan<- tss.Message end chan<- common.SignatureData startRndNum int + Aborting bool } // localMessageStore struct { @@ -96,10 +97,11 @@ type ( r2msgChiF []*big.Int r2msgChiProof []*zkpaffg.ProofAffg r2msgProofLogstar []*zkplogstar.ProofLogstar - r3msgDeltaShare []*big.Int - r3msgBigDeltaShare []*crypto.ECPoint + r3msg𝛿j []*big.Int + r3msgΔj []*crypto.ECPoint r3msgProofLogstar []*zkplogstar.ProofLogstar - r4msgSigmaShare []*big.Int + r4msg𝜎j []*big.Int + r4msgAborting []bool // for identification r6msgH []*big.Int r6msgProofMul []*zkpmul.ProofMul @@ -157,10 +159,11 @@ func NewLocalParty( p.temp.r2msgChiF = make([]*big.Int, partyCount) p.temp.r2msgChiProof = make([]*zkpaffg.ProofAffg, partyCount) p.temp.r2msgProofLogstar = make([]*zkplogstar.ProofLogstar, partyCount) - p.temp.r3msgDeltaShare = make([]*big.Int, partyCount) - p.temp.r3msgBigDeltaShare = make([]*crypto.ECPoint, partyCount) + p.temp.r3msg𝛿j = make([]*big.Int, partyCount) + p.temp.r3msgΔj = make([]*crypto.ECPoint, partyCount) p.temp.r3msgProofLogstar = make([]*zkplogstar.ProofLogstar, partyCount) - p.temp.r4msgSigmaShare = make([]*big.Int, partyCount) + p.temp.r4msg𝜎j = make([]*big.Int, partyCount) + p.temp.r4msgAborting = make([]bool, partyCount) // for identification p.temp.r6msgH = make([]*big.Int, partyCount) p.temp.r6msgProofMul = make([]*zkpmul.ProofMul, partyCount) @@ -261,12 +264,12 @@ func (p *LocalParty) StoreMessage(msg tss.ParsedMessage) (bool, *tss.Error) { p.temp.r2msgChiProof[fromPIdx] = proofChi case *PreSignRound3Message: r3msg := msg.Content().(*PreSignRound3Message) - p.temp.r3msgDeltaShare[fromPIdx] = r3msg.UnmarshalDeltaShare() + p.temp.r3msg𝛿j[fromPIdx] = r3msg.UnmarshalDeltaShare() BigDeltaShare, err := r3msg.UnmarshalBigDeltaShare(p.params.EC()) if err != nil { return false, p.WrapError(err, msg.GetFrom()) } - p.temp.r3msgBigDeltaShare[fromPIdx] = BigDeltaShare + p.temp.r3msgΔj[fromPIdx] = BigDeltaShare proofLogStar, err := r3msg.UnmarshalProofLogstar(p.params.EC()) if err != nil { return false, p.WrapError(err, msg.GetFrom()) @@ -274,7 +277,10 @@ func (p *LocalParty) StoreMessage(msg tss.ParsedMessage) (bool, *tss.Error) { p.temp.r3msgProofLogstar[fromPIdx] = proofLogStar case *SignRound4Message: r4msg := msg.Content().(*SignRound4Message) - p.temp.r4msgSigmaShare[fromPIdx] = r4msg.UnmarshalSigmaShare() + p.temp.r4msg𝜎j[fromPIdx] = r4msg.UnmarshalSigmaShare() + case *SignRound4AbortingMessage: + p.temp.r4msgAborting[fromPIdx] = true + p.Aborting = true case *IdentificationRound6Message: r6msg := msg.Content().(*IdentificationRound6Message) p.temp.r6msgH[fromPIdx] = r6msg.UnmarshalH() diff --git a/ecdsa/signing/local_party_test.go b/ecdsa/signing/local_party_test.go index 7913819..ce7e0b2 100644 --- a/ecdsa/signing/local_party_test.go +++ b/ecdsa/signing/local_party_test.go @@ -11,9 +11,12 @@ import ( "fmt" "math/big" "runtime" + "strings" "sync/atomic" "testing" + "github.com/binance-chain/tss-lib/crypto" + zkplogstar "github.com/binance-chain/tss-lib/crypto/zkp/logstar" "github.com/btcsuite/btcd/btcec" "github.com/ipfs/go-log" "github.com/stretchr/testify/assert" @@ -27,6 +30,8 @@ import ( const ( testParticipants = test.TestParticipants testThreshold = test.TestThreshold + maliciousPartySimulatingAbort = 2 + innocentPartySimulatingAbort = 1 ) func setUp(level string) { @@ -248,6 +253,134 @@ signing: } } +// +func identifiedAbortUpdater(party tss.Party, msg tss.Message, parties []*LocalParty, errCh chan<- *tss.Error) { + // do not send a message from this party back to itself + if party.PartyID() == msg.GetFrom() { + return + } + bz, _, err := msg.WireBytes() + if err != nil { + errCh <- party.WrapError(err) + return + } + pMsg, err := tss.ParseWireMessage(bz, msg.GetFrom(), msg.IsBroadcast()) + if err != nil { + errCh <- party.WrapError(err) + return + } + + // Intercepting a round 3 message to inject a bad zk-proof and trigger an abort + if strings.HasSuffix(msg.Type(),"PreSignRound3Message") && !msg.IsBroadcast() && + msg.GetFrom().Index == maliciousPartySimulatingAbort && + len(msg.GetTo()) > 0 && msg.GetTo()[0].Index==innocentPartySimulatingAbort { + meta := tss.MessageRouting{ + From: msg.GetFrom(), + To: msg.GetTo(), + IsBroadcast: false, + } + i := msg.GetFrom().Index + j := msg.GetTo()[0].Index + + common.Logger.Debugf("intercepting and changing message %s from %s", msg.Type(), msg.GetFrom()) + round := party.Round().(*presign3) + otherRound := parties[i].Round().(*presign3) + ec := tss.EC() + + common.Logger.Debugf(" test - fake proof - i:%v, j: %v, PK: %v, K(C): %v, Γ(g): %v, NTildej(NCap): %v, " + + "H1j(s): %v, H2j(t): %v, ki(x): %v, 𝜌i: %v", + i,j, common.FormatBigInt(round.key.PaillierPKs[i].N), common.FormatBigInt(round.temp.K), + crypto.FormatECPoint(round.temp.Γ), + common.FormatBigInt(round.key.NTildej[j]), common.FormatBigInt(round.key.H1j[j]), common.FormatBigInt(round.key.H2j[j]), + common.FormatBigInt(otherRound.temp.ki), common.FormatBigInt(otherRound.temp.𝜌i)) + + proof, errP := zkplogstar.NewProof(ec, round.key.PaillierPKs[i], round.temp.K, &crypto.ECPoint{}, round.temp.Γ, round.key.NTildej[j], + round.key.H1j[j], round.key.H2j[j], otherRound.temp.ki, otherRound.temp.𝜌i) + if errP!=nil { + common.Logger.Errorf("error changing message %s from %s", msg.Type(), msg.GetFrom()) + } + otherRound.temp.Δi = crypto.ScalarBaseMult(ec, big.NewInt(1)) // trigger inequality for Δ in sign_4 + + r3msg := NewPreSignRound3Message(msg.GetTo()[0], msg.GetFrom(), otherRound.temp.𝛿i, otherRound.temp.Δi, proof) + // repackaging the malicious message + pMsg = tss.NewMessage(meta, r3msg.Content(), tss.NewMessageWrapper(meta, r3msg.Content())) + } + + if _, errUpdate := party.Update(pMsg); errUpdate != nil { + if errUpdate.Culprits()!= nil && len(errUpdate.Culprits())>0 { + errCh <- errUpdate + } + } +} + +func TestAbortIdentification(t *testing.T) { + setUp("debug") + threshold := testThreshold + + // PHASE: load keygen fixtures + keys, signPIDs, err := keygen.LoadKeygenTestFixturesRandomSet(testThreshold+1, testParticipants) + assert.NoError(t, err, "should load keygen fixtures") + assert.Equal(t, testThreshold+1, len(keys)) + assert.Equal(t, testThreshold+1, len(signPIDs)) + + // PHASE: signing + // use a shuffled selection of the list of parties for this test + p2pCtx := tss.NewPeerContext(signPIDs) + parties := make([]*LocalParty, 0, len(signPIDs)) + + errCh := make(chan *tss.Error, len(signPIDs)) + outCh := make(chan tss.Message, len(signPIDs)) + endCh := make(chan common.SignatureData, len(signPIDs)) + + updater := identifiedAbortUpdater + + // init the parties + for i := 0; i < len(signPIDs); i++ { + params := tss.NewParameters(tss.S256(), p2pCtx, signPIDs[i], len(signPIDs), threshold) + + keyDerivationDelta := big.NewInt(0) + P := NewLocalParty(big.NewInt(42), params, keys[i], keyDerivationDelta, outCh, endCh).(*LocalParty) + parties = append(parties, P) + go func(P *LocalParty) { + if err := P.Start(); err != nil { + errCh <- err + } + }(P) + } + +signing: + for { + select { + case errS := <-errCh: + assert.NotNil(t, errS, "there should have been an error") + assert.EqualValues(t, len(errS.Culprits()), 1, "there should have been one culprit") + assert.EqualValues(t, errS.Culprits()[0].Index, maliciousPartySimulatingAbort, "error in test in identification of the malicious party") + break signing + + case msg := <-outCh: + dest := msg.GetTo() + if dest == nil { + for _, P := range parties { + if P.PartyID().Index == msg.GetFrom().Index { + continue + } + go updater(P, msg, parties, errCh) + } + } else { + if dest[0].Index == msg.GetFrom().Index { + t.Fatalf("party %d tried to send a message to itself (%d)", dest[0].Index, msg.GetFrom().Index) + } + go updater(parties[dest[0].Index], msg, parties, errCh) + } + + case sigData := <-endCh: + common.Logger.Debugf("sigData: %v", sigData) + assert.FailNow(t, "signing should not succeed in this test") + break signing + } + } +} + func TestFillTo32BytesInPlace(t *testing.T) { s := big.NewInt(123456789) normalizedS := padToLengthBytesInPlace(s.Bytes(), 32) diff --git a/ecdsa/signing/messages.go b/ecdsa/signing/messages.go index 26ee96f..c91ad9b 100644 --- a/ecdsa/signing/messages.go +++ b/ecdsa/signing/messages.go @@ -161,8 +161,8 @@ func (m *PreSignRound2Message) UnmarshalLogstarProof(ec elliptic.Curve) (*zkplog func NewPreSignRound3Message( to, from *tss.PartyID, - DeltaShare *big.Int, - BigDeltaShare *crypto.ECPoint, + 𝛿i *big.Int, + Δi *crypto.ECPoint, ProofLogstar *zkplogstar.ProofLogstar, ) tss.ParsedMessage { meta := tss.MessageRouting{ @@ -170,10 +170,10 @@ func NewPreSignRound3Message( To: []*tss.PartyID{to}, IsBroadcast: false, } - BigDeltaShareBzs := BigDeltaShare.Bytes() + BigDeltaShareBzs := Δi.Bytes() ProofBz := ProofLogstar.Bytes() content := &PreSignRound3Message{ - DeltaShare: DeltaShare.Bytes(), + DeltaShare: 𝛿i.Bytes(), BigDeltaShare: BigDeltaShareBzs[:], ProofLogstar: ProofBz[:], } @@ -202,6 +202,22 @@ func (m *PreSignRound3Message) UnmarshalProofLogstar(ec elliptic.Curve) (*zkplog // ----- // +func NewSignRound4AbortingMessage( + from *tss.PartyID, +) tss.ParsedMessage { + meta := tss.MessageRouting{ + From: from, + IsBroadcast: true, + } + content := &SignRound4AbortingMessage{} + msg := tss.NewMessageWrapper(meta, content) + return tss.NewMessage(meta, content, msg) +} + +func (m *SignRound4AbortingMessage) ValidateBasic() bool { + return m != nil +} + func NewSignRound4Message( from *tss.PartyID, SigmaShare *big.Int, diff --git a/ecdsa/signing/presign_2.go b/ecdsa/signing/presign_2.go index fc05436..e854328 100644 --- a/ecdsa/signing/presign_2.go +++ b/ecdsa/signing/presign_2.go @@ -138,8 +138,7 @@ func (round *presign2) Start() *tss.Error { round.temp.Γi = Γi // retire unused variables - round.temp.G = nil - round.temp.𝜈i = nil + round.temp.r1msg𝜓0ij = make([]*zkpenc.ProofEnc, round.PartyCount()) // GF TODO return nil diff --git a/ecdsa/signing/presign_3.go b/ecdsa/signing/presign_3.go index 5c28e9b..3a183e3 100644 --- a/ecdsa/signing/presign_3.go +++ b/ecdsa/signing/presign_3.go @@ -136,6 +136,11 @@ func (round *presign3) Start() *tss.Error { errChs = make(chan *tss.Error, len(round.Parties().IDs())-1) wg = sync.WaitGroup{} + round.temp.𝛿i = 𝛿i + round.temp.𝜒i = 𝜒i + round.temp.Δi = Δi + round.temp.Γ = Γ + for j, Pj := range round.Parties().IDs() { if j == i { continue @@ -161,17 +166,12 @@ func (round *presign3) Start() *tss.Error { return err } - round.temp.𝛿i = 𝛿i - round.temp.𝜒i = 𝜒i - round.temp.Δi = Δi - round.temp.Γ = Γ // retire unused variables round.temp.w = nil round.temp.BigWs = nil round.temp.𝛾i = nil round.temp.Γi = nil - round.temp.K = nil - round.temp.𝜌i = nil + round.temp.DeltaShareBetas = nil round.temp.ChiShareBetas = nil round.temp.DeltaShareAlphas = nil @@ -189,7 +189,7 @@ func (round *presign3) Start() *tss.Error { } func (round *presign3) Update() (bool, *tss.Error) { - for j, msg := range round.temp.r3msgDeltaShare { + for j, msg := range round.temp.r3msg𝛿j { if round.ok[j] { continue } @@ -210,5 +210,5 @@ func (round *presign3) CanAccept(msg tss.ParsedMessage) bool { func (round *presign3) NextRound() tss.Round { round.started = false - return &sign4{round} + return &sign4{round, false} } diff --git a/ecdsa/signing/rounds.go b/ecdsa/signing/rounds.go index 913bc6b..ce6c3a1 100644 --- a/ecdsa/signing/rounds.go +++ b/ecdsa/signing/rounds.go @@ -39,6 +39,10 @@ type ( } sign4 struct { *presign3 + AbortingSigning bool + } + identificationPrep struct { + *sign4 } signout struct { *sign4 @@ -46,7 +50,7 @@ type ( // identification rounds identification6 struct { - *sign4 + *identificationPrep } identification7 struct { *identification6 @@ -112,7 +116,14 @@ func (round *base) resetOK() { } } +func (round *base) setOK() { + for j := range round.ok { + round.ok[j] = true + } +} + func (round *base) Dump(dumpCh chan tss.ParsedMessage) { DumpMsg := NewTempDataDumpMessage(round.PartyID(), *round.temp, round.number) dumpCh <- DumpMsg -} \ No newline at end of file +} + diff --git a/ecdsa/signing/sign_4.go b/ecdsa/signing/sign_4.go index f14142f..f12ab00 100644 --- a/ecdsa/signing/sign_4.go +++ b/ecdsa/signing/sign_4.go @@ -20,7 +20,7 @@ import ( func newRound4(params *tss.Parameters, key *keygen.LocalPartySaveData, data *common.SignatureData, temp *localTempData, out chan<- tss.Message, end chan<- common.SignatureData) tss.Round { return &sign4{&presign3{&presign2{&presign1{ - &base{params, key, data, temp, out, end, make([]bool, len(params.Parties().IDs())), false, 4}}}}} + &base{params, key, data, temp, out, end, make([]bool, len(params.Parties().IDs())), false, 4}}}}, false} } func (round *sign4) Start() *tss.Error { @@ -30,6 +30,7 @@ func (round *sign4) Start() *tss.Error { round.number = 4 round.started = true round.resetOK() + round.resetAborting() i := round.PartyID().Index round.ok[i] = true @@ -45,10 +46,10 @@ func (round *sign4) Start() *tss.Error { go func(j int, Pj *tss.PartyID) { defer wg.Done() Kj := round.temp.r1msgK[j] - BigDeltaSharej := round.temp.r3msgBigDeltaShare[j] + Δj := round.temp.r3msgΔj[j] ψDoublePrimeij := round.temp.r3msgProofLogstar[j] - ok := ψDoublePrimeij.Verify(round.EC(), round.key.PaillierPKs[j], Kj, BigDeltaSharej, round.temp.Γ, round.key.NTildei, round.key.H1i, round.key.H2i) + ok := ψDoublePrimeij.Verify(round.EC(), round.key.PaillierPKs[j], Kj, Δj, round.temp.Γ, round.key.NTildei, round.key.H1i, round.key.H2i) if !ok { errChs <- round.WrapError(errors.New("proof verify failed"), Pj) return @@ -73,18 +74,21 @@ func (round *sign4) Start() *tss.Error { if j == i { continue } - 𝛿 = modN.Add(𝛿, round.temp.r3msgDeltaShare[j]) - BigDeltaShare := round.temp.r3msgBigDeltaShare[j] + 𝛿 = modN.Add(𝛿, round.temp.r3msg𝛿j[j]) + Δj := round.temp.r3msgΔj[j] var err error - Δ, err = Δ.Add(BigDeltaShare) + Δ, err = Δ.Add(Δj) if err != nil { return round.WrapError(errors.New("round4: failed to collect BigDelta")) } } - DeltaPoint := crypto.ScalarBaseMult(round.EC(), 𝛿) - if !DeltaPoint.Equals(Δ) { - return round.WrapError(errors.New("verify BigDelta failed")) + if !crypto.ScalarBaseMult(round.EC(), 𝛿).Equals(Δ) { + common.Logger.Errorf("part %v: verify BigDelta failed", round.PartyID()) + round.AbortingSigning = true + round.setOK() + round.out <- NewSignRound4AbortingMessage(round.PartyID()) + return nil } // compute the multiplicative inverse thelta mod q 𝛿Inverse := modN.ModInverse(𝛿) @@ -101,27 +105,39 @@ func (round *sign4) Start() *tss.Error { round.temp.Rx = r round.temp.SigmaShare = 𝜎i // retire unused variables + round.temp.𝜌i = nil + round.temp.K = nil round.temp.r1msgK = make([]*big.Int, round.PartyCount()) - round.temp.r3msgBigDeltaShare = make([]*crypto.ECPoint, round.PartyCount()) - round.temp.r3msgDeltaShare = make([]*big.Int, round.PartyCount()) + round.temp.r3msgΔj = make([]*crypto.ECPoint, round.PartyCount()) + round.temp.r3msg𝛿j = make([]*big.Int, round.PartyCount()) round.temp.r3msgProofLogstar = make([]*zkplogstar.ProofLogstar, round.PartyCount()) return nil } func (round *sign4) Update() (bool, *tss.Error) { - for j, msg := range round.temp.r4msgSigmaShare { + for j, msg := range round.temp.r4msg𝜎j { if round.ok[j] { continue } - if msg == nil { - return false, nil + if msg == nil && !round.temp.r4msgAborting[j] { + if round.temp.r4msgAborting[j] { + round.AbortingSigning = true + } else { + return false, nil + } } round.ok[j] = true } return true, nil } +func (round *sign4) resetAborting() { + for j := range round.temp.r4msgAborting { + round.temp.r4msgAborting[j] = false + } +} + func (round *sign4) CanAccept(msg tss.ParsedMessage) bool { if _, ok := msg.Content().(*SignRound4Message); ok { return msg.IsBroadcast() @@ -131,5 +147,15 @@ func (round *sign4) CanAccept(msg tss.ParsedMessage) bool { func (round *sign4) NextRound() tss.Round { round.started = false + otherPartyAborted := false + for _, abortingMsg := range round.temp.r4msgAborting { + if abortingMsg { + otherPartyAborted = true + break + } + } + if round.AbortingSigning || otherPartyAborted { + return &identificationPrep{round} + } return &signout{round} } diff --git a/ecdsa/signing/sign_out.go b/ecdsa/signing/sign_out.go index 29c7da2..4d5a6d7 100644 --- a/ecdsa/signing/sign_out.go +++ b/ecdsa/signing/sign_out.go @@ -32,7 +32,7 @@ func VerirySig(ec elliptic.Curve, R *crypto.ECPoint, S *big.Int, m *big.Int, PK func newRound5(params *tss.Parameters, key *keygen.LocalPartySaveData, data *common.SignatureData, temp *localTempData, out chan<- tss.Message, end chan<- common.SignatureData) tss.Round { return &signout{&sign4{&presign3{&presign2{&presign1{ - &base{params, key, data, temp, out, end, make([]bool, len(params.Parties().IDs())), false, 5}}}}}} + &base{params, key, data, temp, out, end, make([]bool, len(params.Parties().IDs())), false, 5}}}}, false}} } func (round *signout) Start() *tss.Error { @@ -51,7 +51,7 @@ func (round *signout) Start() *tss.Error { if j == round.PartyID().Index { continue } - Sigma = modN.Add(Sigma, round.temp.r4msgSigmaShare[j]) + Sigma = modN.Add(Sigma, round.temp.r4msg𝜎j[j]) } recid := 0 // byte v = if(R.X > curve.N) then 2 else 0) | (if R.Y.IsEven then 0 else 1); @@ -91,7 +91,8 @@ func (round *signout) Start() *tss.Error { } round.end <- *round.data - + round.temp.G = nil + round.temp.𝜈i = nil return nil } diff --git a/protob/ecdsa-signing.proto b/protob/ecdsa-signing.proto index c0f370e..ab4a312 100644 --- a/protob/ecdsa-signing.proto +++ b/protob/ecdsa-signing.proto @@ -46,6 +46,8 @@ message PreSignRound3Message { message SignRound4Message { bytes SigmaShare = 1; } +message SignRound4AbortingMessage { +} message IdentificationRound6Message { bytes H = 1; diff --git a/tss/message.pb.go b/tss/message.pb.go index ef0d3a1..2e515a8 100644 --- a/tss/message.pb.go +++ b/tss/message.pb.go @@ -7,7 +7,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.17.3 +// protoc v3.18.1 // source: protob/message.proto package tss diff --git a/tss/party.go b/tss/party.go index 7a08bdb..ce0d8a7 100644 --- a/tss/party.go +++ b/tss/party.go @@ -32,7 +32,7 @@ type Party interface { // Private lifecycle methods setRound(Round) *Error - round() Round + Round() Round advance() lock() unlock() @@ -79,7 +79,7 @@ func (p *BaseParty) ValidateMessage(msg ParsedMessage) (bool, *Error) { } func (p *BaseParty) String() string { - return fmt.Sprintf("round: %d", p.round().RoundNumber()) + return fmt.Sprintf("round: %d", p.Round().RoundNumber()) } // ----- @@ -93,7 +93,7 @@ func (p *BaseParty) setRound(round Round) *Error { return nil } -func (p *BaseParty) round() Round { +func (p *BaseParty) Round() Round { return p.rnd } @@ -117,7 +117,7 @@ func BaseStart(p Party, task string, prepare ...func(Round) *Error) *Error { if p.PartyID() == nil || !p.PartyID().ValidateBasic() { return p.WrapError(fmt.Errorf("could not start. this party has an invalid PartyID: %+v", p.PartyID())) } - if p.round() != nil { + if p.Round() != nil { return p.WrapError(errors.New("could not start. this party is in an unexpected state. use the constructor and Start()")) } round := p.FirstRound() @@ -132,11 +132,11 @@ func BaseStart(p Party, task string, prepare ...func(Round) *Error) *Error { return err } } - common.Logger.Infof("party %s: %s round %d starting", p.round().Params().PartyID(), task, 1) + common.Logger.Infof("party %s: %s round %d starting", p.Round().Params().PartyID(), task, 1) defer func() { - common.Logger.Debugf("party %s: %s round %d finished", p.round().Params().PartyID(), task, 1) + common.Logger.Debugf("party %s: %s round %d finished", p.Round().Params().PartyID(), task, 1) }() - return p.round().Start() + return p.Round().Start() } // an implementation of Update that is shared across the different types of parties (keygen, signing, dynamic groups) @@ -152,24 +152,24 @@ func BaseUpdate2(p Party, msg ParsedMessage, task string) (ok bool, err *Error) } p.lock() // data is written to P state below common.Logger.Debugf("party %s received message: %s", p.PartyID(), msg.String()) - if p.round() != nil { - common.Logger.Debugf("party %s round %d update: %s", p.PartyID(), p.round().RoundNumber(), msg.String()) + if p.Round() != nil { + common.Logger.Debugf("party %s round %d update: %s", p.PartyID(), p.Round().RoundNumber(), msg.String()) } if ok, err := p.StoreMessage(msg); err != nil || !ok { return r(false, err) } - if p.round() != nil { - common.Logger.Debugf("party %s: %s round %d update", p.round().Params().PartyID(), task, p.round().RoundNumber()) - if _, err := p.round().Update(); err != nil { + if p.Round() != nil { + common.Logger.Debugf("party %s: %s round %d update", p.Round().Params().PartyID(), task, p.Round().RoundNumber()) + if _, err := p.Round().Update(); err != nil { return r(false, err) } - if p.round().CanProceed() { - if p.advance(); p.round() != nil { - if err := p.round().Start(); err != nil { + if p.Round().CanProceed() { + if p.advance(); p.Round() != nil { + if err := p.Round().Start(); err != nil { return r(false, err) } - rndNum := p.round().RoundNumber() - common.Logger.Infof("party %s: %s round %d started", p.round().Params().PartyID(), task, rndNum) + rndNum := p.Round().RoundNumber() + common.Logger.Infof("party %s: %s round %d started", p.Round().Params().PartyID(), task, rndNum) } else { // finished! the round implementation will have sent the data through the `end` channel. common.Logger.Infof("party %s: %s finished!", p.PartyID(), task) @@ -195,24 +195,24 @@ func BaseUpdate(p Party, msg ParsedMessage, task string) (ok bool, err *Error) { } p.lock() // data is written to P state below common.Logger.Debugf("party %s received message: %s", p.PartyID(), msg.String()) - if p.round() != nil { - common.Logger.Debugf("party %s round %d update: %s", p.PartyID(), p.round().RoundNumber(), msg.String()) + if p.Round() != nil { + common.Logger.Debugf("party %s round %d update: %s", p.PartyID(), p.Round().RoundNumber(), msg.String()) } if ok, err := p.StoreMessage(msg); err != nil || !ok { return r(false, err) } - if p.round() != nil { - common.Logger.Debugf("party %s: %s round %d update", p.round().Params().PartyID(), task, p.round().RoundNumber()) - if _, err := p.round().Update(); err != nil { + if p.Round() != nil { + common.Logger.Debugf("party %s: %s round %d update", p.Round().Params().PartyID(), task, p.Round().RoundNumber()) + if _, err := p.Round().Update(); err != nil { return r(false, err) } - if p.round().CanProceed() { - if p.advance(); p.round() != nil { - if err := p.round().Start(); err != nil { + if p.Round().CanProceed() { + if p.advance(); p.Round() != nil { + if err := p.Round().Start(); err != nil { return r(false, err) } - rndNum := p.round().RoundNumber() - common.Logger.Infof("party %s: %s round %d started", p.round().Params().PartyID(), task, rndNum) + rndNum := p.Round().RoundNumber() + common.Logger.Infof("party %s: %s round %d started", p.Round().Params().PartyID(), task, rndNum) } else { // finished! the round implementation will have sent the data through the `end` channel. common.Logger.Infof("party %s: %s finished!", p.PartyID(), task)