putty-key 1.1.0 → 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0e875308cd1e8bd154bb40ceccf856a1b91cfa74e1858c89b006735016980c10
4
- data.tar.gz: 9306636667b1864c570bc467837e44e3d2490b965d4c3493ff1abe853832d419
3
+ metadata.gz: f1c65726c4468a416efcd8617dab76d88660cf732254089a5a342c16f9eabc5d
4
+ data.tar.gz: cb96dba0c50957fea7add686d9447fcbb65d42bd1bc5bfbd61d81feb98c79a69
5
5
  SHA512:
6
- metadata.gz: 52426f86621cff16b7a55901144e987f8ecc8478c7204511449d8de3d19258c60f472deb98e85996d39f9a3a197e2a2832095d01e09e710454f30d00c99af820
7
- data.tar.gz: acb9f8986b8c42e554e14d54d11cb6d6bde103256780dd31a81e4b828e6109b40d03b19460479868334028a9534a05eb729d04db555678c3db7a0a12651d9b32
6
+ metadata.gz: aa668b32837bb8aacd74d1623ede0b0bef2b6b38c06927b684ee69beb767d611bff059cb4a20b2ffce699523f217af8e815896187ebf1b0cb2b0aabb452ea61d
7
+ data.tar.gz: d1aec232b10f62cdaa62368144d550dee02de413509869731809cf1f314b3e538fa60b77a373751dffa45e36b0bdca06b08041d282c30cdf4e0d4a588779e261
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGES.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changes #
2
2
 
3
+ ## Version 1.1.2 - 16-Oct-2024 ##
4
+
5
+ * Fix `Java::JavaLang::NullPointerException` being raised instead of
6
+ `PuTTY::Key::InvalidStateError` by `OpenSSL::PKey::EC#to_ppk` on JRuby 9.4
7
+ when the key is not initialized.
8
+
9
+
10
+ ## Version 1.1.1 - 23-Oct-2022 ##
11
+
12
+ * Add support for Ruby 3.2.
13
+ * Add support for OpenSSL 3 (requires either Ruby 3.1+, or version 3.0.0+ of the
14
+ openssl gem).
15
+
16
+
3
17
  ## Version 1.1.0 - 24-May-2021 ##
4
18
 
5
19
  * Add support for [format 3 .ppk files](https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/ppk3.html)
data/Gemfile CHANGED
@@ -12,6 +12,33 @@ group :test do
12
12
 
13
13
  # coveralls is no longer maintained, but supports Ruby < 2.3.
14
14
  # coveralls_reborn is maintained, but requires Ruby >= 2.3.
15
- gem 'coveralls', '~> 0.8', require: false if RUBY_VERSION < '2.3'
15
+ gem 'coveralls', git: 'https://github.com/philr/coveralls-ruby.git', require: false if RUBY_VERSION < '2.3'
16
16
  gem 'coveralls_reborn', '~> 0.13', require: false if RUBY_VERSION >= '2.3'
17
+
18
+ # term-ansicolor is a dependency of coveralls. All versions are falsely
19
+ # declared as compatible with any Ruby version.
20
+ #
21
+ # Limit to an earlier compatible version.
22
+ if RUBY_VERSION < '2.5'
23
+ gem 'term-ansicolor', '< 1.9'
24
+ end
25
+
26
+ # tins is a dependency of coveralls. From 1.33.0 it depends on bigdecimal,
27
+ # which can't be installed on old JRuby versions.
28
+ #
29
+ # Limit to an earlier compatible version.
30
+ if RUBY_ENGINE == 'jruby'
31
+ gem 'tins', '< 1.33'
32
+ end
33
+
34
+ # The source version of ffi 1.15.5 is declared as compatible with Ruby >= 2.3.
35
+ # The binary version of 1.15.5 is declared as compatible with Ruby >= 2.4, so
36
+ # doesn't get used. The using the source version results in a segmentation
37
+ # fault during libffi initialization.
38
+ #
39
+ # Binaries of 15.5.0 to 15.5.4 are declared as compatible with Ruby >= 2.3,
40
+ # but don't get used with Bundler 2.3.23 and Ruby 2.3 on Windows.
41
+ #
42
+ # Limit to earlier compatible versions.
43
+ gem 'ffi', '< 1.15.0' if RUBY_VERSION < '2.4' && RUBY_PLATFORM =~ /mingw/
17
44
  end
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2016-2021 Philip Ross
1
+ Copyright (c) 2016-2024 Philip Ross
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy of
4
4
  this software and associated documentation files (the "Software"), to deal in
