@@ -3033,6 +3033,16 @@ fn validate_single_luks2_header(mut reader: impl std::io::Read, hdr_ind: u64) ->
30333033 if area. encryption ( ) != "aes-xts-plain64" {
30343034 bail ! ( "Invalid LUKS keyslot encryption: {}" , area. encryption( ) ) ;
30353035 }
3036+ // Pin where the encrypted key material is read from. The binary area
3037+ // must sit between the two header copies and the encrypted payload;
3038+ // otherwise a host with raw disk access could redirect it elsewhere.
3039+ if area. offset ( ) < 2 * hdr_size || area. offset ( ) + area. size ( ) > PAYLOAD_OFFSET {
3040+ bail ! (
3041+ "Invalid LUKS keyslot area: offset={} size={}" ,
3042+ area. offset( ) ,
3043+ area. size( )
3044+ ) ;
3045+ }
30363046 if * key_size != 64 {
30373047 bail ! ( "Invalid LUKS keyslot key size: {key_size}" ) ;
30383048 }
@@ -3043,6 +3053,9 @@ fn validate_single_luks2_header(mut reader: impl std::io::Read, hdr_ind: u64) ->
30433053 let LuksKdf :: pbkdf2 {
30443054 hash,
30453055 iterations : _,
3056+ // Salts are left unchecked on purpose: the passphrase is
3057+ // high-entropy and KMS-derived, so an attacker-chosen salt
3058+ // buys nothing without it (see security report #552).
30463059 salt : _,
30473060 } = kdf
30483061 else {
@@ -3133,6 +3146,30 @@ fn test_validate_luks2_header() {
31333146 . contains( "Invalid LUKS keyslot encryption" ) ) ;
31343147}
31353148
3149+ #[ test]
3150+ fn test_validate_luks2_header_rejects_out_of_range_keyslot_area ( ) {
3151+ // Redirect the keyslot binary area below the header region. Same length
3152+ // so the surrounding header stays intact; "00768" parses to 768, which is
3153+ // inside the header copies (< 2 * hdr_size) rather than the metadata gap.
3154+ let mut header = include_bytes ! ( "../tests/fixtures/luks_header_good" ) . to_vec ( ) ;
3155+ let needle = br#""offset":"32768""# ;
3156+ let replacement = br#""offset":"00768""# ;
3157+ let mut patched = 0 ;
3158+ let mut i = 0 ;
3159+ while i + needle. len ( ) <= header. len ( ) {
3160+ if & header[ i..i + needle. len ( ) ] == needle {
3161+ header[ i..i + needle. len ( ) ] . copy_from_slice ( replacement) ;
3162+ patched += 1 ;
3163+ i += needle. len ( ) ;
3164+ } else {
3165+ i += 1 ;
3166+ }
3167+ }
3168+ assert_eq ! ( patched, 2 , "expected to patch both header copies" ) ;
3169+ let error = validate_luks2_headers ( & mut & header[ ..] ) . unwrap_err ( ) ;
3170+ assert ! ( error. to_string( ) . contains( "Invalid LUKS keyslot area" ) ) ;
3171+ }
3172+
31363173#[ cfg( test) ]
31373174fn test_app_compose (
31383175 manifest_version : serde_json:: Value ,
0 commit comments