tuneinsight / lattigo

A library for lattice-based multiparty homomorphic encryption in Go
Apache License 2.0
1.22k stars 181 forks source link

Using dckks, getting wrong results with MulRelin #37

Closed Dylan-yxn closed 4 years ago

Dylan-yxn commented 4 years ago

Hi, I am new here. I am learning the use of DCKKS,but I got into trouble:

After Collective relinearization key generation to get rlk , use it in evaluator.MulRelin(ciphertext, ciphertext, rlk, result)

I got the wrong result much bigger than I wanted.

Because there is no example of dckks, can you write me a hint?

Pro7ech commented 4 years ago

Hello Dylan,

We are aware that there are yet no examples for DCKKS, we are working on providing some in the near future. However the key establishment are very similar, if not identical, to the DBFV scheme, for which there are examples.

Edit : I forgot to add that the test files can be used as examples.

Could you post your code and the output that you get?

Dylan-yxn commented 4 years ago

Thanks for your reply.

I am imitating RKG written by DBFV, this is my code:

       ........
        // 2) Collective relinearization key generation
    for _, pi := range P {
        rkg.GenShareRoundOne(pi.rlkEphemSk, pi.sk.Get(), crp, pi.rkgShareOne)
    }
    rkgCombined1, rkgCombined2, rkgCombined3 := rkg.AllocateShares()
    for _, pi := range P {
        rkg.AggregateShareRoundOne(pi.rkgShareOne, rkgCombined1, rkgCombined1)
    }
    for _, pi := range P {
        rkg.GenShareRoundTwo(rkgCombined1, pi.sk.Get(), crp, pi.rkgShareTwo)
    }
    for _, pi := range P {
        rkg.AggregateShareRoundTwo(pi.rkgShareTwo, rkgCombined2, rkgCombined2)
    }
    for _, pi := range P {
        rkg.GenShareRoundThree(rkgCombined2, pi.rlkEphemSk, pi.sk.Get(), pi.rkgShareThree)
    }
    rlk := ckks.NewRelinKey(params)
    for _, pi := range P {
        rkg.AggregateShareRoundThree(pi.rkgShareThree, rkgCombined3, rkgCombined3)
    }
    rkg.GenRelinearizationKey(rkgCombined2, rkgCombined3, rlk)

        encoder := ckks.NewEncoder(params)
    encInputs := make([]*ckks.Ciphertext, N, N)
    encryptor := ckks.NewEncryptorFromPk(params, pk)
    for i := range encInputs {
        encInputs[i] = ckks.NewCiphertext(params, 1,params.MaxLevel(),params.Scale)
    }

    pt := ckks.NewPlaintext(params,params.MaxLevel(),params.Scale)
    for i, pi := range P {
        encoder.Encode(pt,pi.input,slots)
        encryptor.Encrypt(pt, encInputs[i])
    }

    evaluator := ckks.NewEvaluator(params)
        resultop := ckks.NewCiphertext(params,1,params.MaxLevel(),params.Scale)
    evaluator.MulRelin(encInputs[0],encInputs[1],rlk,resultop)
       /*CKS and decrypt*/

My Input is:

               ......
                pi.input = make([]complex128, slots, slots)
        if i == 0 {
            pi.input[0] = complex(6.02,0)
            pi.input[1] = complex(7.23,0)
            pi.input[2] = complex(6.72,0)
            pi.input[3] = complex(6.02,0)
            pi.input[4] = complex(8.44,0)
        }else if i == 1 {
            pi.input[0] = complex(6.33, 0)
            pi.input[1] = complex(7.12, 0)
            pi.input[2] = complex(5.03, 0)
            pi.input[3] = complex(6.64, 0)
            pi.input[4] = complex(9.03, 0)
        }
               ......

The answer I got was:

FXH{ C@)21KXA0V`{) @R4F

The answer should be: (38.1066+0i, 51.4776+0i......)

Pro7ech commented 4 years ago

It looks like your problem is about the scaling. Here is why I think it is :

At some point you must have hard-coded the scale of your result to be your original scale of 2^35 instead of correct scale (when you do a multiplication the resulting scale is the multiplication between the scale of the two inputs). I don't see the end of your code, but probably you created a plaintext with a scale of params.Scale instead of a scale of resultop.Scale(). You can also use DecryptNew, and it will return a plaintext with the correct scale.

Dylan-yxn commented 4 years ago

I tried your method using DecryptNew () but still got the wrong answer, here is my code about decryption:

        /*CKS*/
    zero := params.NewPolyQ()
    cksCombined := cks.AllocateShare()
    for _, pi := range P[1:] {
        cks.GenShare(pi.sk.Get(), zero, resultop, pi.cksShare)
    }
    encOut := ckks.NewCiphertext(params, 1,params.MaxLevel(),params.Scale)
    for _, pi := range P {
        cks.AggregateShares(pi.cksShare, cksCombined, cksCombined)
    }
    cks.KeySwitch(cksCombined,resultop, encOut)

    decryptor := ckks.NewDecryptor(params, P[0].sk)
    res := encoder.Decode(decryptor.DecryptNew(encOut), slots)
    fmt.Printf(" %6f %6f %6f %6f %6f\n",
        round(res[0]), round(res[1]), round(res[2]), round(res[3]), round(res[4]))

I also tried Rescale() but got the same answer. 58ZMQYC69C0}7FV $E66O7

When I used MultByConst(ciphertxt, float64, ciphertext), the answer is right. So I suspect it was rlk before.

Pro7ech commented 4 years ago

Ok, I see what is going on. It is encOut that you have to set with a scale of resultop.Scale(). The cks.KeySwitch is not setting the output ciphertext scale based on the input ciphertext scale, while you would expect it to do it. I'll add an issue and correct this in the v.1.3.1. Thank you for finding this issue.

Dylan-yxn commented 4 years ago

Thank you again for your patience with me!😁

Pro7ech commented 4 years ago

No problem, thank you for testing our library and giving us feedbacks :)