data/Rakefile CHANGED
@@ -4,11 +4,11 @@ require 'rubygems/package_task'
4
4
  require 'rake/testtask'
5
5
 
6
6
  BASE_DIR = File.expand_path(File.dirname(__FILE__))
7
+ GEMSPEC_PATH = File.join(BASE_DIR, 'putty-key.gemspec')
8
+ spec = TOPLEVEL_BINDING.eval(File.read(GEMSPEC_PATH), GEMSPEC_PATH)
7
9
 
8
10
  task :default => :test
9
11
 
10
- spec = eval(File.read('putty-key.gemspec'))
11
-
12
12
  # Attempt to find the private key and return a spec with added options for
13
13
  # signing the gem if found.
14
14
  def add_signing_key(spec)
@@ -25,10 +25,271 @@ module PuTTY
25
25
 
26
26
  private_constant :SSH_CURVES
27
27
 
28
+ # Either a real JRuby NullPointerException, or a fake class that won't be
29
+ # raised. Can be rescued to handle NullPointerException on jruby.
30
+ JavaNullPointerException = RUBY_ENGINE == 'jruby' ? Java::JavaLang::NullPointerException : Class.new(Exception)
31
+ private_constant :JavaNullPointerException
32
+
33
+ # OpenSSL version helper methods.
34
+ #
35
+ # @private
36
+ module Version
37
+ class << self
38
+ # Determines if the Ruby OpenSSL wrapper is using the OpenSSL library
39
+ # (not LibreSSL and not JRuby) and if the version matches the required
40
+ # version.
41
+ #
42
+ # @param major [Integer] The required major version. `nil` if any
43
+ # version of OpenSSL is sufficient.
44
+ # @param minor [Integer] The required minor version.
45
+ # @param fix [Integer] The required fix version.
46
+ # @param patch [Integer] The required patch version.
47
+ # @return [Boolean] `true` if the requirements are met, otherwise
48
+ # `false`.
49
+ def openssl?(major = nil, minor = 0, fix = 0, patch = 0)
50
+ return false if ::OpenSSL::OPENSSL_VERSION.include?('LibreSSL')
51
+ return false if ::OpenSSL::OPENSSL_VERSION.include?('JRuby')
52
+ return true unless major
53
+ required_version = major * 0x10000000 + minor * 0x100000 + fix * 0x1000 + patch * 0x10
54
+ ::OpenSSL::OPENSSL_VERSION_NUMBER >= required_version
55
+ end
56
+ end
57
+ end
58
+ private_constant :Version
59
+
60
+ # Methods to build OpenSSL private keys from a {PPK}.
61
+ #
62
+ # @private
63
+ module PKeyBuilding
64
+ class << self
65
+ # Creates a new OpenSSL DSA private key for the given DSA {PPK}.
66
+ #
67
+ # @param ppk [PPK] A DSA {PPK}.
68
+ # @return [::OpenSSL::PKey::DSA] The OpenSSL DSA private key.
69
+ def ppk_to_dsa(ppk)
70
+ _, p, q, g, pub_key = Util.ssh_unpack(ppk.public_blob, :string, :mpint, :mpint, :mpint, :mpint)
71
+ priv_key = Util.ssh_unpack(ppk.private_blob, :mpint).first
72
+ dsa_from_params(p, q, g, pub_key, priv_key)
73
+ end
74
+
75
+ # Creates a new OpenSSL RSA private key for the given RSA {PPK}.
76
+ #
77
+ # @param ppk [PPK] An RSA {PPK}.
78
+ # @return [::OpenSSL::PKey::RSA] The OpenSSL RSA private key.
79
+ def ppk_to_rsa(ppk)
80
+ _, e, n = Util.ssh_unpack(ppk.public_blob, :string, :mpint, :mpint)
81
+ d, p, q, iqmp = Util.ssh_unpack(ppk.private_blob, :mpint, :mpint, :mpint, :mpint)
82
+ dmp1 = d % (p - 1)
83
+ dmq1 = d % (q - 1)
84
+ rsa_from_params(e, n, d, p, q, iqmp, dmp1, dmq1)
85
+ end
86
+
87
+ # Creates a new OpenSSL EC private key for the given EC {PPK}.
88
+ #
89
+ # @param ppk [PPK] An EC {PPK}.
90
+ # @param ppk_curve [String] The PPK curve name extracted from the
91
+ # PPK algorithm name.
92
+ # @return [::OpenSSL::PKey::EC] The OpenSSL EC private key.
93
+ def ppk_to_ec(ppk, ppk_curve)
94
+ curve = OPENSSL_CURVES[ppk_curve]
95
+ _, _, pub_key = Util.ssh_unpack(ppk.public_blob, :string, :string, :mpint)
96
+ priv_key = Util.ssh_unpack(ppk.private_blob, :mpint).first
97
+ ec_from_params(curve, pub_key, priv_key)
98
+ end
99
+
100
+ private
101
+
102
+ if Version.openssl?(3)
103
+ # OpenSSL v3 private keys are immutable. The Ruby OpenSSL wrapper
104
+ # doesn't provide a method to construct private keys using the
105
+ # parameters. Build DER (ASN.1) encoded versions of the keys.
106
+ #
107
+ # In theory this should be usable universally. However
108
+ # ::OpenSSL::PKey::EC::Point#to_octet_string is only supported from
109
+ # Ruby >= 2.4 and there are issues with JRuby's OpenSSL library
110
+ # (that doesn't make use of OpenSSL).
111
+
112
+ # :nocov_no_openssl3:
113
+
114
+ # Creates a new OpenSSL DSA private key with the given parameters.
115
+ #
116
+ # @param p [::OpenSSL::BN] The p parameter (prime).
117
+ # @param q [::OpenSSL::BN] The q parameter (prime).
118
+ # @param g [::OpenSSL::BN] The g parameter.
119
+ # @param pub_key [::OpenSSL::BN] The public key.
120
+ # @param priv_key [::OpenSSL::BN] The private key.
121
+ # @return [::OpenSSL::PKey::DSA] The OpenSSL DSA private key.
122
+ def dsa_from_params(p, q, g, pub_key, priv_key)
123
+ # https://www.openssl.org/docs/man3.0/man1/openssl-dsa.html (outform parameter).
124
+ sequence = [
125
+ ::OpenSSL::ASN1::Integer.new(0),
126
+ ::OpenSSL::ASN1::Integer.new(p),
127
+ ::OpenSSL::ASN1::Integer.new(q),
128
+ ::OpenSSL::ASN1::Integer.new(g),
129
+ ::OpenSSL::ASN1::Integer.new(pub_key),
130
+ ::OpenSSL::ASN1::Integer.new(priv_key)
131
+ ]
132
+
133
+ ::OpenSSL::PKey::DSA.new(::OpenSSL::ASN1::Sequence.new(sequence).to_der)
134
+ end
135
+
136
+ # Creates a new OpenSSL RSA private key with the given parameters.
137
+ #
138
+ # @param e [::OpenSSL::BN] The public key exponent.
139
+ # @param n [::OpenSSL::BN] The modulus.
140
+ # @param d [::OpenSSL::BN] The private key exponent.
141
+ # @param p [::OpenSSL::BN] The p prime.
142
+ # @param q [::OpenSSL::BN] The q prime.
143
+ # @param iqmp [::OpenSSL::BN] The inverse of q, mod p.
144
+ # @param dmp1 [::OpenSSL::BN] `d` mod (`p` - 1).
145
+ # @param dmq1 [::OpenSSL::BN] `d` mod (`q` - 1).
146
+ # @return [::OpenSSL::PKey::RSA] The OpenSSL RSA private key.
147
+ def rsa_from_params(e, n, d, p, q, iqmp, dmp1, dmq1)
148
+ # RFC 3447 Appendix A.1.2
149
+ sequence = [
150
+ ::OpenSSL::ASN1::Integer.new(0),
151
+ ::OpenSSL::ASN1::Integer.new(n),
152
+ ::OpenSSL::ASN1::Integer.new(e),
153
+ ::OpenSSL::ASN1::Integer.new(d),
154
+ ::OpenSSL::ASN1::Integer.new(p),
155
+ ::OpenSSL::ASN1::Integer.new(q),
156
+ ::OpenSSL::ASN1::Integer.new(dmp1),
157
+ ::OpenSSL::ASN1::Integer.new(dmq1),
158
+ ::OpenSSL::ASN1::Integer.new(iqmp)
159
+ ]
160
+
161
+ ::OpenSSL::PKey::RSA.new(::OpenSSL::ASN1::Sequence.new(sequence).to_der)
162
+ end
163
+
164
+ # Creates a new OpenSSL EC private key with the given parameters.
165
+ #
166
+ # @param curve [String] The name of the OpenSSL EC curve.
167
+ # @param pub_key [::OpenSSL::BN] The public key.
168
+ # @param priv_key [::OpenSSL::BN] The private key.
169
+ # @return [::OpenSSL::PKey::EC] The OpenSSL EC private key.
170
+ def ec_from_params(curve, pub_key, priv_key)
171
+ group = ::OpenSSL::PKey::EC::Group.new(curve)
172
+ point = ::OpenSSL::PKey::EC::Point.new(group, pub_key)
173
+ point_string = point.to_octet_string(:uncompressed)
174
+
175
+ # RFC 5915 Section 3
176
+ sequence = [
177
+ ::OpenSSL::ASN1::Integer.new(1),
178
+ ::OpenSSL::ASN1::OctetString.new(priv_key.to_s(2)),
179
+ ::OpenSSL::ASN1::ObjectId.new(curve, 0, :EXPLICIT),
180
+ ::OpenSSL::ASN1::BitString.new(point_string, 1, :EXPLICIT)
181
+ ]
182
+
183
+ ::OpenSSL::PKey::EC.new(::OpenSSL::ASN1::Sequence.new(sequence).to_der)
184
+ end
185
+ # :nocov_no_openssl3:
186
+ else
187
+ # :nocov_openssl3:
188
+ if ::OpenSSL::PKey::DSA.new.respond_to?(:set_key)
189
+ # :nocov_no_openssl_pkey_dsa_set_key:
190
+
191
+ # Creates a new OpenSSL DSA private key with the given parameters.
192
+ #
193
+ # @param p [::OpenSSL::BN] The p parameter.
194
+ # @param q [::OpenSSL::BN] The q parameter.
195
+ # @param g [::OpenSSL::BN] The g parameter.
196
+ # @param pub_key [::OpenSSL::BN] The public key.
197
+ # @param priv_key [::OpenSSL::BN] The private key.
198
+ # @return [::OpenSSL::PKey::DSA] The OpenSSL DSA private key.
199
+ def dsa_from_params(p, q, g, pub_key, priv_key)
200
+ ::OpenSSL::PKey::DSA.new.tap do |pkey|
201
+ pkey.set_key(pub_key, priv_key)
202
+ pkey.set_pqg(p, q, g)
203
+ end
204
+ end
205
+ # :nocov_no_openssl_pkey_dsa_set_key:
206
+ else
207
+ # :nocov_openssl_pkey_dsa_set_key:
208
+ # Creates a new OpenSSL DSA private key with the given parameters.
209
+ #
210
+ # @param p [::OpenSSL::BN] The p parameter.
211
+ # @param q [::OpenSSL::BN] The q parameter.
212
+ # @param g [::OpenSSL::BN] The g parameter.
213
+ # @param pub_key [::OpenSSL::BN] The public key.
214
+ # @param priv_key [::OpenSSL::BN] The private key.
215
+ # @return [::OpenSSL::PKey::DSA] The OpenSSL DSA private key.
216
+ def dsa_from_params(p, q, g, pub_key, priv_key)
217
+ ::OpenSSL::PKey::DSA.new.tap do |pkey|
218
+ pkey.p, pkey.q, pkey.g, pkey.pub_key, pkey.priv_key = p, q, g, pub_key, priv_key
219
+ end
220
+ end
221
+ # :nocov_openssl_pkey_dsa_set_key:
222
+ end
223
+
224
+ if ::OpenSSL::PKey::RSA.new.respond_to?(:set_factors)
225
+ # :nocov_no_openssl_pkey_rsa_set_factors:
226
+
227
+ # Creates a new OpenSSL RSA private key with the given parameters.
228
+ #
229
+ # @param e [::OpenSSL::BN] The public key exponent.
230
+ # @param n [::OpenSSL::BN] The modulus.
231
+ # @param d [::OpenSSL::BN] The private key exponent.
232
+ # @param p [::OpenSSL::BN] The p prime.
233
+ # @param q [::OpenSSL::BN] The q prime.
234
+ # @param iqmp [::OpenSSL::BN] The inverse of q, mod p.
235
+ # @param dmp1 [::OpenSSL::BN] `d` mod (`p` - 1).
236
+ # @param dmq1 [::OpenSSL::BN] `d` mod (`q` - 1).
237
+ # @return [::OpenSSL::PKey::RSA] The OpenSSL RSA private key.
238
+ def rsa_from_params(e, n, d, p, q, iqmp, dmp1, dmq1)
239
+ ::OpenSSL::PKey::RSA.new.tap do |pkey|
240
+ pkey.set_factors(p, q)
241
+ pkey.set_key(n, e, d)
242
+ pkey.set_crt_params(dmp1, dmq1, iqmp)
243
+ end
244
+ end
245
+ # :nocov_no_openssl_pkey_rsa_set_factors:
246
+ else
247
+ # :nocov_openssl_pkey_rsa_set_factors:
248
+
249
+ # Creates a new OpenSSL RSA private key with the given parameters.
250
+ #
251
+ # @param e [::OpenSSL::BN] The public key exponent.
252
+ # @param n [::OpenSSL::BN] The modulus.
253
+ # @param d [::OpenSSL::BN] The private key exponent.
254
+ # @param p [::OpenSSL::BN] The p prime.
255
+ # @param q [::OpenSSL::BN] The q prime.
256
+ # @param iqmp [::OpenSSL::BN] The inverse of q, mod p.
257
+ # @param dmp1 [::OpenSSL::BN] `d` mod (`p` - 1).
258
+ # @param dmq1 [::OpenSSL::BN] `d` mod (`q` - 1).
259
+ # @return [::OpenSSL::PKey::RSA] The OpenSSL RSA private key.
260
+ def rsa_from_params(e, n, d, p, q, iqmp, dmp1, dmq1)
261
+ ::OpenSSL::PKey::RSA.new.tap do |pkey|
262
+ pkey.e, pkey.n, pkey.d, pkey.p, pkey.q, pkey.iqmp, pkey.dmp1, pkey.dmq1 = e, n, d, p, q, iqmp, dmp1, dmq1
263
+ end
264
+ end
265
+ # :nocov_openssl_pkey_rsa_set_factors:
266
+ end
267
+
268
+ # Creates a new OpenSSL EC private key with the given parameters.
269
+ #
270
+ # @param curve [String] The name of the OpenSSL EC curve.
271
+ # @param pub_key [::OpenSSL::BN] The public key.
272
+ # @param priv_key [::OpenSSL::BN] The private key.
273
+ # @return [::OpenSSL::PKey::EC] The OpenSSL EC private key.
274
+ def ec_from_params(curve, pub_key, priv_key)
275
+ # Old versions of jruby-openssl don't include an EC class (version 0.9.16).
276
+ ec_class = (::OpenSSL::PKey::EC rescue raise ArgumentError, "Unsupported algorithm: #{ppk.algorithm}")
277
+
278
+ ec_class.new(curve).tap do |pkey|
279
+ group = pkey.group || ::OpenSSL::PKey::EC::Group.new(curve)
280
+ pkey.public_key = ::OpenSSL::PKey::EC::Point.new(group, pub_key)
281
+ pkey.private_key = priv_key
282
+ end
283
+ end
284
+ # :nocov_openssl3:
285
+ end
286
+ end
287
+ end
288
+ private_constant :PKeyBuilding
289
+
28
290
  # The {ClassMethods} module is used to extend `OpenSSL::PKey` when
