Is this a secure implementation of password reset email? I am redesigning a password reset email mechanism because the existing implementation scares the hell out of me. My goal is to generate reset codes that are:


Expired
Tamper Resistant
Single Use
Resistant to replay attacks


The format of the reset code I have come up with is:

B64( B64( iv ) || B64( E( uid || timestamp ) ) )


Where B64 is base64 encoding and E is for symmetric encryption. The iv is the initialization vector used for encryption (randomly generated), and is in the clear. The || indicates concatenation and I actually use the string "||" as the separator. The uid is an opaque identifier of the user so I can look them up in the database.

The timestamp allows for a code to be expired.

I am using AES 256 in Galois Counter Mode which should provide protection from modified ciphertext attacks.

The encryption key is stored on the server, and used to perform the encryption.

The iv has a dual use. When the reset code is generated the iv is stored with the user in the database. This is used to enforce single-use of a reset code (it gets cleared when a reset code is processed) as well as prevent use of reset codes generated previously. If the iv in the database does not match the iv used to decrypt the reset code, the request is refused. This may also help with security in the event that the encryption key on the server is somehow compromised, as the attacker has to inject the iv into the database as well to make it work. Of course if the attacker is on the host we are pretty much toast...

Are there any weaknesses in this design I am not seeing, or does it needs additional improvement?
What you describe looks secure to me, but excessively complicated.  The following simpler procedure should suffice:

When a password reset token is requested:


Generate a secure random token and save it (or a cryptographic hash of it) in the database, along with a timestamp.  Overwrite the previous token, if any.
Send the token to the user by e-mail (suitably encoded).


When the user submits a token:


Check that it matches the token in the database, and that the corresponding timestamp is not too old.


The only part of the process which needs to involve any cryptography at all is generating the random token, and optionally hashing it.  You can usually offload the former task to the OS; on Unixish systems, for example, you can read the token from /dev/urandom.  Alternatively, if you're using a decent crypto library, it should provide a routine for generating secure random numbers (which may call the OS random number generator internally).



Ps. The one situation where you might want to go with a more crypto-heavy solution would be if you didn't have access to a convenient database for storing the tokens.  In that case, you could, for example, compute the token as MACK(userid || timestamp), where MACK is a message authentication code keyed by the secret key K, and have the user submit the timestamp along with the token.  (You could also include a random number in the MAC input, but that's not really necessary &mdash; without the key K, an attacker won't be able to forge the MAC.)  However, since you do have a database available, this doesn't really apply.