// Construct a general message Cipher // from a Stream cipher and a cryptographic Hash. func FromStream(newStream func(key []byte) cipher.Stream, newHash func() hash.Hash, blockLen, keyLen, hashLen int, key []byte, options ...interface{}) abstract.Cipher { sc := streamCipher{} sc.newStream = newStream sc.newHash = newHash sc.blockLen = blockLen sc.keyLen = keyLen sc.hashLen = hashLen sc.h = sc.newHash() if key == nil { key = random.Bytes(hashLen, random.Stream) } if len(key) > 0 { sc.Message(nil, nil, key) } if len(options) > 0 { panic("no FromStream options supported yet") } return abstract.Cipher{&sc} }
func (sk *SKEME) mkmac(masterkey, Xb1, Xb2 []byte) (cipher.Stream, []byte) { keylen := sk.ms.KeySize() hmac := hmac.New(sk.suite.Hash, masterkey) hmac.Write(Xb1) hmac.Write(Xb2) key := hmac.Sum(nil)[:keylen] stream := sk.suite.Cipher(key) mac := random.Bytes(keylen, stream) return stream, mac }
func (sk *SKEME) Recv(rm []byte) (bool, error) { M, err := Decrypt(sk.suite, rm, sk.lpri.Set, sk.lpri.Mine, sk.lpri.Pri, sk.hide) if err != nil { return false, err } // Decode the remote DH public key ptlen := sk.suite.PointLen() if len(M) < ptlen { return false, errors.New("SKEME message too short for DH key") } if sk.rX == nil { rXb := M[:ptlen] rX := sk.suite.Point() if err := rX.UnmarshalBinary(M[:ptlen]); err != nil { return false, err } sk.rX = rX // remote DH public key sk.rXb = rXb // Compute the shared secret and the key-confirmation MACs DH := sk.suite.Point().Mul(rX, sk.lx) seed, _ := DH.MarshalBinary() sk.ms = sk.suite.Cipher(seed) mkey := random.Bytes(sk.ms.KeySize(), sk.ms) sk.ls, sk.lmac = sk.mkmac(mkey, sk.lXb, sk.rXb) sk.rs, sk.rmac = sk.mkmac(mkey, sk.rXb, sk.lXb) // Transmit our key-confirmation MAC with the next message sk.lm = append(sk.lm, sk.lmac...) } // Decode and check the remote key-confirmation MAC if present maclo := ptlen machi := maclo + sk.ms.KeySize() if len(M) < machi { return false, nil // not an error, just not done yet } if subtle.ConstantTimeCompare(M[maclo:machi], sk.rmac) == 0 { return false, errors.New("SKEME remote MAC check failed") } // Shared key confirmed, good to go! // (Although remote might still need our key confirmation.) return true, nil }
func TestEmbedData(T *testing.T) { rand := random.Stream bytes := random.Bytes(50, rand) points := Embed(bytes) reconstructed, err := Extract(points) if err != nil { T.Fatalf("Error occured during data extraction: %v", err) } T.Log(points) T.Log(bytes) T.Log(reconstructed) if !reflect.DeepEqual(bytes, reconstructed) { T.Fatal("Reconstructed byte slice did not equal original") } }
// SpongeCipher builds a general message Cipher from a Sponge function. func FromSponge(sponge Sponge, key []byte, options ...interface{}) abstract.Cipher { sc := spongeCipher{} sc.sponge = sponge sc.rate = sponge.Rate() sc.cap = sponge.Capacity() sc.pad = byte(0x7f) // default, unused by standards sc.buf = make([]byte, sc.rate+sc.cap) sc.pos = 0 sc.parseOptions(options) // Key the cipher in some appropriate fashion if key == nil { key = random.Bytes(sponge.Capacity(), random.Stream) } if len(key) > 0 { sc.Message(nil, nil, key) } // Setup normal-case domain-separation byte used for message payloads sc.setDomain(domainPayload, 0) return abstract.Cipher{&sc} }
// Start initiates the RandHound protocol run. The client pseudo-randomly // chooses the server grouping, forms an I1 message for each group, and sends // it to all servers of that group. func (rh *RandHound) Start() error { var err error // Set timestamp rh.time = time.Now() // Choose client randomness rand := random.Bytes(rh.Suite().Hash().Size(), random.Stream) rh.cliRand = rand // Determine server grouping rh.server, rh.key, err = rh.Shard(rand, rh.groups) if err != nil { return err } // Set some group parameters for i, group := range rh.server { rh.threshold[i] = 2 * len(group) / 3 rh.polyCommit[i] = make([]abstract.Point, len(group)) g := make([]int, len(group)) for j, server0 := range group { s0 := server0.RosterIndex rh.ServerIdxToGroupNum[s0] = i rh.ServerIdxToGroupIdx[s0] = j g[j] = s0 } rh.group[i] = g } // Compute session id rh.sid, err = rh.sessionID(rh.nodes, rh.faulty, rh.purpose, rh.time, rh.cliRand, rh.threshold, rh.Public(), rh.key) if err != nil { return err } // Multicast first message to grouped servers for i, group := range rh.server { index := make([]uint32, len(group)) for j, server := range group { index[j] = uint32(server.RosterIndex) } i1 := &I1{ SID: rh.sid, Threshold: rh.threshold[i], Group: index, Key: rh.key[i], } rh.mutex.Lock() // Sign I1 and store signature in i1.Sig if err := signSchnorr(rh.Suite(), rh.Private(), i1); err != nil { rh.mutex.Unlock() return err } rh.i1s[i] = i1 rh.mutex.Unlock() if err := rh.Multicast(i1, group...); err != nil { return err } } return nil }
func (rh *RandHound) handleR1(r1 WR1) error { msg := &r1.R1 idx := r1.RosterIndex grp := rh.ServerIdxToGroupNum[idx] pos := rh.ServerIdxToGroupIdx[idx] rh.mutex.Lock() defer rh.mutex.Unlock() // Verify R1 message signature if err := verifySchnorr(rh.Suite(), rh.key[grp][pos], msg); err != nil { return err } // Verify that server replied to the correct I1 message if err := verifyMessage(rh.Suite(), rh.i1s[grp], msg.HI1); err != nil { return err } // Record R1 message rh.r1s[idx] = msg // Prepare data for recovery of polynomial commits and verification of shares n := len(msg.EncShare) poly := make([][]byte, n) index := make([]int, n) encShare := make([]abstract.Point, n) encProof := make([]ProofCore, n) for i := 0; i < n; i++ { poly[i] = msg.CommitPoly index[i] = msg.EncShare[i].Pos encShare[i] = msg.EncShare[i].Val encProof[i] = msg.EncShare[i].Proof } // Init PVSS and recover polynomial commits H, _ := rh.Suite().Point().Pick(nil, rh.Suite().Cipher(rh.sid)) pvss := NewPVSS(rh.Suite(), H, rh.threshold[grp]) polyCommit, err := pvss.Commits(poly, index) if err != nil { return err } // Record polynomial commits rh.polyCommit[idx] = polyCommit // Return, if we already committed to secrets previously if len(rh.chosenSecret) > 0 { return nil } // Verify encrypted shares good, _, err := pvss.Verify(H, rh.key[grp], polyCommit, encShare, encProof) if err != nil { return err } // Record valid encrypted shares per secret/server for _, g := range good { if _, ok := rh.secret[idx]; !ok { rh.secret[idx] = make([]int, 0) } rh.secret[idx] = append(rh.secret[idx], msg.EncShare[g].Target) } // Check if there is at least a threshold number of reconstructable secrets // in each group. If yes proceed to the next phase. Note the double-usage // of the threshold which is used to determine if enough valid shares for a // single secret are available and if enough secrets for a given group are // available goodSecret := make(map[int][]int) for i, group := range rh.server { var secret []int for _, server := range group { j := server.RosterIndex if share, ok := rh.secret[j]; ok && rh.threshold[i] <= len(share) { secret = append(secret, j) } } if rh.threshold[i] <= len(secret) { goodSecret[i] = secret } } // Proceed, if there are enough good secrets if len(goodSecret) == rh.groups { // Reset secret for the next phase (see handleR2) rh.secret = make(map[int][]int) // Choose secrets that contribute to collective randomness for i := range rh.server { // Randomly remove some secrets so that a threshold of secrets remains rand := random.Bytes(rh.Suite().Hash().Size(), random.Stream) prng := rh.Suite().Cipher(rand) secret := goodSecret[i] for j := 0; j < len(secret)-rh.threshold[i]; j++ { k := int(random.Uint32(prng) % uint32(len(secret))) secret = append(secret[:k], secret[k+1:]...) } rh.chosenSecret[i] = secret } log.Lvlf3("Grouping: %v", rh.group) log.Lvlf3("ChosenSecret: %v", rh.chosenSecret) // Transformation of commitments from int to uint32 to avoid protobuff errors var chosenSecret = make([][]uint32, len(rh.chosenSecret)) for i := range rh.chosenSecret { var l []uint32 for j := range rh.chosenSecret[i] { l = append(l, uint32(rh.chosenSecret[i][j])) } chosenSecret[i] = l } // Prepare a message for each server of a group and send it for i, group := range rh.server { for j, server := range group { // Among the good secrets chosen previously collect all valid // shares, proofs, and polynomial commits intended for the // target server var encShare []Share var polyCommit []abstract.Point for _, k := range rh.chosenSecret[i] { r1 := rh.r1s[k] pc := rh.polyCommit[k] encShare = append(encShare, r1.EncShare[j]) polyCommit = append(polyCommit, pc[j]) } i2 := &I2{ Sig: crypto.SchnorrSig{}, SID: rh.sid, ChosenSecret: chosenSecret, EncShare: encShare, PolyCommit: polyCommit, } if err := signSchnorr(rh.Suite(), rh.Private(), i2); err != nil { return err } rh.i2s[server.RosterIndex] = i2 if err := rh.SendTo(server, i2); err != nil { return err } } } } return nil }
func TestSession(T *testing.T) { numUsers := 5 numNotaries := 2 slot := 0 data, users, notaries := createSetup(numUsers, numNotaries) userciphers := make([]*UserCipherText, numUsers) notaryciphers := make([]*NotaryCipherText, numNotaries) // Create some plaintext bytes and embed them into group elements bytes := random.Bytes(100, random.Stream) plaintext := crypto.Embed(bytes) amount := len(plaintext) // Let the users construct their ciphertexts var err error for i := range users { if i == slot { userciphers[i], err = users[i].GetRealCipherText(data, plaintext) } else { userciphers[i], err = users[i].GetCoverCipherText(data, amount, slot) } } if err != nil { T.Fatalf("Failed to create proof: %v", err) } // Check the validity of the user ciphertexts for i, usercipher := range userciphers { if err := usercipher.Verify(data); err != nil { T.Logf("Ciphertext of user %d was invalid: %v", i, err) T.Fail() } } // Let the notaries construct their ciphertexts for j := range notaries { notaryciphers[j], err = notaries[j].GetCiphertext(data, amount) } if err != nil { T.Fatalf("Failed to create proof: %v", err) } // Check the validity of the notary ciphertexts for j, notarycipher := range notaryciphers { if err := notarycipher.Verify(data); err != nil { T.Logf("Ciphertext of notary %d was invalid: %v", j, err) T.Fail() } } // Reconstruct plaintext messages, err := ReconstructMessage(userciphers, notaryciphers) if err != nil { T.Logf("Failed to reconstruct plaintext: %v", err) T.Fail() } if len(messages) != len(plaintext) { T.Fatal("Got wrong amount of points, was %d, should be %d", len(messages), len(plaintext)) } // Check if the reconstructed plaintext equals the original ones reconstructed, _ := crypto.Extract(messages) if !reflect.DeepEqual(bytes, reconstructed) { T.Fatal("Extracted plaintext does not equal the original") } }