# -*- coding: binary -*-

require 'rex/encoding/xor/exceptions'
require 'rex/encoding/xor/generic'

#
# Routine for xor encoding a buffer by a 2-byte (intel word) key.  The perl
# version used to pad this buffer out to a 2-byte boundary, but I can't think
# of a good reason to do that anymore, so this doesn't.
#

module Rex
module Encoding
module Xor

class DwordAdditive < Generic

  def DwordAdditive.keysize
    4
  end

  def DwordAdditive._packspec
    'V'
  end

  def DwordAdditive.pack_key(key)
    return [ key ].pack(_packspec)
  end
  def DwordAdditive.unpack_key(key)
    return key.unpack(_packspec)[0]
  end

  # hook in the key mutation routine of encode for the additive feedback
  def DwordAdditive._encode_mutate_key(buf, key, pos, len)
    if (pos + 1) % len == 0
      # add the last len bytes (in this case 4) with the key,
      # dropping off any overflow
      key = pack_key(
        unpack_key(key) + unpack_key(buf[pos - (len - 1), len]) &
          (1 << (len << 3)) - 1
      )
    end

    return key
  end

  #
  # I realize this algorithm is broken.  We invalidate some keys
  # in _find_bad_keys that could actually be perfectly fine.  However,
  # it seems to work ok for now, and this is all just a lame adhoc method.
  # Maybe someday we can revisit this and make it a bit less ghetto...
  #

  def DwordAdditive._find_good_key(data, badkeys, badchars)

    ksize  = keysize
    kstart = ""
    ksize.times { kstart << rand(256) } # random key starting place

    key = kstart.dup

    #
    # now for the ghettoness of an algorithm:
    #  try the random key we picked
    #  if the key failed, figure out which key byte corresponds
    #  increment that key byte
    #  if we wrapped a byte all the way around, fail :(
    #

    loop do
      # ok, try to encode it, any bad chars present?
      pos = _check(data, key, badchars)

      # yay, no problems, we found a key!
      break if !pos

      strip = pos % ksize

      # increment the offending key byte
      key[strip] = key[strip] + 1 & 0xff

      # We wrapped around!
      if key[strip] == kstart[strip]
        raise KeySearchError, "Key space exhausted on strip #{strip}!", caller
      end
    end

    return key
  end

end end end end # DwordAdditive/Xor/Encoding/Rex

