orthrus-ssh 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,14 +5,13 @@ README.txt
5
5
  Rakefile
6
6
  bin/orthrus
7
7
  lib/orthrus.rb
8
- lib/orthrus/key.rb
9
- lib/orthrus/key_holder.rb
10
8
  lib/orthrus/ssh.rb
11
9
  lib/orthrus/ssh/agent.rb
12
10
  lib/orthrus/ssh/buffer.rb
13
11
  lib/orthrus/ssh/dsa.rb
14
12
  lib/orthrus/ssh/http_agent.rb
15
13
  lib/orthrus/ssh/key.rb
14
+ lib/orthrus/ssh/key_manager.rb
16
15
  lib/orthrus/ssh/public_key_set.rb
17
16
  lib/orthrus/ssh/rack_app.rb
18
17
  lib/orthrus/ssh/rsa.rb
@@ -22,10 +21,12 @@ test/data/id_dsa
22
21
  test/data/id_dsa.pub
23
22
  test/data/id_rsa
24
23
  test/data/id_rsa.pub
24
+ test/orthrus_case.rb
25
25
  test/sessions.rb
26
26
  test/test_orthrus_ssh_agent.rb
27
27
  test/test_orthrus_ssh_dsa.rb
28
28
  test/test_orthrus_ssh_http_agent.rb
29
+ test/test_orthrus_ssh_key_manager.rb
29
30
  test/test_orthrus_ssh_public_key_set.rb
30
31
  test/test_orthrus_ssh_rackapp.rb
31
32
  test/test_orthrus_ssh_rsa.rb
data/README.txt CHANGED
@@ -30,6 +30,7 @@ A user authentication system built on SSH's key
30
30
 
31
31
  After checking out the source, run:
32
32
 
33
+ $ gem install hoe
33
34
  $ rake newb
34
35
 
35
36
  This task will install any missing dependencies, run the tests/specs,
data/Rakefile CHANGED
@@ -3,7 +3,7 @@
3
3
  require 'rubygems'
4
4
  require 'hoe'
5
5
 
6
- Hoe.plugin :minitest
6
+ Hoe.plugin :minitest
7
7
 
8
8
  Hoe.spec 'orthrus-ssh' do
9
9
  developer('Evan Phoenix', 'evan@phx.io')
@@ -7,7 +7,7 @@ require 'orthrus/ssh/dsa'
7
7
  require 'orthrus/ssh/utils'
8
8
 
9
9
  module Orthrus::SSH
10
- VERSION = '0.5.1'
10
+ VERSION = '0.6.0'
11
11
 
12
12
  def self.load_private(path)
13
13
  data = File.read(path)
@@ -117,7 +117,7 @@ module Orthrus::SSH
117
117
 
118
118
  # Using the agent and the given public key, sign the given data. The
119
119
  # signature is returned in SSH2 format.
