orthrus-ssh 0.5.1 → 0.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.
@@ -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