func (s *Server) ShareMask(clientDH *ClientDH, serverPub *[]byte) error { pub, shared := s.shareSecret(UnmarshalPoint(s.suite, clientDH.Public)) mask := MarshalPoint(shared) for r := 0; r < MaxRounds; r++ { if r == 0 { sha3.ShakeSum256(s.maskss[r][clientDH.Id], mask) } else { sha3.ShakeSum256(s.maskss[r][clientDH.Id], s.maskss[r-1][clientDH.Id]) } } *serverPub = MarshalPoint(pub) return nil }
func (s *Server) ShareSecret(clientDH *ClientDH, serverPub *[]byte) error { pub, shared := s.shareSecret(UnmarshalPoint(s.suite, clientDH.Public)) secret := MarshalPoint(shared) for r := 0; r < MaxRounds; r++ { if r == 0 { sha3.ShakeSum256(s.secretss[r][clientDH.Id], secret) } else { sha3.ShakeSum256(s.secretss[r][clientDH.Id], s.secretss[r-1][clientDH.Id]) } } //s.secretss[clientDH.Id] = make([]byte, len(MarshalPoint(shared))) *serverPub = MarshalPoint(pub) return nil }
func CheckCommitment(commitment []byte, profile *proto.EncodedProfile) bool { // The hash used here is modeled as a random oracle. This means that SHA3 // is fine but SHA2 is not (consider HMAC-SHA2 instead). var commitmentCheck [64]byte sha3.ShakeSum256(commitmentCheck[:], profile.Encoding) // the profile includes a nonce return bytes.Equal(commitment[:], commitmentCheck[:]) }
// ServeIzkp returns an http.Handler that reads an input file and // computes an interactive zero-knowledge proof-of-posession protocol. // (This is completely unused, but isn't it cool?) func ServeIzkp(fn string) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { b, err := ioutil.ReadFile(fn) if err != nil { glog.Errorf("error reading file %s: %s", fn, err) w.WriteHeader(http.StatusInternalServerError) return } chalString := r.Header.Get("x-izkp-challenge") if chalString == "" { glog.Infof("didn't receive a challenge, so using a raw hash") d := make([]byte, 64) sha3.ShakeSum256(d, b) w.Write(d) return } challenge := []byte(chalString) glog.Infof("received a challenge of length %d", len(challenge)) h := sha3.New512() h.Write(challenge) h.Write(b) d := make([]byte, 64) h.Sum(d) w.Write(d) return } }
///////////////////////////////// //Download //////////////////////////////// func (s *Server) GetResponse(cmask ClientMask, response *[]byte) error { t := time.Now() round := cmask.Round % MaxRounds otherBlocks := make([][]byte, len(s.servers)) var wg sync.WaitGroup for i := range otherBlocks { if i == s.id { otherBlocks[i] = make([]byte, BlockSize) } else { wg.Add(1) go func(i int, cmask ClientMask) { defer wg.Done() curBlock := <-s.rounds[round].xorsChan[i][cmask.Id] otherBlocks[i] = curBlock.Block }(i, cmask) } } wg.Wait() <-s.rounds[round].blocksRdy[cmask.Id] if cmask.Id == 0 && profile { fmt.Println(cmask.Id, "down_network:", time.Since(t)) } r := ComputeResponse(s.rounds[round].allBlocks, cmask.Mask, s.secretss[round][cmask.Id]) sha3.ShakeSum256(s.secretss[round][cmask.Id], s.secretss[round][cmask.Id]) Xor(Xors(otherBlocks), r) *response = r return nil }
func ShakeSum256(password string) []byte { buf := []byte(password) // A hash needs to be 64 bytes long to have 256-bit collision resistance. h := make([]byte, 64) // Compute a 64-byte hash of buf and put it in h. sha3.ShakeSum256(h, buf) return h }
func NewSHA3Shake256(payloadLen int) func() { input := NewRand(payloadLen) var hash = make([]byte, 64) return func() { sha3.ShakeSum256(hash, input) } }
func hashToCurve(m []byte) *edwards25519.ExtendedGroupElement { // H(n) = (f(h(n))^8) var hmb [32]byte sha3.ShakeSum256(hmb[:], m) var hm edwards25519.ExtendedGroupElement extra25519.HashToEdwards(&hm, &hmb) edwards25519.GeDouble(&hm, &hm) edwards25519.GeDouble(&hm, &hm) edwards25519.GeDouble(&hm, &hm) return &hm }
//TODO: need to select based on some pseudorandomness/gamma function? // Note that these challenges are different from those of cryptocurrency func (v *Verifier) SelectChallenges(seed []byte) []int64 { challenges := make([]int64, v.beta*int(v.log2)) rands := make([]byte, v.beta*int(v.log2)*8) sha3.ShakeSum256(rands, seed) //PRNG for i := range challenges { val, num := binary.Uvarint(rands[i*8 : (i+1)*8]) if num < 0 { panic("Couldn't read PRNG") } challenges[i] = int64(val % uint64(v.size)) } return challenges }
func (c *Client) DownloadSlot(slot int, rnd uint64) []byte { //all but one server uses the prng technique round := rnd % MaxRounds maskSize := len(c.maskss[round][0]) finalMask := make([]byte, maskSize) SetBit(slot, true, finalMask) mask := Xors(c.maskss[round]) Xor(c.maskss[round][c.myServer], mask) Xor(finalMask, mask) //one response includes all the secrets response := make([]byte, BlockSize) secretsXor := Xors(c.secretss[round]) cMask := ClientMask{Mask: mask, Id: c.id, Round: rnd} t := time.Now() err := c.rpcServers[c.myServer].Call("Server.GetResponse", cMask, &response) if err != nil { log.Fatal("Could not get response: ", err) } if c.id == 0 && profile { fmt.Println(c.id, "down_network_total:", time.Since(t)) } Xor(secretsXor, response) for i := range c.secretss[round] { sha3.ShakeSum256(c.secretss[round][i], c.secretss[round][i]) } for i := range c.maskss[round] { sha3.ShakeSum256(c.maskss[round][i], c.maskss[round][i]) } return response }
func TestKeyserverRejectsMissignedUpdate(t *testing.T) { dieOnCtrlC() kss, caPool, clks, _, ck, clientConfig, teardown := setupRealm(t, 3, 3) defer teardown() stop := stoppableSyncedClocks(clks) defer close(stop) waitForFirstEpoch(kss[0], clientConfig.Realms[0].VerificationPolicy.GetQuorum()) clientTLS, err := clientConfig.Realms[0].ClientTLS.Config(ck) if err != nil { t.Fatal(err) } _, alicePk, aliceEntry, aliceProfile := doRegister(t, kss[0], clientConfig, clientTLS, caPool, clks[0].Now(), alice, 0, proto.Profile{ Nonce: []byte("noncenoncenonceNONCE"), Keys: map[string][]byte{"abc": []byte{1, 2, 3}, "xyz": []byte("TEST 456")}, }) var aliceKeyIdBytes [8]byte sha3.ShakeSum256(aliceKeyIdBytes[:], proto.MustMarshal(alicePk)) aliceKeyid := binary.BigEndian.Uint64(aliceKeyIdBytes[:8]) _, badSk, _ := ed25519.GenerateKey(rand.Reader) conn, err := grpc.Dial(kss[1].publicListen.Addr().String(), grpc.WithTransportCredentials(credentials.NewTLS(clientTLS))) if err != nil { t.Fatal(err) } updateC := proto.NewE2EKSPublicClient(conn) _, err = updateC.Update(context.Background(), &proto.UpdateRequest{ Update: &proto.SignedEntryUpdate{ NewEntry: *aliceEntry, Signatures: map[uint64][]byte{aliceKeyid: ed25519.Sign(badSk, aliceEntry.Encoding)[:]}, }, Profile: *aliceProfile, LookupParameters: &proto.LookupRequest{ UserId: alice, QuorumRequirement: clientConfig.Realms[0].VerificationPolicy.GetQuorum(), }, }) if err == nil { t.Fatalf("update went through even though it was signed with the wrong key") } }
func VerifyLookup(cfg *proto.Config, user string, pf *proto.LookupProof, now time.Time) (keys map[string][]byte, err error) { if pf.UserId != "" && pf.UserId != user { return nil, fmt.Errorf("VerifyLookup: proof specifies different user ID: %q != %q", pf.UserId, user) } realm, err := GetRealmByUser(cfg, user) if err != nil { return nil, err } if !vrf.Verify(realm.VRFPublic, []byte(user), pf.Index, pf.IndexProof) { return nil, fmt.Errorf("VerifyLookup: VRF verification failed") } root, err := VerifyConsensus(realm, pf.Ratifications, now) if err != nil { return } verifiedEntryHash, err := reconstructTreeAndLookup(realm.TreeNonce, root, pf.Index, pf.TreeProof) if err != nil { return nil, fmt.Errorf("VerifyLookup: failed to verify the lookup: %v", err) } if verifiedEntryHash == nil { if pf.Entry != nil { return nil, fmt.Errorf("VerifyLookup: non-empty entry %x did not match verified lookup result <nil>", pf.Entry) } if pf.Profile != nil { return nil, fmt.Errorf("VerifyLookup: non-empty profile %x did not match verified lookup result <nil>", pf.Profile) } return nil, nil } else { var entryHash [32]byte sha3.ShakeSum256(entryHash[:], pf.Entry.Encoding) if !bytes.Equal(entryHash[:], verifiedEntryHash) { return nil, fmt.Errorf("VerifyLookup: entry hash %x did not match verified lookup result %x", entryHash, verifiedEntryHash) } if !CheckCommitment(pf.Entry.ProfileCommitment, pf.Profile) { return nil, fmt.Errorf("VerifyLookup: profile does not match the hash in the entry") } return pf.Profile.Keys, nil } }
func (ks *Keyserver) verifyUpdateEdge(req *proto.UpdateRequest) error { if len(req.Update.NewEntry.Index) != vrf.Size { return fmt.Errorf("index '%x' has wrong length (expected %d)", req.Update.NewEntry.Index, vrf.Size) } prevUpdate, err := ks.getUpdate(req.Update.NewEntry.Index, math.MaxUint64) if err != nil { log.Print(err) return fmt.Errorf("internal error") } if prevUpdate == nil { // registration: check email proof if !ks.insecureSkipEmailProof { email, payload, err := dkim.CheckEmailProof(req.DKIMProof, ks.emailProofToAddr, ks.emailProofSubjectPrefix, ks.lookupTXT, ks.clk.Now) if err != nil { return fmt.Errorf("failed to verify DKIM proof: %s", err) } if got, want := email, req.LookupParameters.UserId; got != want { return fmt.Errorf("requested user ID does not match the email proof: %q != %q", got, want) } lastAtIndex := strings.LastIndex(req.LookupParameters.UserId, "@") if lastAtIndex == -1 { return fmt.Errorf("requested user id is not a valid email address: %q", req.LookupParameters.UserId) } if _, ok := ks.emailProofAllowedDomains[req.LookupParameters.UserId[lastAtIndex+1:]]; !ok { return fmt.Errorf("domain not in registration whitelist: %q", req.LookupParameters.UserId[lastAtIndex+1:]) } entryHash, err := base64.StdEncoding.DecodeString(payload) if err != nil { return fmt.Errorf("bad base64 in email proof: %q", payload) } var entryHashProposed [32]byte sha3.ShakeSum256(entryHashProposed[:], req.Update.NewEntry.Encoding) if !bytes.Equal(entryHashProposed[:], entryHash[:]) { return fmt.Errorf("email proof does not match requested entry: %s vs %s (%x)", base64.StdEncoding.EncodeToString(entryHashProposed[:]), payload, req.Update.NewEntry.Encoding) } } } return ks.verifyUpdateDeterministic(prevUpdate, req) }
// KeyID computes the ID of public key. func KeyID(sv *PublicKey) uint64 { var h [8]byte sha3.ShakeSum256(h[:], MustMarshal(sv)) return binary.LittleEndian.Uint64(h[:8]) }
func doUpdate( t *testing.T, ks *Keyserver, clientConfig *proto.Config, clientTLS *tls.Config, caPool *x509.CertPool, now time.Time, name string, sk *[ed25519.PrivateKeySize]byte, pk *proto.PublicKey, version uint64, profileContents proto.Profile, ) (*proto.EncodedEntry, *proto.EncodedProfile) { conn, err := grpc.Dial(ks.publicListen.Addr().String(), grpc.WithTransportCredentials(credentials.NewTLS(clientTLS))) if err != nil { t.Fatal(err) } publicC := proto.NewE2EKSPublicClient(conn) // First, do a lookup to retrieve the index lookup, err := publicC.Lookup(context.Background(), &proto.LookupRequest{ UserId: name, // We don't care about any signatures here; the server just needs to tell us the index. QuorumRequirement: &proto.QuorumExpr{ Threshold: 0, Candidates: []uint64{}, Subexpressions: []*proto.QuorumExpr{}, }, }) if err != nil { t.Fatal(err) } index := lookup.Index // Do the update var keyidBytes [8]byte sha3.ShakeSum256(keyidBytes[:], proto.MustMarshal(pk)) keyid := binary.BigEndian.Uint64(keyidBytes[:8]) profile := proto.EncodedProfile{ Profile: profileContents, } profile.UpdateEncoding() var commitment [64]byte sha3.ShakeSum256(commitment[:], profile.Encoding) entry := proto.EncodedEntry{ Entry: proto.Entry{ Index: index, Version: version, UpdatePolicy: &proto.AuthorizationPolicy{ PublicKeys: map[uint64]*proto.PublicKey{keyid: pk}, PolicyType: &proto.AuthorizationPolicy_Quorum{Quorum: &proto.QuorumExpr{ Threshold: 1, Candidates: []uint64{keyid}, Subexpressions: []*proto.QuorumExpr{}, }, }}, ProfileCommitment: commitment[:], }, } entry.UpdateEncoding() proof, err := publicC.Update(context.Background(), &proto.UpdateRequest{ Update: &proto.SignedEntryUpdate{ NewEntry: entry, Signatures: map[uint64][]byte{keyid: ed25519.Sign(sk, entry.Encoding)[:]}, }, Profile: profile, LookupParameters: &proto.LookupRequest{ UserId: name, QuorumRequirement: clientConfig.Realms[0].VerificationPolicy.GetQuorum(), }, }) if err != nil { t.Fatal(err) } if got, want := proof.Profile.Encoding, profile.Encoding; !bytes.Equal(got, want) { t.Errorf("updated profile didn't roundtrip: %x != %x", got, want) } _, err = coname.VerifyLookup(clientConfig, name, proof, now) if err != nil { t.Fatal(err) } return &entry, &profile }
func hash(clear string) string { h := make([]byte, 64) sha3.ShakeSum256(h, []byte(clear)) return fmt.Sprintf("%x", h) }
// step is called by run and changes the in-memory state. No i/o allowed. func (ks *Keyserver) step(step *proto.KeyserverStep, rs *proto.ReplicaState, wb kv.Batch) (deferredIO func()) { // ks: &const // step, rs, wb: &mut switch step.Type.(type) { case *proto.KeyserverStep_Update: index := step.GetUpdate().Update.NewEntry.Index prevUpdate, err := ks.getUpdate(index, math.MaxUint64) if err != nil { log.Printf("getUpdate: %s", err) ks.wr.Notify(step.UID, updateOutput{Error: fmt.Errorf("internal error")}) return } if err := ks.verifyUpdateDeterministic(prevUpdate, step.GetUpdate()); err != nil { ks.wr.Notify(step.UID, updateOutput{Error: err}) return } latestTree := ks.merkletree.GetSnapshot(rs.LatestTreeSnapshot) // sanity check: compare previous version in Merkle tree vs in updates table prevEntryHashTree, _, err := latestTree.Lookup(index) if err != nil { ks.wr.Notify(step.UID, updateOutput{Error: fmt.Errorf("internal error")}) return } var prevEntryHash []byte if prevUpdate != nil { prevEntryHash = make([]byte, 32) sha3.ShakeSum256(prevEntryHash, prevUpdate.Update.NewEntry.Encoding) } if !bytes.Equal(prevEntryHashTree, prevEntryHash) { log.Fatalf("ERROR: merkle tree and DB inconsistent for index %x: %x vs %x", index, prevEntryHashTree, prevEntryHash) } var entryHash [32]byte sha3.ShakeSum256(entryHash[:], step.GetUpdate().Update.NewEntry.Encoding) newTree, err := latestTree.BeginModification() if err != nil { ks.wr.Notify(step.UID, updateOutput{Error: fmt.Errorf("internal error")}) return } if err := newTree.Set(index, entryHash[:]); err != nil { log.Printf("setting index '%x' gave error: %s", index, err) ks.wr.Notify(step.UID, updateOutput{Error: fmt.Errorf("internal error")}) return } rs.LatestTreeSnapshot = newTree.Flush(wb).Nr epochNr := rs.LastEpochDelimiter.EpochNumber + 1 wb.Put(tableUpdateRequests(index, epochNr), proto.MustMarshal(step.GetUpdate())) ks.wr.Notify(step.UID, updateOutput{Epoch: epochNr}) rs.PendingUpdates = true ks.updateEpochProposer() if rs.LastEpochNeedsRatification { // We need to wait for the last epoch to appear in the verifier log before // inserting this update. wb.Put(tableUpdatesPendingRatification(rs.NextIndexLog), proto.MustMarshal(step.GetUpdate().Update)) } else { // We can deliver the update to verifiers right away. return ks.verifierLogAppend(&proto.VerifierStep{Type: &proto.VerifierStep_Update{Update: step.GetUpdate().Update}}, rs, wb) } case *proto.KeyserverStep_EpochDelimiter: if step.GetEpochDelimiter().EpochNumber <= rs.LastEpochDelimiter.EpochNumber { return // a duplicate of this step has already been handled } rs.LastEpochDelimiter = *step.GetEpochDelimiter() log.Printf("epoch %d", step.GetEpochDelimiter().EpochNumber) rs.PendingUpdates = false ks.resetEpochTimers(rs.LastEpochDelimiter.Timestamp.Time()) // rs.ThisReplicaNeedsToSignLastEpoch might already be true, if a majority // signed that did not include us. This will make us skip signing the last // epoch, but that's fine. rs.ThisReplicaNeedsToSignLastEpoch = true // However, it's not okay to see a new epoch delimiter before the previous // epoch has been ratified. if rs.LastEpochNeedsRatification { log.Panicf("new epoch delimiter but last epoch not ratified") } rs.LastEpochNeedsRatification = true ks.updateEpochProposer() deferredIO = ks.updateSignatureProposer snapshotNumberBytes := make([]byte, 8) binary.BigEndian.PutUint64(snapshotNumberBytes, rs.LatestTreeSnapshot) wb.Put(tableMerkleTreeSnapshot(step.GetEpochDelimiter().EpochNumber), snapshotNumberBytes) latestTree := ks.merkletree.GetSnapshot(rs.LatestTreeSnapshot) rootHash, err := latestTree.GetRootHash() if err != nil { log.Panicf("ks.latestTree.GetRootHash() failed: %s", err) } teh := &proto.EncodedTimestampedEpochHead{TimestampedEpochHead: proto.TimestampedEpochHead{ Head: proto.EncodedEpochHead{EpochHead: proto.EpochHead{ RootHash: rootHash, PreviousSummaryHash: rs.PreviousSummaryHash, Realm: ks.realm, Epoch: step.GetEpochDelimiter().EpochNumber, IssueTime: step.GetEpochDelimiter().Timestamp, }, Encoding: nil}, Timestamp: step.GetEpochDelimiter().Timestamp, }, Encoding: nil} teh.Head.UpdateEncoding() teh.UpdateEncoding() if rs.PreviousSummaryHash == nil { rs.PreviousSummaryHash = make([]byte, 64) } sha3.ShakeSum256(rs.PreviousSummaryHash[:], teh.Head.Encoding) wb.Put(tableEpochHeads(step.GetEpochDelimiter().EpochNumber), proto.MustMarshal(teh)) case *proto.KeyserverStep_ReplicaSigned: newSEH := step.GetReplicaSigned() epochNr := newSEH.Head.Head.Epoch // get epoch head tehBytes, err := ks.db.Get(tableEpochHeads(epochNr)) if err != nil { log.Panicf("get tableEpochHeads(%d): %s", epochNr, err) } // compare epoch head to signed epoch head if got, want := tehBytes, newSEH.Head.Encoding; !bytes.Equal(got, want) { log.Panicf("replica signed different head: wanted %x, got %x", want, got) } // insert all the new signatures into the ratifications table (there should // actually only be one) newSehBytes := proto.MustMarshal(newSEH) for id := range newSEH.Signatures { // the entry might already exist in the DB (if the proposals got // duplicated), but it doesn't matter wb.Put(tableRatifications(epochNr, id), newSehBytes) } deferredIO = func() { // First write to DB, *then* notify subscribers. That way, if subscribers // start listening before searching the DB, they're guaranteed to see the // signature: either it's already in the DB, or they'll get notified. If // the order was reversed, they could miss the notification but still not // see anything in the DB. ks.signatureBroadcast.Publish(epochNr, newSEH) } if epochNr != rs.LastEpochDelimiter.EpochNumber { break } if rs.ThisReplicaNeedsToSignLastEpoch && newSEH.Signatures[ks.replicaID] != nil { rs.ThisReplicaNeedsToSignLastEpoch = false ks.updateEpochProposer() // updateSignatureProposer should in general be called after writes // have been flushed to db, but given ThisReplicaNeedsToSignLast = // false we know that updateSignatureProposer will not access the db. ks.updateSignatureProposer() } // get all existing ratifications for this epoch allSignatures := make(map[uint64][]byte) existingRatifications, err := ks.allRatificationsForEpoch(epochNr) if err != nil { log.Panicf("allRatificationsForEpoch(%d): %s", epochNr, err) } for _, seh := range existingRatifications { for id, sig := range seh.Signatures { allSignatures[id] = sig } } // check whether the epoch was already ratified wasRatified := coname.VerifyPolicy(ks.serverAuthorized, tehBytes, allSignatures) if wasRatified { break } for id, sig := range newSEH.Signatures { allSignatures[id] = sig } // check whether the epoch has now become ratified nowRatified := coname.VerifyPolicy(ks.serverAuthorized, tehBytes, allSignatures) if !nowRatified { break } if !rs.LastEpochNeedsRatification { log.Panicf("%x: thought last epoch was not already ratified, but it was", ks.replicaID) } rs.LastEpochNeedsRatification = false ks.updateEpochProposer() var teh proto.EncodedTimestampedEpochHead err = teh.Unmarshal(tehBytes) if err != nil { log.Panicf("invalid epoch head %d (%x): %s", epochNr, tehBytes, err) } allSignaturesSEH := &proto.SignedEpochHead{ Head: teh, Signatures: allSignatures, } oldDeferredIO := deferredIO deferredSendEpoch := ks.verifierLogAppend(&proto.VerifierStep{Type: &proto.VerifierStep_Epoch{Epoch: allSignaturesSEH}}, rs, wb) deferredSendUpdates := []func(){} iter := ks.db.NewIterator(kv.BytesPrefix([]byte{tableUpdatesPendingRatificationPrefix})) defer iter.Release() for iter.Next() { update := &proto.SignedEntryUpdate{} err := update.Unmarshal(iter.Value()) if err != nil { log.Panicf("invalid pending update %x: %s", iter.Value(), err) } deferredSendUpdates = append(deferredSendUpdates, ks.verifierLogAppend(&proto.VerifierStep{Type: &proto.VerifierStep_Update{Update: update}}, rs, wb)) wb.Delete(iter.Key()) } deferredIO = func() { oldDeferredIO() // First, send the ratified epoch to verifiers deferredSendEpoch() // Then send updates that were waiting for that epoch to go out for _, f := range deferredSendUpdates { f() } } case *proto.KeyserverStep_VerifierSigned: rNew := step.GetVerifierSigned() for id := range rNew.Signatures { // Note: The signature *must* have been authenticated before being inserted // into the log, or else verifiers could just trample over everyone else's // signatures, including our own. dbkey := tableRatifications(rNew.Head.Head.Epoch, id) wb.Put(dbkey, proto.MustMarshal(rNew)) } ks.wr.Notify(step.UID, nil) return func() { // As above, first write to DB, *then* notify subscribers. ks.signatureBroadcast.Publish(rNew.Head.Head.Epoch, rNew) } default: log.Panicf("unknown step pb in replicated log: %#v", step) } return }
func (s *Server) handleResponses(round uint64) { rnd := round % MaxRounds allBlocks := <-s.rounds[rnd].dblocksChan //store it on this server as well s.rounds[rnd].allBlocks = allBlocks if s.FSMode { t := time.Now() for i := range allBlocks { s.rounds[rnd].upHashes[i] = allBlocks[i].Block[BlockSize:] } for i := range s.rounds[rnd].upHashesRdy { if s.clientMap[i] != s.id { continue } go func(i int) { s.rounds[rnd].upHashesRdy[i] <- true }(i) } var wg sync.WaitGroup for i := 0; i < s.totalClients; i++ { if s.clientMap[i] == s.id { continue } //if it doesnt belong to me, xor things and send it over wg.Add(1) go func(i int, rpcServer *rpc.Client, r uint64) { defer wg.Done() res := ComputeResponse(allBlocks, s.maskss[r][i], s.secretss[r][i]) sha3.ShakeSum256(s.secretss[r][i], s.secretss[r][i]) sha3.ShakeSum256(s.maskss[r][i], s.maskss[r][i]) //fmt.Println(s.id, round, "mask", i, s.maskss[i]) cb := ClientBlock{ CId: i, SId: s.id, Block: Block{ Block: res, Round: round, }, } err := rpcServer.Call("Server.PutClientBlock", cb, nil) if err != nil { log.Fatal("Couldn't put block: ", err) } }(i, s.rpcServers[s.clientMap[i]], rnd) } wg.Wait() if profile { fmt.Println(s.id, "handling_resp:", time.Since(t)) } } for i := range s.rounds[rnd].blocksRdy { if s.clientMap[i] != s.id { continue } go func(i int, round uint64) { s.rounds[rnd].blocksRdy[i] <- true }(i, round) } }
func main() { configPathPtr := flag.String("config", "clientconfig.json", "path to config file") name := flag.String("name", "*****@*****.**", "name to be looked up") lookupOnly := flag.Bool("lookup", false, "only lookup the name") flag.Parse() timeOut := 10 * time.Second configReader, err := os.Open(*configPathPtr) if err != nil { log.Fatalf("Failed to open configuration file: %s", err) } cfg := &proto.Config{} err = jsonpb.Unmarshal(configReader, cfg) if err != nil { log.Fatalf("Failed to parse configuration file: %s", err) } certFile := "ca.crt.pem" caCertPEM, err := ioutil.ReadFile(certFile) if err != nil { log.Fatalf("couldn't read certs from %s", certFile) } caCertDER, caCertPEM := pem.Decode(caCertPEM) if caCertDER == nil { log.Fatalf("failed to parse key PEM") } caCert, err := x509.ParseCertificate(caCertDER.Bytes) if err != nil { log.Fatal(err) } caPool := x509.NewCertPool() caPool.AddCert(caCert) realm := cfg.Realms[0] clientTLS, err := realm.ClientTLS.Config(getKey) if err != nil { log.Fatal(err) } conn, err := grpc.Dial(realm.Addr, grpc.WithTransportCredentials(credentials.NewTLS(clientTLS)), grpc.WithTimeout(timeOut)) if err != nil { log.Fatal(err) } publicC := proto.NewE2EKSPublicClient(conn) // First, do a lookup to retrieve the index lookup, err := publicC.Lookup(context.Background(), &proto.LookupRequest{ UserId: *name, // We don't care about any signatures here; the server just needs to tell us the index. // We could just give an empty quorum requirement if we wanted (although I guess the // spec actually disallows that). QuorumRequirement: realm.VerificationPolicy.GetQuorum(), }) if err != nil { log.Fatal(err) } fmt.Printf("looking up %s:\n", *name) keys, err := coname.VerifyLookup(cfg, *name, lookup, time.Now()) if err != nil { log.Fatal(err) } if keys == nil { fmt.Printf("not present\n") } else { fmt.Printf("keys: %s\n", keys) } index := lookup.Index if *lookupOnly { return } // Then, do the actual update nonce := make([]byte, 16) _, err = rand.Read(nonce) if err != nil { log.Fatal(err) } profile := proto.EncodedProfile{ Profile: proto.Profile{ Nonce: nonce, Keys: map[string][]byte{"abc": []byte("foo bar"), "xyz": []byte("TEST 456")}, }, } profile.UpdateEncoding() var commitment [64]byte sha3.ShakeSum256(commitment[:], profile.Encoding) var version uint64 if lookup.Entry != nil { version = lookup.Entry.Version + 1 } entry := proto.EncodedEntry{ Entry: proto.Entry{ Index: index, Version: version, UpdatePolicy: &proto.AuthorizationPolicy{ PublicKeys: make(map[uint64]*proto.PublicKey), PolicyType: &proto.AuthorizationPolicy_Quorum{ Quorum: &proto.QuorumExpr{ Threshold: 0, Candidates: []uint64{}, Subexpressions: []*proto.QuorumExpr{}, }, }, }, ProfileCommitment: commitment[:], }, } entry.UpdateEncoding() var entryHash [32]byte sha3.ShakeSum256(entryHash[:], entry.Encoding) fmt.Printf("updating profile:\n") proof, err := publicC.Update(context.Background(), &proto.UpdateRequest{ Update: &proto.SignedEntryUpdate{ NewEntry: entry, Signatures: make(map[uint64][]byte), }, Profile: profile, LookupParameters: &proto.LookupRequest{ UserId: *name, QuorumRequirement: realm.VerificationPolicy.GetQuorum(), }, EmailProof: &proto.EmailProof{ProofType: &proto.EmailProof_DKIMProof{DKIMProof: []byte("X-Apparently-To: [email protected]; Mon, 31 Aug 2015 20:22:05 +0000\r\nReturn-Path: <*****@*****.**>\r\nReceived-SPF: pass (domain of yahoo-inc.com designates 216.145.54.155 as permitted sender)\r\nX-YMailISG: 1EhwNaYWLDv2gTUu.9nt0jxdqITV9b92O6Cwv1fdJI0Znuzw\r\n JiRKT62YsHZWJxcnlzt4SKtiChHTSAwHSy9.w97NwmcUQYqFkJkXmsqAjscY\r\n 0KW9efirs1q3YEvUtmN3BkIhbXujDW5L0Ne0YQtZnK.DF_Yi6dp7RCvfzUZf\r\n RskRPc2qP.tEMNji9_S1kSiPCtn8AwFsPHgGZtQOTtEYsTYUCsD4oEnjAJy1\r\n 332kCwDAOk1wj5l7.PwstEHF438ER_KZaMzfFq8UNrhdEwoFYZGSOfYeFV8W\r\n d2XMmN4vAJRV4D0FV9_cYwJRktzNvKqq.WddXZLuc96QR4.hxOOsq4gEEfJ8\r\n rkw8VodZEu_GlY_2lmyW8UBqmZLhO9lFuNsjfPVSVRyTxt4mMFBG_5XQudBO\r\n xudVy7LzRGiwH.3UlpR4Vl7smmSRC2bf4OrR0bZthRngty1VipzNCRVAE8Od\r\n ZjoR092JxYCF.B94f6ZF5FKqbB2QCjns3WKF0aExw.lzX8_Ral0DsXpBdan5\r\n aIU8cGBQuJBoaAjbaEanoVUXlvz0qXHLEDisvKKsc3w.igAQy01OCSik.2Gx\r\n m2XkGfeZ37mSdZhzAVKMR8eM8WkXl_O.uf7kq6vEyWXuhaMQonTjFYcdAtZX\r\n FMxFdkDTLp4bdEn1gu9JWyRYZTYnj3igBJxc0z_tIBlMrVpcf_NFpjBn1di2\r\n _sfojiKsnhUoYHwbjX7KT7yblcK8Re5Fp57g7XWIc2_tmRbY_iaiGhK0qr4x\r\n 72_hfaF0OBgArvDUPYWF50HYIP597qv1ucNyymAPVGxOx1UNOFWej494R6N1\r\n C8VTghtzKCbklLGGnmSIqPRZCZntvEsWHmmabhFwxRiTZQeu_HfN8CA_Gqoq\r\n k4qQgMbq9o22ekLsAj_jDEnUW343npUbD0XAYDOBCKuSRxZubpmXAu1HqmWL\r\n OTbpjEw8lvF_71EyG5qKVZoEiNCxXMUxia1FBp6RS_uDb4E73TgPxe1v1Ctw\r\n dzqBdg4G0DKkeMqDmDUzQUynHnonJpO4M5Bl6cdr2OfvYK7iDpPf1xeqoexb\r\n McFitNfwU6kax4VRMGahTFv2L56ovtsgV.NMuNEAwad_p3zI0kwi3OMfan9K\r\n _5pIOwQYuKc5vD.5Twq9KSOdYHGYZp_8cBv6dZ4UXvc9k1M5491nJ164VQo.\r\n v.qr6ufI2qhXdMTNxzsLw372.iYBA_5xPeT3dvaYt35sjzmjZnpN\r\nX-Originating-IP: [216.145.54.155]\r\nAuthentication-Results: mta2002.corp.mail.gq1.yahoo.com from=yahoo-inc.com; domainkeys=neutral (no sig); from=yahoo-inc.com; dkim=pass (ok)\r\nReceived: from 127.0.0.1 (EHLO mrout6.yahoo.com) (216.145.54.155)\r\n by mta2002.corp.mail.gq1.yahoo.com with SMTPS; Mon, 31 Aug 2015 20:22:05 +0000\r\nReceived: from omp1018.mail.ne1.yahoo.com (omp1018.mail.ne1.yahoo.com [98.138.89.162])\r\n by mrout6.yahoo.com (8.14.9/8.14.9/y.out) with ESMTP id t7VKLum2007505\r\n (version=TLSv1/SSLv3 cipher=DHE-RSA-CAMELLIA256-SHA bits=256 verify=NO)\r\n for <*****@*****.**>; Mon, 31 Aug 2015 13:21:56 -0700 (PDT)\r\nDKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=yahoo-inc.com;\r\n s=cobra; t=1441052517;\r\n bh=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN/XKdLCPjaYaY=;\r\n h=Date:From:Reply-To:To:Subject;\r\n b=bzbQnVqgZoDSfLYqlVlyv++8CntLQ2AQJR84uPzh2OF/mnz+m+H+YfzAzYQc7b+GU\r\n lGgG0DX89hnWgmp4U1LlzNqwUFrhmL8muanSejVHWkrX48grrG1OwNd5z04oO0hFJE\r\n 20ql0lI4tkgSNR7JLoedMVNm/YdrmCiHbuMj2cxg=\r\nReceived: (qmail 99407 invoked by uid 1000); 31 Aug 2015 20:21:56 -0000\r\nDKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo-inc.com; s=ginc1024; t=1441052516; bh=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=; h=Date:From:Reply-To:To:Message-ID:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding; b=r+c21Nf78CMLaL0K1FmgE/CQz70nUJyVsSgQWqu9OlGdIf2GrY9OmwueQ4xHiLu4A5Jd0CAFgxLS5ZkBkPf4xUCvaLmQOYyY+8bfM2JhuG+g2dOMmjjajNqs+iyXfyx+Ak0T2Kahom/b7cpmr5/PiAb2JpL3O0p2StukvGAolp0=\r\nX-YMail-OSG: 0DtuWrwLUzvKrUMVVf5jtWesdteRmLR6oLTuKAGepU.3rTsZeR6zFmacnp0O_Dj\r\n RWZU-\r\nReceived: by 98.138.105.210; Mon, 31 Aug 2015 20:21:55 +0000 \r\nDate: Mon, 31 Aug 2015 20:21:55 +0000 (UTC)\r\nFrom: Daniel Ziegler <*****@*****.**>\r\nReply-To: Daniel Ziegler <*****@*****.**>\r\nTo: Daniel Ziegler <*****@*****.**>\r\nMessage-ID: <*****@*****.**>\r\nSubject: _YAHOO_E2E_KEYSERVER_PROOF_Y4ncLhJeE/qmdXlzCRp5JtgFHy4F4NbKPawk9U8vJ1E=\r\nMIME-Version: 1.0\r\nContent-Type: text/plain; charset=UTF-8\r\nContent-Transfer-Encoding: 7bit\r\nContent-Length: 1\r\n\r\n")}}}) if err != nil { log.Fatalf("update failed on %s: %s", realm.Addr, err) } if got, want := proof.Profile.Encoding, profile.Encoding; !bytes.Equal(got, want) { log.Fatalf("created profile didn't roundtrip: %x != %x", got, want) } _, err = coname.VerifyLookup(cfg, *name, proof, time.Now()) if err != nil { log.Fatal(err) } fmt.Printf("success\n") }
// step is called by run and changes the in-memory state. No i/o allowed. func (vr *Verifier) step(step *proto.VerifierStep, vs *proto.VerifierState, wb kv.Batch) (deferredIO func()) { // vr: &const // step, vs, wb: &mut switch step.Type.(type) { case *proto.VerifierStep_Update: index := step.GetUpdate().NewEntry.Index prevEntry, err := vr.getEntry(index, vs.NextEpoch) if err := coname.VerifyUpdate(prevEntry, step.GetUpdate()); err != nil { // the keyserver should filter all bad updates log.Panicf("%d: bad update %v: %s", vs.NextIndex, *step, err) } var entryHash [32]byte sha3.ShakeSum256(entryHash[:], step.GetUpdate().NewEntry.Encoding) latestTree := vr.merkletree.GetSnapshot(vs.LatestTreeSnapshot) newTree, err := latestTree.BeginModification() if err != nil { log.Panicf("%d: BeginModification(): %s", vs.NextIndex, err) } if err := newTree.Set(index, entryHash[:]); err != nil { log.Panicf("%d: Set(%x,%x): %s", vs.NextIndex, index, entryHash[:], err) } vs.LatestTreeSnapshot = newTree.Flush(wb).Nr wb.Put(tableEntries(index, vs.NextEpoch), step.GetUpdate().NewEntry.Encoding) case *proto.VerifierStep_Epoch: ok := coname.VerifyPolicy(vr.vs.KeyserverAuth, step.GetEpoch().Head.Encoding, step.GetEpoch().Signatures) // the bad steps here will not get persisted to disk right now. do we want them to? if !ok { log.Panicf("%d: keyserver signature verification failed: %#v", vs.NextIndex, *step) } r := step.GetEpoch().Head if r.Head.Realm != vr.realm { log.Panicf("%d: seh for realm %q, expected %q: %#v", vs.NextEpoch, r.Head.Realm, vr.realm, *step) } if r.Head.Epoch != vs.NextEpoch { log.Panicf("%d: got epoch %d instead: %#v", vs.NextEpoch, r.Head.Epoch, *step) } s := r.Head if !bytes.Equal(s.PreviousSummaryHash, vs.PreviousSummaryHash) { log.Panicf("%d: seh with previous summary hash %q, expected %q: %#v", vs.NextEpoch, s.PreviousSummaryHash, vs.PreviousSummaryHash, *step) } latestTree := vr.merkletree.GetSnapshot(vs.LatestTreeSnapshot) rootHash, err := latestTree.GetRootHash() if err != nil { log.Panicf("GetRootHash() failed: %s", err) } if !bytes.Equal(s.RootHash, rootHash) { log.Panicf("%d: seh with root hash %q, expected %q: %#v", vs.NextEpoch, s.RootHash, rootHash, *step) } seh := &proto.SignedEpochHead{ Head: proto.EncodedTimestampedEpochHead{TimestampedEpochHead: proto.TimestampedEpochHead{ Head: s, Timestamp: proto.Time(time.Now()), }, Encoding: nil}, Signatures: make(map[uint64][]byte, 1), } if vs.PreviousSummaryHash == nil { vs.PreviousSummaryHash = make([]byte, 64) } sha3.ShakeSum256(vs.PreviousSummaryHash[:], seh.Head.Head.Encoding) seh.Head.UpdateEncoding() seh.Signatures[vr.id] = ed25519.Sign(vr.signingKey, proto.MustMarshal(&seh.Head))[:] wb.Put(tableRatifications(vs.NextEpoch, vr.id), proto.MustMarshal(seh)) vs.NextEpoch++ return func() { _, err := vr.keyserver.PushRatification(vr.ctx, seh) if err != nil { log.Printf("PushRatification: %s", err) } } default: log.Panicf("%d: unknown step: %#v", vs.NextIndex, *step) } return }
func TestKeyserverUpdateFailsWithoutVersionIncrease(t *testing.T) { nReplicas := 3 cfgs, gks, ck, clientConfig, _, caPool, _, teardown := setupKeyservers(t, nReplicas) defer teardown() logs, dbs, clks, _, teardown2 := setupRaftLogCluster(t, nReplicas, 0) defer teardown2() kss := []*Keyserver{} for i := range cfgs { ks, err := Open(cfgs[i], dbs[i], logs[i], clientConfig.Realms[0].VerificationPolicy, clks[i], gks[i], nil) if err != nil { t.Fatal(err) } ks.insecureSkipEmailProof = true ks.Start() defer ks.Stop() kss = append(kss, ks) } stop := stoppableSyncedClocks(clks) defer close(stop) waitForFirstEpoch(kss[0], clientConfig.Realms[0].VerificationPolicy.Quorum) clientTLS, err := clientConfig.Realms[0].ClientTLS.Config(ck) if err != nil { t.Fatal(err) } doUpdate(t, kss[0], clientConfig, clientTLS, caPool, clks[0].Now(), alice, 0, proto.Profile{ Nonce: []byte("noncenoncenonceNONCE"), Keys: map[string][]byte{"abc": []byte{1, 2, 3}, "xyz": []byte("TEST 456")}, }) conn, err := grpc.Dial(kss[1].publicListen.Addr().String(), grpc.WithTransportCredentials(credentials.NewTLS(clientTLS))) if err != nil { t.Fatal(err) } profile := proto.EncodedProfile{ Profile: proto.Profile{ Nonce: []byte("NONCE"), Keys: map[string][]byte{"abc": []byte{3, 2, 3}, "xyz": []byte("TEST 456")}, }, } profile.UpdateEncoding() var commitment [64]byte sha3.ShakeSum256(commitment[:], profile.Encoding) entry := proto.EncodedEntry{ Entry: proto.Entry{ Version: 0, UpdatePolicy: &proto.AuthorizationPolicy{ PublicKeys: make(map[uint64]*proto.PublicKey), Quorum: &proto.QuorumExpr{ Threshold: 0, Candidates: []uint64{}, Subexpressions: []*proto.QuorumExpr{}, }, }, ProfileCommitment: commitment[:], }, } entry.UpdateEncoding() updateC := proto.NewE2EKSPublicClient(conn) _, err = updateC.Update(context.Background(), &proto.UpdateRequest{ Update: &proto.SignedEntryUpdate{ NewEntry: entry, Signatures: make(map[uint64][]byte), }, Profile: profile, LookupParameters: &proto.LookupRequest{ UserId: alice, QuorumRequirement: clientConfig.Realms[0].VerificationPolicy.Quorum, }, }) if err == nil { t.Fatalf("update went through despite failure to increment version") } }
func (ks *Keyserver) verifyUpdateEdge(req *proto.UpdateRequest) error { if len(req.Update.NewEntry.Index) != vrf.Size { return fmt.Errorf("index '%x' has wrong length (expected %d)", req.Update.NewEntry.Index, vrf.Size) } prevUpdate, err := ks.getUpdate(req.Update.NewEntry.Index, math.MaxUint64) if err != nil { log.Print(err) return fmt.Errorf("internal error") } if prevUpdate == nil { // registration: check email proof if !ks.insecureSkipEmailProof { if req.EmailProof == nil { return fmt.Errorf("No email proof provided") } lastAtIndex := strings.LastIndex(req.LookupParameters.UserId, "@") if lastAtIndex == -1 { return fmt.Errorf("requested user id is not a valid email address: %q", req.LookupParameters.UserId) } // determine registration type switch t := req.EmailProof.ProofType.(type) { case *proto.EmailProof_DKIMProof: if _, ok := ks.dkimProofAllowedDomains[req.LookupParameters.UserId[lastAtIndex+1:]]; !ok { return fmt.Errorf("domain not in registration whitelist: %q", req.LookupParameters.UserId[lastAtIndex+1:]) } email, payload, err := dkim.CheckEmailProof(t.DKIMProof, ks.dkimProofToAddr, ks.dkimProofToAddr, ks.lookupTXT, ks.clk.Now) if err != nil { return fmt.Errorf("failed to verify DKIM proof: %s", err) } if got, want := email, req.LookupParameters.UserId; got != want { return fmt.Errorf("requested user ID does not match the email proof: %q != %q", got, want) } entryHash, err := base64.StdEncoding.DecodeString(payload) if err != nil { return fmt.Errorf("bad base64 in email proof: %q", payload) } var entryHashProposed [32]byte sha3.ShakeSum256(entryHashProposed[:], req.Update.NewEntry.Encoding) if !bytes.Equal(entryHashProposed[:], entryHash[:]) { return fmt.Errorf("email proof does not match requested entry: %s vs %s (%x)", base64.StdEncoding.EncodeToString(entryHashProposed[:]), payload, req.Update.NewEntry.Encoding) } case *proto.EmailProof_OIDCToken: found := false for _, oc := range ks.oidcProofConfig { if _, ok := oc.allowedDomains[req.LookupParameters.UserId[lastAtIndex+1:]]; !ok { continue } found = true email, err := oc.oidcClient.VerifyIDToken(t.OIDCToken) if err != nil { if _, ok := err.(*oidc.ErrExpired); ok { return &errExpired{err: err} } return err } if got, want := email, req.LookupParameters.UserId; got != want { return fmt.Errorf("requested user ID does not match the email proof: %q != %q", got, want) } } if !found { return fmt.Errorf("domain not in registration whitelist: %q", req.LookupParameters.UserId[lastAtIndex+1:]) } case *proto.EmailProof_SAMLResponse: if _, ok := ks.samlProofAllowedDomains[req.LookupParameters.UserId[lastAtIndex+1:]]; !ok { return fmt.Errorf("domain not in registration whitelist: %q", req.LookupParameters.UserId[lastAtIndex+1:]) } email, err := saml.VerifySAMLResponse(t.SAMLResponse, ks.samlProofIDPCert, ks.samlProofConsumerServiceURL, "EmailAddress", ks.samlProofValidity) if err != nil { if _, ok := err.(*saml.ErrExpired); ok { return &errExpired{err: err} } return err } if got, want := email, req.LookupParameters.UserId; got != want { return fmt.Errorf("requested user ID does not match the email proof: %q != %q", got, want) } default: return fmt.Errorf("Invalid email proof type: %T", t) } } } return ks.verifyUpdateDeterministic(prevUpdate, req) }
//share one time secret with the server func (c *Client) ShareSecret() { gen := c.g.Point().Base() rand := c.suite.Cipher(abstract.RandomKey) secret1 := c.g.Scalar().Pick(rand) secret2 := c.g.Scalar().Pick(rand) public1 := c.g.Point().Mul(gen, secret1) public2 := c.g.Point().Mul(gen, secret2) //generate share secrets via Diffie-Hellman w/ all servers //one used for masks, one used for one-time pad cs1 := ClientDH{ Public: MarshalPoint(public1), Id: c.id, } cs2 := ClientDH{ Public: MarshalPoint(public2), Id: c.id, } masks := make([][]byte, len(c.servers)) secrets := make([][]byte, len(c.servers)) var wg sync.WaitGroup for i, rpcServer := range c.rpcServers { wg.Add(1) go func(i int, rpcServer *rpc.Client, cs1 ClientDH, cs2 ClientDH) { defer wg.Done() servPub1 := make([]byte, SecretSize) servPub2 := make([]byte, SecretSize) servPub3 := make([]byte, SecretSize) call1 := rpcServer.Go("Server.ShareMask", &cs1, &servPub1, nil) call2 := rpcServer.Go("Server.ShareSecret", &cs2, &servPub2, nil) call3 := rpcServer.Go("Server.GetEphKey", 0, &servPub3, nil) <-call1.Done <-call2.Done <-call3.Done masks[i] = MarshalPoint(c.g.Point().Mul(UnmarshalPoint(c.suite, servPub1), secret1)) // c.masks[i] = make([]byte, SecretSize) // c.masks[i][c.id] = 1 secrets[i] = MarshalPoint(c.g.Point().Mul(UnmarshalPoint(c.suite, servPub2), secret2)) //secrets[i] = make([]byte, SecretSize) c.ephKeys[i] = UnmarshalPoint(c.suite, servPub3) }(i, rpcServer, cs1, cs2) } wg.Wait() for r := range c.secretss { for i := range c.secretss[r] { if r == 0 { sha3.ShakeSum256(c.secretss[r][i], secrets[i]) } else { sha3.ShakeSum256(c.secretss[r][i], c.secretss[r-1][i]) } } } for r := range c.maskss { for i := range c.maskss[r] { if r == 0 { sha3.ShakeSum256(c.maskss[r][i], masks[i]) } else { sha3.ShakeSum256(c.maskss[r][i], c.maskss[r-1][i]) } } } }
// ComputeCryptoHash should be used in openchain code so that we can change the actual algo used for crypto-hash at one place func ComputeCryptoHash(data []byte) (hash []byte) { hash = make([]byte, 64) sha3.ShakeSum256(hash, data) return }