rsa-g 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 463d0dbc0338873b4d2aa01faab3eef89da7c99b
4
+ data.tar.gz: 2858960e94ac616e97bdd85763add76e7bc86b35
5
+ SHA512:
6
+ metadata.gz: e35f2fb15ea89dd07979a5a01ee665e51631e496952e640672ecbf700066a10541abca47a25dac1cacf26e9d77893e799bb1d3188c5aad8c989ecb31cc9cfb19
7
+ data.tar.gz: f1e482f1acabe397329ab2440e50d8ade9b20377c9ae5e26984400bf79257b9a5a0f8bb0310a4ba5de89bb46a599832c5c262eaf7225178d2cdd9c0a07c08e85
data/AUTHORS ADDED
@@ -0,0 +1 @@
1
+ Gabriel Vian
data/CREDITS ADDED
File without changes
data/README ADDED
@@ -0,0 +1 @@
1
+ README.md
data/UNLICENSE ADDED
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <http://unlicense.org/>
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
data/lib/rsa/key.rb ADDED
@@ -0,0 +1,54 @@
1
+ module RSA
2
+ ##
3
+ # An RSA public or private key.
4
+ #
5
+ # Refer to PKCS #1 v2.1, section 3, pp. 6-8.
6
+ #
7
+ # @see http://www.rsa.com/rsalabs/node.asp?id=2125
8
+ # @see http://en.wikipedia.org/wiki/Public-key_cryptography
9
+ class Key
10
+ ##
11
+ # The RSA modulus, a positive integer.
12
+ #
13
+ # @return [Integer]
14
+ attr_reader :modulus
15
+ alias_method :n, :modulus
16
+
17
+ ##
18
+ # The RSA public or private exponent, a positive integer.
19
+ #
20
+ # @return [Integer]
21
+ attr_reader :exponent
22
+ alias_method :e, :exponent
23
+ alias_method :d, :exponent
24
+
25
+ ##
26
+ # Initializes a new key.
27
+ #
28
+ # @param [Integer, #to_i] modulus
29
+ # @param [Integer, #to_i] exponent
30
+ # @param [Hash{Symbol => Object}] options
31
+ def initialize(modulus, exponent, options = {})
32
+ @modulus = modulus.to_i
33
+ @exponent = exponent.to_i
34
+ @options = options.dup
35
+ end
36
+
37
+ ##
38
+ # Returns `true` if this is a valid RSA key according to {RSA::PKCS1
39
+ # PKCS #1}.
40
+ #
41
+ # @return [Boolean]
42
+ def valid?
43
+ true # TODO: PKCS #1 v2.1, sections 3.1 and 3.2, pp. 6-7.
44
+ end
45
+
46
+ ##
47
+ # Returns a two-element array containing the modulus and exponent.
48
+ #
49
+ # @return [Array]
50
+ def to_a
51
+ [modulus, exponent]
52
+ end
53
+ end # class Key
54
+ end # module RSA
@@ -0,0 +1,241 @@
1
+ module RSA
2
+ ##
3
+ # An RSA key pair.
4
+ #
5
+ # Refer to PKCS #1 v2.1, section 3, pp. 6-8.
6
+ #
7
+ # @see http://www.rsa.com/rsalabs/node.asp?id=2125
8
+ # @see http://en.wikipedia.org/wiki/Public-key_cryptography
9
+ class KeyPair
10
+ ##
11
+ # The RSA private key.
12
+ #
13
+ # @return [Key]
14
+ attr_reader :private_key
15
+ alias_method :private, :private_key
16
+
17
+ ##
18
+ # The RSA public key.
19
+ #
20
+ # @return [Key]
21
+ attr_reader :public_key
22
+ alias_method :public, :public_key
23
+
24
+ ##
25
+ # Initializes a new key pair.
26
+ #
27
+ # @param [Key] private_key
28
+ # @param [Key] public_key
29
+ # @param [Hash{Symbol => Object}] options
30
+ def initialize(private_key, public_key, options = {})
31
+ @private_key = private_key
32
+ @public_key = public_key
33
+ @options = options.dup
34
+ end
35
+
36
+ ##
37
+ # Returns `true` if this key pair contains a private key.
38
+ #
39
+ # @return [Boolean]
40
+ def private_key?
41
+ !!private_key
42
+ end
43
+ alias_method :private?, :private_key? # for OpenSSL compatibility
44
+
45
+ ##
46
+ # Returns `true` if this key pair contains a public key.
47
+ #
48
+ # @return [Boolean]
49
+ def public_key?
50
+ !!public_key
51
+ end
52
+ alias_method :public?, :public_key? # for OpenSSL compatibility
53
+
54
+ ##
55
+ # Returns `true` if this is a valid RSA key pair according to
56
+ # {RSA::PKCS1 PKCS #1}.
57
+ #
58
+ # @return [Boolean]
59
+ # @see Key#valid?
60
+ def valid?
61
+ private_key.valid? && public_key.valid?
62
+ end
63
+
64
+ ##
65
+ # Returns the byte size of this key pair.
66
+ #
67
+ # @return [Integer]
68
+ def bytesize
69
+ Math.log256(modulus).ceil
70
+ end
71
+
72
+ ##
73
+ # Returns the bit size of this key pair.
74
+ #
75
+ # @return [Integer]
76
+ def bitsize
77
+ Math.log2(modulus).ceil
78
+ end
79
+ alias_method :size, :bitsize
80
+
81
+ ##
82
+ # Returns the RSA modulus for this key pair.
83
+ #
84
+ # @return [Integer]
85
+ def modulus
86
+ private_key ? private_key.modulus : public_key.modulus
87
+ end
88
+ alias_method :n, :modulus
89
+
90
+ ##
91
+ # Returns a hash table representation of this key pair.
92
+ #
93
+ # @example
94
+ # key_pair.to_hash #=> {:n => ..., :d => ..., :e => ...}
95
+ #
96
+ # @return [Hash]
97
+ def to_hash
98
+ {:n => modulus, :d => private_key ? private_key.exponent : nil, :e => public_key ? public_key.exponent : nil}
99
+ end
100
+
101
+ ##
102
+ # Encrypts the given `plaintext` using the public key from this key
103
+ # pair.
104
+ #
105
+ # @overload encrypt(plaintext, options = {})
106
+ # @param [Integer] plaintext
107
+ # @param [Hash{Symbol => Object}] options
108
+ # @return [Integer]
109
+ #
110
+ # @overload encrypt(plaintext, options = {})
111
+ # @param [String, IO, StringIO] plaintext
112
+ # @param [Hash{Symbol => Object}] options
113
+ # @return [String]
114
+ #
115
+ # @param [Object] plaintext
116
+ # @param [Hash{Symbol => Object}] options
117
+ # @option options [Symbol, #to_sym] :padding (nil)
118
+ def encrypt(plaintext, options = {})
119
+ case plaintext
120
+ when Integer then encrypt_integer(plaintext, options)
121
+ when String then PKCS1.i2osp(encrypt_integer(PKCS1.os2ip(plaintext), options))
122
+ when StringIO, IO then PKCS1.i2osp(encrypt_integer(PKCS1.os2ip(plaintext.read), options))
123
+ else raise ArgumentError, plaintext.inspect # FIXME
124
+ end
125
+ end
126
+
127
+ ##
128
+ # Decrypts the given `ciphertext` using the private key from this key
129
+ # pair.
130
+ #
131
+ # @overload decrypt(ciphertext, options = {})
132
+ # @param [Integer] ciphertext
133
+ # @param [Hash{Symbol => Object}] options
134
+ # @return [Integer]
135
+ #
136
+ # @overload decrypt(ciphertext, options = {})
137
+ # @param [String, IO, StringIO] ciphertext
138
+ # @param [Hash{Symbol => Object}] options
139
+ # @return [String]
140
+ #
141
+ # @param [Object] ciphertext
142
+ # @param [Hash{Symbol => Object}] options
143
+ # @option options [Symbol, #to_sym] :padding (nil)
144
+ def decrypt(ciphertext, options = {})
145
+ case ciphertext
146
+ when Integer then decrypt_integer(ciphertext, options)
147
+ when String then PKCS1.i2osp(decrypt_integer(PKCS1.os2ip(ciphertext), options))
148
+ when StringIO, IO then PKCS1.i2osp(decrypt_integer(PKCS1.os2ip(ciphertext.read), options))
149
+ else raise ArgumentError, ciphertext.inspect # FIXME
150
+ end
151
+ end
152
+
153
+ ##
154
+ # Signs the given `plaintext` using the private key from this key pair.
155
+ #
156
+ # @overload sign(plaintext, options = {})
157
+ # @param [Integer] plaintext
158
+ # @param [Hash{Symbol => Object}] options
159
+ # @return [Integer]
160
+ #
161
+ # @overload sign(plaintext, options = {})
162
+ # @param [String, IO, StringIO] plaintext
163
+ # @param [Hash{Symbol => Object}] options
164
+ # @return [String]
165
+ #
166
+ # @param [Object] plaintext
167
+ # @param [Hash{Symbol => Object}] options
168
+ # @option options [Symbol, #to_sym] :padding (nil)
169
+ def sign(plaintext, options = {})
170
+ case plaintext
171
+ when Integer then sign_integer(plaintext, options)
172
+ when String then PKCS1.i2osp(sign_integer(PKCS1.os2ip(plaintext), options))
173
+ when StringIO, IO then PKCS1.i2osp(sign_integer(PKCS1.os2ip(plaintext.read), options))
174
+ else raise ArgumentError, plaintext.inspect # FIXME
175
+ end
176
+ end
177
+
178
+ ##
179
+ # Verifies the given `signature` using the public key from this key
180
+ # pair.
181
+ #
182
+ # @overload verify(signature, plaintext, options = {})
183
+ # @param [Integer] signature
184
+ # @param [Integer] plaintext
185
+ # @param [Hash{Symbol => Object}] options
186
+ # @return [Boolean]
187
+ #
188
+ # @overload verify(signature, plaintext, options = {})
189
+ # @param [String, IO, StringIO] signature
190
+ # @param [String, IO, StringIO] plaintext
191
+ # @param [Hash{Symbol => Object}] options
192
+ # @return [Boolean]
193
+ #
194
+ # @param [Object] signature
195
+ # @param [Object] plaintext
196
+ # @param [Hash{Symbol => Object}] options
197
+ # @option options [Symbol, #to_sym] :padding (nil)
198
+ # @return [Boolean]
199
+ def verify(signature, plaintext, options = {})
200
+ signature = case signature
201
+ when Integer then signature
202
+ when String then PKCS1.os2ip(signature)
203
+ when StringIO, IO then PKCS1.os2ip(signature.read)
204
+ else raise ArgumentError, signature.inspect # FIXME
205
+ end
206
+ plaintext = case plaintext
207
+ when Integer then plaintext
208
+ when String then PKCS1.os2ip(plaintext)
209
+ when StringIO, IO then PKCS1.os2ip(plaintext.read)
210
+ else raise ArgumentError, plaintext.inspect # FIXME
211
+ end
212
+ verify_integer(signature, plaintext, options)
213
+ end
214
+
215
+ protected
216
+
217
+ ##
218
+ # @private
219
+ def encrypt_integer(plaintext, options = {})
220
+ PKCS1.rsaep(public_key, plaintext)
221
+ end
222
+
223
+ ##
224
+ # @private
225
+ def decrypt_integer(ciphertext, options = {})
226
+ PKCS1.rsadp(private_key, ciphertext)
227
+ end
228
+
229
+ ##
230
+ # @private
231
+ def sign_integer(plaintext, options = {})
232
+ PKCS1.rsasp1(private_key, plaintext)
233
+ end
234
+
235
+ ##
236
+ # @private
237
+ def verify_integer(signature, plaintext, options = {})
238
+ PKCS1.rsavp1(public_key, signature).eql?(plaintext)
239
+ end
240
+ end # class KeyPair
241
+ end # module RSA
data/lib/rsa/math.rb ADDED
@@ -0,0 +1,277 @@
1
+ module RSA
2
+ ##
3
+ # Mathematical helper functions for RSA.
4
+ module Math
5
+ extend ::Math
6
+
7
+ class ArithmeticError < ArgumentError; end
8
+
9
+ ##
10
+ # Yields an infinite pseudo-prime number sequence.
11
+ #
12
+ # This is a pseudo-prime generator that simply yields the initial values
13
+ # 2 and 3, followed by all positive integers that are not divisible by 2
14
+ # or 3.
15
+ #
16
+ # It works identically to `Prime::Generator23`, the Ruby 1.9 standard
17
+ # library's default pseudo-prime generator implementation.
18
+ #
19
+ # @example
20
+ # RSA::Math.primes.take(5) #=> [2, 3, 5, 7, 11]
21
+ #
22
+ # @yield [p] each pseudo-prime number
23
+ # @yieldparam [Integer] p a pseudo-prime number
24
+ # @return [Enumerator] yielding pseudo-primes
25
+ # @see http://ruby-doc.org/core-1.9/classes/Prime.html
26
+ def self.primes(&block)
27
+ if block_given?
28
+ yield 2; yield 3; yield 5
29
+ prime, step = 5, 4
30
+ loop { yield prime += (step = 6 - step) }
31
+ end
32
+ enum_for(:primes)
33
+ end
34
+
35
+ ##
36
+ # Yields the prime factorization of the nonzero integer `n`.
37
+ #
38
+ # @example
39
+ # RSA::Math.factorize(12).to_a #=> [[2, 2], [3, 1]]
40
+ #
41
+ # @param [Integer] n a nonzero integer
42
+ # @yield [p, e] each prime factor
43
+ # @yieldparam [Integer] p the prime factor base
44
+ # @yieldparam [Integer] e the prime factor exponent
45
+ # @return [Enumerator]
46
+ # @raise [ZeroDivisionError] if `n` is zero
47
+ # @see http://ruby-doc.org/core-1.9/classes/Prime.html
48
+ def self.factorize(n, &block)
49
+ raise ZeroDivisionError if n.zero?
50
+ if block_given?
51
+ n = n.abs if n < 0
52
+ primes.find do |p|
53
+ e = 0
54
+ while (q, r = n.divmod(p); r.zero?)
55
+ n, e = q, e + 1
56
+ end
57
+ yield p, e unless e.zero?
58
+ n <= p
59
+ end
60
+ yield n, 1 if n > 1
61
+ end
62
+ enum_for(:factorize, n)
63
+ end
64
+
65
+ ##
66
+ # Performs a primality test on the integer `n`, returning `true` if it
67
+ # is a prime.
68
+ #
69
+ # @example
70
+ # 1.upto(10).select { |n| RSA::Math.prime?(n) } #=> [2, 3, 5, 7]
71
+ #
72
+ # @param [Integer] n an integer
73
+ # @return [Boolean] `true` if `n` is a prime number, `false` otherwise
74
+ # @see http://en.wikipedia.org/wiki/Primality_test
75
+ # @see http://ruby-doc.org/core-1.9/classes/Prime.html
76
+ def self.prime?(n)
77
+ case
78
+ when n < 0 then prime?(n.abs)
79
+ when n < 2 then false
80
+ else primes do |p|
81
+ q, r = n.divmod(p)
82
+ return true if q < p
83
+ return false if r.zero?
84
+ end
85
+ end
86
+ end
87
+
88
+ ##
89
+ # Returns `true` if the integer `a` is coprime (relatively prime) to
90
+ # integer `b`.
91
+ #
92
+ # @example
93
+ # RSA::Math.coprime?(6, 35) #=> true
94
+ # RSA::Math.coprime?(6, 27) #=> false
95
+ #
96
+ # @param [Integer] a an integer
97
+ # @param [Integer] b an integer
98
+ # @return [Boolean] `true` if `a` and `b` are coprime, `false` otherwise
99
+ # @see http://en.wikipedia.org/wiki/Coprime
100
+ # @see http://mathworld.wolfram.com/RelativelyPrime.html
101
+ def self.coprime?(a, b)
102
+ egcd = self.egcd(a, b)
103
+ (a*egcd[0] + b*egcd[1]).eql?(1)
104
+ end
105
+
106
+ ##
107
+ # Returns the greatest common divisor (GCD) of the two integers `a` and
108
+ # `b`. The GCD is the largest positive integer that divides both numbers
109
+ # without a remainder.
110
+ #
111
+ # @example
112
+ # RSA::Math.gcd(3, 5) #=> 1
113
+ # RSA::Math.gcd(8, 12) #=> 4
114
+ # RSA::Math.gcd(12, 60) #=> 12
115
+ # RSA::Math.gcd(90, 12) #=> 6
116
+ #
117
+ # @param [Integer] a an integer
118
+ # @param [Integer] b an integer
119
+ # @return [Integer] the greatest common divisor of `a` and `b`
120
+ # @see http://en.wikipedia.org/wiki/Greatest_common_divisor
121
+ # @see http://mathworld.wolfram.com/GreatestCommonDivisor.html
122
+ def self.gcd(a, b)
123
+ a.gcd(b)
124
+ end
125
+
126
+ ##
127
+ # Returns the Bezout coefficients of the two nonzero integers `a` and
128
+ # `b` using the extended Euclidean algorithm.
129
+ #
130
+ # @example
131
+ # RSA::Math.egcd(120, 23) #=> [-9, 47]
132
+ # RSA::Math.egcd(421, 111) #=> [-29, 110]
133
+ # RSA::Math.egcd(93, 219) #=> [33, -14]
134
+ # RSA::Math.egcd(4864, 3458) #=> [32, -45]
135
+ #
136
+ # @param [Integer] a a nonzero integer
137
+ # @param [Integer] b a nonzero integer
138
+ # @return [Array(Integer, Integer)] the Bezout coefficients `x` and `y`
139
+ # @raise [ZeroDivisionError] if `a` or `b` is zero
140
+ # @see http://en.wikipedia.org/wiki/B%C3%A9zout's_identity
141
+ # @see http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm
142
+ # @see http://mathworld.wolfram.com/ExtendedGreatestCommonDivisor.html
143
+ def self.egcd(a, b)
144
+ if a.modulo(b).zero?
145
+ [0, 1]
146
+ else
147
+ x, y = egcd(b, a.modulo(b))
148
+ [y, x - y * a.div(b)]
149
+ end
150
+ end
151
+
152
+ ##
153
+ # Returns the modular multiplicative inverse of the integer `b` modulo
154
+ # `m`, where `b <= m`.
155
+ #
156
+ # The running time of the used algorithm, the extended Euclidean
157
+ # algorithm, is on the order of O(log2 _m_).
158
+ #
159
+ # @example
160
+ # RSA::Math.modinv(3, 11) #=> 4
161
+ # RSA::Math.modinv(6, 35) #=> 6
162
+ # RSA::Math.modinv(-6, 35) #=> 29
163
+ # RSA::Math.modinv(6, 36) #=> ArithmeticError
164
+ #
165
+ # @param [Integer] b
166
+ # @param [Integer] m the modulus
167
+ # @return [Integer] the modular multiplicative inverse
168
+ # @raise [ArithmeticError] if `m` <= 0, or if `b` not coprime to `m`
169
+ # @see http://en.wikipedia.org/wiki/Modular_multiplicative_inverse
170
+ # @see http://mathworld.wolfram.com/ModularInverse.html
171
+ def self.modinv(b, m)
172
+ if m > 0 && coprime?(b, m)
173
+ egcd(b, m).first.modulo(m)
174
+ else
175
+ raise ArithmeticError, "modulus #{m} is not positive" if m <= 0
176
+ raise ArithmeticError, "#{b} is not coprime to #{m}"
177
+ end
178
+ end
179
+
180
+ ##
181
+ # Performs modular exponentiation in a memory-efficient manner.
182
+ #
183
+ # This is equivalent to `base**exponent % modulus` but much faster for
184
+ # large exponents.
185
+ #
186
+ # The running time of the used algorithm, the right-to-left binary
187
+ # method, is on the order of O(log _exponent_).
188
+ #
189
+ # @example
190
+ # RSA::Math.modpow(5, 3, 13) #=> 8
191
+ # RSA::Math.modpow(4, 13, 497) #=> 445
192
+ #
193
+ # @param [Integer] base the base
194
+ # @param [Integer] exponent the exponent
195
+ # @param [Integer] modulus the modulus
196
+ # @return [Integer] the result
197
+ # @see http://en.wikipedia.org/wiki/Modular_exponentiation
198
+ def self.modpow(base, exponent, modulus)
199
+ result = 1
200
+ while exponent > 0
201
+ result = (base * result) % modulus unless (exponent & 1).zero?
202
+ base = (base * base) % modulus
203
+ exponent >>= 1
204
+ end
205
+ result
206
+ end
207
+
208
+ ONE = BigDecimal('1')
209
+
210
+ ##
211
+ # Returns the Euler totient for the positive integer `n`.
212
+ #
213
+ # @example
214
+ # (1..5).map { |n| RSA::Math.phi(n) } #=> [1, 1, 2, 2, 4]
215
+ #
216
+ # @param [Integer] n a positive integer, or zero
217
+ # @return [Integer] the Euler totient of `n`
218
+ # @raise [ArgumentError] if `n` < 0
219
+ # @see http://en.wikipedia.org/wiki/Euler's_totient_function
220
+ # @see http://mathworld.wolfram.com/TotientFunction.html
221
+ def self.phi(n)
222
+ case
223
+ when n < 0 then raise ArgumentError, "expected a positive integer, but got #{n}"
224
+ when n < 2 then 1 # by convention
225
+ when prime?(n) then n - 1
226
+ else factorize(n).inject(n) { |product, (p, e)| product * (ONE - (ONE / BigDecimal(p.to_s))) }.round.to_i
227
+ end
228
+ end
229
+
230
+ ##
231
+ # Returns the binary logarithm of `n`.
232
+ #
233
+ # @example
234
+ # RSA::Math.log2(16) #=> 4.0
235
+ # RSA::Math.log2(1024) #=> 10.0
236
+ #
237
+ # @param [Integer] n a positive integer
238
+ # @return [Float] the logarithm
239
+ # @raise [Errno::EDOM] if `n` < 1
240
+ # @see http://en.wikipedia.org/wiki/Binary_logarithm
241
+ def self.log2(n)
242
+ ::Math.log2(n)
243
+ end
244
+
245
+ ##
246
+ # Returns the base-256 logarithm of `n`.
247
+ #
248
+ # @example
249
+ # RSA::Math.log256(16) #=> 0.5
250
+ # RSA::Math.log256(1024) #=> 1.25
251
+ #
252
+ # @param [Integer] n a positive integer
253
+ # @return [Float] the logarithm
254
+ # @raise [Errno::EDOM] if `n` < 1
255
+ # @see http://en.wikipedia.org/wiki/Logarithm
256
+ def self.log256(n)
257
+ ::Math.log(n, 256)
258
+ end
259
+
260
+ ##
261
+ # Returns the natural logarithm of `n`. If the optional argument `b` is
262
+ # given, it will be used as the base of the logarithm.
263
+ #
264
+ # @example
265
+ # RSA::Math.log(16, 2) #=> 4.0
266
+ # RSA::Math.log(16, 256) #=> 0.5
267
+ #
268
+ # @param [Integer] n a positive integer
269
+ # @param [Integer] b a positive integer >= 2, or `nil`
270
+ # @return [Float] the logarithm
271
+ # @raise [Errno::EDOM] if `n` < 1, or if `b` < 2
272
+ # @see http://en.wikipedia.org/wiki/Natural_logarithm
273
+ def self.log(n, b = nil)
274
+ b ? ::Math.log(n, b) : ::Math.log(n)
275
+ end
276
+ end
277
+ end
@@ -0,0 +1,37 @@
1
+ module RSA
2
+ module OpenSSL
3
+ # TODO
4
+ end # module OpenSSL
5
+
6
+ class KeyPair
7
+ ##
8
+ # Generates a new RSA key pair of length `bits`.
9
+ #
10
+ # By default, the public exponent will be 65537 (0x10001) as recommended
11
+ # by {RSA::PKCS1 PKCS #1}.
12
+ #
13
+ # @param [Integer, #to_i] bits
14
+ # @param [Integer, #to_i] exponent
15
+ # @return [KeyPair]
16
+ def self.generate(bits, exponent = 65537)
17
+ pkey = ::OpenSSL::PKey::RSA.generate(bits.to_i, exponent.to_i)
18
+ n, d, e = pkey.n.to_i, pkey.d.to_i, pkey.e.to_i
19
+ self.new(Key.new(n, d), Key.new(n, e))
20
+ end
21
+
22
+ ##
23
+ # Returns this key pair as an `OpenSSL::PKey::RSA` instance.
24
+ #
25
+ # @return [OpenSSL::PKey::RSA]
26
+ def to_openssl
27
+ @openssl_pkey ||= begin
28
+ pkey = ::OpenSSL::PKey::RSA.new
29
+ pkey.n = private_key.modulus if private_key?
30
+ pkey.e = private_key.exponent if private_key?
31
+ pkey.n ||= public_key.modulus if public_key?
32
+ pkey.d = public_key.exponent if public_key?
33
+ pkey
34
+ end
35
+ end
36
+ end # class KeyPair
37
+ end # module RSA
data/lib/rsa/pkcs1.rb ADDED
@@ -0,0 +1,130 @@
1
+ module RSA
2
+ ##
3
+ # Support for the PKCS #1 (aka RFC 3447) padding schemes.
4
+ #
5
+ # @see http://en.wikipedia.org/wiki/PKCS1
6
+ # @see http://tools.ietf.org/html/rfc3447
7
+ # @see http://www.rsa.com/rsalabs/node.asp?id=2125
8
+ module PKCS1
9
+ ##
10
+ # Converts a nonnegative integer into an octet string of a specified
11
+ # length.
12
+ #
13
+ # This is the PKCS #1 I2OSP (Integer-to-Octet-String) primitive.
14
+ # Refer to PKCS #1 v2.1 pp. 8-9, section 4.1.
15
+ #
16
+ # @example
17
+ # RSA::PKCS1.i2osp(9_202_000, 2) #=> ArgumentError: integer too large
18
+ # RSA::PKCS1.i2osp(9_202_000, 3) #=> "\x8C\x69\x50"
19
+ # RSA::PKCS1.i2osp(9_202_000, 4) #=> "\x00\x8C\x69\x50"
20
+ #
21
+ # @param [Integer] x nonnegative integer to be converted
22
+ # @param [Integer] len intended length of the resulting octet string
23
+ # @return [String] octet string of length `len`
24
+ # @see http://tools.ietf.org/html/rfc3447#section-4.1
25
+ # @raise [ArgumentError] if `n` is greater than 256^len
26
+ def self.i2osp(x, len = nil)
27
+ raise ArgumentError, "integer too large" if len && x >= 256**len
28
+
29
+ StringIO.open do |buffer|
30
+ while x > 0
31
+ b = (x & 0xFF).chr
32
+ x >>= 8
33
+ buffer << b
34
+ end
35
+ s = buffer.string
36
+ s.force_encoding(Encoding::BINARY) if s.respond_to?(:force_encoding)
37
+ s.reverse!
38
+ s = len ? s.rjust(len, "\0") : s
39
+ end
40
+ end
41
+
42
+ ##
43
+ # Converts an octet string into a nonnegative integer.
44
+ #
45
+ # This is the PKCS #1 OS2IP (Octet-String-to-Integer) primitive.
46
+ # Refer to PKCS #1 v2.1 p. 9, section 4.2.
47
+ #
48
+ # @example
49
+ # RSA::PKCS1.os2ip("\x8C\x69\x50") #=> 9_202_000
50
+ #
51
+ # @param [String] x octet string to be converted
52
+ # @return [Integer] nonnegative integer
53
+ # @see http://tools.ietf.org/html/rfc3447#section-4.2
54
+ def self.os2ip(x)
55
+ x.bytes.inject(0) { |n, b| (n << 8) + b }
56
+ end
57
+
58
+ ##
59
+ # Produces a ciphertext representative from a message representative
60
+ # under the control of a public key.
61
+ #
62
+ # This is the PKCS #1 RSAEP encryption primitive.
63
+ # Refer to PKCS #1 v2.1 p. 10, section 5.1.1.
64
+ #
65
+ # @param [Key, #to_a] k RSA public key (`n`, `e`)
66
+ # @param [Integer] m message representative, an integer between 0 and `n` - 1
67
+ # @return [Integer] ciphertext representative, an integer between 0 and `n` - 1
68
+ # @raise [ArgumentError] if `m` is out of range
69
+ # @see http://tools.ietf.org/html/rfc3447#section-5.1.1
70
+ def self.rsaep(k, m)
71
+ n, e = k.to_a
72
+ raise ArgumentError, "message representative out of range" unless m >= 0 && m < n
73
+ c = Math.modpow(m, e, n)
74
+ end
75
+
76
+ ##
77
+ # Recovers the message representative from a ciphertext representative
78
+ # under the control of a private key.
79
+ #
80
+ # This is the PKCS #1 RSADP decryption primitive.
81
+ # Refer to PKCS #1 v2.1 pp. 10-11, section 5.1.2.
82
+ #
83
+ # @param [Key, #to_a] k RSA private key (`n`, `d`)
84
+ # @param [Integer] c ciphertext representative, an integer between 0 and `n` - 1
85
+ # @return [Integer] message representative, an integer between 0 and `n` - 1
86
+ # @raise [ArgumentError] if `c` is out of range
87
+ # @see http://tools.ietf.org/html/rfc3447#section-5.1.2
88
+ def self.rsadp(k, c)
89
+ n, d = k.to_a
90
+ raise ArgumentError, "ciphertext representative out of range" unless c >= 0 && c < n
91
+ m = Math.modpow(c, d, n)
92
+ end
93
+
94
+ ##
95
+ # Produces a signature representative from a message representative
96
+ # under the control of a private key.
97
+ #
98
+ # This is the PKCS #1 RSASP1 signature primitive.
99
+ # Refer to PKCS #1 v2.1 pp. 12-13, section 5.2.1.
100
+ #
101
+ # @param [Key, #to_a] k RSA private key (`n`, `d`)
102
+ # @param [Integer] m message representative, an integer between 0 and `n` - 1
103
+ # @return [Integer] signature representative, an integer between 0 and `n` - 1
104
+ # @raise [ArgumentError] if `m` is out of range
105
+ # @see http://tools.ietf.org/html/rfc3447#section-5.2.1
106
+ def self.rsasp1(k, m)
107
+ n, d = k.to_a
108
+ raise ArgumentError, "message representative out of range" unless m >= 0 && m < n
109
+ s = Math.modpow(m, d, n)
110
+ end
111
+
112
+ ##
113
+ # Recovers the message representative from a signature representative
114
+ # under the control of a public key.
115
+ #
116
+ # This is the PKCS #1 RSAVP1 verification primitive.
117
+ # Refer to PKCS #1 v2.1 p. 13, section 5.2.2.
118
+ #
119
+ # @param [Key, #to_a] k RSA public key (`n`, `e`)
120
+ # @param [Integer] s signature representative, an integer between 0 and `n` - 1
121
+ # @return [Integer] message representative, an integer between 0 and `n` - 1
122
+ # @raise [ArgumentError] if `s` is out of range
123
+ # @see http://tools.ietf.org/html/rfc3447#section-5.2.2
124
+ def self.rsavp1(k, s)
125
+ n, e = k.to_a
126
+ raise ArgumentError, "signature representative out of range" unless s >= 0 && s < n
127
+ m = Math.modpow(s, e, n)
128
+ end
129
+ end # module PKCS1
130
+ end # module RSA
@@ -0,0 +1,22 @@
1
+ module RSA
2
+ module VERSION
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ TINY = 4
6
+ EXTRA = nil
7
+
8
+ STRING = [MAJOR, MINOR, TINY, EXTRA].compact.join('.')
9
+
10
+ ##
11
+ # @return [String]
12
+ def self.to_s() STRING end
13
+
14
+ ##
15
+ # @return [String]
16
+ def self.to_str() STRING end
17
+
18
+ ##
19
+ # @return [Array(Integer, Integer, Integer)]
20
+ def self.to_a() [MAJOR, MINOR, TINY] end
21
+ end
22
+ end
data/lib/rsa.rb ADDED
@@ -0,0 +1,27 @@
1
+ require 'bigdecimal' unless defined?(BigDecimal)
2
+ require 'stringio' unless defined?(StringIO)
3
+
4
+ if RUBY_VERSION < '1.9.1'
5
+ # @see http://rubygems.org/gems/backports
6
+ begin
7
+ require 'backports/1.9.1'
8
+ rescue LoadError
9
+ abort "RSA.rb requires Ruby 1.9.1 or the Backports gem (hint: `gem install backports')."
10
+ end
11
+ end
12
+
13
+ module RSA
14
+ autoload :Math, 'rsa/math'
15
+ autoload :PKCS1, 'rsa/pkcs1'
16
+ autoload :Key, 'rsa/key'
17
+ autoload :KeyPair, 'rsa/key_pair'
18
+ autoload :OpenSSL, 'rsa/openssl'
19
+ autoload :VERSION, 'rsa/version'
20
+ end
21
+
22
+ begin
23
+ require 'openssl'
24
+ require 'rsa/openssl'
25
+ rescue LoadError
26
+ # OpenSSL acceleration disabled.
27
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rsa-g
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Gabriel Vian
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-08-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: yard
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.6.0
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.6.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.3.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 1.3.0
41
+ description: RSA.rb is a pure-Ruby implementation of the RSA encryption algorithm
42
+ and the PKCS#1 cryptography standard.
43
+ email:
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - AUTHORS
49
+ - CREDITS
50
+ - README
51
+ - UNLICENSE
52
+ - VERSION
53
+ - lib/rsa.rb
54
+ - lib/rsa/key.rb
55
+ - lib/rsa/key_pair.rb
56
+ - lib/rsa/math.rb
57
+ - lib/rsa/openssl.rb
58
+ - lib/rsa/pkcs1.rb
59
+ - lib/rsa/version.rb
60
+ homepage:
61
+ licenses:
62
+ - Unlicense
63
+ metadata: {}
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 1.8.1
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubyforge_project: rsa
80
+ rubygems_version: 2.6.14.1
81
+ signing_key:
82
+ specification_version: 4
83
+ summary: RSA encryption for Ruby.
84
+ test_files: []