#NOEKEON Block Cipher in PERL
#
#Tom St Denis, tomstdenis@yahoo.com, http://tomstdenis.home.dhs.org
#
#
# Functions you want to call:
#
# 
#      noekeon_encrypt_ecb(blk, key)
#
# Encrypts the four 32-bit words in 'blk' under control of the four 32-bit words in 'key' and returns 
# the ciphertext as four 32-bit words.
#
#
#      noekeon_get_decrypt_key(key)
#
# Converts a key into an decryption key and returns it.
#
#
#      noekeon_decrypt_ecb(blk, key)
#
# Opposite of noekeon_encrypt_ecb().
#
#
#      noekeon_encrypt_cbc(blk, key, iv)
#
# Encrypts a single block in CBC mode like the ECB routine.  It updates the third argument with the new IV and returns
# the ciphertext.
#
#
#      noekeon_decrypt_cbc(blk, key, iv)
#
# Opposite of noekeon_encrypt_cbc().
#

#rotations
sub lrot {
    return (($_[0] << $_[1]) | ($_[0] >> ((32 - $_[1]) & 31))) % (2**32);
}

sub rrot {
    return (($_[0] >> $_[1]) | ($_[0] << ((32 - $_[1]) & 31))) % (2**32);
}

#find the i'th round constant
sub noekeon_RoundCt {
    my($RC, $i) = (0x80, 0);
    for ($i = 1; $i <= $_[0]; $i++) {
        if ($RC & 0x80) {
           $RC = ($RC << 1) ^ 0x11B;
        } else {
           $RC = ($RC << 1);
        }
    }
    return $RC;
}

#Noekeons Theta
sub noekeon_Theta {
    my($temp) = $_[0] ^ $_[2]; $temp ^= lrot($temp,8) ^ rrot($temp,8);
    $_[1] ^= $temp;
    $_[3] ^= $temp;

    # XOR Key material
    $_[0] ^= vec($_[4], 0, 32); $_[1] ^= vec($_[4], 1, 32);
    $_[2] ^= vec($_[4], 2, 32); $_[3] ^= vec($_[4], 3, 32);

    $temp = $_[1] ^ $_[3]; $temp ^= lrot($temp,8) ^ rrot($temp,8);
    $_[0] ^= $temp;
    $_[2] ^= $temp;
}
    
sub noekeon_Pi1 {
    $_[1] = lrot($_[1], 1);
    $_[2] = lrot($_[2], 5);
    $_[3] = lrot($_[3], 2);
}

sub noekeon_Pi2 {
    $_[1] = rrot($_[1], 1);
    $_[2] = rrot($_[2], 5);
    $_[3] = rrot($_[3], 2);
}
    
sub noekeon_Gamma {
    $_[1] ^= ($_[3]|$_[2])^0xFFFFFFFF;
    $_[0] ^= ($_[2]&$_[1]);
    my($tmp) = $_[3]; $_[3] = $_[0]; $_[0] = $tmp;
    $_[2] ^= $_[0] ^ $_[1] ^ $_[3];
    $_[1] ^= ($_[3]|$_[2])^0xFFFFFFFF;
    $_[0] ^= ($_[2]&$_[1]);
}

#round(a,b,c,d,c1,c2,key)
sub noekeon_round {
    #add constant 
    $_[0] ^= $_[4];

    #do theta
    noekeon_Theta($_[0],$_[1],$_[2],$_[3],$_[6]);

    $_[0] ^= $_[5];

    #pi1
    noekeon_Pi1($_[0],$_[1],$_[2],$_[3]);
   
    #gamma
    noekeon_Gamma($_[0],$_[1],$_[2],$_[3]);
 
    #pi2
    noekeon_Pi2($_[0],$_[1],$_[2],$_[3]);
}

#noekeon_encrypt_ecb(blk, key)
sub noekeon_encrypt_ecb {
    #grab input 
    my($a) = vec($_[0], 0, 32);
    my($b) = vec($_[0], 1, 32);
    my($c) = vec($_[0], 2, 32);
    my($d) = vec($_[0], 3, 32);

    #do 16 rounds in forward direction
    for (my($r) = 0; $r < 16; $r++) { noekeon_round($a,$b,$c,$d,noekeon_RoundCt($r),0,$_[1]); }

    #do last theta
    $a ^= noekeon_RoundCt(16);
    noekeon_Theta($a, $b, $c, $d,$_[1]);

    my($res) = 0;
    vec($res, 0, 32) = $a;
    vec($res, 1, 32) = $b;
    vec($res, 2, 32) = $c;
    vec($res, 3, 32) = $d;
    return $res;
}

