2016-06-18 12:46:12 +00:00
/*************************************************************************/
/* file_access_encrypted.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 12:16:55 +00:00
/* https://godotengine.org */
2016-06-18 12:46:12 +00:00
/*************************************************************************/
2019-01-01 11:53:14 +00:00
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
2016-06-18 12:46:12 +00:00
/* */
/* 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-04 23:50:27 +00:00
2014-04-15 01:16:13 +00:00
# include "file_access_encrypted.h"
2017-04-28 16:29:15 +00:00
2018-09-11 16:13:45 +00:00
# include "core/os/copymem.h"
# include "core/print_string.h"
2017-04-28 16:29:15 +00:00
# include "core/variant.h"
2014-04-15 01:16:13 +00:00
2017-04-28 16:29:15 +00:00
# include "thirdparty/misc/aes256.h"
# include "thirdparty/misc/md5.h"
2015-06-30 14:28:43 +00:00
# include <stdio.h>
2014-04-15 01:16:13 +00:00
2017-04-28 16:29:15 +00:00
# define COMP_MAGIC 0x43454447
2017-03-05 15:44:50 +00:00
Error FileAccessEncrypted : : open_and_parse ( FileAccess * p_base , const Vector < uint8_t > & p_key , Mode p_mode ) {
2014-04-15 01:16:13 +00:00
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND_V ( file ! = NULL , ERR_ALREADY_IN_USE ) ;
ERR_FAIL_COND_V ( p_key . size ( ) ! = 32 , ERR_INVALID_PARAMETER ) ;
2014-04-29 00:56:43 +00:00
2017-03-05 15:44:50 +00:00
pos = 0 ;
eofed = false ;
2014-04-15 01:16:13 +00:00
2017-03-05 15:44:50 +00:00
if ( p_mode = = MODE_WRITE_AES256 ) {
2014-04-15 01:16:13 +00:00
data . clear ( ) ;
2017-03-05 15:44:50 +00:00
writing = true ;
file = p_base ;
mode = p_mode ;
key = p_key ;
2014-04-15 01:16:13 +00:00
2017-03-05 15:44:50 +00:00
} else if ( p_mode = = MODE_READ ) {
2014-04-29 00:56:43 +00:00
2017-03-05 15:44:50 +00:00
writing = false ;
key = p_key ;
2014-04-29 00:56:43 +00:00
uint32_t magic = p_base - > get_32 ( ) ;
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND_V ( magic ! = COMP_MAGIC , ERR_FILE_UNRECOGNIZED ) ;
2017-10-13 05:40:19 +00:00
2017-03-05 15:44:50 +00:00
mode = Mode ( p_base - > get_32 ( ) ) ;
ERR_FAIL_INDEX_V ( mode , MODE_MAX , ERR_FILE_CORRUPT ) ;
ERR_FAIL_COND_V ( mode = = 0 , ERR_FILE_CORRUPT ) ;
2017-10-13 05:40:19 +00:00
2014-04-29 00:56:43 +00:00
unsigned char md5d [ 16 ] ;
2017-03-05 15:44:50 +00:00
p_base - > get_buffer ( md5d , 16 ) ;
length = p_base - > get_64 ( ) ;
2017-09-10 13:37:49 +00:00
base = p_base - > get_position ( ) ;
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND_V ( p_base - > get_len ( ) < base + length , ERR_FILE_CORRUPT ) ;
2017-08-31 21:30:35 +00:00
uint32_t ds = length ;
2014-04-29 00:56:43 +00:00
if ( ds % 16 ) {
2017-03-05 15:44:50 +00:00
ds + = 16 - ( ds % 16 ) ;
2014-04-29 00:56:43 +00:00
}
data . resize ( ds ) ;
2017-11-25 03:07:54 +00:00
uint32_t blen = p_base - > get_buffer ( data . ptrw ( ) , ds ) ;
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND_V ( blen ! = ds , ERR_FILE_CORRUPT ) ;
2014-04-29 00:56:43 +00:00
aes256_context ctx ;
2017-11-25 03:07:54 +00:00
aes256_init ( & ctx , key . ptrw ( ) ) ;
2014-04-29 00:56:43 +00:00
2017-03-05 15:44:50 +00:00
for ( size_t i = 0 ; i < ds ; i + = 16 ) {
2014-04-29 00:56:43 +00:00
2018-07-25 01:11:03 +00:00
aes256_decrypt_ecb ( & ctx , & data . write [ i ] ) ;
2014-04-29 00:56:43 +00:00
}
aes256_done ( & ctx ) ;
data . resize ( length ) ;
MD5_CTX md5 ;
MD5Init ( & md5 ) ;
2017-11-25 03:07:54 +00:00
MD5Update ( & md5 , ( uint8_t * ) data . ptr ( ) , data . size ( ) ) ;
2014-04-29 00:56:43 +00:00
MD5Final ( & md5 ) ;
2019-05-29 15:57:37 +00:00
ERR_EXPLAIN ( " 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. " ) ;
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND_V ( String : : md5 ( md5 . digest ) ! = String : : md5 ( md5d ) , ERR_FILE_CORRUPT ) ;
2014-04-29 00:56:43 +00:00
2017-03-05 15:44:50 +00:00
file = p_base ;
2014-04-15 01:16:13 +00:00
}
return OK ;
}
2017-03-05 15:44:50 +00:00
Error FileAccessEncrypted : : open_and_parse_password ( FileAccess * p_base , const String & p_key , Mode p_mode ) {
2014-04-15 01:16:13 +00:00
String cs = p_key . md5_text ( ) ;
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND_V ( cs . length ( ) ! = 32 , ERR_INVALID_PARAMETER ) ;
2014-04-15 01:16:13 +00:00
Vector < uint8_t > key ;
key . resize ( 32 ) ;
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < 32 ; i + + ) {
2014-04-15 01:16:13 +00:00
2018-07-25 01:11:03 +00:00
key . write [ i ] = cs [ i ] ;
2014-04-15 01:16:13 +00:00
}
2017-03-05 15:44:50 +00:00
return open_and_parse ( p_base , key , p_mode ) ;
2014-04-15 01:16:13 +00:00
}
2017-03-05 15:44:50 +00:00
Error FileAccessEncrypted : : _open ( const String & p_path , int p_mode_flags ) {
2014-04-15 01:16:13 +00:00
return OK ;
}
void FileAccessEncrypted : : close ( ) {
if ( ! file )
return ;
if ( writing ) {
Vector < uint8_t > compressed ;
size_t len = data . size ( ) ;
if ( len % 16 ) {
2017-03-05 15:44:50 +00:00
len + = 16 - ( len % 16 ) ;
2014-04-15 01:16:13 +00:00
}
2014-04-29 00:56:43 +00:00
MD5_CTX md5 ;
MD5Init ( & md5 ) ;
2017-11-25 03:07:54 +00:00
MD5Update ( & md5 , ( uint8_t * ) data . ptr ( ) , data . size ( ) ) ;
2014-04-29 00:56:43 +00:00
MD5Final ( & md5 ) ;
2014-04-15 01:16:13 +00:00
compressed . resize ( len ) ;
2017-11-25 03:07:54 +00:00
zeromem ( compressed . ptrw ( ) , len ) ;
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < data . size ( ) ; i + + ) {
2018-07-25 01:11:03 +00:00
compressed . write [ i ] = data [ i ] ;
2014-04-15 01:16:13 +00:00
}
aes256_context ctx ;
2017-11-25 03:07:54 +00:00
aes256_init ( & ctx , key . ptrw ( ) ) ;
2014-04-15 01:16:13 +00:00
2017-03-05 15:44:50 +00:00
for ( size_t i = 0 ; i < len ; i + = 16 ) {
2014-04-15 01:16:13 +00:00
2018-07-25 01:11:03 +00:00
aes256_encrypt_ecb ( & ctx , & compressed . write [ i ] ) ;
2014-04-15 01:16:13 +00:00
}
aes256_done ( & ctx ) ;
file - > store_32 ( COMP_MAGIC ) ;
file - > store_32 ( mode ) ;
2017-03-05 15:44:50 +00:00
file - > store_buffer ( md5 . digest , 16 ) ;
2014-04-15 01:16:13 +00:00
file - > store_64 ( data . size ( ) ) ;
2017-03-05 15:44:50 +00:00
file - > store_buffer ( compressed . ptr ( ) , compressed . size ( ) ) ;
2014-04-15 01:16:13 +00:00
file - > close ( ) ;
memdelete ( file ) ;
2017-03-05 15:44:50 +00:00
file = NULL ;
2014-04-29 00:56:43 +00:00
data . clear ( ) ;
} else {
2014-04-15 01:16:13 +00:00
2014-04-29 00:56:43 +00:00
file - > close ( ) ;
memdelete ( file ) ;
data . clear ( ) ;
2017-03-05 15:44:50 +00:00
file = NULL ;
2014-04-15 01:16:13 +00:00
}
}
2017-03-05 15:44:50 +00:00
bool FileAccessEncrypted : : is_open ( ) const {
2014-04-15 01:16:13 +00:00
2017-03-05 15:44:50 +00:00
return file ! = NULL ;
2014-04-15 01:16:13 +00:00
}
2017-03-05 15:44:50 +00:00
void FileAccessEncrypted : : seek ( size_t p_position ) {
2014-04-15 01:16:13 +00:00
2014-04-29 00:56:43 +00:00
if ( p_position > ( size_t ) data . size ( ) )
2017-03-05 15:44:50 +00:00
p_position = data . size ( ) ;
2014-04-15 01:16:13 +00:00
2017-03-05 15:44:50 +00:00
pos = p_position ;
eofed = false ;
2014-04-15 01:16:13 +00:00
}
2017-03-05 15:44:50 +00:00
void FileAccessEncrypted : : seek_end ( int64_t p_position ) {
2014-04-15 01:16:13 +00:00
2017-03-05 15:44:50 +00:00
seek ( data . size ( ) + p_position ) ;
2014-04-15 01:16:13 +00:00
}
2017-09-10 13:37:49 +00:00
size_t FileAccessEncrypted : : get_position ( ) const {
2014-04-15 01:16:13 +00:00
return pos ;
}
2017-03-05 15:44:50 +00:00
size_t FileAccessEncrypted : : get_len ( ) const {
2014-04-15 01:16:13 +00:00
2014-04-29 00:56:43 +00:00
return data . size ( ) ;
2014-04-15 01:16:13 +00:00
}
2017-03-05 15:44:50 +00:00
bool FileAccessEncrypted : : eof_reached ( ) const {
2014-04-15 01:16:13 +00:00
2014-04-29 00:56:43 +00:00
return eofed ;
}
2014-04-15 01:16:13 +00:00
2017-03-05 15:44:50 +00:00
uint8_t FileAccessEncrypted : : get_8 ( ) const {
2014-04-15 01:16:13 +00:00
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND_V ( writing , 0 ) ;
if ( pos > = data . size ( ) ) {
eofed = true ;
2014-04-29 00:56:43 +00:00
return 0 ;
2014-04-15 01:16:13 +00:00
}
2014-04-29 00:56:43 +00:00
uint8_t b = data [ pos ] ;
pos + + ;
return b ;
2014-04-15 01:16:13 +00:00
}
2017-03-05 15:44:50 +00:00
int FileAccessEncrypted : : get_buffer ( uint8_t * p_dst , int p_length ) const {
2014-04-15 01:16:13 +00:00
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND_V ( writing , 0 ) ;
2014-04-15 01:16:13 +00:00
2017-03-05 15:44:50 +00:00
int to_copy = MIN ( p_length , data . size ( ) - pos ) ;
for ( int i = 0 ; i < to_copy ; i + + ) {
2014-04-29 00:56:43 +00:00
2017-03-05 15:44:50 +00:00
p_dst [ i ] = data [ pos + + ] ;
2014-04-29 00:56:43 +00:00
}
2017-03-05 15:44:50 +00:00
if ( to_copy < p_length ) {
eofed = true ;
2014-04-29 00:56:43 +00:00
}
return to_copy ;
2014-04-15 01:16:13 +00:00
}
2017-03-05 15:44:50 +00:00
Error FileAccessEncrypted : : get_error ( ) const {
2014-04-15 01:16:13 +00:00
2017-03-05 15:44:50 +00:00
return eofed ? ERR_FILE_EOF : OK ;
2014-04-15 01:16:13 +00:00
}
2017-03-05 15:44:50 +00:00
void FileAccessEncrypted : : store_buffer ( const uint8_t * p_src , int p_length ) {
2014-04-15 01:16:13 +00:00
ERR_FAIL_COND ( ! writing ) ;
2017-03-05 15:44:50 +00:00
if ( pos < data . size ( ) ) {
2014-04-15 01:16:13 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < p_length ; i + + ) {
2014-04-15 01:16:13 +00:00
store_8 ( p_src [ i ] ) ;
}
2017-03-05 15:44:50 +00:00
} else if ( pos = = data . size ( ) ) {
2014-04-15 01:16:13 +00:00
2017-03-05 15:44:50 +00:00
data . resize ( pos + p_length ) ;
for ( int i = 0 ; i < p_length ; i + + ) {
2014-04-15 01:16:13 +00:00
2018-07-25 01:11:03 +00:00
data . write [ pos + i ] = p_src [ i ] ;
2014-04-15 01:16:13 +00:00
}
2017-03-05 15:44:50 +00:00
pos + = p_length ;
2014-04-15 01:16:13 +00:00
}
}
2017-09-22 05:56:02 +00:00
void FileAccessEncrypted : : flush ( ) {
ERR_FAIL_COND ( ! writing ) ;
// encrypted files keep data in memory till close()
}
2017-03-05 15:44:50 +00:00
void FileAccessEncrypted : : store_8 ( uint8_t p_dest ) {
2014-04-15 01:16:13 +00:00
ERR_FAIL_COND ( ! writing ) ;
2017-03-05 15:44:50 +00:00
if ( pos < data . size ( ) ) {
2018-07-25 01:11:03 +00:00
data . write [ pos ] = p_dest ;
2014-04-15 01:16:13 +00:00
pos + + ;
2017-03-05 15:44:50 +00:00
} else if ( pos = = data . size ( ) ) {
2014-04-15 01:16:13 +00:00
data . push_back ( p_dest ) ;
pos + + ;
}
}
2017-03-05 15:44:50 +00:00
bool FileAccessEncrypted : : file_exists ( const String & p_name ) {
2014-04-15 01:16:13 +00:00
2017-03-05 15:44:50 +00:00
FileAccess * fa = FileAccess : : open ( p_name , FileAccess : : READ ) ;
2014-04-15 01:16:13 +00:00
if ( ! fa )
return false ;
memdelete ( fa ) ;
return true ;
}
2017-03-05 15:44:50 +00:00
uint64_t FileAccessEncrypted : : _get_modified_time ( const String & p_file ) {
2014-04-15 01:16:13 +00:00
return 0 ;
}
2019-04-07 18:46:52 +00:00
uint32_t FileAccessEncrypted : : _get_unix_permissions ( const String & p_file ) {
return 0 ;
}
Error FileAccessEncrypted : : _set_unix_permissions ( const String & p_file , uint32_t p_permissions ) {
2019-06-03 12:20:43 +00:00
ERR_PRINT ( " Setting UNIX permissions on encrypted files is not implemented yet " ) ;
return ERR_UNAVAILABLE ;
2019-04-07 18:46:52 +00:00
}
2014-04-15 01:16:13 +00:00
FileAccessEncrypted : : FileAccessEncrypted ( ) {
2017-03-05 15:44:50 +00:00
file = NULL ;
pos = 0 ;
eofed = false ;
mode = MODE_MAX ;
writing = false ;
2014-04-15 01:16:13 +00:00
}
FileAccessEncrypted : : ~ FileAccessEncrypted ( ) {
if ( file )
close ( ) ;
}