Skip to content

Commit 7315120

Browse files
author
RadicalRafi
committed
paillier cryptosystem
1 parent bda6c7f commit 7315120

File tree

4 files changed

+229
-0
lines changed

4 files changed

+229
-0
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
## Gaillier
2+
3+
Gaillier is a Golang implementation of [Paillier Cryptosystem](https://www.wikiwand.com/en/Paillier_cryptosystem)
4+

gaillier/gaillier.go

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
This package implements a Paillier cryptosystem
3+
4+
Provides primitives for Public & Private Key Generation / Encryption / Decryption
5+
Provides Functions to operate on the Cyphertext according to Paillier algorithm
6+
7+
@author: radicalrafi
8+
@license: Apache 2.0
9+
10+
*/
11+
12+
package gaillier
13+
14+
import (
15+
"crypto/rand"
16+
"errors"
17+
"io"
18+
"math/big"
19+
)
20+
21+
//Errors definition
22+
23+
/* The Paillier crypto system picks two keys p & q and denotes n = p*q
24+
Messages have to be in the ring Z/nZ (integers modulo n)
25+
Therefore a Message can't be bigger than n
26+
*/
27+
var ErrLongMessage = errors.New("Gaillier Error #1: Message is too long for The Public-Key Size \n Message should be smaller than Key size you choose")
28+
29+
//constants
30+
31+
var one = big.NewInt(1)
32+
33+
//Key structs
34+
35+
type PubKey struct {
36+
KeyLen int
37+
N *big.Int //n = p*q (where p & q are two primes)
38+
G *big.Int //g random integer in Z\*\n^2
39+
Nsq *big.Int //N^2
40+
}
41+
42+
type PrivKey struct {
43+
KeyLen int
44+
PubKey
45+
L *big.Int //lcm((p-1)*(q-1))
46+
U *big.Int //L^-1 modulo n mu = U = (L(g^L mod N^2)^-1)
47+
}
48+
49+
func GenerateKeyPair(random io.Reader, bits int) (*PubKey, *PrivKey, error) {
50+
51+
p, err := rand.Prime(random, bits/2)
52+
53+
if err != nil {
54+
return nil, nil, err
55+
}
56+
57+
q, err := rand.Prime(random, bits/2)
58+
59+
if err != nil {
60+
return nil, nil, err
61+
}
62+
63+
//N = p*q
64+
65+
n := new(big.Int).Mul(p, q)
66+
67+
nSq := new(big.Int).Mul(n, n)
68+
69+
g := new(big.Int).Add(n, one)
70+
71+
//p-1
72+
pMin := new(big.Int).Sub(p, one)
73+
//q-1
74+
qMin := new(big.Int).Sub(q, one)
75+
//(p-1)*(q-1)
76+
l := new(big.Int).Mul(pMin, qMin)
77+
//l^-1 mod n
78+
u := new(big.Int).ModInverse(l, n)
79+
pub := &PubKey{KeyLen: bits, N: n, Nsq: nSq, G: g}
80+
return pub, &PrivKey{PubKey: *pub, KeyLen: bits, L: l, U: u}, nil
81+
}
82+
83+
/*
84+
Encrypt :function to encrypt the message into a paillier cipher text
85+
using the following rule :
86+
cipher = g^m * r^n mod n^2
87+
* r is random integer such as 0 <= r <= n
88+
* m is the message
89+
*/
90+
func Encrypt(pubkey *PubKey, message []byte) ([]byte, error) {
91+
92+
r, err := rand.Prime(rand.Reader, pubkey.KeyLen)
93+
if err != nil {
94+
return nil, err
95+
}
96+
97+
m := new(big.Int).SetBytes(message)
98+
if pubkey.N.Cmp(m) < 1 {
99+
return nil, ErrLongMessage
100+
}
101+
//c = g^m * r^nmod n^2
102+
103+
//g^m
104+
gm := new(big.Int).Exp(pubkey.G, m, pubkey.Nsq)
105+
//r^n
106+
rn := new(big.Int).Exp(r, pubkey.N, pubkey.Nsq)
107+
//prod = g^m * r^n
108+
prod := new(big.Int).Mul(gm, rn)
109+
110+
c := new(big.Int).Mod(prod, pubkey.Nsq)
111+
112+
return c.Bytes(), nil
113+
}
114+
115+
/*
116+
Decrypts a given ciphertext following the rule:
117+
m = L(c^lambda mod n^2).mu mod n
118+
* lambda : L
119+
* mu : U
120+
121+
*/
122+
func Decrypt(privkey *PrivKey, cipher []byte) ([]byte, error) {
123+
124+
c := new(big.Int).SetBytes(cipher)
125+
126+
if privkey.Nsq.Cmp(c) < 1 {
127+
return nil, ErrLongMessage
128+
}
129+
130+
//c^l mod n^2
131+
a := new(big.Int).Exp(c, privkey.L, privkey.Nsq)
132+
133+
//L(x) = x-1 / n we compute L(a)
134+
l := new(big.Int).Div(new(big.Int).Sub(a, one), privkey.N)
135+
136+
//computing m
137+
m := new(big.Int).Mod(new(big.Int).Mul(l, privkey.U), privkey.N)
138+
139+
return m.Bytes(), nil
140+
141+
}

gaillier_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package main
2+
3+
import (
4+
"crypto/rand"
5+
"math/big"
6+
"testing"
7+
8+
"github.com/radicalrafi/gomorph/gaillier"
9+
)
10+
11+
func TestKeyGen(t *testing.T) {
12+
puba, priva, err1 := gaillier.GenerateKeyPair(rand.Reader, 1024)
13+
pubb, privb, err2 := gaillier.GenerateKeyPair(rand.Reader, 2048)
14+
pubc, privc, err3 := gaillier.GenerateKeyPair(rand.Reader, 4096)
15+
16+
if err1 != nil || err2 != nil || err3 != nil {
17+
t.Errorf("Error Generating Keypair :\n Size:1024 %v\nSize:2048 %v\nSize 4096 %v\n", err1, err2, err3)
18+
}
19+
if puba.KeyLen != 1024 || priva.KeyLen != 1024 {
20+
t.Errorf("Error generating correct keypair of size 1024 byte got %d want 1024", puba.KeyLen)
21+
}
22+
if pubb.KeyLen != 2048 || privb.KeyLen != 2048 {
23+
t.Errorf("Error generating correct keypair of size 1024 byte got %d want 2048", puba.KeyLen)
24+
}
25+
if pubc.KeyLen != 4096 || privc.KeyLen != 4096 {
26+
t.Errorf("Error generating correct keypair of size 1024 byte got %d want 4096", puba.KeyLen)
27+
}
28+
29+
}
30+
func TestEncryptDecrypt(t *testing.T) {
31+
32+
case1 := new(big.Int).SetInt64(9132)
33+
case2 := new(big.Int).SetInt64(1492)
34+
35+
pub, priv, err := gaillier.GenerateKeyPair(rand.Reader, 512)
36+
37+
if err != nil {
38+
t.Errorf("Error Generating Keypair")
39+
}
40+
encCase1, errCase1 := gaillier.Encrypt(pub, case1.Bytes())
41+
encCase2, errCase2 := gaillier.Encrypt(pub, case2.Bytes())
42+
43+
if errCase1 != nil || errCase2 != nil {
44+
t.Errorf("Error encrypting keypair %v \n %v", errCase1, errCase2)
45+
}
46+
47+
d1, errDec1 := gaillier.Decrypt(priv, encCase1)
48+
d2, errDec2 := gaillier.Decrypt(priv, encCase2)
49+
50+
decCase1 := new(big.Int).SetBytes(d1)
51+
decCase2 := new(big.Int).SetBytes(d2)
52+
if decCase1.Cmp(case1) != 0 || decCase2.Cmp(case2) != 0 {
53+
t.Errorf("Error Decrypting the message %v \n %v", errDec1, errDec2)
54+
}
55+
56+
}

run.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package main
2+
3+
import (
4+
"crypto/rand"
5+
"fmt"
6+
7+
"github.com/radicalrafi/gomorph/gaillier"
8+
)
9+
10+
func main() {
11+
12+
os := rand.Reader
13+
/*
14+
m, n, err := primes.GenPrimes(1024, rand.Reader)
15+
fmt.Println(m, n, err)
16+
17+
if m.ProbablyPrime(1000) == false {
18+
fmt.Println("m is not a prime")
19+
}
20+
fmt.Println(m.BitLen(), n.BitLen())
21+
*/
22+
a, b, err := gaillier.GenerateKeyPair(os, 1024)
23+
fmt.Println(a.G, a.N, b, err)
24+
h := []byte{'1', '2', '3'}
25+
kp, err := gaillier.Encrypt(a, h)
26+
deckp, err := gaillier.Decrypt(b, kp)
27+
fmt.Println(string(deckp), err)
28+
}

0 commit comments

Comments
 (0)