Base32 Encoder and Decoder

Problem this snippet solves:

I was in the process of working on another code sample that was dependent on the ability to decode Base32 strings. iRules has no built-in support for Base32 encoding, so I wrote my own. The code will only handle UTF-8 text. Unicode text will become fragmented and will not be encoded correctly, but the code will not blow up when it encounters Unicode (important security consideration). If you must encode Unicode, convert to Base64 first.

Code :

when RULE_INIT {
  set b32_tests {
    JBSWY3DPEBLW64TMMQQQ====
    KRUGS4ZANFZSAYLON52GQZLSEBZXI4TJNZTSC===
    KRUGS4ZANFZSAYJAON2HE2LOM4QHO2LUNAQGCIDQMFSCA33GEA2A==== 
    KRUGS4ZANFZSAYJAON2HE2LOM4QHO2LUNBXXK5BAMEQGM2LOMFWCA4LVMFXHI5LN
  }
  
  array set b32_alphabet {
    A 0  B 1  C 2  D 3
    E 4  F 5  G 6  H 7
    I 8  J 9  K 10 L 11
    M 12 N 13 O 14 P 15
    Q 16 R 17 S 18 T 19
    U 20 V 21 W 22 X 23
    Y 24 Z 25 2 26 3 27
    4 28 5 29 6 30 7 31
  }
  
  foreach input $b32_tests { 
    log local0. "       input = $input"
  
    set input [string toupper $input]
    set input [string trim $input =]
    set l [string length $input]
    set output ""
    set n 0
    set j 0
    
    # decode loop is outlined in RFC 4648 (http://tools.ietf.org/html/rfc4648#page-8)
    for { set i 0 } { $i < $l } { incr i } {
      set n [expr $n << 5]
      set n [expr $n + $b32_alphabet([string index $input $i])]
      set j [incr j 5]
     
      if { $j >= 8 } {
        set j [incr j -8]
        append output [format %c [expr ($n & (0xFF << $j)) >> $j]]
      }
    }
    
    log local0. "output/input = $output"
    
    # flip b32_alphabet so that base32 characters can be indexed by an 8-bit integer
    foreach { key value } [array get b32_alphabet] {
      array set b32_alphabet_inv "$value $key"
    }
    
    set input $output
    set output ""
    set l [string length $input]
    set n 0
    set j 0
    
    # encode loop is outlined in RFC 4648 (http://tools.ietf.org/html/rfc4648#page-8)
    for { set i 0 } { $i < $l } { incr i } {
      set n [expr $n << 8]
      set n [expr $n + [scan [string index $input $i] %c]]
      set j [incr j 8]
    
      while { $j >= 5 } {
        set j [incr j -5]
        append output $b32_alphabet_inv([expr ($n & (0x1F << $j)) >> $j])
      }
    }
  
    # pad final input group with zeros to form an integral number of 5-bit groups, then encode
    if { $j > 0 } { append output $b32_alphabet_inv([expr $n << (5 - $j) & 0x1F]) }
    
    # if the final quantum is not an integral multiple of 40, append "=" padding
    set pad [expr 8 - [string length $output] % 8]
    if { ($pad > 0) && ($pad < 8) } { append output [string repeat = $pad] }
    
    log local0. "      output = $output"
    log local0. [string repeat - 20]
  }
}

Tested this on version:

10.0
Published Jan 30, 2015
Version 1.0