#!/usr/bin/env perl
###################################################################
### AES-128 [originally in CTR mode] ###
### bitsliced implementation for Intel Core 2 processors ###
### requires support of SSE extensions up to SSSE3 ###
### Author: Emilia Käsper and Peter Schwabe ###
### Date: 2009-03-19 ###
### Public domain ###
### ###
### See http://homes.esat.kuleuven.be/~ekasper/#software for ###
### further information. ###
###################################################################
#
# September 2011.
#
# Started as transliteration to "perlasm" the original code has
# undergone following changes:
#
# - code was made position-independent;
# - rounds were folded into a loop resulting in >5x size reduction
# from 12.5KB to 2.2KB;
# - above was possibile thanks to mixcolumns() modification that
# allowed to feed its output back to aesenc[last], this was
# achieved at cost of two additional inter-registers moves;
# - some instruction reordering and interleaving;
# - this module doesn't implement key setup subroutine, instead it
# relies on conversion of "conventional" key schedule as returned
# by AES_set_encrypt_key (see discussion below);
# - first and last round keys are treated differently, which allowed
# to skip one shiftrows(), reduce bit-sliced key schedule and
# speed-up conversion by 22%;
# - support for 192- and 256-bit keys was added;
#
# Resulting performance in CPU cycles spent to encrypt one byte out
# of 4096-byte buffer with 128-bit key is:
#
# Emilia's this(*) difference
#
# Core 2 9.30 8.69 +7%
# Nehalem(**) 7.63 6.98 +9%
# Atom 17.1 17.4 -2%(***)
#
# (*) Comparison is not completely fair, because "this" is ECB,
# i.e. no extra processing such as counter values calculation
# and xor-ing input as in Emilia's CTR implementation is
# performed. However, the CTR calculations stand for not more
# than 1% of total time, so comparison is *rather* fair.
#
# (**) Results were collected on Westmere, which is considered to
# be equivalent to Nehalem for this code.
#
# (***) Slowdown on Atom is rather strange per se, because original
# implementation has a number of 9+-bytes instructions, which
# are bad for Atom front-end, and which I eliminated completely.
# In attempt to address deterioration sbox() was tested in FP
# SIMD "domain" (movaps instead of movdqa, xorps instead of
# pxor, etc.). While it resulted in nominal 4% improvement on
# Atom, it hurted Westmere by more than 2x factor.
#
# As for key schedule conversion subroutine. Interface to OpenSSL
# relies on per-invocation on-the-fly conversion. This naturally
# has impact on performance, especially for short inputs. Conversion
# time in CPU cycles and its ratio to CPU cycles spent in 8x block
# function is:
#
# conversion conversion/8x block
# Core 2 410 0.37
# Nehalem 310 0.35
# Atom 570 0.26
#
# The ratio values mean that 128-byte blocks will be processed
# 21-27% slower, 256-byte blocks - 12-16%, 384-byte blocks - 8-11%,
# etc. Then keep in mind that input sizes not divisible by 128 are
# *effectively* slower, especially shortest ones, e.g. consecutive
# 144-byte blocks are processed 44% slower than one would expect,
# 272 - 29%, 400 - 22%, etc. Yet, despite all these "shortcomings"
# it's still faster than ["hyper-threading-safe" code path in]
# aes-x86_64.pl on all lengths above 64 bytes...
#
# <appro@openssl.org>
$flavour = shift;
$output = shift;
if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
die "can't locate x86_64-xlate.pl";
open STDOUT,"| $^X $xlate $flavour $output";
my ($inp,$out,$len,$key,$ivp)=("%rdi","%rsi","%rdx","%rcx");
my @XMM=map("%xmm$_",(15,0..14)); # best on Atom, +10% over (0..15)
{
my ($key,$rounds,$const)=("%rax","%r10d","%r11");
sub sbox {
# input in lsb > [b0, b1, b2, b3, b4, b5, b6, b7] < msb
# output in lsb > [b0, b1, b4, b6, b3, b7, b2, b5] < msb
my @b=@_[0..7];
my @t=@_[8..11];
my @s=@_[12..15];
&InBasisChange (@b);
&Inv_GF256 (@b[6,5,0,3,7,1,4,2],@t,@s);
&OutBasisChange (@b[7,1,4,2,6,5,0,3]);
}
sub InBasisChange {
# input in lsb > [b0, b1, b2, b3, b4, b5, b6, b7] < msb
# output in lsb > [b6, b5, b0, b3, b7, b1, b4, b2] < msb
my @b=@_[0..7];
$code.=<<___;
pxor @b[6], @b[5]
pxor @b[1], @b[2]
pxor @b[0], @b[5]
pxor @b[2], @b[6]
pxor @b[0], @b[3]
pxor @b[3], @b[6]
pxor @b[7], @b[3]
pxor @b[5], @b[7]
pxor @b[4], @b[3]
pxor @b[5], @b[4]