[or-cvs] r10572: initial import (in topf/trunk: . lib lib/fuzz-struct stuff)

benedikt at seul.org benedikt at seul.org
Tue Jun 12 09:27:41 UTC 2007


Author: benedikt
Date: 2007-06-12 05:27:41 -0400 (Tue, 12 Jun 2007)
New Revision: 10572

Added:
   topf/trunk/config.yml
   topf/trunk/lib/
   topf/trunk/lib/dir.rb
   topf/trunk/lib/fuzz-generic.rb
   topf/trunk/lib/fuzz-struct.rb
   topf/trunk/lib/fuzz-struct/
   topf/trunk/lib/fuzz-struct/char-field.rb
   topf/trunk/lib/fuzz-struct/float-field.rb
   topf/trunk/lib/fuzz-struct/fuzz-struct.rb
   topf/trunk/lib/fuzz-struct/hex-octet-field.rb
   topf/trunk/lib/fuzz-struct/nested-field.rb
   topf/trunk/lib/fuzz-struct/octet-field.rb
   topf/trunk/lib/fuzz-struct/pad-field.rb
   topf/trunk/lib/fuzz-struct/signed-field.rb
   topf/trunk/lib/fuzz-struct/text-field.rb
   topf/trunk/lib/fuzz-struct/unsigned-field.rb
   topf/trunk/lib/fuzz-struct/yaml.rb
   topf/trunk/lib/fuzz.rb
   topf/trunk/lib/pkcs1.rb
   topf/trunk/lib/topf.rb
   topf/trunk/stuff/
   topf/trunk/stuff/fuzz-private.pem
   topf/trunk/stuff/fuzz-public.pem
   topf/trunk/stuff/output-dir.rb
   topf/trunk/tor-dir-fuzz.rb
Log:
initial import

Added: topf/trunk/config.yml
===================================================================
--- topf/trunk/config.yml	                        (rev 0)
+++ topf/trunk/config.yml	2007-06-12 09:27:41 UTC (rev 10572)
@@ -0,0 +1,3 @@
+PORT: "2324"
+HOST: 127.0.0.1
+KEYFILE: stuff/fuzz-private.pem

Added: topf/trunk/lib/dir.rb
===================================================================
--- topf/trunk/lib/dir.rb	                        (rev 0)
+++ topf/trunk/lib/dir.rb	2007-06-12 09:27:41 UTC (rev 10572)
@@ -0,0 +1,188 @@
+module TOPF
+    module Base16
+        def Base16.encode(string)
+            temp = []
+            string.each_byte do |i|
+                temp.push i.to_s(16)
+            end
+            temp.join
+        end
+    end
+
+    module Dir
+        DefaultNicknameLength   = 6
+        DefaultOrPort           = 80
+        DefaultDirPort          = 80
+
+        PublicCertificate       =
+%{\n-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAKo3Ijan5HOp6zzV1VJmveh2uvJv0A5qLiJtNVrebMTAM8kte14Fsylb
+DE5UmDryqbOmNi9gIcZKfjuqEKcuhsEPDObLeabNkdP+YGURVQ72IhQT8XRiGWg3
+2i8F9xhp5KPGKhjkXuAxnt2FS9lkOvIDnm4jnOtzfX0/RJRRrBCvAgMBAAE=
+-----END RSA PUBLIC KEY-----}
+
+        class RouterItem < BitStruct
+            char    :item, 6*8, :fuzzable => false 
+            char    :nickname, TOP::Dir::DefaultNicknameLength*8 
+            octets  :address, 32
+            signed  :OrPort, 32
+            signed  :SocksPort, 32
+            signed  :DirPort, 32
+            
+            initial_value.item       = "router"
+            initial_value.nickname      = "foobar"
+            initial_value.address       = "127.0.0.1"
+            initial_value.SocksPort     = 0
+            initial_value.OrPort        = TOP::Dir::DefaultOrPort
+            initial_value.DirPort       = TOP::Dir::DefaultDirPort
+        end
+
+        class PublishedItem < BitStruct
+            char    :item, 9*8
+            char    :time, 19*8
+
+            initial_value.item       = "published"
+            initial_value.time          = "#{Date.today.to_s} #{ Time.now.strftime "%H:%M:%S"}" 
+        end
+
+        class OnionKeyItem < BitStruct
+            char    :item, 9*8
+            rest    :publicKey      # key in PEM Format 
+
+            initial_value.item       = "onion-key"
+            initial_value.publicKey     = TOP::Dir::PublicCertificate
+        end
+
+        class SigningKeyItem < BitStruct
+            char    :item, 11*8
+            rest    :publicKey      # key in PEM Format
+
+            initial_value.item       = "signing-key"
+            initial_value.publicKey     = TOP::Dir::PublicCertificate
+        end
+
+        class BandwidthItem < BitStruct
+            char    :item, 9*8
+            signed  :avg,       128
+            signed  :burst,     128
+            signed  :observed,  128
+
+            initial_value.item      = "bandwidth"
+            initial_value.avg       = 1 
+            initial_value.burst     = 1 
+            initial_value.observed  = 1 
+        end
+
+        class PlatformItem < BitStruct
+            char :item, 8*8
+            rest :string
+
+            initial_value.item = "platform"
+        end
+
+        class FingerprintItem < BitStruct
+            # FORMAT ?? 
+        end 
+
+        class HibernateItem < BitStruct
+            char :item, 11*8
+            unsigned :state, 1
+
+            initial_value.item   = "hibernating"
+            initial_value.state     = 0 
+        end
+
+        class UptimeItem < BitStruct
+            char :item, 6*8 
+            # FORMAT ?? 
+            
+            initial_value.item  = "uptime"
+        end
+
+        class ContactItem < BitStruct
+            char :item, 7*8 
+            rest :info
+
+            initial_value.item  = "contact"
+        end
+
+        class FamilyItem < BitStruct
+            char :item, 6*8
+            rest :names
+
+            initial_value.item = "family"
+        end
+
+        class ReadHistoryItem < BitStruct
+            char :item, 12*8 
+            char :time, 19*8
+
+            initial_value.item = "read-history"
+            initial_value.time          = "#{Date.today.to_s} #{ Time.now.strftime "%H:%M:%S"}" 
+        end
+
+        class WriteHistoryItem < BitStruct
+            char :item, 12*8
+            char :time, 19*8
+
+            initial_value.item = "write-history"
+            initial_value.time          = "#{Date.today.to_s} #{ Time.now.strftime "%H:%M:%S"}" 
+        end
+
+        class EventDnsItem < BitStruct
+            char :item, 8*8
+            unsigned :bool, 1
+                
+            initial_value.item = "eventdns"
+            initial_value.bool = 0
+        end
+
+        class RouterDescriptor
+            def initialize(publicKey, host = "127.0.0.1", port = 80) 
+                @osslkey = OpenSSL::PKey::RSA.new(File.read(publicKey) )
+                @pkcs1 = PKCS1::SignatureScheme::RSASSAPKCS1v1_5.new(Digest::SHA1)
+
+                @key = PKCS1::Key::RSA.new(@osslkey.n.to_i , @osslkey.e.to_i, @osslkey.d.to_i)
+
+                @items = [  RouterItem.new, 
+                            PublishedItem.new,  
+                            OnionKeyItem.new,
+                            SigningKeyItem.new, 
+                            BandwidthItem.new,  
+                    ]
+
+                @host = host
+                @port = port
+
+                connect!
+            end
+
+            def connect!
+            end
+
+            def add_item( item )
+                @items.push item
+            end
+
+            def fuzz!
+                @items.each do |item|
+                    item.fuzz!
+                end
+            end
+
+            def to_s
+                resultString = ""
+                @items.each do |item|
+                    resultString << item.format << "\n"
+                end
+                resultString << "router-signature\n"
+                sig = @pkcs1.sign(@key, resultString)
+
+                resultString << "-----BEGIN SIGNATURE-----\n"
+                resultString << sig
+                resultString << "\n-----END SIGNATURE-----" << "\n"*2
+            end
+        end
+    end
+end
+

Added: topf/trunk/lib/fuzz-generic.rb
===================================================================
--- topf/trunk/lib/fuzz-generic.rb	                        (rev 0)
+++ topf/trunk/lib/fuzz-generic.rb	2007-06-12 09:27:41 UTC (rev 10572)
@@ -0,0 +1,6 @@
+dir = __FILE__
+$:.unshift(File.dirname(dir)) unless
+$:.include?(File.dirname(dir)) || $:.include?(File.expand_path(File.dirname(dir)))
+
+require "fuzz-struct"
+require "fuzz"

Added: topf/trunk/lib/fuzz-struct/char-field.rb
===================================================================
--- topf/trunk/lib/fuzz-struct/char-field.rb	                        (rev 0)
+++ topf/trunk/lib/fuzz-struct/char-field.rb	2007-06-12 09:27:41 UTC (rev 10572)
@@ -0,0 +1,68 @@
+require 'fuzz-struct/fuzz-struct'
+
+class BitStruct
+  # Class for fixed length binary strings of characters.
+  # Declared with BitStruct.char.
+  class CharField < Field
+    #def self.default
+    #  don't define this, since it must specify N nulls and we don't know N
+    #end
+    
+    # Used in describe.
+    def self.class_name
+      @class_name ||= "char"
+    end
+
+    def add_accessors_to(cl, attr = name) # :nodoc:
+      unless offset % 8 == 0
+        raise ArgumentError,
+          "Bad offset, #{offset}, for #{self.class} #{name}." +
+          " Must be multiple of 8."
+      end
+      
+      unless length % 8 == 0
+        raise ArgumentError,
+          "Bad length, #{length}, for #{self.class} #{name}." +
+          " Must be multiple of 8."
+      end
+      
+      offset_byte = offset / 8
+      length_byte = length / 8
+      last_byte = offset_byte + length_byte - 1
+      byte_range = offset_byte..last_byte
+      val_byte_range = 0..length_byte-1
+
+      cl.class_eval do
+        define_method attr do ||
+          self[byte_range].to_s
+        end
+
+        define_method "#{attr}=" do |val|
+          val = val.to_s
+          if val.length < length_byte
+            val += "\0" * (length_byte - val.length)
+          end
+          self[byte_range] = val[val_byte_range]
+        end
+      end
+    end
+  end
+  
+  class << self
+    # Define a char string field in the current subclass of BitStruct,
+    # with the given _name_ and _length_ (in bits). Trailing nulls _are_
+    # considered part of the string.
+    #
+    # If a class is provided, use it for the Field class.
+    # If a string is provided, use it for the display_name.
+    # If a hash is provided, use it for options.
+    #
+    # Note that the accessors have COPY semantics, not reference.
+    #
+    def char(name, length, *rest)
+      opts = parse_options(rest, name, CharField)
+      add_field(name, length, opts)
+    end
+    alias string char
+  end
+end

Added: topf/trunk/lib/fuzz-struct/float-field.rb
===================================================================
--- topf/trunk/lib/fuzz-struct/float-field.rb	                        (rev 0)
+++ topf/trunk/lib/fuzz-struct/float-field.rb	2007-06-12 09:27:41 UTC (rev 10572)
@@ -0,0 +1,82 @@
+require 'fuzz-struct/fuzz-struct'
+
+class BitStruct
+  # Class for floats (single and double precision) in network order.
+  # Declared with BitStruct.float.
+  class FloatField < Field
+    # Used in describe.
+    def self.class_name
+      @class_name ||= "float"
+    end
+    
+    def add_accessors_to(cl, attr = name) # :nodoc:
+      unless offset % 8 == 0
+        raise ArgumentError,
+          "Bad offset, #{offset}, for #{self.class} #{name}." +
+          " Must be multiple of 8."
+      end
+      
+      unless length == 32 or length == 64
+        raise ArgumentError,
+          "Bad length, #{length}, for #{self.class} #{name}." +
+          " Must be 32 or 64."
+      end
+      
+      offset_byte = offset / 8
+      length_byte = length / 8
+      last_byte = offset_byte + length_byte - 1
+      byte_range = offset_byte..last_byte
+
+      endian = (options[:endian] || options["endian"]).to_s
+      case endian
+      when "native"
+        ctl = case length
+          when 32; "f"
+          when 64; "d"
+        end
+      when "little"
+        ctl = case length
+          when 32; "e"
+          when 64; "E"
+        end
+      when "network", "big", ""
+        ctl = case length
+          when 32; "g"
+          when 64; "G"
+        end
+      else
+        raise ArgumentError,
+          "Unrecognized endian option: #{endian.inspect}"
+      end
+      
+      cl.class_eval do
+        define_method attr do ||
+          self[byte_range].unpack(ctl).first
+        end
+
+        define_method "#{attr}=" do |val|
+          self[byte_range] = [val].pack(ctl)
+        end
+      end
+    end
+  end
+
+  class << self
+    # Define a floating point field in the current subclass of BitStruct,
+    # with the given _name_ and _length_ (in bits).
+    #
+    # If a class is provided, use it for the Field class.
+    # If a string is provided, use it for the display_name.
+    # If a hash is provided, use it for options.
+    #
+    # The <tt>:endian => :native</tt> option overrides the default of
+    # <tt>:network</tt> byte ordering, in favor of native byte ordering. Also
+    # permitted are <tt>:big</tt> (same as <tt>:network</tt>) and
+    # <tt>:little</tt>.
+    #
+    def float name, length, *rest
+      opts = parse_options(rest, name, FloatField)
+      add_field(name, length, opts)
+    end
+  end
+end

