@ -4,6 +4,7 @@
* Implementation of save game encryption algorithm .
*/
# include <array>
# include <cstdint>
# include "appfat.h"
@ -21,18 +22,16 @@ struct CodecSignature {
uint16_t unused ;
} ;
# define BlockSize 64
// https://stackoverflow.com/a/45172360 - helper to make up for not having an implicit initializer for std::byte
template < typename . . . Ts >
std : : array < byte , sizeof . . . ( Ts ) > make_bytes ( Ts & & . . . args ) noexcept
{
return { byte ( std : : forward < Ts > ( args ) ) . . . } ;
}
void CodecInitKey ( const char * pszPassword )
{
byte key [ 136 ] ; // last 64 bytes are the SHA1
uint32_t randState = 0x7058 ;
for ( auto & notch : key ) {
randState = randState * 214013 + 2531011 ;
notch = static_cast < byte > ( randState > > 16 ) ; // Downcasting to byte keeps the 2 least-significant bytes
}
byte pw [ 64 ] ; // Repeat password until 64 char long
byte pw [ BlockSize ] ; // Repeat password until 64 char long
std : : size_t j = 0 ;
for ( std : : size_t i = 0 ; i < sizeof ( pw ) ; i + + , j + + ) {
if ( pszPassword [ j ] = = ' \0 ' )
@ -44,21 +43,31 @@ void CodecInitKey(const char *pszPassword)
SHA1Reset ( 0 ) ;
SHA1Calculate ( 0 , pw , digest ) ;
SHA1Clear ( ) ;
// declaring key as a std::array to make the initialization easier, otherwise we would need to explicitly
// declare every value as a byte on platforms that use std::byte.
std : : array < byte , BlockSize > key = make_bytes ( // clang-format off
0xbf , 0x2f , 0x63 , 0xad , 0xd0 , 0x56 , 0x27 , 0xf7 , 0x6e , 0x43 , 0x47 , 0x27 , 0x70 , 0xc7 , 0x5b , 0x42 ,
0x58 , 0xac , 0x1e , 0xea , 0xca , 0x50 , 0x7d , 0x28 , 0x43 , 0x93 , 0xee , 0x68 , 0x07 , 0xf3 , 0x03 , 0xc5 ,
0x5b , 0xf6 , 0x3f , 0x87 , 0xf5 , 0xc9 , 0x28 , 0xea , 0xb1 , 0x26 , 0x9d , 0x22 , 0x85 , 0x7a , 0x6a , 0x9b ,
0xb7 , 0x48 , 0x1a , 0x85 , 0x4d , 0xc8 , 0x0d , 0x90 , 0xc6 , 0xd5 , 0xca , 0xf4 , 0x07 , 0x06 , 0x95 , 0xb5
) ; // clang-format on
for ( std : : size_t i = 0 ; i < sizeof ( key ) ; i + + )
key [ i ] ^ = digest [ i % SHA1HashSize ] ;
key [ i ] ^ = digest [ ( i + 12 ) % SHA1HashSize ] ;
memset ( pw , 0 , sizeof ( pw ) ) ;
memset ( digest , 0 , sizeof ( digest ) ) ;
for ( int n = 0 ; n < 3 ; + + n ) {
SHA1Reset ( n ) ;
SHA1Calculate ( n , & key [ 72 ] , nullptr ) ;
SHA1Calculate ( n , key . data ( ) , nullptr ) ;
}
memset ( key , 0 , sizeof ( key ) ) ;
memset ( key . data ( ) , 0 , sizeof ( key ) ) ;
}
} // namespace
std : : size_t codec_decode ( byte * pbSrcDst , std : : size_t size , const char * pszPassword )
{
byte buf [ 128 ] ;
byte buf [ BlockSize ] ;
byte dst [ SHA1HashSize ] ;
CodecInitKey ( pszPassword ) ;
@ -67,7 +76,7 @@ std::size_t codec_decode(byte *pbSrcDst, std::size_t size, const char *pszPasswo
size - = sizeof ( CodecSignature ) ;
if ( size % BlockSize ! = 0 )
return 0 ;
for ( int i = size ; i ! = 0 ; pbSrcDst + = BlockSize , i - = BlockSize ) {
for ( auto i = size ; i ! = 0 ; pbSrcDst + = BlockSize , i - = BlockSize ) {
memcpy ( buf , pbSrcDst , BlockSize ) ;
SHA1Result ( 0 , dst ) ;
for ( int j = 0 ; j < BlockSize ; j + + ) {
@ -107,7 +116,7 @@ std::size_t codec_get_encoded_len(std::size_t dwSrcBytes)
void codec_encode ( byte * pbSrcDst , std : : size_t size , std : : size_t size64 , const char * pszPassword )
{
byte buf [ 128 ] ;
byte buf [ BlockSize ] ;
byte tmp [ SHA1HashSize ] ;
byte dst [ SHA1HashSize ] ;
@ -115,9 +124,9 @@ void codec_encode(byte *pbSrcDst, std::size_t size, std::size_t size64, const ch
app_fatal ( " Invalid encode parameters " ) ;
CodecInitKey ( pszPassword ) ;
uint16 _t lastChunk = 0 ;
size _t lastChunk = 0 ;
while ( size ! = 0 ) {
uint16 _t chunk = size < BlockSize ? size : BlockSize ;
size _t chunk = size < BlockSize ? size : BlockSize ;
memcpy ( buf , pbSrcDst , chunk ) ;
if ( chunk < BlockSize )
memset ( buf + chunk , 0 , BlockSize - chunk ) ;
@ -138,7 +147,7 @@ void codec_encode(byte *pbSrcDst, std::size_t size, std::size_t size64, const ch
sig - > error = 0 ;
sig - > unused = 0 ;
sig - > checksum = * reinterpret_cast < uint32_t * > ( tmp ) ;
sig - > lastChunkSize = lastChunk ;
sig - > lastChunkSize = static_cast < uint8_t > ( lastChunk ) ; // lastChunk is at most 64 so will always fit in an 8 bit var
SHA1Clear ( ) ;
}