commit 2124510c91023c4b1afdf85cab96d3b5bcc10385 parent 44018f5c90ce4e0f815d77a98c76ac4245287b6b Author: Michael Savage <mikejsavage@gmail.com> Date: Fri Jun 2 02:03:33 +0300 Use patterns to extract manifest signature. Also parse SHA256s instead of storing strings Diffstat:
launcher/main.cc | | | 155 | +++++++++++++++++++++++++++++++++++++++++-------------------------------------- |
diff --git a/launcher/main.cc b/launcher/main.cc @@ -39,6 +39,8 @@ #define GAME_BINARY "medfall" #endif +#define SIGNATURE_PATTERN "%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x" + const u8 PUBLIC_KEY[] = { 0x3b, 0x0a, 0xc7, 0xa1, 0xd1, 0x9e, 0xbd, 0x0c, 0x71, 0x75, 0x4f, 0xfc, 0x2a, 0xef, 0xcf, 0x91, @@ -53,31 +55,80 @@ struct SHA256 { SHA2_CTX ctx; SHA256Init( &ctx ); SHA256Update( &ctx, ( const u8 * ) data, len ); - - u8 digest[ SHA256_DIGEST_LENGTH ]; - SHA256Final( digest, &ctx ); - - static const char hex[] = "0123456789abcdef"; - for( size_t i = 0; i < sizeof( digest ); i++ ) { - e[ i * 2 + 0 ] = hex[ digest[ i ] >> 4 ]; - e[ i * 2 + 1 ] = hex[ digest[ i ] & 0x0f ]; - } - e[ sizeof( e ) - 1 ] = '\0'; + SHA256Final( digest.ptr(), &ctx ); } explicit SHA256( const std::string & str ) : SHA256( str.c_str(), str.size() ) { } - char e[ SHA256_DIGEST_STRING_LENGTH ]; + StaticArray< u8, SHA256_DIGEST_LENGTH > digest; }; static bool operator==( const SHA256 & a, const SHA256 & b ) { - return memcmp( a.e, b.e, sizeof( a.e ) ) == 0; + return memcmp( a.digest.ptr(), b.digest.ptr(), sizeof( a.digest ) ) == 0; } static bool operator!=( const SHA256 & a, const SHA256 & b ) { return !( a == b ); } +static void format( FormatBuffer * fb, const SHA256 & sha, const FormatOpts & opts ) { + str< SHA256_DIGEST_STRING_LENGTH > s; + static const char hex[] = "0123456789abcdef"; + for( size_t i = 0; i < sizeof( sha.digest ); i++ ) { + s += hex[ sha.digest[ i ] >> 4 ]; + s += hex[ sha.digest[ i ] & 0x0f ]; + } + format( fb, s, opts ); +} + +static u8 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 255; +} + +static bool parse_hex( const array< const char > hex, u8 * bytes ) { + if( hex.n % 2 != 0 ) { + return false; + } + + for( size_t i = 0; i < hex.n / 2; i++ ) { + u8 a = hex2dec( hex[ i * 2 ] ); + u8 b = hex2dec( hex[ i * 2 + 1 ] ); + + if( a == 255 || b == 255 ) { + return false; + } + + bytes[ i ] = a * 16 + b; + } + + return true; +} + +static bool parse_sha256( SHA256 * sha256, const array< const char > hex ) { + if( hex.n != SHA256_DIGEST_STRING_LENGTH - 1 ) { + return false; + } + return parse_hex( hex, sha256->digest.ptr() ); +} + struct ManifestEntry { SHA256 checksum; u64 file_size; @@ -117,16 +168,16 @@ static bool parse_manifest( std::map< std::string, ManifestEntry > & manifest, c const str< 256 > file_name( "{}", matches[ 0 ] ); const str< 16 > file_size( "{}", matches[ 2 ] ); u64 size = u64( strtonum( file_size.c_str(), 1, S64_MAX, NULL ) ); + ManifestEntry entry; + entry.file_size = size; - if( matches[ 0 ].n > file_name.len() || matches[ 2 ].n > file_size.len() || size == 0 ) { + bool ok_sha = parse_sha256( &entry.checksum, matches[ 1 ] ); + + if( matches[ 0 ].n > file_name.len() || matches[ 2 ].n > file_size.len() || size == 0 || !ok_sha ) { manifest.clear(); return false; } - ManifestEntry entry; - strlcpy( entry.checksum.e, matches[ 1 ].ptr(), sizeof( entry.checksum.e ) ); - entry.file_size = size; - manifest[ file_name.c_str() ] = entry; } @@ -290,47 +341,6 @@ static WORK_QUEUE_CALLBACK( download_remote_version ) { glfwPostEmptyEvent(); } -static u8 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 255; -} - -bool parse_hex( const array< const char > hex, u8 * bytes ) { - if( hex.n % 2 != 0 ) { - return false; - } - - for( size_t i = 0; i < hex.n / 2; i++ ) { - u8 a = hex2dec( hex[ i * 2 ] ); - u8 b = hex2dec( hex[ i * 2 + 1 ] ); - - if( a == 255 || b == 255 ) { - return false; - } - - bytes[ i ] = a * 16 + b; - } - - return true; -} - static WORK_QUEUE_CALLBACK( download_manifest ) { struct sockaddr_storage address; Version version; @@ -350,26 +360,21 @@ static WORK_QUEUE_CALLBACK( download_manifest ) { updater->state = UPDATER_MANIFEST_FAILED; 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 ]; - if( !parse_hex( array< const char >( body.c_str(), 128 ), signature ) ) { - return; - } + Matches matches; + bool ok = match( &matches, body.c_str(), "(" SIGNATURE_PATTERN ")\n(.*)" ); + if( !ok ) return; - manifest_str = body.c_str() + 129; - size_t manifest_len = body.size() - 129; + u8 signature[ 64 ]; + bool ok_hex = parse_hex( matches[ 0 ], signature ); + ASSERT( ok_hex ); - int signature_ok = crypto_check( signature, PUBLIC_KEY, ( const u8 * ) manifest_str, manifest_len ); - if( signature_ok != 0 ) return; - } + int signature_ok = crypto_check( signature, PUBLIC_KEY, ( const u8 * ) matches[ 1 ].ptr(), matches[ 1 ].n ); + if( signature_ok != 0 ) return; - bool parse_ok = parse_manifest( updater->remote_manifest, manifest_str ); + // parse manifest + bool parse_ok = parse_manifest( updater->remote_manifest, matches[ 1 ].ptr() ); if( !parse_ok ) return; // look for files that have changed and files that should be removed @@ -424,7 +429,7 @@ static WORK_QUEUE_CALLBACK( download_file ) { } std::string body; - str< 256 > path( "/{}", entry.checksum.e ); + str< 128 > path( "/{}", entry.checksum ); GetResult ok = http_get( address, HOST, path.c_str(), &body, download_progress, &file->bytes_downloaded ); // TODO: real error handling ASSERT( ok == GET_OK ); @@ -473,7 +478,7 @@ static WORK_QUEUE_CALLBACK( install_update ) { FILE * manifest_txt = fopen( "manifest.txt", "w" ); for( const auto & kv : manifest ) { - ggprint_to_file( manifest_txt, "{} {} {}\n", kv.first.c_str(), kv.second.checksum.e, kv.second.file_size ); + ggprint_to_file( manifest_txt, "{} {} {}\n", kv.first.c_str(), kv.second.checksum, kv.second.file_size ); } fclose( manifest_txt );