Added: topf/trunk/lib/fuzz-struct/fuzz-struct.rb
===================================================================
--- topf/trunk/lib/fuzz-struct/fuzz-struct.rb	                        (rev 0)
+++ topf/trunk/lib/fuzz-struct/fuzz-struct.rb	2007-06-12 09:27:41 UTC (rev 10572)
@@ -0,0 +1,572 @@
+# Class for packed binary data, with defined bitfields and accessors for them.
+# See {intro.txt}[link:../doc/files/intro_txt.html] for an overview.
+#
+# Data after the end of the defined fields is accessible using the +rest+
+# declaration. See examples/ip.rb. Nested fields can be declared using +nest+.
+# See examples/nest.rb.
+#
+# Note that all string methods are still available: length, grep, etc.
+# The String#replace method is useful.
+#
+class BitStruct < String
+  class Field
+    # Offset of field in bits.
+    attr_reader :offset
+    
+    # Length of field in bits.
+    attr_reader :length
+    alias size length
+    
+    # Name of field (used for its accessors).
+    attr_reader :name
+    
+    # Options, such as :default (varies for each field subclass).
+    # In general, options can be provided as strings or as symbols.
+    attr_reader :options
+    
+    # Display name of field (used for printing).
+    attr_reader :display_name
+    
+    # Default value.
+    attr_reader :default
+    
+    # Format for printed value of field.
+    attr_reader :format
+   
+    # Field fuzzable ?
+    attr_reader :fuzzable
+
+    # Subclasses can override this to define a default for all fields of this
+    # class, not just the one currently being added to a BitStruct class, a
+    # "default default" if you will. The global default, if #default returns
+    # nil, is to fill the field with zero. Most field classes just let this
+    # default stand. The default can be overridden per-field when a BitStruct
+    # class is defined.
+    def self.default; nil; end
+
+    # Used in describe.
+    def self.class_name
+      @class_name ||= name[/\w+$/]
+    end
+    
+    # Used in describe. Can be overridden per-subclass, as in NestedField.
+    def class_name
+      self.class.class_name
+    end
+
+    # Yield the description of this field, as an array of 5 strings: byte
+    # offset, type, name, size, and description. The opts hash may have:
+    #
+    # :expand ::  if the value is true, expand complex fields
+    #
+    # (Subclass implementations may yield more than once for complex fields.)
+    #
+    def describe opts
+      bits = size
+      if bits > 32 and bits % 8 == 0
+        len_str = "%dB" % (bits/8)
+      else
+        len_str = "%db" % bits
+      end
+      
+      byte_offset = offset / 8 + (opts[:byte_offset] || 0)
+      yield ["@%d" % byte_offset, class_name, name, len_str, display_name]
+    end
+
+    # Options are _display_name_, _default_, and _format_ (subclasses of Field
+    # may add other options).
+    def initialize(offset, length, name, opts = {})
+      @offset, @length, @name, @options =
+        offset, length, name, opts
+     
+      @class        = opts[:field_class] || opts["field_class"] 
+      @display_name = opts[:display_name] || opts["display_name"]
+      @default      = opts[:default] || opts["default"] || self.class.default
+      @format       = opts[:format] || opts["format"]
+      
+      if opts.has_key?(:fuzzable) || opts.has_key?("fuzzable")
+          @fuzzable     = opts[:fuzzable] || opts["fuzzable"]
+      else
+          @fuzzable = true
+      end
+    end
+    
+    # Inspect the value of this field in the specified _obj_.
+    def inspect_in_object(obj, opts)
+      val = obj.send(name)
+      str =
+        begin
+          val.inspect(opts)
+        rescue ArgumentError # assume: "wrong number of arguments (1 for 0)"
+          val.inspect
+        end
+        (f=@format) ? (f % str) : str
+    end
+
+    def fuzz(obj, opts)
+        if @fuzzable and rand(2).to_i == 1
+            val = obj.send(name)
+            str =
+                begin
+                    val.inspect(opts)
+                rescue ArgumentError # assume: "wrong number of arguments (1 for 0)"
+                    val.inspect
+                end
+            obj.send "#{name}=", ((f=@format) ? (f % Fuzz::fuzzField(@class, @length, str) ) : Fuzz::fuzzField(@class, @length, str))
+        end
+    end
+
+    # Normally, all fields show up in inspect, but some, such as padding,
+    # should not.
+    def inspectable?; true; end
+
+    def to_s(obj, opts)
+        val = obj.send(name)
+    end
+  end
+  
+  NULL_FIELD = Field.new(0, 0, :null, :display_name => "null field")
+  
+  # Raised when a field is added after an instance has been created. Fields
+  # cannot be added after this point.
+  class ClosedClassError < StandardError; end
+
+  # Raised if the chosen field name is not allowed, either because another
+  # field by that name exists, or because a method by that name exists.
+  class FieldNameError < StandardError; end
+  
+  @default_options = {}
+    
+  class << self
+    # ------------------------
+    # :section: field access methods
+    #
+    # For introspection and metaprogramming.
+    #
+    # ------------------------
+
+    # Return the list of fields for this class.
+    def fields
+      @fields ||= self == BitStruct ? [] : superclass.fields.dup
+    end
+    
+
+    # Return the list of fields defined by this class, not inherited
+    # from the superclass.
+    def own_fields
+      @own_fields ||= []
+    end
+
+    # Add a field to the BitStruct (usually, this is only used internally).
+    def add_field(name, length, opts = {})
+      round_byte_length ## just to make sure this has been calculated
+      ## before adding anything
+      
+      name = name.to_sym
+      
+      if @closed
+        raise ClosedClassError, "Cannot add field #{name}: " +
+          "The definition of the #{self.inspect} BitStruct class is closed."
+      end
+
+      if fields.find {|f|f.name == name}
+        raise FieldNameError, "Field #{name} is already defined as a field."
+      end
+
+      if instance_methods(true).find {|m| m == name}
+        if opts[:allow_method_conflict] || opts["allow_method_conflict"]
+          warn "Field #{name} is already defined as a method."
+        else
+          raise FieldNameError,"Field #{name} is already defined as a method."
+        end
+      end
+      
+      field_class = opts[:field_class]
+      
+      prev = fields[-1] || NULL_FIELD
+      offset = prev.offset + prev.length
+      field = field_class.new(offset, length, name, opts)
+      field.add_accessors_to(self)
+      fields << field
+      own_fields << field
+      @bit_length += field.length
+      @round_byte_length = (bit_length/8.0).ceil
+
+      if @initial_value
+        diff = @round_byte_length - @initial_value.length
+        if diff > 0
+          @initial_value << "\0" * diff
+        end
+      end
+
+      field
+    end
+
+    def parse_options(ary, default_name, default_field_class) # :nodoc:
+      opts = ary.grep(Hash).first || {}
+      opts = default_options.merge(opts)
+      
+      opts[:display_name]  = ary.grep(String).first || default_name
+      opts[:field_class]   = ary.grep(Class).first || default_field_class
+      
+      opts
+    end
+    
+    # Get or set the hash of default options for the class, which apply to all
+    # fields. Changes take effect immediately, so can be used alternatingly with
+    # blocks of field declarations. If +h+ is provided, update the default
+    # options with that hash. Default options are inherited.
+    #
+    # This is especially useful with the <tt>:endian => val</tt> option.
+    def default_options h = nil
+      @default_options ||= superclass.default_options.dup
+      if h
+        @default_options.merge! h
+      end
+      @default_options
+    end
+    
+    # Length, in bits, of this object.
+    def bit_length
+      @bit_length ||= fields.inject(0) {|a, f| a + f.length}
+    end
+    
+    # Length, in bytes (rounded up), of this object.
+    def round_byte_length
+      @round_byte_length ||= (bit_length/8.0).ceil
+    end
+    
+    def closed! # :nodoc:
+      @closed = true
+    end
+
+    def field_by_name name
+      @field_by_name ||= {}
+      field = @field_by_name[name]
+      unless field
+        field = fields.find {|f| f.name == name}
+        @field_by_name[name] = field if field
+      end
+      field
+    end
+
+  end
+  
+  # Return the list of fields for this class.
+  def fields
+    self.class.fields
+  end
+  
+  # Return the field with the given name.
+  def field_by_name name
+    self.class.field_by_name name
+  end
+
+  # ------------------------
+  # :section: metadata inspection methods
+  #
+  # Methods to textually describe the format of a BitStruct subclass.
+  #
+  # ------------------------
+
+  class << self
+    # Default format for describe. Fields are byte, type, name, size,
+    # and description.
+    DESCRIBE_FORMAT = "%8s: %-12s %-14s[%4s] %s"
+    
+    # Can be overridden to use a different format.
+    def describe_format
+      DESCRIBE_FORMAT
+    end
+
+    # Textually describe the fields of this class of BitStructs.
+    # Returns a printable table (array of line strings), based on +fmt+,
+    # which defaults to #describe_format, which defaults to +DESCRIBE_FORMAT+.
+    def describe(fmt = nil, opts = {})
+      if block_given?
+        fields.each do |field|
+          field.describe(opts) do |desc|
+            yield desc
+          end
+        end
+        nil
+        
+      else
+        fmt ||= describe_format
+
+        result = []
+
+        unless opts[:omit_header]
+          result << fmt % ["byte", "type", "name", "size", "description"]
+          result << "-"*70
+        end
+
+        fields.each do |field|
+          field.describe(opts) do |desc|
+            result << fmt % desc
+          end
+        end
+
+        unless opts[:omit_footer]
+          result << @note if @note
+        end
+
+        result
+      end
+    end
+    
+    # Subclasses can use this to append a string (or several) to the #describe
+    # output. Notes are not cumulative with inheritance. When used with no
+    # arguments simply returns the note string
+    def note(*str)
+      @note = str unless str.empty?
+      @note
+    end
+  end
+  
+  # ------------------------
+  # :section: initialization and conversion methods
+  #
+  # ------------------------
+
+  # Initialize the string with the given string or bitstruct, or with a hash of
+  # field=>value pairs, or with the defaults for the BitStruct subclass. Fields
+  # can be strings or symbols. Finally, if a block is given, yield the instance
+  # for modification using accessors.
+  def initialize(value = nil)   # :yields: instance
+    self << self.class.initial_value
+
+    case value
+    when Hash
+      value.each do |k, v|
+        send "#{k}=", v
+      end
+    
+    when nil
+      
+    else
+      self[0, value.length] = value
+    end
+    
+    self.class.closed!
+    yield self if block_given?
+     
+  end
+  
+  DEFAULT_TO_H_OPTS = {
+    :convert_keys   => :to_sym,
+    :include_rest   => true
+  }
+ 
+ 
+  # Returns a hash of {name=>value,...} for each field. By default, include
+  # the rest field.
+  # Keys are symbols derived from field names using +to_sym+, unless
+  # <tt>opts[:convert_keys]<\tt> is set to some other method name.
+  def to_h(opts = DEFAULT_TO_H_OPTS)
+    converter = opts[:convert_keys] || :to_sym
+
+    fields_for_to_h = fields
+    if opts[:include_rest] and (rest_field = self.class.rest_field)
+      fields_for_to_h += [rest_field]
+    end
+    
+    fields_for_to_h.inject({}) do |h,f|
+      h[f.name.send(converter)] = send(f.name)
+      h
+    end
+  end
+  
+  # Returns an array of values of the fields of the BitStruct. By default,
+  # include the rest field.
+  def to_a(include_rest = true)
+    ary =
+      fields.map do |f|
+        send(f.name)
+      end
+    
+    if include_rest and (rest_field = self.class.rest_field)
+      ary << send(rest_field.name)
+    end
+  end
+  
+  class << self
+    # The unique "prototype" object from which new instances are copied.
+    # The fields of this instance can be modified in the class definition
+    # to set default values for the fields in that class. (Otherwise, defaults
+    # defined by the fields themselves are used.) A copy of this object is
+    # inherited in subclasses, which they may override using defaults and
+    # by writing to the initial_value object itself.
+    #
+    # If called with a block, yield the initial value object before returning
+    # it. Useful for customization within a class definition.
+    #
+    def initial_value   # :yields: the initial value
+      unless @initial_value
+        iv = defined?(superclass.initial_value) ? 
+          superclass.initial_value.dup : ""
+        if iv.length < round_byte_length
+          iv << "\0" * (round_byte_length - iv.length)
+        end
+
+        @initial_value = "" # Serves as initval while the real initval is inited
+        @initial_value = new(iv)
+        @closed = false # only creating the first _real_ instance closes.
+        
+        fields.each do |field|
+          @initial_value.send("#{field.name}=", field.default) if field.default
+        end
+      end
+      yield @initial_value if block_given?
+      @initial_value
+    end
+    
+    # Take +data+ (a string or BitStruct) and parse it into instances of
+    # the +classes+, returning them in an array. The classes can be given
+    # as an array or a separate arguments. (For parsing a string into a _single_
+    # BitStruct instance, just use the #new method with the string as an arg.)
+    def parse(data, *classes)
+      classes.flatten.map do |c|
+        c.new(data.slice!(0...c.round_byte_length))
+      end
+    end
+    
+    # Join the given structs (array or multiple args) as a string.
+    # Actually, the inherited String#+ instance method is the same, as is using
+    # Array#join.
+    def join(*structs)
+      structs.flatten.map {|struct|
+          struct.to_s
+      }.join("")
+    end
+  end
+
+  # ------------------------
+  # :section: inspection methods
+  #
+  # ------------------------
+
+  DEFAULT_INSPECT_OPTS = {
+    :format           => "#<%s %s>",
+    :field_format     => "%s=%s",
+    :separator        => ", ",
+    :field_name_meth  => :name,
+    :include_rest     => true
+  }
+  
+  DETAILED_INSPECT_OPTS = {
+    :format           => "%s:\n%s",
+    :field_format     => "%30s = %s",
+    :separator        => "\n",
+    :field_name_meth  => :display_name,
+    :include_rest     => true
+  }
+  
+  # A standard inspect method which does not add newlines.
+  def inspect(opts = DEFAULT_INSPECT_OPTS)
+    field_format = opts[:field_format]
+    field_name_meth = opts[:field_name_meth]
+    
+    fields_for_inspect = fields.select {|field| field.inspectable?}
+    if opts[:include_rest] and (rest_field = self.class.rest_field)
+      fields_for_inspect << rest_field
+    end
+    
+    ary = fields_for_inspect.map do |field|
+      field_format %
+       [field.send(field_name_meth),
+        field.inspect_in_object(self, opts)]
+    end
+        
+    body = ary.join(opts[:separator])
+    
+    opts[:format] % [self.class, body]
+  end
+
+  def fuzz!(opts = DEFAULT_INSPECT_OPTS)
+    field_format = opts[:field_format]
+    field_name_meth = opts[:field_name_meth]
+    
+    fields_for_inspect = fields.select {|field| field.inspectable?}
+    if opts[:include_rest] and (rest_field = self.class.rest_field)
+      fields_for_inspect << rest_field
+    end
+    
+    fields_for_inspect.map do |field|
+        field.fuzz(self, opts)
+    end
+  end
+
+  def format(opts = DEFAULT_INSPECT_OPTS)
+        fields_for_output = fields.select {|field| field.inspectable?}
+
+        if opts[:include_rest] and (rest_field = self.class.rest_field)
+            fields_for_output << rest_field
+        end
+        
+        fields_for_output.map { |field|
+            field.to_s(self, opts)
+        }.join " "
+  end
+
+  # A more visually appealing inspect method that puts each field/value on
+  # a separate line. Very useful when output is scrolling by on a screen.
+  #
+  # (This is actually a convenience method to call #inspect with the
+  # DETAILED_INSPECT_OPTS opts.)
+  def inspect_detailed
+    inspect(DETAILED_INSPECT_OPTS)
+  end
+
+  # ------------------------
+  # :section: field declaration methods
+  #
+  # ------------------------
+  
+  # Define accessors for a variable length substring from the end of
+  # the defined fields to the end of the BitStruct. The _rest_ may behave as
+  # a String or as some other String or BitStruct subclass.
+  #
+  # This does not add a field, which is useful because a superclass can have
+  # a rest method which accesses subclass data. In particular, #rest does
+  # not affect the #round_byte_length class method. Of course, any data
+  # in rest does add to the #length of the BitStruct, calculated as a string.
+  # Also, _rest_ is not inherited.
+  #
+  # The +ary+ argument(s) work as follows:
+  #
+  # If a class is provided, use it for the Field class (String by default).
+  # If a string is provided, use it for the display_name (+name+ by default).
+  # If a hash is provided, use it for options.
+  #
+  # *Warning*: the rest reader method returns a copy of the field, so
+  # accessors on that returned value do not affect the original rest field. 
+  #
+  def self.rest(name, *ary)
+    if @rest_field
+      raise ArgumentError, "Duplicate rest field: #{name.inspect}."
+    end
+    
+    opts = parse_options(ary, name, String)
+    offset = round_byte_length
+    byte_range = offset..-1
+    class_eval do
+      field_class = opts[:field_class]
+      define_method name do ||
+        field_class.new(self[byte_range])
+      end
+
+      define_method "#{name}=" do |val|
+        self[byte_range] = val.to_s
+      end
+      
+      @rest_field = Field.new(offset, -1, name, {
+        :display_name => opts[:display_name],
+        :rest_class => field_class
+      })
+    end
+  end
+  
+  # Not included with the other fields, but accessible separately.
+  def self.rest_field; @rest_field; end
+end

