2016-06-18 20:46:12 +08:00
/**************************************************************************/
/* file_access_encrypted.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2018-01-05 07:50:27 +08:00
2014-04-15 09:16:13 +08:00
# include "file_access_encrypted.h"
2017-04-29 00:29:15 +08:00
2019-07-11 21:21:47 +08:00
# include "core/crypto/crypto_core.h"
2020-11-08 06:33:38 +08:00
# include "core/string/print_string.h"
# include "core/variant/variant.h"
2014-04-15 09:16:13 +08:00
2015-06-30 22:28:43 +08:00
# include <stdio.h>
2014-04-15 09:16:13 +08:00
2024-11-07 18:32:02 +08:00
Error FileAccessEncrypted : : open_and_parse ( Ref < FileAccess > p_base , const Vector < uint8_t > & p_key , Mode p_mode , bool p_with_magic , const Vector < uint8_t > & p_iv ) {
2024-10-11 22:17:49 +08:00
ERR_FAIL_COND_V_MSG ( file . is_valid ( ) , ERR_ALREADY_IN_USE , vformat ( " Can't open file while another file from path '%s' is open. " , file - > get_path_absolute ( ) ) ) ;
2014-04-29 08:56:43 +08:00
ERR_FAIL_COND_V ( p_key . size ( ) ! = 32 , ERR_INVALID_PARAMETER ) ;
pos = 0 ;
eofed = false ;
2020-04-29 01:51:29 +08:00
use_magic = p_with_magic ;
2014-04-15 09:16:13 +08:00
if ( p_mode = = MODE_WRITE_AES256 ) {
data . clear ( ) ;
writing = true ;
file = p_base ;
key = p_key ;
2024-11-07 18:32:02 +08:00
if ( p_iv . is_empty ( ) ) {
iv . resize ( 16 ) ;
CryptoCore : : RandomGenerator rng ;
ERR_FAIL_COND_V_MSG ( rng . init ( ) , FAILED , " Failed to initialize random number generator. " ) ;
Error err = rng . get_random_bytes ( iv . ptrw ( ) , 16 ) ;
ERR_FAIL_COND_V ( err ! = OK , err ) ;
} else {
ERR_FAIL_COND_V ( p_iv . size ( ) ! = 16 , ERR_INVALID_PARAMETER ) ;
iv = p_iv ;
}
2014-04-15 09:16:13 +08:00
2014-04-29 08:56:43 +08:00
} else if ( p_mode = = MODE_READ ) {
2014-05-24 12:35:47 +08:00
writing = false ;
2014-04-29 08:56:43 +08:00
key = p_key ;
2017-10-13 13:40:19 +08:00
2020-04-29 01:51:29 +08:00
if ( use_magic ) {
uint32_t magic = p_base - > get_32 ( ) ;
ERR_FAIL_COND_V ( magic ! = ENCRYPTED_HEADER_MAGIC , ERR_FILE_UNRECOGNIZED ) ;
}
2017-10-13 13:40:19 +08:00
2014-04-29 08:56:43 +08:00
unsigned char md5d [ 16 ] ;
p_base - > get_buffer ( md5d , 16 ) ;
length = p_base - > get_64 ( ) ;
2020-04-29 01:51:29 +08:00
2024-11-07 18:32:02 +08:00
iv . resize ( 16 ) ;
p_base - > get_buffer ( iv . ptrw ( ) , 16 ) ;
2020-04-29 01:51:29 +08:00
2017-09-10 21:37:49 +08:00
base = p_base - > get_position ( ) ;
2021-05-25 14:58:49 +08:00
ERR_FAIL_COND_V ( p_base - > get_length ( ) < base + length , ERR_FILE_CORRUPT ) ;
2019-03-27 01:51:13 +08:00
uint64_t ds = length ;
2014-04-29 08:56:43 +08:00
if ( ds % 16 ) {
ds + = 16 - ( ds % 16 ) ;
}
data . resize ( ds ) ;
2019-03-27 01:51:13 +08:00
uint64_t blen = p_base - > get_buffer ( data . ptrw ( ) , ds ) ;
2014-04-29 08:56:43 +08:00
ERR_FAIL_COND_V ( blen ! = ds , ERR_FILE_CORRUPT ) ;
2020-04-29 01:51:29 +08:00
{
CryptoCore : : AESContext ctx ;
2014-04-29 08:56:43 +08:00
2020-04-29 01:51:29 +08:00
ctx . set_encode_key ( key . ptrw ( ) , 256 ) ; // Due to the nature of CFB, same key schedule is used for both encryption and decryption!
2024-11-07 18:32:02 +08:00
ctx . decrypt_cfb ( ds , iv . ptrw ( ) , data . ptrw ( ) , data . ptrw ( ) ) ;
2014-04-29 08:56:43 +08:00
}
data . resize ( length ) ;
2019-07-02 09:06:52 +08:00
unsigned char hash [ 16 ] ;
ERR_FAIL_COND_V ( CryptoCore : : md5 ( data . ptr ( ) , data . size ( ) , hash ) ! = OK , ERR_BUG ) ;
2014-04-29 08:56:43 +08:00
2019-08-15 10:57:49 +08:00
ERR_FAIL_COND_V_MSG ( String : : md5 ( hash ) ! = String : : md5 ( md5d ) , ERR_FILE_CORRUPT , " The MD5 sum of the decrypted file does not match the expected value. It could be that the file is corrupt, or that the provided decryption key is invalid. " ) ;
2014-04-29 08:56:43 +08:00
file = p_base ;
2014-04-15 09:16:13 +08:00
}
return OK ;
}
2022-03-23 17:08:58 +08:00
Error FileAccessEncrypted : : open_and_parse_password ( Ref < FileAccess > p_base , const String & p_key , Mode p_mode ) {
2014-04-15 09:16:13 +08:00
String cs = p_key . md5_text ( ) ;
ERR_FAIL_COND_V ( cs . length ( ) ! = 32 , ERR_INVALID_PARAMETER ) ;
2022-09-29 17:53:28 +08:00
Vector < uint8_t > key_md5 ;
key_md5 . resize ( 32 ) ;
2014-04-15 09:16:13 +08:00
for ( int i = 0 ; i < 32 ; i + + ) {
2022-09-29 17:53:28 +08:00
key_md5 . write [ i ] = cs [ i ] ;
2014-04-15 09:16:13 +08:00
}
2022-09-29 17:53:28 +08:00
return open_and_parse ( p_base , key_md5 , p_mode ) ;
2014-04-15 09:16:13 +08:00
}
2022-09-05 19:01:31 +08:00
Error FileAccessEncrypted : : open_internal ( const String & p_path , int p_mode_flags ) {
2014-04-15 09:16:13 +08:00
return OK ;
}
2020-05-14 20:29:06 +08:00
2022-04-12 16:15:02 +08:00
void FileAccessEncrypted : : _close ( ) {
2022-03-23 17:08:58 +08:00
if ( file . is_null ( ) ) {
2014-04-15 09:16:13 +08:00
return ;
2020-05-14 22:41:43 +08:00
}
2014-04-15 09:16:13 +08:00
if ( writing ) {
Vector < uint8_t > compressed ;
2019-03-27 01:51:13 +08:00
uint64_t len = data . size ( ) ;
2014-04-15 09:16:13 +08:00
if ( len % 16 ) {
len + = 16 - ( len % 16 ) ;
}
2019-07-02 09:06:52 +08:00
unsigned char hash [ 16 ] ;
ERR_FAIL_COND ( CryptoCore : : md5 ( data . ptr ( ) , data . size ( ) , hash ) ! = OK ) ; // Bug?
2014-04-29 08:56:43 +08:00
2014-04-15 09:16:13 +08:00
compressed . resize ( len ) ;
2021-04-27 22:19:21 +08:00
memset ( compressed . ptrw ( ) , 0 , len ) ;
2014-04-15 09:16:13 +08:00
for ( int i = 0 ; i < data . size ( ) ; i + + ) {
2018-07-25 09:11:03 +08:00
compressed . write [ i ] = data [ i ] ;
2014-04-15 09:16:13 +08:00
}
2019-07-02 09:06:52 +08:00
CryptoCore : : AESContext ctx ;
ctx . set_encode_key ( key . ptrw ( ) , 256 ) ;
2014-04-15 09:16:13 +08:00
2020-04-29 01:51:29 +08:00
if ( use_magic ) {
file - > store_32 ( ENCRYPTED_HEADER_MAGIC ) ;
2014-04-15 09:16:13 +08:00
}
2019-07-02 09:06:52 +08:00
file - > store_buffer ( hash , 16 ) ;
2014-04-15 09:16:13 +08:00
file - > store_64 ( data . size ( ) ) ;
2024-11-07 18:32:02 +08:00
file - > store_buffer ( iv . ptr ( ) , 16 ) ;
2014-04-15 09:16:13 +08:00
2024-11-07 18:32:02 +08:00
ctx . encrypt_cfb ( len , iv . ptrw ( ) , compressed . ptrw ( ) , compressed . ptrw ( ) ) ;
2020-04-29 01:51:29 +08:00
file - > store_buffer ( compressed . ptr ( ) , compressed . size ( ) ) ;
2014-04-29 08:56:43 +08:00
data . clear ( ) ;
2014-04-15 09:16:13 +08:00
}
2022-04-12 16:15:02 +08:00
file . unref ( ) ;
2014-04-15 09:16:13 +08:00
}
bool FileAccessEncrypted : : is_open ( ) const {
2024-07-26 17:52:26 +08:00
return file . is_valid ( ) ;
2014-04-15 09:16:13 +08:00
}
2019-09-08 01:22:33 +08:00
String FileAccessEncrypted : : get_path ( ) const {
2022-03-23 17:08:58 +08:00
if ( file . is_valid ( ) ) {
2019-09-08 01:22:33 +08:00
return file - > get_path ( ) ;
2020-05-14 22:41:43 +08:00
} else {
2019-09-08 01:22:33 +08:00
return " " ;
2020-05-14 22:41:43 +08:00
}
2019-09-08 01:22:33 +08:00
}
String FileAccessEncrypted : : get_path_absolute ( ) const {
2022-03-23 17:08:58 +08:00
if ( file . is_valid ( ) ) {
2019-09-08 01:22:33 +08:00
return file - > get_path_absolute ( ) ;
2020-05-14 22:41:43 +08:00
} else {
2019-09-08 01:22:33 +08:00
return " " ;
2020-05-14 22:41:43 +08:00
}
2019-09-08 01:22:33 +08:00
}
2019-03-27 01:51:13 +08:00
void FileAccessEncrypted : : seek ( uint64_t p_position ) {
2021-05-25 14:58:49 +08:00
if ( p_position > get_length ( ) ) {
p_position = get_length ( ) ;
2020-05-14 22:41:43 +08:00
}
2014-04-15 09:16:13 +08:00
2014-04-29 08:56:43 +08:00
pos = p_position ;
eofed = false ;
2014-04-15 09:16:13 +08:00
}
void FileAccessEncrypted : : seek_end ( int64_t p_position ) {
2021-05-25 14:58:49 +08:00
seek ( get_length ( ) + p_position ) ;
2014-04-15 09:16:13 +08:00
}
2020-05-14 20:29:06 +08:00
2019-03-27 01:51:13 +08:00
uint64_t FileAccessEncrypted : : get_position ( ) const {
2014-04-15 09:16:13 +08:00
return pos ;
}
2020-05-14 20:29:06 +08:00
2021-05-25 14:58:49 +08:00
uint64_t FileAccessEncrypted : : get_length ( ) const {
2014-04-29 08:56:43 +08:00
return data . size ( ) ;
2014-04-15 09:16:13 +08:00
}
bool FileAccessEncrypted : : eof_reached ( ) const {
2014-04-29 08:56:43 +08:00
return eofed ;
}
2014-04-15 09:16:13 +08:00
2019-03-27 01:51:13 +08:00
uint64_t FileAccessEncrypted : : get_buffer ( uint8_t * p_dst , uint64_t p_length ) const {
2021-03-17 05:55:11 +08:00
ERR_FAIL_COND_V ( ! p_dst & & p_length > 0 , - 1 ) ;
2021-03-09 09:37:35 +08:00
ERR_FAIL_COND_V_MSG ( writing , - 1 , " File has not been opened in read mode. " ) ;
2014-04-15 09:16:13 +08:00
2021-05-25 14:58:49 +08:00
uint64_t to_copy = MIN ( p_length , get_length ( ) - pos ) ;
2024-05-21 00:07:27 +08:00
memcpy ( p_dst , data . ptr ( ) + pos , to_copy ) ;
pos + = to_copy ;
2014-04-29 08:56:43 +08:00
if ( to_copy < p_length ) {
eofed = true ;
}
return to_copy ;
2014-04-15 09:16:13 +08:00
}
Error FileAccessEncrypted : : get_error ( ) const {
2014-04-29 08:56:43 +08:00
return eofed ? ERR_FILE_EOF : OK ;
2014-04-15 09:16:13 +08:00
}
2023-06-16 02:21:43 +08:00
bool FileAccessEncrypted : : store_buffer ( const uint8_t * p_src , uint64_t p_length ) {
ERR_FAIL_COND_V_MSG ( ! writing , false , " File has not been opened in write mode. " ) ;
ERR_FAIL_COND_V ( ! p_src & & p_length > 0 , false ) ;
2014-04-15 09:16:13 +08:00
2024-05-21 00:07:27 +08:00
if ( pos + p_length > = get_length ( ) ) {
2023-06-16 02:21:43 +08:00
ERR_FAIL_COND_V ( data . resize ( pos + p_length ) ! = OK , false ) ;
2014-04-15 09:16:13 +08:00
}
2024-05-21 00:07:27 +08:00
memcpy ( data . ptrw ( ) + pos , p_src , p_length ) ;
pos + = p_length ;
2023-06-16 02:21:43 +08:00
return true ;
2014-04-15 09:16:13 +08:00
}
2017-09-22 13:56:02 +08:00
void FileAccessEncrypted : : flush ( ) {
2020-12-16 01:00:39 +08:00
ERR_FAIL_COND_MSG ( ! writing , " File has not been opened in write mode. " ) ;
2017-09-22 13:56:02 +08:00
// encrypted files keep data in memory till close()
}
2014-04-15 09:16:13 +08:00
bool FileAccessEncrypted : : file_exists ( const String & p_name ) {
2022-03-23 17:08:58 +08:00
Ref < FileAccess > fa = FileAccess : : open ( p_name , FileAccess : : READ ) ;
if ( fa . is_null ( ) ) {
2014-04-15 09:16:13 +08:00
return false ;
2020-05-14 22:41:43 +08:00
}
2014-04-15 09:16:13 +08:00
return true ;
}
uint64_t FileAccessEncrypted : : _get_modified_time ( const String & p_file ) {
return 0 ;
}
2023-08-08 14:53:41 +08:00
BitField < FileAccess : : UnixPermissionFlags > FileAccessEncrypted : : _get_unix_permissions ( const String & p_file ) {
if ( file . is_valid ( ) ) {
return file - > _get_unix_permissions ( p_file ) ;
}
2019-04-08 02:46:52 +08:00
return 0 ;
}
2023-08-08 14:53:41 +08:00
Error FileAccessEncrypted : : _set_unix_permissions ( const String & p_file , BitField < FileAccess : : UnixPermissionFlags > p_permissions ) {
if ( file . is_valid ( ) ) {
return file - > _set_unix_permissions ( p_file , p_permissions ) ;
}
return FAILED ;
}
bool FileAccessEncrypted : : _get_hidden_attribute ( const String & p_file ) {
if ( file . is_valid ( ) ) {
return file - > _get_hidden_attribute ( p_file ) ;
}
return false ;
}
Error FileAccessEncrypted : : _set_hidden_attribute ( const String & p_file , bool p_hidden ) {
if ( file . is_valid ( ) ) {
return file - > _set_hidden_attribute ( p_file , p_hidden ) ;
}
return FAILED ;
}
bool FileAccessEncrypted : : _get_read_only_attribute ( const String & p_file ) {
if ( file . is_valid ( ) ) {
return file - > _get_read_only_attribute ( p_file ) ;
}
return false ;
}
Error FileAccessEncrypted : : _set_read_only_attribute ( const String & p_file , bool p_ro ) {
if ( file . is_valid ( ) ) {
return file - > _set_read_only_attribute ( p_file , p_ro ) ;
}
return FAILED ;
2019-04-08 02:46:52 +08:00
}
2023-02-16 21:25:32 +08:00
void FileAccessEncrypted : : close ( ) {
_close ( ) ;
}
2014-04-15 09:16:13 +08:00
FileAccessEncrypted : : ~ FileAccessEncrypted ( ) {
2022-04-12 16:15:02 +08:00
_close ( ) ;
2014-04-15 09:16:13 +08:00
}