sshkey 1.5.1 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|