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.
@@ -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: []