sshkey 1.5.1 → 1.6.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.
- data/.travis.yml +3 -0
- data/README.md +2 -2
- data/lib/sshkey.rb +41 -24
- data/lib/sshkey/exception.rb +3 -0
- data/lib/sshkey/version.rb +1 -1
- data/test/sshkey_test.rb +24 -0
- metadata +3 -3
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -13,7 +13,7 @@ Tested on the following Rubies: MRI 1.8.7, 1.9.2, 1.9.3, 2.0.0, REE, JRuby (1.7.
|
|
13
13
|
### Generate a new key
|
14
14
|
|
15
15
|
When generating a new keypair the default key type is 2048-bit RSA, but you can supply the `type` (RSA or DSA) and `bits` in the options.
|
16
|
-
You can also (optionally) supply a `comment` or `passphrase
|
16
|
+
You can also (optionally) supply a `comment` or `passphrase`.
|
17
17
|
|
18
18
|
```ruby
|
19
19
|
k = SSHKey.generate
|
@@ -23,7 +23,7 @@ k = SSHKey.generate(:type => "DSA", :bits => 1024, :comment => "foo@bar.com", :p
|
|
23
23
|
|
24
24
|
### Use your existing key
|
25
25
|
|
26
|
-
Return an SSHKey object from an existing RSA or DSA private key (provided as a string)
|
26
|
+
Return an SSHKey object from an existing RSA or DSA private key (provided as a string).
|
27
27
|
|
28
28
|
```ruby
|
29
29
|
k = SSHKey.new(File.read("~/.ssh/id_rsa"), :comment => "foo@bar.com")
|
data/lib/sshkey.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
|
1
3
|
require 'openssl'
|
2
4
|
require 'base64'
|
3
5
|
require 'digest/md5'
|
4
6
|
require 'digest/sha1'
|
7
|
+
require 'sshkey/exception'
|
5
8
|
|
6
9
|
class SSHKey
|
7
10
|
SSH_TYPES = {"rsa" => "ssh-rsa", "dsa" => "ssh-dss"}
|
@@ -46,31 +49,22 @@ class SSHKey
|
|
46
49
|
#
|
47
50
|
def valid_ssh_public_key?(ssh_public_key)
|
48
51
|
ssh_type, encoded_key = parse_ssh_public_key(ssh_public_key)
|
49
|
-
|
50
|
-
type = SSH_TYPES.invert[ssh_type]
|
51
|
-
prefix = [0,0,0,7].pack("C*")
|
52
|
-
decoded = Base64.decode64(encoded_key)
|
53
|
-
|
54
|
-
# Base64 decoding is too permissive, so we should validate if encoding is correct
|
55
|
-
return false unless Base64.encode64(decoded).gsub("\n", "") == encoded_key
|
56
|
-
return false unless decoded.sub!(/^#{prefix}#{ssh_type}/, "")
|
57
|
-
|
58
|
-
unpacked = decoded.unpack("C*")
|
59
|
-
data = []
|
60
|
-
index = 0
|
61
|
-
until unpacked[index].nil?
|
62
|
-
datum_size = from_byte_array unpacked[index..index+4-1], 4
|
63
|
-
index = index + 4
|
64
|
-
datum = from_byte_array unpacked[index..index+datum_size-1], datum_size
|
65
|
-
data << datum
|
66
|
-
index = index + datum_size
|
67
|
-
end
|
68
|
-
|
69
|
-
SSH_CONVERSION[type].size == data.size
|
52
|
+
SSH_CONVERSION[SSH_TYPES.invert[ssh_type]].size == unpacked_byte_array(ssh_type, encoded_key).size
|
70
53
|
rescue
|
71
54
|
false
|
72
55
|
end
|
73
56
|
|
57
|
+
# Bits
|
58
|
+
#
|
59
|
+
# Returns ssh public key bits or false depending on the validity of the public key provided
|
60
|
+
#
|
61
|
+
# ==== Parameters
|
62
|
+
# * ssh_public_key<~String> - "ssh-rsa AAAAB3NzaC1yc2EA...."
|
63
|
+
#
|
64
|
+
def ssh_public_key_bits(ssh_public_key)
|
65
|
+
unpacked_byte_array( *parse_ssh_public_key(ssh_public_key) ).last.size * 8
|
66
|
+
end
|
67
|
+
|
74
68
|
# Fingerprints
|
75
69
|
#
|
76
70
|
# Accepts either a public or private key
|
@@ -96,9 +90,31 @@ class SSHKey
|
|
96
90
|
|
97
91
|
private
|
98
92
|
|
93
|
+
def unpacked_byte_array(ssh_type, encoded_key)
|
94
|
+
prefix = [0,0,0,7].pack("C*")
|
95
|
+
decoded = Base64.decode64(encoded_key)
|
96
|
+
|
97
|
+
# Base64 decoding is too permissive, so we should validate if encoding is correct
|
98
|
+
if Base64.encode64(decoded).gsub("\n", "") != encoded_key || decoded.sub!(/^#{prefix}#{ssh_type}/, "").nil?
|
99
|
+
raise PublicKeyError, "validation error"
|
100
|
+
end
|
101
|
+
|
102
|
+
unpacked = decoded.unpack("C*")
|
103
|
+
data = []
|
104
|
+
index = 0
|
105
|
+
until unpacked[index].nil?
|
106
|
+
datum_size = from_byte_array unpacked[index..index+4-1], 4
|
107
|
+
index = index + 4
|
108
|
+
datum = from_byte_array unpacked[index..index+datum_size-1], datum_size
|
109
|
+
data << datum
|
110
|
+
index = index + datum_size
|
111
|
+
end
|
112
|
+
return data
|
113
|
+
end
|
114
|
+
|
99
115
|
def from_byte_array(byte_array, expected_size = nil)
|
116
|
+
raise PublicKeyError, "byte array too short" if !expected_size.nil? && expected_size != byte_array.size
|
100
117
|
num = 0
|
101
|
-
raise "Byte array too short" if !expected_size.nil? && expected_size != byte_array.size
|
102
118
|
byte_array.reverse.each_with_index do |item, index|
|
103
119
|
num += item * 256**(index)
|
104
120
|
end
|
@@ -116,8 +132,9 @@ class SSHKey
|
|
116
132
|
def parse_ssh_public_key(public_key)
|
117
133
|
parsed = public_key.split(" ")
|
118
134
|
parsed.each_with_index do |el, index|
|
119
|
-
|
135
|
+
return parsed[index..(index+1)] if SSH_TYPES.invert[el]
|
120
136
|
end
|
137
|
+
raise PublicKeyError, "cannot determine key type"
|
121
138
|
end
|
122
139
|
end
|
123
140
|
|
@@ -192,7 +209,7 @@ class SSHKey
|
|
192
209
|
|
193
210
|
# Determine the length (bits) of the key as an integer
|
194
211
|
def bits
|
195
|
-
|
212
|
+
self.class.ssh_public_key_bits(ssh_public_key)
|
196
213
|
end
|
197
214
|
|
198
215
|
# Randomart
|
data/lib/sshkey/version.rb
CHANGED
data/test/sshkey_test.rb
CHANGED
@@ -253,6 +253,30 @@ EOF
|
|
253
253
|
assert !SSHKey.valid_ssh_public_key?(invalid5)
|
254
254
|
end
|
255
255
|
|
256
|
+
def test_ssh_public_key_bits
|
257
|
+
expected1 = "ssh-rsa #{SSH_PUBLIC_KEY1} me@example.com"
|
258
|
+
expected2 = "ssh-rsa #{SSH_PUBLIC_KEY2} me@example.com"
|
259
|
+
expected3 = "ssh-dss #{SSH_PUBLIC_KEY3} me@example.com"
|
260
|
+
expected4 = "ssh-rsa #{SSH_PUBLIC_KEY1}"
|
261
|
+
expected5 = %Q{from="trusted.eng.cam.ac.uk",no-port-forwarding,no-pty ssh-rsa #{SSH_PUBLIC_KEY1}}
|
262
|
+
invalid1 = "#{SSH_PUBLIC_KEY1} me@example.com"
|
263
|
+
|
264
|
+
assert_equal 2048, SSHKey.ssh_public_key_bits(expected1)
|
265
|
+
assert_equal 2048, SSHKey.ssh_public_key_bits(expected2)
|
266
|
+
assert_equal 1024, SSHKey.ssh_public_key_bits(expected3)
|
267
|
+
assert_equal 2048, SSHKey.ssh_public_key_bits(expected4)
|
268
|
+
assert_equal 2048, SSHKey.ssh_public_key_bits(expected5)
|
269
|
+
assert_equal 512, SSHKey.ssh_public_key_bits(SSHKey.generate(:bits => 512).ssh_public_key)
|
270
|
+
|
271
|
+
exception1 = assert_raises(SSHKey::PublicKeyError) { SSHKey.ssh_public_key_bits( expected1.gsub('A','.') ) }
|
272
|
+
exception2 = assert_raises(SSHKey::PublicKeyError) { SSHKey.ssh_public_key_bits( expected1[0..-20] ) }
|
273
|
+
exception3 = assert_raises(SSHKey::PublicKeyError) { SSHKey.ssh_public_key_bits(invalid1) }
|
274
|
+
|
275
|
+
assert_equal( "validation error", exception1.message )
|
276
|
+
assert_equal( "byte array too short", exception2.message )
|
277
|
+
assert_equal( "cannot determine key type", exception3.message )
|
278
|
+
end
|
279
|
+
|
256
280
|
def test_exponent
|
257
281
|
assert_equal 35, @key1.key_object.e.to_i
|
258
282
|
assert_equal 35, @key2.key_object.e.to_i
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sshkey
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-09-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -41,6 +41,7 @@ files:
|
|
41
41
|
- README.md
|
42
42
|
- Rakefile
|
43
43
|
- lib/sshkey.rb
|
44
|
+
- lib/sshkey/exception.rb
|
44
45
|
- lib/sshkey/version.rb
|
45
46
|
- sshkey.gemspec
|
46
47
|
- test/sshkey_test.rb
|
@@ -70,4 +71,3 @@ specification_version: 3
|
|
70
71
|
summary: SSH private/public key generator in Ruby
|
71
72
|
test_files:
|
72
73
|
- test/sshkey_test.rb
|
73
|
-
has_rdoc:
|