Added: topf/trunk/lib/fuzz-struct/hex-octet-field.rb
===================================================================
--- topf/trunk/lib/fuzz-struct/hex-octet-field.rb	                        (rev 0)
+++ topf/trunk/lib/fuzz-struct/hex-octet-field.rb	2007-06-12 09:27:41 UTC (rev 10572)
@@ -0,0 +1,36 @@
+require 'fuzz-struct/char-field'
+
+class BitStruct
+  # Class for char fields that can be accessed with values like
+  # "xx:xx:xx:xx", where each xx is up to 2 hex digits representing a
+  # single octet. The original string-based accessors are still available with
+  # the <tt>_chars</tt> suffix.
+  # 
+  # Declared with BitStruct.hex_octets.
+  class HexOctetField < BitStruct::OctetField
+    # Used in describe.
+    def self.class_name
+      @class_name ||= "hex_octets"
+    end
+    
+    SEPARATOR = ":"
+    FORMAT    = "%02x"
+    BASE      = 16
+  end
+  
+  class << self
+    # Define an octet string field in the current subclass of BitStruct,
+    # with the given _name_ and _length_ (in bits). Trailing nulls are
+    # not considered part of the string. The field is accessed using
+    # period-separated hex digits.
+    #
+    # If a class is provided, use it for the Field class.
+    # If a string is provided, use it for the display_name.
+    # If a hash is provided, use it for options.
+    #
+    def hex_octets(name, length, *rest)
+      opts = parse_options(rest, name, HexOctetField)
+      add_field(name, length, opts)
+    end
+  end
+end

Added: topf/trunk/lib/fuzz-struct/nested-field.rb
===================================================================
--- topf/trunk/lib/fuzz-struct/nested-field.rb	                        (rev 0)
+++ topf/trunk/lib/fuzz-struct/nested-field.rb	2007-06-12 09:27:41 UTC (rev 10572)
@@ -0,0 +1,127 @@
+require 'fuzz-struct/fuzz-struct'
+
+class BitStruct
+  # Class for nesting a BitStruct as a field within another BitStruct.
+  # Declared with BitStruct.nest.
+  class NestedField < Field
+    attr_reader :nested_class
+    
+    def initialize(*args)
+      super
+    end
+    
+    # Used in describe.
+    def self.class_name
+      @class_name ||= "nest"
+    end
+    
+    def class_name
+      @class_name ||= nested_class.name[/\w+$/]
+    end
+    
+    def nested_class
+      @nested_class ||= options[:nested_class] || options["nested_class"]
+    end
+
+    def describe opts
+      if opts[:expand]
+        opts = opts.dup
+        opts[:byte_offset] = offset / 8
+        opts[:omit_header] = opts[:omit_footer] = true
+        nested_class.describe(nil, opts) {|desc| yield desc}
+      else
+        super
+      end
+    end
+
+    def add_accessors_to(cl, attr = name) # :nodoc:
+      unless offset % 8 == 0
+        raise ArgumentError,
+          "Bad offset, #{offset}, for nested field #{name}." +
+          " Must be multiple of 8."
+      end
+      
+      unless length % 8 == 0
+        raise ArgumentError,
+          "Bad length, #{length}, for nested field #{name}." +
+          " Must be multiple of 8."
+      end
+      
+      offset_byte = offset / 8
+      length_byte = length / 8
+      last_byte = offset_byte + length_byte - 1
+      byte_range = offset_byte..last_byte
+      val_byte_range = 0..length_byte-1
+
+      nc = nested_class
+      
+      cl.class_eval do
+        define_method attr do ||
+          nc.new(self[byte_range])
+        end
+
+        define_method "#{attr}=" do |val|
+          if val.length != length_byte
+            raise ArgumentError, "Size mismatch in nested struct assignment " +
+              "to #{attr} with value #{val.inspect}"
+          end
+          
+          if val.class != nc
+            warn "Type mismatch in nested struct assignment " +
+              "to #{attr} with value #{val.inspect}"
+          end
+          
+          self[byte_range] = val[val_byte_range]
+        end
+      end
+    end
+  end
+  
+  class << self
+    # Define a nested field in the current subclass of BitStruct,
+    # with the given _name_ and _nested_class_. Length is determined from
+    # _nested_class_.
+    #
+    # In _rest_:
+    #
+    # If a class is provided, use it for the Field class (i.e. <=NestedField).
+    # If a string is provided, use it for the display_name.
+    # If a hash is provided, use it for options.
+    #
+    # WARNING: the accessors have COPY semantics, not reference. When you call a
+    # reader method to get the nested structure, you get a *copy* of that data.
+    #
+    # For example:
+    #
+    #   class Sub < BitStruct
+    #     unsigned :x,    8
+    #   end
+    #
+    #   class A < BitStruct
+    #     nest    :n,  Sub
+    #   end
+    #
+    #   a = A.new
+    #
+    #   p a  # ==> #<A n=#<Sub x=0>>
+    #
+    #   # This fails to set x in a.
+    #   a.n.x = 3
+    #   p a  # ==> #<A n=#<Sub x=0>>
+    #
+    #   # This works
+    #   n = a.n
+    #   n.x = 3
+    #   a.n = n
+    #   p a  # ==> #<A n=#<Sub x=3>>
+    # 
+    def nest(name, nested_class, *rest)
+      opts = parse_options(rest, name, NestedField)
+      opts[:default] ||= nested_class.initial_value.dup
+      opts[:nested_class] = nested_class
+      field = add_field(name, nested_class.bit_length, opts)
+      field
+    end
+    alias struct nest
+  end
+end

