commit 27a27f1264bfcb4d814b4df7e70219852143bca0 parent d326d8d02ec116bf126e631dbf8dccd3427b18f4 Author: Michael Savage <mikejsavage@gmail.com> Date: Sun Apr 23 00:34:49 +0300 Check manifest signatures in the updater Diffstat:
launcher/main.cc | | | 119 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------- |
diff --git a/launcher/main.cc b/launcher/main.cc @@ -28,12 +28,21 @@ #include "libs/imgui/imgui.h" #include "libs/imgui/imgui_impl_glfw_gl3.h" +#include "libs/monocypher/monocypher.h" + #if PLATFORM_WINDOWS #define GAME_BINARY "medfall.exe" #else #define GAME_BINARY "medfall" #endif +const u8 PUBLIC_KEY[] = { + 0x3b, 0x0a, 0xc7, 0xa1, 0xd1, 0x9e, 0xbd, 0x0c, + 0x71, 0x75, 0x4f, 0xfc, 0x2a, 0xef, 0xcf, 0x91, + 0x93, 0xd0, 0x58, 0x94, 0x79, 0xc2, 0xeb, 0x16, + 0x30, 0x74, 0x62, 0x88, 0xe8, 0x18, 0x03, 0xb6, +}; + struct SHA256 { SHA256() { } @@ -282,6 +291,28 @@ static WORK_QUEUE_CALLBACK( download_remote_version ) { } } +static int hex2dec( char hex ) { + if( hex == '0' ) return 0; + if( hex == '1' ) return 1; + if( hex == '2' ) return 2; + if( hex == '3' ) return 3; + if( hex == '4' ) return 4; + if( hex == '5' ) return 5; + if( hex == '6' ) return 6; + if( hex == '7' ) return 7; + if( hex == '8' ) return 8; + if( hex == '9' ) return 9; + + if( hex == 'a' || hex == 'A' ) return 10; + if( hex == 'b' || hex == 'B' ) return 11; + if( hex == 'c' || hex == 'C' ) return 12; + if( hex == 'd' || hex == 'D' ) return 13; + if( hex == 'e' || hex == 'E' ) return 14; + if( hex == 'f' || hex == 'F' ) return 15; + + return -1; +} + static WORK_QUEUE_CALLBACK( download_manifest ) { struct sockaddr_storage address; Version version; @@ -291,41 +322,65 @@ static WORK_QUEUE_CALLBACK( download_manifest ) { version = updater->remote_version; } - char path[ 256 ]; - snprintf( path, sizeof( path ), "/%u.%u.%u.%u.txt", version.a, version.b, version.c, version.d ); - std::string manifest_str; - bool ok = http_get( address, HOST, path, &manifest_str ) == GET_OK; + str< 256 > path( "/{}.{}.{}.{}.txt", version.a, version.b, version.c, version.d ); + std::string body; + bool download_ok = http_get( address, HOST, path.c_str(), &body ) == GET_OK; + Updater * updater = SCOPED_ACQUIRE( locked_updater ); - if( ok ) { - ok = parse_manifest( &updater->remote_manifest, manifest_str.c_str() ); - } - if( ok ) { - // look for files that have changed and files that should be removed - for( const auto & kv : updater->local_manifest ) { - const auto & it = updater->remote_manifest.find( kv.first ); - if( it == updater->remote_manifest.end() ) { - updater->files_to_remove.push_back( kv.first ); - } - else if( it->second.checksum != kv.second.checksum ) { - updater->files_to_update.push_back( FileUpdate( kv.first ) ); - updater->update_size += it->second.file_size; - } - } + // we haven't failed yet, but it makes bail-out code simpler. advance + // state after everything succeeds + updater->state = UPDATER_MANIFEST_FAILED; - // look for new files - for( const auto & kv : updater->remote_manifest ) { - const auto & it = updater->local_manifest.find( kv.first ); - if( it == updater->local_manifest.end() ) { - updater->files_to_update.push_back( FileUpdate( kv.first ) ); - updater->update_size += kv.second.file_size; - } + if( !download_ok ) return; + if( body.size() < 129 ) return; + if( body[ 128 ] != '\n' ) return; + + const char * manifest_str; + + // check the manifest signature is ok + { + u8 signature[ 64 ]; + for( size_t i = 0; i < sizeof( signature ); i++ ) { + char a = hex2dec( body[ i * 2 ] ); + char b = hex2dec( body[ i * 2 + 1 ] ); + + if( a == -1 || b == -1 ) return; + + signature[ i ] = checked_cast< u8 >( a * 16 + b ); } - updater->state = UPDATER_NEED_UPDATE; + manifest_str = body.c_str() + 129; + size_t manifest_len = body.size() - 129; + + int signature_ok = crypto_check( signature, PUBLIC_KEY, ( u8 * ) manifest_str, manifest_len ); + if( signature_ok != 0 ) return; } - else { - updater->state = UPDATER_MANIFEST_FAILED; + + bool parse_ok = parse_manifest( &updater->remote_manifest, manifest_str ); + if( !parse_ok ) return; + + // look for files that have changed and files that should be removed + for( const auto & kv : updater->local_manifest ) { + const auto & it = updater->remote_manifest.find( kv.first ); + if( it == updater->remote_manifest.end() ) { + updater->files_to_remove.push_back( kv.first ); + } + else if( it->second.checksum != kv.second.checksum ) { + updater->files_to_update.push_back( FileUpdate( kv.first ) ); + updater->update_size += it->second.file_size; + } } + + // look for new files + for( const auto & kv : updater->remote_manifest ) { + const auto & it = updater->local_manifest.find( kv.first ); + if( it == updater->local_manifest.end() ) { + updater->files_to_update.push_back( FileUpdate( kv.first ) ); + updater->update_size += kv.second.file_size; + } + } + + updater->state = UPDATER_NEED_UPDATE; } static HTTP_PROGRESS_CALLBACK( download_progress ) { @@ -466,7 +521,7 @@ int main() { ImGui_ImplGlfwGL3_Init( window, true ); - ImFont * small; + // ImFont * small; ImFont * large; ImFont * verylarge; @@ -476,7 +531,7 @@ int main() { ImGuiIO & io = ImGui::GetIO(); io.IniFilename = NULL; io.Fonts->AddFontFromMemoryCompressedTTF( data, size, 16.0f ); - small = io.Fonts->AddFontFromMemoryCompressedTTF( data, size, 14.0f ); + // small = io.Fonts->AddFontFromMemoryCompressedTTF( data, size, 14.0f ); large = io.Fonts->AddFontFromMemoryCompressedTTF( data, size, 32.0f ); verylarge = io.Fonts->AddFontFromMemoryCompressedTTF( data, size, 128.0f ); } @@ -566,7 +621,7 @@ int main() { ImGui::PushStyleColor( ImGuiCol_ButtonHovered, ImColor( 192, 192, 64 ) ); ImGui::PushStyleColor( ImGuiCol_ButtonActive, ImColor( 225, 225, 96 ) ); - str< 256 > button_text( "Update to v%u.%u.%u.%u - %.2fMb", + str< 256 > button_text( "Update to v{}.{}.{}.{} - {.2}Mb", updater->remote_version.a, updater->remote_version.b, updater->remote_version.c,