From e145938e1ed1389ac837e8583a9ac01ae1056d02 Mon Sep 17 00:00:00 2001 From: Will McGinnis Date: Sun, 26 Jul 2026 15:52:44 -0700 Subject: [PATCH 1/3] Parse macOS 26 FileVault keybags (22-byte KEK metadata, expanded container VEK entry, big-endian DER long-form lengths) --- libfsapfs/libfsapfs_key_encrypted_key.c | 161 ++++++++++++++++-------- 1 file changed, 112 insertions(+), 49 deletions(-) diff --git a/libfsapfs/libfsapfs_key_encrypted_key.c b/libfsapfs/libfsapfs_key_encrypted_key.c index 00e01553..05ecc970 100644 --- a/libfsapfs/libfsapfs_key_encrypted_key.c +++ b/libfsapfs/libfsapfs_key_encrypted_key.c @@ -224,7 +224,7 @@ int libfsapfs_key_encrypted_key_read_data( } else if( byte_value == 0x82 ) { - byte_stream_copy_to_uint16_little_endian( + byte_stream_copy_to_uint16_big_endian( &( data[ data_offset ] ), value_data_size ); @@ -318,7 +318,7 @@ int libfsapfs_key_encrypted_key_read_data( } else if( byte_value == 0x82 ) { - byte_stream_copy_to_uint16_little_endian( + byte_stream_copy_to_uint16_big_endian( &( data[ data_offset ] ), value_data_size ); @@ -416,6 +416,14 @@ int libfsapfs_key_encrypted_key_read_data( break; case 0xa3: + { + /* The nested wrapped-KEK object must be captured from its own + * tag byte. macOS 26 grew the container VEK entry past 255 + * bytes, so this a3 can carry a 2-byte (0x82) length; the old + * "data_offset - 2" hard-coded a 2-byte header and would start + * mid-length. Derive the real header size from byte_value. */ + size_t wrapped_kek_object_header_size = 2; + if( value_data_size == 0 ) { libcerror_error_set( @@ -428,10 +436,19 @@ int libfsapfs_key_encrypted_key_read_data( return( -1 ); } - wrapped_kek_object_data = &( data[ data_offset - 2 ] ); - wrapped_kek_object_data_size = value_data_size + 2; + if( byte_value == 0x81 ) + { + wrapped_kek_object_header_size = 3; + } + else if( byte_value == 0x82 ) + { + wrapped_kek_object_header_size = 4; + } + wrapped_kek_object_data = &( data[ data_offset - wrapped_kek_object_header_size ] ); + wrapped_kek_object_data_size = wrapped_kek_object_header_size + value_data_size; break; + } default: break; @@ -455,6 +472,7 @@ int libfsapfs_key_encrypted_key_read_data( byte_value = wrapped_kek_object_data[ data_offset++ ]; if( ( ( byte_value & 0x80 ) != 0 ) + && ( byte_value != 0x81 ) && ( byte_value != 0x82 ) ) { libcerror_error_set( @@ -471,9 +489,13 @@ int libfsapfs_key_encrypted_key_read_data( { value_data_size = (uint16_t) byte_value; } + else if( byte_value == 0x81 ) + { + value_data_size = (uint16_t) wrapped_kek_object_data[ data_offset++ ]; + } else if( byte_value == 0x82 ) { - byte_stream_copy_to_uint16_little_endian( + byte_stream_copy_to_uint16_big_endian( &( wrapped_kek_object_data[ data_offset ] ), value_data_size ); @@ -544,6 +566,7 @@ int libfsapfs_key_encrypted_key_read_data( byte_value = wrapped_kek_object_data[ data_offset++ ]; if( ( ( byte_value & 0x80 ) != 0 ) + && ( byte_value != 0x81 ) && ( byte_value != 0x82 ) ) { libcerror_error_set( @@ -560,9 +583,13 @@ int libfsapfs_key_encrypted_key_read_data( { value_data_size = (uint16_t) byte_value; } + else if( byte_value == 0x81 ) + { + value_data_size = (uint16_t) wrapped_kek_object_data[ data_offset++ ]; + } else if( byte_value == 0x82 ) { - byte_stream_copy_to_uint16_little_endian( + byte_stream_copy_to_uint16_big_endian( &( wrapped_kek_object_data[ data_offset ] ), value_data_size ); @@ -672,7 +699,11 @@ int libfsapfs_key_encrypted_key_read_data( break; case 0x82: - if( value_data_size != 8 ) + /* macOS 26 (Tahoe) grew this inner KEK-metadata attribute from + * 8 bytes to 22 bytes: a 6-byte header followed by a 16-byte + * wrapping-context UUID. Accept both sizes. */ + if( ( value_data_size != 8 ) + && ( value_data_size != 22 ) ) { libcerror_error_set( error, @@ -690,6 +721,26 @@ int libfsapfs_key_encrypted_key_read_data( kek_metadata->encryption_method, key_encrypted_key->encryption_method ); + /* In the macOS 26 22-byte layout the first 4 bytes are NOT the + * legacy encryption_method; the 40-byte wrapped KEK implies + * AES-256 (method 0). Normalise so the unwrap path runs. */ + if( value_data_size == 22 ) + { +#if defined( HAVE_DEBUG_OUTPUT ) + if( libcnotify_verbose != 0 ) + { + libcnotify_printf( + "%s: macOS 26 22-byte KEK metadata:\n", + function ); + libcnotify_print_data( + &( wrapped_kek_object_data[ data_offset ] ), + 22, + 0 ); + } +#endif + key_encrypted_key->encryption_method = 0; + } + #if defined( HAVE_DEBUG_OUTPUT ) if( libcnotify_verbose != 0 ) { @@ -753,8 +804,7 @@ int libfsapfs_key_encrypted_key_read_data( break; case 0x84: - if( ( value_data_size == 0 ) - || ( value_data_size > 8 ) ) + if( value_data_size == 0 ) { libcerror_error_set( error, @@ -766,57 +816,58 @@ int libfsapfs_key_encrypted_key_read_data( return( -1 ); } - key_encrypted_key->number_of_iterations = 0; - - while( value_data_size > 0 ) + /* A password-protected KEK entry stores a <=8-byte PBKDF2 + * iteration count here. A macOS 26 container VEK entry + * (KB_TAG_VOLUME_KEY, key-unwrapped, no password) instead + * carries a 16-byte crypto-state value that must not be read + * as an iteration count. Only interpret <=8 bytes. */ + if( value_data_size <= 8 ) { - key_encrypted_key->number_of_iterations <<= 8; - key_encrypted_key->number_of_iterations |= wrapped_kek_object_data[ data_offset++ ]; + key_encrypted_key->number_of_iterations = 0; - value_data_size--; - } + while( value_data_size > 0 ) + { + key_encrypted_key->number_of_iterations <<= 8; + key_encrypted_key->number_of_iterations |= wrapped_kek_object_data[ data_offset++ ]; + + value_data_size--; + } #if defined( HAVE_DEBUG_OUTPUT ) - if( libcnotify_verbose != 0 ) - { - libcnotify_printf( - "%s: number of iterations\t\t: %" PRIu32 "\n", - function, - key_encrypted_key->number_of_iterations ); + if( libcnotify_verbose != 0 ) + { + libcnotify_printf( + "%s: number of iterations\t\t: %" PRIu32 "\n", + function, + key_encrypted_key->number_of_iterations ); - libcnotify_printf( - "\n" ); - } + libcnotify_printf( + "\n" ); + } #endif /* defined( HAVE_DEBUG_OUTPUT ) */ - + } break; case 0x85: - if( value_data_size != 16 ) + /* A password-protected KEK entry stores a 16-byte PBKDF2 salt + * here; a macOS 26 container VEK entry carries an unrelated + * (e.g. 3-byte) crypto-state value. Only copy a 16-byte salt. */ + if( value_data_size == 16 ) { - libcerror_error_set( - error, - LIBCERROR_ERROR_DOMAIN_RUNTIME, - LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE, - "%s: unsupported salt attribute value data size: %" PRIu16 ".", - function, - value_data_size ); - - return( -1 ); - } - if( memory_copy( - key_encrypted_key->salt, - &( wrapped_kek_object_data[ data_offset ] ), - 16 ) == NULL ) - { - libcerror_error_set( - error, - LIBCERROR_ERROR_DOMAIN_MEMORY, - LIBCERROR_MEMORY_ERROR_COPY_FAILED, - "%s: unable to copy salt.", - function ); + if( memory_copy( + key_encrypted_key->salt, + &( wrapped_kek_object_data[ data_offset ] ), + 16 ) == NULL ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_MEMORY, + LIBCERROR_MEMORY_ERROR_COPY_FAILED, + "%s: unable to copy salt.", + function ); - return( -1 ); + return( -1 ); + } } break; @@ -1186,6 +1237,18 @@ int libfsapfs_key_encrypted_key_unlock_with_password( 0, 32 ); +#if defined( HAVE_DEBUG_OUTPUT ) + if( libcnotify_verbose != 0 ) + { + libcnotify_printf( + "%s: post-unwrap check IV + KEK (expect a6a6a6a6a6a6a6a6 prefix on success):\n", + function ); + libcnotify_print_data( + wrapped_kek, + used_kek_data_size, + 0 ); + } +#endif if( memory_compare( wrapped_kek, libfsapfs_key_encrypted_key_wrapped_kek_initialization_vector, From ca9993e955e9e7029429027351f43015052e1246 Mon Sep 17 00:00:00 2001 From: Will McGinnis Date: Mon, 27 Jul 2026 17:05:37 -0700 Subject: [PATCH 2/3] Reworked macOS 26 keybag parsing changes Reduced the diff of the previous commit and added the bounds checking that change makes necessary. * Gated the relaxed keyblob[4] and keyblob[5] guards on a 22-byte keyblob[2] metadata attribute, so that a corrupt legacy password KEK, such as one with a 15-byte keyblob[5], is still rejected instead of parsing with an all-zero salt and being reported as a wrong password. * Added availability checks ahead of every DER tag and length byte read. Accepting the 0x81 long form consumed a length byte before the existing bounds check, so a record ending in a bare 0x81 read past the data. The same was already reachable through the 0x82 form, and through the attribute tag and length byte pair at all four tag and length decode sites. * Bound the object value data size by the number of bytes remaining after the length bytes instead of by a hardcoded 2-byte header, which under-counts by 1 or 2 bytes for the long forms. * Bound the attributes by the object value data instead of by the data, so that trailing data is no longer parsed as an attribute. * Corrected the number of iterations debug output format specifier to PRIu64. * Removed a debug output statement unrelated to the format change. --- libfsapfs/libfsapfs_key_encrypted_key.c | 288 ++++++++++++++++-------- 1 file changed, 191 insertions(+), 97 deletions(-) diff --git a/libfsapfs/libfsapfs_key_encrypted_key.c b/libfsapfs/libfsapfs_key_encrypted_key.c index 05ecc970..157f6aee 100644 --- a/libfsapfs/libfsapfs_key_encrypted_key.c +++ b/libfsapfs/libfsapfs_key_encrypted_key.c @@ -154,6 +154,9 @@ int libfsapfs_key_encrypted_key_read_data( const uint8_t *wrapped_kek_object_data = NULL; static char *function = "libfsapfs_key_encrypted_key_read_data"; size_t data_offset = 0; + size_t object_value_data_end = 0; + size_t wrapped_kek_object_header_size = 0; + uint16_t kek_metadata_value_data_size = 0; uint16_t value_data_size = 0; uint16_t wrapped_kek_object_data_size = 0; uint8_t byte_value = 0; @@ -220,16 +223,51 @@ int libfsapfs_key_encrypted_key_read_data( } else if( byte_value == 0x81 ) { + if( ( data_size - data_offset ) < 1 ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS, + "%s: invalid object extended value data size value out of bounds.", + function ); + + return( -1 ); + } value_data_size = (uint16_t) data[ data_offset++ ]; } else if( byte_value == 0x82 ) { + if( ( data_size - data_offset ) < 2 ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS, + "%s: invalid object extended value data size value out of bounds.", + function ); + + return( -1 ); + } byte_stream_copy_to_uint16_big_endian( &( data[ data_offset ] ), value_data_size ); data_offset += 2; } + if( value_data_size > ( data_size - data_offset ) ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS, + "%s: invalid object value data size value out of bounds.", + function ); + + return( -1 ); + } + object_value_data_end = data_offset + value_data_size; + #if defined( HAVE_DEBUG_OUTPUT ) if( libcnotify_verbose != 0 ) { @@ -278,19 +316,22 @@ int libfsapfs_key_encrypted_key_read_data( return( -1 ); } - if( value_data_size > ( data_size - 2 ) ) + /* The attributes are bound by the object value data, not by the data, + * so that trailing data is not parsed as an attribute. + */ + while( data_offset < object_value_data_end ) { - libcerror_error_set( - error, - LIBCERROR_ERROR_DOMAIN_RUNTIME, - LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS, - "%s: invalid object value data size value out of bounds.", - function ); + if( ( object_value_data_end - data_offset ) < 2 ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS, + "%s: invalid attribute value data size value out of bounds.", + function ); - return( -1 ); - } - while( data_offset < data_size ) - { + return( -1 ); + } value_tag = data[ data_offset++ ]; byte_value = data[ data_offset++ ]; @@ -314,10 +355,32 @@ int libfsapfs_key_encrypted_key_read_data( } else if( byte_value == 0x81 ) { + if( ( object_value_data_end - data_offset ) < 1 ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS, + "%s: invalid attribute extended value data size value out of bounds.", + function ); + + return( -1 ); + } value_data_size = (uint16_t) data[ data_offset++ ]; } else if( byte_value == 0x82 ) { + if( ( object_value_data_end - data_offset ) < 2 ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS, + "%s: invalid attribute extended value data size value out of bounds.", + function ); + + return( -1 ); + } byte_stream_copy_to_uint16_big_endian( &( data[ data_offset ] ), value_data_size ); @@ -344,8 +407,7 @@ int libfsapfs_key_encrypted_key_read_data( { break; } - if( ( data_offset >= data_size ) - || ( value_data_size > ( data_size - data_offset ) ) ) + if( value_data_size > ( object_value_data_end - data_offset ) ) { libcerror_error_set( error, @@ -416,14 +478,6 @@ int libfsapfs_key_encrypted_key_read_data( break; case 0xa3: - { - /* The nested wrapped-KEK object must be captured from its own - * tag byte. macOS 26 grew the container VEK entry past 255 - * bytes, so this a3 can carry a 2-byte (0x82) length; the old - * "data_offset - 2" hard-coded a 2-byte header and would start - * mid-length. Derive the real header size from byte_value. */ - size_t wrapped_kek_object_header_size = 2; - if( value_data_size == 0 ) { libcerror_error_set( @@ -436,6 +490,10 @@ int libfsapfs_key_encrypted_key_read_data( return( -1 ); } + /* The wrapped KEK packed object is parsed from its own tag + * byte onwards, hence the size of the tag and length bytes + * needs to be determined. + */ if( byte_value == 0x81 ) { wrapped_kek_object_header_size = 3; @@ -444,11 +502,14 @@ int libfsapfs_key_encrypted_key_read_data( { wrapped_kek_object_header_size = 4; } + else + { + wrapped_kek_object_header_size = 2; + } wrapped_kek_object_data = &( data[ data_offset - wrapped_kek_object_header_size ] ); - wrapped_kek_object_data_size = wrapped_kek_object_header_size + value_data_size; + wrapped_kek_object_data_size = (uint16_t) ( wrapped_kek_object_header_size + value_data_size ); break; - } default: break; @@ -560,8 +621,19 @@ int libfsapfs_key_encrypted_key_read_data( return( -1 ); } - while( data_offset < wrapped_kek_object_data_size ) + while( data_offset < (size_t) wrapped_kek_object_data_size ) { + if( ( (size_t) wrapped_kek_object_data_size - data_offset ) < 2 ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS, + "%s: invalid attribute value data size value out of bounds.", + function ); + + return( -1 ); + } value_tag = wrapped_kek_object_data[ data_offset++ ]; byte_value = wrapped_kek_object_data[ data_offset++ ]; @@ -585,10 +657,32 @@ int libfsapfs_key_encrypted_key_read_data( } else if( byte_value == 0x81 ) { + if( ( (size_t) wrapped_kek_object_data_size - data_offset ) < 1 ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS, + "%s: invalid attribute extended value data size value out of bounds.", + function ); + + return( -1 ); + } value_data_size = (uint16_t) wrapped_kek_object_data[ data_offset++ ]; } else if( byte_value == 0x82 ) { + if( ( (size_t) wrapped_kek_object_data_size - data_offset ) < 2 ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS, + "%s: invalid attribute extended value data size value out of bounds.", + function ); + + return( -1 ); + } byte_stream_copy_to_uint16_big_endian( &( wrapped_kek_object_data[ data_offset ] ), value_data_size ); @@ -615,8 +709,7 @@ int libfsapfs_key_encrypted_key_read_data( { break; } - if( ( data_offset >= wrapped_kek_object_data_size ) - || ( value_data_size > ( wrapped_kek_object_data_size - data_offset ) ) ) + if( value_data_size > ( (size_t) wrapped_kek_object_data_size - data_offset ) ) { libcerror_error_set( error, @@ -699,9 +792,9 @@ int libfsapfs_key_encrypted_key_read_data( break; case 0x82: - /* macOS 26 (Tahoe) grew this inner KEK-metadata attribute from - * 8 bytes to 22 bytes: a 6-byte header followed by a 16-byte - * wrapping-context UUID. Accept both sizes. */ + /* macOS 26 grew this attribute from 8 bytes to 22 bytes, + * namely a 6-byte header followed by a 16-byte identifier. + */ if( ( value_data_size != 8 ) && ( value_data_size != 22 ) ) { @@ -715,29 +808,19 @@ int libfsapfs_key_encrypted_key_read_data( return( -1 ); } - kek_metadata = (fsapfs_key_bag_kek_metadata_t *) &( wrapped_kek_object_data[ data_offset ] ); + kek_metadata_value_data_size = value_data_size; + kek_metadata = (fsapfs_key_bag_kek_metadata_t *) &( wrapped_kek_object_data[ data_offset ] ); byte_stream_copy_to_uint32_little_endian( kek_metadata->encryption_method, key_encrypted_key->encryption_method ); - /* In the macOS 26 22-byte layout the first 4 bytes are NOT the - * legacy encryption_method; the 40-byte wrapped KEK implies - * AES-256 (method 0). Normalise so the unwrap path runs. */ + /* In the 22-byte variant the first 4 bytes are not the + * encryption method. The 40-byte wrapped KEK corresponds with + * AES-256. + */ if( value_data_size == 22 ) { -#if defined( HAVE_DEBUG_OUTPUT ) - if( libcnotify_verbose != 0 ) - { - libcnotify_printf( - "%s: macOS 26 22-byte KEK metadata:\n", - function ); - libcnotify_print_data( - &( wrapped_kek_object_data[ data_offset ] ), - 22, - 0 ); - } -#endif key_encrypted_key->encryption_method = 0; } @@ -804,7 +887,16 @@ int libfsapfs_key_encrypted_key_read_data( break; case 0x84: - if( value_data_size == 0 ) + /* A password-protected KEK entry stores a <= 8-byte PBKDF2 + * iteration count here. A macOS 26 container key bag VEK + * entry, which is unwrapped with a key instead of a password, + * carries a 16-byte value that is not an iteration count. The + * 22-byte KEK metadata attribute distinguishes the two, so + * that a corrupt legacy entry is still rejected. + */ + if( ( value_data_size == 0 ) + || ( ( value_data_size > 8 ) + && ( kek_metadata_value_data_size != 22 ) ) ) { libcerror_error_set( error, @@ -816,58 +908,72 @@ int libfsapfs_key_encrypted_key_read_data( return( -1 ); } - /* A password-protected KEK entry stores a <=8-byte PBKDF2 - * iteration count here. A macOS 26 container VEK entry - * (KB_TAG_VOLUME_KEY, key-unwrapped, no password) instead - * carries a 16-byte crypto-state value that must not be read - * as an iteration count. Only interpret <=8 bytes. */ - if( value_data_size <= 8 ) + if( value_data_size > 8 ) { - key_encrypted_key->number_of_iterations = 0; + break; + } + key_encrypted_key->number_of_iterations = 0; - while( value_data_size > 0 ) - { - key_encrypted_key->number_of_iterations <<= 8; - key_encrypted_key->number_of_iterations |= wrapped_kek_object_data[ data_offset++ ]; + while( value_data_size > 0 ) + { + key_encrypted_key->number_of_iterations <<= 8; + key_encrypted_key->number_of_iterations |= wrapped_kek_object_data[ data_offset++ ]; - value_data_size--; - } + value_data_size--; + } #if defined( HAVE_DEBUG_OUTPUT ) - if( libcnotify_verbose != 0 ) - { - libcnotify_printf( - "%s: number of iterations\t\t: %" PRIu32 "\n", - function, - key_encrypted_key->number_of_iterations ); + if( libcnotify_verbose != 0 ) + { + libcnotify_printf( + "%s: number of iterations\t\t: %" PRIu64 "\n", + function, + key_encrypted_key->number_of_iterations ); - libcnotify_printf( - "\n" ); - } -#endif /* defined( HAVE_DEBUG_OUTPUT ) */ + libcnotify_printf( + "\n" ); } +#endif /* defined( HAVE_DEBUG_OUTPUT ) */ + break; case 0x85: /* A password-protected KEK entry stores a 16-byte PBKDF2 salt - * here; a macOS 26 container VEK entry carries an unrelated - * (e.g. 3-byte) crypto-state value. Only copy a 16-byte salt. */ - if( value_data_size == 16 ) + * here. A macOS 26 container key bag VEK entry carries an + * unrelated 3-byte value that is not a salt. The 22-byte KEK + * metadata attribute distinguishes the two, so that a corrupt + * legacy entry is still rejected. + */ + if( ( value_data_size != 16 ) + && ( kek_metadata_value_data_size != 22 ) ) { - if( memory_copy( - key_encrypted_key->salt, - &( wrapped_kek_object_data[ data_offset ] ), - 16 ) == NULL ) - { - libcerror_error_set( - error, - LIBCERROR_ERROR_DOMAIN_MEMORY, - LIBCERROR_MEMORY_ERROR_COPY_FAILED, - "%s: unable to copy salt.", - function ); + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE, + "%s: unsupported salt attribute value data size: %" PRIu16 ".", + function, + value_data_size ); - return( -1 ); - } + return( -1 ); + } + if( value_data_size != 16 ) + { + break; + } + if( memory_copy( + key_encrypted_key->salt, + &( wrapped_kek_object_data[ data_offset ] ), + 16 ) == NULL ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_MEMORY, + LIBCERROR_MEMORY_ERROR_COPY_FAILED, + "%s: unable to copy salt.", + function ); + + return( -1 ); } break; @@ -1237,18 +1343,6 @@ int libfsapfs_key_encrypted_key_unlock_with_password( 0, 32 ); -#if defined( HAVE_DEBUG_OUTPUT ) - if( libcnotify_verbose != 0 ) - { - libcnotify_printf( - "%s: post-unwrap check IV + KEK (expect a6a6a6a6a6a6a6a6 prefix on success):\n", - function ); - libcnotify_print_data( - wrapped_kek, - used_kek_data_size, - 0 ); - } -#endif if( memory_compare( wrapped_kek, libfsapfs_key_encrypted_key_wrapped_kek_initialization_vector, From 3d8afbe0ecf285a9a76ebf74072328846a82559f Mon Sep 17 00:00:00 2001 From: Will McGinnis Date: Mon, 27 Jul 2026 17:05:37 -0700 Subject: [PATCH 3/3] Added tests for libfsapfs_key_encrypted_key_read_data Covers every branch the macOS 26 keybag change adds, which previously had no test at all. Real records, from a disposable macOS 26.5.2 virtual machine whose account password is "admin": - data1: per-user KEK record (162 bytes) with a 22-byte keyblob[2] metadata attribute. - data2: personal recovery key KEK record (162 bytes) of the same container, a second independent record with a 22-byte keyblob[2] metadata attribute and a different HMAC, identifier, wrapped KEK, iteration count and salt. - data3: container key bag VEK entry (388 bytes), which uses 2-byte long-form DER lengths on the object and on the keyblob, a 1-byte long-form DER length on keyblob[8], a 16-byte keyblob[4] and a 3-byte keyblob[5]. Records derived from those: - data4: KEK record with the pre-macOS 26 8-byte keyblob[2] metadata attribute, to safeguard the original code path. - data5 and data6: records that use the 1-byte and 2-byte long-form DER lengths on the object, on the keyblob and on the keyblob attributes. The unsupported long-form attributes precede keyblob[4] and keyblob[5], so a change that stopped parsing at them is caught by the number of iterations and salt asserts. - data7: record followed by trailing data that itself looks like a wrapped KEK packed object attribute, which must not be parsed. - error_data1 to error_data4: an unsupported 12-byte keyblob[2] metadata attribute, an 8-byte keyblob[2] metadata attribute combined with a 15-byte keyblob[5] or with a 16-byte keyblob[4], and an empty keyblob[4]. - error_data5 to error_data14: minimal records truncated at a DER tag or length byte, and records with attribute value data running past the object that contains it. Every regular case starts from a freshly initialized key encrypted key, since libfsapfs_key_encrypted_key_read_data only sets the attributes a record actually carries, and asserts the parsed HMAC, identifier, wrapped KEK, salt, number of iterations and encryption method. --- tests/fsapfs_test_key_encrypted_key.c | 1584 ++++++++++++++++++++++++- 1 file changed, 1562 insertions(+), 22 deletions(-) diff --git a/tests/fsapfs_test_key_encrypted_key.c b/tests/fsapfs_test_key_encrypted_key.c index fe452dbd..36741ddc 100644 --- a/tests/fsapfs_test_key_encrypted_key.c +++ b/tests/fsapfs_test_key_encrypted_key.c @@ -21,6 +21,7 @@ #include #include +#include #include #if defined( HAVE_STDLIB_H ) || defined( WINAPI ) @@ -37,6 +38,361 @@ #if defined( __GNUC__ ) && !defined( LIBFSAPFS_DLL_IMPORT ) +/* macOS 26 per-user KEK record (162 bytes) with a 22-byte keyblob[2] + * metadata attribute, captured from a disposable admin/admin VM. + */ +uint8_t fsapfs_test_key_encrypted_key_data1[ 162 ] = { + 0x30, 0x81, 0x9f, 0x80, 0x01, 0x00, 0x81, 0x20, 0x6b, 0x1c, 0x2e, 0xc7, 0x80, 0x18, 0x60, 0x47, + 0x0d, 0x39, 0x0e, 0x0b, 0x4d, 0xb6, 0x59, 0x4c, 0x86, 0x74, 0xd0, 0x50, 0xa0, 0xf7, 0x88, 0x70, + 0x67, 0x38, 0x2d, 0x4d, 0x45, 0xe8, 0x30, 0xb9, 0x82, 0x08, 0x07, 0xda, 0xe2, 0x4e, 0xc2, 0x05, + 0xfe, 0x57, 0xa3, 0x6e, 0x80, 0x01, 0x00, 0x81, 0x10, 0xfa, 0xa4, 0x13, 0xcd, 0xc9, 0xf6, 0x45, + 0x43, 0xba, 0x84, 0xb8, 0x94, 0x39, 0xc4, 0x07, 0x32, 0x82, 0x16, 0x49, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x96, 0xd7, 0x25, 0xec, 0x34, 0xd8, 0x42, 0xac, 0x97, 0x64, 0xe4, 0x3f, 0x81, 0x80, 0x4f, + 0x57, 0x83, 0x28, 0x7c, 0xb2, 0xae, 0xf5, 0x82, 0xde, 0x67, 0x50, 0x23, 0xe9, 0x77, 0xb6, 0xe6, + 0xff, 0xca, 0x85, 0x45, 0xf8, 0x08, 0x15, 0x5e, 0x2b, 0x73, 0x75, 0xf8, 0x3c, 0xcf, 0x65, 0xe9, + 0xf4, 0x1b, 0x8c, 0xe7, 0x12, 0x09, 0x9b, 0xfb, 0x67, 0x59, 0x9f, 0x84, 0x03, 0x08, 0x89, 0x50, + 0x85, 0x10, 0x51, 0x42, 0xaf, 0x0b, 0xf4, 0x34, 0xde, 0x02, 0x9f, 0x9f, 0x4f, 0x1a, 0x85, 0x38, + 0x43, 0x0e }; + +/* macOS 26 personal recovery key (PRK) KEK record (162 bytes) of the + * same container. A second, independent record with a 22-byte + * keyblob[2] metadata attribute, carrying the crypto user identifier + * Apple uses for the personal recovery key: + * EBC6C064-0000-11AA-AA11-00306543ECAC. + */ +uint8_t fsapfs_test_key_encrypted_key_data2[ 162 ] = { + 0x30, 0x81, 0x9f, 0x80, 0x01, 0x00, 0x81, 0x20, 0x5a, 0xb0, 0xa1, 0x4d, 0xbe, 0xbc, 0xdf, 0x6b, + 0xea, 0x99, 0xbf, 0x48, 0xe9, 0xcd, 0xd2, 0x5e, 0xfc, 0xf6, 0x3b, 0xed, 0xd0, 0x32, 0xfc, 0xe4, + 0x9a, 0x00, 0xc5, 0x5c, 0x42, 0x7d, 0x85, 0xfd, 0x82, 0x08, 0x8c, 0x69, 0xca, 0xc5, 0xec, 0xf6, + 0x3a, 0x99, 0xa3, 0x6e, 0x80, 0x01, 0x00, 0x81, 0x10, 0xeb, 0xc6, 0xc0, 0x64, 0x00, 0x00, 0x11, + 0xaa, 0xaa, 0x11, 0x00, 0x30, 0x65, 0x43, 0xec, 0xac, 0x82, 0x16, 0x49, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x96, 0xd7, 0x25, 0xec, 0x34, 0xd8, 0x42, 0xac, 0x97, 0x64, 0xe4, 0x3f, 0x81, 0x80, 0x4f, + 0x57, 0x83, 0x28, 0xd1, 0x92, 0x75, 0xdf, 0x33, 0x8b, 0x1b, 0x76, 0x83, 0x6c, 0x09, 0xfa, 0x12, + 0x34, 0xb9, 0x35, 0x5d, 0x4f, 0xac, 0xee, 0xe2, 0x51, 0xd7, 0xf4, 0x54, 0x92, 0xac, 0x33, 0xe1, + 0xfd, 0x0d, 0xef, 0xae, 0xfd, 0x61, 0x4d, 0xb1, 0x43, 0xba, 0x65, 0x84, 0x03, 0x0a, 0x6b, 0x25, + 0x85, 0x10, 0xd8, 0x3c, 0x49, 0x3c, 0x5a, 0x10, 0x78, 0xdd, 0xc1, 0xe3, 0x6b, 0x3d, 0x4d, 0x37, + 0xdf, 0xa7 }; + +/* macOS 26 container key bag VEK entry (388 bytes): 2-byte long-form + * DER lengths on the outer SEQUENCE and the keyblob, a 22-byte + * metadata attribute, a 16-byte keyblob[4], a 3-byte keyblob[5] and + * additional keyblob[6], [7] and [8] attributes. + */ +uint8_t fsapfs_test_key_encrypted_key_data3[ 388 ] = { + 0x30, 0x82, 0x01, 0x80, 0x80, 0x01, 0x00, 0x81, 0x20, 0x73, 0x24, 0xfa, 0x3a, 0x90, 0xb7, 0x5a, + 0xe5, 0xc5, 0x55, 0x19, 0x3b, 0xac, 0xc1, 0x77, 0x00, 0x7c, 0x09, 0x69, 0xbd, 0x85, 0x12, 0x47, + 0xcf, 0xa9, 0x48, 0x24, 0xf2, 0x0f, 0xe4, 0xbb, 0x22, 0x82, 0x08, 0xee, 0x54, 0x57, 0x36, 0xc5, + 0x9b, 0x91, 0xb5, 0xa3, 0x82, 0x01, 0x4d, 0x80, 0x01, 0x00, 0x81, 0x10, 0x66, 0xeb, 0x31, 0xc4, + 0x19, 0x7c, 0x43, 0xa2, 0xb8, 0x33, 0xc5, 0x20, 0x52, 0x88, 0x4e, 0x9c, 0x82, 0x16, 0x29, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x96, 0xd7, 0x25, 0xec, 0x34, 0xd8, 0x42, 0xac, 0x97, 0x64, 0xe4, 0x3f, + 0x81, 0x80, 0x4f, 0x57, 0x83, 0x28, 0xd7, 0xec, 0xb1, 0xc0, 0x14, 0x0b, 0x97, 0x65, 0x88, 0x89, + 0xbb, 0xce, 0xe5, 0xf4, 0x1d, 0xee, 0x20, 0xeb, 0xd8, 0x9e, 0x48, 0x96, 0x0d, 0x7d, 0x3c, 0x00, + 0x28, 0xed, 0x1b, 0xe4, 0x9c, 0x44, 0xce, 0x8a, 0x1b, 0xbf, 0x17, 0x32, 0x4e, 0x3d, 0x84, 0x10, + 0x8b, 0x84, 0xc6, 0x38, 0xa6, 0x23, 0x48, 0xaa, 0x8e, 0x5e, 0x8d, 0x6d, 0x87, 0xff, 0x33, 0x89, + 0x85, 0x03, 0x35, 0xad, 0x36, 0x86, 0x10, 0xfa, 0x4f, 0xc6, 0xc0, 0x2a, 0x69, 0xe4, 0x71, 0xc2, + 0x97, 0xa3, 0xd8, 0xca, 0x9f, 0x6d, 0xdc, 0x87, 0x10, 0xd3, 0xc8, 0xb8, 0x78, 0x90, 0x9b, 0xa8, + 0x55, 0x11, 0xe0, 0x14, 0x77, 0x68, 0x52, 0x15, 0x26, 0x88, 0x81, 0xb8, 0x77, 0x91, 0x82, 0x20, + 0x68, 0x55, 0x87, 0x79, 0x79, 0x4b, 0x7c, 0xd2, 0x00, 0xf0, 0x71, 0x41, 0xe2, 0xea, 0x17, 0xa6, + 0x4c, 0x55, 0xbe, 0xa3, 0xbe, 0xb2, 0xee, 0x1b, 0x91, 0x14, 0x58, 0x04, 0xf8, 0x6b, 0xbd, 0x58, + 0x19, 0xcd, 0x33, 0x5f, 0x96, 0xd6, 0xcd, 0xb8, 0xb3, 0xbf, 0x9d, 0x7f, 0x5e, 0x4a, 0xe4, 0x82, + 0xb7, 0xb6, 0x3d, 0xcc, 0x1c, 0x9a, 0x5a, 0xa9, 0x87, 0xc9, 0x72, 0x0d, 0xc6, 0x34, 0x6d, 0x1f, + 0x24, 0x76, 0x83, 0x08, 0x7b, 0x94, 0x04, 0xeb, 0xda, 0x95, 0x81, 0xa5, 0x2e, 0xbb, 0x98, 0xcc, + 0xca, 0xc2, 0x31, 0x13, 0xcc, 0x6e, 0x18, 0x8b, 0x40, 0xb9, 0xa3, 0x7f, 0x4d, 0x38, 0x79, 0xa6, + 0x33, 0x72, 0x91, 0x9e, 0x55, 0x2a, 0xe0, 0xe5, 0x44, 0xaa, 0xd6, 0xc0, 0x37, 0x0a, 0x3c, 0xb7, + 0x18, 0xde, 0x57, 0xd7, 0xa7, 0x0f, 0x1c, 0x67, 0xee, 0x32, 0xb1, 0xdc, 0x63, 0x6d, 0x2a, 0x94, + 0xce, 0xdd, 0xd6, 0x94, 0x4a, 0x65, 0x39, 0xb6, 0x4c, 0x84, 0xc5, 0x77, 0x56, 0x98, 0x19, 0xbf, + 0x4a, 0x80, 0x40, 0xf4, 0xae, 0x9c, 0x41, 0x30, 0xe1, 0xfb, 0x57, 0xa1, 0x77, 0x40, 0xdf, 0x34, + 0x70, 0x4d, 0xfb, 0xa1, 0x5f, 0x7b, 0x3f, 0x09, 0x18, 0x22, 0x29, 0xee, 0x56, 0x2c, 0x60, 0xa8, + 0x2d, 0x29, 0x5b, 0x92 }; + +/* KEK record with the pre-macOS 26 8-byte keyblob[2] metadata + * attribute, to safeguard the original code path. + */ +uint8_t fsapfs_test_key_encrypted_key_data4[ 148 ] = { + 0x30, 0x81, 0x91, 0x80, 0x01, 0x00, 0x81, 0x20, 0x6b, 0x1c, 0x2e, 0xc7, 0x80, 0x18, 0x60, 0x47, + 0x0d, 0x39, 0x0e, 0x0b, 0x4d, 0xb6, 0x59, 0x4c, 0x86, 0x74, 0xd0, 0x50, 0xa0, 0xf7, 0x88, 0x70, + 0x67, 0x38, 0x2d, 0x4d, 0x45, 0xe8, 0x30, 0xb9, 0x82, 0x08, 0x07, 0xda, 0xe2, 0x4e, 0xc2, 0x05, + 0xfe, 0x57, 0xa3, 0x60, 0x80, 0x01, 0x00, 0x81, 0x10, 0xfa, 0xa4, 0x13, 0xcd, 0xc9, 0xf6, 0x45, + 0x43, 0xba, 0x84, 0xb8, 0x94, 0x39, 0xc4, 0x07, 0x32, 0x82, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x83, 0x28, 0x7c, 0xb2, 0xae, 0xf5, 0x82, 0xde, 0x67, 0x50, 0x23, 0xe9, 0x77, + 0xb6, 0xe6, 0xff, 0xca, 0x85, 0x45, 0xf8, 0x08, 0x15, 0x5e, 0x2b, 0x73, 0x75, 0xf8, 0x3c, 0xcf, + 0x65, 0xe9, 0xf4, 0x1b, 0x8c, 0xe7, 0x12, 0x09, 0x9b, 0xfb, 0x67, 0x59, 0x9f, 0x84, 0x03, 0x08, + 0x89, 0x50, 0x85, 0x10, 0x51, 0x42, 0xaf, 0x0b, 0xf4, 0x34, 0xde, 0x02, 0x9f, 0x9f, 0x4f, 0x1a, + 0x85, 0x38, 0x43, 0x0e }; + +/* KEK record with 1-byte long-form (0x81) DER lengths on the object and + * on the keyblob attribute. The unsupported keyblob[6] attribute + * precedes keyblob[4] and keyblob[5], so both remain observable. + */ +uint8_t fsapfs_test_key_encrypted_key_data5[ 181 ] = { + 0x30, 0x81, 0xb2, 0x80, 0x01, 0x00, 0x81, 0x20, 0x6b, 0x1c, 0x2e, 0xc7, 0x80, 0x18, 0x60, 0x47, + 0x0d, 0x39, 0x0e, 0x0b, 0x4d, 0xb6, 0x59, 0x4c, 0x86, 0x74, 0xd0, 0x50, 0xa0, 0xf7, 0x88, 0x70, + 0x67, 0x38, 0x2d, 0x4d, 0x45, 0xe8, 0x30, 0xb9, 0x82, 0x08, 0x07, 0xda, 0xe2, 0x4e, 0xc2, 0x05, + 0xfe, 0x57, 0xa3, 0x81, 0x80, 0x80, 0x01, 0x00, 0x81, 0x10, 0xfa, 0xa4, 0x13, 0xcd, 0xc9, 0xf6, + 0x45, 0x43, 0xba, 0x84, 0xb8, 0x94, 0x39, 0xc4, 0x07, 0x32, 0x82, 0x16, 0x49, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x96, 0xd7, 0x25, 0xec, 0x34, 0xd8, 0x42, 0xac, 0x97, 0x64, 0xe4, 0x3f, 0x81, 0x80, + 0x4f, 0x57, 0x83, 0x28, 0x7c, 0xb2, 0xae, 0xf5, 0x82, 0xde, 0x67, 0x50, 0x23, 0xe9, 0x77, 0xb6, + 0xe6, 0xff, 0xca, 0x85, 0x45, 0xf8, 0x08, 0x15, 0x5e, 0x2b, 0x73, 0x75, 0xf8, 0x3c, 0xcf, 0x65, + 0xe9, 0xf4, 0x1b, 0x8c, 0xe7, 0x12, 0x09, 0x9b, 0xfb, 0x67, 0x59, 0x9f, 0x86, 0x10, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x84, 0x03, + 0x08, 0x89, 0x50, 0x85, 0x10, 0x51, 0x42, 0xaf, 0x0b, 0xf4, 0x34, 0xde, 0x02, 0x9f, 0x9f, 0x4f, + 0x1a, 0x85, 0x38, 0x43, 0x0e }; + +/* KEK record with 2-byte long-form (0x82) DER lengths on the object and + * on the keyblob attribute, and with 1-byte and 2-byte long-form DER + * lengths on the keyblob[6] and keyblob[7] attributes. Both precede + * keyblob[4] and keyblob[5], so those remain observable. + */ +uint8_t fsapfs_test_key_encrypted_key_data6[ 600 ] = { + 0x30, 0x82, 0x02, 0x54, 0x80, 0x01, 0x00, 0x81, 0x20, 0x6b, 0x1c, 0x2e, 0xc7, 0x80, 0x18, 0x60, + 0x47, 0x0d, 0x39, 0x0e, 0x0b, 0x4d, 0xb6, 0x59, 0x4c, 0x86, 0x74, 0xd0, 0x50, 0xa0, 0xf7, 0x88, + 0x70, 0x67, 0x38, 0x2d, 0x4d, 0x45, 0xe8, 0x30, 0xb9, 0x82, 0x08, 0x07, 0xda, 0xe2, 0x4e, 0xc2, + 0x05, 0xfe, 0x57, 0xa3, 0x82, 0x02, 0x21, 0x80, 0x01, 0x00, 0x81, 0x10, 0xfa, 0xa4, 0x13, 0xcd, + 0xc9, 0xf6, 0x45, 0x43, 0xba, 0x84, 0xb8, 0x94, 0x39, 0xc4, 0x07, 0x32, 0x82, 0x16, 0x49, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x96, 0xd7, 0x25, 0xec, 0x34, 0xd8, 0x42, 0xac, 0x97, 0x64, 0xe4, 0x3f, + 0x81, 0x80, 0x4f, 0x57, 0x83, 0x28, 0x7c, 0xb2, 0xae, 0xf5, 0x82, 0xde, 0x67, 0x50, 0x23, 0xe9, + 0x77, 0xb6, 0xe6, 0xff, 0xca, 0x85, 0x45, 0xf8, 0x08, 0x15, 0x5e, 0x2b, 0x73, 0x75, 0xf8, 0x3c, + 0xcf, 0x65, 0xe9, 0xf4, 0x1b, 0x8c, 0xe7, 0x12, 0x09, 0x9b, 0xfb, 0x67, 0x59, 0x9f, 0x86, 0x81, + 0x80, 0x00, 0x03, 0x06, 0x09, 0x0c, 0x0f, 0x12, 0x15, 0x18, 0x1b, 0x1e, 0x21, 0x24, 0x27, 0x2a, + 0x2d, 0x30, 0x33, 0x36, 0x39, 0x3c, 0x3f, 0x42, 0x45, 0x48, 0x4b, 0x4e, 0x51, 0x54, 0x57, 0x5a, + 0x5d, 0x60, 0x63, 0x66, 0x69, 0x6c, 0x6f, 0x72, 0x75, 0x78, 0x7b, 0x7e, 0x81, 0x84, 0x87, 0x8a, + 0x8d, 0x90, 0x93, 0x96, 0x99, 0x9c, 0x9f, 0xa2, 0xa5, 0xa8, 0xab, 0xae, 0xb1, 0xb4, 0xb7, 0xba, + 0xbd, 0xc0, 0xc3, 0xc6, 0xc9, 0xcc, 0xcf, 0xd2, 0xd5, 0xd8, 0xdb, 0xde, 0xe1, 0xe4, 0xe7, 0xea, + 0xed, 0xf0, 0xf3, 0xf6, 0xf9, 0xfc, 0xff, 0x02, 0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17, 0x1a, + 0x1d, 0x20, 0x23, 0x26, 0x29, 0x2c, 0x2f, 0x32, 0x35, 0x38, 0x3b, 0x3e, 0x41, 0x44, 0x47, 0x4a, + 0x4d, 0x50, 0x53, 0x56, 0x59, 0x5c, 0x5f, 0x62, 0x65, 0x68, 0x6b, 0x6e, 0x71, 0x74, 0x77, 0x7a, + 0x7d, 0x87, 0x82, 0x01, 0x2c, 0x00, 0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38, 0x3f, 0x46, + 0x4d, 0x54, 0x5b, 0x62, 0x69, 0x70, 0x77, 0x7e, 0x85, 0x8c, 0x93, 0x9a, 0xa1, 0xa8, 0xaf, 0xb6, + 0xbd, 0xc4, 0xcb, 0xd2, 0xd9, 0xe0, 0xe7, 0xee, 0xf5, 0xfc, 0x03, 0x0a, 0x11, 0x18, 0x1f, 0x26, + 0x2d, 0x34, 0x3b, 0x42, 0x49, 0x50, 0x57, 0x5e, 0x65, 0x6c, 0x73, 0x7a, 0x81, 0x88, 0x8f, 0x96, + 0x9d, 0xa4, 0xab, 0xb2, 0xb9, 0xc0, 0xc7, 0xce, 0xd5, 0xdc, 0xe3, 0xea, 0xf1, 0xf8, 0xff, 0x06, + 0x0d, 0x14, 0x1b, 0x22, 0x29, 0x30, 0x37, 0x3e, 0x45, 0x4c, 0x53, 0x5a, 0x61, 0x68, 0x6f, 0x76, + 0x7d, 0x84, 0x8b, 0x92, 0x99, 0xa0, 0xa7, 0xae, 0xb5, 0xbc, 0xc3, 0xca, 0xd1, 0xd8, 0xdf, 0xe6, + 0xed, 0xf4, 0xfb, 0x02, 0x09, 0x10, 0x17, 0x1e, 0x25, 0x2c, 0x33, 0x3a, 0x41, 0x48, 0x4f, 0x56, + 0x5d, 0x64, 0x6b, 0x72, 0x79, 0x80, 0x87, 0x8e, 0x95, 0x9c, 0xa3, 0xaa, 0xb1, 0xb8, 0xbf, 0xc6, + 0xcd, 0xd4, 0xdb, 0xe2, 0xe9, 0xf0, 0xf7, 0xfe, 0x05, 0x0c, 0x13, 0x1a, 0x21, 0x28, 0x2f, 0x36, + 0x3d, 0x44, 0x4b, 0x52, 0x59, 0x60, 0x67, 0x6e, 0x75, 0x7c, 0x83, 0x8a, 0x91, 0x98, 0x9f, 0xa6, + 0xad, 0xb4, 0xbb, 0xc2, 0xc9, 0xd0, 0xd7, 0xde, 0xe5, 0xec, 0xf3, 0xfa, 0x01, 0x08, 0x0f, 0x16, + 0x1d, 0x24, 0x2b, 0x32, 0x39, 0x40, 0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x7f, 0x86, + 0x8d, 0x94, 0x9b, 0xa2, 0xa9, 0xb0, 0xb7, 0xbe, 0xc5, 0xcc, 0xd3, 0xda, 0xe1, 0xe8, 0xef, 0xf6, + 0xfd, 0x04, 0x0b, 0x12, 0x19, 0x20, 0x27, 0x2e, 0x35, 0x3c, 0x43, 0x4a, 0x51, 0x58, 0x5f, 0x66, + 0x6d, 0x74, 0x7b, 0x82, 0x89, 0x90, 0x97, 0x9e, 0xa5, 0xac, 0xb3, 0xba, 0xc1, 0xc8, 0xcf, 0xd6, + 0xdd, 0xe4, 0xeb, 0xf2, 0xf9, 0x00, 0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38, 0x3f, 0x46, + 0x4d, 0x54, 0x5b, 0x62, 0x69, 0x70, 0x77, 0x7e, 0x85, 0x8c, 0x93, 0x9a, 0xa1, 0xa8, 0xaf, 0xb6, + 0xbd, 0xc4, 0xcb, 0xd2, 0xd9, 0xe0, 0xe7, 0xee, 0xf5, 0xfc, 0x03, 0x0a, 0x11, 0x18, 0x1f, 0x26, + 0x2d, 0x84, 0x03, 0x08, 0x89, 0x50, 0x85, 0x10, 0x51, 0x42, 0xaf, 0x0b, 0xf4, 0x34, 0xde, 0x02, + 0x9f, 0x9f, 0x4f, 0x1a, 0x85, 0x38, 0x43, 0x0e }; + +/* KEK record followed by trailing data that itself looks like a wrapped + * KEK packed object attribute. The attributes are bound by the object + * value data, so the trailing data must not be parsed. + */ +uint8_t fsapfs_test_key_encrypted_key_data7[ 223 ] = { + 0x30, 0x81, 0x91, 0x80, 0x01, 0x00, 0x81, 0x20, 0x6b, 0x1c, 0x2e, 0xc7, 0x80, 0x18, 0x60, 0x47, + 0x0d, 0x39, 0x0e, 0x0b, 0x4d, 0xb6, 0x59, 0x4c, 0x86, 0x74, 0xd0, 0x50, 0xa0, 0xf7, 0x88, 0x70, + 0x67, 0x38, 0x2d, 0x4d, 0x45, 0xe8, 0x30, 0xb9, 0x82, 0x08, 0x07, 0xda, 0xe2, 0x4e, 0xc2, 0x05, + 0xfe, 0x57, 0xa3, 0x60, 0x80, 0x01, 0x00, 0x81, 0x10, 0xfa, 0xa4, 0x13, 0xcd, 0xc9, 0xf6, 0x45, + 0x43, 0xba, 0x84, 0xb8, 0x94, 0x39, 0xc4, 0x07, 0x32, 0x82, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x83, 0x28, 0x7c, 0xb2, 0xae, 0xf5, 0x82, 0xde, 0x67, 0x50, 0x23, 0xe9, 0x77, + 0xb6, 0xe6, 0xff, 0xca, 0x85, 0x45, 0xf8, 0x08, 0x15, 0x5e, 0x2b, 0x73, 0x75, 0xf8, 0x3c, 0xcf, + 0x65, 0xe9, 0xf4, 0x1b, 0x8c, 0xe7, 0x12, 0x09, 0x9b, 0xfb, 0x67, 0x59, 0x9f, 0x84, 0x03, 0x08, + 0x89, 0x50, 0x85, 0x10, 0x51, 0x42, 0xaf, 0x0b, 0xf4, 0x34, 0xde, 0x02, 0x9f, 0x9f, 0x4f, 0x1a, + 0x85, 0x38, 0x43, 0x0e, 0xa3, 0x49, 0x80, 0x01, 0x00, 0x81, 0x10, 0x00, 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x82, 0x08, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x28, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67 }; + +/* KEK record with an unsupported 12-byte keyblob[2] metadata + * attribute value data size. + */ +uint8_t fsapfs_test_key_encrypted_key_error_data1[ 152 ] = { + 0x30, 0x81, 0x95, 0x80, 0x01, 0x00, 0x81, 0x20, 0x6b, 0x1c, 0x2e, 0xc7, 0x80, 0x18, 0x60, 0x47, + 0x0d, 0x39, 0x0e, 0x0b, 0x4d, 0xb6, 0x59, 0x4c, 0x86, 0x74, 0xd0, 0x50, 0xa0, 0xf7, 0x88, 0x70, + 0x67, 0x38, 0x2d, 0x4d, 0x45, 0xe8, 0x30, 0xb9, 0x82, 0x08, 0x07, 0xda, 0xe2, 0x4e, 0xc2, 0x05, + 0xfe, 0x57, 0xa3, 0x64, 0x80, 0x01, 0x00, 0x81, 0x10, 0xfa, 0xa4, 0x13, 0xcd, 0xc9, 0xf6, 0x45, + 0x43, 0xba, 0x84, 0xb8, 0x94, 0x39, 0xc4, 0x07, 0x32, 0x82, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x28, 0x7c, 0xb2, 0xae, 0xf5, 0x82, 0xde, 0x67, + 0x50, 0x23, 0xe9, 0x77, 0xb6, 0xe6, 0xff, 0xca, 0x85, 0x45, 0xf8, 0x08, 0x15, 0x5e, 0x2b, 0x73, + 0x75, 0xf8, 0x3c, 0xcf, 0x65, 0xe9, 0xf4, 0x1b, 0x8c, 0xe7, 0x12, 0x09, 0x9b, 0xfb, 0x67, 0x59, + 0x9f, 0x84, 0x03, 0x08, 0x89, 0x50, 0x85, 0x10, 0x51, 0x42, 0xaf, 0x0b, 0xf4, 0x34, 0xde, 0x02, + 0x9f, 0x9f, 0x4f, 0x1a, 0x85, 0x38, 0x43, 0x0e }; + +/* KEK record with an 8-byte keyblob[2] metadata attribute and a 15-byte + * keyblob[5] salt. The 22-byte metadata attribute gates the macOS 26 + * behaviour, so this legacy record must still be rejected. + */ +uint8_t fsapfs_test_key_encrypted_key_error_data2[ 147 ] = { + 0x30, 0x81, 0x90, 0x80, 0x01, 0x00, 0x81, 0x20, 0x6b, 0x1c, 0x2e, 0xc7, 0x80, 0x18, 0x60, 0x47, + 0x0d, 0x39, 0x0e, 0x0b, 0x4d, 0xb6, 0x59, 0x4c, 0x86, 0x74, 0xd0, 0x50, 0xa0, 0xf7, 0x88, 0x70, + 0x67, 0x38, 0x2d, 0x4d, 0x45, 0xe8, 0x30, 0xb9, 0x82, 0x08, 0x07, 0xda, 0xe2, 0x4e, 0xc2, 0x05, + 0xfe, 0x57, 0xa3, 0x5f, 0x80, 0x01, 0x00, 0x81, 0x10, 0xfa, 0xa4, 0x13, 0xcd, 0xc9, 0xf6, 0x45, + 0x43, 0xba, 0x84, 0xb8, 0x94, 0x39, 0xc4, 0x07, 0x32, 0x82, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x83, 0x28, 0x7c, 0xb2, 0xae, 0xf5, 0x82, 0xde, 0x67, 0x50, 0x23, 0xe9, 0x77, + 0xb6, 0xe6, 0xff, 0xca, 0x85, 0x45, 0xf8, 0x08, 0x15, 0x5e, 0x2b, 0x73, 0x75, 0xf8, 0x3c, 0xcf, + 0x65, 0xe9, 0xf4, 0x1b, 0x8c, 0xe7, 0x12, 0x09, 0x9b, 0xfb, 0x67, 0x59, 0x9f, 0x84, 0x03, 0x08, + 0x89, 0x50, 0x85, 0x0f, 0x51, 0x42, 0xaf, 0x0b, 0xf4, 0x34, 0xde, 0x02, 0x9f, 0x9f, 0x4f, 0x1a, + 0x85, 0x38, 0x43 }; + +/* KEK record with an 8-byte keyblob[2] metadata attribute and a 16-byte + * keyblob[4] number of iterations. Rejected for the same reason. + */ +uint8_t fsapfs_test_key_encrypted_key_error_data3[ 161 ] = { + 0x30, 0x81, 0x9e, 0x80, 0x01, 0x00, 0x81, 0x20, 0x6b, 0x1c, 0x2e, 0xc7, 0x80, 0x18, 0x60, 0x47, + 0x0d, 0x39, 0x0e, 0x0b, 0x4d, 0xb6, 0x59, 0x4c, 0x86, 0x74, 0xd0, 0x50, 0xa0, 0xf7, 0x88, 0x70, + 0x67, 0x38, 0x2d, 0x4d, 0x45, 0xe8, 0x30, 0xb9, 0x82, 0x08, 0x07, 0xda, 0xe2, 0x4e, 0xc2, 0x05, + 0xfe, 0x57, 0xa3, 0x6d, 0x80, 0x01, 0x00, 0x81, 0x10, 0xfa, 0xa4, 0x13, 0xcd, 0xc9, 0xf6, 0x45, + 0x43, 0xba, 0x84, 0xb8, 0x94, 0x39, 0xc4, 0x07, 0x32, 0x82, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x83, 0x28, 0x7c, 0xb2, 0xae, 0xf5, 0x82, 0xde, 0x67, 0x50, 0x23, 0xe9, 0x77, + 0xb6, 0xe6, 0xff, 0xca, 0x85, 0x45, 0xf8, 0x08, 0x15, 0x5e, 0x2b, 0x73, 0x75, 0xf8, 0x3c, 0xcf, + 0x65, 0xe9, 0xf4, 0x1b, 0x8c, 0xe7, 0x12, 0x09, 0x9b, 0xfb, 0x67, 0x59, 0x9f, 0x84, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, + 0x10, 0x51, 0x42, 0xaf, 0x0b, 0xf4, 0x34, 0xde, 0x02, 0x9f, 0x9f, 0x4f, 0x1a, 0x85, 0x38, 0x43, + 0x0e }; + +/* KEK record with an empty keyblob[4] number of iterations. + */ +uint8_t fsapfs_test_key_encrypted_key_error_data4[ 159 ] = { + 0x30, 0x81, 0x9c, 0x80, 0x01, 0x00, 0x81, 0x20, 0x6b, 0x1c, 0x2e, 0xc7, 0x80, 0x18, 0x60, 0x47, + 0x0d, 0x39, 0x0e, 0x0b, 0x4d, 0xb6, 0x59, 0x4c, 0x86, 0x74, 0xd0, 0x50, 0xa0, 0xf7, 0x88, 0x70, + 0x67, 0x38, 0x2d, 0x4d, 0x45, 0xe8, 0x30, 0xb9, 0x82, 0x08, 0x07, 0xda, 0xe2, 0x4e, 0xc2, 0x05, + 0xfe, 0x57, 0xa3, 0x6b, 0x80, 0x01, 0x00, 0x81, 0x10, 0xfa, 0xa4, 0x13, 0xcd, 0xc9, 0xf6, 0x45, + 0x43, 0xba, 0x84, 0xb8, 0x94, 0x39, 0xc4, 0x07, 0x32, 0x82, 0x16, 0x49, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x96, 0xd7, 0x25, 0xec, 0x34, 0xd8, 0x42, 0xac, 0x97, 0x64, 0xe4, 0x3f, 0x81, 0x80, 0x4f, + 0x57, 0x83, 0x28, 0x7c, 0xb2, 0xae, 0xf5, 0x82, 0xde, 0x67, 0x50, 0x23, 0xe9, 0x77, 0xb6, 0xe6, + 0xff, 0xca, 0x85, 0x45, 0xf8, 0x08, 0x15, 0x5e, 0x2b, 0x73, 0x75, 0xf8, 0x3c, 0xcf, 0x65, 0xe9, + 0xf4, 0x1b, 0x8c, 0xe7, 0x12, 0x09, 0x9b, 0xfb, 0x67, 0x59, 0x9f, 0x84, 0x00, 0x85, 0x10, 0x51, + 0x42, 0xaf, 0x0b, 0xf4, 0x34, 0xde, 0x02, 0x9f, 0x9f, 0x4f, 0x1a, 0x85, 0x38, 0x43, 0x0e }; + +/* Truncated record in which the object 0x81 long-form length byte is + * missing. + */ +uint8_t fsapfs_test_key_encrypted_key_error_data5[ 2 ] = { + 0x30, 0x81 }; + +/* Truncated record in which only 1 of the 2 object 0x82 long-form length + * bytes is present. + */ +uint8_t fsapfs_test_key_encrypted_key_error_data6[ 3 ] = { + 0x30, 0x82, 0x01 }; + +/* Truncated record in which an attribute tag is present but its length + * byte is missing. + */ +uint8_t fsapfs_test_key_encrypted_key_error_data7[ 3 ] = { + 0x30, 0x01, 0x80 }; + +/* Truncated record in which the attribute 0x81 long-form length byte is + * missing. + */ +uint8_t fsapfs_test_key_encrypted_key_error_data8[ 4 ] = { + 0x30, 0x02, 0x80, 0x81 }; + +/* Truncated record in which only 1 of the 2 attribute 0x82 long-form + * length bytes is present. + */ +uint8_t fsapfs_test_key_encrypted_key_error_data9[ 5 ] = { + 0x30, 0x03, 0x80, 0x82, 0x00 }; + +/* Record with attribute value data running past the object value data. + */ +uint8_t fsapfs_test_key_encrypted_key_error_data10[ 6 ] = { + 0x30, 0x04, 0x80, 0x03, 0x00, 0x00 }; + +/* Truncated record in which a wrapped KEK packed object attribute tag is + * present but its length byte is missing. + */ +uint8_t fsapfs_test_key_encrypted_key_error_data11[ 8 ] = { + 0x30, 0x06, 0xa3, 0x04, 0x80, 0x01, 0x00, 0x84 }; + +/* Truncated record in which the wrapped KEK packed object attribute 0x81 + * long-form length byte is missing. + */ +uint8_t fsapfs_test_key_encrypted_key_error_data12[ 9 ] = { + 0x30, 0x07, 0xa3, 0x05, 0x80, 0x01, 0x00, 0x84, 0x81 }; + +/* Truncated record in which neither of the 2 wrapped KEK packed object + * attribute 0x82 long-form length bytes is present. + */ +uint8_t fsapfs_test_key_encrypted_key_error_data13[ 9 ] = { + 0x30, 0x07, 0xa3, 0x05, 0x80, 0x01, 0x00, 0x84, 0x82 }; + +/* Record with wrapped KEK packed object attribute value data running past + * the wrapped KEK packed object. + */ +uint8_t fsapfs_test_key_encrypted_key_error_data14[ 10 ] = { + 0x30, 0x08, 0xa3, 0x06, 0x80, 0x01, 0x00, 0x81, 0x10, 0x00 }; + +/* Expected HMAC of the per-user KEK record. + */ +uint8_t fsapfs_test_key_encrypted_key_hmac1[ 32 ] = { + 0x6b, 0x1c, 0x2e, 0xc7, 0x80, 0x18, 0x60, 0x47, 0x0d, 0x39, 0x0e, 0x0b, 0x4d, 0xb6, 0x59, 0x4c, + 0x86, 0x74, 0xd0, 0x50, 0xa0, 0xf7, 0x88, 0x70, 0x67, 0x38, 0x2d, 0x4d, 0x45, 0xe8, 0x30, 0xb9 }; + +/* Expected keyblob[1] identifier of the per-user KEK record. + */ +uint8_t fsapfs_test_key_encrypted_key_identifier1[ 16 ] = { + 0xfa, 0xa4, 0x13, 0xcd, 0xc9, 0xf6, 0x45, 0x43, 0xba, 0x84, 0xb8, 0x94, 0x39, 0xc4, 0x07, 0x32 }; + +/* Expected keyblob[3] wrapped KEK of the per-user KEK record. + */ +uint8_t fsapfs_test_key_encrypted_key_wrapped_kek1[ 40 ] = { + 0x7c, 0xb2, 0xae, 0xf5, 0x82, 0xde, 0x67, 0x50, 0x23, 0xe9, 0x77, 0xb6, 0xe6, 0xff, 0xca, 0x85, + 0x45, 0xf8, 0x08, 0x15, 0x5e, 0x2b, 0x73, 0x75, 0xf8, 0x3c, 0xcf, 0x65, 0xe9, 0xf4, 0x1b, 0x8c, + 0xe7, 0x12, 0x09, 0x9b, 0xfb, 0x67, 0x59, 0x9f }; + +/* Expected keyblob[5] salt of the per-user KEK record. + */ +uint8_t fsapfs_test_key_encrypted_key_salt1[ 16 ] = { + 0x51, 0x42, 0xaf, 0x0b, 0xf4, 0x34, 0xde, 0x02, 0x9f, 0x9f, 0x4f, 0x1a, 0x85, 0x38, 0x43, 0x0e }; + +/* Expected HMAC of the PRK KEK record. + */ +uint8_t fsapfs_test_key_encrypted_key_hmac2[ 32 ] = { + 0x5a, 0xb0, 0xa1, 0x4d, 0xbe, 0xbc, 0xdf, 0x6b, 0xea, 0x99, 0xbf, 0x48, 0xe9, 0xcd, 0xd2, 0x5e, + 0xfc, 0xf6, 0x3b, 0xed, 0xd0, 0x32, 0xfc, 0xe4, 0x9a, 0x00, 0xc5, 0x5c, 0x42, 0x7d, 0x85, 0xfd }; + +/* Expected keyblob[1] identifier of the PRK KEK record. + */ +uint8_t fsapfs_test_key_encrypted_key_identifier2[ 16 ] = { + 0xeb, 0xc6, 0xc0, 0x64, 0x00, 0x00, 0x11, 0xaa, 0xaa, 0x11, 0x00, 0x30, 0x65, 0x43, 0xec, 0xac }; + +/* Expected keyblob[3] wrapped KEK of the PRK KEK record. + */ +uint8_t fsapfs_test_key_encrypted_key_wrapped_kek2[ 40 ] = { + 0xd1, 0x92, 0x75, 0xdf, 0x33, 0x8b, 0x1b, 0x76, 0x83, 0x6c, 0x09, 0xfa, 0x12, 0x34, 0xb9, 0x35, + 0x5d, 0x4f, 0xac, 0xee, 0xe2, 0x51, 0xd7, 0xf4, 0x54, 0x92, 0xac, 0x33, 0xe1, 0xfd, 0x0d, 0xef, + 0xae, 0xfd, 0x61, 0x4d, 0xb1, 0x43, 0xba, 0x65 }; + +/* Expected keyblob[5] salt of the PRK KEK record. + */ +uint8_t fsapfs_test_key_encrypted_key_salt2[ 16 ] = { + 0xd8, 0x3c, 0x49, 0x3c, 0x5a, 0x10, 0x78, 0xdd, 0xc1, 0xe3, 0x6b, 0x3d, 0x4d, 0x37, 0xdf, 0xa7 }; + +/* Expected HMAC of the container key bag VEK entry. + */ +uint8_t fsapfs_test_key_encrypted_key_hmac3[ 32 ] = { + 0x73, 0x24, 0xfa, 0x3a, 0x90, 0xb7, 0x5a, 0xe5, 0xc5, 0x55, 0x19, 0x3b, 0xac, 0xc1, 0x77, 0x00, + 0x7c, 0x09, 0x69, 0xbd, 0x85, 0x12, 0x47, 0xcf, 0xa9, 0x48, 0x24, 0xf2, 0x0f, 0xe4, 0xbb, 0x22 }; + +/* Expected keyblob[1] identifier (volume identifier) of the VEK entry. + */ +uint8_t fsapfs_test_key_encrypted_key_identifier3[ 16 ] = { + 0x66, 0xeb, 0x31, 0xc4, 0x19, 0x7c, 0x43, 0xa2, 0xb8, 0x33, 0xc5, 0x20, 0x52, 0x88, 0x4e, 0x9c }; + +/* Expected keyblob[3] wrapped volume key of the VEK entry. + */ +uint8_t fsapfs_test_key_encrypted_key_wrapped_kek3[ 40 ] = { + 0xd7, 0xec, 0xb1, 0xc0, 0x14, 0x0b, 0x97, 0x65, 0x88, 0x89, 0xbb, 0xce, 0xe5, 0xf4, 0x1d, 0xee, + 0x20, 0xeb, 0xd8, 0x9e, 0x48, 0x96, 0x0d, 0x7d, 0x3c, 0x00, 0x28, 0xed, 0x1b, 0xe4, 0x9c, 0x44, + 0xce, 0x8a, 0x1b, 0xbf, 0x17, 0x32, 0x4e, 0x3d }; + +/* An all-zero salt. The VEK entry carries a 3-byte keyblob[5] that is + * not a PBKDF2 salt, so the salt must be left untouched. + */ +uint8_t fsapfs_test_key_encrypted_key_empty_salt[ 16 ] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + /* Tests the libfsapfs_key_encrypted_key_initialize function * Returns 1 if successful or 0 if not */ @@ -270,34 +626,1218 @@ int fsapfs_test_key_encrypted_key_free( return( 0 ); } -#endif /* defined( __GNUC__ ) && !defined( LIBFSAPFS_DLL_IMPORT ) */ - -/* The main program +/* Tests the libfsapfs_key_encrypted_key_read_data function + * Returns 1 if successful or 0 if not */ -#if defined( HAVE_WIDE_SYSTEM_CHARACTER ) -int wmain( - int argc FSAPFS_TEST_ATTRIBUTE_UNUSED, - wchar_t * const argv[] FSAPFS_TEST_ATTRIBUTE_UNUSED ) -#else -int main( - int argc FSAPFS_TEST_ATTRIBUTE_UNUSED, - char * const argv[] FSAPFS_TEST_ATTRIBUTE_UNUSED ) -#endif +int fsapfs_test_key_encrypted_key_read_data( + void ) { - FSAPFS_TEST_UNREFERENCED_PARAMETER( argc ) - FSAPFS_TEST_UNREFERENCED_PARAMETER( argv ) + libcerror_error_t *error = NULL; + libfsapfs_key_encrypted_key_t *key_encrypted_key = NULL; + int result = 0; -#if defined( __GNUC__ ) && !defined( LIBFSAPFS_DLL_IMPORT ) + /* Every regular case starts from a freshly initialized key encrypted + * key, since libfsapfs_key_encrypted_key_read_data only sets the + * attributes a record actually carries. + */ - FSAPFS_TEST_RUN( - "libfsapfs_key_encrypted_key_initialize", - fsapfs_test_key_encrypted_key_initialize ); + /* Test regular case of a macOS 26 per-user KEK record, which has a + * 22-byte keyblob[2] metadata attribute instead of an 8-byte one. The + * first 4 bytes of a 22-byte metadata attribute are not the encryption + * method, so it is normalized to 0 (AES-256). + */ + result = libfsapfs_key_encrypted_key_initialize( + &key_encrypted_key, + &error ); - FSAPFS_TEST_RUN( - "libfsapfs_key_encrypted_key_free", - fsapfs_test_key_encrypted_key_free ); + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "key_encrypted_key", + key_encrypted_key ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + fsapfs_test_key_encrypted_key_data1, + 162, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = memory_compare( + key_encrypted_key->hmac, + fsapfs_test_key_encrypted_key_hmac1, + 32 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + result = memory_compare( + key_encrypted_key->identifier, + fsapfs_test_key_encrypted_key_identifier1, + 16 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + result = memory_compare( + key_encrypted_key->wrapped_kek, + fsapfs_test_key_encrypted_key_wrapped_kek1, + 40 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + result = memory_compare( + key_encrypted_key->salt, + fsapfs_test_key_encrypted_key_salt1, + 16 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + FSAPFS_TEST_ASSERT_EQUAL_UINT64( + "key_encrypted_key->number_of_iterations", + key_encrypted_key->number_of_iterations, + (uint64_t) 559440 ); + + FSAPFS_TEST_ASSERT_EQUAL_UINT32( + "key_encrypted_key->encryption_method", + key_encrypted_key->encryption_method, + (uint32_t) 0 ); + + result = libfsapfs_key_encrypted_key_free( + &key_encrypted_key, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "key_encrypted_key", + key_encrypted_key ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + /* Test regular case of the personal recovery key KEK record of the same + * container. A second, independent 22-byte metadata record. + */ + result = libfsapfs_key_encrypted_key_initialize( + &key_encrypted_key, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "key_encrypted_key", + key_encrypted_key ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + fsapfs_test_key_encrypted_key_data2, + 162, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = memory_compare( + key_encrypted_key->hmac, + fsapfs_test_key_encrypted_key_hmac2, + 32 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + result = memory_compare( + key_encrypted_key->identifier, + fsapfs_test_key_encrypted_key_identifier2, + 16 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + result = memory_compare( + key_encrypted_key->wrapped_kek, + fsapfs_test_key_encrypted_key_wrapped_kek2, + 40 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + result = memory_compare( + key_encrypted_key->salt, + fsapfs_test_key_encrypted_key_salt2, + 16 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + FSAPFS_TEST_ASSERT_EQUAL_UINT64( + "key_encrypted_key->number_of_iterations", + key_encrypted_key->number_of_iterations, + (uint64_t) 682789 ); + + FSAPFS_TEST_ASSERT_EQUAL_UINT32( + "key_encrypted_key->encryption_method", + key_encrypted_key->encryption_method, + (uint32_t) 0 ); + + result = libfsapfs_key_encrypted_key_free( + &key_encrypted_key, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "key_encrypted_key", + key_encrypted_key ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + /* Test regular case of a macOS 26 container key bag VEK entry. It uses + * 2-byte long-form DER lengths on the outer SEQUENCE and the keyblob, a + * 1-byte long-form DER length on keyblob[8], and it carries a 16-byte + * keyblob[4] and a 3-byte keyblob[5] that are neither an iteration count + * nor a PBKDF2 salt, so the number of iterations and the salt must be + * left at their initial value. + */ + result = libfsapfs_key_encrypted_key_initialize( + &key_encrypted_key, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "key_encrypted_key", + key_encrypted_key ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + fsapfs_test_key_encrypted_key_data3, + 388, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); - /* TODO: add tests for libfsapfs_key_encrypted_key_read_data */ + result = memory_compare( + key_encrypted_key->hmac, + fsapfs_test_key_encrypted_key_hmac3, + 32 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + result = memory_compare( + key_encrypted_key->identifier, + fsapfs_test_key_encrypted_key_identifier3, + 16 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + result = memory_compare( + key_encrypted_key->wrapped_kek, + fsapfs_test_key_encrypted_key_wrapped_kek3, + 40 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + result = memory_compare( + key_encrypted_key->salt, + fsapfs_test_key_encrypted_key_empty_salt, + 16 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + FSAPFS_TEST_ASSERT_EQUAL_UINT64( + "key_encrypted_key->number_of_iterations", + key_encrypted_key->number_of_iterations, + (uint64_t) 0 ); + + FSAPFS_TEST_ASSERT_EQUAL_UINT32( + "key_encrypted_key->encryption_method", + key_encrypted_key->encryption_method, + (uint32_t) 0 ); + + result = libfsapfs_key_encrypted_key_free( + &key_encrypted_key, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "key_encrypted_key", + key_encrypted_key ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + /* Test regular case of a KEK record with the pre-macOS 26 8-byte + * keyblob[2] metadata attribute, where the first 4 bytes are the + * encryption method. + */ + result = libfsapfs_key_encrypted_key_initialize( + &key_encrypted_key, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "key_encrypted_key", + key_encrypted_key ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + fsapfs_test_key_encrypted_key_data4, + 148, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = memory_compare( + key_encrypted_key->hmac, + fsapfs_test_key_encrypted_key_hmac1, + 32 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + result = memory_compare( + key_encrypted_key->identifier, + fsapfs_test_key_encrypted_key_identifier1, + 16 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + result = memory_compare( + key_encrypted_key->wrapped_kek, + fsapfs_test_key_encrypted_key_wrapped_kek1, + 40 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + result = memory_compare( + key_encrypted_key->salt, + fsapfs_test_key_encrypted_key_salt1, + 16 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + FSAPFS_TEST_ASSERT_EQUAL_UINT64( + "key_encrypted_key->number_of_iterations", + key_encrypted_key->number_of_iterations, + (uint64_t) 559440 ); + + FSAPFS_TEST_ASSERT_EQUAL_UINT32( + "key_encrypted_key->encryption_method", + key_encrypted_key->encryption_method, + (uint32_t) 2 ); + + result = libfsapfs_key_encrypted_key_free( + &key_encrypted_key, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "key_encrypted_key", + key_encrypted_key ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + /* Test regular case of a KEK record with 1-byte long-form (0x81) DER + * lengths on the object and on the keyblob attribute. + */ + result = libfsapfs_key_encrypted_key_initialize( + &key_encrypted_key, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "key_encrypted_key", + key_encrypted_key ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + fsapfs_test_key_encrypted_key_data5, + 181, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = memory_compare( + key_encrypted_key->hmac, + fsapfs_test_key_encrypted_key_hmac1, + 32 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + result = memory_compare( + key_encrypted_key->identifier, + fsapfs_test_key_encrypted_key_identifier1, + 16 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + result = memory_compare( + key_encrypted_key->wrapped_kek, + fsapfs_test_key_encrypted_key_wrapped_kek1, + 40 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + result = memory_compare( + key_encrypted_key->salt, + fsapfs_test_key_encrypted_key_salt1, + 16 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + FSAPFS_TEST_ASSERT_EQUAL_UINT64( + "key_encrypted_key->number_of_iterations", + key_encrypted_key->number_of_iterations, + (uint64_t) 559440 ); + + FSAPFS_TEST_ASSERT_EQUAL_UINT32( + "key_encrypted_key->encryption_method", + key_encrypted_key->encryption_method, + (uint32_t) 0 ); + + result = libfsapfs_key_encrypted_key_free( + &key_encrypted_key, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "key_encrypted_key", + key_encrypted_key ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + /* Test regular case of a KEK record with 2-byte long-form (0x82) DER + * lengths on the object and on the keyblob attribute, and 1-byte and + * 2-byte long-form DER lengths on keyblob attributes. The recognized + * keyblob[4] and keyblob[5] follow both long-form attributes, so they + * are only set if parsing continued past them. + */ + result = libfsapfs_key_encrypted_key_initialize( + &key_encrypted_key, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "key_encrypted_key", + key_encrypted_key ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + fsapfs_test_key_encrypted_key_data6, + 600, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = memory_compare( + key_encrypted_key->hmac, + fsapfs_test_key_encrypted_key_hmac1, + 32 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + result = memory_compare( + key_encrypted_key->identifier, + fsapfs_test_key_encrypted_key_identifier1, + 16 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + result = memory_compare( + key_encrypted_key->wrapped_kek, + fsapfs_test_key_encrypted_key_wrapped_kek1, + 40 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + result = memory_compare( + key_encrypted_key->salt, + fsapfs_test_key_encrypted_key_salt1, + 16 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + FSAPFS_TEST_ASSERT_EQUAL_UINT64( + "key_encrypted_key->number_of_iterations", + key_encrypted_key->number_of_iterations, + (uint64_t) 559440 ); + + FSAPFS_TEST_ASSERT_EQUAL_UINT32( + "key_encrypted_key->encryption_method", + key_encrypted_key->encryption_method, + (uint32_t) 0 ); + + result = libfsapfs_key_encrypted_key_free( + &key_encrypted_key, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "key_encrypted_key", + key_encrypted_key ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + /* Test regular case of a KEK record followed by trailing data that + * itself looks like a wrapped KEK packed object attribute. The + * attributes are bound by the object value data, so the trailing + * wrapped KEK packed object must not replace the one in the record. + */ + result = libfsapfs_key_encrypted_key_initialize( + &key_encrypted_key, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "key_encrypted_key", + key_encrypted_key ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + fsapfs_test_key_encrypted_key_data7, + 223, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = memory_compare( + key_encrypted_key->hmac, + fsapfs_test_key_encrypted_key_hmac1, + 32 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + result = memory_compare( + key_encrypted_key->identifier, + fsapfs_test_key_encrypted_key_identifier1, + 16 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + result = memory_compare( + key_encrypted_key->wrapped_kek, + fsapfs_test_key_encrypted_key_wrapped_kek1, + 40 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + result = memory_compare( + key_encrypted_key->salt, + fsapfs_test_key_encrypted_key_salt1, + 16 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + FSAPFS_TEST_ASSERT_EQUAL_UINT64( + "key_encrypted_key->number_of_iterations", + key_encrypted_key->number_of_iterations, + (uint64_t) 559440 ); + + FSAPFS_TEST_ASSERT_EQUAL_UINT32( + "key_encrypted_key->encryption_method", + key_encrypted_key->encryption_method, + (uint32_t) 2 ); + + result = libfsapfs_key_encrypted_key_free( + &key_encrypted_key, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "key_encrypted_key", + key_encrypted_key ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + /* Test error cases + */ + result = libfsapfs_key_encrypted_key_initialize( + &key_encrypted_key, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "key_encrypted_key", + key_encrypted_key ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = libfsapfs_key_encrypted_key_read_data( + NULL, + fsapfs_test_key_encrypted_key_data1, + 162, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + NULL, + 162, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + fsapfs_test_key_encrypted_key_data1, + 1, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + fsapfs_test_key_encrypted_key_data1, + (size_t) SSIZE_MAX + 1, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + + /* Test error case of a record truncated inside the object value data + */ + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + fsapfs_test_key_encrypted_key_data1, + 32, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + + /* Test error case of an unsupported keyblob[2] metadata attribute value + * data size, which is neither 8 nor 22. + */ + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + fsapfs_test_key_encrypted_key_error_data1, + 152, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + + /* Test error case of an 8-byte keyblob[2] metadata attribute combined + * with a 15-byte keyblob[5]. Only the 22-byte metadata attribute + * allows keyblob[5] to be something other than a PBKDF2 salt, so a + * corrupt legacy record must still be reported as unsupported. + */ + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + fsapfs_test_key_encrypted_key_error_data2, + 147, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + + /* Test error case of an 8-byte keyblob[2] metadata attribute combined + * with a 16-byte keyblob[4]. Rejected for the same reason. + */ + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + fsapfs_test_key_encrypted_key_error_data3, + 161, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + + /* Test error case of an empty keyblob[4] number of iterations. + */ + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + fsapfs_test_key_encrypted_key_error_data4, + 159, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + + /* Test error case of a record truncated before the object 0x81 + * long-form length byte. + */ + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + fsapfs_test_key_encrypted_key_error_data5, + 2, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + + /* Test error case of a record truncated inside the object 0x82 + * long-form length bytes. + */ + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + fsapfs_test_key_encrypted_key_error_data6, + 3, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + + /* Test error case of a record truncated before an attribute length byte. + */ + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + fsapfs_test_key_encrypted_key_error_data7, + 3, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + + /* Test error case of a record truncated before an attribute 0x81 + * long-form length byte. + */ + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + fsapfs_test_key_encrypted_key_error_data8, + 4, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + + /* Test error case of a record truncated inside the attribute 0x82 + * long-form length bytes. + */ + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + fsapfs_test_key_encrypted_key_error_data9, + 5, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + + /* Test error case of attribute value data running past the object value + * data. + */ + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + fsapfs_test_key_encrypted_key_error_data10, + 6, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + + /* Test error case of a record truncated before a wrapped KEK packed + * object attribute length byte. + */ + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + fsapfs_test_key_encrypted_key_error_data11, + 8, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + + /* Test error case of a record truncated before a wrapped KEK packed + * object attribute 0x81 long-form length byte. + */ + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + fsapfs_test_key_encrypted_key_error_data12, + 9, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + + /* Test error case of a record truncated before the wrapped KEK packed + * object attribute 0x82 long-form length bytes. + */ + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + fsapfs_test_key_encrypted_key_error_data13, + 9, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + + /* Test error case of wrapped KEK packed object attribute value data + * running past the wrapped KEK packed object. + */ + result = libfsapfs_key_encrypted_key_read_data( + key_encrypted_key, + fsapfs_test_key_encrypted_key_error_data14, + 10, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + + /* Clean up + */ + result = libfsapfs_key_encrypted_key_free( + &key_encrypted_key, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "key_encrypted_key", + key_encrypted_key ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + return( 1 ); + +on_error: + if( error != NULL ) + { + libcerror_error_free( + &error ); + } + if( key_encrypted_key != NULL ) + { + libfsapfs_key_encrypted_key_free( + &key_encrypted_key, + NULL ); + } + return( 0 ); +} + +#endif /* defined( __GNUC__ ) && !defined( LIBFSAPFS_DLL_IMPORT ) */ + +/* The main program + */ +#if defined( HAVE_WIDE_SYSTEM_CHARACTER ) +int wmain( + int argc FSAPFS_TEST_ATTRIBUTE_UNUSED, + wchar_t * const argv[] FSAPFS_TEST_ATTRIBUTE_UNUSED ) +#else +int main( + int argc FSAPFS_TEST_ATTRIBUTE_UNUSED, + char * const argv[] FSAPFS_TEST_ATTRIBUTE_UNUSED ) +#endif +{ + FSAPFS_TEST_UNREFERENCED_PARAMETER( argc ) + FSAPFS_TEST_UNREFERENCED_PARAMETER( argv ) + +#if defined( __GNUC__ ) && !defined( LIBFSAPFS_DLL_IMPORT ) + + FSAPFS_TEST_RUN( + "libfsapfs_key_encrypted_key_initialize", + fsapfs_test_key_encrypted_key_initialize ); + + FSAPFS_TEST_RUN( + "libfsapfs_key_encrypted_key_free", + fsapfs_test_key_encrypted_key_free ); + + FSAPFS_TEST_RUN( + "libfsapfs_key_encrypted_key_read_data", + fsapfs_test_key_encrypted_key_read_data ); /* TODO: add tests for libfsapfs_key_encrypted_key_unlock_with_password */