2016-06-18 14:46:12 +02:00
/*************************************************************************/
/* file_access_encrypted.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2016-06-18 14:46:12 +02:00
/*************************************************************************/
2020-01-01 11:16:22 +01:00
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
2016-06-18 14:46:12 +02: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-05 00:50:27 +01:00
2014-04-15 03:16:13 +02:00
# include "file_access_encrypted.h"
2017-04-28 18:29:15 +02:00
2019-07-11 15:21:47 +02:00
# include "core/crypto/crypto_core.h"
2018-09-11 18:13:45 +02:00
# include "core/os/copymem.h"
# include "core/print_string.h"
2017-04-28 18:29:15 +02:00
# include "core/variant.h"
2014-04-15 03:16:13 +02:00
2015-06-30 16:28:43 +02:00
# include <stdio.h>
2014-04-15 03:16:13 +02:00
2020-04-28 19:51:29 +02:00
Error FileAccessEncrypted : : open_and_parse ( FileAccess * p_base , const Vector < uint8_t > & p_key , Mode p_mode , bool p_with_magic ) {
2020-04-02 01:20:12 +02:00
ERR_FAIL_COND_V_MSG ( file ! = nullptr , ERR_ALREADY_IN_USE , " Can't open file while another file from path ' " + file - > get_path_absolute ( ) + " ' is open. " ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( p_key . size ( ) ! = 32 , ERR_INVALID_PARAMETER ) ;
2014-04-29 02:56:43 +02:00
2017-03-05 16:44:50 +01:00
pos = 0 ;
eofed = false ;
2020-04-28 19:51:29 +02:00
use_magic = p_with_magic ;
2014-04-15 03:16:13 +02:00
2017-03-05 16:44:50 +01:00
if ( p_mode = = MODE_WRITE_AES256 ) {
2014-04-15 03:16:13 +02:00
data . clear ( ) ;
2017-03-05 16:44:50 +01:00
writing = true ;
file = p_base ;
key = p_key ;
2014-04-15 03:16:13 +02:00
2017-03-05 16:44:50 +01:00
} else if ( p_mode = = MODE_READ ) {
writing = false ;
key = p_key ;
2017-10-13 07:40:19 +02:00
2020-04-28 19:51:29 +02: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 07:40:19 +02:00
2014-04-29 02:56:43 +02:00
unsigned char md5d [ 16 ] ;
2017-03-05 16:44:50 +01:00
p_base - > get_buffer ( md5d , 16 ) ;
length = p_base - > get_64 ( ) ;
2020-04-28 19:51:29 +02:00
unsigned char iv [ 16 ] ;
for ( int i = 0 ; i < 16 ; i + + ) {
iv [ i ] = p_base - > get_8 ( ) ;
}
2017-09-10 15:37:49 +02:00
base = p_base - > get_position ( ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( p_base - > get_len ( ) < base + length , ERR_FILE_CORRUPT ) ;
2017-08-31 23:30:35 +02:00
uint32_t ds = length ;
2014-04-29 02:56:43 +02:00
if ( ds % 16 ) {
2017-03-05 16:44:50 +01:00
ds + = 16 - ( ds % 16 ) ;
2014-04-29 02:56:43 +02:00
}
data . resize ( ds ) ;
2017-11-25 04:07:54 +01:00
uint32_t blen = p_base - > get_buffer ( data . ptrw ( ) , ds ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( blen ! = ds , ERR_FILE_CORRUPT ) ;
2014-04-29 02:56:43 +02:00
2020-04-28 19:51:29 +02:00
{
CryptoCore : : AESContext ctx ;
2014-04-29 02:56:43 +02:00
2020-04-28 19:51:29 +02:00
ctx . set_encode_key ( key . ptrw ( ) , 256 ) ; // Due to the nature of CFB, same key schedule is used for both encryption and decryption!
ctx . decrypt_cfb ( ds , iv , data . ptrw ( ) , data . ptrw ( ) ) ;
2014-04-29 02:56:43 +02:00
}
data . resize ( length ) ;
2019-07-02 03:06:52 +02:00
unsigned char hash [ 16 ] ;
ERR_FAIL_COND_V ( CryptoCore : : md5 ( data . ptr ( ) , data . size ( ) , hash ) ! = OK , ERR_BUG ) ;
2014-04-29 02:56:43 +02:00
2019-08-15 04:57:49 +02: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 02:56:43 +02:00
2017-03-05 16:44:50 +01:00
file = p_base ;
2014-04-15 03:16:13 +02:00
}
return OK ;
}
2017-03-05 16:44:50 +01:00
Error FileAccessEncrypted : : open_and_parse_password ( FileAccess * p_base , const String & p_key , Mode p_mode ) {
2014-04-15 03:16:13 +02:00
String cs = p_key . md5_text ( ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( cs . length ( ) ! = 32 , ERR_INVALID_PARAMETER ) ;
2014-04-15 03:16:13 +02:00
Vector < uint8_t > key ;
key . resize ( 32 ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < 32 ; i + + ) {
2018-07-25 03:11:03 +02:00
key . write [ i ] = cs [ i ] ;
2014-04-15 03:16:13 +02:00
}
2017-03-05 16:44:50 +01:00
return open_and_parse ( p_base , key , p_mode ) ;
2014-04-15 03:16:13 +02:00
}
2017-03-05 16:44:50 +01:00
Error FileAccessEncrypted : : _open ( const String & p_path , int p_mode_flags ) {
2014-04-15 03:16:13 +02:00
return OK ;
}
2020-05-14 14:29:06 +02:00
2014-04-15 03:16:13 +02:00
void FileAccessEncrypted : : close ( ) {
2020-05-14 16:41:43 +02:00
if ( ! file ) {
2014-04-15 03:16:13 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2014-04-15 03:16:13 +02:00
2020-04-28 19:51:29 +02:00
_release ( ) ;
file - > close ( ) ;
memdelete ( file ) ;
file = nullptr ;
}
void FileAccessEncrypted : : release ( ) {
if ( ! file ) {
return ;
}
_release ( ) ;
file = nullptr ;
}
void FileAccessEncrypted : : _release ( ) {
2014-04-15 03:16:13 +02:00
if ( writing ) {
Vector < uint8_t > compressed ;
size_t len = data . size ( ) ;
if ( len % 16 ) {
2017-03-05 16:44:50 +01:00
len + = 16 - ( len % 16 ) ;
2014-04-15 03:16:13 +02:00
}
2019-07-02 03:06:52 +02:00
unsigned char hash [ 16 ] ;
ERR_FAIL_COND ( CryptoCore : : md5 ( data . ptr ( ) , data . size ( ) , hash ) ! = OK ) ; // Bug?
2014-04-29 02:56:43 +02:00
2014-04-15 03:16:13 +02:00
compressed . resize ( len ) ;
2017-11-25 04:07:54 +01:00
zeromem ( compressed . ptrw ( ) , len ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < data . size ( ) ; i + + ) {
2018-07-25 03:11:03 +02:00
compressed . write [ i ] = data [ i ] ;
2014-04-15 03:16:13 +02:00
}
2019-07-02 03:06:52 +02:00
CryptoCore : : AESContext ctx ;
ctx . set_encode_key ( key . ptrw ( ) , 256 ) ;
2014-04-15 03:16:13 +02:00
2020-04-28 19:51:29 +02:00
if ( use_magic ) {
file - > store_32 ( ENCRYPTED_HEADER_MAGIC ) ;
2014-04-15 03:16:13 +02:00
}
2019-07-02 03:06:52 +02:00
file - > store_buffer ( hash , 16 ) ;
2014-04-15 03:16:13 +02:00
file - > store_64 ( data . size ( ) ) ;
2020-04-28 19:51:29 +02:00
unsigned char iv [ 16 ] ;
for ( int i = 0 ; i < 16 ; i + + ) {
iv [ i ] = Math : : rand ( ) % 256 ;
file - > store_8 ( iv [ i ] ) ;
}
2014-04-29 02:56:43 +02:00
2020-04-28 19:51:29 +02:00
ctx . encrypt_cfb ( len , iv , compressed . ptrw ( ) , compressed . ptrw ( ) ) ;
file - > store_buffer ( compressed . ptr ( ) , compressed . size ( ) ) ;
2014-04-29 02:56:43 +02:00
data . clear ( ) ;
2014-04-15 03:16:13 +02:00
}
}
2017-03-05 16:44:50 +01:00
bool FileAccessEncrypted : : is_open ( ) const {
2020-04-02 01:20:12 +02:00
return file ! = nullptr ;
2014-04-15 03:16:13 +02:00
}
2019-09-07 19:22:33 +02:00
String FileAccessEncrypted : : get_path ( ) const {
2020-05-14 16:41:43 +02:00
if ( file ) {
2019-09-07 19:22:33 +02:00
return file - > get_path ( ) ;
2020-05-14 16:41:43 +02:00
} else {
2019-09-07 19:22:33 +02:00
return " " ;
2020-05-14 16:41:43 +02:00
}
2019-09-07 19:22:33 +02:00
}
String FileAccessEncrypted : : get_path_absolute ( ) const {
2020-05-14 16:41:43 +02:00
if ( file ) {
2019-09-07 19:22:33 +02:00
return file - > get_path_absolute ( ) ;
2020-05-14 16:41:43 +02:00
} else {
2019-09-07 19:22:33 +02:00
return " " ;
2020-05-14 16:41:43 +02:00
}
2019-09-07 19:22:33 +02:00
}
2017-03-05 16:44:50 +01:00
void FileAccessEncrypted : : seek ( size_t p_position ) {
2020-05-14 16:41:43 +02:00
if ( p_position > ( size_t ) data . size ( ) ) {
2017-03-05 16:44:50 +01:00
p_position = data . size ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-04-15 03:16:13 +02:00
2017-03-05 16:44:50 +01:00
pos = p_position ;
eofed = false ;
2014-04-15 03:16:13 +02:00
}
2017-03-05 16:44:50 +01:00
void FileAccessEncrypted : : seek_end ( int64_t p_position ) {
seek ( data . size ( ) + p_position ) ;
2014-04-15 03:16:13 +02:00
}
2020-05-14 14:29:06 +02:00
2017-09-10 15:37:49 +02:00
size_t FileAccessEncrypted : : get_position ( ) const {
2014-04-15 03:16:13 +02:00
return pos ;
}
2020-05-14 14:29:06 +02:00
2017-03-05 16:44:50 +01:00
size_t FileAccessEncrypted : : get_len ( ) const {
2014-04-29 02:56:43 +02:00
return data . size ( ) ;
2014-04-15 03:16:13 +02:00
}
2017-03-05 16:44:50 +01:00
bool FileAccessEncrypted : : eof_reached ( ) const {
2014-04-29 02:56:43 +02:00
return eofed ;
}
2014-04-15 03:16:13 +02:00
2017-03-05 16:44:50 +01:00
uint8_t FileAccessEncrypted : : get_8 ( ) const {
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_V_MSG ( writing , 0 , " File has not been opened in read mode. " ) ;
2017-03-05 16:44:50 +01:00
if ( pos > = data . size ( ) ) {
eofed = true ;
2014-04-29 02:56:43 +02:00
return 0 ;
2014-04-15 03:16:13 +02:00
}
2014-04-29 02:56:43 +02:00
uint8_t b = data [ pos ] ;
pos + + ;
return b ;
2014-04-15 03:16:13 +02:00
}
2020-05-14 14:29:06 +02:00
2017-03-05 16:44:50 +01:00
int FileAccessEncrypted : : get_buffer ( uint8_t * p_dst , int p_length ) const {
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_V_MSG ( writing , 0 , " File has not been opened in read mode. " ) ;
2014-04-15 03:16:13 +02:00
2017-03-05 16:44:50 +01:00
int to_copy = MIN ( p_length , data . size ( ) - pos ) ;
for ( int i = 0 ; i < to_copy ; i + + ) {
p_dst [ i ] = data [ pos + + ] ;
2014-04-29 02:56:43 +02:00
}
2017-03-05 16:44:50 +01:00
if ( to_copy < p_length ) {
eofed = true ;
2014-04-29 02:56:43 +02:00
}
return to_copy ;
2014-04-15 03:16:13 +02:00
}
2017-03-05 16:44:50 +01:00
Error FileAccessEncrypted : : get_error ( ) const {
return eofed ? ERR_FILE_EOF : OK ;
2014-04-15 03:16:13 +02:00
}
2017-03-05 16:44:50 +01:00
void FileAccessEncrypted : : store_buffer ( const uint8_t * p_src , int p_length ) {
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_MSG ( ! writing , " File has not been opened in read mode. " ) ;
2014-04-15 03:16:13 +02:00
2017-03-05 16:44:50 +01:00
if ( pos < data . size ( ) ) {
for ( int i = 0 ; i < p_length ; i + + ) {
2014-04-15 03:16:13 +02:00
store_8 ( p_src [ i ] ) ;
}
2017-03-05 16:44:50 +01:00
} else if ( pos = = data . size ( ) ) {
data . resize ( pos + p_length ) ;
for ( int i = 0 ; i < p_length ; i + + ) {
2018-07-25 03:11:03 +02:00
data . write [ pos + i ] = p_src [ i ] ;
2014-04-15 03:16:13 +02:00
}
2017-03-05 16:44:50 +01:00
pos + = p_length ;
2014-04-15 03:16:13 +02:00
}
}
2017-09-22 07:56:02 +02:00
void FileAccessEncrypted : : flush ( ) {
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_MSG ( ! writing , " File has not been opened in read mode. " ) ;
2017-09-22 07:56:02 +02:00
// encrypted files keep data in memory till close()
}
2017-03-05 16:44:50 +01:00
void FileAccessEncrypted : : store_8 ( uint8_t p_dest ) {
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_MSG ( ! writing , " File has not been opened in read mode. " ) ;
2014-04-15 03:16:13 +02:00
2017-03-05 16:44:50 +01:00
if ( pos < data . size ( ) ) {
2018-07-25 03:11:03 +02:00
data . write [ pos ] = p_dest ;
2014-04-15 03:16:13 +02:00
pos + + ;
2017-03-05 16:44:50 +01:00
} else if ( pos = = data . size ( ) ) {
2014-04-15 03:16:13 +02:00
data . push_back ( p_dest ) ;
pos + + ;
}
}
2017-03-05 16:44:50 +01:00
bool FileAccessEncrypted : : file_exists ( const String & p_name ) {
FileAccess * fa = FileAccess : : open ( p_name , FileAccess : : READ ) ;
2020-05-14 16:41:43 +02:00
if ( ! fa ) {
2014-04-15 03:16:13 +02:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-04-15 03:16:13 +02:00
memdelete ( fa ) ;
return true ;
}
2017-03-05 16:44:50 +01:00
uint64_t FileAccessEncrypted : : _get_modified_time ( const String & p_file ) {
2014-04-15 03:16:13 +02:00
return 0 ;
}
2019-04-07 20:46:52 +02: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-08-15 04:57:49 +02:00
ERR_PRINT ( " Setting UNIX permissions on encrypted files is not implemented yet. " ) ;
2019-06-03 14:20:43 +02:00
return ERR_UNAVAILABLE ;
2019-04-07 20:46:52 +02:00
}
2014-04-15 03:16:13 +02:00
FileAccessEncrypted : : ~ FileAccessEncrypted ( ) {
2020-05-14 16:41:43 +02:00
if ( file ) {
2014-04-15 03:16:13 +02:00
close ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-04-15 03:16:13 +02:00
}