paillier 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|