From 8a9743f08e27ecbb2093a7b10dabb7cbf3e14092 Mon Sep 17 00:00:00 2001 From: didier amyot Date: Sat, 29 Nov 2014 22:40:35 -0800 Subject: [PATCH] imported from https://bitbucket.org/ustraca/crypto --- doc.go | 38 ++++ encoding.go | 256 +++++++++++++++++++++++ encoding_test.go | 123 +++++++++++ paillier.go | 116 +++++++++++ paillier_test.go | 70 +++++++ thresholdkey.go | 291 ++++++++++++++++++++++++++ thresholdkey_generator.go | 200 ++++++++++++++++++ thresholdkey_generator_test.go | 259 +++++++++++++++++++++++ thresholdkey_test.go | 369 +++++++++++++++++++++++++++++++++ utils.go | 28 +++ utils_test.go | 55 +++++ 11 files changed, 1805 insertions(+) create mode 100644 doc.go create mode 100644 encoding.go create mode 100644 encoding_test.go create mode 100644 paillier.go create mode 100644 paillier_test.go create mode 100644 thresholdkey.go create mode 100644 thresholdkey_generator.go create mode 100644 thresholdkey_generator_test.go create mode 100644 thresholdkey_test.go create mode 100644 utils.go create mode 100644 utils_test.go diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..69a6a91 --- /dev/null +++ b/doc.go @@ -0,0 +1,38 @@ +//The MIT License (MIT) + +//Copyright (c) 2013 didier amyot + +//Permission is hereby granted, free of charge, to any person obtaining a copy +//of this software and associated documentation files (the "Software"), to deal +//in the Software without restriction, including without limitation the rights +//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//copies of the Software, and to permit persons to whom the Software is +//furnished to do so, subject to the following conditions: + +//The above copyright notice and this permission notice shall be included in +//all copies or substantial portions of the Software. + +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +//THE SOFTWARE. + +/* +Implementation of the paillier cryptosystem. See +http://en.wikipedia.org/wiki/Paillier_cryptosystem for an introduction. + +The threshold paillier cryptosystem is a translation of the java version +that can be found at http://cs.utdallas.edu/dspl/cgi-bin/pailliertoolbox/ that +is an implementation of the algorithm defined in Damgård's paper +"A Generalization of Paillier's Public-Key System with Applications to +Electronic Voting". + +Another important feature of the library is the ease of serilisation of all +the structs in JSON and BSON. + +Implements the paillier cryptosystem and threshold paillier crytposystem. +*/ +package paillier diff --git a/encoding.go b/encoding.go new file mode 100644 index 0000000..df70725 --- /dev/null +++ b/encoding.go @@ -0,0 +1,256 @@ +package paillier + +// all the methods relative to the set/get json/bson should be stored +// in this file. + +import ( + "encoding/json" + "errors" + "fmt" + "math/big" + + "gopkg.in/mgo.v2/bson" +) + +func fromHex(hex string) (*big.Int, error) { + n, err := new(big.Int).SetString(hex, 16) + if !err { + msg := fmt.Sprintf("Cannot convert %s to int as hexadecimal", hex) + return nil, errors.New(msg) + } + return n, nil +} + +func all(oks []bool) bool { + for _, ok := range oks { + if !ok { + return false + } + } + return true +} + +type dbCypher struct { + C string +} + +func (this *Cypher) GetBSON() (interface{}, error) { + return &dbCypher{fmt.Sprintf("%x", this.C)}, nil +} + +func (cypher *Cypher) SetBSON(raw bson.Raw) error { + c := dbCypher{} + if err := raw.Unmarshal(&c); err != nil { + return err + } + var ok bool + cypher.C, ok = new(big.Int).SetString(c.C, 16) + if !ok { + return errors.New("big int not in hexadecimal format") + } + return nil +} + +type dbThresholdKey struct { + TotalNumberOfDecryptionServers int + Threshold int + V string + Vi []string + N string + G string +} + +func (this *dbThresholdKey) FromThresholdKey(key *ThresholdKey) { + this.TotalNumberOfDecryptionServers = key.TotalNumberOfDecryptionServers + this.Threshold = key.Threshold + this.V = fmt.Sprintf("%x", key.V) + this.N = fmt.Sprintf("%x", key.N) + this.G = fmt.Sprintf("%x", key.G) + this.Vi = make([]string, len(key.Vi)) + for i, vi := range key.Vi { + this.Vi[i] = fmt.Sprintf("%x", vi) + } +} + +func (this *dbThresholdKey) ToThresholdKey(key *ThresholdKey) error { + key.TotalNumberOfDecryptionServers = this.TotalNumberOfDecryptionServers + key.Threshold = this.Threshold + oks := make([]bool, 3) + key.V, oks[0] = new(big.Int).SetString(this.V, 16) + key.N, oks[1] = new(big.Int).SetString(this.N, 16) + key.G, oks[2] = new(big.Int).SetString(this.G, 16) + if !all(oks) { + return errors.New("not hexadecimal") + } + key.Vi = make([]*big.Int, len(this.Vi)) + var ok bool + for i, vi := range this.Vi { + key.Vi[i], ok = new(big.Int).SetString(vi, 16) + if !ok { + return errors.New("not hexadecimal") + } + + } + return nil +} + +func (this *ThresholdKey) GetBSON() (interface{}, error) { + r := new(dbThresholdKey) + r.FromThresholdKey(this) + return r, nil +} + +func (this *ThresholdKey) SetBSON(raw bson.Raw) error { + r := new(dbThresholdKey) + if err := raw.Unmarshal(r); err != nil { + return err + } + return r.ToThresholdKey(this) +} + +type dbPrivateKey struct { + N string `bson:",omitempty"` + G string `bson:",omitempty"` + Lambda string `bson:",omitempty"` + Mu string `bson:",omitempty"` +} + +func (this *PrivateKey) GetBSON() (interface{}, error) { + m := make(map[string]string) + if this.G != nil { + m["g"] = fmt.Sprintf("%x", this.G) + } + if this.N != nil { + m["n"] = fmt.Sprintf("%x", this.N) + } + if this.Lambda != nil { + m["lambda"] = fmt.Sprintf("%x", this.Lambda) + } + if this.Mu != nil { + m["mu"] = fmt.Sprintf("%x", this.Mu) + } + return m, nil +} + +func (this *PrivateKey) SetBSON(raw bson.Raw) error { + var err error = nil + c := new(dbPrivateKey) + raw.Unmarshal(c) + + if c.N != "" { + this.N, err = fromHex(c.N) + if err != nil { + return err + } + } + if c.G != "" { + this.G, err = fromHex(c.G) + if err != nil { + return err + } + } + if c.Lambda != "" { + this.Lambda, err = fromHex(c.Lambda) + if err != nil { + return err + } + } + if c.Mu != "" { + this.Mu, err = fromHex(c.Mu) + } + + return err +} + +type dbPartialDecryptionZKP struct { + Z string `json:"z"` + E string `json:"e"` + C string `json:"c"` + V string `json:"v"` + G string `json:"g"` + N string `json:"n"` + Vi []string `json:"vi"` + Decryption string `json:"decryption"` + Id int `json:"id"` + TotalNumberOfDecryptionServers int `json:"total_number_of_decryption_servers"` + Threshold int `json:"threshold"` +} + +func (this *dbPartialDecryptionZKP) FromPartialDecryptionZKP(pd *PartialDecryptionZKP) { + this.Id = pd.Id + this.TotalNumberOfDecryptionServers = pd.Key.TotalNumberOfDecryptionServers + this.Threshold = pd.Key.Threshold + this.Z = fmt.Sprintf("%x", pd.Z) + this.E = fmt.Sprintf("%x", pd.E) + this.G = fmt.Sprintf("%x", pd.Key.G) + this.N = fmt.Sprintf("%x", pd.Key.N) + this.C = fmt.Sprintf("%x", pd.C) + this.V = fmt.Sprintf("%x", pd.Key.V) + this.Decryption = fmt.Sprintf("%x", pd.Decryption) + this.Vi = make([]string, len(pd.Key.Vi)) + for i, vi := range pd.Key.Vi { + this.Vi[i] = fmt.Sprintf("%x", vi) + } +} + +func (this *dbPartialDecryptionZKP) ToPartialDecryptionZKP(pd *PartialDecryptionZKP) error { + pd.Key = new(ThresholdKey) + + var oks = make([]bool, 7) + + pd.Id = this.Id + pd.Key.TotalNumberOfDecryptionServers = this.TotalNumberOfDecryptionServers + pd.Key.Threshold = this.Threshold + pd.Z, oks[0] = new(big.Int).SetString(this.Z, 16) + pd.E, oks[1] = new(big.Int).SetString(this.E, 16) + pd.C, oks[2] = new(big.Int).SetString(this.C, 16) + pd.Key.V, oks[3] = new(big.Int).SetString(this.V, 16) + pd.Key.G, oks[4] = new(big.Int).SetString(this.G, 16) + pd.Key.N, oks[5] = new(big.Int).SetString(this.N, 16) + pd.Decryption, oks[6] = new(big.Int).SetString(this.Decryption, 16) + + if !all(oks) { + fmt.Println(oks) + fmt.Println(this.E, "|", this.Z, "|", this.C, "|", this.V, "|", this.Decryption) + return errors.New("numbers not in hexadecimal format") + } + + pd.Key.Vi = make([]*big.Int, len(this.Vi)) + var ok bool + for i, vi := range this.Vi { + pd.Key.Vi[i], ok = new(big.Int).SetString(vi, 16) + if !ok { + return errors.New("numbers not in hexadecimal format") + } + } + + return nil +} + +func (this *PartialDecryptionZKP) MarshalJSON() ([]byte, error) { + db := new(dbPartialDecryptionZKP) + db.FromPartialDecryptionZKP(this) + return json.Marshal(db) +} + +func (this *PartialDecryptionZKP) UnmarshalJSON(data []byte) error { + db := new(dbPartialDecryptionZKP) + if err := json.Unmarshal(data, db); err != nil { + return err + } + return db.ToPartialDecryptionZKP(this) +} + +func (this *PartialDecryptionZKP) GetBSON() (interface{}, error) { + db := new(dbPartialDecryptionZKP) + db.FromPartialDecryptionZKP(this) + return db, nil +} + +func (this *PartialDecryptionZKP) SetBSON(raw bson.Raw) error { + db := new(dbPartialDecryptionZKP) + if err := raw.Unmarshal(db); err != nil { + return err + } + return db.ToPartialDecryptionZKP(this) +} diff --git a/encoding_test.go b/encoding_test.go new file mode 100644 index 0000000..660a5e2 --- /dev/null +++ b/encoding_test.go @@ -0,0 +1,123 @@ +package paillier + +import ( + "crypto/rand" + "encoding/json" + "math/big" + "reflect" + "testing" + + "gopkg.in/mgo.v2/bson" +) + +func AssertBSONIsGood(object, dump interface{}, t *testing.T) { + data, err := bson.Marshal(object) + if err != nil { + t.Error(err) + return + } + if err = bson.Unmarshal(data, dump); err != nil { + t.Error(err) + return + } + if !reflect.DeepEqual(object, dump) { + t.Fail() + } +} + +func AssertJSONIsGood(object, dump interface{}, t *testing.T) { + data, err := json.Marshal(object) + if err != nil { + t.Error(err) + return + } + if err = json.Unmarshal(data, dump); err != nil { + t.Error(err) + return + } + if !reflect.DeepEqual(object, dump) { + t.Fail() + } +} + +func TestCypherGetBSON(t *testing.T) { + key := CreatePrivateKey(big.NewInt(101), big.NewInt(113)) + cypher, err := key.Encrypt(big.NewInt(100), rand.Reader) + if err != nil { + t.Error(err) + return + } + data, err := bson.Marshal(cypher) + if err != nil { + t.Error(err) + return + } + cypher2 := new(Cypher) + if err = bson.Unmarshal(data, cypher2); err != nil { + t.Error(err) + return + } + if !reflect.DeepEqual(cypher, cypher2) { + t.Fail() + } +} + +func TestSetPrivateKeyBson(t *testing.T) { + key := CreatePrivateKey(big.NewInt(101), big.NewInt(113)) + AssertBSONIsGood(key, new(PrivateKey), t) + +} + +func TestGetBSONEmptyKey(t *testing.T) { + key := new(PrivateKey) + m, err := key.GetBSON() + if err != nil { + t.Error(err) + } + if !reflect.DeepEqual(make(map[string]string), m) { + t.Error(m) + } +} + +func TestPartialDecryptionZKPJsonification(t *testing.T) { + pd := new(PartialDecryptionZKP) + pd.Key = new(ThresholdKey) + pd.Key.Threshold = 98 + pd.Key.TotalNumberOfDecryptionServers = 230 + pd.Id = 1 + pd.Key.Vi = []*big.Int{b(77), b(67)} // vi is 67 + pd.Key.N = b(131) + pd.Key.V = b(101) + pd.Decryption = b(171) + pd.E = b(112) + pd.C = b(99) + pd.Key.N = b(345) + pd.Key.G = b(99) + pd.Z = b(88) + + AssertJSONIsGood(pd, new(PartialDecryptionZKP), t) +} + +func TestPartialDecryptionZKPBSONification(t *testing.T) { + pd := new(PartialDecryptionZKP) + pd.Key = new(ThresholdKey) + pd.Key.Threshold = 98 + pd.Key.TotalNumberOfDecryptionServers = 230 + pd.Id = 1 + pd.Key.Vi = []*big.Int{b(77), b(67)} // vi is 67 + pd.Key.N = b(131) + pd.Key.V = b(101) + pd.Decryption = b(171) + pd.E = b(112) + pd.C = b(99) + pd.Key.N = b(345) + pd.Key.G = b(99) + pd.Z = b(88) + + AssertBSONIsGood(pd, new(PartialDecryptionZKP), t) +} + +func TestThresholdKeyBSON(t *testing.T) { + key := &ThresholdKey{PublicKey{b(9), b(8), nil}, 7, 6, b(3), []*big.Int{b(2), b(34)}} + AssertBSONIsGood(key, new(ThresholdKey), t) +} diff --git a/paillier.go b/paillier.go new file mode 100644 index 0000000..15b8cbb --- /dev/null +++ b/paillier.go @@ -0,0 +1,116 @@ +package paillier + +import ( + "fmt" + "io" + "math/big" +) + +type PublicKey struct { + N, G *big.Int // usually G is set to N+1 + n2 *big.Int // the cache value of N^2 +} + +func (this *PublicKey) GetBSON() (interface{}, error) { + m := make(map[string]string) + m["g"] = fmt.Sprintf("%x", this.G) + m["n"] = fmt.Sprintf("%x", this.N) + return m, nil +} + +func (this *PublicKey) GetNSquare() *big.Int { + if this.n2 != nil { + return this.n2 + } + this.n2 = new(big.Int).Mul(this.N, this.N) + return this.n2 +} + +// Encode a plain text in a cypher one. The plain text must be smaller that +// N and bigger or equal than zero. random is usually rand.Reader from the +// package crypto/rand. +// +// Returns an error if an error has be returned by io.Reader. +func (pub *PublicKey) Encrypt(m *big.Int, random io.Reader) (*Cypher, error) { + r, err := GetRandomNumberInMultiplicativeGroup(pub.N, random) + if err != nil { + return nil, err + } + nSquare := pub.GetNSquare() + + gm := new(big.Int).Exp(pub.G, m, nSquare) + rn := new(big.Int).Exp(r, pub.N, nSquare) + return &Cypher{new(big.Int).Mod(new(big.Int).Mul(rn, gm), nSquare)}, nil +} + +// Takes two cypher texts and returns a 3rd one that encode +// the sum of the two plain texts. +func (this *PublicKey) Add(cypher1, cypher2 *Cypher) *Cypher { + m := new(big.Int).Mul(cypher1.C, cypher2.C) + return &Cypher{new(big.Int).Mod(m, this.GetNSquare())} +} + +type PrivateKey struct { + PublicKey + Lambda, Mu *big.Int +} + +func (this *PrivateKey) String() string { + ret := fmt.Sprintf("g : %x", this.G) + ret += fmt.Sprintf("n : %x", this.N) + ret += fmt.Sprintf("lambda: %x", this.Lambda) + ret += fmt.Sprintf("mu : %x", this.Mu) + return ret +} + +func (priv *PrivateKey) Decrypt(cypher *Cypher) (msg *big.Int) { + tmp := new(big.Int).Exp(cypher.C, priv.Lambda, priv.GetNSquare()) + msg = new(big.Int).Mod(new(big.Int).Mul(L(tmp, priv.N), priv.Mu), priv.N) + return +} + +type Cypher struct { + C *big.Int +} + +func (this *Cypher) String() string { + return fmt.Sprintf("%x", this.C) +} + +func L(u, n *big.Int) *big.Int { + t := new(big.Int).Add(u, big.NewInt(-1)) + return new(big.Int).Div(t, n) +} + +func LCM(x, y *big.Int) *big.Int { + return new(big.Int).Mul(new(big.Int).Div(x, new(big.Int).GCD(nil, nil, x, y)), y) +} + +func minusOne(x *big.Int) *big.Int { + return new(big.Int).Add(x, big.NewInt(-1)) +} + +func computeMu(g, lambda, n *big.Int) *big.Int { + n2 := new(big.Int).Mul(n, n) + u := new(big.Int).Exp(g, lambda, n2) + return new(big.Int).ModInverse(L(u, n), n) +} + +func computeLamda(p, q *big.Int) *big.Int { + return LCM(minusOne(p), minusOne(q)) +} + +func CreatePrivateKey(p, q *big.Int) *PrivateKey { + n := new(big.Int).Mul(p, q) + lambda := new(big.Int).Mul(minusOne(p), minusOne(q)) + g := new(big.Int).Add(n, big.NewInt(1)) + mu := new(big.Int).ModInverse(lambda, n) + return &PrivateKey{ + PublicKey: PublicKey{ + N: n, + G: g, + }, + Lambda: lambda, + Mu: mu, + } +} diff --git a/paillier_test.go b/paillier_test.go new file mode 100644 index 0000000..1f3adcd --- /dev/null +++ b/paillier_test.go @@ -0,0 +1,70 @@ +package paillier + +import ( + "crypto/rand" + "math/big" + "reflect" + "testing" +) + +func TestLCM(t *testing.T) { + a := big.NewInt(2 * 3 * 3 * 3 * 5 * 5) + b := big.NewInt(3 * 3 * 5 * 5 * 57 * 11) + exp := big.NewInt(3 * 3 * 5 * 5) + if reflect.DeepEqual(exp, LCM(a, b)) { + t.Fail() + } +} + +func TestL(t *testing.T) { + u := big.NewInt(21) + n := big.NewInt(3) + exp := big.NewInt(6) + if !reflect.DeepEqual(exp, L(u, n)) { + t.Error("L function is not good") + } +} + +func TestComputeMu(t *testing.T) { + p := big.NewInt(13) + q := big.NewInt(11) + + lambda := computeLamda(p, q) + g := big.NewInt(5000) + n := new(big.Int).Mul(p, q) + + exp := big.NewInt(3) + if !reflect.DeepEqual(computeMu(g, lambda, n), exp) { + t.Error("lambda is not well computed") + } +} + +func TestEncryptDecryptSmall(t *testing.T) { + p := big.NewInt(13) + q := big.NewInt(11) + for i := 1; i < 10; i++ { + privateKey := CreatePrivateKey(p, q) + + inicialValue := big.NewInt(100) + cypher, err := privateKey.Encrypt(inicialValue, rand.Reader) + if err != nil { + t.Error(err) + } + returnedValue := privateKey.Decrypt(cypher) + if !reflect.DeepEqual(inicialValue, returnedValue) { + t.Error("wrond decryption ", returnedValue, " is not ", inicialValue) + } + } + +} + +func TestAddCypher(t *testing.T) { + privateKey := CreatePrivateKey(big.NewInt(13), big.NewInt(11)) + cypher1, _ := privateKey.Encrypt(big.NewInt(12), rand.Reader) + cypher2, _ := privateKey.Encrypt(big.NewInt(13), rand.Reader) + cypher3 := privateKey.Add(cypher1, cypher2) + m := privateKey.Decrypt(cypher3) + if !reflect.DeepEqual(m, big.NewInt(25)) { + t.Error(m) + } +} diff --git a/thresholdkey.go b/thresholdkey.go new file mode 100644 index 0000000..1cd1c6d --- /dev/null +++ b/thresholdkey.go @@ -0,0 +1,291 @@ +package paillier + +import ( + "crypto/rand" + "crypto/sha256" + "errors" + "io" + "math/big" + + "bitbucket.org/ustraca/crypto" +) + +type ThresholdKey struct { + PublicKey + TotalNumberOfDecryptionServers int + Threshold int + V *big.Int + Vi []*big.Int +} + +// returns the value of (4*delta**2)** -1 mod n +func (this *ThresholdKey) CombineSharesConstant() *big.Int { + tmp := new(big.Int).Mul(FOUR, new(big.Int).Mul(this.Delta(), this.Delta())) + return (&big.Int{}).ModInverse(tmp, this.N) +} + +// returns the factorial of the number of TotalNumberOfDecryptionServers +func (this *ThresholdKey) Delta() *big.Int { + return crypto.Factorial(this.TotalNumberOfDecryptionServers) +} + +func (this *ThresholdKey) MakeVerificationBeforeCombiningPartialDecryptions(shares []*PartialDecryption) error { + if len(shares) < this.Threshold { + return errors.New("Threshold not meet") + } + tmp := make(map[int]bool) + for _, share := range shares { + tmp[share.Id] = true + } + if len(tmp) != len(shares) { + return errors.New("two shares has been created by the same server") + } + return nil +} + +func (this *ThresholdKey) UpdateLambda(share1, share2 *PartialDecryption, lambda *big.Int) *big.Int { + num := new(big.Int).Mul(lambda, big.NewInt(int64(-share2.Id))) + denom := big.NewInt(int64(share1.Id - share2.Id)) + return new(big.Int).Div(num, denom) +} + +func (this *ThresholdKey) ComputeLambda(share *PartialDecryption, shares []*PartialDecryption) *big.Int { + lambda := this.Delta() + for _, share2 := range shares { + if share2.Id != share.Id { + lambda = this.UpdateLambda(share, share2, lambda) + } + } + return lambda +} + +func (this *ThresholdKey) updateCprime(cprime, lambda *big.Int, share *PartialDecryption) *big.Int { + twoLambda := new(big.Int).Mul(TWO, lambda) + ret := this.exp(share.Decryption, twoLambda, this.GetNSquare()) + ret = new(big.Int).Mul(cprime, ret) + return new(big.Int).Mod(ret, this.GetNSquare()) +} + +func (this *ThresholdKey) divide(a, b *big.Int) *big.Int { + if a.Cmp(ZERO) == -1 { + if b.Cmp(ZERO) == -1 { + return new(big.Int).Div(new(big.Int).Neg(a), new(big.Int).Neg(b)) + } + return new(big.Int).Neg(new(big.Int).Div(new(big.Int).Neg(a), b)) + } + return new(big.Int).Div(a, b) +} + +func (this *ThresholdKey) exp(a, b, c *big.Int) *big.Int { + if b.Cmp(ZERO) == -1 { + ret := new(big.Int).Exp(a, new(big.Int).Neg(b), c) + return new(big.Int).ModInverse(ret, c) + } + return new(big.Int).Exp(a, b, c) + +} + +func (this *ThresholdKey) computeDecryption(cprime *big.Int) *big.Int { + l := L(cprime, this.N) + return new(big.Int).Mod(new(big.Int).Mul(this.CombineSharesConstant(), l), this.N) +} + +func (this *ThresholdKey) CombinePartialDecryptions(shares []*PartialDecryption) (*big.Int, error) { + if err := this.MakeVerificationBeforeCombiningPartialDecryptions(shares); err != nil { + return nil, err + } + + cprime := ONE + for _, share := range shares { + lambda := this.ComputeLambda(share, shares) + cprime = this.updateCprime(cprime, lambda, share) + } + + return this.computeDecryption(cprime), nil +} + +func (this *ThresholdKey) CombinePartialDecryptionsZKP(shares []*PartialDecryptionZKP) (*big.Int, error) { + ret := make([]*PartialDecryption, 0) + for _, share := range shares { + if share.Verify() { + ret = append(ret, &share.PartialDecryption) + } + } + return this.CombinePartialDecryptions(ret) +} + +// Verify if the decryption of `encryptedMessage` has well been done. +// It verifies all the zero-knoledge proofs, the value of the decrypted +// and decrypted message. +// The method returns `nil` if everything is good. Otherwise it returns an +// explicative message +func (this *ThresholdKey) VerifyDecryption(encryptedMessage, decryptedMessage *big.Int, shares []*PartialDecryptionZKP) error { + for _, share := range shares { + if share.C.Cmp(encryptedMessage) != 0 { + return errors.New("The encrypted message is not the same than the one in the shares") + } + } + res, err := this.CombinePartialDecryptionsZKP(shares) + if err != nil { + return err + } + if res.Cmp(decryptedMessage) != 0 { + return errors.New("The decrypted message is not the same than the one in the shares") + } + return nil +} + +type ThresholdPrivateKey struct { + ThresholdKey + Id int + Share *big.Int +} + +// Decrypt the cypher text and returns the partial decryption +func (this *ThresholdPrivateKey) Decrypt(c *big.Int) *PartialDecryption { + ret := new(PartialDecryption) + ret.Id = this.Id + exp := new(big.Int).Mul(this.Share, new(big.Int).Mul(TWO, this.Delta())) + ret.Decryption = new(big.Int).Exp(c, exp, this.GetNSquare()) + + return ret +} + +func (this *ThresholdPrivateKey) copyVi() []*big.Int { + ret := make([]*big.Int, len(this.Vi)) + for i, vi := range this.Vi { + ret[i] = new(big.Int).Add(vi, big.NewInt(0)) + } + return ret +} + +func (this *ThresholdPrivateKey) GetThresholdKey() *ThresholdKey { + ret := new(ThresholdKey) + ret.Threshold = this.Threshold + ret.TotalNumberOfDecryptionServers = this.TotalNumberOfDecryptionServers + ret.V = new(big.Int).Add(this.V, big.NewInt(0)) + ret.Vi = this.copyVi() + ret.N = new(big.Int).Add(this.N, big.NewInt(0)) + ret.G = new(big.Int).Add(this.N, big.NewInt(1)) + return ret +} + +func (this *ThresholdPrivateKey) ComputeZ(r, e *big.Int) *big.Int { + tmp := new(big.Int).Mul(e, this.Delta()) + tmp = new(big.Int).Mul(tmp, this.Share) + return new(big.Int).Add(r, tmp) + +} + +func (this *ThresholdPrivateKey) computeHash(a, b, c4, ci2 *big.Int) *big.Int { + hash := sha256.New() + hash.Write(a.Bytes()) + hash.Write(b.Bytes()) + hash.Write(c4.Bytes()) + hash.Write(ci2.Bytes()) + return new(big.Int).SetBytes(hash.Sum([]byte{})) + +} + +func (this *ThresholdPrivateKey) DecryptAndProduceZNP(c *big.Int, random io.Reader) (*PartialDecryptionZKP, error) { + pd := new(PartialDecryptionZKP) + pd.Key = this.GetThresholdKey() + pd.C = c + pd.Id = this.Id + pd.Decryption = this.Decrypt(c).Decryption + + // choose random number + r, err := rand.Int(random, this.GetNSquare()) + if err != nil { + return nil, err + } + // compute a + c4 := new(big.Int).Exp(c, FOUR, nil) + a := new(big.Int).Exp(c4, r, this.GetNSquare()) + + // compute b + b := new(big.Int).Exp(this.V, r, this.GetNSquare()) + + // compute hash + ci2 := new(big.Int).Exp(pd.Decryption, big.NewInt(2), nil) + + pd.E = this.computeHash(a, b, c4, ci2) + + pd.Z = this.ComputeZ(r, pd.E) + + return pd, nil +} + +// Verify if the partial decryption key is well formed. If well formed, +// the method return nil else an explicative error is returned. +func (this *ThresholdPrivateKey) Validate(random io.Reader) error { + m, err := rand.Int(random, this.N) + if err != nil { + return err + } + c, err := this.Encrypt(m, random) + if err != nil { + return err + } + proof, err := this.DecryptAndProduceZNP(c.C, random) + if err != nil { + return err + } + if !proof.Verify() { + return errors.New("invalid share.") + } + return nil +} + +type PartialDecryption struct { + Id int + Decryption *big.Int +} + +type PartialDecryptionZKP struct { + PartialDecryption + Key *ThresholdKey // the public key used to encrypt + E *big.Int // the challenge + Z *big.Int // the value needed to check to verify the decryption + C *big.Int // the input cypher text + +} + +func (this *PartialDecryptionZKP) verifyPart1() *big.Int { + c4 := new(big.Int).Exp(this.C, FOUR, nil) + decryption2 := new(big.Int).Exp(this.Decryption, TWO, nil) + + a1 := new(big.Int).Exp(c4, this.Z, this.Key.GetNSquare()) + a2 := new(big.Int).Exp(decryption2, this.E, this.Key.GetNSquare()) + a2 = new(big.Int).ModInverse(a2, this.Key.GetNSquare()) + a := new(big.Int).Mod(new(big.Int).Mul(a1, a2), this.Key.GetNSquare()) + return a +} + +func (this *PartialDecryptionZKP) neg(n *big.Int) *big.Int { + return new(big.Int).Neg(n) +} + +func (this *PartialDecryptionZKP) verifyPart2() *big.Int { + vi := this.Key.Vi[this.Id-1] + b1 := new(big.Int).Exp(this.Key.V, this.Z, this.Key.GetNSquare()) + b2 := new(big.Int).Exp(vi, this.E, this.Key.GetNSquare()) + b2 = new(big.Int).ModInverse(b2, this.Key.GetNSquare()) + b := new(big.Int).Mod(new(big.Int).Mul(b1, b2), this.Key.GetNSquare()) + return b +} + +func (this *PartialDecryptionZKP) Verify() bool { + a := this.verifyPart1() + b := this.verifyPart2() + hash := sha256.New() + hash.Write(a.Bytes()) + hash.Write(b.Bytes()) + c4 := new(big.Int).Exp(this.C, FOUR, nil) + hash.Write(c4.Bytes()) + ci2 := new(big.Int).Exp(this.Decryption, TWO, nil) + hash.Write(ci2.Bytes()) + + expectedE := new(big.Int).SetBytes(hash.Sum([]byte{})) + return this.E.Cmp(expectedE) == 0 +} diff --git a/thresholdkey_generator.go b/thresholdkey_generator.go new file mode 100644 index 0000000..bee27b0 --- /dev/null +++ b/thresholdkey_generator.go @@ -0,0 +1,200 @@ +package paillier + +import ( + "crypto/rand" + "io" + "math/big" + + "bitbucket.org/ustraca/crypto" +) + +type ThresholdKeyGenerator struct { + nbits int + TotalNumberOfDecryptionServers int + Threshold int + Random io.Reader + + //Both p1 and q1 are primes of length nbits - 1 + p1 *big.Int + q1 *big.Int + + p *big.Int //p is prime and p=2*p1+1 + q *big.Int //q is prime and q=2*q1+1 + n *big.Int // n=p*q + m *big.Int //m = p1*q1 + nSquare *big.Int //nSquare = n*n + nm *big.Int // nm = n*m + + // As specified in the paper, d must satify d=1 mod n and d=0 mod m + d *big.Int + + // A generator of QR in Z_{n^2} + v *big.Int + + // the polynomial coefficients to hide a secret. See Shamir. + polynomialCoefficients []*big.Int +} + +// preferable way to counstruct the ThresholdKeyGenerator. No verification +// is done on the input values. You need to bu sure that nbits is big enough +// and that Threshold > TotalNumberOfDecryptionServers / 2 +func GetThresholdKeyGenerator(nbits, TotalNumberOfDecryptionServers, Threshold int, random io.Reader) *ThresholdKeyGenerator { + ret := new(ThresholdKeyGenerator) + ret.nbits = nbits + ret.TotalNumberOfDecryptionServers = TotalNumberOfDecryptionServers + ret.Threshold = Threshold + ret.Random = random + return ret +} + +func (this *ThresholdKeyGenerator) GenerateSafePrimes() (*big.Int, *big.Int, error) { + return crypto.GenerateSafePrimes(this.nbits, this.Random) +} + +func (this *ThresholdKeyGenerator) InitPandP1() error { + var err error + this.p, this.p1, err = this.GenerateSafePrimes() + return err +} + +func (this *ThresholdKeyGenerator) InitQandQ1() error { + var err error + this.q, this.q1, err = this.GenerateSafePrimes() + return err +} + +func (this *ThresholdKeyGenerator) InitShortcuts() { + this.n = new(big.Int).Mul(this.p, this.q) + this.m = new(big.Int).Mul(this.p1, this.q1) + this.nSquare = new(big.Int).Mul(this.n, this.n) + this.nm = new(big.Int).Mul(this.n, this.m) + +} + +func (this *ThresholdKeyGenerator) ArePsAndQsGood() bool { + if this.p.Cmp(this.q) == 0 { + return false + } + if this.p.Cmp(this.q1) == 0 { + return false + } + if this.p1.Cmp(this.q) == 0 { + return false + } + return true +} + +func (this *ThresholdKeyGenerator) InitPsAndQs() error { + if err := this.InitPandP1(); err != nil { + return err + } + if err := this.InitQandQ1(); err != nil { + return err + } + if !this.ArePsAndQsGood() { + return this.InitPsAndQs() + } + return nil +} + +func (this *ThresholdKeyGenerator) ComputeV() error { + var err error + this.v, err = crypto.GetRandomGeneratorOfTheQuadraticResidue(this.nSquare, this.Random) + return err +} + +// Choose d such that d=0 mod m and d=1 mod n, using Chinese remainder thm +// we can find d using Chinese remainder thm note that $d=(m. (m^-1 mod n))$ +func (this *ThresholdKeyGenerator) InitD() { + mInverse := new(big.Int).ModInverse(this.m, this.n) + this.d = new(big.Int).Mul(mInverse, this.m) +} + +func (this *ThresholdKeyGenerator) InitNumerialValues() error { + if err := this.InitPsAndQs(); err != nil { + return err + } + this.InitShortcuts() + this.InitD() + return this.ComputeV() +} + +func (this *ThresholdKeyGenerator) GenerateHidingPolynomial() error { + this.polynomialCoefficients = make([]*big.Int, this.Threshold) + this.polynomialCoefficients[0] = this.d + var err error + for i := 1; i < this.Threshold; i++ { + this.polynomialCoefficients[i], err = rand.Int(this.Random, this.nm) + if err != nil { + return err + } + } + return nil +} + +func (this *ThresholdKeyGenerator) ComputeShare(index int) *big.Int { + share := big.NewInt(0) + for i := 0; i < this.Threshold; i++ { + a := this.polynomialCoefficients[i] + b := new(big.Int).Exp(big.NewInt(int64(index+1)), big.NewInt(int64(i)), nil) + tmp := new(big.Int).Mul(a, b) + share = new(big.Int).Add(share, tmp) + } + return new(big.Int).Mod(share, this.nm) +} + +func (this *ThresholdKeyGenerator) CreateShares() []*big.Int { + shares := make([]*big.Int, this.TotalNumberOfDecryptionServers) + for i := 0; i < this.TotalNumberOfDecryptionServers; i++ { + shares[i] = this.ComputeShare(i) + } + return shares +} + +func (this *ThresholdKeyGenerator) Delta() *big.Int { + return crypto.Factorial(this.TotalNumberOfDecryptionServers) +} + +func (this *ThresholdKeyGenerator) CreateViArray(shares []*big.Int) (viArray []*big.Int) { + viArray = make([]*big.Int, len(shares)) + delta := this.Delta() + for i, share := range shares { + tmp := new(big.Int).Mul(share, delta) + viArray[i] = new(big.Int).Exp(this.v, tmp, this.nSquare) + } + return viArray +} + +func (this *ThresholdKeyGenerator) CreatePrivateKey(i int, share *big.Int, viArray []*big.Int) *ThresholdPrivateKey { + ret := new(ThresholdPrivateKey) + ret.N = this.n + ret.G = new(big.Int).Add(ret.N, ONE) + ret.V = this.v + + ret.TotalNumberOfDecryptionServers = this.TotalNumberOfDecryptionServers + ret.Threshold = this.Threshold + ret.Share = share + ret.Id = i + 1 + ret.Vi = viArray + return ret +} + +func (this *ThresholdKeyGenerator) CreatePrivateKeys() []*ThresholdPrivateKey { + shares := this.CreateShares() + viArray := this.CreateViArray(shares) + ret := make([]*ThresholdPrivateKey, this.TotalNumberOfDecryptionServers) + for i := 0; i < this.TotalNumberOfDecryptionServers; i++ { + ret[i] = this.CreatePrivateKey(i, shares[i], viArray) + } + return ret +} + +func (this *ThresholdKeyGenerator) Generate() ([]*ThresholdPrivateKey, error) { + if err := this.InitNumerialValues(); err != nil { + return nil, err + } + if err := this.GenerateHidingPolynomial(); err != nil { + return nil, err + } + return this.CreatePrivateKeys(), nil +} diff --git a/thresholdkey_generator_test.go b/thresholdkey_generator_test.go new file mode 100644 index 0000000..fbc3e1f --- /dev/null +++ b/thresholdkey_generator_test.go @@ -0,0 +1,259 @@ +package paillier + +import ( + "crypto/rand" + "math/big" + "reflect" + "testing" +) + +var MockGenerateSafePrimes = func() (*big.Int, *big.Int, error) { + return big.NewInt(887), big.NewInt(443), nil +} + +func AreSafePrimes(p, q *big.Int, expectedLength int, t *testing.T) { + if l := p.BitLen(); l != expectedLength { + t.Error("p does not have the good length. ", l) + } + if l := q.BitLen(); l != expectedLength-1 { + t.Error("q does not have the good length. ", l) + } + if !p.ProbablyPrime(100) { + t.Error("p is not a probable prime :(") + } + if !q.ProbablyPrime(100) { + t.Error("q is not a probable prime :(") + } + if p.Int64() != 2*q.Int64()+1 { + t.Error("p does not equals 2 * q + 1") + } +} + +func TestGenerateSafePrimesOfThresholdKeyGenerator(t *testing.T) { + tkh := new(ThresholdKeyGenerator) + tkh.nbits = 10 + tkh.Random = rand.Reader + p, q, err := tkh.GenerateSafePrimes() + if err != nil { + t.Error(err) + return + } + AreSafePrimes(p, q, 10, t) + +} + +func TestInitPandP1(t *testing.T) { + tkh := new(ThresholdKeyGenerator) + tkh.nbits = 10 + tkh.Random = rand.Reader + + tkh.InitPandP1() + AreSafePrimes(tkh.p, tkh.p1, 10, t) + +} + +func TestInitQandQ1(t *testing.T) { + tkh := new(ThresholdKeyGenerator) + tkh.nbits = 10 + tkh.Random = rand.Reader + + tkh.InitQandQ1() + AreSafePrimes(tkh.q, tkh.q1, 10, t) +} + +func TestInitPsAndQs(t *testing.T) { + tkh := new(ThresholdKeyGenerator) + tkh.nbits = 10 + tkh.Random = rand.Reader + + tkh.InitPsAndQs() + AreSafePrimes(tkh.q, tkh.q1, 10, t) + AreSafePrimes(tkh.q, tkh.q1, 10, t) +} + +func TestArePsAndQsGood(t *testing.T) { + tkh := new(ThresholdKeyGenerator) + tkh.p, tkh.p1, tkh.q, tkh.q1 = b(6), b(5), b(4), b(3) + if !tkh.ArePsAndQsGood() { + t.Fail() + } + + tkh.p, tkh.p1, tkh.q, tkh.q1 = b(6), b(5), b(6), b(3) + if tkh.ArePsAndQsGood() { + t.Fail() + } + + tkh.p, tkh.p1, tkh.q, tkh.q1 = b(6), b(5), b(5), b(3) + if tkh.ArePsAndQsGood() { + t.Fail() + } +} + +func TestInitShortcuts(t *testing.T) { + tkh := new(ThresholdKeyGenerator) + tkh.p, tkh.p1, tkh.q, tkh.q1 = b(11), b(7), b(5), b(3) + tkh.InitShortcuts() + + if n(tkh.n) != 11*5 { + t.Error("wrong n", tkh.n) + } + if n(tkh.m) != 7*3 { + t.Error("wrong m", tkh.m) + } + if n(tkh.nm) != 11*5*7*3 { + t.Error("wrong nm", tkh.nm) + } + if n(tkh.nSquare) != 11*5*11*5 { + t.Error("wrong nSquare", tkh.nSquare) + } +} + +func TestInitD(t *testing.T) { + tkh := new(ThresholdKeyGenerator) + tkh.p, tkh.p1, tkh.q, tkh.q1 = b(863), b(431), b(839), b(419) + tkh.InitShortcuts() + tkh.InitD() + if n(tkh.d)%n(tkh.m) != 0 { + t.Fail() + } + if n(tkh.d)%n(tkh.n) != 1 { + t.Fail() + } +} + +func TestInitNumerialValues(t *testing.T) { + tkh := new(ThresholdKeyGenerator) + tkh.nbits = 10 + tkh.Random = rand.Reader + + if err := tkh.InitNumerialValues(); err != nil { + t.Error(err) + } +} + +func TestGenerateHidingPolynomial(t *testing.T) { + tkh := new(ThresholdKeyGenerator) + tkh.nbits = 10 + tkh.Threshold = 10 + tkh.Random = rand.Reader + if err := tkh.InitNumerialValues(); err != nil { + t.Error(err) + return + } + if err := tkh.GenerateHidingPolynomial(); err != nil { + t.Error(err) + } + p := tkh.polynomialCoefficients + if len(p) != tkh.Threshold { + t.Fail() + return + } + if n(p[0]) != n(tkh.d) { + t.Fail() + } + for i := 1; i < len(p); i++ { + if j := n(p[i]); j < 0 || j >= n(tkh.nm) { + t.Fail() + } + } +} + +func TestComputeShare(t *testing.T) { + tkh := new(ThresholdKeyGenerator) + tkh.nbits = 10 + tkh.Threshold = 3 + tkh.TotalNumberOfDecryptionServers = 5 + tkh.nm = b(103) + tkh.polynomialCoefficients = []*big.Int{b(29), b(88), b(51)} + share := tkh.ComputeShare(2) + if n(share) != 31 { + t.Error("error computing a share. ", share) + } +} + +func TestCreateShares(t *testing.T) { + tkh := new(ThresholdKeyGenerator) + tkh.nbits = 10 + tkh.Threshold = 10 + tkh.TotalNumberOfDecryptionServers = 100 + tkh.Random = rand.Reader + if err := tkh.InitNumerialValues(); err != nil { + t.Error(err) + return + } + if err := tkh.GenerateHidingPolynomial(); err != nil { + t.Error(err) + return + } + + if shares := tkh.CreateShares(); len(shares) != 100 { + t.Fail() + } +} + +func TestCreateViArray(t *testing.T) { + tkh := new(ThresholdKeyGenerator) + tkh.TotalNumberOfDecryptionServers = 10 + tkh.v = b(54) + tkh.nSquare = b(101 * 101) + vArr := tkh.CreateViArray([]*big.Int{b(12), b(90), b(103)}) + exp := []*big.Int{b(6162), b(304), b(2728)} + if !reflect.DeepEqual(vArr, exp) { + t.Fail() + } +} + +func TestGetThresholdKeyGenerator(t *testing.T) { + tkh := GetThresholdKeyGenerator(50, 10, 6, rand.Reader) + if err := tkh.InitNumerialValues(); err != nil { + t.Error(nil) + } +} + +func TestGenerate(t *testing.T) { + tkh := GetThresholdKeyGenerator(32, 10, 6, rand.Reader) + tpks, err := tkh.Generate() + if err != nil { + t.Error(err) + return + } + if len(tpks) != 10 { + t.Fail() + } + for i, tpk := range tpks { + if tpk.Id != i+1 { + t.Fail() + } + if len(tpk.Vi) != 10 { + t.Fail() + } + if tpk.G == nil || tpk.N == nil { + t.Fail() + } + if tpk.Threshold != 6 || tpk.TotalNumberOfDecryptionServers != 10 { + t.Fail() + } + } +} + +func TestComputeV(t *testing.T) { + tkh := GetThresholdKeyGenerator(32, 10, 6, rand.Reader) + tkh.n = b(1907 * 1823) + tkh.nSquare = new(big.Int).Mul(tkh.n, tkh.n) + for i := 0; i < 100; i++ { + if err := tkh.ComputeV(); err != nil { + t.Error(err) + return + } + if tkh.v.Cmp(tkh.nSquare) > 0 { + t.Error("v is too big") + } + if tkh.v.Cmp(tkh.n) > 0 { + return + } + } + t.Error(`v has never been bigger than n. It is suspicious in the sense< + than it was taken in the range 0...n**2 -1 + `) + +} diff --git a/thresholdkey_test.go b/thresholdkey_test.go new file mode 100644 index 0000000..23578e2 --- /dev/null +++ b/thresholdkey_test.go @@ -0,0 +1,369 @@ +package paillier + +import ( + "crypto/rand" + "math/big" + "reflect" + "testing" +) + +func GetThresholdPrivateKey() *ThresholdPrivateKey { + tkh := GetThresholdKeyGenerator(32, 10, 6, rand.Reader) + tpks, err := tkh.Generate() + if err != nil { + panic(err) + } + return tpks[6] +} + +func TestDelta(t *testing.T) { + tk := new(ThresholdKey) + tk.TotalNumberOfDecryptionServers = 6 + if delta := tk.Delta(); 720 != n(delta) { + t.Error("Delta is not 720 but", delta) + } +} + +func TestCombineSharesConstant(t *testing.T) { + tk := new(ThresholdKey) + tk.N = big.NewInt(101 * 103) + tk.TotalNumberOfDecryptionServers = 6 + + if c := tk.CombineSharesConstant(); !reflect.DeepEqual(big.NewInt(4558), c) { + t.Error("wrong combined key. ", c) + } +} + +func TestDecrypt(t *testing.T) { + key := new(ThresholdPrivateKey) + key.TotalNumberOfDecryptionServers = 10 + key.N = b(101 * 103) + key.Share = b(862) + key.Id = 9 + c := b(56) + + partial := key.Decrypt(c) + + if partial.Id != 9 { + t.Fail() + } + if n(partial.Decryption) != 40644522 { + t.Error("wrong decryption ", partial.Decryption) + } +} + +func TestCopyVi(t *testing.T) { + key := new(ThresholdPrivateKey) + key.Vi = []*big.Int{b(34), b(2), b(29)} + vi := key.copyVi() + if !reflect.DeepEqual(vi, key.Vi) { + t.Fail() + } + key.Vi[1] = b(89) + if reflect.DeepEqual(vi, key.Vi) { + t.Fail() + } +} + +func TestEncryptWithThresholdKey(t *testing.T) { + pd := GetThresholdPrivateKey() + _, err := pd.Encrypt(big.NewInt(876), rand.Reader) + if err != nil { + t.Fail() + } +} + +func TestDecryptWithThresholdKey(t *testing.T) { + pd := GetThresholdPrivateKey() + c, err := pd.Encrypt(big.NewInt(876), rand.Reader) + if err != nil { + t.Fail() + } + pd.Decrypt(c.C) +} + +func TestVerifyPart1(t *testing.T) { + pd := new(PartialDecryptionZKP) + pd.Key = new(ThresholdKey) + pd.Key.N = b(131) + pd.Decryption = b(101) + pd.C = b(99) + pd.E = b(112) + pd.Z = b(88) + + if a := pd.verifyPart1(); n(a) != 11986 { + t.Error("wrong a ", a) + } +} + +func TestVerifyPart2(t *testing.T) { + pd := new(PartialDecryptionZKP) + pd.Key = new(ThresholdKey) + pd.Id = 1 + pd.Key.Vi = []*big.Int{b(77), b(67)} // vi is 67 + pd.Key.N = b(131) + pd.Key.V = b(101) + pd.E = b(112) + pd.Z = b(88) + if b := pd.verifyPart2(); n(b) != 14602 { + t.Error("wrong b ", b) + } +} + +func TestDecryptAndProduceZNP(t *testing.T) { + pd := GetThresholdPrivateKey() + c, err := pd.Encrypt(big.NewInt(876), rand.Reader) + if err != nil { + t.Error(err) + return + } + znp, err := pd.DecryptAndProduceZNP(c.C, rand.Reader) + if err != nil { + t.Error(err) + return + } + + if !znp.Verify() { + t.Fail() + } +} + +func TestMakeVerificationBeforeCombiningPartialDecryptions(t *testing.T) { + tk := new(ThresholdKey) + tk.Threshold = 2 + if tk.MakeVerificationBeforeCombiningPartialDecryptions([]*PartialDecryption{}) == nil { + t.Fail() + } + prms := []*PartialDecryption{new(PartialDecryption), new(PartialDecryption)} + prms[1].Id = 1 + if tk.MakeVerificationBeforeCombiningPartialDecryptions(prms) != nil { + t.Fail() + } + prms[1].Id = 0 + if tk.MakeVerificationBeforeCombiningPartialDecryptions(prms) == nil { + t.Fail() + } +} + +func TestUpdateLambda(t *testing.T) { + tk := new(ThresholdKey) + lambda := b(11) + share1 := &PartialDecryption{3, b(5)} + share2 := &PartialDecryption{7, b(3)} + res := tk.UpdateLambda(share1, share2, lambda) + if n(res) != 20 { + t.Error("wrong lambda", n(res)) + } +} + +func TestupdateCprime(t *testing.T) { + tk := new(ThresholdKey) + tk.N = b(99) + cprime := b(77) + lambda := b(52) + share := &PartialDecryption{3, b(5)} + cprime = tk.updateCprime(cprime, lambda, share) + if n(cprime) != 8558 { + t.Error("wrong cprime", cprime) + } + +} + +func TestEncryptingDecryptingSimple(t *testing.T) { + tkh := GetThresholdKeyGenerator(10, 2, 1, rand.Reader) + tpks, err := tkh.Generate() + if err != nil { + t.Error(err) + return + } + message := b(100) + c, err := tpks[1].Encrypt(message, rand.Reader) + if err != nil { + t.Error(err) + return + } + share1 := tpks[0].Decrypt(c.C) + message2, err := tpks[0].CombinePartialDecryptions([]*PartialDecryption{share1}) + if err != nil { + t.Error(err) + } + if n(message) != n(message2) { + t.Error("decrypted message is not the same one than the input one ", message2) + } +} + +func TestEncryptingDecrypting(t *testing.T) { + tkh := GetThresholdKeyGenerator(10, 2, 2, rand.Reader) + tpks, err := tkh.Generate() + if err != nil { + t.Error(err) + return + } + message := b(100) + c, err := tpks[1].Encrypt(message, rand.Reader) + if err != nil { + t.Error(err) + return + } + share1 := tpks[0].Decrypt(c.C) + share2 := tpks[1].Decrypt(c.C) + message2, err := tpks[0].CombinePartialDecryptions([]*PartialDecryption{share1, share2}) + if err != nil { + t.Error(err) + } + if n(message) != n(message2) { + t.Error("The decrypted cyphered is not original massage but ", message2) + } +} + +func TestDecryption(t *testing.T) { + // test the correct decryption of '100'. + share1 := &PartialDecryption{1, b(384111638639)} + share2 := &PartialDecryption{2, b(235243761043)} + tk := new(ThresholdKey) + tk.Threshold = 2 + tk.TotalNumberOfDecryptionServers = 2 + tk.N = b(637753) + tk.G = b(637754) + tk.V = b(70661107826) + if msg, err := tk.CombinePartialDecryptions([]*PartialDecryption{share1, share2}); err != nil { + t.Error(err) + } else if n(msg) != 100 { + t.Error("decrypted message was not 100 but ", msg) + } + +} + +func TestDivide(t *testing.T) { + tk := new(ThresholdKey) + if r := tk.divide(b(77), b(4)); n(r) != 19 { + t.Error("77 / 4 != 19 ( ", r, " )") + } + if r := tk.divide(b(-77), b(-4)); n(r) != 19 { + t.Error("-77 / -4 != 19 ( ", r, " )") + } + if r := tk.divide(b(-77), b(4)); n(r) != -19 { + t.Error("-77 / 4 != -19 ( ", r, " )") + } + if r := tk.divide(b(77), b(-4)); n(r) != -19 { + t.Error("77 / -4 != -19 ( ", r, " )") + } +} + +func TestValidate(t *testing.T) { + pk := GetThresholdPrivateKey() + if err := pk.Validate(rand.Reader); err != nil { + t.Error(err) + } + pk.Id++ + if err := pk.Validate(rand.Reader); err == nil { + t.Fail() + } +} + +func TestCombinePartialDecryptionsZKP(t *testing.T) { + tkh := GetThresholdKeyGenerator(10, 2, 2, rand.Reader) + tpks, err := tkh.Generate() + if err != nil { + t.Error(err) + return + } + message := b(100) + c, err := tpks[1].Encrypt(message, rand.Reader) + if err != nil { + t.Error(err) + return + } + share1, err := tpks[0].DecryptAndProduceZNP(c.C, rand.Reader) + if err != nil { + t.Error(err) + return + } + share2, err := tpks[1].DecryptAndProduceZNP(c.C, rand.Reader) + if err != nil { + t.Error(err) + return + } + message2, err := tpks[0].CombinePartialDecryptionsZKP([]*PartialDecryptionZKP{share1, share2}) + if err != nil { + t.Error(err) + } + if n(message) != n(message2) { + t.Error("The decrypted cyphered is not original massage but ", message2) + } + share1.E = b(687687678) + _, err = tpks[0].CombinePartialDecryptionsZKP([]*PartialDecryptionZKP{share1, share2}) + if err == nil { + t.Fail() + } +} + +func TestCombinePartialDecryptionsWith100Shares(t *testing.T) { + tkh := GetThresholdKeyGenerator(10, 100, 50, rand.Reader) + tpks, err := tkh.Generate() + if err != nil { + t.Error(err) + return + } + message := b(100) + c, err := tpks[1].Encrypt(message, rand.Reader) + if err != nil { + t.Error(err) + return + } + shares := make([]*PartialDecryption, 75) + for i := 0; i < 75; i++ { + shares[i] = tpks[i].Decrypt(c.C) + } + + message2, err := tpks[0].CombinePartialDecryptions(shares) + if err != nil { + t.Error(err) + } + if n(message) != n(message2) { + t.Error("The decrypted cyphered is not original massage but ", message2) + } +} + +func TestVerifyDecryption(t *testing.T) { + tkh := GetThresholdKeyGenerator(10, 2, 2, rand.Reader) + tpks, err := tkh.Generate() + + pk := &tpks[0].ThresholdKey + if err != nil { + t.Error(err) + return + } + expt := b(101) + cypher, err := tpks[0].Encrypt(expt, rand.Reader) + if err != nil { + t.Error(err) + return + } + pd1, err := tpks[0].DecryptAndProduceZNP(cypher.C, rand.Reader) + if err != nil { + t.Error(err) + return + } + pd2, err := tpks[1].DecryptAndProduceZNP(cypher.C, rand.Reader) + if err != nil { + t.Error(err) + return + } + pds := []*PartialDecryptionZKP{pd1, pd2} + if err != nil { + t.Error(err) + return + } + + if err = pk.VerifyDecryption(cypher.C, b(101), pds); err != nil { + t.Error(err) + } + if err = pk.VerifyDecryption(cypher.C, b(100), pds); err == nil { + t.Error(err) + } + if err = pk.VerifyDecryption(new(big.Int).Add(b(1), cypher.C), b(101), pds); err == nil { + t.Error(err) + } + +} diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..f3d9572 --- /dev/null +++ b/utils.go @@ -0,0 +1,28 @@ +package paillier + +import ( + "crypto/rand" + "io" + "math/big" +) + +var ZERO = big.NewInt(0) +var ONE = big.NewInt(1) +var TWO = big.NewInt(2) +var FOUR = big.NewInt(4) + +// Generate a random element in the group of all the elements in Z/nZ that +// has a multiplicative inverse. +func GetRandomNumberInMultiplicativeGroup(n *big.Int, random io.Reader) (*big.Int, error) { + r, err := rand.Int(random, n) + if err != nil { + return nil, err + } + zero := big.NewInt(0) + one := big.NewInt(1) + if zero.Cmp(r) == 0 || one.Cmp(new(big.Int).GCD(nil, nil, n, r)) != 0 { + return GetRandomNumberInMultiplicativeGroup(n, random) + } + return r, nil + +} diff --git a/utils_test.go b/utils_test.go new file mode 100644 index 0000000..43e4721 --- /dev/null +++ b/utils_test.go @@ -0,0 +1,55 @@ +package paillier + +import ( + "crypto/rand" + "math/big" + "testing" +) + +func b(i int) *big.Int { + return big.NewInt(int64(i)) +} + +func n(i *big.Int) int { + return int(i.Int64()) +} + +func areRelativelyPrime(a, b int) bool { + if b == 0 { + return a == 1 + } + return areRelativelyPrime(b, a%b) +} + +func TestConstants(t *testing.T) { + if n(ZERO) != 0 { + t.Fail() + } + + if n(ONE) != 1 { + t.Fail() + } + + if n(TWO) != 2 { + t.Fail() + } + + if n(FOUR) != 4 { + t.Fail() + } +} + +func TestGetRandomNumberInMultiplicativeGroup(t *testing.T) { + k := b(2 * 3 * 5 * 7) + for i := 0; i < 100; i++ { + m, err := GetRandomNumberInMultiplicativeGroup(k, rand.Reader) + if err != nil { + t.Error(err) + return + } + if !areRelativelyPrime(n(k), n(m)) { + t.Fail() + } + } + +}