sshkey 1.5.1 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -11,3 +11,6 @@ rvm:
11
11
  - jruby-head
12
12
  - rbx-18mode
13
13
  - rbx-19mode
14
+ matrix:
15
+ allow_failures:
16
+ - rvm: ruby-head
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")
@@ -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
- break parsed[index..(index+1)] if !SSH_TYPES.invert[el].nil?
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
- key_object.to_text.match(/Private-Key:\s\((\d*)\sbit\)/)[1].to_i
212
+ self.class.ssh_public_key_bits(ssh_public_key)
196
213
  end
197
214
 
198
215
  # Randomart
@@ -0,0 +1,3 @@
1
+ class SSHKey
2
+ class PublicKeyError < StandardError; end
3
+ end
@@ -1,3 +1,3 @@
1
1
  class SSHKey
2
- VERSION = "1.5.1"
2
+ VERSION = "1.6.0"
3
3
  end
@@ -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.5.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-04-03 00:00:00.000000000 Z
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: