Commits

Mahlon Smith committed 40659af

Updates for building under Ruby 2.x.

- Update all C macros
- Use stdlib where possible (no more ruby-aes-normal)
- Dependency updates
- Various typo correction and trailing whitespace fixes
- Use accessors instead of methods for fakekey

Comments (0)

Files changed (6)

 
 Includes Prototypes for low-level Yubikey OTP functions 
 witten by Simon Josefsson <simon@josefsson.org>.
-Copyright (c) 2006, 2007, 2008, 2009 Yubico AB
+Copyright (c) 2006, 2007, 2008, 2009, 2012 Yubico AB
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
   p.email          = "nolith@abisso.org"
   p.platform       = Gem::Platform::RUBY
   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.runtime_dependencies << ['bit-struct', '~> 0.15']
+  p.development_dependencies << ['rspec', '~> 2']
+  p.development_dependencies << ['yard', '~> 0.8']
   p.spec_pattern = ['test/**/*_spec.rb']
-  p.has_rdoc = true
 end
 
 Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
-#!/usr/bin/env ruby -wKU
+#!/usr/bin/env ruby -wU
 
 require 'yubiruby'
 
-#!/usr/bin/env ruby -wKU
+#!/usr/bin/env ruby -wU
 
 require 'yubiruby'
 