120
- def sign(key, data)
120
+ def sign(key, data, b64armor=false)
121
121
  type, reply = send_and_wait(SSH2_AGENT_SIGN_REQUEST,
122
122
  :string, Buffer.from(:key, key),
123
123
  :string, data,
@@ -130,13 +130,12 @@ module Orthrus::SSH
130
130
  end
131
131
 
132
132
  b = Buffer.new reply.read_string
133
- [b.read_string, b.read_string]
134
- end
133
+ type = b.read_string
134
+ sign = b.read_string
135
135
 
136
- def hexsign(key, data)
137
- type, sig = sign key, data
136
+ sign = Utils.encode64 sign if b64armor
138
137
 
139
- [type, [sig].pack("m").gsub("\n","")]
138
+ [type, sign]
140
139
  end
141
140
 
142
141
  private
@@ -50,7 +50,7 @@ module Orthrus::SSH
50
50
 
51
51
  class DSAPublicKey < PublicKey
52
52
  def self.parse(data)
53
- raw = data.unpack("m").first
53
+ raw = Utils.decode64 data
54
54
 
55
55
  b = Buffer.new raw
56
56
 
@@ -7,16 +7,16 @@ require 'net/http'
7
7
 
8
8
  module Orthrus::SSH
9
9
  class HTTPAgent
10
- def initialize(url)
10
+ def initialize(url, key_manager=nil)
11
11
  @url = url
12
- @keys = []
12
+ @key_manager ||= KeyManager.new
13
13
  @access_token = nil
14
14
  end
15
15
 
16
16
  attr_reader :access_token
17
17
 
18
- def add_key(key)
19
- @keys << Orthrus::SSH.load_private(key)
18
+ def load_key(key)
19
+ @key_manager.load_key key
20
20
  end
21
21
 
22
22
  def check(user, k)
@@ -48,11 +48,11 @@ module Orthrus::SSH
48
48
  end
49
49
 
50
50
  def start(user)
51
- @keys.each do |k|
51
+ @key_manager.each_key do |k|
52
52
  sid, data = check(user, k)
53
53
  next unless sid
54
54
 
55
- sig = k.hexsign(data)
55
+ sig = @key_manager.sign k, data, true
56
56
 
57
57
  token = negotiate(k, sid, sig)
58
58
  if token
@@ -61,22 +61,6 @@ module Orthrus::SSH
61
61
  end
62
62
  end
63
63
 
64
- if Agent.available?
65
- agent = Agent.connect
66
- agent.identities.each do |k|
67
- sid, data = check(user, k)
68
- next unless sid
69
-
70
- type, sig = agent.hexsign k, data
71
-
72
- token = negotiate(k, sid, sig)
73
- if token
74
- @access_token = token
75
- return
76
- end
77
- end
78
- end
79
-
80
64
  raise "Unable to find key to authenticate with"
81
65
  end
82
66
  end
@@ -4,10 +4,11 @@ module Orthrus::SSH
4
4
  @key = k
5
5
  @digest = digest
6
6
  @comment = nil
7
+ @source = nil
7
8
  end
8
9
 
9
10
  attr_reader :key
10
- attr_accessor :comment
11
+ attr_accessor :comment, :source
11
12
 
12
13
  def rsa?
13
14
  @key.kind_of? OpenSSL::PKey::RSA
@@ -25,26 +26,25 @@ module Orthrus::SSH
25
26
  def inspect
26
27
  "#<#{self.class} #{fingerprint}>"
27
28
  end
28
- end
29
29
 
30
- class PrivateKey < Key
31
- def sign(data)
32
- @key.sign @digest.new, data
30
+ def ==(o)
31
+ return false unless o.kind_of? Orthrus::SSH::Key
32
+ @key.to_pem == o.key.to_pem
33
33
  end
34
+ end
34
35
 
35
- def hexsign(data)
36
- [sign(data)].pack("m").gsub("\n","")
36
+ class PrivateKey < Key
37
+ def sign(data, b64armor=false)
38
+ s = @key.sign @digest.new, data
39
+ b64armor ? Utils.encode64(s) : s
37
40
  end
38
41
  end
39
42
 
40
43
  class PublicKey < Key
41
- def verify(sign, data)
44
+ def verify(sign, data, b64armor=false)
45
+ sign = Utils.decode64 sign if b64armor
42
46
  @key.verify @digest.new, sign, data
43
47
  end
44
-
45
- def hexverify(sign, data)
46
- verify sign.unpack("m").first, data
47
- end
48
48
  end
49
49
 
50
50
  end
@@ -0,0 +1,59 @@
1
+ require 'orthrus/ssh'
2
+ require 'orthrus/ssh/agent'
3
+
4
+ module Orthrus::SSH
5
+ class KeyManager
6
+ def initialize(try_agent=true)
7
+ @keys = []
8
+
9
+ agent = nil
10
+ if try_agent and Agent.available?
11
+ begin
12
+ agent = Agent.connect
13
+ rescue IOError
14
+ # ignore
15
+ end
16
+ end
17
+
18
+ @agent = agent
19
+ end
20
+
21
+ attr_reader :keys
22
+
23
+ def add_key(key)
24
+ @keys << key
25
+ end
26
+
27
+ def load_key(path)
28
+ add_key Orthrus::SSH.load_private(path)
29
+ end
30
+
31
+ def agent_identities
32
+ @agent && @agent.identities
33
+ end
34
+
35
+ def each_key
36
+ @keys.each { |x| yield x }
37
+ if @agent
38
+ @agent.identities.each do |x|
39
+ x.source = @agent
40
+ yield x
41
+ end
42
+ end
43
+ end
44
+
45
+ def sign(key, data, b64armor=false)
46
+ if key.source.kind_of? Agent
47
+ _, sign = key.source.sign key, data
48
+ else
49
+ sign = key.sign data
50
+ end
51
+
52
+ if b64armor
53
+ Utils.encode64 sign
54
+ else
55
+ sign
56
+ end
57
+ end
58
+ end
59
+ end
@@ -52,7 +52,7 @@ module Orthrus::SSH
52
52
 
53
53
  sig = req.params['sig']
54
54
 
55
- if pub.hexverify(sig, nonce)
55
+ if pub.verify(sig, nonce, true)
56
56
  form "code=verified&access_token=1"
57
57
  else
58
58
  form "code=fail"
@@ -16,7 +16,7 @@ module Orthrus::SSH
16
16
 
17
17
  return d unless base64
18
18
 
19
- [d].pack("m").gsub("\n","")
19
+ Utils.encode64 d
20
20
  end
21
21
 
22
22
  def type
@@ -30,7 +30,7 @@ module Orthrus::SSH
30
30
 
31
31
  class RSAPublicKey < PublicKey
32
32
  def self.parse(data)
33
- raw = data.unpack("m").first
33
+ raw = Utils.decode64 data
34
34
 
35
35
  b = Buffer.new raw
36
36
 
@@ -22,5 +22,12 @@ module Orthrus::SSH
22
22
  OpenSSL::Digest::SHA1.hexdigest data
23
23
  end
24
24
 
25
+ def self.encode64(data)
26
+ [data].pack("m").gsub("\n","")
27
+ end
28
+
29
+ def self.decode64(data)
30
+ data.unpack("m").first
31
+ end
25
32
  end
26
33
  end
@@ -0,0 +1,22 @@
1
+ class OrthrusTestCase < MiniTest::Unit::TestCase
2
+ DATA_PATH = File.expand_path "../data", __FILE__
3
+
4
+ def setup
5
+ @id_rsa = File.join DATA_PATH, "id_rsa"
6
+ @rsa = Orthrus::SSH.load_private @id_rsa
7
+
8
+ @rsa_pub = Orthrus::SSH.load_public File.join(DATA_PATH, "id_rsa.pub")
9
+ end
10
+
11
+ def added_to_agent(path)
12
+ begin
13
+ `chmod 0600 #{path}; ssh-add #{path} 2>&1`
14
+ fail unless $?.exitstatus == 0
15
+
16
+ yield
17
+ ensure
18
+ `ssh-add -d #{path} 2>&1`
19
+ end
20
+ end
21
+
22
+ end
@@ -7,11 +7,12 @@ require 'orthrus/ssh/http_agent'
7
7
  require 'stringio'
8
8
 
9
9
  require 'sessions'
10
+ require 'orthrus_case'
10
11
 
11
- class TestOrthrusSSHHTTPAgent < MiniTest::Unit::TestCase
12
- DATA_PATH = File.expand_path "../data", __FILE__
13
-
12
+ class TestOrthrusSSHHTTPAgent < OrthrusTestCase
14
13
  def setup
14
+ super
15
+
15
16
  @@app ||= Orthrus::SSH::RackApp.new OrthrusTestSessions.new
16
17
  @app = @@app
17
18
  @@server ||= begin
@@ -25,10 +26,6 @@ class TestOrthrusSSHHTTPAgent < MiniTest::Unit::TestCase
25
26
 
26
27
  sleep 1
27
28
 
28
- @id_rsa = File.join DATA_PATH, "id_rsa"
29
- @rsa = Orthrus::SSH.load_private @id_rsa
30
-
31
- @rsa_pub = Orthrus::SSH.load_public File.join(DATA_PATH, "id_rsa.pub")
32
29
  @app.sessions.add_key "evan", @rsa_pub
33
30
  end
34
31
 
@@ -41,7 +38,7 @@ class TestOrthrusSSHHTTPAgent < MiniTest::Unit::TestCase
41
38
  url = URI.parse "http://127.0.0.1:8787/"
42
39
  h = Orthrus::SSH::HTTPAgent.new url
43
40
 
44
- h.add_key @id_rsa
41
+ h.load_key @id_rsa
45
42
 
46
43
  h.start "evan"
47
44
 
@@ -51,11 +48,7 @@ class TestOrthrusSSHHTTPAgent < MiniTest::Unit::TestCase
51
48
  def test_access_token_from_agent
52
49
  skip unless Orthrus::SSH::Agent.available?
53
50
 
54
- begin
55
- `chmod 0600 #{@id_rsa}; ssh-add #{@id_rsa} 2>&1`
56
-
57
- fail unless $?.exitstatus == 0
58
-
51
+ added_to_agent @id_rsa do
59
52
  assert Orthrus::SSH::Agent.connect.identities.any? { |id|
60
53
  id.public_identity == @rsa_pub.public_identity
61
54
  }
@@ -66,8 +59,6 @@ class TestOrthrusSSHHTTPAgent < MiniTest::Unit::TestCase
66
59
  h.start "evan"
67
60
 
68
61
  assert_equal "1", h.access_token
69
- ensure
70
- `ssh-add -d #{@id_rsa} 2>&1`
71
62
  end
72
63
  end
73
64
  end
@@ -0,0 +1,77 @@
1
+ require 'minitest/unit'
2
+ require 'minitest/autorun'
3
+
4
+ require 'orthrus/ssh/key_manager'
5
+
6
+ require 'orthrus_case'
7
+
8
+ class TestOrthrusSSHKeyManager < OrthrusTestCase
9
+ def setup
10
+ super
11
+
12
+ @kg = Orthrus::SSH::KeyManager.new
13
+ end
14
+
15
+ def test_add_key
16
+ @kg.add_key @rsa
17
+
18
+ assert_equal @rsa, @kg.keys.first
19
+ end
20
+
21
+ def test_load_key
22
+ @kg.load_key @id_rsa
23
+ assert_equal @rsa, @kg.keys.first
24
+ end
25
+
26
+ def test_agent_identities
27
+ kg = @kg.agent_identities.first
28
+ assert_kind_of Orthrus::SSH::Key, kg
29
+ end
30
+
31
+ def test_each_key
32
+ @kg.add_key @rsa
33
+
34
+ keys = []
35
+ @kg.each_key { |x| keys << x }
36
+
37
+ assert keys.include?(@rsa)
38
+ end
39
+
40
+ def test_each_keys_with_agent
41
+ keys = []
42
+
43
+ added_to_agent @id_rsa do
44
+ @kg.each_key { |x| keys << x }
45
+ end
46
+
47
+ assert keys.include?(@rsa_pub)
48
+ end
49
+
50
+ def test_sign
51
+ @kg.add_key @rsa
52
+
53
+ data = "hello"
54
+ sign = @kg.sign @rsa, data
55
+
56
+ assert @rsa_pub.verify(sign, data)
57
+ end
58
+
59
+ def test_sign_with_agent
60
+ added_to_agent @id_rsa do
61
+ data = "hello"
62
+
63
+ id = nil
64
+ @kg.each_key do |k|
65
+ if k == @rsa_pub
66
+ id = k
67
+ break
68
+ end
69
+ end
70
+
71
+ assert id
72
+
73
+ sign = @kg.sign id, data
74
+ assert @rsa_pub.verify(sign, data)
75
+ end
76
+ end
77
+ end
@@ -73,7 +73,7 @@ class TestOrthrusSSHRackApp < MiniTest::Unit::TestCase
73
73
 
74
74
  data = params['nonce']
75
75
 
76
- sig = Rack::Utils.escape @rsa.hexsign(data)
76
+ sig = Rack::Utils.escape @rsa.sign(data, true)
77
77
 
78
78
  env["QUERY_STRING"] = "state=signed&sig=#{sig}&session_id=1"
79
79
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: orthrus-ssh
3
3
  version: !ruby/object:Gem::Version
4
- hash: 9
4
+ hash: 7
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 5
9
- - 1
10
- version: 0.5.1
8
+ - 6
9
+ - 0
10
+ version: 0.6.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Evan Phoenix
@@ -82,14 +82,13 @@ files:
82
82
  - Rakefile
83
83
  - bin/orthrus
84
84
  - lib/orthrus.rb
85
- - lib/orthrus/key.rb
86
- - lib/orthrus/key_holder.rb
87
85
  - lib/orthrus/ssh.rb
88
86
  - lib/orthrus/ssh/agent.rb
89
87
  - lib/orthrus/ssh/buffer.rb
90
88
  - lib/orthrus/ssh/dsa.rb
91
89
  - lib/orthrus/ssh/http_agent.rb
92
90
  - lib/orthrus/ssh/key.rb
91
+ - lib/orthrus/ssh/key_manager.rb
93
92
  - lib/orthrus/ssh/public_key_set.rb
94
93
  - lib/orthrus/ssh/rack_app.rb
95
94
  - lib/orthrus/ssh/rsa.rb
@@ -99,10 +98,12 @@ files:
99
98
  - test/data/id_dsa.pub
100
99
  - test/data/id_rsa
101
100
  - test/data/id_rsa.pub
101
+ - test/orthrus_case.rb
102
102
  - test/sessions.rb
103
103
  - test/test_orthrus_ssh_agent.rb
104
104
  - test/test_orthrus_ssh_dsa.rb
105
105
  - test/test_orthrus_ssh_http_agent.rb
106
+ - test/test_orthrus_ssh_key_manager.rb
106
107
  - test/test_orthrus_ssh_public_key_set.rb
107
108
  - test/test_orthrus_ssh_rackapp.rb
108
109
  - test/test_orthrus_ssh_rsa.rb
@@ -145,6 +146,7 @@ test_files:
145
146
  - test/test_orthrus_ssh_agent.rb
146
147
  - test/test_orthrus_ssh_dsa.rb
147
148
  - test/test_orthrus_ssh_http_agent.rb
149
+ - test/test_orthrus_ssh_key_manager.rb
148
150
  - test/test_orthrus_ssh_public_key_set.rb
149
151
  - test/test_orthrus_ssh_rackapp.rb
150
152
  - test/test_orthrus_ssh_rsa.rb
@@ -1,12 +0,0 @@
1
- require 'openssl'
2
-
3
- module Orthrus
4
-
5
- CURVE = "secp224k1"
6
-
7
- class Key
8
- def self.new_pair
9
-
10
- end
11
- end
12
- end
@@ -1,15 +0,0 @@
1
- module Orthrus
2
- class KeyHolder
3
- def initialize
4
- @keys = {}
5
- end
6
-
7
- def add_key(name, key)
8
- @keys[name] = key
9
- end
10
-
11
- def key(name)
12
- @keys[name]
13
- end
14
- end
15
- end