Added: topf/trunk/lib/fuzz-struct/octet-field.rb
===================================================================
--- topf/trunk/lib/fuzz-struct/octet-field.rb	                        (rev 0)
+++ topf/trunk/lib/fuzz-struct/octet-field.rb	2007-06-12 09:27:41 UTC (rev 10572)
@@ -0,0 +1,61 @@
+require 'fuzz-struct/char-field'
+
+class BitStruct
+  # Class for char fields that can be accessed with values like
+  # "xxx.xxx.xxx.xxx", where each xxx is up to 3 decimal digits representing a
+  # single octet. The original string-based accessors are still available with
+  # the <tt>_chars</tt> suffix.
+  # 
+  # Declared with BitStruct.octets.
+  class OctetField < BitStruct::CharField
+    # Used in describe.
+    def self.class_name
+      @class_name ||= "octets"
+    end
+    
+    SEPARATOR = "."
+    FORMAT    = "%d"
+    BASE      = 10
+
+    def add_accessors_to(cl, attr = name) # :nodoc:
+      attr_chars = "#{attr}_chars"
+      super(cl, attr_chars)
+      sep   = self.class::SEPARATOR
+      base  = self.class::BASE
+      fmt   = self.class::FORMAT
+      
+      cl.class_eval do
+        define_method attr do ||
+          ary = []
+          send(attr_chars).each_byte do  |c|
+            ary << fmt % c
+          end
+          ary.join(sep)
+        end
+        
+        old_writer = "#{attr_chars}="
+
+        define_method "#{attr}=" do |val|
+          data = val.split(sep).map{|s|s.to_i(base)}.pack("c*")
+          send(old_writer, data)
+        end
+      end
+    end
+  end
+  
+  class << self
+    # Define an octet string field in the current subclass of BitStruct,
+    # with the given _name_ and _length_ (in bits). Trailing nulls are
+    # not considered part of the string. The field is accessed using
+    # period-separated decimal digits.
+    #
+    # If a class is provided, use it for the Field class.
+    # If a string is provided, use it for the display_name.
+    # If a hash is provided, use it for options.
+    #
+    def octets(name, length, *rest)
+      opts = parse_options(rest, name, OctetField)
+      add_field(name, length, opts)
+    end
+  end
+end

Added: topf/trunk/lib/fuzz-struct/pad-field.rb
===================================================================
--- topf/trunk/lib/fuzz-struct/pad-field.rb	                        (rev 0)
+++ topf/trunk/lib/fuzz-struct/pad-field.rb	2007-06-12 09:27:41 UTC (rev 10572)
@@ -0,0 +1,32 @@
+require 'fuzz-struct/fuzz-struct'
+
+class BitStruct
+  # Class for fixed length padding.
+  class PadField < Field
+    # Used in describe.
+    def self.class_name
+      @class_name ||= "padding"
+    end
+
+    def add_accessors_to(cl, attr = name) # :nodoc:
+      # No accessors for padding.
+    end
+
+    def inspectable?; false; end
+  end
+  
+  class << self
+    # Define a padding field in the current subclass of BitStruct,
+    # with the given _name_ and _length_ (in bits).
+    #
+    # If a class is provided, use it for the Field class.
+    # If a string is provided, use it for the display_name.
+    # If a hash is provided, use it for options.
+    #
+    def pad(name, length, *rest)
+      opts = parse_options(rest, name, PadField)
+      add_field(name, length, opts)
+    end
+    alias padding pad
+  end
+end

Added: topf/trunk/lib/fuzz-struct/signed-field.rb
===================================================================
--- topf/trunk/lib/fuzz-struct/signed-field.rb	                        (rev 0)
+++ topf/trunk/lib/fuzz-struct/signed-field.rb	2007-06-12 09:27:41 UTC (rev 10572)
@@ -0,0 +1,238 @@
+require 'fuzz-struct/fuzz-struct'
+
+class BitStruct
+  # Class for signed integers in network order, 1-16 bits, or 8n bits.
+  # Declared with BitStruct.signed.
+  class SignedField < Field
+    # Used in describe.
+    def self.class_name
+      @class_name ||= "signed"
+    end
+    
+    def add_accessors_to(cl, attr = name) # :nodoc:
+      offset_byte = offset / 8
+      offset_bit = offset % 8
+      
+      length_bit = offset_bit + length
+      length_byte = (length_bit/8.0).ceil
+      last_byte = offset_byte + length_byte - 1
+      max = 2**length-1
+      mid = 2**(length-1)
+      max_unsigned = 2**length
+      to_signed = proc {|n| (n>=mid) ? n - max_unsigned : n}
+#      to_signed = proc {|n| (n>=mid) ? -((n ^ max) + 1) : n}
+      
+      divisor = options[:fixed] || options["fixed"]
+      divisor_f = divisor && divisor.to_f
+#      if divisor and not divisor.is_a? Fixnum
+#        raise ArgumentError, "fixed-point divisor must be a fixnum"
+#      end
+
+      endian = (options[:endian] || options["endian"]).to_s
+      case endian
+      when "native"
+        ctl = length <= 16 ? "s" : "l"
+        if length == 16 or length == 32
+          to_signed = proc {|n| n}
+          # with pack support, to_signed can be replaced with no-op
+        end
+      when "little"
+        ctl = length <= 16 ? "v" : "V"
+      when "network", "big", ""
+        ctl = length <= 16 ? "n" : "N"
+      else
+        raise ArgumentError,
+          "Unrecognized endian option: #{endian.inspect}"
+      end
+      
+      data_is_big_endian =
+        ([1234].pack(ctl) == [1234].pack(length <= 16 ? "n" : "N"))
+      
+      if length_byte == 1
+        rest = 8 - length_bit
+        mask  = ["0"*offset_bit + "1"*length + "0"*rest].pack("B8")[0]
+        mask2 = ["1"*offset_bit + "0"*length + "1"*rest].pack("B8")[0]
+        
+        cl.class_eval do
+          if divisor
+            define_method attr do ||
+              to_signed[(self[offset_byte] & mask) >> rest] / divisor_f
+            end
+
+            define_method "#{attr}=" do |val|
+              val = (val * divisor).round
+              self[offset_byte] =
+                (self[offset_byte] & mask2) | ((val<<rest) & mask)
+            end
+
+          else
+            define_method attr do ||
+              to_signed[(self[offset_byte] & mask) >> rest]
+            end
+
+            define_method "#{attr}=" do |val|
+              self[offset_byte] =
+                (self[offset_byte] & mask2) | ((val<<rest) & mask)
+            end
+          end
+        end
+      
+      elsif offset_bit == 0 and length % 8 == 0
+        field_length = length
+        byte_range = offset_byte..last_byte
+        
+        cl.class_eval do
+          case field_length
+          when 8
+            if divisor
+              define_method attr do ||
+                to_signed[self[offset_byte]] / divisor_f
+              end
+
+              define_method "#{attr}=" do |val|
+                val = (val * divisor).round
+                self[offset_byte] = val
+              end
+          
+            else
+              define_method attr do ||
+                to_signed[self[offset_byte]]
+              end
+
+              define_method "#{attr}=" do |val|
+                self[offset_byte] = val
+              end
+            end
+        
+          when 16, 32
+            if divisor
+              define_method attr do ||
+                to_signed[self[byte_range].unpack(ctl).first] / divisor_f
+              end
+
+              define_method "#{attr}=" do |val|
+                val = (val * divisor).round
+                self[byte_range] = [val].pack(ctl)
+              end
+            
+            else
+              define_method attr do ||
+                to_signed[self[byte_range].unpack(ctl).first]
+              end
+
+              define_method "#{attr}=" do |val|
+                self[byte_range] = [val].pack(ctl)
+              end
+            end
+          
+          else
+            reader_helper = proc do |substr|
+              bytes = substr.unpack("C*")
+              bytes.reverse! unless data_is_big_endian
+              bytes.inject do |sum, byte|
+                (sum << 8) + byte
+              end
+            end
+            
+            writer_helper = proc do |val|
+              bytes = []
+              val += max_unsigned if val < 0
+              while val > 0
+                bytes.push val % 256
+                val = val >> 8
+              end
+              if bytes.length < length_byte
+                bytes.concat [0] * (length_byte - bytes.length)
+              end
+
+              bytes.reverse! if data_is_big_endian
+              bytes.pack("C*")
+            end
+            
+            if divisor
+              define_method attr do ||
+                to_signed[reader_helper[self[byte_range]] / divisor_f]
+              end
+              
+              define_method "#{attr}=" do |val|
+                self[byte_range] = writer_helper[(val * divisor).round]
+              end
+            
+            else
+              define_method attr do ||
+                to_signed[reader_helper[self[byte_range]]]
+              end
+              
+              define_method "#{attr}=" do |val|
+                self[byte_range] = writer_helper[val]
+              end
+            end
+          end
+        end
+
+      elsif length_byte == 2 # unaligned field that fits within two whole bytes
+        byte_range = offset_byte..last_byte
+        rest = 16 - length_bit
+        
+        mask  = ["0"*offset_bit + "1"*length + "0"*rest]
+        mask = mask.pack("B16").unpack(ctl).first
+        
+        mask2 = ["1"*offset_bit + "0"*length + "1"*rest]
+        mask2 = mask2.pack("B16").unpack(ctl).first
+
+        cl.class_eval do
+          if divisor
+            define_method attr do ||
+              to_signed[(self[byte_range].unpack(ctl).first & mask) >> rest] /
+                 divisor_f
+            end
+
+            define_method "#{attr}=" do |val|
+              val = (val * divisor).round
+              x = (self[byte_range].unpack(ctl).first & mask2) |
+                ((val<<rest) & mask)
+              self[byte_range] = [x].pack(ctl)
+            end
+
+          else
+            define_method attr do ||
+              to_signed[(self[byte_range].unpack(ctl).first & mask) >> rest]
+            end
+
+            define_method "#{attr}=" do |val|
+              x = (self[byte_range].unpack(ctl).first & mask2) |
+                ((val<<rest) & mask)
+              self[byte_range] = [x].pack(ctl)
+            end
+          end
+        end
+      
+      else
+        raise "unsupported: #{inspect}"
+      end
+    end
+  end
+
+  class << self
+    # Define a signed integer field in the current subclass of BitStruct,
+    # with the given _name_ and _length_ (in bits).
+    #
+    # If a class is provided, use it for the Field class.
+    # If a string is provided, use it for the display_name.
+    # If a hash is provided, use it for options.
+    #
+    # SignedField adds the <tt>:fixed => divisor</tt> option, which specifies
+    # that the internally stored value is interpreted as a fixed point real
+    # number with the specified +divisor+.
+    #
+    # The <tt>:endian => :native</tt> option overrides the default of
+    # <tt>:network</tt> byte ordering, in favor of native byte ordering. Also
+    # permitted are <tt>:big</tt> (same as <tt>:network</tt>) and
+    # <tt>:little</tt>.
+    #
+    def signed name, length, *rest
+      opts = parse_options(rest, name, SignedField)
+      add_field(name, length, opts)
+    end
+  end
+end

Added: topf/trunk/lib/fuzz-struct/text-field.rb
===================================================================
--- topf/trunk/lib/fuzz-struct/text-field.rb	                        (rev 0)
+++ topf/trunk/lib/fuzz-struct/text-field.rb	2007-06-12 09:27:41 UTC (rev 10572)
@@ -0,0 +1,63 @@
+require 'fuzz-struct/fuzz-struct'
+
+class BitStruct
+  # Class for null-terminated printable text strings.
+  # Declared with BitStruct.text.
+  class TextField < Field
+    # Used in describe.
+    def self.class_name
+      @class_name ||= "text"
+    end
+
+    def add_accessors_to(cl, attr = name) # :nodoc:
+      unless offset % 8 == 0
+        raise ArgumentError,
+          "Bad offset, #{offset}, for #{self.class} #{name}." +
+          " Must be multiple of 8."
+      end
+      
+      unless length % 8 == 0
+        raise ArgumentError,
+          "Bad length, #{length}, for #{self.class} #{name}." +
+          " Must be multiple of 8."
+      end
+      
+      offset_byte = offset / 8
+      length_byte = length / 8
+      last_byte = offset_byte + length_byte - 1
+      byte_range = offset_byte..last_byte
+      val_byte_range = 0..length_byte-1
+
+      cl.class_eval do
+        define_method attr do ||
+          self[byte_range].sub(/\0*$/, "").to_s
+        end
+
+        define_method "#{attr}=" do |val|
+          val = val.to_s
+          if val.length < length_byte
+            val += "\0" * (length_byte - val.length)
+          end
+          self[byte_range] = val[val_byte_range]
+        end
+      end
+    end
+  end
+  
+  class << self
+    # Define a printable text string field in the current subclass of BitStruct,
+    # with the given _name_ and _length_ (in bits). Trailing nulls are
+    # _not_ considered part of the string.
+    #
+    # If a class is provided, use it for the Field class.
+    # If a string is provided, use it for the display_name.
+    # If a hash is provided, use it for options.
+    #
+    # Note that the accessors have COPY semantics, not reference.
+    #
+    def text(name, length, *rest)
+      opts = parse_options(rest, name, TextField)
+      add_field(name, length, opts)
+    end
+  end
+end