29
291
  # using the PuTTY::Key refinement or calling {PuTTY::Key.global_install}.
30
292
  # This adds a `from_ppk` class method to `OpenSSL::PKey`.
31
- #
32
293
  module ClassMethods
33
294
  # Creates a new `OpenSSL::PKey` from a PuTTY private key (instance of
34
295
  # {PPK}).
@@ -50,53 +311,11 @@ module PuTTY
50
311
 
51
312
  case ppk.algorithm
52
313
  when 'ssh-dss'
53
- ::OpenSSL::PKey::DSA.new.tap do |pkey|
54
- _, p, q, g, pub_key = Util.ssh_unpack(ppk.public_blob, :string, :mpint, :mpint, :mpint, :mpint)
55
- priv_key = Util.ssh_unpack(ppk.private_blob, :mpint).first
56
-
57
- if pkey.respond_to?(:set_key)
58
- # :nocov_no_openssl_pkey_dsa_set_key:
59
- pkey.set_key(pub_key, priv_key)
60
- pkey.set_pqg(p, q, g)
61
- # :nocov_no_openssl_pkey_dsa_set_key:
62
- else
63
- # :nocov_openssl_pkey_dsa_set_key:
64
- pkey.p, pkey.q, pkey.g, pkey.pub_key, pkey.priv_key = p, q, g, pub_key, priv_key
65
- # :nocov_openssl_pkey_dsa_set_key:
66
- end
67
- end
314
+ PKeyBuilding.ppk_to_dsa(ppk)
68
315
  when 'ssh-rsa'
