TLDR: Insecure crypto isn't even safe if you can't see the encrypted value.
The other posts are pretty good about explaining a lot of reasons why you shouldn't write your own crypto, in an environment where the attacker can see the encrypted value, but they miss something really important: you also shouldn't write your own crypto when the attacker can't see the encrypted message.
There's this thing called a "side channel." Side channels are (usually1) unintended things that leak information about what your application is doing. For example, maybe it takes more CPU cycles - and therefore more time - to compare a password that is partially correct against the "encrypted"2 value. This would let an attacker accelerate a brute force attack to slowly learn the correct password.
Let's take a naive example. Let's pretend it takes 1 second to test a single character of the submitted password against the value stored in the database. Pretend the correct password is 8 characters long, and an invalid password is rejected at the first incorrect result. The algorithm might look something like:
boolean encrypt_password(string password) {
if(not isascii(password) ) { return false; } // ERROR!
string result;
foreach(char c : password) {
result += daves_magic_that_takes_1s(c)
}
return true;
}
boolean is_correct_password(input, pw_from_db) {
if(input.length != pw_from_db.length) { return false }
foreach(char c_in, c_db : input, password) {
c_in = daves_magic_that_takes_1s(c_in)
if(c_in != c_db){ return false}
}
return true; // valid password!
}
Now, let's imagine the valid password is "password" and the attacker tries the input "a". This fails, because the passwords are the wrong length. The attacker may randomly try various passwords. Every incorrect password longer or shorter than "password" takes less than a second to process. Let's say they soon try "12345678". "12345678" is the same length as "password" so it takes one second to process. The timing is different and the attacker notices. They try several more times to verify, and it's consistent.
The attacker now tries several 8 character passwords. They all take 1 second. The attacker has discovered a side channel that tells them that the valid password is probably 8 characters long. Now they need to determine which 8 character password is the right one.
The attacker starts randomly trying 8 character passwords. Eventually, they try "p2345678" and notice that this takes 2 seconds to complete. They test a bunch and discover that all attempts that start with "p" take 2 seconds to complete. The attacker guesses that the algorithm has a side channel that tells them how many characters they have correct.
Now, instead of having to attempt all 96^8 passwords to brute force the valid one, the attacker only has to try 96*8 passwords3. Depending upon how many passwords can be tested in parallel, they probably can successfully brute force the password in very reasonable time. This is great for the attacker! And it's terrible for the security of your system.4
How do we protect against timing side channels? We guarantee that all operations where timing would leak sensitive information MUST always take the same amount of time to execute.
This may look like a very simple example. It has happened in the wild. Searching NVD for "timing side channel" will get you lots of real world vulnerabilities that all produce the same kind of results, allowing an attacker to learn secret information to which they are not authorized. By definition, if all operations take the same amount of time regardless of the input, then the amount of time something takes doesn't tell you anything about the input.
In the real world, side channels are incredibly easy to introduce. Dave probably hasn't even heard of them, and is probably a good engineer concerned with the performance of his system - which is actually an anti-pattern for protecting against side channels. Dave's algorithm may very well have both obvious and subtle side channels that he'll never discover, but that researchers and attackers know to look for, and can pretty easily write automated tests to detect.5
So, just because you can't see the crypto doesn't mean that you can't see the side effects of bad crypto, and use those side effects to learn the protected secret.
Endnotes
1: Well, if you're an intelligence agent or a good newspaper journalist, you probably intentionally set up side channels so that you can communicate with your agents/sources without "the enemy" knowing. Similarly, if you're devious, you might create a crypto protocol with a side channel in it with the intention of leaking secret information.
2: Since we can and should always assume custom crypto is insecure (for the reasons others have mentioned in this thread and more), we probably shouldn't call the use of custom crypto algorithms "encryption" or "decryption"... maybe "insecure encryption" or "broken decryption"...
3: I ignore brute force attacks succeeding, on average, when the attacker has tried 50% + 1 passwords, for the sake of simplicity of description. I'm focusing on side channels, not on brute force attacks. I also gloss over the math of a brute force attack, since that's also tangential to the main topic; some Google-Fu on behalf of the reader should locate plenty of resources that go deep into the details.
4: "1 second is way too slow", right? No real world system could be checked for real world timing side channels over the internet, right? Wrong. I don't have the references on hand, but there was research a number of years a go showing that you can statistically test for timing on the order of milliseconds over HTTP transactions.
5 In fact, I'd be willing to bet that there are either frameworks or existing tools (most likely both) that you can use to test your application for obvious side channels, if you were to exercise your Google-Fu.