#get a decrypt key
sub noekeon_get_decrypt_key {
    #do theta on key thingy
    my($a) = vec($_[0], 0, 32);
    my($b) = vec($_[0], 1, 32);
    my($c) = vec($_[0], 2, 32);
    my($d) = vec($_[0], 3, 32);
    my($e) = 0;
    for (my($i) = 0; $i < 4; $i++) { vec($e, $i, 32) = 0; }
    noekeon_Theta($a,$b,$c,$d,$e);
    my($tkey) = 0;
    vec($tkey, 0, 32) = $a;
    vec($tkey, 1, 32) = $b;
    vec($tkey, 2, 32) = $c;
    vec($tkey, 3, 32) = $d;
    return $tkey;
}
   
#noekeon_decrypt_ecb(blk, key)
sub noekeon_decrypt_ecb {
    #grab input 
    my($a) = vec($_[0], 0, 32);
    my($b) = vec($_[0], 1, 32);
    my($c) = vec($_[0], 2, 32);
    my($d) = vec($_[0], 3, 32);

    #do 16 rounds in forward direction
    for (my($r) = 16; $r > 0; $r--) { noekeon_round($a,$b,$c,$d,0,noekeon_RoundCt($r),$_[1]); }

    #undo last theta
    noekeon_Theta($a, $b, $c, $d,$_[1]);
    $a ^= noekeon_RoundCt(0);

    my($res) = 0;
    vec($res, 0, 32) = $a;
    vec($res, 1, 32) = $b;
    vec($res, 2, 32) = $c;
    vec($res, 3, 32) = $d;
    return $res;
}

#encode a block in CBC mode
#noekeon_encrypt_cbc(blk, key, iv)
sub noekeon_encrypt_cbc {
    for (my($i) = 0; $i < 4; $i++) { vec($_[0], $i, 32) ^= vec($_[2], $i, 32); }
    my($tmp) = noekeon_encrypt_ecb($_[0], $_[1]);
    for ($i = 0; $i < 4; $i++) { vec($_[2], $i, 32) = vec($tmp, $i, 32); }
    return $tmp;
}

#decode a block in CBC mode
#noekeon_decrypt_cbc(blk, key, iv)
sub noekeon_decrypt_cbc {
    my($tmp) = noekeon_decrypt_ecb($_[0], $_[1]);
    for (my($i) = 0; $i < 4; $i++) { vec($tmp, $i, 32) ^= vec($_[2], $i, 32); vec($_[2], $i, 32) = vec($_[0], $i, 32); }
    return $tmp;
}

#
#
#
# ------> DEMO <-------
# make up a key, IV and msg
$blk1 = $iv = $blk2 = $key = 0;
for ($i = 0; $i < 4; $i++) { vec($blk1, $i, 32) = vec($iv, $i, 32) = vec($key, $i, 32) = $i; vec($blk2, $i, 32) = $i + 4; }

# get decrypt key
$dkey = noekeon_get_decrypt_key($key);

#encrypt it
$ct1 = noekeon_encrypt_cbc($blk1, $key, $iv);
$ct2 = noekeon_encrypt_cbc($blk2, $key, $iv);
for ($i = 0; $i < 4; $i++) { print sprintf("%08lx ", vec($ct1, $i, 32)); } print "\n";
for ($i = 0; $i < 4; $i++) { print sprintf("%08lx ", vec($ct2, $i, 32)); } print "\n";

#reset iv 
for ($i = 0; $i < 4; $i++) { vec($iv, $i, 32) = $i; } print "\n\n";

#decrypt it
$pt1 = noekeon_decrypt_cbc($ct1, $dkey, $iv);
$pt2 = noekeon_decrypt_cbc($ct2, $dkey, $iv);
for ($i = 0; $i < 4; $i++) { print sprintf("%08lx ", vec($pt1, $i, 32)); } print "\n";
for ($i = 0; $i < 4; $i++) { print sprintf("%08lx ", vec($pt2, $i, 32)); } print "\n";