69
- ::OpenSSL::PKey::RSA.new.tap do |pkey|
70
- _, e, n = Util.ssh_unpack(ppk.public_blob, :string, :mpint, :mpint)
71
- d, p, q, iqmp = Util.ssh_unpack(ppk.private_blob, :mpint, :mpint, :mpint, :mpint)
72
-
73
- dmp1 = d % (p - 1)
74
- dmq1 = d % (q - 1)
75
-
76
- if pkey.respond_to?(:set_factors)
77
- # :nocov_no_openssl_pkey_rsa_set_factors:
78
- pkey.set_factors(p, q)
79
- pkey.set_key(n, e, d)
80
- pkey.set_crt_params(dmp1, dmq1, iqmp)
81
- # :nocov_no_openssl_pkey_rsa_set_factors:
82
- else
83
- # :nocov_openssl_pkey_rsa_set_factors:
84
- pkey.e, pkey.n, pkey.d, pkey.p, pkey.q, pkey.iqmp, pkey.dmp1, pkey.dmq1 = e, n, d, p, q, iqmp, dmp1, dmq1
85
- # :nocov_openssl_pkey_rsa_set_factors:
86
- end
87
- end
316
+ PKeyBuilding.ppk_to_rsa(ppk)
88
317
  when /\Aecdsa-sha2-(nistp(?:256|384|521))\z/