Added: topf/trunk/lib/fuzz-struct/unsigned-field.rb
===================================================================
--- topf/trunk/lib/fuzz-struct/unsigned-field.rb	                        (rev 0)
+++ topf/trunk/lib/fuzz-struct/unsigned-field.rb	2007-06-12 09:27:41 UTC (rev 10572)
@@ -0,0 +1,228 @@
+require 'fuzz-struct/fuzz-struct'
+
+class BitStruct
+  # Class for unsigned integers in network order, 1-16 bits, or 8n bits.
+  # Declared with BitStruct.unsigned.
+  class UnsignedField < Field
+    # Used in describe.
+    def self.class_name
+      @class_name ||= "unsigned"
+    end
+    
+    def add_accessors_to(cl, attr = name) # :nodoc:
+      offset_byte = offset / 8
+      offset_bit = offset % 8
+      
+      length_bit = offset_bit + length
+      length_byte = (length_bit/8.0).ceil
+      last_byte = offset_byte + length_byte - 1
+      
+      divisor = options[:fixed] || options["fixed"]
+      divisor_f = divisor && divisor.to_f
+#      if divisor and not divisor.is_a? Fixnum
+#        raise ArgumentError, "fixed-point divisor must be a fixnum"
+#      end
+      
+      endian = (options[:endian] || options["endian"]).to_s
+      case endian
+      when "native"
+        ctl = length <= 16 ? "S" : "L"
+      when "little"
+        ctl = length <= 16 ? "v" : "V"
+      when "network", "big", ""
+        ctl = length <= 16 ? "n" : "N"
+      else
+        raise ArgumentError,
+          "Unrecognized endian option: #{endian.inspect}"
+      end
+      
+      data_is_big_endian =
+        ([1234].pack(ctl) == [1234].pack(length <= 16 ? "n" : "N"))
+      
+      if length_byte == 1
+        rest = 8 - length_bit
+        mask  = ["0"*offset_bit + "1"*length + "0"*rest].pack("B8")[0]
+        mask2 = ["1"*offset_bit + "0"*length + "1"*rest].pack("B8")[0]
+        
+        cl.class_eval do
+          if divisor
+            define_method attr do ||
+              ((self[offset_byte] & mask) >> rest) / divisor_f
+            end
+
+            define_method "#{attr}=" do |val|
+              val = (val * divisor).round
+              self[offset_byte] =
+                (self[offset_byte] & mask2) | ((val<<rest) & mask)
+            end
+
+          else
+            define_method attr do ||
+              (self[offset_byte] & mask) >> rest
+            end
+
+            define_method "#{attr}=" do |val|
+              self[offset_byte] =
+                (self[offset_byte] & mask2) | ((val<<rest) & mask)
+            end
+          end
+        end
+      
+      elsif offset_bit == 0 and length % 8 == 0
+        field_length = length
+        byte_range = offset_byte..last_byte
+        
+        cl.class_eval do
+          case field_length
+          when 8
+            if divisor
+              define_method attr do ||
+                self[offset_byte] / divisor_f
+              end
+
+              define_method "#{attr}=" do |val|
+                val = (val * divisor).round
+                self[offset_byte] = val
+              end
+          
+            else
+              define_method attr do ||
+                self[offset_byte]
+              end
+
+              define_method "#{attr}=" do |val|
+                self[offset_byte] = val
+              end
+            end
+        
+          when 16, 32
+            if divisor
+              define_method attr do ||
+                self[byte_range].unpack(ctl).first / divisor_f
+              end
+
+              define_method "#{attr}=" do |val|
+                val = (val * divisor).round
+                self[byte_range] = [val].pack(ctl)
+              end
+            
+            else
+              define_method attr do ||
+                self[byte_range].unpack(ctl).first
+              end
+
+              define_method "#{attr}=" do |val|
+                self[byte_range] = [val].pack(ctl)
+              end
+            end
+          
+          else
+            reader_helper = proc do |substr|
+              bytes = substr.unpack("C*")
+              bytes.reverse! unless data_is_big_endian
+              bytes.inject do |sum, byte|
+                (sum << 8) + byte
+              end
+            end
+            
+            writer_helper = proc do |val|
+              bytes = []
+              while val > 0
+                bytes.push val % 256
+                val = val >> 8
+              end
+              if bytes.length < length_byte
+                bytes.concat [0] * (length_byte - bytes.length)
+              end
+
+              bytes.reverse! if data_is_big_endian
+              bytes.pack("C*")
+            end
+            
+            if divisor
+              define_method attr do ||
+                reader_helper[self[byte_range]] / divisor_f
+              end
+              
+              define_method "#{attr}=" do |val|
+                self[byte_range] = writer_helper[(val * divisor).round]
+              end
+            
+            else
+              define_method attr do ||
+                reader_helper[self[byte_range]]
+              end
+              
+              define_method "#{attr}=" do |val|
+                self[byte_range] = writer_helper[val]
+              end
+            end
+          end
+        end
+
+      elsif length_byte == 2 # unaligned field that fits within two whole bytes
+        byte_range = offset_byte..last_byte
+        rest = 16 - length_bit
+        
+        mask  = ["0"*offset_bit + "1"*length + "0"*rest]
+        mask = mask.pack("B16").unpack(ctl).first
+        
+        mask2 = ["1"*offset_bit + "0"*length + "1"*rest]
+        mask2 = mask2.pack("B16").unpack(ctl).first
+
+        cl.class_eval do
+          if divisor
+            define_method attr do ||
+              ((self[byte_range].unpack(ctl).first & mask) >> rest) /
+                 divisor_f
+            end
+
+            define_method "#{attr}=" do |val|
+              val = (val * divisor).round
+              x = (self[byte_range].unpack(ctl).first & mask2) |
+                ((val<<rest) & mask)
+              self[byte_range] = [x].pack(ctl)
+            end
+
+          else
+            define_method attr do ||
+              (self[byte_range].unpack(ctl).first & mask) >> rest
+            end
+
+            define_method "#{attr}=" do |val|
+              x = (self[byte_range].unpack(ctl).first & mask2) |
+                ((val<<rest) & mask)
+              self[byte_range] = [x].pack(ctl)
+            end
+          end
+        end
+      
+      else
+        raise "unsupported: #{inspect}"
+      end
+    end
+  end
+
+  class << self
+    # Define a unsigned integer field in the current subclass of BitStruct,
+    # with the given _name_ and _length_ (in bits).
+    #
+    # If a class is provided, use it for the Field class.
+    # If a string is provided, use it for the display_name.
+    # If a hash is provided, use it for options.
+    #
+    # UnsignedField adds the <tt>:fixed => divisor</tt> option, which specifies
+    # that the internally stored value is interpreted as a fixed point real
+    # number with the specified +divisor+.
+    #
+    # The <tt>:endian => :native</tt> option overrides the default of
+    # <tt>:network</tt> byte ordering, in favor of native byte ordering. Also
+    # permitted are <tt>:big</tt> (same as <tt>:network</tt>) and
+    # <tt>:little</tt>.
+    #
+    def unsigned name, length, *rest
+      opts = parse_options(rest, name, UnsignedField)
+      add_field(name, length, opts)
+    end
+  end
+end

Added: topf/trunk/lib/fuzz-struct/yaml.rb
===================================================================
--- topf/trunk/lib/fuzz-struct/yaml.rb	                        (rev 0)
+++ topf/trunk/lib/fuzz-struct/yaml.rb	2007-06-12 09:27:41 UTC (rev 10572)
@@ -0,0 +1,70 @@
+require 'fuzz-struct/fuzz-struct'
+require 'yaml'
+
+class BitStruct
+  if RUBY_VERSION == "1.8.2"
+    def is_complex_yaml? # :nodoc:
+      true
+    end
+
+    YAML.add_ruby_type(/^bitstruct/) do |type, val|
+      subtype, subclass = YAML.read_type_class(type, Object)
+      subclass.new(val)
+    end
+
+    def to_yaml_type # :nodoc:
+      "!ruby/bitstruct:#{self.class}"
+    end
+
+    def to_yaml( opts = {} ) # :nodoc:
+      opts[:DocType] = self.class if Hash === opts
+      YAML.quick_emit(self.object_id, opts) do |out|
+        out.map(to_yaml_type) do |map|
+          fields.each do |field|
+            fn = field.name
+            map.add(fn, send(fn))
+          end
+        end
+      end
+    end
+
+  else
+    yaml_as "tag:path.berkeley.edu,2006:bitstruct"
+
+    def to_yaml_properties # :nodoc:
+      yaml_fields = fields.select {|field| field.inspectable?}
+      props = yaml_fields.map {|f| f.name.to_s}
+      if (rest_field = self.class.rest_field)
+        props << rest_field.name.to_s
+      end
+      props
+    end
+
+    # Return YAML representation of the BitStruct.
+    def to_yaml( opts = {} )
+      YAML::quick_emit( object_id, opts ) do |out|
+        out.map( taguri, to_yaml_style ) do |map|
+          to_yaml_properties.each do |m|
+            map.add( m, send( m ) )
+          end
+        end
+      end
+    end
+
+    def self.yaml_new( klass, tag, val ) # :nodoc:
+      unless Hash === val
+        raise YAML::TypeError, "Invalid BitStruct: " + val.inspect
+      end
+
+      bitstruct_name, bitstruct_type = YAML.read_type_class( tag, BitStruct )
+
+      st = bitstruct_type.new
+
+      val.each do |k,v|
+        st.send( "#{k}=", v )
+      end
+
+      st
+    end
+  end
+end

Added: topf/trunk/lib/fuzz-struct.rb
===================================================================
--- topf/trunk/lib/fuzz-struct.rb	                        (rev 0)
+++ topf/trunk/lib/fuzz-struct.rb	2007-06-12 09:27:41 UTC (rev 10572)
@@ -0,0 +1,16 @@
+# A Convenience to load all field classes and yaml handling.
+
+dir = __FILE__
+$:.unshift(File.dirname(dir)) unless
+$:.include?(File.dirname(dir)) || $:.include?(File.expand_path(File.dirname(dir)))
+
+require 'fuzz-struct/unsigned-field'
+require 'fuzz-struct/signed-field'
+require 'fuzz-struct/octet-field'
+require 'fuzz-struct/hex-octet-field'
+require 'fuzz-struct/char-field'
+require 'fuzz-struct/text-field'
+require 'fuzz-struct/nested-field'
+require 'fuzz-struct/float-field'
+require 'fuzz-struct/pad-field'
+require 'fuzz-struct/yaml'

