Skip to content
Browse files

First version of Orthrus

  • Loading branch information...
0 parents commit cc7f3cea2862255c44c325a375ed5cf273f27a31 @evanphx committed Mar 24, 2012
23 .autotest
@@ -0,0 +1,23 @@
+# -*- ruby -*-
+
+require 'autotest/restart'
+
+# Autotest.add_hook :initialize do |at|
+# at.extra_files << "../some/external/dependency.rb"
+#
+# at.libs << ":../some/external"
+#
+# at.add_exception 'vendor'
+#
+# at.add_mapping(/dependency.rb/) do |f, _|
+# at.files_matching(/test_.*rb$/)
+# end
+#
+# %w(TestA TestB).each do |klass|
+# at.extra_class_map[klass] = "test/test_misc.rb"
+# end
+# end
+
+# Autotest.add_hook :run_command do |at|
+# system "rake build"
+# end
6 History.txt
@@ -0,0 +1,6 @@
+=== 1.0.0 / 2012-03-18
+
+* 1 major enhancement
+
+ * Birthday!
+
8 Manifest.txt
@@ -0,0 +1,8 @@
+.autotest
+History.txt
+Manifest.txt
+README.txt
+Rakefile
+bin/orthrus
+lib/orthrus.rb
+test/test_orthrus.rb
57 README.txt
@@ -0,0 +1,57 @@
+= orthrus
+
+* FIX (url)
+
+== DESCRIPTION:
+
+FIX (describe your package)
+
+== FEATURES/PROBLEMS:
+
+* FIX (list of features or problems)
+
+== SYNOPSIS:
+
+ FIX (code sample of usage)
+
+== REQUIREMENTS:
+
+* FIX (list of requirements)
+
+== INSTALL:
+
+* FIX (sudo gem install, anything else)
+
+== DEVELOPERS:
+
+After checking out the source, run:
+
+ $ rake newb
+
+This task will install any missing dependencies, run the tests/specs,
+and generate the RDoc.
+
+== LICENSE:
+
+(The MIT License)
+
+Copyright (c) 2012 FIX
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12 Rakefile
@@ -0,0 +1,12 @@
+# -*- ruby -*-
+
+require 'rubygems'
+require 'hoe'
+
+ Hoe.plugin :minitest
+
+Hoe.spec 'orthrus' do
+ developer('Evan Phoenix', 'evan@phx.io')
+end
+
+# vim: syntax=ruby
3 bin/orthrus
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+
+abort "you need to write me"
3 lib/orthrus.rb
@@ -0,0 +1,3 @@
+class Orthrus
+ VERSION = '1.0.0'
+end
12 lib/orthrus/key.rb
@@ -0,0 +1,12 @@
+require 'openssl'
+
+module Orthrus
+
+ CURVE = "secp224k1"
+
+ class Key
+ def self.new_pair
+
+ end
+ end
+end
15 lib/orthrus/key_holder.rb
@@ -0,0 +1,15 @@
+module Orthrus
+ class KeyHolder
+ def initialize
+ @keys = {}
+ end
+
+ def add_key(name, key)
+ @keys[name] = key
+ end
+
+ def key(name)
+ @keys[name]
+ end
+ end
+end
40 lib/orthrus/ssh.rb
@@ -0,0 +1,40 @@
+require 'openssl'
+
+module Orthrus; end
+
+require 'orthrus/ssh/rsa'
+require 'orthrus/ssh/dsa'
+require 'orthrus/ssh/utils'
+
+module Orthrus::SSH
+ def self.load_private(path)
+ data = File.read(path)
+ if data.index("-----BEGIN RSA PRIVATE KEY-----") == 0
+ k = OpenSSL::PKey::RSA.new data
+ return RSAPrivateKey.new k
+ elsif data.index("-----BEGIN DSA PRIVATE KEY-----") == 0
+ k = OpenSSL::PKey::DSA.new data
+ return DSAPrivateKey.new k
+ else
+ raise "Unknown key type in '#{path}'"
+ end
+ end
+
+ def self.parse_public(data)
+ type, key, comment = data.split " ", 3
+
+ case type
+ when "ssh-rsa"
+ RSAPublicKey.parse key
+ when "ssh-dss"
+ DSAPublicKey.parse key
+ else
+ raise "Unknown key type - #{type}"
+ end
+ end
+
+ def self.load_public(path)
+ parse_public File.read(path)
+ end
+
+end
43 lib/orthrus/ssh/dsa.rb
@@ -0,0 +1,43 @@
+require 'orthrus/ssh/key'
+
+module Orthrus::SSH
+ class DSAPrivateKey < PrivateKey
+ def initialize(k)
+ super k, OpenSSL::Digest::DSS1
+ end
+
+ def public_identity
+ d = Utils.write_string("ssh-dss") +
+ Utils.write_bignum(@key.p) +
+ Utils.write_bignum(@key.q) +
+ Utils.write_bignum(@key.g) +
+ Utils.write_bignum(@key.pub_key)
+
+ [d].pack("m").gsub("\n","")
+ end
+ end
+
+ class DSAPublicKey < PublicKey
+ def self.parse(data)
+ raw = data.unpack("m").first
+
+ type = Utils.read_string(raw)
+ unless type == "ssh-dss"
+ raise "Unvalid key data"
+ end
+
+ k = OpenSSL::PKey::DSA.new
+ k.p = Utils.read_bignum raw
+ k.q = Utils.read_bignum raw
+ k.g = Utils.read_bignum raw
+ k.pub_key = Utils.read_bignum raw
+
+ new k
+ end
+
+ def initialize(k)
+ super k, OpenSSL::Digest::DSS1
+ end
+ end
+
+end
44 lib/orthrus/ssh/http_agent.rb
@@ -0,0 +1,44 @@
+require 'orthrus/ssh'
+require 'uri'
+
+require 'net/http'
+
+module Orthrus::SSH
+ class HTTPAgent
+ def initialize(url)
+ @url = url
+ @keys = []
+ @access_token = nil
+ end
+
+ attr_reader :access_token
+
+ def add_key(key)
+ @keys << Orthrus::SSH.load_private(key)
+ end
+
+ def start
+ @keys.each do |k|
+ id = Rack::Utils.escape(k.public_identity)
+
+ url = @url + "?state=find&id=#{id}"
+ response = Net::HTTP.get_response url
+ params = Rack::Utils.parse_query response.body
+
+ sid = params['session_id']
+ data = params['nonce']
+
+ sig = Rack::Utils.escape [k.sign(data)].pack("m")
+
+ url = @url + "?state=signed&sig=#{sig}&session_id=#{sid}"
+
+ response = Net::HTTP.get_response url
+ params = Rack::Utils.parse_query response.body
+
+ if params['code'] == "verified"
+ @access_token = params['access_token']
+ end
+ end
+ end
+ end
+end
29 lib/orthrus/ssh/key.rb
@@ -0,0 +1,29 @@
+module Orthrus::SSH
+ class Key
+ def initialize(k, digest)
+ @key = k
+ @digest = digest
+ end
+
+ def rsa?
+ @key.kind_of? OpenSSL::PKey::RSA
+ end
+
+ def dsa?
+ @key.kind_of? OpenSSL::PKey::DSA
+ end
+ end
+
+ class PrivateKey < Key
+ def sign(data)
+ @key.sign @digest.new, data
+ end
+ end
+
+ class PublicKey < Key
+ def verify(sign, data)
+ @key.verify @digest.new, sign, data
+ end
+ end
+
+end
30 lib/orthrus/ssh/public_key_set.rb
@@ -0,0 +1,30 @@
+require 'orthrus/ssh'
+
+module Orthrus::SSH
+ class PublicKeySet
+ def self.load_file(path)
+ keys = {}
+
+ File.readlines(path).each do |x|
+ type, dig, comment = x.split(" ", 3)
+
+ keys[dig] = Orthrus::SSH.parse_public x
+ end
+
+ new keys
+ end
+
+ def initialize(keys)
+ @keys = keys
+ end
+
+ def find(dig)
+ @keys[dig]
+ end
+
+ def num_keys
+ @keys.size
+ end
+ end
+end
+
55 lib/orthrus/ssh/rack_app.rb
@@ -0,0 +1,55 @@
+require 'orthrus/ssh'
+require 'rack'
+
+module Orthrus::SSH
+ class RackApp
+ def initialize(sessions)
+ @keys = {}
+ @sessions = sessions
+ end
+
+ attr_reader :keys
+
+ def call(env)
+ req = Rack::Request.new(env)
+
+ case req.params['state']
+ when 'find'
+ find req
+ when 'signed'
+ verify req
+ else
+ [500, {}, ["unknown state"]]
+ end
+ end
+
+ def form(body)
+ [200,
+ { "Content-Type" => "application/x-www-form-urlencoded" },
+ [body]
+ ]
+ end
+
+ def find(req)
+ id = req.params["id"]
+ unless pub = @keys[id]
+ return form("code=unknown")
+ end
+
+ session, nonce = @sessions.new_session(pub)
+
+ form "code=check&session_id=#{session}&nonce=#{nonce}"
+ end
+
+ def verify(req)
+ id = req.params["session_id"].to_i
+ nonce, pub = @sessions.find_session(id)
+
+ sig = req.params['sig'].unpack("m").first
+
+ if pub.verify(sig, nonce)
+ form "code=verified&access_token=1"
+ end
+ end
+ end
+end
38 lib/orthrus/ssh/rsa.rb
@@ -0,0 +1,38 @@
+require 'orthrus/ssh/key'
+
+module Orthrus::SSH
+ class RSAPrivateKey < PrivateKey
+ def initialize(k)
+ super k, OpenSSL::Digest::SHA1
+ end
+
+ def public_identity
+ d = Utils.write_string("ssh-rsa") +
+ Utils.write_bignum(@key.e) +
+ Utils.write_bignum(@key.n)
+
+ [d].pack("m").gsub("\n","")
+ end
+ end
+
+ class RSAPublicKey < PublicKey
+ def self.parse(data)
+ raw = data.unpack("m").first
+
+ type = Utils.read_string(raw)
+ unless type == "ssh-rsa"
+ raise "Unvalid key data"
+ end
+
+ k = OpenSSL::PKey::RSA.new
+ k.e = Utils.read_bignum(raw)
+ k.n = Utils.read_bignum(raw)
+
+ new k
+ end
+
+ def initialize(k)
+ super k, OpenSSL::Digest::SHA1
+ end
+ end
+end
30 lib/orthrus/ssh/utils.rb
@@ -0,0 +1,30 @@
+module Orthrus::SSH
+ module Utils
+ def self.read_string(str)
+ i = str.slice!(0,4).unpack("N").first
+ str.slice! 0, i
+ end
+
+ def self.read_bignum(str)
+ OpenSSL::BN.new read_string(str), 2
+ end
+
+ def self.write_bignum(bn)
+ # Cribbed from net-ssh
+ if bn.zero?
+ return [0].pack("N")
+ else
+ buf = bn.to_s(2)
+ if buf[0][7] == 1
+ return [buf.length+1, 0, buf].pack("NCA*")
+ else
+ return [buf.length, buf].pack("NA*")
+ end
+ end
+ end
+
+ def self.write_string(str)
+ [str.size].pack("N") + str
+ end
+ end
+end
2 test/data/authorized_keys
@@ -0,0 +1,2 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAK8JRdWMWVcnFxRn8Pwhu6a7W8HQGPd93GY4JbKTTZjDrJEjKMnNUaFVZpu13MQwow8kaolqS+6tUaNDTFY+bymYcVWdQKWFAC/8MrOeJZq1GugNBxk9RC/VZBvW4nDTPgXhRftSxB7KBk0Tvk70fbwffaVGW7A+GBJKXYHdA/PdAAAAFQCjif6zl1zEITOq8us9WfvvBiQCkQAAAIEAqwFVbyhPWEVp5Ierq1syOJv4Hjqnbo/69mU/mFpnhIhreJ+uNIaFlEdwJY+WaQ7mUpWKCjhRMcDICQ+Lgq3zjaZhSe6F+SBHWI2ISDgRs73W2z42oECRrQoNJ9WZSF+6l+cHEHsD6Qv+B4cYtr7JJj7IGtMZzDO+zhQaUWTNeUoAAACADRsQHiTTr+50iRiiC26dq+W8GxksI4A5nESv3LpbqMI/SNzglK5OlLkKw403Ox7O1Hb+uZmmOZQmk1OTPjS3QPT+LC/lkUANpP7T7Bkh9RwUUVJF3qvhHDH94BDO3clZKdV558gSHi+T9P31h1gcw5rAVh+yK2ZyZGV/jP0r0z0= evan@aero.local
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDDgYlt6gUVZUZE4xgW2TRvi8HjVgrWZ5e6Av76/H3PzvZpsgHSZyDiU1rgVsgwfb1NmJiwflNpILLprSmp3RRqdOEKzEPgxdscQY1sJtTQcmdlWeIvN6KvmImPwV9krqtN8vji7Zqr0N3mcDmdK1MbQ56Cjx5l6/y9rYGLmIZvoLOLDVe3olOHjpapHQLHrQL3c/2Il5y+9aXR1c/gKFeEwwhRL6hcSIufBnanXqVGa5QNrfzw4si8oAIWDNfXDGRdFkxrnGxHOguj8hFeYXNtz6OHu2UPbvum9sUNHXdDHBYSTPqUJfdLvo49ZMqShcEgNrlBe8rx7ooPdDas40mH evan@aero.local
12 test/data/id_dsa
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBuwIBAAKBgQCvCUXVjFlXJxcUZ/D8Ibumu1vB0Bj3fdxmOCWyk02Yw6yRIyjJ
+zVGhVWabtdzEMKMPJGqJakvurVGjQ0xWPm8pmHFVnUClhQAv/DKzniWatRroDQcZ
+PUQv1WQb1uJw0z4F4UX7UsQeygZNE75O9H28H32lRluwPhgSSl2B3QPz3QIVAKOJ
+/rOXXMQhM6ry6z1Z++8GJAKRAoGBAKsBVW8oT1hFaeSHq6tbMjib+B46p26P+vZl
+P5haZ4SIa3ifrjSGhZRHcCWPlmkO5lKVigo4UTHAyAkPi4Kt842mYUnuhfkgR1iN
+iEg4EbO91ts+NqBAka0KDSfVmUhfupfnBxB7A+kL/geHGLa+ySY+yBrTGcwzvs4U
+GlFkzXlKAoGADRsQHiTTr+50iRiiC26dq+W8GxksI4A5nESv3LpbqMI/SNzglK5O
+lLkKw403Ox7O1Hb+uZmmOZQmk1OTPjS3QPT+LC/lkUANpP7T7Bkh9RwUUVJF3qvh
+HDH94BDO3clZKdV558gSHi+T9P31h1gcw5rAVh+yK2ZyZGV/jP0r0z0CFEY8QELo
+OZ+AwfX3cTZZTQMaNgAm
+-----END DSA PRIVATE KEY-----
1 test/data/id_dsa.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAK8JRdWMWVcnFxRn8Pwhu6a7W8HQGPd93GY4JbKTTZjDrJEjKMnNUaFVZpu13MQwow8kaolqS+6tUaNDTFY+bymYcVWdQKWFAC/8MrOeJZq1GugNBxk9RC/VZBvW4nDTPgXhRftSxB7KBk0Tvk70fbwffaVGW7A+GBJKXYHdA/PdAAAAFQCjif6zl1zEITOq8us9WfvvBiQCkQAAAIEAqwFVbyhPWEVp5Ierq1syOJv4Hjqnbo/69mU/mFpnhIhreJ+uNIaFlEdwJY+WaQ7mUpWKCjhRMcDICQ+Lgq3zjaZhSe6F+SBHWI2ISDgRs73W2z42oECRrQoNJ9WZSF+6l+cHEHsD6Qv+B4cYtr7JJj7IGtMZzDO+zhQaUWTNeUoAAACADRsQHiTTr+50iRiiC26dq+W8GxksI4A5nESv3LpbqMI/SNzglK5OlLkKw403Ox7O1Hb+uZmmOZQmk1OTPjS3QPT+LC/lkUANpP7T7Bkh9RwUUVJF3qvhHDH94BDO3clZKdV558gSHi+T9P31h1gcw5rAVh+yK2ZyZGV/jP0r0z0= evan@aero.local
27 test/data/id_rsa
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEAw4GJbeoFFWVGROMYFtk0b4vB41YK1meXugL++vx9z872abIB
+0mcg4lNa4FbIMH29TZiYsH5TaSCy6a0pqd0UanThCsxD4MXbHEGNbCbU0HJnZVni
+Lzeir5iJj8FfZK6rTfL44u2aq9Dd5nA5nStTG0Oego8eZev8va2Bi5iGb6Cziw1X
+t6JTh46WqR0Cx60C93P9iJecvvWl0dXP4ChXhMMIUS+oXEiLnwZ2p16lRmuUDa38
+8OLIvKACFgzX1wxkXRZMa5xsRzoLo/IRXmFzbc+jh7tlD277pvbFDR13QxwWEkz6
+lCX3S76OPWTKkoXBIDa5QXvK8e6KD3Q2rONJhwIDAQABAoIBAH0yRr+MTRUWdZlH
+k/WNwnZsGQ1r3CTQ0ejcYkx3xFl/P20QAPqr7/L/TgK7kBb9bmxye9UKEIAR4ICj
+0zpjyN8jWbmAdTdLfLTrhZTsiPuzR2Mv3BhAmH26QN0+B8iB0lFodtlbLuE4L+GR
+nFN5mw6qjqcs31qFdKRCp+KtGeoA8B2OvPLH6tBoLo4UPtB9OgZR9nhi6ZSkUuNP
+xzFU+QgtfBumGOfspwa8Cy+zatwI0Xw4O4FY5bIcp7n+E43pzMLVnkrBxQKouugR
+ldgW2PYm5yX/ffuKdjruSbXRb1Li+Ikx7rK76GfbBSzr33qH1PDqoOdPS4JPuAzj
+wh4YLoECgYEA6q6GJBFui3xFmN+CXVDGeNGXutxxVksMtbHw3P3xwmerfs3+KQD6
+UIb2r22dfu/dILAIkgsx6lsUpvpj0vyyav7mNVRv06qbcGs/zxTquTZXd7NA8+Gm
+ZPv2WKugdsVKHO22D40pFdBM13GPnYo2FGSIsvTC2oZHScJxpnySrKMCgYEA1UP9
+O0CrpYqI7Ktt3iPeX/a+tTCIcmxoWvKwq29dEg6Ck4JmLDcQETQms7tflbNyjX3L
+VgzmBEeRo8cz0YpvSHFmcgy7F0Rnk2ijb85ppJF0rJS0yBJOlHzoYsqIlbFWihl/
+QEy0sBMXQjcvLvIDpiQDzWNdLf7DS9aNKKAyec0CgYA+3rRW80iPG6K1eqM9Boe1
+FEk2qRm/yWlFP79MJMfgkc9SsDK3n2hvrEhn5NC9kdrGiAIzxcYAh5f3x7p4anQN
+z+2yOcWfieQMcN7uRic/qPwzuBTdgQUHpqxvQsNBLkdViqUsc1+fVWdQjD6yMLWe
+LvSkJIgS7MgqTWoO9O6CSwKBgDhB3yMqRB0/Fi+YaTsYKykVZelWDChjAIQ9UO1o
+SxzgRwGyfFFdlRd0smDnJKfQ1n8Ml/7zGBo45upVOg4kfoaVo3iicxgIK2pvR+3O
+fX+z/xsnfyjn62KwMH0fADi8tx9m6nKDyYZJAvGsrP2tSdkh1v7vHz1q3wm6ZzI4
+UBhhAoGAGno46WOio1hGOQoT5GFvyMPBsZAWvFdWSd14gOUzbX4cf+hRsBW4KqZy
+v3jMQ1YgYXf0yvXODKycuFP9uz9HcznwwahT23T0jYtDWkGeMqdoncpenarsfVjF
+PUQgoSpveFwz5UtsP3WhZKCl3/et7FDN1hbXqmWF1i6YbnjIJo8=
+-----END RSA PRIVATE KEY-----
1 test/data/id_rsa.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDDgYlt6gUVZUZE4xgW2TRvi8HjVgrWZ5e6Av76/H3PzvZpsgHSZyDiU1rgVsgwfb1NmJiwflNpILLprSmp3RRqdOEKzEPgxdscQY1sJtTQcmdlWeIvN6KvmImPwV9krqtN8vji7Zqr0N3mcDmdK1MbQ56Cjx5l6/y9rYGLmIZvoLOLDVe3olOHjpapHQLHrQL3c/2Il5y+9aXR1c/gKFeEwwhRL6hcSIufBnanXqVGa5QNrfzw4si8oAIWDNfXDGRdFkxrnGxHOguj8hFeYXNtz6OHu2UPbvum9sUNHXdDHBYSTPqUJfdLvo49ZMqShcEgNrlBe8rx7ooPdDas40mH evan@aero.local
46 test/test_orthrus_ssh_dsa.rb
@@ -0,0 +1,46 @@
+require 'minitest/autorun'
+require 'minitest/unit'
+
+require 'orthrus/ssh'
+
+class TestOrthrusSSHDSA < MiniTest::Unit::TestCase
+ DATA_PATH = File.expand_path "../data", __FILE__
+
+ def setup
+ @id_dsa = File.join DATA_PATH, "id_dsa"
+ @id_dsa_pub = File.join DATA_PATH, "id_dsa.pub"
+ end
+
+ def pub_key
+ Orthrus::SSH.load_public @id_dsa_pub
+ end
+
+ def priv_key
+ Orthrus::SSH.load_private @id_dsa
+ end
+
+ def test_load_private
+ s = Orthrus::SSH.load_private @id_dsa
+ assert_kind_of Orthrus::SSH::PrivateKey, s
+ assert s.dsa?, "key not dsa"
+ end
+
+ def test_load_public
+ s = Orthrus::SSH.load_public @id_dsa_pub
+ assert_kind_of Orthrus::SSH::PublicKey, s
+ assert s.dsa?, "key not dsa"
+ end
+
+ def test_sign_and_verify
+ data = "hello"
+
+ assert pub_key.verify(priv_key.sign(data), data)
+ end
+
+ def test_public_identity
+ s = Orthrus::SSH.load_private @id_dsa
+ check = File.read(@id_dsa_pub).split(" ")[1]
+
+ assert_equal check, s.public_identity
+ end
+end
59 test/test_orthrus_ssh_http_agent.rb
@@ -0,0 +1,59 @@
+require 'minitest/unit'
+
+require 'orthrus/ssh/rack_app'
+require 'orthrus/ssh/http_agent'
+
+class TestOrthrusSSHHTTPAgent < MiniTest::Unit::TestCase
+ DATA_PATH = File.expand_path "../data", __FILE__
+
+ class Sessions
+ def new_session(pub)
+ @pub = pub
+ [1, "secret"]
+ end
+
+ def find_session(id)
+ ["secret", @pub]
+ end
+
+ def access_token
+ "1"
+ end
+ end
+
+ def setup
+ @app = Orthrus::SSH::RackApp.new Sessions.new
+ @server = Rack::Server.new :app => @app, :Port => 8787
+
+ @old_stderr = $stderr
+ $stderr = StringIO.new
+
+ t = @thread = Thread.new do
+ @server.start { |s| Thread.current[:server] = s }
+ end
+
+ sleep 1
+
+ @id_rsa = File.join DATA_PATH, "id_rsa"
+ @rsa = Orthrus::SSH.load_private @id_rsa
+
+ @rsa_pub = Orthrus::SSH.load_public File.join(DATA_PATH, "id_rsa.pub")
+ @app.keys[@rsa.public_identity] = @rsa_pub
+ end
+
+ def teardown
+ @thread.kill
+ $stderr = @old_stderr
+ end
+
+ def test_access_token
+ url = URI.parse "http://127.0.0.1:8787/"
+ h = Orthrus::SSH::HTTPAgent.new url
+
+ h.add_key @id_rsa
+
+ h.start
+
+ assert_equal "1", h.access_token
+ end
+end
29 test/test_orthrus_ssh_public_key_set.rb
@@ -0,0 +1,29 @@
+require 'minitest/autorun'
+require 'minitest/unit'
+
+require 'orthrus/ssh/public_key_set'
+
+class TestOrthrusSSHPublicKeySet < MiniTest::Unit::TestCase
+ DATA_PATH = File.expand_path "../data", __FILE__
+
+ def setup
+ @auth_keys = File.join DATA_PATH, "authorized_keys"
+ @id_dsa = File.join DATA_PATH, "id_dsa"
+ @id_rsa = File.join DATA_PATH, "id_rsa"
+ end
+
+ def test_load_authorized_keys
+ s = Orthrus::SSH::PublicKeySet.load_file @auth_keys
+ assert_equal 2, s.num_keys
+ end
+
+ def test_find
+ s = Orthrus::SSH::PublicKeySet.load_file @auth_keys
+ k = Orthrus::SSH.load_private @id_rsa
+
+ j = s.find(k.public_identity)
+
+ assert_kind_of Orthrus::SSH::RSAPublicKey, j
+ end
+
+end
92 test/test_orthrus_ssh_rackapp.rb
@@ -0,0 +1,92 @@
+require 'minitest/unit'
+
+require 'orthrus/ssh'
+require 'orthrus/ssh/rack_app'
+
+require 'stringio'
+
+class TestOrthrusSSHRackApp < MiniTest::Unit::TestCase
+ DATA_PATH = File.expand_path "../data", __FILE__
+
+ class Sessions
+ def new_session(pub)
+ @pub = pub
+ [1, "secret"]
+ end
+
+ def find_session(id)
+ ["secret", @pub]
+ end
+
+ def access_token
+ 1
+ end
+ end
+
+ def setup
+ @id_rsa = File.join DATA_PATH, "id_rsa"
+ @rsa = Orthrus::SSH.load_private @id_rsa
+
+ @id_rsa_pub = File.join DATA_PATH, "id_rsa.pub"
+ @rsa_pub = Orthrus::SSH.load_public @id_rsa_pub
+
+ @app = Orthrus::SSH::RackApp.new Sessions.new
+ end
+
+ def test_call_unable_to_find_identity
+ id = @rsa.public_identity
+
+ env = {
+ "rack.input" => StringIO.new,
+ "QUERY_STRING" => "state=find&id=#{Rack::Utils.escape(id)}"
+ }
+
+ code, headers, body = @app.call(env)
+
+ assert_equal "application/x-www-form-urlencoded",
+ headers["Content-Type"]
+
+ assert_equal "code=unknown", body[0]
+ end
+
+ def test_call_requests_signature
+ id = @rsa.public_identity
+ @app.keys[id] = @rsa_pub
+
+ env = {
+ "rack.input" => StringIO.new,
+ "QUERY_STRING" => "state=find&id=#{Rack::Utils.escape(id)}"
+ }
+
+ code, headers, body = @app.call(env)
+
+ assert_equal "application/x-www-form-urlencoded",
+ headers["Content-Type"]
+
+ assert_equal ["code=check&session_id=1&nonce=secret"], body
+ end
+
+ def test_call_verifies_signature
+ id = @rsa.public_identity
+ @app.keys[id] = @rsa_pub
+
+ env = {
+ "rack.input" => StringIO.new,
+ "QUERY_STRING" => "state=find&id=#{Rack::Utils.escape(id)}"
+ }
+
+ code, headers, body = @app.call(env)
+
+ params = Rack::Utils.parse_query(body.first)
+
+ data = params['nonce']
+
+ sig = Rack::Utils.escape [@rsa.sign(data)].pack("m")
+
+ env["QUERY_STRING"] = "state=signed&sig=#{sig}&session_id=1"
+
+ code, headers, body = @app.call(env)
+
+ assert_equal ["code=verified&access_token=1"], body
+ end
+end
46 test/test_orthrus_ssh_rsa.rb
@@ -0,0 +1,46 @@
+require 'minitest/autorun'
+require 'minitest/unit'
+
+require 'orthrus/ssh'
+
+class TestOrthrusSSHRSA < MiniTest::Unit::TestCase
+ DATA_PATH = File.expand_path "../data", __FILE__
+
+ def setup
+ @id_rsa = File.join DATA_PATH, "id_rsa"
+ @id_rsa_pub = File.join DATA_PATH, "id_rsa.pub"
+ end
+
+ def pub_key
+ Orthrus::SSH.load_public @id_rsa_pub
+ end
+
+ def priv_key
+ Orthrus::SSH.load_private @id_rsa
+ end
+
+ def test_load_private
+ s = Orthrus::SSH.load_private @id_rsa
+ assert_kind_of Orthrus::SSH::PrivateKey, s
+ assert s.rsa?, "key not RSA"
+ end
+
+ def test_load_public
+ s = Orthrus::SSH.load_public @id_rsa_pub
+ assert_kind_of Orthrus::SSH::PublicKey, s
+ assert s.rsa?, "key not RSA"
+ end
+
+ def test_sign_and_verify
+ data = "hello"
+
+ assert pub_key.verify(priv_key.sign(data), data)
+ end
+
+ def test_public_identity
+ s = Orthrus::SSH.load_private @id_rsa
+ check = File.read(@id_rsa_pub).split(" ")[1]
+
+ assert_equal check, s.public_identity
+ end
+end

0 comments on commit cc7f3ce

Please sign in to comment.
Something went wrong with that request. Please try again.