89
- curve = OPENSSL_CURVES[$1]
90
-
91
- # Old versions of jruby-openssl don't include an EC class (version 0.9.16).
92
- ec_class = (::OpenSSL::PKey::EC rescue raise ArgumentError, "Unsupported algorithm: #{ppk.algorithm}")
93
-
94
- ec_class.new(curve).tap do |pkey|
95
- _, _, point = Util.ssh_unpack(ppk.public_blob, :string, :string, :mpint)
96
- group = pkey.group || ::OpenSSL::PKey::EC::Group.new(curve)
97
- pkey.public_key = ::OpenSSL::PKey::EC::Point.new(group, point)
98
- pkey.private_key = Util.ssh_unpack(ppk.private_blob, :mpint).first
99
- end
318
+ PKeyBuilding.ppk_to_ec(ppk, $1)
100
319
  else
101
320
  raise ArgumentError, "Unsupported algorithm: #{ppk.algorithm}"
102
321
  end
@@ -141,7 +360,12 @@ module PuTTY
141
360
  # @raise [UnsupportedCurveError] If the key uses a curve that is not
142
361
  # supported by PuTTY.
143
362
  def to_ppk
144
- curve = group && group.curve_name
363
+ g = group
364
+ curve = g && begin
365
+ g.curve_name
366
+ rescue JavaNullPointerException
367
+ nil
368
+ end
145
369
  raise InvalidStateError, 'The key has not been fully initialized (a curve name must be assigned)' unless curve
