paillier 1.0.0 → 1.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/lib/paillier.rb +42 -11
- data/lib/paillier/keys.rb +25 -0
- data/lib/paillier/primes.rb +29 -29
- data/lib/paillier/zkp.rb +311 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 784166f6728fa330be600b47c2be1a643ff20471b6029723083a94806681a93a
|
4
|
+
data.tar.gz: '05960d00f769c0be21772270addb3a9f247b925621b2ebc7d78d7a86e8aac5fc'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9288c168ac05ed5584d6c7878ec248b0dea02b0c3c9bd866c810c5f4a962677fc65a6338573835eb6e4e651fdb39bb4239d561f7600ec6898e3b1adfae55dff6
|
7
|
+
data.tar.gz: 84c1f5a371d5ba2e6be59c3193b9a1814882cff4a843549f5e0d10842869c2b074e1402f4fe359a71428e2d49ed07545cc92c5ad8dd218f9df04083d1d2b6cae
|
data/lib/paillier.rb
CHANGED
@@ -8,11 +8,10 @@ require 'bigdecimal/math' # For 'log'
|
|
8
8
|
require_relative 'paillier/primes'
|
9
9
|
require_relative 'paillier/keys'
|
10
10
|
require_relative 'paillier/signatures'
|
11
|
+
require_relative 'paillier/zkp'
|
11
12
|
|
12
13
|
module Paillier
|
13
14
|
|
14
|
-
KeySize = 2048
|
15
|
-
|
16
15
|
def self.gcd(u,v) # :nodoc:
|
17
16
|
while(v > 0)
|
18
17
|
u, v = v, u % v
|
@@ -100,7 +99,7 @@ module Paillier
|
|
100
99
|
while( true )
|
101
100
|
# We have to use BigMath here to make sure 'log' doesn't round
|
102
101
|
# to infinity and throw an exception
|
103
|
-
big_n = BigDecimal
|
102
|
+
big_n = BigDecimal(pub.n)
|
104
103
|
r = Primes.generateCoprime(BigMath.log(big_n, 2).round, pub.n)
|
105
104
|
if( r > 0 and r < pub.n )
|
106
105
|
break
|
@@ -123,8 +122,11 @@ module Paillier
|
|
123
122
|
#
|
124
123
|
# Arguments:
|
125
124
|
# publicKey: (Paillier::PublicKey)
|
126
|
-
# plaintext: (Int)
|
125
|
+
# plaintext: (Int, OpenSSL::BN, String)
|
127
126
|
def self.encrypt(publicKey, plaintext)
|
127
|
+
if( plaintext.is_a?(String) )
|
128
|
+
plaintext = OpenSSL::BN.new(plaintext)
|
129
|
+
end
|
128
130
|
return rEncrypt(publicKey, plaintext)[1]
|
129
131
|
end
|
130
132
|
|
@@ -136,9 +138,15 @@ module Paillier
|
|
136
138
|
#
|
137
139
|
# Arguments:
|
138
140
|
# publicKey: (Paillier::PublicKey)
|
139
|
-
# a: (Int)
|
140
|
-
# b: (Int)
|
141
|
+
# a: (Int, OpenSSL::BN, String)
|
142
|
+
# b: (Int, OpenSSL::BN, String)
|
141
143
|
def self.eAdd(publicKey, a, b)
|
144
|
+
if( a.is_a?(String) )
|
145
|
+
a = OpenSSL::BN.new(a)
|
146
|
+
end
|
147
|
+
if( b.is_a?(String) )
|
148
|
+
b = OpenSSL::BN.new(b)
|
149
|
+
end
|
142
150
|
return a.to_bn.mod_mul(b, publicKey.n_sq)
|
143
151
|
end
|
144
152
|
|
@@ -150,9 +158,15 @@ module Paillier
|
|
150
158
|
#
|
151
159
|
# Arguments:
|
152
160
|
# publicKey: (Paillier::PublicKey)
|
153
|
-
# a: (Int)
|
154
|
-
#
|
161
|
+
# a: (Int, OpenSSL::BN, String)
|
162
|
+
# n: (Int, OpenSSL::BN, String)
|
155
163
|
def self.eAddConst(publicKey, a, n)
|
164
|
+
if( a.is_a?(String) )
|
165
|
+
a = OpenSSL::BN.new(a)
|
166
|
+
end
|
167
|
+
if( n.is_a?(String) )
|
168
|
+
n = OpenSSL::BN.new(n)
|
169
|
+
end
|
156
170
|
return a.to_bn.mod_mul(modPow(publicKey.g, n, publicKey.n_sq), publicKey.n_sq)
|
157
171
|
end
|
158
172
|
|
@@ -164,7 +178,15 @@ module Paillier
|
|
164
178
|
#
|
165
179
|
# Arguments:
|
166
180
|
# publicKey: (Paillier::PublicKey)
|
181
|
+
# a: (Int, OpenSSL::BN, String)
|
182
|
+
# n: (Int, OpenSSL::BN, String)
|
167
183
|
def self.eMulConst(publicKey, a, n)
|
184
|
+
if( a.is_a?(String) )
|
185
|
+
a = OpenSSL::BN.new(a)
|
186
|
+
end
|
187
|
+
if( n.is_a?(String) )
|
188
|
+
n = OpenSSL::BN.new(n)
|
189
|
+
end
|
168
190
|
return modPow(a, n, publicKey.n_sq)
|
169
191
|
end
|
170
192
|
|
@@ -177,8 +199,11 @@ module Paillier
|
|
177
199
|
# Arguments:
|
178
200
|
# privKey: (Paillier::PrivateKey)
|
179
201
|
# pubKey: (Paillier::PublicKey)
|
180
|
-
# ciphertext: (Int)
|
202
|
+
# ciphertext: (Int, OpenSSL::BN, String)
|
181
203
|
def self.decrypt(privKey, pubKey, ciphertext)
|
204
|
+
if( ciphertext.is_a?(String) )
|
205
|
+
ciphertext = OpenSSL::BN.new(ciphertext)
|
206
|
+
end
|
182
207
|
# We want to run: x = ((cipher ** priv.l) % pub.n_sq) - 1
|
183
208
|
# But the numbers are too big, so we'll use openssl
|
184
209
|
x = ciphertext.to_bn.mod_exp(privKey.l, pubKey.n_sq) - 1
|
@@ -195,8 +220,11 @@ module Paillier
|
|
195
220
|
# Arguments:
|
196
221
|
# priv: (Paillier::PrivateKey)
|
197
222
|
# pub: (Paillier::PublicKey)
|
198
|
-
# data: (Int)
|
223
|
+
# data: (Int, OpenSSL::BN, String)
|
199
224
|
def self.sign(priv, pub, data)
|
225
|
+
if( data.is_a?(String) )
|
226
|
+
data = OpenSSL::BN.new(data)
|
227
|
+
end
|
200
228
|
hashData = hash(data)
|
201
229
|
# L(u) = (u-1)/n
|
202
230
|
numerators1 = ((hashData.to_bn.mod_exp(priv.l, pub.n_sq) - 1) / pub.n.to_bn)[0]
|
@@ -225,9 +253,12 @@ module Paillier
|
|
225
253
|
#
|
226
254
|
# Arguments:
|
227
255
|
# pub: (Paillier::PublicKey)
|
228
|
-
# message: (Int)
|
256
|
+
# message: (Int, OpenSSL::BN, String)
|
229
257
|
# sig: (Paillier::Signature)
|
230
258
|
def self.validSignature?(pub, message, sig)
|
259
|
+
if( message.is_a?(String) )
|
260
|
+
message = OpenSSL::BN.new(message)
|
261
|
+
end
|
231
262
|
hash = Digest::SHA256.hexdigest(message.to_s).to_i(16)
|
232
263
|
# We want to run (g ** s1) * (s2 ** n) % (n**2)
|
233
264
|
# But all those numbers are huge, so we approach it in stages
|
data/lib/paillier/keys.rb
CHANGED
@@ -6,6 +6,31 @@ module Paillier
|
|
6
6
|
@l = l
|
7
7
|
@m = m
|
8
8
|
end
|
9
|
+
|
10
|
+
# Serialize a private key to string form
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
# >> priv, pub = Paillier.generateKeypair(2048)
|
14
|
+
# >> priv.to_s
|
15
|
+
# => "110107191408889682017277609474037601699496910..."
|
16
|
+
#
|
17
|
+
def to_s
|
18
|
+
return "#{@l},#{@m}"
|
19
|
+
end
|
20
|
+
|
21
|
+
# De-serialize a private key string back into object form
|
22
|
+
#
|
23
|
+
# Example:
|
24
|
+
# >> s = priv.to_s
|
25
|
+
# >> newPriv = Paillier::PrivateKey.from_s(s)
|
26
|
+
# => #<Paillier::PrivateKey>
|
27
|
+
#
|
28
|
+
# Arguments:
|
29
|
+
# string (String)
|
30
|
+
def PrivateKey.from_s(string)
|
31
|
+
l,m = string.split(",")
|
32
|
+
return PrivateKey.new(l.to_i, m.to_i)
|
33
|
+
end
|
9
34
|
end
|
10
35
|
|
11
36
|
class PublicKey
|
data/lib/paillier/primes.rb
CHANGED
@@ -10,33 +10,37 @@ module Paillier
|
|
10
10
|
return int.to_s(2).length
|
11
11
|
end
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
13
|
+
# This is an implementation of the Rabin-Miller primality test.
|
14
|
+
# Previous versions used Little Fermat, but that is not effective
|
15
|
+
# in all cases; specifically, it can be thwarted by Carmichael
|
16
|
+
# numbers. We use 50 rounds as the default, in order to get a certainty
|
17
|
+
# of 2^-100 that we have found a prime. This implementation is adapted
|
18
|
+
# from https://rosettacode.org/wiki/Miller-Rabin_primality_test#Ruby
|
19
|
+
def self.probabilisticPrimeTest(target, k=50)
|
20
|
+
d = target-1
|
21
|
+
s = 0
|
22
|
+
while d % 2 == 0
|
23
|
+
d /= 2
|
24
|
+
s += 1
|
25
|
+
end
|
26
|
+
k.times do
|
27
|
+
a = 2 + rand(target-4)
|
28
|
+
x = a.to_bn.mod_exp(d, target)
|
29
|
+
next if x == 1 || x == target-1
|
30
|
+
(s - 1).times do
|
31
|
+
x = x.to_bn.mod_exp(2, target)
|
32
|
+
return false if x == 1
|
33
|
+
break if x == target - 1
|
34
|
+
end
|
35
|
+
return false if x != target-1
|
36
|
+
end
|
37
|
+
return true # probs prime
|
38
|
+
end
|
32
39
|
|
33
|
-
def self.isProbablyPrime?(possible, k=
|
40
|
+
def self.isProbablyPrime?(possible, k=50)
|
34
41
|
if( possible == 1 )
|
35
42
|
return true
|
36
43
|
end
|
37
|
-
if( k.nil? )
|
38
|
-
k = defaultK(bitLength(possible))
|
39
|
-
end
|
40
44
|
for i in SmallPrimes
|
41
45
|
if( possible == i )
|
42
46
|
return true
|
@@ -59,14 +63,10 @@ module Paillier
|
|
59
63
|
end
|
60
64
|
|
61
65
|
# Get a random prime of appropriate length
|
62
|
-
def self.generatePrime(bits, k=
|
66
|
+
def self.generatePrime(bits, k=50)
|
63
67
|
if( bits < 8 )
|
64
68
|
raise "Bits less than eight!"
|
65
69
|
end
|
66
|
-
if( k == nil )
|
67
|
-
k = defaultK(bits)
|
68
|
-
end
|
69
|
-
|
70
70
|
while( true )
|
71
71
|
lowerBound = (2 ** (bits-1) + 1)
|
72
72
|
size = ((2 ** bits) - lowerBound)
|
@@ -82,7 +82,7 @@ module Paillier
|
|
82
82
|
raise "Bits less than eight!"
|
83
83
|
end
|
84
84
|
|
85
|
-
# If we find a number not
|
85
|
+
# If we find a number not coprime to n then finding `p` and `q` is trivial.
|
86
86
|
# This will almost never happen for keys of reasonable size, so if
|
87
87
|
# `coprime_to` is big enough we won't bother running the expensive test.
|
88
88
|
no_test_needed = false
|
data/lib/paillier/zkp.rb
ADDED
@@ -0,0 +1,311 @@
|
|
1
|
+
|
2
|
+
# needed for bignums
|
3
|
+
require 'set'
|
4
|
+
require 'openssl'
|
5
|
+
require 'securerandom'
|
6
|
+
require_relative 'primes'
|
7
|
+
|
8
|
+
BitStringLength = 256 #:nodoc:
|
9
|
+
|
10
|
+
module Paillier
|
11
|
+
|
12
|
+
module ZKP
|
13
|
+
|
14
|
+
# The ZKP class is used for performing zero-knowledge content proofs.
|
15
|
+
# Initialization of a ZKP object generates a commitment that the user can send along with a ciphertext to another party.
|
16
|
+
# That party can then use the commitment to confirm that the ciphertext exists in the set of pre-determined valid messages.
|
17
|
+
class ZKP
|
18
|
+
|
19
|
+
# Commitment generated by the ZKP object on initialization, used in ZKPVerify? function
|
20
|
+
attr_reader :commitment
|
21
|
+
|
22
|
+
# Ciphertext generated by the ZKP object; accessible using both myZKP.ciphertext and myZKP.cyphertext
|
23
|
+
attr_reader :ciphertext, :cyphertext
|
24
|
+
|
25
|
+
|
26
|
+
# Constructor function for a ZKP object. On initialization, generates a ZKPCommit object for use in verification
|
27
|
+
#
|
28
|
+
# Example:
|
29
|
+
# >> myZKP = Paillier::ZKP::ZKP.new(key, 65, [23, 38, 52, 65, 77, 94])
|
30
|
+
# => [#<@p = plaintext>, #<@pubkey = <key>>, #<@ciphertext = <ciphertext>>, #<@cyphertext = <ciphertext>>, #<@commitment = <commitment>>]
|
31
|
+
#
|
32
|
+
# Arguments:
|
33
|
+
# public_key: The key to be used for the encryption (Paillier::PublicKey)
|
34
|
+
# plaintext: The message to be encrypted (Integer)
|
35
|
+
# valid_messages: The set of valid messages for encryption (Array)
|
36
|
+
#
|
37
|
+
# NOTE: the order of valid_messages should be the same for both prover and verifier
|
38
|
+
def initialize(public_key, plaintext, valid_messages)
|
39
|
+
@p = plaintext
|
40
|
+
@pubkey = public_key
|
41
|
+
@r, @ciphertext = Paillier.rEncrypt(@pubkey, @p)
|
42
|
+
@cyphertext = @ciphertext
|
43
|
+
@a_s = Array.new()
|
44
|
+
@e_s = Array.new()
|
45
|
+
@z_s = Array.new()
|
46
|
+
@power = nil
|
47
|
+
@commitment = nil
|
48
|
+
|
49
|
+
# we generate a random value omega such that omega is coprime to n
|
50
|
+
while( true )
|
51
|
+
big_n = BigDecimal( @pubkey.n )
|
52
|
+
@omega = Primes.generateCoprime(BigMath.log(big_n, 2).round, @pubkey.n)
|
53
|
+
if( @omega > 0 and @omega < @pubkey.n)
|
54
|
+
break
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# a_p = omega ^ n (mod n^2); 'a' calculation for the plaintext
|
59
|
+
a_p = @omega.to_bn.mod_exp( @pubkey.n, @pubkey.n_sq)
|
60
|
+
|
61
|
+
valid_messages.each_with_index do |m_k, k|
|
62
|
+
# g_mk = g ^ m_k (mod n^2)
|
63
|
+
g_mk = @pubkey.g.to_bn.mod_exp(m_k.to_bn, @pubkey.n_sq)
|
64
|
+
|
65
|
+
# u_k = c / g_mk (mod n^2)
|
66
|
+
# NOTE: this is modular algebra, so u_k = c * invmod(g_mk) (mod n^2)
|
67
|
+
u_k = @ciphertext.to_bn.mod_mul(Paillier.modInv(g_mk, @pubkey.n_sq), @pubkey.n_sq)
|
68
|
+
unless ( @p == m_k )
|
69
|
+
# randomly generate a coprime of n for z_k
|
70
|
+
while( true )
|
71
|
+
big_n = BigDecimal(@pubkey.n)
|
72
|
+
z_k = Primes::generateCoprime(BigMath.log(big_n, 2).round, @pubkey.n)
|
73
|
+
if( z_k > 0 and z_k < @pubkey.n )
|
74
|
+
break
|
75
|
+
end
|
76
|
+
end
|
77
|
+
@z_s.push(z_k.to_bn)
|
78
|
+
|
79
|
+
# generate a random e < 2^BitStringLength
|
80
|
+
e_k = SecureRandom.random_number((2 ** 256) - 1)
|
81
|
+
@e_s.push(e_k.to_bn)
|
82
|
+
|
83
|
+
# calculate z_k
|
84
|
+
# z_nth = z^n (mod n^2)
|
85
|
+
z_nth = z_k.to_bn.mod_exp(@pubkey.n, @pubkey.n_sq)
|
86
|
+
# u_eth = u^e_k (mod n^2)
|
87
|
+
u_eth = u_k.to_bn.mod_exp(e_k.to_bn, @pubkey.n_sq)
|
88
|
+
# a_k = z_nth / u_eth (mod n^2) = z_nth * invmod(u_eth) (mod n^2)
|
89
|
+
a_k = z_nth.to_bn.mod_mul( Paillier.modInv(u_eth, @pubkey.n_sq), @pubkey.n_sq )
|
90
|
+
|
91
|
+
@a_s.push(a_k.to_bn)
|
92
|
+
else
|
93
|
+
@power = k
|
94
|
+
@a_s.push(a_p.to_bn)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
# attempting to craft a ZKP object with an invalid message throws exception
|
98
|
+
if(@power == nil)
|
99
|
+
raise ArgumentError, "Input message does not exist in array of valid messages.", caller
|
100
|
+
end
|
101
|
+
# we have now generated all a_s, and all e_s and z_s, save for e_p and z_p
|
102
|
+
# to generate e_p and z_p, we need to generate the challenge string, hash(a_s)
|
103
|
+
# to make the proof non-interactive
|
104
|
+
sha256 = OpenSSL::Digest::SHA256.new
|
105
|
+
for a_k in @a_s do
|
106
|
+
sha256 << a_k.to_s
|
107
|
+
end
|
108
|
+
challenge_string = sha256.digest.unpack('H*')[0].to_i(16)
|
109
|
+
|
110
|
+
# now that we have the "challenge string", we calculate e_p and z_p
|
111
|
+
e_sum = 0.to_bn
|
112
|
+
big_mod = 2.to_bn
|
113
|
+
big_mod = big_mod ** 256
|
114
|
+
for e_k in @e_s do
|
115
|
+
e_sum = (e_sum + e_k).to_bn % big_mod
|
116
|
+
end
|
117
|
+
# the sum of all e_s must add up to the challenge_string
|
118
|
+
e_p = (OpenSSL::BN.new(challenge_string) - e_sum).to_bn % big_mod
|
119
|
+
# r_ep = r ^ e_p (mod n)
|
120
|
+
r_ep = @r.to_bn.mod_exp(e_p.to_bn, @pubkey.n)
|
121
|
+
# z_p = omega * r^e_p (mod n)
|
122
|
+
z_p = @omega.to_bn.mod_mul(r_ep.to_bn, @pubkey.n)
|
123
|
+
|
124
|
+
@e_s.insert(@power, e_p.to_bn)
|
125
|
+
@z_s.insert(@power, z_p.to_bn)
|
126
|
+
@commitment = ZKPCommit.new(@a_s, @e_s, @z_s)
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Wrapper function that creates a ZKP object for the user.
|
132
|
+
# Instead of needing to call Paillier::ZKP::ZKP.new(args), the user calls Paillier::ZKP.new(args).
|
133
|
+
#
|
134
|
+
# Example:
|
135
|
+
# >> myZKP = Paillier::ZKP.new(key, 65, [23, 38, 52, 65, 77, 94])
|
136
|
+
# => [#<@p = plaintext>, #<@pubkey = <key>>, #<@ciphertext = <ciphertext>>, #<@cyphertext = <ciphertext>>, #<@commitment = <commitment>>]
|
137
|
+
#
|
138
|
+
# Arguments:
|
139
|
+
# public_key: The key to be used for the encryption (Paillier::PublicKey)
|
140
|
+
# plaintext: The message to be encrypted (Integer)
|
141
|
+
# valid_messages: The set of valid messages for encryption (Array)
|
142
|
+
#
|
143
|
+
# NOTE: the order of valid_messages should be the same for both prover and verifier
|
144
|
+
def self.new(pubkey, message, valid_messages)
|
145
|
+
return Paillier::ZKP::ZKP.new(pubkey, message, valid_messages)
|
146
|
+
end
|
147
|
+
|
148
|
+
# Function that verifies whether a ciphertext is within the set of valid messages.
|
149
|
+
#
|
150
|
+
# Example:
|
151
|
+
#
|
152
|
+
# >> Paillier::ZKP.verifyZKP?(key, ciphertext, [23, 38, 65, 77, 94], commitment)
|
153
|
+
# => true
|
154
|
+
#
|
155
|
+
# Arguments:
|
156
|
+
# pubkey: The key used for the encryption (Paillier::PublicKey)
|
157
|
+
# ciphertext: The ciphertext generated using the public key (OpenSSL::BN)
|
158
|
+
# valid_messages: The set of valid messages for encryption (Array)
|
159
|
+
# commitment: The commitment generated by the prover (Paillier::ZKP::ZKPCommit)
|
160
|
+
#
|
161
|
+
# NOTE: the order of valid_messages should be the same for both prover and verifier
|
162
|
+
def self.verifyZKP?(pubkey, ciphertext, valid_messages, commitment)
|
163
|
+
u_s = Array.new
|
164
|
+
for m_k in valid_messages do
|
165
|
+
# g_mk = g ^ m_k (mod n^2)
|
166
|
+
g_mk = pubkey.g.to_bn.mod_exp(m_k.to_bn, pubkey.n_sq)
|
167
|
+
# u_k = c / g_mk (mod n^2) = c * invmod(g_mk) (mod n^2)
|
168
|
+
u_k = OpenSSL::BN.new(ciphertext).mod_mul( Paillier.modInv(g_mk, pubkey.n_sq), pubkey.n_sq )
|
169
|
+
u_s.push(u_k)
|
170
|
+
end
|
171
|
+
|
172
|
+
# calculate the challenge_string
|
173
|
+
sha256 = OpenSSL::Digest::SHA256.new
|
174
|
+
for a_k in commitment.a_s do
|
175
|
+
sha256 << a_k.to_s
|
176
|
+
end
|
177
|
+
challenge_string = sha256.digest.unpack('H*')[0].to_i(16)
|
178
|
+
|
179
|
+
e_sum = 0.to_bn
|
180
|
+
big_mod = 2.to_bn
|
181
|
+
big_mod = big_mod ** 256
|
182
|
+
for e_k in commitment.e_s do
|
183
|
+
e_sum = (e_sum + e_k.to_bn) % big_mod
|
184
|
+
end
|
185
|
+
|
186
|
+
# first we check that the sum matches correctly
|
187
|
+
unless e_sum == OpenSSL::BN.new(challenge_string)
|
188
|
+
return false
|
189
|
+
end
|
190
|
+
# then we check that z_k^n = a_k * (u_k^e_k) (mod n^2)
|
191
|
+
for i in (0 .. (commitment.z_s.size - 1)) do
|
192
|
+
a_k = commitment.a_s[i]
|
193
|
+
e_k = commitment.e_s[i]
|
194
|
+
u_k = u_s[i]
|
195
|
+
z_k = commitment.z_s[i]
|
196
|
+
# left hand side
|
197
|
+
# z_kn = z_k ^ n (mod n^2)
|
198
|
+
z_kn = z_k.to_bn.mod_exp(pubkey.n, pubkey.n_sq)
|
199
|
+
# right hand side
|
200
|
+
# u_ke = u_k ^ e_k (mod n^2)
|
201
|
+
u_ke = u_k.to_bn.mod_exp(e_k, pubkey.n_sq)
|
202
|
+
# a_kue = a_k * u_ke (mod n^2)
|
203
|
+
a_kue = a_k.to_bn.mod_mul(u_ke, pubkey.n_sq)
|
204
|
+
|
205
|
+
# z_k ^ n ?= a_k * (u_k ^ e_k)
|
206
|
+
unless(z_kn == a_kue)
|
207
|
+
return false
|
208
|
+
end
|
209
|
+
end
|
210
|
+
# if it passes both tests, then we have validated the contents
|
211
|
+
return true
|
212
|
+
end
|
213
|
+
|
214
|
+
# Wrapper class used for containing the components of the ZKP commitment
|
215
|
+
class ZKPCommit
|
216
|
+
|
217
|
+
attr_reader :a_s, :e_s, :z_s #:nodoc:
|
218
|
+
|
219
|
+
def initialize(a_s, e_s, z_s) # :nodoc:
|
220
|
+
@a_s = a_s
|
221
|
+
@e_s = e_s
|
222
|
+
@z_s = z_s
|
223
|
+
end
|
224
|
+
|
225
|
+
# Serializes a commitment
|
226
|
+
#
|
227
|
+
# Example:
|
228
|
+
#
|
229
|
+
# >> myZKP = Paillier::ZKP.new(key, 65, [23, 38, 52, 65, 77, 94])
|
230
|
+
# => [#<@p = plaintext>, #<@pubkey = <key>>, #<@ciphertext = <ciphertext>>, #<@cyphertext = <ciphertext>>, #<@commitment = <commitment>>]
|
231
|
+
# >> myZKP.commitment.to_s
|
232
|
+
# => "<a1>,<a2>,<a3>,<a4>,<a5>,<a6>,;<e1>,<e2>,<e3>,<e4>,<e5>,;<z1>,<z2>,<z3>,<z4>,<z5>,"
|
233
|
+
def to_s()
|
234
|
+
a_s_string = @a_s.join(',')
|
235
|
+
e_s_string = @e_s.join(',')
|
236
|
+
z_s_string = @z_s.join(',')
|
237
|
+
return "#{a_s_string};#{e_s_string};#{z_s_string}"
|
238
|
+
end
|
239
|
+
|
240
|
+
# Deserializes a commitment
|
241
|
+
#
|
242
|
+
# Example:
|
243
|
+
#
|
244
|
+
# >> commit = Paillier::ZKP::ZKPCommit.from_s(commitment_string)
|
245
|
+
# => #<Paillier::ZKP::ZKPCommit: @a_s=[<a1>,<a2>, .. ,<an>], @e_s=[<e1>,<e2>, .. ,<en>], @z_s=[<z1>,<z2>, .. ,<zn>]>
|
246
|
+
#
|
247
|
+
# Arguments:
|
248
|
+
# commitment_string: Serialization of a commitment (String)
|
249
|
+
def ZKPCommit.from_s(string)
|
250
|
+
# these will hold the final result from string-parsing
|
251
|
+
a_s = Array.new
|
252
|
+
e_s = Array.new
|
253
|
+
z_s = Array.new
|
254
|
+
|
255
|
+
# separate at the semicolons
|
256
|
+
a_s_string, e_s_string, z_s_string = string.split(";")
|
257
|
+
|
258
|
+
# separate at the commas
|
259
|
+
a_s_strings = a_s_string.split(",")
|
260
|
+
e_s_strings = e_s_string.split(",")
|
261
|
+
z_s_strings = z_s_string.split(",")
|
262
|
+
|
263
|
+
# convert into arrays of bignums
|
264
|
+
for a in a_s_strings do
|
265
|
+
a_s.push(OpenSSL::BN.new(a))
|
266
|
+
end
|
267
|
+
for e in e_s_strings do
|
268
|
+
e_s.push(OpenSSL::BN.new(e))
|
269
|
+
end
|
270
|
+
for z in z_s_strings do
|
271
|
+
z_s.push(OpenSSL::BN.new(z))
|
272
|
+
end
|
273
|
+
|
274
|
+
# create the object with these arrays
|
275
|
+
return ZKPCommit.new(a_s, e_s, z_s)
|
276
|
+
end
|
277
|
+
|
278
|
+
# == operator overload to compare two ZKP commit objects
|
279
|
+
def ==(y) #:nodoc:
|
280
|
+
# if the array sizes don't match return false
|
281
|
+
if @a_s.size != y.a_s.size
|
282
|
+
return false
|
283
|
+
end
|
284
|
+
if @e_s.size != y.e_s.size
|
285
|
+
return false
|
286
|
+
end
|
287
|
+
if @z_s.size != y.z_s.size
|
288
|
+
return false
|
289
|
+
end
|
290
|
+
# if the corresponding elements in the arrays don't math return false
|
291
|
+
for i in (0 .. (@a_s.size - 1)) do
|
292
|
+
if(@a_s[i] != y.a_s[i])
|
293
|
+
return false
|
294
|
+
end
|
295
|
+
end
|
296
|
+
for i in (0 .. (@e_s.size - 1)) do
|
297
|
+
if(@e_s[i] != y.e_s[i])
|
298
|
+
return false
|
299
|
+
end
|
300
|
+
end
|
301
|
+
for i in (0 .. (@z_s.size - 1)) do
|
302
|
+
if(@z_s[i] != y.z_s[i])
|
303
|
+
return false
|
304
|
+
end
|
305
|
+
end
|
306
|
+
# else return true
|
307
|
+
return true
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paillier
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daylighting Society
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-11-10 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: An implementation of Paillier homomorphic addition public key system
|
14
14
|
email: paillier@daylightingsociety.org
|
@@ -20,6 +20,7 @@ files:
|
|
20
20
|
- lib/paillier/keys.rb
|
21
21
|
- lib/paillier/primes.rb
|
22
22
|
- lib/paillier/signatures.rb
|
23
|
+
- lib/paillier/zkp.rb
|
23
24
|
homepage: https://paillier.daylightingsociety.org
|
24
25
|
licenses:
|
25
26
|
- LGPL-3.0
|
@@ -39,8 +40,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
39
40
|
- !ruby/object:Gem::Version
|
40
41
|
version: '0'
|
41
42
|
requirements: []
|
42
|
-
|
43
|
-
rubygems_version: 2.6.4
|
43
|
+
rubygems_version: 3.0.3
|
44
44
|
signing_key:
|
45
45
|
specification_version: 4
|
46
46
|
summary: Paillier Homomorphic Cryptosystem
|