paillier 1.0.0
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 +7 -0
- data/lib/paillier.rb +240 -0
- data/lib/paillier/keys.rb +44 -0
- data/lib/paillier/primes.rb +103 -0
- data/lib/paillier/signatures.rb +34 -0
- metadata +47 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f63285c7920e05242437975a03efa519083e7eed
|
4
|
+
data.tar.gz: dd8ac8fd45845371b140c96bcba512e4ece04607
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7951656a310e1b9a1ad402de9b765a51be1a690537cb24390527abfecf67389866921c35bc2b97174097fa65acabeb52686c5c14009af1c1f297bf950442728c
|
7
|
+
data.tar.gz: bbbc5f266370f5f55d87932d5744acf4c518208eae95237f10da1b41f73ed5cfebd048b20bf62ad7520a89b435fa46e5057d6ee7bef78e834c025fd73045ce6b
|
data/lib/paillier.rb
ADDED
@@ -0,0 +1,240 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'openssl'
|
4
|
+
require 'digest'
|
5
|
+
require 'bigdecimal'
|
6
|
+
require 'bigdecimal/math' # For 'log'
|
7
|
+
|
8
|
+
require_relative 'paillier/primes'
|
9
|
+
require_relative 'paillier/keys'
|
10
|
+
require_relative 'paillier/signatures'
|
11
|
+
|
12
|
+
module Paillier
|
13
|
+
|
14
|
+
KeySize = 2048
|
15
|
+
|
16
|
+
def self.gcd(u,v) # :nodoc:
|
17
|
+
while(v > 0)
|
18
|
+
u, v = v, u % v
|
19
|
+
end
|
20
|
+
return u
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.extendedGcd(a, b) # :nodoc:
|
24
|
+
# Make sure we're always using Bignums instead of ints
|
25
|
+
# The behavior of the division operator is very different for bignums,
|
26
|
+
# so it's important to use a consistent data type.
|
27
|
+
a = a.to_bn
|
28
|
+
b = b.to_bn
|
29
|
+
# Can't use .abs with bignums, implemented it manually
|
30
|
+
last_remainder = (a > 0) ? a : (-1 * a)
|
31
|
+
remainder = (b > 0) ? b : (-1 * b)
|
32
|
+
x, last_x, y, last_y = 0, 1, 1, 0
|
33
|
+
while( remainder != 0 )
|
34
|
+
t_last_remainder = remainder
|
35
|
+
quotient, remainder = last_remainder / remainder
|
36
|
+
last_remainder = t_last_remainder
|
37
|
+
x, last_x = last_x - quotient*x, x
|
38
|
+
y, last_y = last_y - quotient*y, y
|
39
|
+
end
|
40
|
+
|
41
|
+
return last_remainder, last_x * (a < 0 ? -1 : 1)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Multiplicative inverse of 'a' mod 'p'
|
45
|
+
# Returns 'b' such that a * b == 1 mod p
|
46
|
+
def self.modInv(a, p) # :nodoc:
|
47
|
+
if a == 0
|
48
|
+
raise ArgumentError, "0 has no inverse mod #{p}"
|
49
|
+
end
|
50
|
+
(g, x) = extendedGcd(a, p)
|
51
|
+
if( g != 1 )
|
52
|
+
raise ArgumentError, "#{a} has no inverse mod #{p}"
|
53
|
+
end
|
54
|
+
return x % p
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns modular exponent
|
58
|
+
# (base ** exponent) % modulus
|
59
|
+
# Handles very big numbers
|
60
|
+
def self.modPow(base, exponent, modulus) # :nodoc:
|
61
|
+
return base.to_bn.mod_exp(exponent, modulus)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Generates a new public private keypair
|
65
|
+
#
|
66
|
+
# Example:
|
67
|
+
# >> Paillier.generateKeypair(2048)
|
68
|
+
# => [#<Paillier::PrivateKey>, #<Paillier::PublicKey>]
|
69
|
+
#
|
70
|
+
# Arguments:
|
71
|
+
# bits: (Int)
|
72
|
+
def self.generateKeypair(bits)
|
73
|
+
p = Primes.generatePrime(bits/2)
|
74
|
+
q = Primes.generatePrime(bits/2)
|
75
|
+
n = p * q
|
76
|
+
|
77
|
+
# actual keygen grumble grumble
|
78
|
+
# all the libraries we found did it wrong and just made lambda = phi(n)
|
79
|
+
# and set mu to phi(n)^-1
|
80
|
+
# this is the actual spec for it
|
81
|
+
lambda_n = ( (p-1) * (q-1) ) / gcd( (p-1), (q-1) )
|
82
|
+
|
83
|
+
# this is technically a shortcut but not incorrect, the public key is unrelated to the private one
|
84
|
+
g = n + 1
|
85
|
+
|
86
|
+
# intermediary step
|
87
|
+
u = (g.to_bn.mod_exp(lambda_n, n * n)).to_i
|
88
|
+
# intermediary step
|
89
|
+
u_2 = (u - 1) / n
|
90
|
+
# now we have mu
|
91
|
+
mu = Paillier.modInv(u_2, n)
|
92
|
+
|
93
|
+
return PrivateKey.new(lambda_n, mu), PublicKey.new(n)
|
94
|
+
end
|
95
|
+
|
96
|
+
# For a Zero-Knowledge Proofs we need to demonstrate that we know 'r',
|
97
|
+
# therefore 'r' must be returned.
|
98
|
+
def self.rEncrypt(pub, plain) # :nodoc:
|
99
|
+
r = -1
|
100
|
+
while( true )
|
101
|
+
# We have to use BigMath here to make sure 'log' doesn't round
|
102
|
+
# to infinity and throw an exception
|
103
|
+
big_n = BigDecimal.new(pub.n)
|
104
|
+
r = Primes.generateCoprime(BigMath.log(big_n, 2).round, pub.n)
|
105
|
+
if( r > 0 and r < pub.n )
|
106
|
+
break
|
107
|
+
end
|
108
|
+
end
|
109
|
+
# We want to run: x = ((r ** pub.n) % pub.n_sq)
|
110
|
+
# But the numbers are too big, so we'll use openssl
|
111
|
+
x = r.to_bn.mod_exp(pub.n, pub.n_sq)
|
112
|
+
# We want to run: cipher = (((g ** plain) % pub.n_sq) * x) % pub.n_sq
|
113
|
+
# But similarly the math is real slow and we'll use openssl
|
114
|
+
cipher = (pub.g.to_bn.mod_exp(plain, pub.n_sq)).mod_mul(x, pub.n_sq)
|
115
|
+
return r, cipher
|
116
|
+
end
|
117
|
+
|
118
|
+
# Encrypts a message with the provided public key
|
119
|
+
#
|
120
|
+
# Example:
|
121
|
+
# >> Paillier.encrypt(publicKey, 3)
|
122
|
+
# => #<OpenSSL::BN:cyphertext>
|
123
|
+
#
|
124
|
+
# Arguments:
|
125
|
+
# publicKey: (Paillier::PublicKey)
|
126
|
+
# plaintext: (Int)
|
127
|
+
def self.encrypt(publicKey, plaintext)
|
128
|
+
return rEncrypt(publicKey, plaintext)[1]
|
129
|
+
end
|
130
|
+
|
131
|
+
# Adds one encrypted int to another
|
132
|
+
#
|
133
|
+
# Example:
|
134
|
+
# >> Paillier.eAdd(publicKey, cx, cy)
|
135
|
+
# => #<OpenSSL::BN::cyphertext>
|
136
|
+
#
|
137
|
+
# Arguments:
|
138
|
+
# publicKey: (Paillier::PublicKey)
|
139
|
+
# a: (Int)
|
140
|
+
# b: (Int)
|
141
|
+
def self.eAdd(publicKey, a, b)
|
142
|
+
return a.to_bn.mod_mul(b, publicKey.n_sq)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Adds a plaintext constant 'n' to an encrypted int
|
146
|
+
#
|
147
|
+
# Example:
|
148
|
+
# >> Paillier.eAddConst(publicKey, cyphertext, 3)
|
149
|
+
# => #<OpenSSL::BN::cyphertext>
|
150
|
+
#
|
151
|
+
# Arguments:
|
152
|
+
# publicKey: (Paillier::PublicKey)
|
153
|
+
# a: (Int)
|
154
|
+
# b: (Int)
|
155
|
+
def self.eAddConst(publicKey, a, n)
|
156
|
+
return a.to_bn.mod_mul(modPow(publicKey.g, n, publicKey.n_sq), publicKey.n_sq)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Multiplies an encrypted int by a constant
|
160
|
+
#
|
161
|
+
# Example:
|
162
|
+
# >> Paillier.eMulConst(publicKey, cyphertext, 2)
|
163
|
+
# => #<OpenSSL::BN::cyphertext>
|
164
|
+
#
|
165
|
+
# Arguments:
|
166
|
+
# publicKey: (Paillier::PublicKey)
|
167
|
+
def self.eMulConst(publicKey, a, n)
|
168
|
+
return modPow(a, n, publicKey.n_sq)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Decrypts a message, returning plaintext
|
172
|
+
#
|
173
|
+
# Example:
|
174
|
+
# >> Paillier.decrypt(priv, pub, Paillier.encrypt(priv, pub, 3))
|
175
|
+
# => 3
|
176
|
+
#
|
177
|
+
# Arguments:
|
178
|
+
# privKey: (Paillier::PrivateKey)
|
179
|
+
# pubKey: (Paillier::PublicKey)
|
180
|
+
# ciphertext: (Int)
|
181
|
+
def self.decrypt(privKey, pubKey, ciphertext)
|
182
|
+
# We want to run: x = ((cipher ** priv.l) % pub.n_sq) - 1
|
183
|
+
# But the numbers are too big, so we'll use openssl
|
184
|
+
x = ciphertext.to_bn.mod_exp(privKey.l, pubKey.n_sq) - 1
|
185
|
+
plain = (x.to_i / pubKey.n.to_i).to_bn.mod_mul(privKey.m, pubKey.n)
|
186
|
+
return plain
|
187
|
+
end
|
188
|
+
|
189
|
+
# Returns a detached signature for any message
|
190
|
+
#
|
191
|
+
# Example:
|
192
|
+
# >> Paillier.sign(priv, pub, 3)
|
193
|
+
# => #<Paillier::Signature>
|
194
|
+
#
|
195
|
+
# Arguments:
|
196
|
+
# priv: (Paillier::PrivateKey)
|
197
|
+
# pub: (Paillier::PublicKey)
|
198
|
+
# data: (Int)
|
199
|
+
def self.sign(priv, pub, data)
|
200
|
+
hashData = hash(data)
|
201
|
+
# L(u) = (u-1)/n
|
202
|
+
numerators1 = ((hashData.to_bn.mod_exp(priv.l, pub.n_sq) - 1) / pub.n.to_bn)[0]
|
203
|
+
denominators1 = ((pub.g.to_bn.mod_exp(priv.l, pub.n_sq) - 1) / pub.n.to_bn)[0]
|
204
|
+
#s1 = ((numerators1[0] / denominators1[0]))[0] % pub.n
|
205
|
+
inverse_denom = Paillier.modInv(denominators1.to_i, pub.n)
|
206
|
+
s1 = numerators1.to_bn.mod_mul(inverse_denom, pub.n)
|
207
|
+
|
208
|
+
inverse_n = Paillier.modInv(pub.n, priv.l)
|
209
|
+
inverse_g = Paillier.modInv(pub.g.to_bn.mod_exp(s1.to_bn, pub.n).to_i, pub.n)
|
210
|
+
s2 = (hashData * inverse_g).to_bn.mod_exp(inverse_n, pub.n)
|
211
|
+
|
212
|
+
return Signature.new(s1, s2)
|
213
|
+
end
|
214
|
+
|
215
|
+
def self.hash(message) # :nodoc:
|
216
|
+
return Digest::SHA256.hexdigest(message.to_s).to_i(16)
|
217
|
+
end
|
218
|
+
|
219
|
+
# Validates the signature for a given message
|
220
|
+
# Returns true if signature is good, false otherwise
|
221
|
+
#
|
222
|
+
# Example:
|
223
|
+
# >> Paillier.validSignature(pub, 3, Paillier.sign(priv, pub, 3))
|
224
|
+
# => true
|
225
|
+
#
|
226
|
+
# Arguments:
|
227
|
+
# pub: (Paillier::PublicKey)
|
228
|
+
# message: (Int)
|
229
|
+
# sig: (Paillier::Signature)
|
230
|
+
def self.validSignature?(pub, message, sig)
|
231
|
+
hash = Digest::SHA256.hexdigest(message.to_s).to_i(16)
|
232
|
+
# We want to run (g ** s1) * (s2 ** n) % (n**2)
|
233
|
+
# But all those numbers are huge, so we approach it in stages
|
234
|
+
a = pub.g.to_bn.mod_exp(sig.s1, pub.n_sq)
|
235
|
+
b = sig.s2.to_bn.mod_exp(pub.n, pub.n_sq)
|
236
|
+
sighash = a.mod_mul(b, pub.n_sq)
|
237
|
+
return (hash == sighash)
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Paillier
|
2
|
+
class PrivateKey
|
3
|
+
attr_reader :l, :m # :nodoc:
|
4
|
+
|
5
|
+
def initialize(l,m) # :nodoc:
|
6
|
+
@l = l
|
7
|
+
@m = m
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class PublicKey
|
12
|
+
attr_reader :n, :n_sq, :g # :nodoc:
|
13
|
+
|
14
|
+
def initialize(n) # :nodoc:
|
15
|
+
@n = n
|
16
|
+
@n_sq = n * n
|
17
|
+
@g = n+1
|
18
|
+
end
|
19
|
+
|
20
|
+
# Serialize a public key to string form
|
21
|
+
#
|
22
|
+
# Example:
|
23
|
+
# >> priv, pub = Paillier.generateKeypair(2048)
|
24
|
+
# >> pub.to_s
|
25
|
+
# => "110107191408889682017277609474037601699496910..."
|
26
|
+
#
|
27
|
+
def to_s
|
28
|
+
return "#{@n}"
|
29
|
+
end
|
30
|
+
|
31
|
+
# De-serialize a public key string back into object form
|
32
|
+
#
|
33
|
+
# Example:
|
34
|
+
# >> s = pub.to_s
|
35
|
+
# >> newPub = Paillier::PublicKey.from_s(s)
|
36
|
+
# => #<Paillier::PublicKey>
|
37
|
+
#
|
38
|
+
# Arguments:
|
39
|
+
# string (String)
|
40
|
+
def PublicKey.from_s(string)
|
41
|
+
return PublicKey.new(string.to_i)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
module Paillier
|
5
|
+
module Primes # :nodoc:
|
6
|
+
|
7
|
+
SmallPrimes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
|
8
|
+
|
9
|
+
def self.bitLength(int)
|
10
|
+
return int.to_s(2).length
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.defaultK(bits)
|
14
|
+
double = bits * 2
|
15
|
+
return (40 > double) ? 40 : double
|
16
|
+
end
|
17
|
+
|
18
|
+
# This is based on the Wikipedia article on the Fermat primality test
|
19
|
+
# returns true if probably prime, false if definitely composite
|
20
|
+
def self.probabilisticPrimeTest(target, k)
|
21
|
+
for _ in (1 .. k)
|
22
|
+
a = rand(2 .. (target-2))
|
23
|
+
# We want to run "x = (a ** target-1) % target", but the values
|
24
|
+
# are huge. Instead we call out to openssl and do it with mod_exp
|
25
|
+
mod = a.to_bn.mod_exp(target-1, target)
|
26
|
+
if( mod != 1 )
|
27
|
+
return false # Def composite
|
28
|
+
end
|
29
|
+
end
|
30
|
+
return true # probs prime
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.isProbablyPrime?(possible, k=nil)
|
34
|
+
if( possible == 1 )
|
35
|
+
return true
|
36
|
+
end
|
37
|
+
if( k.nil? )
|
38
|
+
k = defaultK(bitLength(possible))
|
39
|
+
end
|
40
|
+
for i in SmallPrimes
|
41
|
+
if( possible == i )
|
42
|
+
return true
|
43
|
+
elsif( possible % i == 0 )
|
44
|
+
return false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
# This isn't a known prime number, so we'll check for primality
|
48
|
+
# probabilistically
|
49
|
+
return probabilisticPrimeTest(possible, k)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Expensive test to prove coprimality.
|
53
|
+
# If GCD == 1, the numbers are coprime
|
54
|
+
def self.isCoprime?(p, q)
|
55
|
+
while(q > 0)
|
56
|
+
p, q = q, p % q
|
57
|
+
end
|
58
|
+
return (p == 1)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Get a random prime of appropriate length
|
62
|
+
def self.generatePrime(bits, k=nil)
|
63
|
+
if( bits < 8 )
|
64
|
+
raise "Bits less than eight!"
|
65
|
+
end
|
66
|
+
if( k == nil )
|
67
|
+
k = defaultK(bits)
|
68
|
+
end
|
69
|
+
|
70
|
+
while( true )
|
71
|
+
lowerBound = (2 ** (bits-1) + 1)
|
72
|
+
size = ((2 ** bits) - lowerBound)
|
73
|
+
possible = (lowerBound + SecureRandom.random_number(size)) | 1
|
74
|
+
if isProbablyPrime?(possible, k)
|
75
|
+
return possible
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.generateCoprime(bits, coprime_to)
|
81
|
+
if( bits < 8 )
|
82
|
+
raise "Bits less than eight!"
|
83
|
+
end
|
84
|
+
|
85
|
+
# If we find a number not coprome to n then finding `p` and `q` is trivial.
|
86
|
+
# This will almost never happen for keys of reasonable size, so if
|
87
|
+
# `coprime_to` is big enough we won't bother running the expensive test.
|
88
|
+
no_test_needed = false
|
89
|
+
if( coprime_to > (2 ** 1024) )
|
90
|
+
no_test_needed = true
|
91
|
+
end
|
92
|
+
|
93
|
+
while( true )
|
94
|
+
lowerBound = (2 ** (bits-1) + 1)
|
95
|
+
size = ((2 ** bits) - lowerBound)
|
96
|
+
possible = (lowerBound + SecureRandom.random_number(size)) | 1
|
97
|
+
if no_test_needed or isCoprime?(possible, coprime_to)
|
98
|
+
return possible
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Paillier
|
2
|
+
class Signature
|
3
|
+
attr_reader :s1, :s2 # :nodoc:
|
4
|
+
|
5
|
+
def initialize(s1, s2) # :nodoc:
|
6
|
+
@s1 = s1.to_i
|
7
|
+
@s2 = s2.to_i
|
8
|
+
end
|
9
|
+
|
10
|
+
# Serialize a signature to string form
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
# >> sig = Paillier.sign(priv, pub, 3)
|
14
|
+
# >> sig.to_s
|
15
|
+
# => "127609169397718360449546194929999128..."
|
16
|
+
def to_s()
|
17
|
+
return "#{s1},#{s2}"
|
18
|
+
end
|
19
|
+
|
20
|
+
# De-serialize a signature string to object form
|
21
|
+
#
|
22
|
+
# Example:
|
23
|
+
# >> s = sig.to_s
|
24
|
+
# >> newSig = Paillier::Signature.from_s(s)
|
25
|
+
# => #<Paillier::Signature>
|
26
|
+
#
|
27
|
+
# Arguments:
|
28
|
+
# string (String)
|
29
|
+
def Signature.from_s(string)
|
30
|
+
(s1, s2) = string.split(",")
|
31
|
+
return Signature.new(s1, s2)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: paillier
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Daylighting Society
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-12-18 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: An implementation of Paillier homomorphic addition public key system
|
14
|
+
email: paillier@daylightingsociety.org
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- lib/paillier.rb
|
20
|
+
- lib/paillier/keys.rb
|
21
|
+
- lib/paillier/primes.rb
|
22
|
+
- lib/paillier/signatures.rb
|
23
|
+
homepage: https://paillier.daylightingsociety.org
|
24
|
+
licenses:
|
25
|
+
- LGPL-3.0
|
26
|
+
metadata: {}
|
27
|
+
post_install_message:
|
28
|
+
rdoc_options: []
|
29
|
+
require_paths:
|
30
|
+
- lib
|
31
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0'
|
36
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
requirements: []
|
42
|
+
rubyforge_project:
|
43
|
+
rubygems_version: 2.6.4
|
44
|
+
signing_key:
|
45
|
+
specification_version: 4
|
46
|
+
summary: Paillier Homomorphic Cryptosystem
|
47
|
+
test_files: []
|