146
370
  ssh_curve = SSH_CURVES[curve]
147
371
  raise UnsupportedCurveError, "The curve '#{curve}' is not supported" unless ssh_curve
@@ -201,13 +425,30 @@ module PuTTY
201
425
  end
202
426
 
203
427
  OpenSSL.const_get(:PKEY_CLASSES).each do |name, openssl_class|
428
+ mod = OpenSSL.const_get(name)
204
429
  refine openssl_class do
205
- include OpenSSL.const_get(name)
430
+ if defined?(::Refinement) && kind_of?(::Refinement)
431
+ # :nocov_no_refinement_class:
432
+ import_methods(mod)
433
+ # :nocov_no_refinement_class:
434
+ else
435
+ # :nocov_refinement_class:
436
+ include mod
437
+ # :nocov_refinement_class:
438
+ end
206
439
  end if respond_to?(:refine, true)
207
440
  end
208
441
 
209
442
  refine ::OpenSSL::PKey.singleton_class do
210
- include OpenSSL::ClassMethods
443
+ if defined?(::Refinement) && kind_of?(::Refinement)
444
+ # :nocov_no_refinement_class:
445
+ import_methods(OpenSSL::ClassMethods)
446
+ # :nocov_no_refinement_class:
447
+ else
448
+ # :nocov_refinement_class:
449
+ include OpenSSL::ClassMethods
450
+ # :nocov_refinement_class:
451
+ end
211
452
  end if respond_to?(:refine, true)
