// SignatureRequest treats external request to this service. func (cs *CoSi) SignatureRequest(req *SignatureRequest) (network.Body, onet.ClientError) { if req.Roster.ID == onet.RosterID(uuid.Nil) { req.Roster.ID = onet.RosterID(uuid.NewV4()) } tree := req.Roster.GenerateBinaryTree() tni := cs.NewTreeNodeInstance(tree, tree.Root, cosi.Name) pi, err := cosi.NewProtocol(tni) if err != nil { return nil, onet.NewClientErrorCode(4100, "Couldn't make new protocol: "+err.Error()) } cs.RegisterProtocolInstance(pi) pcosi := pi.(*cosi.CoSi) pcosi.SigningMessage(req.Message) h, err := crypto.HashBytes(network.Suite.Hash(), req.Message) if err != nil { return nil, onet.NewClientErrorCode(4101, "Couldn't hash message: "+err.Error()) } response := make(chan []byte) pcosi.RegisterSignatureHook(func(sig []byte) { response <- sig }) log.Lvl3("Cosi Service starting up root protocol") go pi.Dispatch() go pi.Start() sig := <-response if log.DebugVisible() > 1 { fmt.Printf("%s: Signed a message.\n", time.Now().Format("Mon Jan 2 15:04:05 -0700 MST 2006")) } return &SignatureResponse{ Hash: h, Signature: sig, }, nil }
// ProposeVote calls the 'accept'-vote on the current propose-configuration func (i *Identity) ProposeVote(accept bool) onet.ClientError { log.Lvl3("Voting proposal") if i.Proposed == nil { return onet.NewClientErrorCode(ErrorConfigMissing, "No proposed config") } log.Lvlf3("Voting %t on %s", accept, i.Proposed.Device) if !accept { return nil } hash, err := i.Proposed.Hash() if err != nil { return onet.NewClientErrorCode(ErrorOnet, err.Error()) } sig, err := crypto.SignSchnorr(network.Suite, i.Private, hash) if err != nil { return onet.NewClientErrorCode(ErrorOnet, err.Error()) } pvr := &ProposeVoteReply{} cerr := i.Client.SendProtobuf(i.Cothority.RandomServerIdentity(), &ProposeVote{ ID: i.ID, Signer: i.DeviceName, Signature: &sig, }, pvr) if cerr != nil { return cerr } if pvr.Data != nil { log.Lvl2("Threshold reached and signed") i.Config = i.Proposed i.Proposed = nil } else { log.Lvl2("Threshold not reached") } return nil }
// SetChildrenSkipBlock creates a new SkipChain if that 'service' doesn't exist // yet. func (s *Service) SetChildrenSkipBlock(scsb *SetChildrenSkipBlock) (network.Body, onet.ClientError) { parentID := scsb.ParentID childID := scsb.ChildID parent, ok := s.getSkipBlockByID(parentID) if !ok { return nil, onet.NewClientErrorCode(ErrorBlockNotFound, "couldn't find skipblock") } child, ok := s.getSkipBlockByID(childID) if !ok { return nil, onet.NewClientErrorCode(ErrorBlockNotFound, "couldn't find skipblock") } child.ParentBlockID = parentID parent.ChildSL = NewBlockLink() parent.ChildSL.Hash = childID cerr := s.startPropagation([]*SkipBlock{child, parent}) if cerr != nil { return nil, cerr } // Parent-block is always of type roster, but child-block can be // data or roster. reply := &SetChildrenSkipBlockReply{parent, child} s.save() return reply, nil }
// signNewSkipBlock should start a BFT-signature on the newest block // which will propagate and update all forward-links of all blocks. // As a simple solution it verifies the validity of the block, // simulates a signature and propagates the latest and newest block. func (s *Service) signNewSkipBlock(latest, newest *SkipBlock) (*SkipBlock, *SkipBlock, error) { log.Lvl4("Signing new block", newest, "on block", latest) if newest != nil && newest.Roster == nil { log.Lvl3("Got a data-block") if newest.ParentBlockID.IsNull() { return nil, nil, onet.NewClientErrorCode(ErrorBlockNoParent, "Data skipblock without parent") } parent, ok := s.getSkipBlockByID(newest.ParentBlockID) if !ok { return nil, nil, onet.NewClientErrorCode(ErrorBlockNoParent, "Didn't find parent block") } newest.Roster = parent.Roster } // Now verify if it's a valid block if err := s.verifyNewSkipBlock(latest, newest); err != nil { return nil, nil, onet.NewClientErrorCode(ErrorVerification, "Verification of newest SkipBlock failed: "+err.Error()) } // Sign it err := s.startBFTSignature(newest) if err != nil { return nil, nil, err } if err := newest.VerifySignatures(); err != nil { log.Error("Couldn't verify signature: " + err.Error()) return nil, nil, err } newblocks := make([]*SkipBlock, 1) if latest == nil { // Genesis-block only newblocks[0] = newest } else { // Adjust forward-links if it's an additional block var err error newblocks, err = s.addForwardLinks(newest) if err != nil { return nil, nil, err } latest = newblocks[1] } // Store and propagate the new SkipBlocks log.Lvl4("Finished signing new block", newest) if err = s.startPropagation(newblocks); err != nil { return nil, nil, err } return latest, newblocks[0], nil }
// CreateIdentity will register a new SkipChain and add it to our list of // managed identities. func (s *Service) CreateIdentity(ai *CreateIdentity) (network.Body, onet.ClientError) { log.Lvlf3("%s Creating new identity with config %+v", s, ai.Config) ids := &Storage{ Latest: ai.Config, } log.Lvl3("Creating Root-skipchain") var cerr onet.ClientError ids.Root, cerr = s.skipchain.CreateRoster(ai.Roster, 2, 10, skipchain.VerifyNone, nil) if cerr != nil { return nil, cerr } log.Lvl3("Creating Data-skipchain") ids.Root, ids.Data, cerr = s.skipchain.CreateData(ids.Root, 2, 10, skipchain.VerifyNone, ai.Config) if cerr != nil { return nil, cerr } roster := ids.Root.Roster replies, err := s.propagateIdentity(roster, &PropagateIdentity{ids}, propagateTimeout) if err != nil { return nil, onet.NewClientErrorCode(ErrorOnet, err.Error()) } if replies != len(roster.List) { log.Warn("Did only get", replies, "out of", len(roster.List)) } log.Lvlf2("New chain is\n%x", []byte(ids.Data.Hash)) s.save() return &CreateIdentityReply{ Root: ids.Root, Data: ids.Data, }, nil }
// ProposeSend only stores the proposed configuration internally. Signatures // come later. func (s *Service) ProposeSend(p *ProposeSend) (network.Body, onet.ClientError) { log.Lvl2(s, "Storing new proposal") sid := s.getIdentityStorage(p.ID) if sid == nil { return nil, onet.NewClientErrorCode(ErrorBlockMissing, "Didn't find Identity") } roster := sid.Root.Roster replies, err := s.propagateConfig(roster, p, propagateTimeout) if err != nil { return nil, onet.NewClientErrorCode(ErrorOnet, err.Error()) } if replies != len(roster.List) { log.Warn("Did only get", replies, "out of", len(roster.List)) } return nil, nil }
// GetResponsible searches for the block that is responsible for us - for // - Data - it's his parent // - else - it's himself func (sb *SkipBlock) GetResponsible(s *Service) (*onet.Roster, onet.ClientError) { el := sb.Roster if el == nil { // We're a data-block, so use the parent's Roster if sb.ParentBlockID.IsNull() { return nil, onet.NewClientErrorCode(ErrorBlockContent, "Didn't find a Roster") } parent, ok := s.getSkipBlockByID(sb.ParentBlockID) if !ok { return nil, onet.NewClientErrorCode(ErrorBlockNoParent, "No Roster and no parent") } if parent.Roster == nil { return nil, onet.NewClientErrorCode(ErrorBlockContent, "Parent doesn't have Roster") } el = parent.Roster } return el, nil }
func (s *Service) startBFTSignature(block *SkipBlock) error { done := make(chan bool) // create the message we want to sign for this round msg := []byte(block.Hash) el, cerr := block.GetResponsible(s) if cerr != nil { return cerr } switch len(el.List) { case 0: return errors.New("Found empty Roster") case 1: return errors.New("Need more than 1 entry for Roster") } // Start the protocol tree := el.GenerateNaryTreeWithRoot(2, s.ServerIdentity()) node, e := s.CreateProtocolOnet(skipchainBFT, tree) if e != nil { return onet.NewClientErrorCode(ErrorOnet, "Couldn't create new node: "+e.Error()) } // Register the function generating the protocol instance root := node.(*bftcosi.ProtocolBFTCoSi) root.Msg = msg data, e := network.MarshalRegisteredType(block) if e != nil { return errors.New("Couldn't marshal block: " + cerr.Error()) } root.Data = data // in testing-mode with more than one host and service per cothority-instance // we might have the wrong verification-function, so set it again here. root.VerificationFunction = s.bftVerify // function that will be called when protocol is finished by the root root.RegisterOnDone(func() { done <- true }) go node.Start() select { case <-done: block.BlockSig = root.Signature() if len(block.BlockSig.Exceptions) != 0 { return errors.New("Not everybody signed off the new block") } if e := block.BlockSig.Verify(network.Suite, el.Publics()); e != nil { return errors.New("Couldn't verify signature") } case <-time.After(time.Second * 60): return errors.New("Timed out while waiting for signature") } return nil }
// ProposeUpdate returns an eventual config-proposition func (s *Service) ProposeUpdate(cnc *ProposeUpdate) (network.Body, onet.ClientError) { log.Lvl3(s, "Sending proposal-update to client") sid := s.getIdentityStorage(cnc.ID) if sid == nil { return nil, onet.NewClientErrorCode(ErrorBlockMissing, "Didn't find Identity") } sid.Lock() defer sid.Unlock() return &ProposeUpdateReply{ Propose: sid.Proposed, }, nil }
// GetUpdateChain returns a slice of SkipBlocks which describe the part of the // skipchain from the latest block the caller knows of to the actual latest // SkipBlock. // Somehow comparable to search in SkipLists. func (s *Service) GetUpdateChain(latestKnown *GetUpdateChain) (network.Body, onet.ClientError) { block, ok := s.getSkipBlockByID(latestKnown.LatestID) if !ok { return nil, onet.NewClientErrorCode(ErrorBlockNotFound, "Couldn't find latest skipblock") } // at least the latest know and the next block: blocks := []*SkipBlock{block} log.Lvl3("Starting to search chain") for len(block.ForwardLink) > 0 { link := block.ForwardLink[len(block.ForwardLink)-1] block, ok = s.getSkipBlockByID(link.Hash) if !ok { return nil, onet.NewClientErrorCode(ErrorBlockNotFound, "Missing block in forward-chain") } blocks = append(blocks, block) } log.Lvl3("Found", len(blocks), "blocks") reply := &GetUpdateChainReply{blocks} return reply, nil }
// ConfigUpdate returns a new configuration update func (s *Service) ConfigUpdate(cu *ConfigUpdate) (network.Body, onet.ClientError) { sid := s.getIdentityStorage(cu.ID) if sid == nil { return nil, onet.NewClientErrorCode(ErrorBlockMissing, "Didn't find Identity") } sid.Lock() defer sid.Unlock() log.Lvl3(s, "Sending config-update") return &ConfigUpdateReply{ Config: sid.Latest, }, nil }
// notify other services about new/updated skipblock func (s *Service) startPropagation(blocks []*SkipBlock) onet.ClientError { log.Lvlf3("Starting to propagate for service %x", s.Context.ServerIdentity().ID[0:8]) for _, block := range blocks { roster := block.Roster if roster == nil { // Suppose it's a dataSkipBlock sb, ok := s.getSkipBlockByID(block.ParentBlockID) if !ok { return onet.NewClientErrorCode(ErrorBlockNoParent, "Didn't find Roster nor parent") } roster = sb.Roster } replies, e := s.Propagate(roster, block, propagateTimeout) if e != nil { return onet.NewClientErrorCode(ErrorOnet, e.Error()) } if replies != len(roster.List) { log.Warn("Did only get", replies, "out of", len(roster.List)) } } return nil }
// ConfigUpdate asks if there is any new config available that has already // been approved by others and updates the local configuration func (i *Identity) ConfigUpdate() onet.ClientError { log.Lvl3("ConfigUpdate", i) if i.Cothority == nil || len(i.Cothority.List) == 0 { return onet.NewClientErrorCode(ErrorListMissing, "Didn't find any list in the cothority") } cur := &ConfigUpdateReply{} err := i.Client.SendProtobuf(i.Cothority.RandomServerIdentity(), &ConfigUpdate{ID: i.ID}, cur) if err != nil { return err } // TODO - verify new config i.Config = cur.Config return nil }
// LinkParentChildBlock sends a request to create a link from the parent to the // child block and inversely. The child-block is supposed to already have // the parentBlockID set and be accepted. func (c *Client) LinkParentChildBlock(parent, child *SkipBlock) (*SkipBlock, *SkipBlock, onet.ClientError) { log.Lvl3(parent, child) if err := child.VerifySignatures(); err != nil { return nil, nil, onet.NewClientError(err) } if !bytes.Equal(parent.Hash, child.ParentBlockID) { return nil, nil, onet.NewClientErrorCode(ErrorBlockNoParent, "Child doesn't point to that parent") } host := parent.Roster.RandomServerIdentity() reply := &SetChildrenSkipBlockReply{} cerr := c.SendProtobuf(host, &SetChildrenSkipBlock{parent.Hash, child.Hash}, reply) if cerr != nil { return nil, nil, cerr } return reply.Parent, reply.Child, nil }
// AttachToIdentity proposes to attach it to an existing Identity func (i *Identity) AttachToIdentity(ID ID) onet.ClientError { i.ID = ID cerr := i.ConfigUpdate() if cerr != nil { return cerr } if _, exists := i.Config.Device[i.DeviceName]; exists { return onet.NewClientErrorCode(ErrorAccountDouble, "Adding with an existing account-name") } confPropose := i.Config.Copy() confPropose.Device[i.DeviceName] = &Device{i.Public} cerr = i.ProposeSend(confPropose) if cerr != nil { return cerr } return nil }
// ProposeSkipBlock takes a hash for the latest valid SkipBlock and a SkipBlock // that will be verified. If the verification returns true, the new SkipBlock // will be signed and added to the chain and returned. // If the the latest block given is nil it verify if we are actually creating // the first (genesis) block and creates it. If it is called with nil although // there already exist previous blocks, it will return an error. func (s *Service) ProposeSkipBlock(psbd *ProposeSkipBlock) (network.Body, onet.ClientError) { prop := psbd.Proposed var prev *SkipBlock if !psbd.LatestID.IsNull() { // We're appending a block to an existing chain var ok bool prev, ok = s.getSkipBlockByID(psbd.LatestID) if !ok { return nil, onet.NewClientErrorCode(ErrorBlockNotFound, "Didn't find latest block") } prop.MaximumHeight = prev.MaximumHeight prop.BaseHeight = prev.BaseHeight prop.ParentBlockID = prev.ParentBlockID prop.VerifierID = prev.VerifierID prop.Index = prev.Index + 1 index := prop.Index for prop.Height = 1; index%prop.BaseHeight == 0; prop.Height++ { index /= prop.BaseHeight if prop.Height >= prop.MaximumHeight { break } } log.Lvl4("Found height", prop.Height, "for index", prop.Index, "and maxHeight", prop.MaximumHeight, "and base", prop.BaseHeight) prop.BackLinkIds = make([]SkipBlockID, prop.Height) pointer := prev for h := range prop.BackLinkIds { for pointer.Height < h+1 { var ok bool pointer, ok = s.getSkipBlockByID(pointer.BackLinkIds[0]) if !ok { return nil, onet.NewClientErrorCode(ErrorBlockNotFound, "Didn't find convenient SkipBlock for height "+ strconv.Itoa(h)) } } prop.BackLinkIds[h] = pointer.Hash } } else { // A new chain is created, suppose all arguments in SkipBlock // are correctly set up prop.Index = 0 if prop.MaximumHeight == 0 { return nil, onet.NewClientErrorCode(ErrorParameterWrong, "Set a maximumHeight > 0") } if prop.BaseHeight == 0 { return nil, onet.NewClientErrorCode(ErrorParameterWrong, "Set a baseHeight > 0") } prop.Height = prop.MaximumHeight prop.ForwardLink = make([]*BlockLink, 0) // genesis block has a random back-link: bl := make([]byte, 32) rand.Read(bl) prop.BackLinkIds = []SkipBlockID{SkipBlockID(bl)} } if prop.Roster != nil { prop.Aggregate = prop.Roster.Aggregate } el, cerr := prop.GetResponsible(s) if cerr != nil { return nil, cerr } prop.AggregateResp = el.Aggregate prop.updateHash() var err error prev, prop, err = s.signNewSkipBlock(prev, prop) if err != nil { return nil, onet.NewClientErrorCode(ErrorVerification, "Verification error: "+err.Error()) } s.save() reply := &ProposedSkipBlockReply{ Previous: prev, Latest: prop, } return reply, nil }
// ProposeVote takes int account a vote for the proposed config. It also verifies // that the voter is in the latest config. // An empty signature signifies that the vote has been rejected. func (s *Service) ProposeVote(v *ProposeVote) (network.Body, onet.ClientError) { log.Lvl2(s, "Voting on proposal") // First verify if the signature is legitimate sid := s.getIdentityStorage(v.ID) if sid == nil { return nil, onet.NewClientErrorCode(ErrorBlockMissing, "Didn't find identity") } // Putting this in a function because of the lock which needs to be held // over all calls that might return an error. cerr := func() onet.ClientError { sid.Lock() defer sid.Unlock() log.Lvl3("Voting on", sid.Proposed.Device) owner, ok := sid.Latest.Device[v.Signer] if !ok { return onet.NewClientErrorCode(ErrorAccountMissing, "Didn't find signer") } if sid.Proposed == nil { return onet.NewClientErrorCode(ErrorConfigMissing, "No proposed block") } hash, err := sid.Proposed.Hash() if err != nil { return onet.NewClientErrorCode(ErrorOnet, "Couldn't get hash") } if _, exists := sid.Votes[v.Signer]; exists { return onet.NewClientErrorCode(ErrorVoteDouble, "Already voted for that block") } log.Lvl3(v.Signer, "voted", v.Signature) if v.Signature != nil { err = crypto.VerifySchnorr(network.Suite, owner.Point, hash, *v.Signature) if err != nil { return onet.NewClientErrorCode(ErrorVoteSignature, "Wrong signature: "+err.Error()) } } return nil }() if cerr != nil { return nil, cerr } // Propagate the vote _, err := s.propagateConfig(sid.Root.Roster, v, propagateTimeout) if err != nil { return nil, onet.NewClientErrorCode(ErrorOnet, cerr.Error()) } if len(sid.Votes) >= sid.Latest.Threshold || len(sid.Votes) == len(sid.Latest.Device) { // If we have enough signatures, make a new data-skipblock and // propagate it log.Lvl3("Having majority or all votes") // Making a new data-skipblock log.Lvl3("Sending data-block with", sid.Proposed.Device) reply, cerr := s.skipchain.ProposeData(sid.Root, sid.Data, sid.Proposed) if cerr != nil { return nil, cerr } _, msg, _ := network.UnmarshalRegistered(reply.Latest.Data) log.Lvl3("SB signed is", msg.(*Config).Device) usb := &UpdateSkipBlock{ ID: v.ID, Latest: reply.Latest, } _, err = s.propagateSkipBlock(sid.Root.Roster, usb, propagateTimeout) if err != nil { return nil, onet.NewClientErrorCode(ErrorOnet, cerr.Error()) } s.save() return &ProposeVoteReply{sid.Data}, nil } return nil, nil }