The one I prefer, which you've mostly laid out, is: new passwords are entered as bcrypt(pw) and then stored as "B-$result", old passwords are re-hashed as bcrypt(hash) = bcrypt(md5(pw)) and stored as "M-$result", then your auth function works as follows:
def auth(user, pw):
hash = get_hash(user)
if hash starts with "B-":
return hash == bcrypt(pw)
else if hash starts with "M-":
return hash == bcrypt(md5(pw))
else:
# remove this once you've rehashed your entire database
return hash == md5(pw)
The naïve solution is to skip the "B-"/"M-"/"" annotation but if you do that you've introduced a situation where attackers can login to old passwords using md5 leaked from another source.
The one I prefer, which you've mostly laid out, is: new passwords are entered as bcrypt(pw) and then stored as "B-$result", old passwords are re-hashed as bcrypt(hash) = bcrypt(md5(pw)) and stored as "M-$result", then your auth function works as follows:
The naïve solution is to skip the "B-"/"M-"/"" annotation but if you do that you've introduced a situation where attackers can login to old passwords using md5 leaked from another source.