212
453
  end
213
454
  end
data/lib/putty/key/ppk.rb CHANGED
@@ -319,7 +319,19 @@ module PuTTY
319
319
  def derive_keys(format, cipher = nil, passphrase = nil, argon2_params = nil)
320
320
  if format >= 3
321
321
  return derive_format_3_keys(cipher, passphrase, argon2_params) if cipher
322
- return [''.b, nil, nil, nil]
322
+
323
+ # An empty string should work for the MAC, but ::OpenSSL::HMAC fails
324
+ # when used with OpenSSL 3:
325
+ #
326
+ # EVP_PKEY_new_mac_key: malloc failure (OpenSSL::HMACError).
327
+ #
328
+ # See https://github.com/ruby/openssl/pull/538 and
329
+ # https://github.com/openssl/openssl/issues/13089.
330
+ #
331
+ # Ruby 3.1.3 should contain the workaround from ruby/openssl PR 538.
332
+ #
333
+ # Use "\0" as the MAC key for a workaround for Ruby < 3.1.3.
334
+ return ["\0".b, nil, nil, nil]
323
335
  end
324
336
 
325
337
  mac_key = derive_format_2_mac_key(passphrase)
@@ -481,7 +493,7 @@ module PuTTY
481
493
  # after padding bytes have been appended prior to encryption).
482
494
  #
483
495
  # @param format [Integer] The format of the .ppk file.
484
- # @param passphrase [String] The encryption passphrase.
496
+ # @param mac_key [String] The HMAC key.
485
497
  # @param encryption_type [String] The value of the Encryption field.
