paillier 1.0.0

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