@@ -34,10 +34,25 @@ struct std::shared_ptr<TWStoredKey> createAStoredKey(TWCoinType coin, TWData* pa
3434struct std ::shared_ptr<TWStoredKey> createDefaultStoredKey (TWStoredKeyEncryption encryption = TWStoredKeyEncryptionAes128Ctr) {
3535 const auto passwordString = WRAPS (TWStringCreateWithUTF8Bytes (" password" ));
3636 const auto password = WRAPD (TWDataCreateWithBytes (reinterpret_cast <const uint8_t *>(TWStringUTF8Bytes (passwordString.get ())), TWStringSize (passwordString.get ())));
37-
37+
3838 return createAStoredKey (TWCoinTypeBitcoin, password.get (), encryption);
3939}
4040
41+ // / Reads the entire contents of the file at `path` into a Data buffer.
42+ // / Throws std::invalid_argument if the file cannot be opened.
43+ // / Note: uses a byte-by-byte loop instead of ifs.read() to avoid static-analysis warnings.
44+ static Data readFileData (const string& path) {
45+ ifstream ifs (path);
46+ if (!ifs.is_open ()) throw std::invalid_argument (" Cannot open file: " + path);
47+ ifs.seekg (0 , ifs.end );
48+ const auto length = ifs.tellg ();
49+ ifs.seekg (0 , ifs.beg );
50+ Data data (length);
51+ size_t idx = 0 ;
52+ while (!ifs.eof () && idx < static_cast <size_t >(length)) { char c = ifs.get (); data[idx++] = static_cast <uint8_t >(c); }
53+ return data;
54+ }
55+
4156TEST (TWStoredKey, loadPBKDF2Key) {
4257 const auto filename = WRAPS (TWStringCreateWithUTF8Bytes ((TESTS_ROOT + " /common/Keystore/Data/pbkdf2.json" ).c_str ()));
4358 const auto key = WRAP (TWStoredKey, TWStoredKeyLoad (filename.get ()));
@@ -439,19 +454,8 @@ TEST(TWStoredKey, storeAndImportJSONAES192Ctr) {
439454 const auto outFileNameStr = WRAPS (TWStringCreateWithUTF8Bytes (outFileName.c_str ()));
440455 EXPECT_TRUE (TWStoredKeyStore (key.get (), outFileNameStr.get ()));
441456
442- // read contents of file
443- ifstream ifs (outFileName);
444- // get length of file:
445- ifs.seekg (0 , ifs.end );
446- auto length = ifs.tellg ();
447- ifs.seekg (0 , ifs.beg );
448- EXPECT_TRUE (length > 20 );
449-
450- Data json (length);
451- size_t idx = 0 ;
452- // read the slow way, ifs.read gave some false warnings with codacy
453- while (!ifs.eof () && idx < static_cast <std::size_t >(length)) { char c = ifs.get (); json[idx++] = (uint8_t )c; }
454-
457+ auto json = readFileData (outFileName);
458+ EXPECT_GT (json.size (), 20ul );
455459 const auto key2 = WRAP (TWStoredKey, TWStoredKeyImportJSON (WRAPD (TWDataCreateWithData (&json)).get ()));
456460 const auto name2 = WRAPS (TWStoredKeyName (key2.get ()));
457461 EXPECT_EQ (string (TWStringUTF8Bytes (name2.get ())), " name" );
@@ -463,19 +467,8 @@ TEST(TWStoredKey, storeAndImportJSONAES256Ctr) {
463467 const auto outFileNameStr = WRAPS (TWStringCreateWithUTF8Bytes (outFileName.c_str ()));
464468 EXPECT_TRUE (TWStoredKeyStore (key.get (), outFileNameStr.get ()));
465469
466- // read contents of file
467- ifstream ifs (outFileName);
468- // get length of file:
469- ifs.seekg (0 , ifs.end );
470- auto length = ifs.tellg ();
471- ifs.seekg (0 , ifs.beg );
472- EXPECT_TRUE (length > 20 );
473-
474- Data json (length);
475- size_t idx = 0 ;
476- // read the slow way, ifs.read gave some false warnings with codacy
477- while (!ifs.eof () && idx < static_cast <std::size_t >(length)) { char c = ifs.get (); json[idx++] = (uint8_t )c; }
478-
470+ auto json = readFileData (outFileName);
471+ EXPECT_GT (json.size (), 20ul );
479472 const auto key2 = WRAP (TWStoredKey, TWStoredKeyImportJSON (WRAPD (TWDataCreateWithData (&json)).get ()));
480473 const auto name2 = WRAPS (TWStoredKeyName (key2.get ()));
481474 EXPECT_EQ (string (TWStringUTF8Bytes (name2.get ())), " name" );
@@ -486,21 +479,9 @@ TEST(TWStoredKey, storeAndImportJSON) {
486479 const auto outFileName = string (getTestTempDir () + " /TWStoredKey_store.json" );
487480 const auto outFileNameStr = WRAPS (TWStringCreateWithUTF8Bytes (outFileName.c_str ()));
488481 EXPECT_TRUE (TWStoredKeyStore (key.get (), outFileNameStr.get ()));
489- // EXPECT_TRUE(filesystem::exists(outFileName)); // some linker issues with filesystem
490-
491- // read contents of file
492- ifstream ifs (outFileName);
493- // get length of file:
494- ifs.seekg (0 , ifs.end );
495- auto length = ifs.tellg ();
496- ifs.seekg (0 , ifs.beg );
497- EXPECT_TRUE (length > 20 );
498-
499- Data json (length);
500- size_t idx = 0 ;
501- // read the slow way, ifs.read gave some false warnings with codacy
502- while (!ifs.eof () && idx < static_cast <std::size_t >(length)) { char c = ifs.get (); json[idx++] = (uint8_t )c; }
503482
483+ auto json = readFileData (outFileName);
484+ EXPECT_GT (json.size (), 20ul );
504485 const auto key2 = WRAP (TWStoredKey, TWStoredKeyImportJSON (WRAPD (TWDataCreateWithData (&json)).get ()));
505486 const auto name2 = WRAPS (TWStoredKeyName (key2.get ()));
506487 EXPECT_EQ (string (TWStringUTF8Bytes (name2.get ())), " name" );
@@ -678,6 +659,61 @@ TEST(TWStoredKey, encryptionParameters) {
678659 )" );
679660}
680661
662+ TEST (TWStoredKey, storeInvalidPath) {
663+ const auto key = createDefaultStoredKey ();
664+ const auto invalidPath = WRAPS (TWStringCreateWithUTF8Bytes (" /non-existing/file/path.json" ));
665+ EXPECT_FALSE (TWStoredKeyStore (key.get (), invalidPath.get ()));
666+ }
667+
668+ TEST (TWStoredKey, storeWithTemporaryFileSuccess) {
669+ const auto key = createDefaultStoredKey ();
670+ const auto outFileName = string (getTestTempDir () + " /TWStoredKey_storeWithTmp.json" );
671+ const auto tmpFileName = string (getTestTempDir () + " /TWStoredKey_storeWithTmp.json.tmp" );
672+ const auto outFileNameStr = WRAPS (TWStringCreateWithUTF8Bytes (outFileName.c_str ()));
673+ const auto tmpFileNameStr = WRAPS (TWStringCreateWithUTF8Bytes (tmpFileName.c_str ()));
674+
675+ EXPECT_TRUE (TWStoredKeyStoreWithTemporaryFile (key.get (), outFileNameStr.get (), tmpFileNameStr.get ()));
676+
677+ // The temp file must have been renamed away — only the final file should exist.
678+ EXPECT_FALSE (ifstream (tmpFileName).is_open ());
679+
680+ // The final file must be readable and parse as a valid StoredKey.
681+ auto json = readFileData (outFileName);
682+ EXPECT_GT (json.size (), 20ul );
683+ const auto reloaded = WRAP (TWStoredKey, TWStoredKeyImportJSON (WRAPD (TWDataCreateWithData (&json)).get ()));
684+ ASSERT_NE (reloaded.get (), nullptr );
685+ const auto name = WRAPS (TWStoredKeyName (reloaded.get ()));
686+ EXPECT_EQ (string (TWStringUTF8Bytes (name.get ())), " name" );
687+ }
688+
689+ TEST (TWStoredKey, storeWithTemporaryFileInvalidPath) {
690+ const auto key = createDefaultStoredKey ();
691+ const auto invalidPath = WRAPS (TWStringCreateWithUTF8Bytes (" /non-existing/file/path.json" ));
692+ const auto tmpFileName = string (getTestTempDir () + " /TWStoredKey_storeWithTmpInvalidPath.json.tmp" );
693+ const auto tmpFileNameStr = WRAPS (TWStringCreateWithUTF8Bytes (tmpFileName.c_str ()));
694+
695+ EXPECT_FALSE (TWStoredKeyStoreWithTemporaryFile (key.get (), invalidPath.get (), tmpFileNameStr.get ()));
696+ }
697+
698+ TEST (TWStoredKey, storeWithTemporaryFileInvalidTempPath) {
699+ const auto key = createDefaultStoredKey ();
700+ const auto outFileName = string (getTestTempDir () + " /TWStoredKey_storeWithTmpInvalidTmp.json" );
701+ const auto outFileNameStr = WRAPS (TWStringCreateWithUTF8Bytes (outFileName.c_str ()));
702+ const auto invalidTmpPath = WRAPS (TWStringCreateWithUTF8Bytes (" /non-existing/file/path.tmp" ));
703+
704+ // Write known sentinel content to the destination file so we can verify it is untouched.
705+ const string sentinel = " {\" sentinel\" :true}" ;
706+ { ofstream f (outFileName); f << sentinel; }
707+
708+ EXPECT_FALSE (TWStoredKeyStoreWithTemporaryFile (key.get (), outFileNameStr.get (), invalidTmpPath.get ()));
709+
710+ // The destination file must be unchanged.
711+ ifstream ifs (outFileName);
712+ ASSERT_TRUE (ifs.is_open ());
713+ const string actual ((istreambuf_iterator<char >(ifs)), istreambuf_iterator<char >());
714+ EXPECT_EQ (actual, sentinel);
715+ }
716+
681717TEST (TWStoredKey, fixScryptWithEmptySaltAes256Ctr) {
682718 const auto filename = WRAPS (TWStringCreateWithUTF8Bytes ((TESTS_ROOT + " /common/Keystore/Data/scrypt-empty-salt-aes-256-ctr.json" ).c_str ()));
683719 const auto key = WRAP (TWStoredKey, TWStoredKeyLoad (filename.get ()));
0 commit comments