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.
@@ -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: