/// if we pack, compress, encode on the way in, that means, therefore, we /// need to decode, uncompress, then unpack on our way out. Right? /// /// This function will base64-DECODE the string contents, then uncompress them using /// zlib, and then unpack the result using whatever is the default packer (MsgPack, Protobuf, etc). /// /// I originally added compression because message sizes were too big. Now I'm adding packing, /// to solve any issues of binary compatibility across various platforms. // bool OTASCIIArmor::GetAndUnpackString(OTString & strData, bool bLineBreaks) const //bLineBreaks=true { size_t outSize = 0; uint8_t * pData = NULL; strData.Release(); if (GetLength() < 1) { return true; } // -------------------------------------------------------------- pData = OTCrypto::It()->Base64Decode(this->Get(), &outSize, bLineBreaks); // pData = OT_base64_decode(Get(), &outSize, (bLineBreaks ? 1 : 0)); if (pData) { std::string str_decoded( pData, pData+outSize ); delete [] pData; pData=NULL; std::string str_uncompressed = decompress_string( str_decoded ); // --------------------------------------- // PUT THE PACKED BUFFER HERE, AND UNPACK INTO strData // -------------------------------------------------------- OTDB::OTPacker * pPacker = OTASCIIArmor::GetPacker(); // No need to check for failure, since this already ASSERTS. No need to cleanup either. OTDB::PackedBuffer * pBuffer = pPacker->CreateBuffer(); // Need to clean this up. OT_ASSERT(NULL != pBuffer); OTCleanup<OTDB::PackedBuffer> theBufferAngel(*pBuffer); // This will make sure buffer is deleted later. pBuffer->SetData(reinterpret_cast<const uint8_t *>(str_uncompressed.data()), str_uncompressed.size()); // ----------------------------- OTDB::OTDBString * pOTDBString = dynamic_cast<OTDB::OTDBString *>(OTDB::CreateObject(OTDB::STORED_OBJ_STRING)); OT_ASSERT(NULL != pOTDBString); OTCleanup<OTDB::OTDBString> theStringAngel(*pOTDBString); // clean up this string. bool bUnpacked = pPacker->Unpack(*pBuffer, *pOTDBString); // ---------------------- if (false == bUnpacked) { OTLog::Error("Failed unpacking string in OTASCIIArmor::GetAndUnpackString.\n"); return false; } // -------------------------------------------------------- // This enforces the null termination. (using the 2nd parameter as nEnforcedMaxLength) strData.Set(pOTDBString->m_string.c_str(), static_cast<uint32_t> (pOTDBString->m_string.length())); return true; } else { OTLog::Error("OTASCIIArmor::GetAndUnpackString: NULL pData while base64-decoding pData.\n"); return false; } }
bool OTPayment::GetMemo(OTString & strOutput) const { strOutput.Release(); // ---------------------- if (!m_bAreTempValuesSet) return false; bool bSuccess = false; switch (m_Type) { case OTPayment::CHEQUE: case OTPayment::VOUCHER: case OTPayment::INVOICE: case OTPayment::PAYMENT_PLAN: if (m_strMemo.Exists()) { strOutput = m_strMemo; bSuccess = true; } else bSuccess = false; break; case OTPayment::SMART_CONTRACT: case OTPayment::PURSE: bSuccess = false; break; default: OTLog::Error("OTPayment::GetAmount: Bad payment type!\n"); break; } return bSuccess; }
/// if we pack, compress, encode on the way in, that means, therefore, we /// need to decode, uncompress, then unpack on our way out. Right? /// /// This function will base64-DECODE the string contents, then uncompress them using /// zlib, and then unpack the result using whatever is the default packer (MsgPack, Protobuf, etc). /// /// I originally added compression because message sizes were too big. Now I'm adding packing, /// to solve any issues of binary compatibility across various platforms. // bool OTASCIIArmor::GetAndUnpackString(OTString & strData, bool bLineBreaks) const //bLineBreaks=true { size_t outSize = 0; uint8_t * pData = NULL; strData.Release(); if (GetLength() < 1) { return true; } // -------------------------------------------------------------- pData = OTCrypto::It()->Base64Decode(this->Get(), &outSize, bLineBreaks); // pData = OT_base64_decode(Get(), &outSize, (bLineBreaks ? 1 : 0)); if (pData) { // ------------------------------------------- // EASY ZLIB // long nDestLen = DEFAULT_BUFFER_SIZE_EASYZLIB; // todo stop hardcoding numbers (but this one is OK I think.) unsigned char* pDest = new unsigned char [nDestLen+10]; // For safety. OT_ASSERT(NULL != pDest); int nErr = ezuncompress( pDest, &nDestLen, pData, static_cast<long> (outSize) ); if ( nErr == EZ_BUF_ERROR ) { delete [] pDest; pDest = new unsigned char [nDestLen]; // enough room now OT_ASSERT(NULL != pDest); nErr = ezuncompress( pDest, &nDestLen, pData, static_cast<long> (outSize) ); } // Now we're done with this memory, let's free it. delete [] pData; pData=NULL; // ---------------------------------------- if ( nErr == EZ_BUF_ERROR ) { delete [] pDest; pDest = NULL; OT_FAIL_MSG("Buffer error in OTASCIIArmor::GetAndUnpackString\n"); } else if ( nErr == EZ_STREAM_ERROR ) { delete [] pDest; pDest = NULL; OT_FAIL_MSG("pDest is NULL in OTASCIIArmor::GetAndUnpackString\n"); } else if ( nErr == EZ_DATA_ERROR ) { delete [] pDest; pDest = NULL; OTLog::vError("corrupted pSrc passed to ezuncompress OTASCIIArmor::GetAndUnpackString, size: %d\n", outSize); OT_FAIL; } else if ( nErr == EZ_MEM_ERROR ) { delete [] pDest; pDest = NULL; OT_FAIL_MSG("Out of memory in OTASCIIArmor::GetAndUnpackString\n"); } // --------------------------------------- // PUT THE PACKED BUFFER HERE, AND UNPACK INTO strData // -------------------------------------------------------- OTDB::OTPacker * pPacker = OTASCIIArmor::GetPacker(); // No need to check for failure, since this already ASSERTS. No need to cleanup either. OTDB::PackedBuffer * pBuffer = pPacker->CreateBuffer(); // Need to clean this up. OT_ASSERT(NULL != pBuffer); OTCleanup<OTDB::PackedBuffer> theBufferAngel(*pBuffer); // This will make sure buffer is deleted later. const size_t theDestLen = nDestLen; pBuffer->SetData(pDest, // const unsigned char * theDestLen); delete [] pDest; pDest=NULL; // ----------------------------- OTDB::OTDBString * pOTDBString = dynamic_cast<OTDB::OTDBString *>(OTDB::CreateObject(OTDB::STORED_OBJ_STRING)); OT_ASSERT(NULL != pOTDBString); OTCleanup<OTDB::OTDBString> theStringAngel(*pOTDBString); // clean up this string. bool bUnpacked = pPacker->Unpack(*pBuffer, *pOTDBString); // ---------------------- if (false == bUnpacked) { OTLog::Error("Failed unpacking string in OTASCIIArmor::GetAndUnpackString.\n"); return false; } // -------------------------------------------------------- // This enforces the null termination. (using the 2nd parameter as nEnforcedMaxLength) strData.Set(pOTDBString->m_string.c_str(), static_cast<uint32_t> (pOTDBString->m_string.length())); return true; } else { OTLog::Error("OTASCIIArmor::GetAndUnpackString: NULL pData while base64-decoding pData.\n"); return false; } }
bool OTASCIIArmor::GetString(OTString & theData, bool bLineBreaks) const //bLineBreaks=true { return GetAndUnpackString(theData, bLineBreaks); size_t outSize = 0; uint8_t * pData = NULL; theData.Release(); if (GetLength() < 1) { return true; } pData = OT_base64_decode(Get(), &outSize, (bLineBreaks ? 1 : 0)); if (pData) { long nDestLen = DEFAULT_BUFFER_SIZE_EASYZLIB; // todo stop hardcoding numbers (but this one is OK I think.) unsigned char* pDest = new unsigned char [nDestLen+10]; // For safety. OT_ASSERT(NULL != pDest); int nErr = ezuncompress( pDest, &nDestLen, pData, outSize ); if ( nErr == EZ_BUF_ERROR ) { delete [] pDest; pDest = new unsigned char [nDestLen]; // enough room now OT_ASSERT(NULL != pDest); nErr = ezuncompress( pDest, &nDestLen, pData, outSize ); } // Now we're done with this memory, let's free it. delete [] pData; pData=NULL; // ---------------------------------------- if ( nErr == EZ_BUF_ERROR ) { delete [] pDest; pDest = NULL; OT_ASSERT_MSG(false, "Buffer error in OTASCIIArmor::GetString\n"); return false; // not really necessary but just making sure. } else if ( nErr == EZ_STREAM_ERROR ) { delete [] pDest; pDest = NULL; OT_ASSERT_MSG(false, "pDest is NULL in OTASCIIArmor::GetString\n"); return false; // not really necessary but just making sure. } else if ( nErr == EZ_DATA_ERROR ) { delete [] pDest; pDest = NULL; OTLog::vError("corrupted pSrc passed to ezuncompress OTASCIIArmor::GetString, size: %d\n", outSize); OT_ASSERT(false); return false; // not really necessary but just making sure. } else if ( nErr == EZ_MEM_ERROR ) { delete [] pDest; pDest = NULL; OT_ASSERT_MSG(false, "Out of memory in OTASCIIArmor::GetString\n"); return false; // not really necessary but just making sure. } // This enforces the null termination. (using the extra parameter nDestLen as nEnforcedMaxLength) theData.Set((const char*)pDest, nDestLen); delete [] pDest; pDest=NULL; return true; } else { OTLog::Error("NULL pData while base64_decodeing pData.\n"); return false; } }
int main (int argc, char **argv) { OTLog::vOutput(0, "\n\nWelcome to Open Transactions... Test Client -- version %s\n" "(transport build: OTMessage -> TCP -> SSL)\n" "IF YOU PREFER TO USE ZMQ (message based), then rebuild from main folder like this:\n" "cd ..; make clean; make\n\n", OTLog::Version()); OT_API::InitOTAPI(); // ----------------------------------------------------------------------- // The beginnings of an INI file!! OTString strPath; { CSimpleIniA ini; // We're assuming this file is on the path. SI_Error rc = ini.LoadFile("./.ot_ini"); // todo: stop hardcoding. if (rc >=0) { const char * pVal = ini.GetValue("paths", "client_path", SERVER_PATH_DEFAULT); // todo stop hardcoding. if (NULL != pVal) strPath.Set(pVal); else strPath.Set(SERVER_PATH_DEFAULT); } else { strPath.Set(SERVER_PATH_DEFAULT); } } // ----------------------------------------------------------------------- OTString strCAFile, strKeyFile, strSSLPassword; if (argc < 2) { OTLog::vOutput(0, "\n==> USAGE: %s <SSL-password> <absolute_path_to_data_folder>\n\n" #if defined (FELLOW_TRAVELER) "(Password defaults to '%s' if left blank.)\n" "(Folder defaults to '%s' if left blank.)\n" #else "(The test password is always 'test'.\n'cd data_folder' then 'pwd' to see the absolute path.)\n" #endif "\n\n", argv[0] #if defined (FELLOW_TRAVELER) , KEY_PASSWORD, strPath.Get() #endif ); #if defined (FELLOW_TRAVELER) strSSLPassword.Set(KEY_PASSWORD); OTString strClientPath(strPath.Get()); g_OT_API.Init(strClientPath); // SSL gets initialized in here, before any keys are loaded. #else exit(1); #endif } else if (argc < 3) { OTLog::vOutput(0, "\n==> USAGE: %s <SSL-password> <absolute_path_to_data_folder>\n\n" #if defined (FELLOW_TRAVELER) "(Folder defaults to '%s' if left blank.)\n" #endif "\n\n", argv[0] #if defined (FELLOW_TRAVELER) , strPath.Get() #endif ); #if defined (FELLOW_TRAVELER) strSSLPassword.Set(argv[1]); OTString strClientPath(strPath.Get()); g_OT_API.Init(strClientPath); // SSL gets initialized in here, before any keys are loaded. #else exit(1); #endif } else { strSSLPassword.Set(argv[1]); OTString strClientPath(argv[2]); g_OT_API.Init(strClientPath); // SSL gets initialized in here, before any keys are loaded. } OTLog::vOutput::(0, "Using as path to data folder: %s\n", OTLog::Path()); strCAFile. Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), CA_FILE); strKeyFile.Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), KEY_FILE); // ------------------------------------------------------------------------------ // // Basically, loop: // // 1) Present a prompt, and get a user string of input. Wait for that. // // 2) Process it out as an OTMessage to the server. It goes down the pipe. // // 3) Sleep for 1 second. // // 4) Awake and check for messages to be read in response from the server. // Loop. As long as there are any responses there, then process and handle // them all. // Then continue back up to the prompt at step (1). char buf[200] = ""; int retVal = 0; int nExpectResponse = 0; OTLog::Output(0, "You may wish to 'load' then 'connect' then 'stat'.\n"); OTLog::vOutput(4, "Starting client loop. u_header size in C code is %d.\n", OT_CMD_HEADER_SIZE); for(;;) { buf[0] = 0; // Making it fresh again. nExpectResponse = 0; // 1) Present a prompt, and get a user string of input. Wait for that. OTLog::Output(0, "\nWallet> "); if (NULL == fgets(buf, sizeof(buf)-10, stdin)) // Leaving myself 10 extra bytes at the end for safety's sake. break; OTLog::Output(0, ".\n..\n...\n....\n.....\n......\n.......\n........\n.........\n..........\n" "...........\n............\n.............\n"); // so we can process the user input std::string strLine = buf; // 1.5 The one command that doesn't involve a message to the server (so far) // is the command to load the wallet from disk (which we do before we can // do anything else.) That and maybe the message to CONNECT to the server. // Load wallet.xml if (strLine.compare(0,4,"load") == 0) { OTLog::Output(0, "User has instructed to load wallet.xml...\n"); g_OT_API.GetWallet()->LoadWallet("wallet.xml"); // g_OT_API.GetWallet()->SaveWallet("NEWwallet.xml"); // todo remove this test code. continue; } else if (strLine.compare(0,5,"clear") == 0) { if (NULL == g_pTemporaryNym) { OTLog::Output(0, "No Nym yet available. Try 'load'.\n"); continue; } g_pTemporaryNym->RemoveAllNumbers(); g_pTemporaryNym->SaveSignedNymfile(*g_pTemporaryNym); OTLog::Output(0, "Successfully removed all issued and transaction numbers. Saving nym...\n"); continue; } else if (strLine.compare(0,7,"payment") == 0) { if (NULL == g_pTemporaryNym) { OTLog::Output(0, "No Nym yet available to sign the payment plan with. Try 'load'.\n"); continue; } OTLog::Output(0, "Enter your Asset Account ID that the payments will come from: "); OTString strTemp; strTemp.OTfgets(std::cin); const OTIdentifier ACCOUNT_ID(strTemp), USER_ID(*g_pTemporaryNym); OTAccount * pAccount = g_OT_API.GetWallet()->GetAccount(ACCOUNT_ID); if (NULL == pAccount) { OTLog::Output(0, "That account isn't loaded right now. Try 'load'.\n"); continue; } // To write a payment plan, like a cheque, we need to burn one of our transaction numbers. (Presumably // the wallet is also storing a couple of these, since they are needed to perform any transaction.) // // I don't have to contact the server to write a payment plan -- as long as I already have a transaction // number I can use to write it. Otherwise I'd have to ask the server to send me one first. OTString strServerID(pAccount->GetRealServerID()); long lTransactionNumber=0; if (false == g_pTemporaryNym->GetNextTransactionNum(*g_pTemporaryNym, strServerID, lTransactionNumber)) { OTLog::Output(0, "Payment Plans are written offline, but you still need a transaction number\n" "(and you have none, currently.) Try using 'n' to request another transaction number.\n"); continue; } // ----------------------------------------------------------------------- OTString str_RECIPIENT_USER_ID, str_RECIPIENT_ACCT_ID, strConsideration; // Get the Recipient Nym ID OTLog::Output(0, "Enter the Recipient's User ID (NymID): "); str_RECIPIENT_USER_ID.OTfgets(std::cin); // THEN GET AN ACCOUNT ID in that same asset type OTLog::Output(0, "Enter the Recipient's ACCOUNT ID (of the same asset type as your account): "); str_RECIPIENT_ACCT_ID.OTfgets(std::cin); OTLog::Output(0, "Enter a memo describing consideration for the payment plan: "); strConsideration.OTfgets(std::cin); const OTIdentifier RECIPIENT_USER_ID(str_RECIPIENT_USER_ID), RECIPIENT_ACCT_ID(str_RECIPIENT_ACCT_ID); OTPaymentPlan thePlan(pAccount->GetRealServerID(), pAccount->GetAssetTypeID(), pAccount->GetRealAccountID(), pAccount->GetUserID(), RECIPIENT_ACCT_ID, RECIPIENT_USER_ID); // ----------------------------------------------------------------------- // Valid date range (in seconds) OTLog::Output(0, " 6 minutes == 360 Seconds\n" "10 minutes == 600 Seconds\n" "1 hour == 3600 Seconds\n" "1 day == 86400 Seconds\n" "30 days == 2592000 Seconds\n" "3 months == 7776000 Seconds\n" "6 months == 15552000 Seconds\n\n" ); long lExpirationInSeconds = 86400; OTLog::vOutput(0, "How many seconds before payment plan expires? (defaults to 1 day: %ld): ", lExpirationInSeconds); strTemp.Release(); strTemp.OTfgets(std::cin); if (strTemp.GetLength() > 1) lExpirationInSeconds = atol(strTemp.Get()); // ----------------------------------------------------------------------- time_t VALID_FROM = time(NULL); // This time is set to TODAY NOW OTLog::vOutput(0, "Payment plan becomes valid for processing STARTING date\n" "(defaults to now, in seconds) [%ld]: ", VALID_FROM); strTemp.Release(); strTemp.OTfgets(std::cin); if (strTemp.GetLength() > 2) VALID_FROM = atol(strTemp.Get()); const time_t VALID_TO = VALID_FROM + lExpirationInSeconds; // now + 86400 // ----------------------------------------------------------------------- bool bSuccessSetAgreement = thePlan.SetAgreement(lTransactionNumber, strConsideration, VALID_FROM, VALID_TO); if (!bSuccessSetAgreement) { OTLog::Output(0, "Failed trying to set the agreement!\n"); // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE NUMBERS. g_pTemporaryNym->AddTransactionNum(*g_pTemporaryNym, strServerID, lTransactionNumber, true); // bSave=true continue; } bool bSuccessSetInitialPayment = true; // the default, in case user chooses not to even have this payment. bool bSuccessSetPaymentPlan = true; // the default, in case user chooses not to have a payment plan // ----------------------------------------------------------------------- OTLog::Output(0, "What is the Initial Payment Amount, if any? [0]: "); strTemp.Release(); strTemp.OTfgets(std::cin); long lInitialPayment = atol(strTemp.Get()); if (lInitialPayment > 0) { time_t PAYMENT_DELAY = 60; // 60 seconds. OTLog::vOutput(0, "From the Start Date forward, how long until the Initial Payment should charge?\n" "(defaults to one minute, in seconds) [%d]: ", PAYMENT_DELAY); strTemp.Release(); strTemp.OTfgets(std::cin); if ((strTemp.GetLength() > 1) && atol(strTemp.Get())>0) PAYMENT_DELAY = atol(strTemp.Get()); // ----------------------------------------------------------------------- bSuccessSetInitialPayment = thePlan.SetInitialPayment(lInitialPayment, PAYMENT_DELAY); } if (!bSuccessSetInitialPayment) { OTLog::Output(0, "Failed trying to set the initial payment!\n"); // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE NUMBERS. g_pTemporaryNym->AddTransactionNum(*g_pTemporaryNym, strServerID, lTransactionNumber, true); // bSave=true continue; } // ----------------------------------------------------------------------- OTLog::Output(0, "What is the regular payment amount, if any? [0]: "); strTemp.Release(); strTemp.OTfgets(std::cin); long lRegularPayment = atol(strTemp.Get()); if (lRegularPayment > 0) // If there are regular payments. { // ----------------------------------------------------------------------- time_t PAYMENT_DELAY = 120; // 120 seconds. OTLog::vOutput(0, "From the Start Date forward, how long until the Regular Payments start?\n" "(defaults to two minutes, in seconds) [%d]: ", PAYMENT_DELAY); strTemp.Release(); strTemp.OTfgets(std::cin); if ((strTemp.GetLength() > 1) && atol(strTemp.Get())>0) PAYMENT_DELAY = atol(strTemp.Get()); // ----------------------------------------------------------------------- time_t PAYMENT_PERIOD = 30; // 30 seconds. OTLog::vOutput(0, "Once payments begin, how much time should elapse between each payment?\n" "(defaults to thirty seconds) [%d]: ", PAYMENT_PERIOD); strTemp.Release(); strTemp.OTfgets(std::cin); if ((strTemp.GetLength() > 1) && atol(strTemp.Get())>0) PAYMENT_PERIOD = atol(strTemp.Get()); // ----------------------------------------------------------------------- time_t PLAN_LENGTH = 0; // 0 seconds (for no max length). OTLog::vOutput(0, "From start date, do you want the plan to expire after a certain maximum time?\n" "(defaults to 0 for no) [%d]: ", PLAN_LENGTH); strTemp.Release(); strTemp.OTfgets(std::cin); if (strTemp.GetLength() > 1) PLAN_LENGTH = atol(strTemp.Get()); // ----------------------------------------------------------------------- OTLog::Output(0, "Should there be some maximum number of payments? (Zero for no maximum.) [0]: "); strTemp.Release(); strTemp.OTfgets(std::cin); int nMaxPayments = atoi(strTemp.Get()); bSuccessSetPaymentPlan = thePlan.SetPaymentPlan(lRegularPayment, PAYMENT_DELAY, PAYMENT_PERIOD, PLAN_LENGTH, nMaxPayments); } if (!bSuccessSetPaymentPlan) { OTLog::Output(0, "Failed trying to set the payment plan!\n"); // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE NUMBERS. g_pTemporaryNym->AddTransactionNum(*g_pTemporaryNym, strServerID, lTransactionNumber, true); // bSave=true continue; } thePlan.SignContract(*g_pTemporaryNym); thePlan.SaveContract(); OTString strPlan(thePlan); OTLog::vOutput(0, "\n\n(Make sure Both Parties sign the payment plan before submitting to server):\n\n\n%s\n", strPlan.Get()); continue; } else if (strLine.compare(0,6,"cheque") == 0) { if (NULL == g_pTemporaryNym) { OTLog::Output(0, "No Nym yet available to sign the cheque with. Try 'load'.\n"); continue; } OTLog::Output(0, "Enter the ID for your Asset Account that the cheque will be drawn on: "); OTString strTemp; strTemp.OTfgets(std::cin); const OTIdentifier ACCOUNT_ID(strTemp), USER_ID(*g_pTemporaryNym); OTAccount * pAccount = g_OT_API.GetWallet()->GetAccount(ACCOUNT_ID); if (NULL == pAccount) { OTLog::Output(0, "That account isn't loaded right now. Try 'load'.\n"); continue; } // To write a cheque, we need to burn one of our transaction numbers. (Presumably the wallet // is also storing a couple of these, since they are needed to perform any transaction.) // // I don't have to contact the server to write a cheque -- as long as I already have a transaction // number I can use to write it. Otherwise I'd have to ask the server to send me one first. OTString strServerID(pAccount->GetRealServerID()); long lTransactionNumber=0; if (false == g_pTemporaryNym->GetNextTransactionNum(*g_pTemporaryNym, strServerID, lTransactionNumber)) { OTLog::Output(0, "Cheques are written offline, but you still need a transaction number\n" "(and you have none, currently.) Try using 'n' to request another transaction number.\n"); continue; } OTCheque theCheque(pAccount->GetRealServerID(), pAccount->GetAssetTypeID()); // Recipient OTLog::Output(0, "Enter a User ID for the recipient of this cheque (defaults to blank): "); OTString strRecipientUserID; strRecipientUserID.OTfgets(std::cin); const OTIdentifier RECIPIENT_USER_ID(strRecipientUserID.Get()); // Amount OTLog::Output(0, "Enter an amount: "); strTemp.Release(); strTemp.OTfgets(std::cin); const long lAmount = atol(strTemp.Get()); // ----------------------------------------------------------------------- // Memo OTLog::Output(0, "Enter a memo for your check: "); OTString strChequeMemo; strChequeMemo.OTfgets(std::cin); // ----------------------------------------------------------------------- // Valid date range (in seconds) OTLog::Output(0, " 6 minutes == 360 Seconds\n" "10 minutes == 600 Seconds\n" "1 hour == 3600 Seconds\n" "1 day == 86400 Seconds\n" "30 days == 2592000 Seconds\n" "3 months == 7776000 Seconds\n" "6 months == 15552000 Seconds\n\n" ); long lExpirationInSeconds = 3600; OTLog::vOutput(0, "How many seconds before cheque expires? (defaults to 1 hour: %ld): ", lExpirationInSeconds); strTemp.Release(); strTemp.OTfgets(std::cin); if (strTemp.GetLength() > 1) lExpirationInSeconds = atol(strTemp.Get()); // ----------------------------------------------------------------------- time_t VALID_FROM = time(NULL); // This time is set to TODAY NOW OTLog::vOutput(0, "Cheque may be cashed STARTING date (defaults to now, in seconds) [%ld]: ", VALID_FROM); strTemp.Release(); strTemp.OTfgets(std::cin); if (strTemp.GetLength() > 2) VALID_FROM = atol(strTemp.Get()); const time_t VALID_TO = VALID_FROM + lExpirationInSeconds; // now + 3600 // ----------------------------------------------------------------------- bool bIssueCheque = theCheque.IssueCheque(lAmount, lTransactionNumber, VALID_FROM, VALID_TO, ACCOUNT_ID, USER_ID, strChequeMemo, (strRecipientUserID.GetLength() > 2) ? &(RECIPIENT_USER_ID) : NULL); if (bIssueCheque) { theCheque.SignContract(*g_pTemporaryNym); theCheque.SaveContract(); OTString strCheque(theCheque); OTLog::vOutput(0, "\n\nOUTPUT:\n\n\n%s\n", strCheque.Get()); } else { OTLog::Output(0, "Failed trying to issue the cheque!\n"); // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE NUMBERS. g_pTemporaryNym->AddTransactionNum(*g_pTemporaryNym, strServerID, lTransactionNumber, true); // bSave=true } continue; } else if (strLine.compare(0,7,"decrypt") == 0) { if (NULL == g_pTemporaryNym) { OTLog::Output(0, "No Nym yet available to decrypt with.\n"); continue; } OTLog::Output(0, "Enter text to be decrypted:\n> "); OTASCIIArmor theArmoredText; char decode_buffer[200]; // Safe since we only read sizeof - 1 do { decode_buffer[0] = 0; if (NULL != fgets(decode_buffer, sizeof(decode_buffer)-1, stdin)) { theArmoredText.Concatenate("%s\n", decode_buffer); OTLog::Output(0, "> "); } else { break; } } while (strlen(decode_buffer)>1); OTEnvelope theEnvelope(theArmoredText); OTString strDecodedText; theEnvelope.Open(*g_pTemporaryNym, strDecodedText); OTLog::vOutput(0, "\n\nDECRYPTED TEXT:\n\n%s\n\n", strDecodedText.Get()); continue; } else if (strLine.compare(0,6,"decode") == 0) { OTLog::Output(0, "Enter text to be decoded:\n> "); OTASCIIArmor theArmoredText; char decode_buffer[200]; // Safe since we only read sizeof - 1. do { decode_buffer[0] = 0; if (NULL != fgets(decode_buffer, sizeof(decode_buffer)-1, stdin)) { theArmoredText.Concatenate("%s\n", decode_buffer); OTLog::Output(0, "> "); } else { break; } } while (strlen(decode_buffer)>1); OTString strDecodedText(theArmoredText); OTLog::vOutput(0, "\n\nDECODED TEXT:\n\n%s\n\n", strDecodedText.Get()); continue; } else if (strLine.compare(0,6,"encode") == 0) { OTLog::Output(0, "Enter text to be ascii-encoded (terminate with ~ on a new line):\n> "); OTString strDecodedText; char decode_buffer[200]; // Safe since we only read sizeof - 1. do { decode_buffer[0] = 0; if ((NULL != fgets(decode_buffer, sizeof(decode_buffer)-1, stdin)) && (decode_buffer[0] != '~')) { strDecodedText.Concatenate("%s", decode_buffer); OTLog::Output(0, "> "); } else { break; } } while (decode_buffer[0] != '~'); OTASCIIArmor theArmoredText(strDecodedText); OTLog::vOutput(0, "\n\nENCODED TEXT:\n\n%s\n\n", theArmoredText.Get()); continue; } else if (strLine.compare(0,4,"hash") == 0) { OTLog::Output(0, "Enter text to be hashed (terminate with ~ on a new line):\n> "); OTString strDecodedText; char decode_buffer[200]; // Safe since we only read sizeof - 1. do { decode_buffer[0] = 0; if ((NULL != fgets(decode_buffer, sizeof(decode_buffer)-1, stdin)) && (decode_buffer[0] != '~')) { strDecodedText.Concatenate("%s\n", decode_buffer); OTLog::Output(0, "> "); } else { break; } } while (decode_buffer[0] != '~'); OTIdentifier theIdentifier; theIdentifier.CalculateDigest(strDecodedText); OTString strHash(theIdentifier); OTLog::vOutput(0, "\n\nMESSAGE DIGEST:\n\n%s\n\n", strHash.Get()); continue; } else if (strLine.compare(0,4,"stat") == 0) { OTLog::Output(0, "User has instructed to display wallet contents...\n"); OTString strStat; g_OT_API.GetWallet()->DisplayStatistics(strStat); OTLog::vOutput(0, "%s\n", strStat.Get()); continue; } else if (strLine.compare(0,4,"help") == 0) { OTLog::Output(0, "User has instructed to display the help file...\n"); system("more ../docs/CLIENT-COMMANDS.txt"); continue; } else if (strLine.compare(0,4,"quit") == 0) { OTLog::Output(0, "User has instructed to exit the wallet...\n"); break; } // 1.6 Connect to the first server in the wallet. (assuming it loaded a server contract.) else if (strLine.compare(0,7,"connect") == 0) { OTLog::Output(0, "User has instructed to connect to the first server available in the wallet.\n"); if (NULL == g_pTemporaryNym) { OTLog::Output(0, "No Nym yet available to connect with. Try 'load'.\n"); continue; } // Wallet, after loading, should contain a list of server // contracts. Let's pull the hostname and port out of // the first contract, and connect to that server. bool bConnected = g_OT_API.GetClient()->ConnectToTheFirstServerOnList(*g_pTemporaryNym, strCAFile, strKeyFile, strSSLPassword); if (bConnected) OTLog::Output(0, "Success. (Connected to the first notary server on your wallet's list.)\n"); else { OTLog::Output(0, "Either the wallet is not loaded, or there was an error connecting to server.\n"); } continue; } if (!g_OT_API.GetClient()->IsConnected()) { OTLog::Output(0, "(You are not connected to a notary server--you cannot send commands.)\n"); continue; } // 2) Process it out as an OTMessage to the server. It goes down the pipe. g_OT_API.GetClient()->ProcessMessageOut(buf, &nExpectResponse); // 3) Sleep for 1 second. #ifdef _WIN32 OT_Sleep(1000); #else sleep (1); #endif bool bFoundMessage = false; // 4) While there are messages to be read in response from the server, // then process and handle them all. do { OTMessage * pMsg = new OTMessage; OT_ASSERT(NULL != pMsg); // If this returns true, that means a Message was // received and processed into an OTMessage object (theMsg) bFoundMessage = g_OT_API.GetClient()->ProcessInBuffer(*pMsg); if (true == bFoundMessage) { // OTString strReply; // theMsg.SaveContract(strReply); // OTLog::vOutput(0, "\n\n**********************************************\n" // "Successfully in-processed server response.\n\n%s\n", strReply.Get()); g_OT_API.GetClient()->ProcessServerReply(*pMsg); // the Client takes ownership and will handle cleanup. } else { delete pMsg; pMsg = NULL; } } while (true == bFoundMessage); } // for OTLog::Output(0, "Finished running client.\n"); #ifdef _WIN32 WSACleanup(); #endif return retVal; }
bool OTEnvelope::Decrypt(OTString & theOutput, const OTSymmetricKey & theKey, const OTPassword & thePassword) { const char * szFunc = "OTEnvelope::Decrypt"; // ------------------------------------------------ OT_ASSERT((thePassword.isPassword() && (thePassword.getPasswordSize() > 0)) || (thePassword.isMemory() && (thePassword.getMemorySize() > 0))); OT_ASSERT(theKey.IsGenerated()); // ----------------------------------------------- OTPassword theRawSymmetricKey; if (false == theKey.GetRawKeyFromPassphrase(thePassword, theRawSymmetricKey)) { OTLog::vError("%s: Failed trying to retrieve raw symmetric key using password. (Wrong password?)\n", szFunc); return false; } // ----------------------------------------------- // uint32_t nRead = 0; uint32_t nRunningTotal = 0; m_dataContents.reset(); // Reset the fread position on this object to 0. // **************************************************************************** // // Read the ENVELOPE TYPE (as network order version -- and convert to host version.) // // 0 == Error // 1 == Asymmetric Key (this function -- Seal / Open) // 2 == Symmetric Key (other functions -- Encrypt / Decrypt use this.) // Anything else: error. // uint16_t env_type_n = 0; if (0 == (nRead = m_dataContents.OTfread(reinterpret_cast<uint8_t*>(&env_type_n), static_cast<uint32_t>(sizeof(env_type_n))))) { OTLog::vError("%s: Error reading Envelope Type. Expected asymmetric(1) or symmetric (2).\n", szFunc); return false; } nRunningTotal += nRead; OT_ASSERT(nRead == static_cast<uint32_t>(sizeof(env_type_n))); // ---------------------------------------------------------------------------- // convert that envelope type from network to HOST endian. // const uint16_t env_type = static_cast<uint16_t>(ntohs(static_cast<uint16_t>(env_type_n))); // nRunningTotal += env_type; // NOPE! Just because envelope type is 1 or 2, doesn't mean we add 1 or 2 extra bytes to the length here. Nope! if (2 != env_type) { const uint32_t l_env_type = static_cast<uint32_t>(env_type); OTLog::vError("%s: Error: Expected Envelope for Symmetric key (type 2) but instead found type: %ld.\n", szFunc, l_env_type); return false; } // **************************************************************************** // // Read network-order IV size (and convert to host version) // const uint32_t max_iv_length = OTCryptoConfig::SymmetricIvSize(); // I believe this is a max length, so it may not match the actual length of the IV. // Read the IV SIZE (network order version -- convert to host version.) // uint32_t iv_size_n = 0; if (0 == (nRead = m_dataContents.OTfread(reinterpret_cast<uint8_t*>(&iv_size_n), static_cast<uint32_t>(sizeof(iv_size_n))))) { OTLog::vError("%s: Error reading IV Size.\n", szFunc); return false; } nRunningTotal += nRead; OT_ASSERT(nRead == static_cast<uint32_t>(sizeof(iv_size_n))); // ---------------------------------------------------------------------------- // convert that iv size from network to HOST endian. // const uint32_t iv_size_host_order = ntohl(iv_size_n); if (iv_size_host_order > max_iv_length) { OTLog::vError("%s: Error: iv_size (%ld) is larger than max_iv_length (%ld).\n", szFunc, static_cast<long>(iv_size_host_order), static_cast<long>(max_iv_length)); return false; } // nRunningTotal += iv_size_host_order; // Nope! // **************************************************************************** // // Then read the IV (initialization vector) itself. // OTPayload theIV; theIV.SetPayloadSize(iv_size_host_order); if (0 == (nRead = m_dataContents.OTfread(static_cast<uint8_t*>(const_cast<void *>(theIV.GetPayloadPointer())), static_cast<uint32_t>(iv_size_host_order)))) { OTLog::vError("%s: Error reading initialization vector.\n", szFunc); return false; } nRunningTotal += nRead; OT_ASSERT(nRead == static_cast<uint32_t>(iv_size_host_order)); OT_ASSERT(nRead <= max_iv_length); // ---------------------------------------------------------------------------- // We create an OTPayload object to store the ciphertext itself, which begins AFTER the end of the IV. // So we see pointer + nRunningTotal as the starting point for the ciphertext. // the size of the ciphertext, meanwhile, is the size of the entire thing, MINUS nRunningTotal. // OTPayload theCipherText(static_cast<const void*>( static_cast<const uint8_t *>(m_dataContents.GetPointer()) + nRunningTotal ), m_dataContents.GetSize() - nRunningTotal); // ---------------------------------------------------------------------------- // Now we've got all the pieces together, let's try to decrypt it... // OTPayload thePlaintext; // for output. const bool bDecrypted = OTCrypto::It()->Decrypt(theRawSymmetricKey, // The symmetric key, in clear form. // ------------------------------- static_cast<const char *>(theCipherText.GetPayloadPointer()), // This is the Ciphertext. theCipherText.GetSize(), // ------------------------------- theIV, // ------------------------------- thePlaintext); // OUTPUT. (Recovered plaintext.) You can pass OTPassword& OR OTPayload& here (either will work.) // ----------------------------------------------- // theOutput is where we'll put the decrypted data. // theOutput.Release(); if (bDecrypted) { // ----------------------------------------------------- // Make sure it's null-terminated... // uint32_t nIndex = thePlaintext.GetSize()-1; (static_cast<uint8_t*>(const_cast<void *>(thePlaintext.GetPointer())))[nIndex] = '\0'; // ----------------------------------------------------- // Set it into theOutput (to return the plaintext to the caller) // theOutput.Set(static_cast<const char *>(thePlaintext.GetPointer())); // ---------------- } return bDecrypted; }
bool OTEnvelope::Open(const OTPseudonym & theRecipient, OTString & theContents) { bool retval = false; EVP_CIPHER_CTX ctx; unsigned char buffer[4096]; unsigned char buffer_out[4096 + EVP_MAX_IV_LENGTH]; unsigned char iv[EVP_MAX_IV_LENGTH]; size_t len = 0; int len_out = 0; unsigned char * ek = NULL; int eklen = 0; uint32_t eklen_n = 0; memset(buffer, 0, 4096); memset(buffer_out, 0, 4096 + EVP_MAX_IV_LENGTH); memset(iv, 0, EVP_MAX_IV_LENGTH); OTAsymmetricKey & privateKey = (OTAsymmetricKey &)theRecipient.GetPrivateKey(); EVP_PKEY * pkey = (EVP_PKEY *)privateKey.GetKey(); if (NULL == pkey) { OTLog::Error("Null private key in OTEnvelope::Open\n"); return false; } EVP_CIPHER_CTX_init(&ctx); ek = (unsigned char*)malloc(EVP_PKEY_size(pkey)); // I assume this is for the AES key OT_ASSERT(NULL != ek); memset(ek, 0, EVP_PKEY_size(pkey)); eklen = EVP_PKEY_size(pkey); //int EVP_OpenInit(EVP_CIPHER_CTX *ctx, //EVP_CIPHER *type, //unsigned char *ek, //int ekl, //unsigned char *iv, //EVP_PKEY *priv); //EVP_OpenInit() initializes a cipher context ctx for decryption with cipher type. It decrypts the encrypted // symmetric key of length ekl bytes passed in the ek parameter using the private key priv. The IV is supplied // in the iv parameter. theContents.Release(); // This is where we'll put the decrypted data. m_dataContents.reset(); // reset the fread position on this object. int nReadLength = 0; int nReadKey = 0; int nReadIV = 0; // First we read the encrypted key size. if (0 == (nReadLength = m_dataContents.OTfread((char*)&eklen_n, sizeof(eklen_n)))) { OTLog::Error("Error reading encrypted key size in OTEnvelope::Open\n"); free(ek); ek = NULL; return false; } // convert it from network to host endian. eklen = ntohl(eklen_n); // Next we read the encrypted key itself. if (0 == (nReadKey = m_dataContents.OTfread((char*)ek, eklen))) { OTLog::Error("Error reading encrypted key size in OTEnvelope::Open\n"); free(ek); ek = NULL; return false; } // Next we read the initialization vector. if (0 == (nReadIV = m_dataContents.OTfread((char*)iv, EVP_CIPHER_iv_length(EVP_aes_128_cbc())))) { OTLog::Error("Error reading initialization vector in OTEnvelope::Open\n"); free(ek); ek = NULL; return false; } OTData ciphertext((const void*)((unsigned char *)m_dataContents.GetPointer() + nReadLength + nReadKey + nReadIV), m_dataContents.GetSize() - nReadLength - nReadKey - nReadIV); // Now we process ciphertext and write the decrypted data to plaintext. OTData plaintext; if (!EVP_OpenInit(&ctx, EVP_aes_128_cbc(), ek, eklen, iv, pkey)) { OTLog::Error("EVP_OpenInit: failed.\n"); free(ek); ek = NULL; return false; } while ((len = ciphertext.OTfread((char*)buffer, sizeof(buffer))) > 0) { if (!EVP_OpenUpdate(&ctx, buffer_out, &len_out, buffer, len)) { OTLog::Error("EVP_OpenUpdate: failed.\n"); free(ek); ek = NULL; return false; } OTData dataOpenUpdate(buffer_out, len_out); plaintext += dataOpenUpdate; } if (!EVP_OpenFinal(&ctx, buffer_out, &len_out)) { OTLog::Error("EVP_OpenFinal: failed.\n"); free(ek); ek = NULL; return false; } OTData dataOpenFinal(buffer_out, len_out); plaintext += dataOpenFinal; // Make sure it's null terminated int nIndex = plaintext.GetSize()-1; ((unsigned char*)plaintext.GetPointer())[nIndex] = 0; // Set it into theContents (to return the plaintext to the caller) theContents.Set((const char *)plaintext.GetPointer()); retval = true; free(ek); ek = NULL; return retval; }
// Used when importing/exporting a Nym to/from the wallet. // bool OTKeypair::ReEncrypt(OTPassword & theExportPassword, bool bImporting, OTString & strOutput) { // -------------------------------------- OT_ASSERT(NULL != m_pkeyPublic ); OT_ASSERT(NULL != m_pkeyPrivate); // -------------------------------------- OT_ASSERT(this->HasPublicKey() ); OT_ASSERT(this->HasPrivateKey()); // -------------------------------------- // If we were importing, we were in the exported format but now we're in the internal format. // Therefore we want to use the wallet's internal cached master passphrase to save. Therefore // strReason will be used for the import case. // // But if we were exporting, then we were in the internal format and just re-encrypted to the // export format. So we'd want to pass the export passphrase when saving. // const OTString strReasonAbove(bImporting ? "Enter the new export passphrase. (Above ReEncryptPrivateKey in OTKeypair::ReEncrypt)" : "Enter your wallet's master passphrase. (Above ReEncryptPrivateKey in OTKeypair::ReEncrypt)"); const OTString strReasonBelow(bImporting ? "Enter your wallet's master passphrase. (Below ReEncryptPrivateKey in OTKeypair::ReEncrypt)" : "Enter the new export passphrase. (Below ReEncryptPrivateKey in OTKeypair::ReEncrypt)"); // -------------------------------------- // At this point the public key was loaded from a public key, not a cert, // but the private key was loaded from the cert. Therefore we'll save the // public cert from the private key, and then use that to reload the public // key after ReEncrypting. (Otherwise the public key would be there, but it // would be missing the x509, which is only available in the cert, not the // pubkey alone -- and without the x509 being there, the "SaveAndReload" call // below would fail. // Why don't I just stick the Cert itself into the public data, instead of // sticking the public key in there? Because not all key credentials will use // certs. Some will use pubkeys from certs, and some will use pubkeys not from // certs. But I might still just stick it in there, and code things to be able to // load either indiscriminately. After all, that's what I'm doing already in the // asset and server contracts. But even in those cases, there will be times when // only a pubkey is available, not a cert, so I'll probably still find myself having // to do this. Hmm... // --------------------------------------- const bool bReEncrypted = m_pkeyPrivate->ReEncryptPrivateKey(theExportPassword, bImporting); // <==== IMPORT or EXPORT occurs here. bool bGotCert = false; if (bReEncrypted) { // ------------------------------------------------ // Keys won't be right until this happens. Todo: eliminate this need. bGotCert = this->SaveAndReloadBothKeysFromTempFile(&strOutput, &strReasonBelow, bImporting ? NULL : &theExportPassword); } const bool bSuccess = (bReEncrypted && bGotCert); // -------------------------------------- if (!bSuccess) { strOutput.Release(); OTLog::vError("%s: Failure, either when re-encrypting, or when subsequently retrieving " "the public/private keys. bImporting == %s\n", __FUNCTION__, bImporting ? "true" : "false"); } // -------------------------------------- return bSuccess; }