#!/usr/bin/perl -w
#
##############################################################################
#
# File: base64_byte_frequency.pl
#
# Purpose: To summarize the bytes within a file that contains base64 encoded
#          data (one record per line).  This script was developed to assist
#          in visualizing SPA packets generated by the fwknop client.
#
# Author: Michael Rash (mbr@cipherdyne.org)
#
# Copyright (C) 2008 Michael Rash (mbr@cipherdyne.org)
#
# License: GNU Public License version 2 (GPLv2):
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
#    USA
#
##############################################################################
#
# $Id: base64_byte_frequency.pl 1247 2008-09-14 16:12:01Z mbr $
#

use strict;

my %frequency = ();
my %position_chars = ();
my $min_len = 0;
my $file = '';

if (defined $ARGV[0]) {
    $file = $ARGV[0] or die "$0 <file>";
} else {
    die "$0 <file>";
}

### map base64 encoded chars to numeric values for Gnuplot
my %char_map = (
    '+' => 0,  '/' => 1,  '0' => 2,  '1' => 3,  '2' => 4,  '3' => 5,
    '4' => 6,  '5' => 7,  '6' => 8,  '7' => 9,  '8' => 10, '9' => 11,
    'A' => 12, 'B' => 13, 'C' => 14, 'D' => 15, 'E' => 16, 'F' => 17,
    'G' => 18, 'H' => 19, 'I' => 20, 'J' => 21, 'K' => 22, 'L' => 23,
    'M' => 24, 'N' => 25, 'O' => 26, 'P' => 27, 'Q' => 28, 'R' => 29,
    'S' => 30, 'T' => 31, 'U' => 32, 'V' => 33, 'W' => 34, 'X' => 35,
    'Y' => 36, 'Z' => 37, 'a' => 38, 'b' => 39, 'c' => 40, 'd' => 41,
    'e' => 42, 'f' => 43, 'g' => 44, 'h' => 45, 'i' => 46, 'j' => 47,
    'k' => 48, 'l' => 49, 'm' => 50, 'n' => 51, 'o' => 52, 'p' => 53,
    'q' => 54, 'r' => 55, 's' => 56, 't' => 57, 'u' => 58, 'v' => 59,
    'w' => 60, 'x' => 61, 'y' => 62, 'z' => 63, '=' => 64
);

print "[+] Parsing SPA packets from: $file...\n";
open F, "< $file" or die "$0 <file>";
while (<F>) {
    next unless /\S/;
    my @chars = split //, $_;
    my $position = 0;
    for my $char (@chars) {
        next unless $char =~ /\S/;
        unless (defined $char_map{$char}) {
            print "[-] char map for $char not defined.\n";
        }
        $frequency{$char}++;
        $position_chars{$position}{$char}++;
        $position++;
    }
    $min_len = $position if $position == 0;
    $min_len = $position if $position < $min_len;
}
close F;

print "[+] Writing 2D results to: $file.2d...\n";
open D2, "> $file.2d" or die $!;
for my $char (sort {$char_map{$a} <=> $char_map{$b}} keys %char_map) {
    next unless defined $frequency{$char};
    print D2 "$char_map{$char}, $frequency{$char}  ### $char\n";
}
close D2;

print "[+] Writing 3D results to: $file.3d...\n";
open D3, "> $file.3d" or die $!;
for my $position (sort {$a <=> $b} keys %position_chars) {
    for my $char (sort {$char_map{$a} <=> $char_map{$b}} keys %char_map) {
        next unless defined $position_chars{$position}{$char};
        print D3 "$position, $char_map{$char}, ",
            "$position_chars{$position}{$char}  ### $char\n";
    }
}
close D3;

exit 0;
