binpad

Padding binary data.

slebetman : I developed the following snippet as a general purpose padding-unpadding mechanism. I needed it for encrypting arbritrary sized data using block cyphers like blowfish or des.

It uses an escape character mechanism for padding binary data so it doesn't matter if the padding character exists in the original data. Also, it ensures that character escapes are themselves aligned to the block size so it also adds padding in the middle of the data in cases where the 2 byte escape sequence crosses block boundries.

The final feature is that instead of a single padding character it randomly selects from a list of padding characters (what can I say, I'm paranoid). Hopefully this makes it harder to crack a given cypher.

The code:

  #############################################
  # Implements byte padding for block based protocols
  # thus converting them into stream capable protocols.
  #############################################

  ###
  # Randomly choose a list item
  ###
  proc lrandom ls {
    set len [llength $ls]
    lindex $ls [expr {int(rand()*$len)}]
  }

  ##
  # Use this proc to define the escape character you
  # want to use and the list of padding characters:
  ##
  proc makemaps {escapeChar charList} {
    global pad

    # Chars used for padding (to be chosen at random):
    set pad(chars) $charList
    set pad(escape) $escapeChar

    # Generate mapping table:
    set i 1
    set pad(map) [list $escapeChar ${escapeChar}[binary format c $i]]
    foreach x $charList {
      incr i
      lappend pad(map) $x
      lappend pad(map) $escapeChar$i
    }
    unset i

    # Generate reverse mapping table:
    set pad(unmap) ""
    foreach {x y} $pad(map) {
      lappend pad(unmap) $y
      lappend pad(unmap) $x
    }
    foreach x $charList {
      lappend pad(unmap) $x
      lappend pad(unmap) {}
    }
  }

  # My preferred escape character and set of
  # padding characters
  makemaps \252 {
    \377 \376 \375 \374 \373 \372 \371 \370
    \367 \366 \365 \364 \363 \362 \361 \360
  }

  ##
  # Makes data length a multiple of n by padding:
  ##
  proc pad {n data} {
    global pad
    set data [string map $pad(map) $data]
    set ret ""
    set nn [expr {$n-1}]
    while {[string length $data] != 0} {
      set x [string range $data 0 $nn]
      if {[string index $x end] == "$pad(escape)"} {
        set x [string range $x 0 end-1]
        set data [string range $data $nn end]
      } else {
        set data [string range $data $n end]
      }
      set len [string length $x]
      set r [expr {($n-($len%$n))%$n}]
      for {} {$r > 0} {incr r -1} {
        append x [lrandom $pad(chars)]
      }
      append ret $x
    }
    return $ret
  }

  ##
  # Convert padded data back to original form:
  ##
  proc unpad {data} {
    global pad
    string map $pad(unmap) $data
  }

An example of how to use the above pad and unpad commands with DES:

  # Use [pad 8] since DES block size is 8 bytes:
  set cryptedtext [DES::des -mode encode -key "12345678" [pad 8 $plaintext]]
  set text [unpad [DES::des -mode decode -key "12345678" $cryptedtext]]

If you prefer "%" as the escape character and "x" as the padding character:

  % makemaps % x
  % set foo [pad 8 "sexy"]
  se%2yxxx
  % unpad $foo
  sexy

Since padding is done at the block level rather than the whole string it is safe to process the padded data block by block. This is important in cases where the data may be fragmented for example if getting the data from a socket:

  % set foo [pad 8 "the x factor"]
  the %2 factorxxx
  % set result [unpad [string range $foo 0 7]]
  the x f
  % append result [unpad [string range $foo 8 end]
  actor
  % set result
  the x factor