486
498
  # @param padded_private_blob [String] The private blob after padding bytes
487
499
  # have been appended prior to encryption.
@@ -3,6 +3,6 @@
3
3
  module PuTTY
4
4
  module Key
5
5
  # The PuTTY::Key version number.
6
- VERSION = '1.1.0'
6
+ VERSION = '1.1.2'
7
7
  end
8
8
  end
data/putty-key.gemspec CHANGED
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.join('..', 'lib', 'putty', 'key', 'version'), __FILE__)
1
+ require_relative 'lib/putty/key/version'
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'putty-key'
data/test/test_helper.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'putty/key'
4
+
3
5
  TEST_TYPE = (ENV['TEST_TYPE'] || 'refinement').to_sym
4
6
  raise "Unrecognized TEST_TYPE: #{TEST_TYPE}" unless [:refinement, :global].include?(TEST_TYPE)
5
7
 
@@ -15,6 +17,13 @@ if TEST_COVERAGE
15
17
  "#{object.respond_to?(method) ? '' : 'no_'}#{Regexp.escape(object.class.name.downcase.gsub('::', '_'))}_#{Regexp.escape(method)}"
16
18
  end
17
19
 
20
+ feature_support = [
21
+ ['openssl3', PuTTY::Key::OpenSSL.const_get(:Version).openssl?(3)],
22
+ ['refinement_class', defined?(Refinement)]
23
+ ].map do |feature, available|
24
+ "#{available ? '' : 'no_'}#{feature}"
25
+ end
26
+
18
27
  SimpleCov.command_name TEST_TYPE.to_s
19
28
 
20
29
  SimpleCov.formatters = [
@@ -23,13 +32,11 @@ if TEST_COVERAGE
23
32
 
24
33
  SimpleCov.start do
25
34
  add_filter 'test'
26
- nocov_token "nocov_(#{method_support.join('|')})"
35
+ nocov_token "nocov_(#{(method_support + feature_support).join('|')})"
27
36
  project_name 'PuTTY::Key'
28
37
  end
29
38
  end
30
39
 
31
- require 'putty/key'
32
-
33
40
  require 'fileutils'
34
41
  require 'minitest/autorun'
35
42
  require 'tmpdir'
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: putty-key
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Philip Ross
@@ -29,7 +29,7 @@ cert_chain:
29
29
  J3Zn/kSTjTekiaspyGbczC3PUaeJNxr+yCvR4sk71Xmk/GaKKGOHedJ1uj/LAXrA
30
30
  MR0mpl7b8zCg0PFC1J73uw==
31
31
  -----END CERTIFICATE-----
32
- date: 2021-05-24 00:00:00.000000000 Z
32
+ date: 2024-10-16 00:00:00.000000000 Z
33
33
  dependencies:
34
34
  - !ruby/object:Gem::Dependency
35
35
  name: ffi
@@ -139,9 +139,9 @@ licenses:
139
139
  metadata:
140
140
  bug_tracker_uri: https://github.com/philr/putty-key/issues
141
141
  changelog_uri: https://github.com/philr/putty-key/blob/master/CHANGES.md
142
- documentation_uri: https://rubydoc.info/gems/putty-key/1.1.0
142
+ documentation_uri: https://rubydoc.info/gems/putty-key/1.1.2
143
143
  homepage_uri: https://github.com/philr/putty-key
144
- source_code_uri: https://github.com/philr/putty-key/tree/v1.1.0
144
+ source_code_uri: https://github.com/philr/putty-key/tree/v1.1.2
145
145
  post_install_message:
146
146
  rdoc_options:
147
147
  - "--title"
@@ -164,7 +164,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
164
164
  version: '0'
165
165
  requirements:
166
166
  - libargon2 to handle format 3 .ppk files
167
- rubygems_version: 3.2.3
167
+ rubygems_version: 3.5.16
168
168
  signing_key:
169
169
  specification_version: 4
170
170
  summary: PuTTY private key (.ppk) library. Supports reading and writing with a refinement
metadata.gz.sig CHANGED
Binary file