- Is it secure, in this case, to employ a sign-then-encrypt method? I've read several opinions on this and walked away more confused than when I started.
First of all, given that your sample code is using HMAC, not digital signatures, I assume you mean MAC-then-encrypt.
The generally safe way to combine a MAC and encryption is encrypt-then-MAC: first encrypt the message, then compute a MAC on the ciphertext (including the IV and any associated data you may have!). When decrypting, first verify the MAC, and only then try to decrypt the message.
Encrypt-then-MAC is safe because it guarantees that the MAC and the cipher each only need to do their own job: when encrypting, the MAC only receives input that has already been encrypted, so it cannot accidentally leak information about the plaintext; when decrypting, the cipher only receives input that has already been authenticated by the MAC, so it doesn't need to worry about securely handling forged or modified messages.
However, MAC-then-encrypt (or even MAC-and-encrypt) can be safe if the MAC and the cipher are chosen appropriately. In particular, a sufficient condition for the security of MAC-then-encrypt is that the cipher mode must be length-preserving — that is, the ciphertext (not counting the IV) must be exactly as long as the input plaintext.
The CFB, OFB and CTR modes are all length-preserving, but CBC is not (unless combined with a modification known as "ciphertext stealing"). In particular, because normal CBC mode requires messages to be padded to an integer number of cipher blocks, it is often vulnerable to padding oracle attacks unless protected with a MAC applied after encryption (and thus verified before decryption). Thus, you should not use CBC mode with MAC-then-encrypt.
- I've read that cbc-mode should be used when employing a sign-then-encrypt method. Is this the default mode for CryptoJS.AES.encrypt()?
That's hard to say, and may depend on which version of CryptoJS you're using. This page says the default is OFB mode. This page (for CryptoJS v3) says it's CFB. Both of those appear to be unofficial third-party sources, but I haven't been able to find any comprehensive official documentation for this library. Apparently it's "still a bit sparse." :(
In any case, as noted above, you should not use CBC mode with MAC-then-encrypt. If you want to use CBC, apply the MAC after encryption.
- CryptoJS.AES seems pretty generic. Is this defaulting to AES256? Or is that something that I need to explicitly declare in my code?
See previous answer. According to the linked Google Groups post (which may or may not be reliable, of course), the library can handle AES-128, AES-192 or AES-256, presumably depending on the size of the key you pass in. If you pass in just a password, I have no idea which AES variant it'll choose.
(I'd also like to take this opportunity to reiterate my dislike of poorly documented crypto libraries. It doesn't matter how well your library is coded, if nobody can tell what it's supposed to be doing and how to use it correctly and safely.)
- When passing a string to CryptoJS.AES.encrypt() I've read that it automagically generates a key and and iv 'behind the scenes'. Would it be more secure to generate my own key and iv, or should I just let CryptoJS handle that?
It looks like if you pass the
encrypt() method a plain string as the key, it assumes that this string is a user-supplied password, and applies a password-based key derivation function like PBKDF2 on it. Such functions are deliberately designed to be slow, in order to resist brute-force password cracking. Thus, whenever possible, it's more efficient to call the key derivation function only once yourself, and to store the raw AES and/or HMAC key that it outputs.
You almost certainly don't have any reason to supply your own IV; any decent crypto library should be able to generate perfectly good random IVs for you.
- Is there more security to be had by employing a sign-encrypt-sign method, or is that just adding complexity to my app logic?
In general, there's no point in doing MAC-encrypt-MAC instead of plain encrypt-then-MAC. That said, it shouldn't hurt either (beyond wasting a few CPU cycles, and increasing your ciphertext length a little), and it might protect you against some implementation mistakes (like forgetting to include the IV in the outer MAC input).
However, what you really should do, if at all possible, is use an integrated authenticated encryption mode like SIV (my current personal favorite) or GCM (or even the new GCM-SIV mode that combines them). Those encryption modes come with message authentication built in, and take care of combining the encryption and the authentication safely and correctly.
(For actual digital signatures, sign-encrypt-sign might sometimes be useful, since the double signing protects against some attacks where e.g. the attacker takes a valid encrypt-then-signed message, strips off the signature and substitutes their own, or where a malicious recipient decrypts a signed-then-encrypted message and resends it to someone else, keeping the original signature. In most cases, however, there are more effective ways to avoid such attacks. See e.g. this earlier question for more details.)