ext/libyubikey/libyubikey.c

 static VALUE modhex_sing_encode(VALUE self, VALUE obj) 
 {
 	VALUE str = StringValue(obj);
-	int src_size = RSTRING(str)->len;
+	int src_size = RSTRING_LEN(str);
 	char *dst = (char*) malloc((2*src_size+1)*sizeof(char));
 	/* ModHex encode input string SRC of length SRCSIZE and put the zero
 	   terminated output string in DST.  The size of the output string DST
 	   must be at least 2*SRCSIZE+1.  The output string is always
 	   2*SRCSIZE large plus the terminating zero.  */
-	yubikey_modhex_encode(dst, RSTRING(str)->ptr, src_size);
+	yubikey_modhex_encode(dst, RSTRING_PTR(str), src_size);
 	
 	return rb_str_new2(dst);
 }
 static VALUE modhex_sing_decode(VALUE self, VALUE obj) 
 {
 	VALUE str = StringValue(obj);
-	int src_size = RSTRING(str)->len;
+	int src_size = RSTRING_LEN(str);
 	if (rb_funcall(str, rb_intern(MODHEX_VALID), 0) == Qfalse)
 		rb_raise(rb_eval_string("YubiRuby::NoModHexEncodedError"), "");
 	char *dst = (char*) malloc(((src_size/2)+1)*sizeof(char));
 	/* ModHex decode input string SRC of length DSTSIZE/2 into output
 	   string DST.  The output string DST is always DSTSIZE/2 large plus
 	   the terminating zero.  */
-	yubikey_modhex_decode(dst, RSTRING(str)->ptr, src_size);
+	yubikey_modhex_decode(dst, RSTRING_PTR(str), src_size);
 	
 	return rb_str_new(dst, src_size/2);
 }
 static VALUE modhex_sing_is_modhex(VALUE self, VALUE obj) 
 {
 	VALUE str = StringValue(obj);
-	if (yubikey_modhex_p(RSTRING(str)->ptr) != 0)
+	if (yubikey_modhex_p(RSTRING_PTR(str)) != 0)
 		return Qtrue;
 	else
 		return Qfalse;
 	if (sKey == Qnil)
 		rb_raise(rb_eTypeError, "wrong parameter type.");
 		
-	if (RSTRING(sKey)->len == YUBIKEY_KEY_SIZE*2) {
+	if (RSTRING_LEN(sKey) == YUBIKEY_KEY_SIZE*2) {
 		/* Hex */
-		if (yubikey_hex_p(RSTRING(sKey)->ptr) == 0)
+		if (yubikey_hex_p(RSTRING_PTR(sKey)) == 0)
 			rb_raise(rb_eTypeError, "not an HEX string."); //TODO:ArgumentError
 		char* buff = (char*) malloc(sizeof(char)*(YUBIKEY_KEY_SIZE +1));
-		yubikey_hex_decode( buff, RSTRING(sKey)->ptr, YUBIKEY_KEY_SIZE );
+		yubikey_hex_decode( buff, RSTRING_PTR(sKey), YUBIKEY_KEY_SIZE );
 		MEMCPY(&data->key, buff, uint8_t, YUBIKEY_KEY_SIZE);
 	}
 	
 	if (str == Qnil)
 		rb_raise(rb_eTypeError, "wrong argument type.");
 	
-	int start = RSTRING(str)->len - 32;
+	int start = RSTRING_LEN(str)- 32;
 	
 	if (start < 0)
 		rb_raise(rb_eTypeError, "OTP too short."); //Argument error
 	/* Decrypt TOKEN using KEY and store output in OUT structure.  Note
 	   that there is no error checking whether the output data is valid or
 	   not, use yubikey_check_* for that. */
-	yubikey_parse ((uint8_t*)(RSTRING(str)->ptr+start), data->key, data->token);	
+	yubikey_parse ((uint8_t*)(RSTRING_PTR(str)+start), data->key, data->token);	
 	
 #ifndef NDEBUG
 	printf ("Output:\n");
 	VALUE out = yubikey_to_str(self);
 	
 	// *2 becaouse of hex
-	return rb_str_new(RSTRING(out)->ptr, YUBIKEY_UID_SIZE*2 );
+	return rb_str_new(RSTRING_PTR(out), YUBIKEY_UID_SIZE*2 );
 }
 
 /* call-seq:
 static VALUE crc16_sing_calc(VALUE self, VALUE data) 
 {
 	VALUE str = StringValue(data);
-	return INT2NUM(yubikey_crc16(RSTRING(str)->ptr, RSTRING(str)->len));
+	return INT2NUM(yubikey_crc16( (uint8_t *)RSTRING_PTR(str), RSTRING_LEN(str)));
 }
 
 /* Implementation of Yubikey OTP functions.
 	
 	mCRC16 = rb_define_module_under(mYubiRuby, "CRC16");
 	rb_define_singleton_method(mCRC16, "calculate", crc16_sing_calc, 1);
-}
+}

lib/fake_yubikey.rb

 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-require 'rubygems'
-require 'ruby-aes'
+require 'securerandom'
+require 'openssl'
 require 'bit-struct'
 
-# 1-complement is uses to computer crc field
+# 1-complement is uses to compute crc field
 #
 #
 class Numeric
   #
   # This class simulate a yubikey allowing you to make some tests
   #
-  class FakeYubikey 
+  class FakeYubikey
     # Maximum allowed length for a modhex encoded public id
     PUBLIC_ID_MAX_LEN = 32
-    
-    # 
-    # Yubikey internal data 
+
+    #
+    # Yubikey internal data
     #
     class Data < BitStruct
       YUBIKEY_BLOCK_SIZE = 16
       YUBIKEY_KEY_SIZE = 16
       YUBIKEY_UID_SIZE = 6
       SESSION_COUNTER_OVERFLOW = 0x7FFF
-    
+
       hex_octets  :uid,     YUBIKEY_UID_SIZE*8,     "User ID"
-      unsigned    :session_counter,    16, {:endian=>:little, :display_name=>"Session Counter"}  
+      unsigned    :session_counter,    16, {:endian=>:little, :display_name=>"Session Counter"}
       unsigned    :timestamp_low,   16, {:endian=>:little, :display_name=>"Timestamp low"}
       unsigned    :timestamp_high,  8, "Timestamp high"
       unsigned    :session_use,  8, "Session Use"
       unsigned    :random,   16,  {:endian=>:little, :display_name=>"random"}
       unsigned    :crc,  16, {:endian=>:little, :display_name=>"crc"}
-      
+
     end
 
     attr_accessor :key
-    #attr_reader :public_id
-    
+	attr_reader :public_id, :data
+
     def initialize
       @public_id = ""
       @data = YubiRuby::FakeYubikey::Data.new
       @data.session_counter = 0
       init
     end
-    
-    # 
-    # Sets the public id of the key. 
-    # 
+
+    #
+    # 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 
+    # @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)
-        raise YubikeySessionCounterOverflow.new 
+        raise YubikeySessionCounterOverflow.new
       end
       @data.session_use = 0
       @data.timestamp_low = rand(2**16)
       @data.timestamp_high = rand(2**8)
     end
-    
+
     %w{session_use timestamp_low timestamp_high session_counter random crc}.each do |name|
-      define_method name do 
+      define_method name do
         @data.send name
       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
         raise NoHexEncodedError.new
       end
     end
-    
+
     # Returns the value of attribute uid.
     def uid
       @data.uid.gsub(':','')
     end
-    
-    # Simulates the pression of the yubikey's button
+
+    # Simulates the pressing of the yubikey's button
     #
     # session_use will increase and if it overflows also
     # session_counter will be increased
       @data.session_counter +=1 if @data.session_use == 0
       generate_otp
     end
-    
+
     def to_s
       @data.to_s
     end
-    
-    def self.generate_specific_otp_and_fake_yubikey(key, uid, session_counter, 
+
+    def self.generate_specific_otp_and_fake_yubikey(key, uid, session_counter,
         timestamp_low, timestamp_high, session_use, random)
       y_key = FakeYubikey.new
       y_key.key = key
       y_key.data.random = random
       [y_key.generate_otp, y_key]
     end
-    
-    def data
-      @data
-    end
-    
+
     # Generates an OTP without altering any internal data.
     def generate_otp
+      @cipher = OpenSSL::Cipher.new( "AES-128-ECB" ) unless @cipher
+      @cipher.encrypt
+      @cipher.key = YubiRuby::HEX.decode( @key )
+
       @data.crc = YubiRuby::CRC16.calculate(@data.to_s[0...-2]).ones_complement 16
-      data = Aes.encrypt_block(128, 'ECB', YubiRuby::HEX.decode(@key), nil, 
-        @data.to_s).modhex_encode
+      data = @cipher.update( @data.to_s ).modhex_encode
+
       "#{@public_id}#{data}"
+
+	ensure
+		@cipher.reset
     end
-    
-    #serialization support 
+
+    #serialization support
     def marshal_dump
         {'key' => key, 'uid' => uid, 'session_counter' => @data.session_counter,
           'public_id' => public_id }
     end
-    
-    #serialization support 
+
+    #serialization support
     def marshal_load(data)
       @data = YubiRuby::FakeYubikey::Data.new
       self.key = data['key']
       self.public_id = data['public_id']
       init
     end
-    
-    
+
+
     private
     def produce_random_bytes_in_hex(size)
-      tmp = " "*size
-      0.upto(size-1) { |i| tmp[i] = rand(2**8)}
-      YubiRuby::HEX.encode(tmp)
+      YubiRuby::HEX.encode( SecureRandom.random_bytes(size) )
     end
   end
-end
+end
   #   "foo bar".hex_encode        #=> "666f6f20626172"
   #   "666f6f20626172".hex_decode #=> "foo bar"
   module HEX
-    
+
     # call-seq:
     #   YubiRuby::HEX.encode("string") -> "hex string"
     #
     # Encodes <tt>obj.to_str</tt> into an <tt>hex string</tt>.
     #
     def self.encode( obj )
-      s = obj.to_str
-      s.unpack('U'*s.length).collect {|x| tmp = x.to_s 16; tmp.length == 1 ? "0#{tmp}" : tmp }.join
-    rescue ArgumentError
-      #non UTF-8 string
-      s = obj.to_str
-      out = []
-      0.upto(s.length-1) do |i|
-        tmp = s[i].to_s 16
-        out << (tmp.length == 1 ? "0#{tmp}" : tmp )
-      end
-      out.join
+	  return obj.unpack( 'H*' ).first
     end
-    
+
     # call-seq:
     #   hex_encode -> "hex string"
     #
     def hex_encode
       HEX.encode(self)
     end
-    
+
     # call-seq:
-    #   YubiRuby::HEX.decode("hex string") -> "string" 
+    #   YubiRuby::HEX.decode("hex string") -> "string"
     #
     # Decodes <tt>obj.to_str</tt> into a <tt>string</tt>.
     #
-    # An <tt>hex string</tt> length must be pair, if not a 
-    # NoHexEncodedError excpetion is raised
     def self.decode( obj )
-      s = obj.to_str
-      dec = ""
-      if hex?(s)
-        (s.length/2).times { |i| dec << s[i*2,2].hex.chr }
-      else
-        raise NoHexEncodedError.new
-      end
-      
-      return dec
+      raise NoHexEncodedError unless self.hex?( obj )
+      return obj.to_s.scan(/[[:xdigit:]]{2}/).map{|h| h.hex.chr }.join
     end
-    
+
     # call-seq:
     #   hex_decode -> "string"
     #
     def hex_decode
       HEX.decode(self)
     end
-    
+
     # call-seq:
     #   YubiRuby::HEX.hex?("hex string") -> true or false
     #
       return false unless (s.length % 2 == 0)
       return (s.upcase =~ /[^A-F0-9]/).nil?
     end
-    
+
     # call-seq:
     #   hex? -> true or false
     #