putty-key 1.1.0 → 1.1.2

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 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