// Low-level // // (Internal) ASCII-Armored key ====> (Internal) Actual loaded OpenSSL key. // // EVP_PKEY * OTAsymmetricKey_OpenSSL::OTAsymmetricKey_OpenSSLPrivdp::InstantiatePublicKey(OTPasswordData * pPWData/*=NULL*/) { OT_ASSERT(m_pKey == NULL); OT_ASSERT(backlink->m_p_ascKey != NULL); OT_ASSERT(backlink->IsPublic()); const char * szFunc = "OTAsymmetricKey_OpenSSL::InstantiatePublicKey"; // ------------------------------ EVP_PKEY * pReturnKey = NULL; OTPayload theData; // ----------------------------------------------- // This base64 decodes the string m_p_ascKey into the // binary payload object "theData" // backlink->m_p_ascKey->GetData(theData); if (theData.GetSize() > 0) { // ------------------------------------------- // Next, copy theData's contents into a new BIO_mem_buf, // so OpenSSL can load the key out of it. // OpenSSL_BIO keyBio = BIO_new_mem_buf(static_cast<char*>(const_cast<void*>(theData.GetPayloadPointer())), theData.GetSize()); OT_ASSERT_MSG(NULL != keyBio, "OTAsymmetricKey_OpenSSL::InstantiatePublicKey: Assert: NULL != keyBio \n"); // ------------------------------------------- // Next we load up the key from the BIO string into an instantiated key object. // OTPasswordData thePWData("OTAsymmetricKey_OpenSSL::InstantiatePublicKey is calling PEM_read_bio_PUBKEY..."); if (NULL == pPWData) pPWData = &thePWData; pReturnKey = PEM_read_bio_PUBKEY(keyBio, NULL, OTAsymmetricKey::GetPasswordCallback(), pPWData); // ------------------------------------------- // ------------------------------------------- backlink->ReleaseKeyLowLevel(); // Release whatever loaded key I might have already had. if (NULL != pReturnKey) { m_pKey = pReturnKey; OTLog::vOutput(4, "%s: Success reading public key from ASCII-armored data:\n\n%s\n\n", szFunc, backlink->m_p_ascKey->Get()); return m_pKey; } } OTLog::vError("%s: Failed reading public key from ASCII-armored data:\n\n%s\n\n", szFunc, backlink->m_p_ascKey->Get()); return NULL; }
void SetupHeader( union u_header * pCMD, int nTypeID, int nCmdID, OTPayload & thePayload) { pCMD->fields.type_id = nTypeID; pCMD->fields.command_id = nCmdID; // pCMD->fields.size = thePayload.GetSize(); pCMD->fields.size = htonl(thePayload.GetSize()); // think this is causing problems pCMD->fields.checksum = CalcChecksum(pCMD->buf, OT_CMD_HEADER_SIZE-1); BYTE byChecksum = (BYTE)pCMD->fields.checksum; int nChecksum = byChecksum; uint32_t nTemp = thePayload.GetSize(); fprintf(stderr, "(Payload size %d, TYPE %d command, checksum: %d...)\n", nTemp, nTypeID, nChecksum); }
void SetupHeader( union u_header * pCMD, int nTypeID, int nCmdID, OTPayload & thePayload) { OT_ASSERT(NULL != pCMD); pCMD->fields.type_id = (nTypeID > 0) ? static_cast<BYTE>(nTypeID) : '\0'; pCMD->fields.command_id = (nCmdID > 0) ? static_cast<BYTE>(nCmdID) : '\0'; // pCMD->fields.size = thePayload.GetSize(); pCMD->fields.size = htonl(thePayload.GetSize()); // think this is causing problems pCMD->fields.checksum = CalcChecksum(pCMD->buf, OT_CMD_HEADER_SIZE-1); BYTE byChecksum = (BYTE)pCMD->fields.checksum; int nChecksum = byChecksum; uint32_t nTemp = thePayload.GetSize(); OTLog::vOutput(4, "(Payload size %d, TYPE %d command, checksum: %d...)\n", nTemp, nTypeID, nChecksum); }
// The above method uses this one internally... bool OTAccount::GenerateNewAccount(const OTPseudonym & theServer, const OTMessage & theMessage, const OTAccount::AccountType eAcctType/*=OTAccount::simple*/) { // First we generate a secure random number into a binary object. OTPayload thePayload; thePayload.SetPayloadSize(100); if (!RAND_bytes((unsigned char*)thePayload.GetPayloadPointer(), 100)) { OTLog::Error("The PRNG is not seeded!\n"); // abort( ); return false; } // Next we calculate that binary object into a message digest (an OTIdentifier). OTIdentifier newID; if (!newID.CalculateDigest(thePayload)) { OTLog::Error("Error generating new account ID.\n"); return false; } // Next we get that digest (which is a binary hash number) // and extract a human-readable standard string format of that hash, // into an OTString. OTString strID(newID); SetRealAccountID(newID); // Set the account number based on what we just generated. SetPurportedAccountID(newID); // Might as well set them both. (Safe here to do so, for once.) m_strName.Set(strID); // So it's not blank. The user can always change it. // Next we create the full path filename for the account using the ID. m_strFilename.Format("%s%s%s%s%s", OTLog::Path(), OTLog::PathSeparator(), OTLog::AccountFolder(), OTLog::PathSeparator(), strID.Get()); // Then we try to load it, in order to make sure that it doesn't already exist. if (LoadContractRawFile()) { OTLog::Error("Error generating new account ID, account already exists.\n"); return false; } // Set up the various important starting values of the account. m_AcctType = eAcctType; // account type defaults to OTAccount::simple. But there are also issuer accts... // for basket accounts, the server is the user. if (OTAccount::basket == eAcctType) { theServer.GetIdentifier(m_AcctUserID); } else { m_AcctUserID.SetString(theMessage.m_strNymID); } m_AcctAssetTypeID.SetString(theMessage.m_strAssetID); OTString TEMPstr(m_AcctAssetTypeID); OTLog::vOutput(3, "Creating new account, type:\n%s\nChanged to ID then back to string:\n%s\n", theMessage.m_strAssetID.Get(), TEMPstr.Get()); OTIdentifier SERVER_ID(theMessage.m_strServerID); SetRealServerID(SERVER_ID); // todo this assumes the serverID on the message is correct. It's vetted, but still... SetPurportedServerID(SERVER_ID); const time_t tDate = time(NULL); // Today, now. m_BalanceDate.Format("%d", tDate); m_BalanceAmount.Set("0"); // Sign the Account (so we know that we did)... Otherwise someone could put a fake // account file on the server if the code wasn't designed to verify the signature on the // account. SignContract(theServer); SaveContract(); // Save the Account to storage (based on its ID.) SaveAccount(); // Don't know why I had this here. Putting SaveAccount() instead. // OTString strFilename(m_strFilename); // SaveContract(strFilename.Get()); // Saves the account to a specific filename // No need to create the inbox and outbox ledgers...they will be created automatically // if they do not exist when they are needed. return true; }
bool OTPAYLOAD_GetMessage(OTPayload & thePayload, OTMessage & theMessage) { return thePayload.GetMessage(theMessage); }
// Process my reply back out to the client. @something. // For TCP / SSL mode. void OTClientConnection::ProcessReply(OTMessage &theReply) { OT_ASSERT(NULL != m_pPublicKey); int err = 0; uint32_t nwritten = 0; bool bSendCommand = false; bool bSendPayload = false; u_header theCMD; OTPayload thePayload; memset((void *)theCMD.buf, 0, OT_CMD_HEADER_SIZE); // todo cast // For now let's send ALL replies in Envelopes (encrypted to public key of client) // IF we have a public key, that is. Otherwise we send as a normal message. // // All messages already require either a public key, or a nymID used to look up a // public key. So given that I have that information when I reply, I might as well // ENCRYPT my reply to that same public key. More secure that way. // // The wallet (and server) are both ready to open and process these encrypted envelopes. // If GetKey() returns something, that means the key was set in there, it's // not just a null pointer. This means we can use it! So let's encrypt to it. if (m_pPublicKey->IsPublic()) { OTString strEnvelopeContents(theReply); // Save the ready-to-go message into a string. OTEnvelope theEnvelope; // Seal the string up into an encrypted Envelope theEnvelope.Seal(*m_pPublicKey, strEnvelopeContents); // From here on out, theMessage is disposable. OTPayload takes over. // OTMessage doesn't care about checksums and headers. thePayload.SetEnvelope(theEnvelope); // Now that the payload is ready, we'll set up the header. SetupHeader(&theCMD, CMD_TYPE_1, TYPE_1_CMD_2, thePayload); } else { thePayload.SetMessage(theReply); // Now that the payload is ready, we'll set up the header. SetupHeader(&theCMD, CMD_TYPE_1, TYPE_1_CMD_1, thePayload); } bSendCommand = true; bSendPayload = true; OTLog::vOutput(2, "\n****************************************************************\n" "===> Finished setting up header for response.\nFirst 9 bytes are: %d %d %d %d %d %d %d %d %d...\n", theCMD.buf[0], theCMD.buf[1], theCMD.buf[2], theCMD.buf[3], theCMD.buf[4], theCMD.buf[5], theCMD.buf[6], theCMD.buf[7], theCMD.buf[8]); // ------------------------------------------------------------------------------ /* // Write to Client strcpy(buffer, "Hello Client!"); SFSocketWrite(clientSocket, buffer, strlen(buffer)); */ if (bSendCommand) { const unsigned int nHeaderSize = OT_CMD_HEADER_SIZE; for (nwritten = 0; nwritten < nHeaderSize; nwritten += err) { // err = SFSocketWrite(m_pSocket, theCMD.buf + nwritten, nHeaderSize - nwritten); #ifdef _WIN32 if (0 == err || SOCKET_ERROR == err) // 0 means disconnect. error means error. >0 means bytes read. #else if (err <= 0) #endif break; } } // At this point, we have sent the header across the pipe. if (bSendPayload) { uint32_t nPayloadSize = thePayload.GetSize(); for (nwritten = 0; nwritten < nPayloadSize; nwritten += err) { // err = SFSocketWrite(m_pSocket, (unsigned char *)thePayload.GetPayloadPointer() + nwritten, nPayloadSize - nwritten); #ifdef _WIN32 if (0 == err || SOCKET_ERROR == err) // 0 means disconnect. error means error. >0 means bytes read. #else if (err <= 0) #endif break; } } // At this point, we have sent the payload across the pipe. OTLog::Output(2, "...Done.\n"); }
// A certain number of bytes are expected in the payload, according to the header. // This function tries to read that many bytes, and inserts them into an OTPayload object. // From there, a simple method call extracts the message, we return true, and the message // gets added to our internal list for processing. bool OTClientConnection::ProcessType1Cmd(u_header & theCMD, OTMessage & theMessage) { // At this point, the checksum has already validated. // Might as well get the PAYLOAD next. // int err; uint32_t nread, lSize = theCMD.fields.size; // Make sure our byte-order is correct here. // theCMD.fields.size = ntohl(theCMD.fields.size); // I was doing this twice!! This is already done when the header is first read. // setup the buffer we are reading into OTPayload thePayload; nread = thePayload.ReadBytesFrom(m_Buffer, lSize); /* // actually read the payload from the socket into the buffer. for (nread = 0; nread < theCMD.fields.size; nread += err) { err = SFSocketRead(m_pSocket, (unsigned char *)thePayload.GetPayloadPointer() + nread, theCMD.fields.size - nread); // if we don't read anything more, stop reading and move on if (err <= 0) break; } */ // TODO fix the buffering so that if a complete command has not yet been received, it saves the other // bytes instead of discarding them. For now I'll just sleep for a second to make sure the entire command // was received. // sleep(1); // ------------------------------------------------------------ // Try to interpret the command number. // Right now we support signed messages and encrypted envelopes containing // signed messages. switch (theCMD.fields.command_id) { case TYPE_1_CMD_1: OTLog::Output(2, "Received Type 1 CMD 1:\nThere is a signed OTMessage in the payload.\n"); break; case TYPE_1_CMD_2: OTLog::Output(2, "Received Type 1 CMD 2:\n" "There is an encrypted OTEnvelope (containing signed OTMessage) in the payload.\n"); break; default: OTLog::vError("Received unexpected command number %d in OTClientConnection::ProcessType1Cmd\n", theCMD.fields.command_id); break; } // ------------------------------------------------------------ // Hm, that's weird. It was a 0 size payload message. DoS? if (theCMD.fields.size == 0) { OTLog::Output(2, "(The payload was a 0 size.)\n"); return true; } // Uh-oh, somehow the number of bytes read was less than what we expected... else if (nread < theCMD.fields.size) { // TODO: Verify that the amount read matched the amount expected // if not, we have a problem that needs to be handled. // Long term solution is to buffer the data as a comes in and just // add it to the buffer. // Then if we don't have the complete message yet, we just come around next // time some data is read, and we add that to the buffer, THEN we check to see // if there are enough bytes yet read to match the amount expected according to // the header. // // Until I can do that, I'm not yet TRULY asynchronous. TODO: lookup a good buffer class. OTLog::Error("Number of bytes read did NOT match size in header.\n"); return false; } else OTLog::vOutput(2, "Loaded a payload, size: %d\n", theCMD.fields.size); // ------------------------------------------------------------ // Okay so now we've received the expected size from the socket. Let's transfer it // into an object type that we can manipulate here in code. (Message or Envelope.) // a signed OTMessage if (TYPE_1_CMD_1 == theCMD.fields.command_id) { #ifdef _WIN32 if (OTPAYLOAD_GetMessage(thePayload, theMessage)) #else if (thePayload.GetMessage(theMessage)) #endif { OTLog::Output(2, "Successfully retrieved payload message...\n"); if (theMessage.ParseRawFile()) { OTLog::Output(2, "Successfully parsed payload message.\n"); return true; } else { OTLog::Error("Error parsing message.\n"); return false; } } else { OTLog::Error("Error retrieving message from payload.\n"); return false; } } // A base64-encoded envelope, encrypted, and containing a signed message. else if (TYPE_1_CMD_2 == theCMD.fields.command_id) { OTEnvelope theEnvelope; if (thePayload.GetEnvelope(theEnvelope)) { OTLog::Output(2, "Successfully retrieved envelope from payload...\n"); OTString strEnvelopeContents; // Decrypt the Envelope. if (m_pServer && theEnvelope.Open(m_pServer->GetServerNym(), strEnvelopeContents)) { // All decrypted, now let's load the results into an OTMessage. // No need to call theMessage.ParseRawFile() after, since // LoadContractFromString handles it. // if (strEnvelopeContents.Exists() && theMessage.LoadContractFromString(strEnvelopeContents)) { OTLog::Output(2, "Success loading message out of the envelope contents and parsing it.\n"); return true; } else { OTLog::Error("Error loading message from envelope contents.\n"); return false; } } else { OTLog::Error("Unable to open envelope.\n"); return false; } } else { OTLog::Error("Error retrieving message from payload.\n"); return false; } } return true; }
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::Encrypt(const OTString & theInput, OTSymmetricKey & theKey, const OTPassword & thePassword) { OT_ASSERT((thePassword.isPassword() && (thePassword.getPasswordSize() > 0)) || (thePassword.isMemory() && (thePassword.getMemorySize() > 0))); OT_ASSERT(theInput.Exists()); // ----------------------------------------------- // Generate a random initialization vector. // OTPayload theIV; if (false == theIV.Randomize(OTCryptoConfig::SymmetricIvSize())) { OTLog::vError("%s: Failed trying to randomly generate IV.\n", __FUNCTION__); return false; } // ----------------------------------------------- // If the symmetric key hasn't already been generated, we'll just do that now... // (The passphrase is used to derive another key that is used to encrypt the // actual symmetric key, and to access it later.) // if ((false == theKey.IsGenerated()) && (false == theKey.GenerateKey(thePassword))) { OTLog::vError("%s: Failed trying to generate symmetric key using password.\n", __FUNCTION__); return false; } // ----------------------------------------------- if (!theKey.HasHashCheck()) { if(!theKey.GenerateHashCheck(thePassword)) { OTLog::vError("%s: Failed trying to generate hash check using password.\n", __FUNCTION__); return false; } } OT_ASSERT(theKey.HasHashCheck()); OTPassword theRawSymmetricKey; if (false == theKey.GetRawKeyFromPassphrase(thePassword, theRawSymmetricKey)) { OTLog::vError("%s: Failed trying to retrieve raw symmetric key using password.\n", __FUNCTION__); return false; } // ----------------------------------------------- // OTPayload theCipherText; const bool bEncrypted = OTCrypto::It()->Encrypt(theRawSymmetricKey, // The symmetric key, in clear form. // ------------------------------- theInput.Get(), // This is the Plaintext. theInput.GetLength() + 1, // for null terminator // ------------------------------- theIV, // Initialization vector. // ------------------------------- theCipherText); // OUTPUT. (Ciphertext.) // ----------------------------------------------- // // Success? // if (!bEncrypted) { OTLog::vError("%s: (static) call failed to encrypt. Wrong key? (Returning false.)\n", __FUNCTION__); return false; } // ----------------------------------------------- // // This is where the envelope final contents will be placed, // including the envelope type, the size of the IV, the IV // itself, and the ciphertext. // m_dataContents.Release(); // ----------------------------------------------- // Write the ENVELOPE TYPE (network order version.) // // 0 == Error // 1 == Asymmetric Key (other functions -- Seal / Open.) // 2 == Symmetric Key (this function -- Encrypt / Decrypt.) // Anything else: error. uint16_t env_type_n = static_cast<uint16_t>(htons(static_cast<uint16_t>(2))); // Calculate "network-order" version of envelope type 2. m_dataContents.Concatenate(reinterpret_cast<void *>(&env_type_n), // (uint32_t here is the 2nd parameter to Concatenate, and has nothing to do with env_type_n being uint16_t) static_cast<uint32_t>(sizeof(env_type_n))); // ------------------------------------------------------------ // // Write IV size (in network-order) // uint32_t ivlen = OTCryptoConfig::SymmetricIvSize(); // Length of IV for this cipher... OT_ASSERT(ivlen >= theIV.GetSize()); uint32_t ivlen_n = htonl(theIV.GetSize()); // Calculate "network-order" version of iv length. m_dataContents.Concatenate(reinterpret_cast<void *>(&ivlen_n), static_cast<uint32_t>(sizeof(ivlen_n))); // Write the IV itself. // m_dataContents.Concatenate(theIV.GetPayloadPointer(), static_cast<uint32_t>(theIV.GetSize())); // ------------------------------------------------------------ // Write the Ciphertext. // m_dataContents.Concatenate(theCipherText.GetPayloadPointer(), static_cast<uint32_t>(theCipherText.GetSize())); // We don't write the size of the ciphertext before the ciphertext itself, // since the decryption is able to deduce the size based on the total envelope // size minus the other pieces. We might still want to add that size here, however. // (for security / safety reasons.) // ----------------------------------------------- return true; }
// The above method uses this one internally... bool OTAccount::GenerateNewAccount(const OTPseudonym & theServer, const OTMessage & theMessage, const OTAccount::AccountType eAcctType/*=OTAccount::simple*/, int64_t lStashTransNum/*=0*/) { const char *szFunc = "OTAccount::GenerateNewAccount"; // ----------------------------------------------- // First we generate a secure random number into a binary object... // OTPayload thePayload; if (false == thePayload.Randomize(100)) // todo hardcoding. Plus: is 100 bytes of random a little much here? { OTLog::vError("%s: Failed trying to acquire random numbers.\n", szFunc); return false; } // -------------------------------------------------- // // Next we calculate that binary object into a message digest (an OTIdentifier). // OTIdentifier newID; if (!newID.CalculateDigest(thePayload)) { OTLog::vError("%s: Error generating new account ID.\n", szFunc); return false; } // -------------------------------------------------- // // Next we get that digest (which is a binary hash number) // and extract a human-readable standard string format of that hash, // into an OTString. // OTString strID(newID); SetRealAccountID(newID); // Set the account number based on what we just generated. SetPurportedAccountID(newID); // Might as well set them both. (Safe here to do so, for once.) m_strName.Set(strID); // So it's not blank. The user can always change it. // Next we create the full path filename for the account using the ID. // m_strFoldername = OTFolders::Account().Get(); m_strFilename = strID.Get(); // Then we try to load it, in order to make sure that it doesn't already exist. // -------------------------------------------------------------------- if (OTDB::Exists(m_strFoldername.Get(), m_strFilename.Get())) { OTLog::vError("%s: Account already exists: %s\n", szFunc, m_strFilename.Get()); return false; } // -------------------------------------------------------------------- // Set up the various important starting values of the account. m_AcctType = eAcctType; // account type defaults to OTAccount::simple. But there are also issuer accts... // -------------------------------------------------------------------- if (IsInternalServerAcct()) // basket, basketsub, mint, voucher, and stash accounts are all "owned" by the server. { theServer.GetIdentifier(m_AcctUserID); } else { m_AcctUserID.SetString(theMessage.m_strNymID); } // -------------------------------------------------------------------- m_AcctAssetTypeID.SetString(theMessage.m_strAssetID); OTLog::vOutput(3, "%s: Creating new account, type:\n%s\n", szFunc, theMessage.m_strAssetID.Get()); OTIdentifier SERVER_ID(theMessage.m_strServerID); SetRealServerID(SERVER_ID); // todo this assumes the serverID on the message is correct. It's vetted, but still... SetPurportedServerID(SERVER_ID); const time64_t tDate = OTTimeGetCurrentTime(); // Today, now. m_BalanceDate.Format("%d", tDate); m_BalanceAmount.Set("0"); // -------------------------------------------------------------------- if (IsStashAcct()) { OT_ASSERT_MSG(lStashTransNum > 0, "You created a stash account, but with a zero-or-negative transaction number for its cron item."); m_lStashTransNum = lStashTransNum; } // -------------------------------------------------------------------- // Sign the Account (so we know that we did)... Otherwise someone could put a fake // account file on the server if the code wasn't designed to verify the signature on the // account. SignContract(theServer); SaveContract(); // Save the Account to storage (based on its ID.) SaveAccount(); // Don't know why I had this here. Putting SaveAccount() instead. // OTString strFilename(m_strFilename); // SaveContract(strFilename.Get()); // Saves the account to a specific filename // No need to create the inbox and outbox ledgers...they will be created automatically // if they do not exist when they are needed. return true; }
//static bool OTAsymmetricKey_OpenSSL::OTAsymmetricKey_OpenSSLPrivdp::ArmorPrivateKey(EVP_PKEY & theKey, OTASCIIArmor & ascKey, Timer & theTimer, OTPasswordData * pPWData/*=NULL*/, OTPassword * pImportPassword/*=NULL*/) { bool bReturnVal = false; ascKey.Release(); // ---------------------------------------- // Create a new memory buffer on the OpenSSL side OpenSSL_BIO bmem = BIO_new(BIO_s_mem()); OT_ASSERT(NULL != bmem); int64_t lSize = 0; // ---------------------------------------- // write a private key to that buffer, from theKey // OTPasswordData thePWData("OTAsymmetricKey_OpenSSL::ArmorPrivateKey is calling PEM_write_bio_PrivateKey..."); if (NULL == pPWData) pPWData = &thePWData; int32_t nWriteBio = 0; if (NULL == pImportPassword) nWriteBio = PEM_write_bio_PrivateKey(bmem, &theKey, EVP_des_ede3_cbc(), // todo should this algorithm be hardcoded? NULL, 0, OTAsymmetricKey::GetPasswordCallback(), pPWData); else nWriteBio = PEM_write_bio_PrivateKey(bmem, &theKey, EVP_des_ede3_cbc(), // todo should this algorithm be hardcoded? NULL, 0, 0, const_cast<void*>(reinterpret_cast<const void*>(pImportPassword->getPassword()))); if (0 == nWriteBio) { OTLog::vError("%s: Failed writing EVP_PKEY to memory buffer.\n", __FUNCTION__); } else { // TODO (remove theTimer entirely. OTCachedKey replaces already.) // I set this timer because the above required a password. But now that master key is working, // the above would flow through even WITHOUT the user typing his passphrase (since master key still // not timed out.) Resulting in THIS timer being reset! Todo: I already shortened this timer to 30 // seconds, but need to phase it down to 0 and then remove it entirely! Master key takes over now! // theTimer.start(); // Note: this isn't the ultimate timer solution. See notes in ReleaseKeyLowLevel. // -------------------- OTLog::vOutput(5, "%s: Success writing EVP_PKEY to memory buffer.\n", __FUNCTION__); OTPayload theData; char * pChar = NULL; // After the below call, pChar will point to the memory buffer where the private key supposedly is, // and lSize will contain the size of that memory. // lSize = BIO_get_mem_data(bmem, &pChar); uint32_t nSize = static_cast<uint32_t>(lSize); if (nSize > 0) { // Set the buffer size in our own memory. theData.SetPayloadSize(nSize); // void * pv = OTPassword::safe_memcpy((static_cast<char*>(const_cast<void*>(theData.GetPayloadPointer()))), // destination theData.GetSize(), // size of destination buffer. pChar, // source nSize); // length of source. // bool bZeroSource=false); // if true, sets the source buffer to zero after copying is done. // ------------------------------------------------ // This base64 encodes the private key data, which // is already encrypted to its passphase as well. // ascKey.SetData(theData); OTLog::vOutput(5, "%s: Success copying private key into memory.\n", __FUNCTION__); bReturnVal = true; } else { OTLog::vError("%s: Failed copying private key into memory.\n", __FUNCTION__); } } return bReturnVal; }
EVP_PKEY * OTAsymmetricKey_OpenSSL::OTAsymmetricKey_OpenSSLPrivdp::InstantiatePrivateKey(OTPasswordData * pPWData/*=NULL*/) { OT_ASSERT(m_pKey == NULL); OT_ASSERT(backlink->m_p_ascKey != NULL); OT_ASSERT(backlink->IsPrivate()); // ------------------------------ EVP_PKEY * pReturnKey = NULL; OTPayload theData; // after base64-decoding the ascii-armored string, the (encrypted) binary will be stored here. // -------------------------------------- // This line base64 decodes the ascii-armored string into binary object theData... // backlink->m_p_ascKey->GetData(theData); // theData now contains binary data, the encrypted private key itself, no longer in text-armoring. // // Note, for future optimization: the ASCII-ARMORING could be used for serialization, but the BIO (still encrypted) // could be used in RAM for this object. Otherwise you just have to do the extra step of ascii-decoding it first to get // the BIO, before being able to instantiate the key itself from that. That final step can't change, but I can remove // the step before it, in most cases, by just storing the BIO itself, instead of the ascii-armored string. Or perhaps // make them both available...hm. // -------------------------------------- // Copy the encrypted binary private key data into an OpenSSL memory BIO... // if (theData.GetSize() > 0) { OpenSSL_BIO keyBio = BIO_new_mem_buf(static_cast<char*>(const_cast<void*>(theData.GetPayloadPointer())), theData.GetSize()); // theData will zeroMemory upon destruction. OT_ASSERT_MSG(NULL != keyBio, "OTAsymmetricKey_OpenSSL::InstantiatePrivateKey: Assert: NULL != keyBio \n"); // -------------------------------------- // Here's thePWData we use if we didn't have anything else: // OTPasswordData thePWData("OTAsymmetricKey_OpenSSL::InstantiatePrivateKey is calling PEM_read_bio_PrivateKey..."); if (NULL == pPWData) pPWData = &thePWData; pReturnKey = PEM_read_bio_PrivateKey( keyBio, NULL, OTAsymmetricKey::GetPasswordCallback(), pPWData ); // Free the BIO and related buffers, filters, etc. backlink->ReleaseKeyLowLevel(); // -------------------------------------- if (NULL != pReturnKey) { m_pKey = pReturnKey; // TODO (remove theTimer entirely. OTCachedKey replaces already.) // I set this timer because the above required a password. But now that master key is working, // the above would flow through even WITHOUT the user typing his passphrase (since master key still // not timed out.) Resulting in THIS timer being reset! Todo: I already shortened this timer to 30 // seconds, but need to phase it down to 0 and then remove it entirely! Master key takes over now! // backlink->m_timer.start(); // Note: this isn't the ultimate timer solution. See notes in ReleaseKeyLowLevel. OTLog::vOutput(4, "%s: Success reading private key from ASCII-armored data.\n\n", __FUNCTION__); // OTLog::vOutput(4, "%s: Success reading private key from ASCII-armored data:\n\n%s\n\n", // __FUNCTION__, m_p_ascKey->Get()); return m_pKey; } } OTLog::vError("%s: Failed reading private key from ASCII-armored data.\n\n", __FUNCTION__); // OTLog::vError("%s: Failed reading private key from ASCII-armored data:\n\n%s\n\n", // __FUNCTION__, m_p_ascKey->Get()); return NULL; }
// Take a public key, theKey (input), and create an armored version of // it into ascKey (output.) // // OpenSSL loaded key ===> ASCII-Armored export of same key. // //static // bool OTAsymmetricKey_OpenSSL::OTAsymmetricKey_OpenSSLPrivdp::ArmorPublicKey(EVP_PKEY & theKey, OTASCIIArmor & ascKey) { bool bReturnVal = false; const char * szFunc = "OTAsymmetricKey_OpenSSL::ArmorPublicKey"; ascKey.Release(); // ---------------------------------------- // Create a new memory buffer on the OpenSSL side OpenSSL_BIO bmem = BIO_new(BIO_s_mem()); OT_ASSERT_MSG(NULL != bmem, "OTAsymmetricKey_OpenSSL::ArmorPublicKey: ASSERT: NULL != bmem"); int64_t lSize = 0; // ---------------------------------------- // write a public key to that buffer, from theKey (parameter.) // int32_t nWriteBio = PEM_write_bio_PUBKEY(bmem, &theKey); if (0 == nWriteBio) { OTLog::vError("%s: Error: Failed writing EVP_PKEY to memory buffer.\n", szFunc); } else { OTLog::vOutput(5, "%s: Success writing EVP_PKEY to memory buffer.\n", szFunc); OTPayload theData; char * pChar = NULL; // After the below call, pChar will point to the memory buffer where the public key // supposedly is, and lSize will contain the size of that memory. // lSize = BIO_get_mem_data(bmem, &pChar); uint32_t nSize = static_cast<uint32_t>(lSize); // todo security, etc. Fix this assumed type conversion. if (nSize > 0) { // Set the buffer size in our own memory. theData.SetPayloadSize(nSize); // void * pv = OTPassword::safe_memcpy((static_cast<char*>(const_cast<void*>(theData.GetPayloadPointer()))), // destination theData.GetSize(), // size of destination buffer. pChar, // source nSize); // length of source. // bool bZeroSource=false); // if true, sets the source buffer to zero after copying is done. // ------------------------------------------------ // This base64 encodes the public key data // ascKey.SetData(theData); OTLog::vOutput(5, "%s: Success copying public key into memory.\n", szFunc); bReturnVal = true; } else { OTLog::vError("%s: Failed copying public key into memory.\n", szFunc); } } return bReturnVal; }
// NOTE: OpenSSL will store the EVP_PKEY inside the X509, and when I get it, // I'm not supposed to destroy the x509 until I destroy the EVP_PKEY FIRST! // (AND it reference-counts.) // Since I want ability to destroy the two, independent of each other, I made // static functions here for copying public and private keys, so I am ALWAYS // working with MY OWN copy of any given key, and not OpenSSL's reference-counted // one. // // Furthermore, BIO_mem_buf doesn't allocate its own memory, but uses the memory // you pass to it. You CANNOT free that memory until you destroy the BIO. // // That's why you see me copying one bio into a payload, before copying it into // the next bio. Todo security: copy it into an OTPassword here, instead of an // OTPayload, which is safer, and more appropriate for a private key. Make sure // OTPassword can accommodate a bit larger size than what it does now. // //static // CALLER must EVP_pkey_free! EVP_PKEY * OTAsymmetricKey_OpenSSL::OTAsymmetricKey_OpenSSLPrivdp::CopyPrivateKey(EVP_PKEY & theKey, OTPasswordData * pPWData/*=NULL*/, OTPassword * pImportPassword/*=NULL*/) { const EVP_CIPHER * pCipher = EVP_des_ede3_cbc(); // todo should this algorithm be hardcoded? // ---------------------------------------- // Create a new memory buffer on the OpenSSL side OpenSSL_BIO bmem = BIO_new(BIO_s_mem()); OT_ASSERT(NULL != bmem); EVP_PKEY * pReturnKey = NULL; // ---------------------------------------- // write a private key to that buffer, from theKey // OTPasswordData thePWDataWrite("OTAsymmetricKey_OpenSSL::CopyPrivateKey is calling PEM_write_bio_PrivateKey..."); // todo optimization: might just remove the password callback here, and just write the private key in the clear, // and then load it up again, saving the encrypt/decrypt step that otherwise occurs, and then as long as we OpenSSL_cleanse // the BIO, then it SHOULD stil be safe, right? // int32_t nWriteBio = false; if (NULL == pImportPassword) nWriteBio = PEM_write_bio_PrivateKey(bmem, &theKey, pCipher, NULL, 0, OTAsymmetricKey::GetPasswordCallback(), NULL == pPWData ? &thePWDataWrite : pPWData); else nWriteBio = PEM_write_bio_PrivateKey(bmem, &theKey, pCipher, NULL, 0, 0, const_cast<void*>(reinterpret_cast<const void*>(pImportPassword->getPassword()))); // ------------------------------------------------------------------------ if (0 == nWriteBio) { OTLog::vError("%s: Failed writing EVP_PKEY to memory buffer.\n", __FUNCTION__); } else { OTLog::vOutput(5, "%s: Success writing EVP_PKEY to memory buffer.\n", __FUNCTION__); char * pChar = NULL; // After the below call, pChar will point to the memory buffer where the private key supposedly is, // and lSize will contain the size of that memory. // const int64_t lSize = BIO_get_mem_data(bmem, &pChar); const uint32_t nSize = static_cast<uint32_t>(lSize); if (nSize > 0) { OTPayload theData; // Set the buffer size in our own memory. theData.SetPayloadSize(nSize); void * pv = OTPassword::safe_memcpy((static_cast<char*>(const_cast<void*>(theData.GetPayloadPointer()))), // destination theData.GetSize(), // size of destination buffer. pChar, // source nSize); // length of source. // bool bZeroSource=false); // if true, sets the source buffer to zero after copying is done. if (NULL != pv) { // ----------------------------------------------- // Next, copy theData's contents into a new BIO_mem_buf, // so OpenSSL can load the key out of it. // OpenSSL_BIO keyBio = BIO_new_mem_buf(static_cast<char*>(const_cast<void*>(theData.GetPayloadPointer())), theData.GetSize()); OT_ASSERT_MSG(NULL != keyBio, "OTAsymmetricKey_OpenSSL::CopyPrivateKey: Assert: NULL != keyBio \n"); // ------------------------------------------- // Next we load up the key from the BIO string into an instantiated key object. // OTPasswordData thePWData("OTAsymmetricKey_OpenSSL::CopyPrivateKey is calling PEM_read_bio_PUBKEY..."); if (NULL == pImportPassword) pReturnKey = PEM_read_bio_PrivateKey( keyBio, NULL, OTAsymmetricKey::GetPasswordCallback(), NULL == pPWData ? &thePWData : pPWData); else pReturnKey = PEM_read_bio_PrivateKey( keyBio, NULL, 0, const_cast<void*>(reinterpret_cast<const void*>(pImportPassword->getPassword()))); // ------------------------------------------- } else OTLog::vError("%s: Error: Failed copying memory from BIO into OTPayload.\n"); // ------------------------------------------- } else { OTLog::vError("%s: Failed copying private key into memory.\n", __FUNCTION__); } } return pReturnKey; }
//static // CALLER must EVP_pkey_free! EVP_PKEY * OTAsymmetricKey_OpenSSL::OTAsymmetricKey_OpenSSLPrivdp::CopyPublicKey(EVP_PKEY & theKey, OTPasswordData * pPWData/*=NULL*/, OTPassword * pImportPassword/*=NULL*/) { // ---------------------------------------- // Create a new memory buffer on the OpenSSL side OpenSSL_BIO bmem = BIO_new(BIO_s_mem()); OT_ASSERT_MSG(NULL != bmem, "OTAsymmetricKey_OpenSSL::CopyPublicKey: ASSERT: NULL != bmem"); EVP_PKEY * pReturnKey = NULL; // ---------------------------------------- // write a public key to that buffer, from theKey (parameter.) // int32_t nWriteBio = PEM_write_bio_PUBKEY(bmem, &theKey); if (0 == nWriteBio) { OTLog::vError("%s: Error: Failed writing EVP_PKEY to memory buffer.\n", __FUNCTION__); } else { OTLog::vOutput(5, "%s: Success writing EVP_PKEY to memory buffer.\n", __FUNCTION__); char * pChar = NULL; // After the below call, pChar will point to the memory buffer where the public key // supposedly is, and lSize will contain the size of that memory. // const int64_t lSize = BIO_get_mem_data(bmem, &pChar); const uint32_t nSize = static_cast<uint32_t>(lSize); if (nSize > 0) { OTPayload theData; // Set the buffer size in our own memory. theData.SetPayloadSize(nSize); void * pv = OTPassword::safe_memcpy((static_cast<char*>(const_cast<void*>(theData.GetPayloadPointer()))), // destination theData.GetSize(), // size of destination buffer. pChar, // source nSize); // length of source. // bool bZeroSource=false); // if true, sets the source buffer to zero after copying is done. if (NULL != pv) { // ----------------------------------------------- // Next, copy theData's contents into a new BIO_mem_buf, // so OpenSSL can load the key out of it. // OpenSSL_BIO keyBio = BIO_new_mem_buf(static_cast<char*>(const_cast<void*>(theData.GetPayloadPointer())), theData.GetSize()); OT_ASSERT_MSG(NULL != keyBio, "OTAsymmetricKey_OpenSSL::CopyPublicKey: Assert: NULL != keyBio \n"); // ------------------------------------------- // Next we load up the key from the BIO string into an instantiated key object. // OTPasswordData thePWData(NULL == pImportPassword ? "Enter your wallet master passphrase. (OTAsymmetricKey_OpenSSL::CopyPublicKey is calling PEM_read_bio_PUBKEY...)" : "Enter the passphrase for your exported Nym."); if (NULL == pImportPassword) pReturnKey = PEM_read_bio_PUBKEY(keyBio, NULL, OTAsymmetricKey::GetPasswordCallback(), NULL == pPWData ? &thePWData : pPWData); else pReturnKey = PEM_read_bio_PUBKEY(keyBio, NULL, 0, pImportPassword); // ------------------------------------------- // We don't need the BIO anymore. // Free the BIO and related buffers, filters, etc. (auto with scope). // } else OTLog::vError("%s: Error: Failed copying memory from BIO into OTPayload.\n"); // ------------------------------------------- } else { OTLog::vError("%s: Failed copying private key into memory.\n", __FUNCTION__); } } return pReturnKey; }