The MD5 message digest algorithm is a hash function that was designed for cryptography but is no longer considered safe for that purpose. This is not to say that it isn’t still useful for other things. It still can take a chunk of data of arbitrary size (it could be 0x1, or it could be the text of Moby Dick) and transform it into a fixed 128-bit chunk of data that appears to be random garbage, but in fact the same input to the function will always produce the same output. Often, you just want the hash of some string or some data and you don’t need or want to import a whole crypto library for such a limited, trivial use.
With that in mind, I’m presenting here my own implementation (available as a gist) of the md5 hash function in one small (180 lines including white space and tests) file with no dependencies (not even Foundation) comprising two extensions.
The first is an extension on String, which allows you to do things like
It looks like this:
Not much there, but it’s worth noting that under the hoodString.UTF8View is a collection of UInt8s, and therefore the String extension can easily invoke the logic contained in the collection extension.
So, this other extension on Collection is constrained to those where Iterator.Element is UInt8. This includes not only String.UTF8View but also Foundation’s Data type, which allows things like this:
The md5 method (really a “computed property”) on the collection is slightly more interesting. It lazily reduces the array of bytes into a single hexadecimal string.
The real logic is contained in the private md5Digest computed property of the collection. It takes the bytes in the collection and returns the array of bytes that make up the hash. It is this result that is then turned into a String in the md5 computed property above.
I’m not going to go into point-by-point detail, but the code closely follows the explanation of md5 presented in RFC 1321 by Ronald Rivest. Within the code, I’ve interspersed snippets of documentation from Wikipedia and RFC 1321 as signposts for the curious to follow. Note: I have intentionally leaned away from writing good, idiomatic, designed-for-clarity Swift by retaining the historical names for variables and functions, which are the heritage of a different era. If you want to understand this code, you will probably want to compare it to somedescriptions of the algorithm, and these will likely use the same names as the original. This code is not necessarily intended to pass a review. 😉
Needless to say, this implementation passes the original test suite provided by RFC 1321.
And that’s all there is to it! Thanks for reading!