rsa-g 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/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: []
|