Jeffrey,Thank you very much for your insights and details.I was indeed looking to do HMAC on cipher text, not directly on clear text. I asked the question wrong.Your code using putMessage=true was what I needed, but it's also good to know that ChannelSwitch is the equivalent of Fork.I looked at the crash in the sample that you gave, and started by assuming there was something in HashVerificationFilter that made it send the wrong data to CBC_Encryption; I followed the data through all the hoops all the way down to AESNI_AdvancedProcessBlocks(), until I realized it was on its subkeys parameter that the read violation was occurring. Turns out the key wasn't set:CBC_Mode<AES>::Encryption encryptor; encryptor.SetKeyWithIV(keys. data() + 0 /*key*/, 16, keys.data() + 16 /*iv*/, 16); CBC_Mode<AES>::Decryption decryptor; encryptor.SetKeyWithIV(keys. data() + 0 /*key*/, 16, keys.data() + 16 /*iv*/, 16); //needs to be decryptor.SetKeyWithIV....
Quite an easy mistake to make with boilerplate code, and it's a bit disconcerting that the decryption class wouldn't at a minimum make a basic check that the key was set by the client, before proceeding to use it. >You will *always* have to touch the data twice. If I understand you correctly, you are saying each block of data/chunk needs to be read twice (once in memory, from there), once as input to the encryption cipher, and once as input to the hmac? That makes sense. I was referring to not reading it off the disk twice, e.g. in the case of doing hmac on plain text from a file, not going twice through the input stream (first to encrypt, then resetting the stream, and reading through it again for the mac). putMessage=true, or Fork/ChannelSwitch help with that. By the way, you are linking to wiki pages (Authenticated_Encryption, ChannelSwitch), that I see no way to get to from cryptopp main wiki pagehttp://www.cryptopp.com/wiki/Main_Page I had no clue they existed, and it seems there are many more. I see no table of contents, no index and no search, how does one get to the documentation without knowing exact URL's of specific pages? The source code is incredibly sparsely documented, is there annotated code elsewhere? Am I looking in the wrong place? How is one supposed to know what putMessage=true means in HashFilter()? Or the fact that HashVerificationFilter() takes in the message + hash at the end if you use HashVerificationFilter::HASH_AT_END and it outputs just the message with the hash stripped if you use HashVerificationFilter::PUT_MESSAGE?Or the fact that ChannelSwitch exists and what it's used for? People just browse through source code, look at classes and go "there it is! that's what I'm looking for!"? :) One more question, how does HashVerificationFilter check the hash, does it set a status somewhere? Because it doesn't throw an exception or anything for the wrong hash.Thank you very much for your time. It's truly appreciated. From: Jeffrey Walton <[email protected]> To: [email protected] Cc: [email protected] Sent: Saturday, April 4, 2015 11:13 PM Subject: Re: processing same stream in parallel > Unfortunately, it appears to have a bug. The unfortunate thing is the bug > appears to be in the Crypto++ library (modulo me doing something dumb). Forgot to mention... The issue appears to lie somewhere in the HashVerificationFilter. On Saturday, April 4, 2015 at 10:54:42 PM UTC-4, Jeffrey Walton wrote: > I need to encrypt a stream (with a symmetric algorithm) > and compute its HMAC at the same time. You have to be careful here. Combining a confidentiality mode with an authenticity mode can cause some trouble if you combine them incorrectly. Its probably best to use a mode that does it for you, like EAX, CCM or GCM mode. Getting authenticated encryption wrong is easy. SSL/TLS and SSH got them wrong, and they've been dealing with the associated bugs for decades. IPSec got it right with Encrypt-Then-Authenticate. To have a chance at getting it right, you have to encrypt the data first and then MAC it. That's what IPSec does. See http://www.cryptopp.com/wiki/ Authenticated_Encryption - private for more details. > In Botan you do it with the Fork class. Crypto++ has a ChannelSwitch which allows you to take data and send it to multiple sinks. But I don't think that's what you want in the is case. If you were doing authenticated encryption using Encrypt-And-Authenticate (like SSH), then you could use the ChannelSwitch. > Is there an easy way to do that in crypto++, without going through it twice Yes, just pipeline the data. Have the output of the encryptor feed the HMAC. You can read more about pipelining at http://www.cryptopp.com/wiki/ Pipeline - private. > without going through it twice You will *always* have to touch the data twice. The only mode that supports touching the data once is CWC mode. Its patented, so everything else touches the data twice. With that out of the way, below is a program that kind of does what you want. It streams plaintext data through and encryptor and then a HMAC. Unfortunately, it appears to have a bug. The unfortunate thing is the bug appears to be in the Crypto++ library (modulo me doing something dumb). I have not analyzed the stack trace, yet. I'm tossing it out there in case someone else has time to look at it and develop a patch. ******** static const int DIGEST_SIZE = HMAC<SHA256>::DIGESTSIZE; SecByteBlock keys(16 /*AES key*/ + 16 /*AES IV*/ + 16 /*HMAC key*/); OS_GenerateRandomBlock(false, keys, keys.size()); CBC_Mode<AES>::Encryption encryptor; encryptor.SetKeyWithIV(keys. data() + 0 /*key*/, 16, keys.data() + 16 /*iv*/, 16); CBC_Mode<AES>::Decryption decryptor; encryptor.SetKeyWithIV(keys. data() + 0 /*key*/, 16, keys.data() + 16 /*iv*/, 16); HMAC<SHA256> hmac1; hmac1.SetKey(keys.data() + 32, 16); HMAC<SHA256> hmac2; hmac2.SetKey(keys.data() + 32, 16); HexEncoder encoder(new FileSink(cout)); cout << "AES key: "; encoder.Put(keys.data() + 0, 16); encoder.MessageEnd(); cout << endl; cout << "AES IV: "; encoder.Put(keys.data() + 16, 16); encoder.MessageEnd(); cout << endl; cout << "HMAC key: "; encoder.Put(keys.data() + 32, 16); encoder.MessageEnd(); cout << endl; string plain = "Now is the time for all good men to come to the aide of their country"; string cipher, recover; cout << plain << endl; // Encrypt with HMAC StringSource ss1(plain, true /*pumpAll*/, new StreamTransformationFilter( encryptor, new HashFilter(hmac1, new StringSink(cipher), true /*putMessage*/, DIGEST_SIZE))); cout << "Ciphertext+MAC: "; encoder.Put((byte*)cipher. data(), cipher.size()); encoder.MessageEnd(); cout << endl; // Decrypt with HMAC verification StringSource ss2(cipher, true /*pumpAll*/, new HashVerificationFilter(hmac2, new StreamTransformationFilter( decryptor, new StringSink(recover)), HASH_AT_END | PUT_MESSAGE, DIGEST_SIZE)); cout << recover << endl; -- -- You received this message because you are subscribed to the "Crypto++ Users" Google Group. To unsubscribe, send an email to [email protected]. More information about Crypto++ and this group is available at http://www.cryptopp.com. --- You received this message because you are subscribed to the Google Groups "Crypto++ Users" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout . -- -- You received this message because you are subscribed to the "Crypto++ Users" Google Group. To unsubscribe, send an email to [email protected]. More information about Crypto++ and this group is available at http://www.cryptopp.com. --- You received this message because you are subscribed to the Google Groups "Crypto++ Users" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