Added: topf/trunk/lib/fuzz.rb
===================================================================
--- topf/trunk/lib/fuzz.rb	                        (rev 0)
+++ topf/trunk/lib/fuzz.rb	2007-06-12 09:27:41 UTC (rev 10572)
@@ -0,0 +1,96 @@
+module Fuzz
+    MAX_RAND = 1000
+    MAX_STRING_PERMUTATIONS = 3 
+
+    def Fuzz.fuzzField(type, length, value)
+        case type.to_s
+            when "BitStruct::CharField"
+                return Fuzz::fuzzString(value, length )
+            when "BitStruct::UnsignedField"
+                return Fuzz::fuzzUnsignedNumber(length.to_i)
+            when "BitStruct::SignedField"
+                return Fuzz::fuzzSignedNumber(length.to_i)
+            when "BitStruct::OctetField"
+                return value.split(".").collect{ |octet| Fuzz::fuzzString(octet, 2) }.join( "." )
+            else
+                if value.class.to_s == "String"
+                    return Fuzz::fuzzString(value, length/8 )
+                else
+                    return value
+                end
+        end
+    end
+
+    def Fuzz.fuzzString(string, length)
+        if Fuzz::MAX_STRING_PERMUTATIONS > 0
+            rand( Fuzz::MAX_STRING_PERMUTATIONS ).times do |i|
+                case rand(5)
+                when 0 
+                    string.insert( rand(string.size), "A"*rand( length ))
+                when 1
+                    string.insert( rand(string.size), "%n"*rand( length ))
+                when 2
+                    string.insert( rand(string.size), Fuzz::randCharacter( string[rand(string.size)] )*rand( length ))
+                when 3
+                    rand(5) == 0 ? string = "" : nil
+                end
+            end
+        end
+        string.gsub(/\\+/, "\\")
+    end
+
+    def Fuzz.randCharacter(default)
+        case rand(9)
+        when 0
+            return '\0'
+        when 1
+            return 0x0.chr
+        when 2
+            return ']'
+        when 3
+            return ')'
+        when 4
+            return '}'
+        when 5
+            return '>'
+        when 6
+            return '\n'
+        when 7
+            return '\''
+        when 8
+            return '\"'
+        else 
+            return default
+        end
+    end
+
+    def Fuzz.fuzzSignedNumber(bits)
+        case rand(3)
+            when 0
+                rand(5).to_i   # small numbers
+            when 1
+                return -1.to_i
+            when 2
+                return (2.power!(bits)-1)/2 #biggest 2s complement number
+            when 3
+                return -(2.power!(bits))/2  #smallest 2s complement number     
+        end
+    end
+
+    def Fuzz.fuzzUnsignedNumber(bits)
+        case rand(1)
+        when 0
+            rand(5).to_i # small numbers
+        when 1
+            return 2.power!(bits)-1       #biggest number
+        end
+    end
+
+    def Fuzz.coreObserver(file)
+        if Dir.new(File.expand_path(File.dirname(file)) ).entries.select{|x| x=~/\.core/}.size != 0
+            return true
+        else
+            return false
+        end
+    end
+end

