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 +7 -0
- data/AUTHORS +1 -0
- data/CREDITS +0 -0
- data/README +1 -0
- data/UNLICENSE +24 -0
- data/VERSION +1 -0
- data/lib/rsa/key.rb +54 -0
- data/lib/rsa/key_pair.rb +241 -0
- data/lib/rsa/math.rb +277 -0
- data/lib/rsa/openssl.rb +37 -0
- data/lib/rsa/pkcs1.rb +130 -0
- data/lib/rsa/version.rb +22 -0
- data/lib/rsa.rb +27 -0
- metadata +84 -0
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
|
data/lib/rsa/key_pair.rb
ADDED
@@ -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
|
data/lib/rsa/openssl.rb
ADDED
@@ -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
|
data/lib/rsa/version.rb
ADDED
@@ -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: []
|