To be fair to Dave, in terms of homebrew password security this is one of the better cases as all it just a little obsfuscation (and really not much) masking
hash = SHA1(salt + MD5'(Password)) where
MD5' does a reversible swap of the order of the bytes of the MD5 hash. Now the username/time/random/crypt-part is just used to generate a salt, and the only requirement we have of salts is that they just need to have a very good likelihood of uniqueness; so while its over-complicated there's really no use talking about it.
hash = sha1($salt+md5'($password)). Rearranging the MD5 adds no security (
ccddeeff8899aabb0011223344556677) the swap does not prevent you from using md5 rainbow tables after (e.g., look at the code and reverse the swap). However, the presence of the unique salt makes the rainbow tables infeasible.
Now to be critical, this boils down to a simple cryptographic hash function applied twice; which is better than storing plaintext password (see plaintext offenders ), and better than storing an unsalted hash (see linkedin leak ). However, in the age of cheap massively-parallel GPUs, this is too weak for modern use. Anyone with a bit of general-purpose GPU programming experience that got on the live server somehow (to obtain the hashes with their salt) can likely see his source code, try out their own password as a test case and then can brute-force any particular password at a rate of billions of attempts per second per GPU.
So if any user is using a password on a list of a million or so previously leaked password (say from linkedin), the attacker can near instantaneously crack it. If some user's password is a random 8-letter from the char set A-Za-z0-9; it would take about 60 hours on average to break per GPU (so if you had 60 GPUs; would take 1 hour). Using common cracking techniques exploiting forms of common passwords could speed it up dramatically. Its also worth noting that since
$password passes through a 128-bit MD5 hashing function, there is absolutely no benefit in using more than 128-bits of entropy in the passphrase (though to be fair that's a very secure password; like a 10 word diceware passphrase or random 22 character alphanumeric password).
Really, you should be using iterated cryptographic hash functions; that is something like bcrypt or PBKDF that slows down the brute-force rate of attackers by a large constant factor (say 105) (so instead of 60 hours to crack a random 8-char password from a 62-char set (A-Za-z0-9); it takes 6 million hours (~700 years to likely be broken) with a single GPU, and this gets better with stronger passwords (e.g., a 10 char password would take ~3 million years; so even with a million GPUs it would take 3 years). So a little keystrengthening moves relatively weak password (8 random chars from 62-charset) out of the range of feasibility of being cracked by an attacker. For more on the upper limits of using a simple keystrengthened password see this answer.
Collision Attacks vs Pre-Image Attacks (or Why Collision Attacks on a hash function are irrelevant for password hashing)
KeithS's answer, while it gives good advice (use bcrypt and not a simple cryptographic hash function to hash your password) originally criticized MD5 and SHA1 for the wrong rationale (don't use MD5 as its vulnerable to collision attacks). There's a subtle difference between pre-image and collision attacks and the arguing in the comments was too condensed, so I am elaborating here. I highly suggest reading the wikipedia article on pre-image attacks.
A pre-image attack says given a specific 128-bit hash written in hexadecimal:
h=ad2baf26a87795b3c8a8366a08b44112, a specific hash function
H, please find any message
m such that
h=H(m); note that there are N=2128 different distinct hashes. Now if your cryptographic hash function is not broken, each bit in your hash has a 50% chance of being 0 or 1 for a random message m. Then I will need on average to generate hashes for roughly N=2128 hashes before I am lucky enough to find any message
m such that
h=H(m) (this message may not be the same input that was used to originally generate the hash -- but this still falls under the category of 'pre-image' attack).
A collision attack says find me any two messages
m2 such that
H(m1) = H(m2). Note that
H(m1)) are all free to change. This case is a much easier problem since if I generate M hashes for M different messages, I am not just comparing the M messages to one specific hash (so have M chances of a finding a collision), now I have M*(M-1)/2 pairs of hashes, so roughly M^2 chances of having a collision. So in this case, I will need roughly to generate roughly sqrt(N)~264 hashes before its likely that one of them will collide with another one on an ideal 128-bit hash.
Let's look at the two types of birthday problems. The collision problem translates into the common birthday "paradox"; how many people do you need in a room before its quite that two people share a birthday with N=365 days in a year. The answer is a paradox as you only need about sqrt(N) ~ 23 people before its likely that two people share a birthday (as with 23 people in a room you have 253 different pairs of people that could match). (I do know that sqrt(365) != 23: I am using approximate math not focusing on insignificant constant factors. Re-doing the calculation with sqrt(365) ~ 19 people in the room then
P(two share birthday) = 19! * comb(365,19)/365**n = 37.9%, which while not strictly 50% still means its fairly likely to happen). Note for the collision birthday problem, you can't have N+1=366 people in a room and there be a chance of not having a collision (ignoring leap days); at best the first 365 people had different birthdays and the last person generated a collision.
The pre-image problem is a very different question, how many people do I need in a room before it is quite likely that someone has one specific birthday (say B=December 18th). In this case, I need roughly N ~ 365 people before its likely to happen. E.g., with 365 people in the room you have a
P(somebody has birthday B) = 1 - (1 - 1/365)^(# people) so for
# people = 365 you have a 63% chance that someone's birthday will be some fixed date B. In this case, you can easily imagine having any number of people being in a room and there being no one who has a birthday on one specific date. (Say you only invited people into the room if there birthday was not a given date; there's no limit to the number of people you could invite).
When a hash function like MD5/SHA1 is broken for collision attacks that means you can generate a collision in less work than the brute force time of sqrt(N) ~ 2numbits/2. For MD5 it only takes about ~ 2^24 time to generate a collision; for SHA1 it takes ~ 2^61 time. That means collision attacks on MD5 are extremely easy to make; but practical attacks on SHA1 are still difficult. But collision attacks only matter if you don't care what hash you are trying to match. These collision attacks are quite relevant for some applications like cryptographically signing messages to ensure message integrity, so beware of using MD5/SHA1 in those cases. However, when you have a unique salt, and a specific hash you are trying to match to authenticate, collision attacks do not matter.