# HG changeset patch # User Alessio Caiazza # Date 1269190557 -3600 # Node ID 0c2ebe513c3224eb90438bb3543de639760755ad # Parent 0a90b0f886e8257dc9777df5c190d5077f0b7e73 Now FakeYubikey supports marshaling and public_id. It's compliant with Yubico validation server. Using RSpec for Test and Yard for documentation. yubikey apps (inutility) which generate a fake yubikey and saves its state under ~/.yubiruby diff -r 0a90b0f886e8257dc9777df5c190d5077f0b7e73 -r 0c2ebe513c3224eb90438bb3543de639760755ad .hgignore --- a/.hgignore Sun Mar 21 14:46:15 2010 +0100 +++ b/.hgignore Sun Mar 21 17:55:57 2010 +0100 @@ -17,6 +17,8 @@ *.gem *.bundle Makefile +YubiRuby.gemspec +Manifest syntax: regexp .*\#.*\#$ diff -r 0a90b0f886e8257dc9777df5c190d5077f0b7e73 -r 0c2ebe513c3224eb90438bb3543de639760755ad CHANGELOG --- a/CHANGELOG Sun Mar 21 14:46:15 2010 +0100 +++ b/CHANGELOG Sun Mar 21 17:55:57 2010 +0100 @@ -1,4 +1,4 @@ -v1.0.0. broken api compatibility. TestCase. fixed some bugs in (mod)hex decoding. Exceptions. FakeYubikey generator +v1.0.0. broken api compatibility. TestCase. fixed some bugs in (mod)hex decoding. Exceptions. FakeYubikey generator. yubikey app for personal key management. v0.1.2. backported fixes from 1.0.0 diff -r 0a90b0f886e8257dc9777df5c190d5077f0b7e73 -r 0c2ebe513c3224eb90438bb3543de639760755ad Manifest --- a/Manifest Sun Mar 21 14:46:15 2010 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -CHANGELOG -LICENSE -Manifest -README -Rakefile -bin/modhex -ext/libyubikey/extconf.rb -ext/libyubikey/libyubikey.c -ext/libyubikey/ykaes.c -ext/libyubikey/ykcrc.c -ext/libyubikey/ykhex.c -ext/libyubikey/ykmodhex.c -ext/libyubikey/ykparse.c -ext/libyubikey/yubikey.h -lib/exceptions.rb -lib/fake_yubikey.rb -lib/hex.rb -lib/yubiruby.rb -test/tc_crc16.rb -test/tc_fake_yubikey.rb -test/tc_hex.rb -test/tc_modhex.rb -test/test_helper.rb diff -r 0a90b0f886e8257dc9777df5c190d5077f0b7e73 -r 0c2ebe513c3224eb90438bb3543de639760755ad README --- a/README Sun Mar 21 14:46:15 2010 +0100 +++ b/README Sun Mar 21 17:55:57 2010 +0100 @@ -0,0 +1,44 @@ += YubyRuby + +Yubico Server wrapper in Ruby + + +== Installation + + $ gem install YubiRuby + +== Some examples + +Checking an OTP + + require 'rubygems' + require 'YubiRuby' + + key = "6df89690b5f51bd9ac912c5004781e86" #use your AES key + y = YubiRuby::Yubikey.new(key); + puts y.key + otp = gets().strip + puts y.parse(otp) + puts "Ouput: #{y}" + puts "uid: #{y.uid}" + puts "session counter: #{y.session_counter}" + puts "capslock: #{y.triggered_by_capslock?}" + puts "timestamp low/high: #{y.timestamp_low}/#{y.timestamp_high}" + puts "session use: #{y.session_use}" + puts "random: #{y.random}" + puts "crc: #{y.crc}" + puts "crc residue: #{y.crc_residue}" + puts "crc residue ok?: #{y.crc?} (#{y.crc_residue} == #{YubiRuby::Yubikey::CRC_OK_RESIDUE})" + +Generating a valid yubikey + + key = YubiRuby::FakeYubikey.new + key.set_random_key + key.set_random_uid + key.public_id = "vvefeeccebek" + puts "Yubikey generated" + puts "public id:\t#{key.public_id}" + puts "key:\t\t#{key.key}" + puts "uid:\t\t#{key.uid}" + puts "And now 3 otp" + 3.times { puts key.otp } \ No newline at end of file diff -r 0a90b0f886e8257dc9777df5c190d5077f0b7e73 -r 0c2ebe513c3224eb90438bb3543de639760755ad Rakefile --- a/Rakefile Sun Mar 21 14:46:15 2010 +0100 +++ b/Rakefile Sun Mar 21 17:55:57 2010 +0100 @@ -42,11 +42,12 @@ p.author = "Alessio Caiazza" p.email = "nolith@abisso.org" p.platform = Gem::Platform::RUBY - p.ignore_pattern = ["tmp/**/*", "script/*"] + p.ignore_pattern = ["tmp/**/*", "script/*", "meta/**/*"] p.development_dependencies = ['yard', 'rspec'] p.runtime_dependencies << ['ruby-aes-normal', '~> 1.0'] p.runtime_dependencies << ['bit-struct'] #, '~> 0.13.6'] p.spec_pattern = ['test/**/*_spec.rb'] + p.has_rdoc = true end Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext } diff -r 0a90b0f886e8257dc9777df5c190d5077f0b7e73 -r 0c2ebe513c3224eb90438bb3543de639760755ad YubiRuby.gemspec --- a/YubiRuby.gemspec Sun Mar 21 14:46:15 2010 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,70 +0,0 @@ -# -*- encoding: utf-8 -*- - -Gem::Specification.new do |s| - s.name = %q{YubiRuby} - s.version = "1.0.0" - - s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version= - s.authors = ["Alessio Caiazza"] - s.date = %q{2010-02-16} - s.default_executable = %q{modhex} - s.description = %q{Yubikey integration - - -Includes Prototypes for low-level Yubikey OTP functions -witten by Simon Josefsson . -Copyright (c) 2006, 2007, 2008, 2009 Yubico AB -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -} - s.email = %q{nolith@abisso.org} - s.executables = ["modhex"] - s.extensions = ["ext/libyubikey/extconf.rb"] - s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README", "bin/modhex", "ext/libyubikey/extconf.rb", "ext/libyubikey/libyubikey.c", "ext/libyubikey/ykaes.c", "ext/libyubikey/ykcrc.c", "ext/libyubikey/ykhex.c", "ext/libyubikey/ykmodhex.c", "ext/libyubikey/ykparse.c", "ext/libyubikey/yubikey.h", "lib/exceptions.rb", "lib/fake_yubikey.rb", "lib/hex.rb", "lib/yubiruby.rb"] - s.files = ["CHANGELOG", "LICENSE", "Manifest", "README", "Rakefile", "bin/modhex", "ext/libyubikey/extconf.rb", "ext/libyubikey/libyubikey.c", "ext/libyubikey/ykaes.c", "ext/libyubikey/ykcrc.c", "ext/libyubikey/ykhex.c", "ext/libyubikey/ykmodhex.c", "ext/libyubikey/ykparse.c", "ext/libyubikey/yubikey.h", "lib/exceptions.rb", "lib/fake_yubikey.rb", "lib/hex.rb", "lib/yubiruby.rb", "test/tc_crc16.rb", "test/tc_fake_yubikey.rb", "test/tc_hex.rb", "test/tc_modhex.rb", "test/test_helper.rb", "YubiRuby.gemspec"] - s.homepage = %q{http://bitbucket.org/nolith/yubiruby} - s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "YubiRuby", "--main", "README"] - s.require_paths = ["lib", "ext"] - s.rubyforge_project = %q{yubiruby} - s.rubygems_version = %q{1.3.5} - s.summary = %q{Yubikey integration - Includes Prototypes for low-level Yubikey OTP functions witten by Simon Josefsson . Copyright (c) 2006, 2007, 2008, 2009 Yubico AB All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.} - s.test_files = ["test/test_helper.rb"] - - if s.respond_to? :specification_version then - current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION - s.specification_version = 3 - - if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then - s.add_runtime_dependency(%q, ["~> 1.0"]) - s.add_runtime_dependency(%q, [">= 0"]) - else - s.add_dependency(%q, ["~> 1.0"]) - s.add_dependency(%q, [">= 0"]) - end - else - s.add_dependency(%q, ["~> 1.0"]) - s.add_dependency(%q, [">= 0"]) - end -end diff -r 0a90b0f886e8257dc9777df5c190d5077f0b7e73 -r 0c2ebe513c3224eb90438bb3543de639760755ad bin/yubikey --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bin/yubikey Sun Mar 21 17:55:57 2010 +0100 @@ -0,0 +1,39 @@ +#!/usr/bin/env ruby -wKU + +require 'yubiruby' + + +if ARGV.length > 1 + msg=<<-EOF +Usage: #{$PROGRAM_NAME} [-v] + +Produce an OTP data + +-v for verbose output +EOF + abort msg +end + +verbose = ARGV.include? "-h" + +key = nil +begin + File.open(File.expand_path("~/.yubiruby")) do |f| + key = Marshal.load(f) + end +rescue Errno::ENOENT + puts "Key not found. Generating a new one." + key = YubiRuby::FakeYubikey.new + key.set_random_key + key.set_random_uid + tmp = " "*3 + 0.upto(2) { |i| tmp[i] = rand(2**8)} + key.public_id = "vv" + YubiRuby::HEX.encode(tmp).modhex_encode[2..-1] + puts "Yubikey generated" + puts "public id:\t#{key.public_id}" + puts "key:\t\t#{key.key}" + puts "uid:\t\t#{key.uid}" +end + +puts key.otp +File.open(File.expand_path("~/.yubiruby"), 'w+') { |f| Marshal.dump(key, f) } diff -r 0a90b0f886e8257dc9777df5c190d5077f0b7e73 -r 0c2ebe513c3224eb90438bb3543de639760755ad lib/fake_yubikey.rb --- a/lib/fake_yubikey.rb Sun Mar 21 14:46:15 2010 +0100 +++ b/lib/fake_yubikey.rb Sun Mar 21 17:55:57 2010 +0100 @@ -42,10 +42,15 @@ module YubiRuby # - # This class simulate a yubikey allowing you make some test + # This class simulate a yubikey allowing you to make some tests # class FakeYubikey + # Maximum allowed length for a modhex encoded public id + PUBLIC_ID_MAX_LEN = 32 + # + # Yubikey internal data + # class Data < BitStruct YUBIKEY_BLOCK_SIZE = 16 YUBIKEY_KEY_SIZE = 16 @@ -63,13 +68,40 @@ end attr_accessor :key + #attr_reader :public_id def initialize + @public_id = "" @data = YubiRuby::FakeYubikey::Data.new @data.session_counter = 0 init end + # + # Sets the public id of the key. + # + # public_id must be a modhex valid string no longer than + # 32 char, if longer it will be truncated. + # + # @param [String, #read] value the new public_id + # @return [String] the new public_id + def public_id=(value) + raise NoModHexEncodedError unless value.modhex? + + value = value[0, PUBLIC_ID_MAX_LEN] if value.length > PUBLIC_ID_MAX_LEN + raise NoModHexEncodedError unless value.modhex? + + @public_id = value + end + + # Returns the value of attribute public_id. + def public_id + @public_id + end + + # Simaluates the power on sequence of a YubuKey. + # + # This will increase session_counter def init @data.session_counter += 1 if (@data.session_counter == Data::SESSION_COUNTER_OVERFLOW+1) @@ -86,14 +118,17 @@ end end + # Generates a random key attribute def set_random_key @key = produce_random_bytes_in_hex Data::YUBIKEY_KEY_SIZE end + # Generates a random uid attribute def set_random_uid self.uid= produce_random_bytes_in_hex(Data::YUBIKEY_UID_SIZE) end + # Sets the attribute uid def uid=(value) if YubiRuby::HEX.decode(value).size == Data::YUBIKEY_UID_SIZE @data.uid = "#{value[0,2]}:#{value[2,2]}:#{value[4,2]}:#{value[6,2]}:#{value[8,2]}:#{value[10,2]}" @@ -102,10 +137,15 @@ end end + # Returns the value of attribute uid. def uid @data.uid.gsub(':','') end + # Simulates the pression of the yubikey's button + # + # session_use will increase and if it overflows also + # session_counter will be increased def otp @data.random = rand(2**16) @data.session_use += 1 @@ -134,11 +174,31 @@ @data end + # Generates an OTP without altering any internal data. def generate_otp @data.crc = YubiRuby::CRC16.calculate(@data.to_s[0...-2]).ones_complement 16 - Aes.encrypt_block(128, 'ECB', YubiRuby::HEX.decode(@key), nil, @data.to_s).modhex_encode + data = Aes.encrypt_block(128, 'ECB', YubiRuby::HEX.decode(@key), nil, + @data.to_s).modhex_encode + "#{@public_id}#{data}" end + #serialization support + def marshal_dump + {'key' => key, 'uid' => uid, 'session_counter' => @data.session_counter, + 'public_id' => public_id } + end + + #serialization support + def marshal_load(data) + @data = YubiRuby::FakeYubikey::Data.new + self.key = data['key'] + self.uid = data['uid'] + self.data.session_counter = data['session_counter'] + self.public_id = data['public_id'] + init + end + + private def produce_random_bytes_in_hex(size) tmp = " "*size diff -r 0a90b0f886e8257dc9777df5c190d5077f0b7e73 -r 0c2ebe513c3224eb90438bb3543de639760755ad tasks/yard_doc.rake --- a/tasks/yard_doc.rake Sun Mar 21 14:46:15 2010 +0100 +++ b/tasks/yard_doc.rake Sun Mar 21 17:55:57 2010 +0100 @@ -8,7 +8,7 @@ task :documentation => :'documentation:generate' namespace :documentation do YARD::Rake::YardocTask.new :generate do |t| - t.files = ['lib/**/*.rb'] + t.files = ['lib/**/*.rb', 'ext/**/*.c'] t.options = ['--output-dir', File.join('meta', 'documentation'), '--readme', 'README'] end diff -r 0a90b0f886e8257dc9777df5c190d5077f0b7e73 -r 0c2ebe513c3224eb90438bb3543de639760755ad test/fake_yubikey_spec.rb --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/fake_yubikey_spec.rb Sun Mar 21 17:55:57 2010 +0100 @@ -0,0 +1,155 @@ +require 'test/path_loader' + +describe 'YubiRuby::FakeYubikey' do + @@UID = 'ffa4f1fa55e0' + @@KEY = '884b1fc18da3888b59460f6e33acabf5' + + before :all do + @key = YubiRuby::FakeYubikey.new + end + + it "should start in a valid state" do + @key.uid.should == "000000000000" + @key.session_use.should == 0 + @key.session_counter.should == 1 + @key.public_id.should == "" + end + + it "should be serializable" do + @key.public_id = 'hehkhbhv' + @key.key = @@KEY + @key.uid = @@UID + 3.times { @key.init } + File.open('key.dump', 'w+') do |f| + Marshal.dump(@key, f) + end + + key2 = nil + File.open('key.dump') do |f| + key2 = Marshal.load(f) + end + + key2.public_id.should == @key.public_id + key2.uid.should == @key.uid + key2.data.session_counter.should == @key.session_counter+1 + key2.key.should == @key.key + end + + describe "#set_random_uid" do + it "should generate a hex valid string" do + @key.set_random_uid + @key.uid.hex?.should == true + end + end + + describe "#set_random_key" do + it "should generate a hex valid string" do + @key.set_random_key + @key.key.hex?.should == true + end + end + + describe '#otp' do + it "should generate valid otp" do + aes = @key.set_random_key + @key.set_random_uid + + validator = YubiRuby::Yubikey.new aes + validator.parse(@key.otp).should == true + validator.uid.should == @key.uid + validator.session_use.should == @key.session_use + validator.session_counter.should == @key.session_counter + end + + it "should produce a session overflow after 2**8 generations" do + @key = YubiRuby::FakeYubikey.new + aes = @key.set_random_key + uid = @key.set_random_uid + session = @key.session_counter + 1.upto(2**8-1) do |i| + @key.otp + @key.session_counter.should == session + @key.session_use.should == i + end + @key.otp + @key.session_counter.should == session+1 + @key.session_use.should == 0 + end + + it "should raise YubiRuby::YubikeySessionCounterOverflow after SESSION_COUNTER_OVERFLOW initialization" do + key = YubiRuby::FakeYubikey.new + aes = key.set_random_key + uid = key.set_random_uid + (session = key.session_counter).should == 1 + 2.upto(YubiRuby::FakeYubikey::Data::SESSION_COUNTER_OVERFLOW) do |i| + lambda { key.init }.should_not raise_error(YubiRuby::YubikeySessionCounterOverflow) + end + lambda { key.init }.should raise_error(YubiRuby::YubikeySessionCounterOverflow) + end + + it "should starts with public_id" do + @key = YubiRuby::FakeYubikey.new + @key.key = @@KEY + @key.uid = @@UID + @key.public_id = 'hehkhbhv' + @key.otp[0,8].should == 'hehkhbhv' + end + end + + describe '#generate_specific_otp_and_fake_yubikey' do + it "should work ;)" do + aes = 'df5b9d7591b0b3c2819f7a0dd7d1547d' + session = 53 + tsl = 31716 + tsh = 106 + counter = 0 + random = 30172 + crc = 58822 + uid = "6d8dec078841" + check_otp = 'rfechkcdkjrflgrjlirdgivnedbbtcik' + result = YubiRuby::FakeYubikey.generate_specific_otp_and_fake_yubikey( + aes, uid, session, tsl, tsh, counter, random) + mycrc = YubiRuby::CRC16.calculate(result[1].to_s[0...-2]).ones_complement 16 + + lambda { + YubiRuby::Yubikey.new(aes).parse(result[0]).should == true + }.should_not raise_error(TypeError) + result[1].key.should == aes + result[1].session_counter.should == session + result[1].timestamp_low.should == tsl + result[1].timestamp_high.should == tsh + result[1].session_use.should == counter + result[1].random.should == random + result[1].crc.should == crc + result[1].uid.should == uid + result[0].should == check_otp + end + end + + describe "#public_id=" do + it "should accepts modhex strings" do + id = 'rfechkcdkjrflgrjlirdgivnedbbtcik' + @key.public_id = id + @key.public_id.should == id + id = 'hehkhbhv' + @key.public_id = id + @key.public_id.should == id + end + + it "should accepts only modhex strings" do + id = @key.public_id + lambda {@key.public_id = "pluto"}.should raise_error(YubiRuby::NoModHexEncodedError) + @key.public_id.should == id + lambda{@key.public_id = "pip!8"}.should raise_error(YubiRuby::NoModHexEncodedError) + @key.public_id.should == id + end + + it "should truncate longer string" do + id = 'rfechkcdkjrflgrjlirdgivnedbbtcikrfechkcdkjrflgrjlirdgivnedbbtcik' + (id.length > 32).should == true + + @key.public_id = id + @key.public_id.should == id[0,32] + end + end +end diff -r 0a90b0f886e8257dc9777df5c190d5077f0b7e73 -r 0c2ebe513c3224eb90438bb3543de639760755ad test/hex_spec.rb --- a/test/hex_spec.rb Sun Mar 21 14:46:15 2010 +0100 +++ b/test/hex_spec.rb Sun Mar 21 17:55:57 2010 +0100 @@ -1,4 +1,4 @@ -require 'yubiruby' +require 'test/path_loader' describe 'YubiRuby::HEX' do describe '#encode' do diff -r 0a90b0f886e8257dc9777df5c190d5077f0b7e73 -r 0c2ebe513c3224eb90438bb3543de639760755ad test/modhex_spec.rb --- a/test/modhex_spec.rb Sun Mar 21 14:46:15 2010 +0100 +++ b/test/modhex_spec.rb Sun Mar 21 17:55:57 2010 +0100 @@ -1,4 +1,4 @@ -require 'yubiruby' +require 'test/path_loader' describe 'YubiRuby::ModHex' do diff -r 0a90b0f886e8257dc9777df5c190d5077f0b7e73 -r 0c2ebe513c3224eb90438bb3543de639760755ad test/path_loader.rb --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/path_loader.rb Sun Mar 21 17:55:57 2010 +0100 @@ -0,0 +1,6 @@ +$:.unshift File.join(File.dirname(__FILE__),'..','lib') +$:.unshift File.join(File.dirname(__FILE__),'..','ext') + +require 'yubiruby' + +String.send(:include, YubiRuby::HEX) \ No newline at end of file diff -r 0a90b0f886e8257dc9777df5c190d5077f0b7e73 -r 0c2ebe513c3224eb90438bb3543de639760755ad test/test_helper.rb --- a/test/test_helper.rb Sun Mar 21 14:46:15 2010 +0100 +++ b/test/test_helper.rb Sun Mar 21 17:55:57 2010 +0100 @@ -1,12 +1,7 @@ -$:.unshift File.join(File.dirname(__FILE__),'..','lib') -$:.unshift File.join(File.dirname(__FILE__),'..','ext') +require 'path_loader' require "test/unit" -require 'yubiruby' - -String.send(:include, YubiRuby::HEX) - require "tc_modhex" require "tc_hex" require "tc_crc16"