Added: topf/trunk/lib/pkcs1.rb
===================================================================
--- topf/trunk/lib/pkcs1.rb	                        (rev 0)
+++ topf/trunk/lib/pkcs1.rb	2007-06-12 09:27:41 UTC (rev 10572)
@@ -0,0 +1,823 @@
+# Copyright 2004, 2005  NAKAMURA, Hiroshi <nakahiro at sarion.co.jp>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+require 'openssl'
+require 'digest/md5'
+require 'digest/sha1'
+require 'digest/sha2'
+
+
+module PKCS1
+
+
+# 1 Introduction
+
+
+# 2 Notation
+
+module Util
+module_function
+
+  def nbits(num)
+    idx = num.size * 8 - 1
+    while idx >= 0
+      if num[idx].nonzero?
+        return idx + 1
+      end
+      idx -= 1
+    end
+    0
+  end
+
+  def divceil(a, b)
+    (a + b - 1) / b
+  end
+
+  def xor(a, b)
+    if a.size != b.size
+      raise ArgumentError
+    end
+    ary = []
+    a.size.times do |idx|
+      ary << (a[idx] ^ b[idx])
+    end
+    ary.pack("c*")
+  end
+end
+
+
+# 3 Key types
+module Key
+  class RSAPublicKey
+    attr_reader :n
+    attr_reader :e
+
+    def initialize(n, e)
+      @n, @e = n, e
+      @n_bn = OpenSSL::BN.new(@n.to_s)
+      @e_bn = OpenSSL::BN.new(@e.to_s)
+    end
+
+    def encrypt(m)
+      calc(m)
+    end
+
+    # M = S ^ e (mod n)
+    def verify(s)
+      if s < 0 or s >= @n
+        raise ArgumentError, "signature representative out of range"
+      end
+      calc(s)
+    end
+
+  private
+
+    # c = m ^ e (mod n)
+    def calc(input)
+      input_bn = OpenSSL::BN.new(input.to_s)
+      (input_bn.mod_exp(@e_bn, @n_bn)).to_i
+    end
+  end
+
+
+  class RSAPrivateKey
+    include Util
+
+    attr_reader :public_key
+    attr_reader :version
+    attr_reader :n
+    attr_reader :d
+
+    def initialize(n, e, d)
+      @version = 0
+      @n, @d = n, d
+      @n_bn = OpenSSL::BN.new(@n.to_s)
+      @d_bn = OpenSSL::BN.new(@d.to_s)
+      @public_key = RSAPublicKey.new(n, e)
+    end
+
+    def modbytes
+      divceil(modbits, 8)
+    end
+
+    def modbits
+      nbits(@n)
+    end
+
+    def encrypt(m)
+      @public_key.encrypt(m)
+    end
+
+    def decrypt(c)
+      calc(c)
+    end
+
+    def sign(m)
+      if m < 0 or m >= @n
+        raise ArgumentError, "message representative out of range"
+      end
+      calc(m)
+    end
+
+    def verify(s)
+      @public_key.verify(s)
+    end
+
+  private
+
+    # s = m ^ d (mod n)
+    def calc(input)
+      input_bn = OpenSSL::BN.new(input.to_s)
+      (input_bn.mod_exp(@d_bn, @n_bn)).to_i
+    end
+  end
+
+
+  class RSACRTPrivateKey
+    include Util
+
+    attr_reader :public_key
+    attr_reader :version
+    attr_reader :n
+    attr_reader :p
+    attr_reader :q
+    attr_reader :dp
+    attr_reader :dq
+    attr_reader :qinv
+
+    def initialize(n, e, p, q, dp, dq, qinv)
+      @version = 0
+      @n = n
+      @n_bn = OpenSSL::BN.new(@n.to_s)
+      @p, @q, @dp, @dq, @qinv = p, q, dp, dq, qinv
+      @p_bn = OpenSSL::BN.new(@p.to_s)
+      @q_bn = OpenSSL::BN.new(@q.to_s)
+      @dp_bn = OpenSSL::BN.new(@dp.to_s)
+      @dq_bn = OpenSSL::BN.new(@dq.to_s)
+      @qinv_bn = OpenSSL::BN.new(@qinv.to_s)
+      @public_key = RSAPublicKey.new(n, e)
+    end
+
+    def modbytes
+      divceil(modbits, 8)
+    end
+
+    def modbits
+      nbits(@n)
+    end
+
+    def encrypt(m)
+      @public_key.encrypt(m)
+    end
+
+    def decrypt(c)
+      calc(c)
+    end
+
+    def sign(m)
+      if m < 0 or m >= @n
+        raise ArgumentError, "message representative out of range"
+      end
+      calc(m)
+    end
+
+    def verify(s)
+      @public_key.verify(s)
+    end
+
+  private
+
+    # s1 = m ^ dp (mod p)
+    # s2 = m ^ dq (mod q)
+    # h = (s1 - s2) * qinv (mod p)
+    # s = s2 + q * h
+    def calc(input)
+      input_bn = OpenSSL::BN.new(input.to_s)
+      s1 = input_bn.mod_exp(@dp_bn, @p_bn)
+      s2 = input_bn.mod_exp(@dq_bn, @q_bn)
+      h = @qinv_bn.mod_mul(s1 - s2, @p_bn)
+      (s2 + q * h).to_i
+    end
+  end
+
+
+  RSA = RSAPrivateKey
+  RSACRT = RSACRTPrivateKey
+end
+
+
+# 4 Data conversion primitives
+module DataConversion
+module_function
+
+  # Integer to Octet String primitive
+  def i2osp(x, len)
+    if x >= 256 ** len
+      raise ArgumentError, "integer too large"
+    end
+    os = to_bytes(x).sub(/^\x00+/, '')
+    "\x00" * (len - os.size) + os
+  end
+
+  # Octet String to Integer primitive
+  def os2ip(x)
+    from_bytes(x)
+  end
+
+  def to_bytes(num)
+    bits = num.size * 8
+    pos = value = 0
+    str = ""
+    for idx in 0..(bits - 1)
+      if num[idx].nonzero?
+        value |= (num[idx] << pos)
+      end
+      pos += 1
+      if pos == 32
+        str = [value].pack("N") + str
+        pos = value = 0
+      end
+    end
+    str
+  end
+
+  def from_bytes(bytes)
+    num = 0
+    bytes.each_byte do |c|
+      num <<= 8
+      num |= c
+    end
+    num
+  end
+end
+
+
+# 5 Cryptographic primitives
+module CryptographicPrimitive
+module_function
+
+  def rsaep(key, msg)
+    key.encrypt(msg)
+  end
+
+  def rsadp(key, cipher)
+    key.decrypt(cipher)
+  end
+
+  def rsasp1(key, msg)
+    key.sign(msg)
+  end
+
+  def rsavp1(key, sig)
+    key.verify(sig)
+  end
+end
+
+
+# 6 Overview of schemes
+
+
+# 7 Encryption schemes
+module EncryptionScheme
+module_function
+  
+  def rsaes_oaep_encrypt(key, msg, seed = nil, label = '')
+    RSAESOAEP.new.encrypt(key, msg, seed, label)
+  end
+
+  def rsaes_oaep_decrypt(key, cipher, seed = nil, label = '')
+    RSAESOAEP.new.decrypt(key, cipher, seed, label)
+  end
+
+
+  class RSAESOAEP
+    include DataConversion
+    include CryptographicPrimitive
+
+    def initialize(digest = Digest::SHA1)
+      @hlen = Hash.size(digest)
+      @encryption_encoder = EncryptionEncoding::EMEOAEP.new(digest)
+    end
+
+    def encrypt(key, msg, seed = nil, label = '')
+      k = key.modbytes
+      if msg.size > k - 2 * @hlen - 2
+        raise ArgumentError, "message too long"
+      end
+      em = @encryption_encoder.encode(msg, k, seed, label)
+      m = os2ip(em)
+      c = rsaep(key, m)
+      i2osp(c, k)
+    end
+
+    def decrypt(key, cipher, label = '')
+      k = key.modbytes
+      if cipher.size != k
+        raise ArgumentError, "decryption error"
+      end
+      if k < 2 * @hlen + 2
+        raise ArgumentError, "decryption error"
+      end
+      c = os2ip(cipher)
+      m = rsadp(key, c)
+      em = i2osp(m, k)
+      @encryption_encoder.decode(em, k, label)
+    end
+  end
+end
+
+
+# 7.1.1 1.b and 7.1.2 3 Encoding methods for encryption
+module EncryptionEncoding
+module_function
+
+  def eme_oaep_encode(msg, embits, seed = nil)
+    EMEOAEP.new.encode(msg, embits, seed)
+  end
+
+  def eme_oaep_decode(msg, em, embits)
+    EMEOAEP.new.decode(msg, em, embits)
+  end
+
+
+  class EMEOAEP
+    include Util
+
+    def initialize(digest = Digest::SHA1, mgf = nil)
+      @digest = digest
+      @hlen = Hash.size(@digest)
+      @mgf = mgf || MaskGeneration::MGF1.new(@digest)
+    end
+
+    def encode(msg, embytes, seed = nil, label = '')
+      lhash = dohash(label)
+      ps = "\x00" * (embytes - msg.size - 2 * @hlen - 2)
+      db = lhash + ps + "\x01" + msg
+      seed ||= OpenSSL::Random.random_bytes(@hlen)
+      dbmask = @mgf.generate(seed, embytes - @hlen - 1)
+      maskeddb = xor(db, dbmask)
+      seedmask = @mgf.generate(maskeddb, @hlen)
+      maskedseed = xor(seed, seedmask)
+      "\x00" + maskedseed + maskeddb
+    end
+
+    def decode(em, embytes, label = '')
+      lhash = dohash(label)
+      y = em[0]
+      maskedseed = em[1, @hlen]
+      maskeddb = em[1 + @hlen, embytes - @hlen - 1]
+      seedmask = @mgf.generate(maskeddb, @hlen)
+      seed = xor(maskedseed, seedmask)
+      dbmask = @mgf.generate(seed, embytes - @hlen - 1)
+      db = xor(maskeddb, dbmask)
+      lhashdash = db[0, @hlen]
+      if lhashdash != lhash
+        raise ArgumentError, "decryption error"
+      end
+      psm = db[@hlen, embytes - 2 * @hlen - 1]
+      if /\A(\x00*)\x01([\x00-\xff]*)\z/ =~ psm
+        ps, m = $1, $2
+      else
+        raise ArgumentError, "decryption error"
+      end
+      m
+    end
+
+  private
+
+    def dohash(msg)
+      @digest.digest(msg)
+    end
+  end
+end
+
+
+# 8 Signature schemes
+module SignatureScheme
+module_function
+
+  def rsassa_pss_sign(key, msg)
+    RSASSAPSS.new.sign(key, msg)
+  end
+
+  def rsassa_pss_verify(key, msg, sig)
+    RSASSAPSS.new.verify(key, msg, sig)
+  end
+
+  def rsassa_pss_sign_hash(key, hash)
+    RSASSAPSS.new.sign_hash(key, hash)
+  end
+
+  def rsassa_pss_verify_hash(key, hash, sig)
+    RSASSAPSS.new.verify_hash(key, hash, sig)
+  end
+
+  def rsassa_pkcs1v1_5_sign(key, msg)
+    RSASSAPKCS1v1_5.new.sign(key, msg)
+  end
+
+  def rsassa_pkcs1v1_5_verify(key, msg, sig)
+    RSASSAPKCS1v1_5.new.verify(key, msg, sig)
+  end
+
+  def rsassa_pkcs1v1_5_sign_hash(key, hash)
+    RSASSAPKCS1v1_5.new.sign_hash(key, hash)
+  end
+
+  def rsassa_pkcs1v1_5_verify_hash(key, hash, sig)
+    RSASSAPKCS1v1_5.new.verify_hash(key, hash, sig)
+  end
+
+
+  class RSASSAPSS
+    include Util
+    include DataConversion
+    include CryptographicPrimitive
+
+    def initialize(digest = Digest::SHA1, slen = 20, mgf = nil)
+      @signature_encoder = SignatureEncoding::EMSAPSS.new(digest, slen, mgf)
+    end
+
+    def sign(key, msg, salt = nil)
+      modbits = key.modbits
+      em = @signature_encoder.encode(msg, modbits - 1, salt)
+      sign_em(key, em, modbits)
+    end
+
+    def sign_hash(key, hash, salt = nil)
+      modbits = key.modbits
+      em = @signature_encoder.encode_hash(hash, modbits - 1, salt)
+      sign_em(key, em, modbits)
+    end
+
+    def verify(key, msg, sig)
+      modbits = key.modbits
+      em = decode_em(key, sig, modbits)
+      @signature_encoder.verify(msg, em, modbits - 1)
+    end
+
+    def verify_hash(key, hash, sig)
+      modbits = key.modbits
+      em = decode_em(key, sig, modbits)
+      @signature_encoder.verify_hash(hash, em, modbits - 1)
+    end
+
+  private
+
+    def sign_em(key, em, modbits)
+      m = os2ip(em)
+      s = rsasp1(key, m)
+      s = i2osp(s, divceil(modbits, 8))
+      s
+    end
+
+    def decode_em(key, sig, modbits)
+      if sig.size != divceil(modbits - 1, 8)
+        raise ArgumentError, "invalid signature"
+      end
+      s = os2ip(sig)
+      m = rsavp1(key, s)
+      emlen = divceil(modbits - 1, 8)
+      i2osp(m, emlen)
+    end
+  end
+
+
+  class RSASSAPKCS1v1_5
+    include DataConversion
+    include CryptographicPrimitive
+
+    def initialize(digest = Digest::SHA1)
+      @signature_encoder = SignatureEncoding::EMSAPKCS1v1_5.new(digest)
+    end
+
+    def sign(key, msg)
+      k = key.modbytes
+      em = @signature_encoder.encode(msg, k)
+      sign_em(key, em, k)
+    end
+
+    def sign_hash(key, hash)
+      k = key.modbytes
+      em = @signature_encoder.encode_hash(hash, k)
+      sign_em(key, em, k)
+    end
+
+    def verify(key, msg, sig)
+      k = key.modbytes
+      em = decode_em(key, sig, k)
+      emdash = @signature_encoder.encode(msg, k)
+      unless em == emdash
+        raise ArgumentError, "invalid signature"
+      end
+      true
+    end
+
+    def verify_hash(key, hash, sig)
+      k = key.modbytes
+      em = decode_em(key, sig, k)
+      emdash = @signature_encoder.encode_hash(hash, k)
+      unless em == emdash
+        raise ArgumentError, "invalid signature"
+      end
+      true
+    end
+
+  private
+
+    def sign_em(key, em, k)
+      m = os2ip(em)
+      s = rsasp1(key, m)
+      i2osp(s, k)
+    end
+
+    def decode_em(key, sig, k)
+      if sig.size != k
+        raise ArgumentError, "invalid signature"
+      end
+      s = os2ip(sig)
+      m = rsavp1(key, s)
+      i2osp(m, k)
+    end
+  end
+end
+
+
+# 9 Encoding methods for signatures with appendix
+module SignatureEncoding
+module_function
+
+  def emsa_pss_encode(msg, embits, salt = nil)
+    EMSAPSS.new.encode(msg, embits, salt)
+  end
+
+  def emsa_pss_verify(msg, em, embits)
+    EMSAPSS.new.verify(msg, em, embits)
+  end
+
+  def emsa_pkcs1_v1_5_encode(msg, emlen)
+    EMSAPKCS1v1_5.new.encode(msg, emlen)
+  end
+
+
+  class EMSAPSS
+    include Util
+
+    def initialize(digest = Digest::SHA1, slen = 20, mgf = nil)
+      @digest = digest
+      @slen = slen
+      @hlen = Hash.size(@digest)
+      @mgf = mgf || MaskGeneration::MGF1.new(@digest)
+    end
+
+    def encode(msg, embits, salt = nil)
+      mhash = dohash(msg)
+      encode_hash(mhash, embits, salt)
+    end
+
+    def encode_hash(mhash, embits, salt = nil)
+      emlen = divceil(embits, 8)
+      if emlen < @hlen + @slen + 2
+        raise ArgumentError, "encoding error"
+      end
+      salt ||= OpenSSL::Random.random_bytes(@slen)
+      mdash = "\x00" * 8 + mhash + salt
+      h = dohash(mdash)
+      ps = "\x00" * (emlen - @slen - @hlen - 2)
+      db = ps + "\x01" + salt
+      dbmask = @mgf.generate(h, emlen - @hlen - 1)
+      maskeddb = xor(db, dbmask)
+      maskeddb[0] &= (0xff >> (8 * emlen - embits))
+      p [emlen, embits, (0xff >> (8 * emlen - embits))]
+      em = maskeddb + h + "\xbc"
+      em
+    end
+
+    def verify(msg, em, embits)
+      mhash = dohash(msg)
+      verify_hash(mhash, em, embits)
+    end
+
+    def verify_hash(mhash, em, embits)
+      emlen = divceil(embits, 8)
+      if emlen < @hlen + @slen + 2
+        raise ArgumentError, "inconsistent"
+      end
+      if em[-1] != 0xbc
+        raise ArgumentError, "inconsistent"
+      end
+      maskeddb = em[0, emlen - @hlen - 1]
+      h = em[emlen - @hlen - 1, @hlen]
+      if maskeddb[0] & (0xff >> (8 * emlen - embits)) != maskeddb[0]
+        raise ArgumentError, "inconsistent"
+      end
+      dbmask = @mgf.generate(h, emlen - @hlen - 1)
+      db = xor(maskeddb, dbmask)
+      db[0] &= (0xff >> (8 * emlen - embits))
+      if /\A\x00+\z/ !~ db[0, emlen - @hlen - @slen - 2]
+        raise ArgumentError, "inconsistent"
+      end
+      if db[emlen - @hlen - @slen - 2] != 0x01
+        raise ArgumentError, "inconsistent"
+      end
+      salt = db[db.size - @slen, @slen]
+      mdash = "\x00" * 8 + mhash + salt
+      hdash = dohash(mdash)
+      if h != hdash
+        raise ArgumentError, "inconsistent"
+      end
+      true
+    end
+
+  private
+
+    def dohash(msg)
+      @digest.digest(msg)
+    end
+  end
+
+
+  class EMSAPKCS1v1_5
+    def initialize(digest = Digest::SHA1)
+      @digest = digest
+    end
+
+    def encode(msg, emlen)
+      h = dohash(msg)
+      encode_hash(h, emlen)
+    end
+
+    def encode_hash(h, emlen)
+      t = Hash.asnoid(@digest) + h
+      if emlen < t.size + 11
+        raise ArgumentError, "intended encoded message length too short"
+      end
+      ps = "\xff" * (emlen - t.size - 3)
+      "\x00\x01" + ps + "\x00" + t
+    end
+
+  private
+
+    def dohash(msg)
+      @digest.digest(msg)
+    end
+  end
+end
+
+
+# B.1 Hash functions
+module Hash
+  ALGORITHMS = {
+    # MD5: 1.2.840.113549.2.5
+    Digest::MD5 => [16, [0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x86, 0x48,
+      0x86, 0xF7, 0x0D, 0x02, 0x05, 0x00, 0x04, 0x10]],
+    # SHA-1: 1.3.14.3.2.26
+    Digest::SHA1 => [20, [0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0E, 0x03,
+      0x02, 0x1A, 0x05, 0x00, 0x04, 0x14]],
+    # SHA256: 2.16.840.1.101.3.4.2.1
+    Digest::SHA256 => [32, [0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+      0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20]],
+    # SHA384: 2.16.840.1.101.3.4.2.2
+    Digest::SHA384 => [48, [0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+      0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30]],
+    # SHA512: 2.16.840.1.101.3.4.2.3
+    Digest::SHA512 => [64, [0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+      0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40]],
+  }
+
+module_function
+
+  def asnoid(digest)
+    unless ALGORITHMS.key?(digest)
+      raise ArgumentError, "unknown digest: #{digest}"
+    end
+    ALGORITHMS[digest][1].pack("c*")
+  end
+
+  def size(digest)
+    unless ALGORITHMS.key?(digest)
+      raise ArgumentError, "unknown digest: #{digest}"
+    end
+    ALGORITHMS[digest][0]
+  end
+end
+
+
+# B.2 Mask generation functions
+module MaskGeneration
+module_function
+
+  def mgf1(seed, masklen)
+    MGF1.new.generate(seed, masklen)
+  end
+
+
+  class MGF1
+    include Util
+    include DataConversion
+
+    def initialize(digest = Digest::SHA1)
+      @digest = digest
+      @hlen = Hash.size(@digest)
+    end
+
+    def generate(seed, masklen)
+      if masklen > (2 << 31) * @hlen
+        raise ArgumentError, "mask too long"
+      end
+      t = ""
+      divceil(masklen, @hlen).times do |counter|
+        t += dohash(seed + i2osp(counter, 4))
+      end
+      t[0, masklen]
+    end
+
+  private
+
+    def dohash(msg)
+      @digest.digest(msg)
+    end
+  end
+end
+
+
+end
+
+
+if $0 == __FILE__
+  pkeyfile = ARGV.shift or raise "pkey file not given"
+  osslkey = OpenSSL::PKey::RSA.new(File.read(pkeyfile))
+  n = osslkey.n.to_i
+  e = osslkey.e.to_i
+  d = osslkey.d.to_i
+  p = osslkey.p.to_i
+  q = osslkey.q.to_i
+  dp = osslkey.dmp1.to_i
+  dq = osslkey.dmq1.to_i
+  qinv = osslkey.iqmp.to_i
+
+  require 'pgp/hexdump'
+
+  rsapss = PKCS1::SignatureScheme::RSASSAPSS.new(Digest::SHA256, 0, PKCS1::MaskGeneration::MGF1.new(Digest::SHA1))
+  key = PKCS1::Key::RSA.new(n, e, d)
+  puts PGP::HexDump.encode(rsapss.sign(key, "hello world"))
+  exit
+
+  puts
+
+  key2 = PKCS1::Key::RSACRTPrivateKey.new(n, d, p, q, dp, dq, qinv)
+  puts PGP::HexDump.encode(rsapss.sign_hash(key2, "hello world", "\0"*8))
+
+  p PKCS1::DataConversion.i2osp(65537, 3)
+  p PKCS1::DataConversion.os2ip(PKCS1::DataConversion.i2osp(65537, 3))
+
+  key = PKCS1::Key::RSA.new(osslkey.n.to_i, osslkey.e.to_i, osslkey.d.to_i)
+  msg = "hello"
+  p PKCS1::DataConversion.i2osp(PKCS1::CryptographicPrimitive.rsadp(key, PKCS1::CryptographicPrimitive.rsaep(key, PKCS1::DataConversion.os2ip(msg))), msg.size)
+  p PKCS1::DataConversion.i2osp(PKCS1::CryptographicPrimitive.rsavp1(key, PKCS1::CryptographicPrimitive.rsasp1(key, PKCS1::DataConversion.os2ip(msg))), msg.size)
+
+  p PKCS1::MaskGeneration.mgf1("abc", 20)
+  p PKCS1::MaskGeneration.mgf1("abcd", 20)
+
+  pss = PKCS1::SignatureEncoding::EMSAPSS.new(Digest::SHA1, 8)
+  p pss.encode("hello", 1023)
+  p pss.verify("hello", pss.encode("hello", 1023), 1023)
+
+  rsapss = PKCS1::SignatureScheme::RSASSAPSS.new(Digest::SHA1, 0)
+  p rsapss.sign(key, "hello")
+  p rsapss.verify(key, "hello", rsapss.sign(key, "hello"))
+
+  msg = "foo\nbar" * 1024
+  hash = Digest::SHA1.digest(msg)
+  p rsapss.verify_hash(key, hash, rsapss.sign_hash(key, hash))
+  p rsapss.verify(key, msg, rsapss.sign_hash(key, hash))
+  p rsapss.verify_hash(key, hash, rsapss.sign(key, msg))
+
+  exit
+
+  p PKCS1::SignatureEncoding.emsa_pkcs1_v1_5_encode("foo", 128)
+  rsapkcs1 = PKCS1::SignatureScheme::RSASSAPKCS1v1_5.new(Digest::SHA1)
+  p rsapkcs1.sign(key, "hello")
+  p rsapkcs1.verify(key, "hello", rsapkcs1.sign(key, "hello"))
+  p osslkey.sign(OpenSSL::Digest::SHA1.new, "hello")
+  p osslkey.verify(OpenSSL::Digest::SHA1.new, rsapkcs1.sign(key, "hello"), "hello")
+
+  rsaoaep = PKCS1::EncryptionScheme::RSAESOAEP.new(Digest::SHA1)
+  msg = "hello"
+  p rsaoaep.encrypt(key, msg)
+  p osslkey.public_encrypt(msg, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
+  p osslkey.private_decrypt(rsaoaep.encrypt(key, msg), OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING) == msg
+  p rsaoaep.decrypt(key, rsaoaep.encrypt(key, msg)) == msg
+end

Added: topf/trunk/lib/topf.rb
===================================================================
--- topf/trunk/lib/topf.rb	                        (rev 0)
+++ topf/trunk/lib/topf.rb	2007-06-12 09:27:41 UTC (rev 10572)
@@ -0,0 +1,9 @@
+dir = __FILE__
+$:.unshift(File.dirname(dir)) unless
+$:.include?(File.dirname(dir)) || $:.include?(File.expand_path(File.dirname(dir)))
+
+require "fuzz-struct"
+require "fuzz"
+require "pkcs1"
+require "dir"
+require "control"

Added: topf/trunk/stuff/fuzz-private.pem
===================================================================
--- topf/trunk/stuff/fuzz-private.pem	                        (rev 0)
+++ topf/trunk/stuff/fuzz-private.pem	2007-06-12 09:27:41 UTC (rev 10572)
@@ -0,0 +1,30 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,CAB4E89285F21271
+
+l5DJpslp/MUrcQN4nMfv6D1kivwtUYKsu6dhH7v6/CDz/BX4onqoaHVxYnk5uiJA
+O05hnq9zGtinFa/lKt+Cj3ibPLkDbMxvmHP7FvP9UF5Xe5oyM02aIvoqpVplNA49
+NGcpTUpGXwvMYZC8YChG6T02vqCtMGrGImJ8LFnoypqMM2dgng/hCRkbxphOHf1j
+JfnPFFTHhh7728gwszJaOrH8egPCm8w9yWtkbB2ph+VSS3qUvwyhTHsddfgmHMni
+ROwMKN8KLIeSJGf5XQRmaOFCm70PgYZN7Cx2WKrZP26uIkbvejyywB5+5BNSPZci
+vBjULFZ1bf4wo/wzTNAQ4AyNu7ckNzeOuBhFZ0VDRi+Zywy6/pRoLNZlq7TZHjk2
+bvy+X2pk090ZzVkrT/7enWATDnRdetPL/4HTe0lQTa3eo2SD+awN9U+Liat+5X32
+lcuu9vU4yl56ExJe5VGTa88D0j7CIivJ+AbraOPLwLIRLwbTamJtOHkMMKr7LKfd
+PUVJwqJz1sIjiTaJ6SKOIZ/NtmXfh0SpzVRCuZ3AafL+Jq3kJeAea2pDEfp1u5Zz
+O23h0cu2eG35Ryc5+tLbSxuz2ukBaJp5hDOaqEiZ7KYuKyoBTysL2RKNK0pUzg5B
+2SHRsuGSy4yMcyn845W6llKMb6dQOfPx3V1NkvjOfcsfU7GAYAqOc+a22Jfz2jjX
+OUYtjuEP/IwBu4HnjlfWUQfhhxmgsiS3DvGqMykjr+6pScr7PIGR57axlSj7GFDP
+/kVvUXwPV1hQgjcN/II2RDe8oBJYDoBqi30nlqkDqR6qH7/HSCuzBR3SvLtU+hy7
+WfaICWE5U2XgxY27l+NqiOFrwJic/OzeOY0YEU0PqPnnLeGLoFzgw2GYf4GlqOMZ
+njyAbD7GLR61xMyQxwN0bspb+3uoq47ptJLmcdsB1ybJJsONx+Z/ZH1IQU1navGT
+uNYzN0JNAzKbiflcoykVdBfzLOMv0IjfVCLphg+akljvahf0QUC1LEYSrpHhpaxc
+eMnJxSf+k3AzIQLUIgVJoR5aRcMINlbZVc64HH3jccy8otP77FyB/AwP2FLU9D7U
+R5dWAUodLpDOWgrZLjJV1tB6zSqWsHQ/wWnAd4t+Q/nRxW+UOWd+/OWI/LwFFBra
+Vu8rdIk6MntZ3HDzuW2W5v7U8O5Xhwejp5TPdjs2gdwEoJeNi4HA6gSOuYwVGKng
+j5Eari7tfJe6f6LxSPcLto4jGNP1Q2RNN4ERGt+YPqPPWUsqOz3qFwNBfBtykP5s
+mKb4Hi3ZbPLyuHrIEX12xhRk+s3PEQAKYY/dpllv6ooGf3gaXT5PPjx0U2HGZJec
+My+N0D3/lrYTTBVH3nGufqssIYdC1sCtDyBWyX68UtEeDimjTqfdWTqzfVSV9F0U
+h8CwfTQKJZv0F4q0VBNWNz5BSeXSLAX1jq8TsebuQjL591z1rzlzTudsFzqiqmWk
+YD2+uUW9GxTRlhFl8pr6yWtV1vwQ/moyKFRRRRv8RjKqJnTSsUv1aQdlC6d7fud+
+GEoBhrozDF1Bfnaud2nS8AHN3XBIQxgQTv04szFXoZe8vhpOPMLehElBRcw79s+R
+-----END RSA PRIVATE KEY-----

Added: topf/trunk/stuff/fuzz-public.pem
===================================================================
--- topf/trunk/stuff/fuzz-public.pem	                        (rev 0)
+++ topf/trunk/stuff/fuzz-public.pem	2007-06-12 09:27:41 UTC (rev 10572)
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICmDCCAYACAQAwUzELMAkGA1UEBhMCREUxHTAbBgNVBAoTFEZ1enppbmcgT3Jn
+YW5pc2F0aW9uMQ0wCwYDVQQLEwRGdXp6MRYwFAYDVQQDEw1GUUROLUZ1enpob3N0
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlBb6Byx7MKX3cPh9cD/v
+/7FamBnCfrO3+dCNrEzZRuaS9BMbF0FbxKDeWdbEpytPw7h1Khse4wGCg3JafL3k
+z/Lt/A87MfNMbeBYkfXUA436zdp4ambgeV55AGWxHbLCDCqLc4l7o0wVvwXdNV08
+lZj+Co517ZD2jiKeJOfyd6D1Vt1i/kx0CcLYuY2aQCwXXiCvKaA3j4pWN33eRIFv
+CO8yDB1DyxmhQJdzt8xRQLohV5CVWYgi9krk/pcZd5beczSYLy3iokMvjN42y5hY
+oytd/kOXw7R5QW1+0gqU7RRpkDQ8QhBLuawg6JpnCXXSULylgw5u03bszXHCFSvX
+KQIDAQABoAAwDQYJKoZIhvcNAQEFBQADggEBAGqziC9+GwZFPO0Oij8BMuP9B7yN
+vi5+04dVQ3865hVAhI0eFhaa3vkR1zXe8dDEi0IlVPgKIjBht3pjCTlwPrAM0sAv
+zeEzZUlpng+9QDL47yumS6QZZ1PceORio0hwaOHsgBVfx86O1N0WXniuSTAsawkJ
+5DKsH4EHHmIuQgTUXwVT5Aby2edD7h9Tqv0gvMF63dXqtsACUNPKpJT+HSiaph5P
+cQuap2OB1QpGTqo5V9cX2sml5KMRhUiJXGwSuhSsY/LgS8b/c4M7UyPDpfVb9u6D
+QQ4XS9MqEXjCtuMTC+uaomzbHtdzRTkBvZgNUL4RzlkJ/d3aoal3fl81sCQ=
+-----END CERTIFICATE REQUEST-----

Added: topf/trunk/stuff/output-dir.rb
===================================================================
--- topf/trunk/stuff/output-dir.rb	                        (rev 0)
+++ topf/trunk/stuff/output-dir.rb	2007-06-12 09:27:41 UTC (rev 10572)
@@ -0,0 +1,6 @@
+require "zlib"
+
+begin
+    puts Zlib::Inflate.new.inflate( Net::HTTP.get(URI.parse( 'http://127.0.0.1:2323/tor/server/all.z') ) )
+end
+

Added: topf/trunk/tor-dir-fuzz.rb
===================================================================
--- topf/trunk/tor-dir-fuzz.rb	                        (rev 0)
+++ topf/trunk/tor-dir-fuzz.rb	2007-06-12 09:27:41 UTC (rev 10572)
@@ -0,0 +1,40 @@
+require "lib/topf"
+
+require "net/http"
+require "base64"
+require "yaml"
+
+begin
+    config = YAML::load_file "config.yml"
+
+    host    = config["HOST"] 
+    port    = config["PORT"] 
+    keyFile = config["KEYFILE"]
+
+    raise "option missing" if !host or !port or !keyFile
+
+    reset = 0
+    rd = TOP::Dir::RouterDescriptor.new( keyFile, host, port )
+    dirServer = Net::HTTP.new(host, port)
+
+    begin
+        puts "lets stress #{host} a bit..."
+        while true
+            response, body = dirServer.post "/tor/", rd.to_s
+            rd.fuzz!
+            if (reset += 1) == 10
+                rd = TOP::Dir::RouterDescriptor.new
+                reset = 0
+            end
+        end
+    rescue
+        # something happened.. show the error-message and routerdescriptor that caused this mess..
+        puts $!
+
+        puts ">"*50
+        puts rd.to_s
+        puts ">"*50
+
+        dirServer.finish
+    end
+end



More information about the tor-commits mailing list