// Lucre step 2 (client generates coin request) // nDenomination must be one of the denominations supported by the mint. // sets m_nTokenCount and populates the maps with prototokens (in ASCII-armored format.) bool OTToken::GenerateTokenRequest(const OTPseudonym & theNym, OTMint & theMint, long lDenomination, int nTokenCount/*=OTToken::nMinimumPrototokenCount*/) { // OTLog::vError("%s <bank public info> <coin request private output file> <coin request public output file>\n", argv[0]); if (OTToken::blankToken != m_State) { OTLog::Error("Blank token expected in OTToken::GenerateTokenRequest\n"); return false; } // We are supposed to set these values here. // The server actually sets them again, for security reasons. // But we should still set them since server may choose to reject the request. SetSeriesAndExpiration(theMint.GetSeries(), theMint.GetValidFrom(), theMint.GetValidTo()); SetDumper(stderr); BIO *bioBank = BIO_new(BIO_s_mem()); // Input. We must supply the bank's public lucre info BIO *bioCoin = BIO_new(BIO_s_mem()); // These two are output. We must write these bios, after BIO *bioPublicCoin = BIO_new(BIO_s_mem()); // the operation, back into some form we can use // This version base64-DECODES the ascii-armored string passed in, // and then sets the decoded plaintext string onto the string. //OTString::OTString(const OTASCIIArmor & strValue) OTASCIIArmor ascPublicMint; theMint.GetPublic(ascPublicMint, lDenomination); // OTLog::vError("DEBUG: OTToken public asc: \n%s\n", ascPublicMint.Get()); OTString strPublicMint(ascPublicMint); // OTLog::vError("DEBUG: OTToken public str: \n%s\n", strPublicMint.Get()); // Get the bank's public key (now decoded in strPublicMint) // and put it into bioBank so we can use it with Lucre. BIO_puts(bioBank, strPublicMint.Get()); // Instantiate a PublicBank (Lucre) object. // We will use it to generate all the prototokens in the loop below. PublicBank bank; bank.ReadBIO(bioBank); Release(); const int nFinalTokenCount = (nTokenCount < OTToken::nMinimumPrototokenCount) ? OTToken::nMinimumPrototokenCount : nTokenCount; // Token count is actually 1 (always) with Lucre, although this lib has potential to work with // multiple proto-tokens, you can see this loop as though it always executes just once. for (int i = 0; i < nFinalTokenCount; i++) { CoinRequest req(bank); // write the private coin request to BIO req.WriteBIO(bioCoin); // write the public coin request to BIO ((PublicCoinRequest *)&req)->WriteBIO(bioPublicCoin); // Convert the two bios to our format char privateCoinBuffer[4096], publicCoinBuffer[4096]; // todo stop hardcoding these string lengths int privatecoinLen = BIO_read(bioCoin, privateCoinBuffer, 4000); // cutting it a little short on purpose, with the buffer. Just makes me feel more comfortable for some reason. int publiccoinLen = BIO_read(bioPublicCoin, publicCoinBuffer, 4000); if (privatecoinLen && publiccoinLen) { // With this, we have the Lucre public and private bank info converted to OTStrings OTString strPublicCoin; strPublicCoin.Set(publicCoinBuffer, publiccoinLen); OTString strPrivateCoin; strPrivateCoin.Set(privateCoinBuffer, privatecoinLen); OTASCIIArmor * pArmoredPublic = new OTASCIIArmor(strPublicCoin); OTASCIIArmor * pArmoredPrivate = new OTASCIIArmor(); OT_ASSERT_MSG(((NULL != pArmoredPublic) && (NULL != pArmoredPrivate)), "ERROR: Unable to allocate memory in OTToken::GenerateTokenRequest\n"); // Change the state. It's no longer a blank token, but a prototoken. m_State = OTToken::protoToken; // Seal the private coin info up into an encrypted Envelope // and set it onto pArmoredPrivate (which was just added to our internal map, above.) OTEnvelope theEnvelope; theEnvelope.Seal(theNym, strPrivateCoin); // Todo check the return values on these two functions theEnvelope.GetAsciiArmoredData(*pArmoredPrivate); m_mapPublic[i] = pArmoredPublic; m_mapPrivate[i] = pArmoredPrivate; m_nTokenCount = nFinalTokenCount; SetDenomination(lDenomination); } else { // Error condition } // Free the Private and Public coins and allocate them fresh, for the next iteration of the loop. BIO_free_all(bioCoin); BIO_free_all(bioPublicCoin); bioCoin = BIO_new(BIO_s_mem()); bioPublicCoin = BIO_new(BIO_s_mem()); } // Cleanup openssl resources. BIO_free_all(bioBank); BIO_free_all(bioCoin); BIO_free_all(bioPublicCoin); return true; }
// **** VERIFY THE TOKEN WHEN REDEEMED AT THE SERVER // Lucre step 5: token verifies when it is redeemed by merchant. // IMPORTANT: while stored on the client side, the tokens are // encrypted to the client side nym. But when he redeems them to // the server, he re-encrypts them first to the SERVER's public nym. // So by the time it comes to verify, we are opening this envelope // with the Server's Nym. bool OTToken::VerifyToken(OTPseudonym & theNotary, OTMint & theMint) { //OTLog::vError("%s <bank info> <coin>\n",argv[0]); SetDumper(stderr); if (OTToken::spendableToken != m_State) { OTLog::Error("Expected spendable token in OTToken::VerifyToken\n"); return false; } // load the bank and coin info into the bios // The Mint private info is encrypted in m_ascPrivate. So I need to extract that // first before I can use it. OTEnvelope theEnvelope(m_ascSpendable); OTString strContents; // output from opening the envelope. // Decrypt the Envelope into strContents if (!theEnvelope.Open(theNotary, strContents)) return false; // todo log error, etc. // Verify that the series is correct... // (Otherwise, someone passed us the wrong Mint and the // thing won't verify anyway, since we'd have the wrong keys.) if (m_nSeries != theMint.GetSeries() || // Someone might, however, in a clever attack, choose to leave // the series intact, but change the expiration dates, so that the // mint keys continue to work properly for this token, but then // when we check the date, it APPEARS good, when really the dates // were altered! To prevent this, we explicitly verify the series // information on the token against the same info on the mint, // BEFORE checking the date. m_VALID_FROM != theMint.GetValidFrom() || m_VALID_TO != theMint.GetValidTo()) { OTLog::vOutput(0, "Token series information doesn't match Mint series information!\n"); return false; } // Verify whether token has expired...expiration date is validated here. // We know the series is correct or the key wouldn't verify below... and // we know that the dates are correct because we compared them against the // mint of that series above. So now we just make sure that the CURRENT date // and time is within the range described on the token. if (!VerifyCurrentDate()) { OTLog::Output(0, "Token is expired!\n"); return false; } // pass the cleartext Lucre spendable coin data to the Mint to be verified. if (theMint.VerifyToken(theNotary, strContents, GetDenomination())) // Here's the boolean output: coin is verified! { OTLog::Output(0, "Token verified!\n"); return true; } else { OTLog::Output(0, "Bad coin!\n"); return false; } }