2

Ok, so every now and then you come across problems that you've solved before using various frameworks and libraries and whatnot found on the internet and your problem is solved relatively quick and easy and you also learn why your problem was a problem in the first place.

However, sometimes you come across problems that make absolute 0 sense, and even worse when the solutions make negative sense.

My problem is that I want to take Data and make an MD5 hash out of it.

I find all kinds of solutions but none of them work.

What's really bugging me out actually is how unnecessarily complicated the solutions seem to be for a trivial task as getting an MD5 hash out of anything.

I am trying to use the Crypto and CommonCrypto frameworks by Soffes and they seem fairly easy, right? Right?

Yes!

But why am I still getting the error fatal error: unexpectedly found nil while unwrapping an Optional value?

From what I understand, the data served by myData.md5 in the extension of Crypto by Soffes seem to be "optional". But why?

The code I am trying to execute is:

print(" md5 result: " + String(data: myData.md5, encoding: .utf8)!)

where myData has data in it 100% because after the above line of code, I send that data to a server, and the data exists.

On top of that, printing the count of myData.md5.count by print(String(myData.md5.count)) works perfectly.

So my question is basically: How do I MD5 hash a Data and print it as a string?

Edit:

What I have tried

That works

MD5:ing the string test in a PHP script gives me 098f6bcd4621d373cade4e832627b4f6 and the Swift code "test".md5() also gives me 098f6bcd4621d373cade4e832627b4f6

That doesn't work

Converting the UInt8 byte array from Data.md5() to a string that represents the correct MD5 value.

The different tests I've done are the following:

var hash = ""
for byte in myData.data.md5() {
    hash +=  String(format: "%02x", byte)
}
print("loop = " + hash) //test 1

print("myData.md5().toHexString() = " +  myData.md5().toHexString()) //test 2

print("CryptoSwift.Digest.md5([UInt8](myData)) = " + CryptoSwift.Digest.md5([UInt8](myData)).toHexString()) //test 3

All three tests with the 500 byte test data give me the MD5 value 56f6955d148ad6b6abbc9088b4ae334d while my PHP script gives me 6081d190b3ec6de47a74d34f6316ac6b

Test Sample (64 bytes): Raw data:

FFD8FFE0 00104A46 49460001 01010048 00480000 FFE13572 45786966 00004D4D
002A0000 0008000B 01060003 00000001 00020000 010F0002 00000012 00000092

Test 1, 2 and 3 MD5: 7f0a012239d9fde5a46071640d2d8c83

PHP MD5: 06eb0c71d8839a4ac91ee42c129b8ba3

PHP Code: echo md5($_FILES["file"]["tmp_name"])

jww
  • 97,681
  • 90
  • 411
  • 885
vaid
  • 1,390
  • 12
  • 33
  • You say that `myData` and `myData.md5` are both valid as you get data on server and the count in print statement. Based on only this data, and looking at your line of code, only thing that stands out is the encoding `utf8` you have used to convert it to string. – Swapnil Luktuke Nov 29 '16 at 07:11
  • Found [this SO question](http://stackoverflow.com/questions/24123518/how-to-use-cc-md5-method-in-swift-language) which mentions that the `CC_MD5` function used in the Crypto code does not work in Swift. Although this contradicts with your point about `myData.md5` being non-null. – Swapnil Luktuke Nov 29 '16 at 07:22
  • Yes it is very weird. I am trying CryptoSwift right now and I get same error. – vaid Nov 29 '16 at 07:24
  • When you say `print(String(myData.md5.count))` works perfectly, do you mean it actually shows correct count? How did you validate that. – Swapnil Luktuke Nov 29 '16 at 07:28
  • That's the thing, I get a count of `16`, but how do I know what those 16 bytes are? I can not validate until I can print them, right? – vaid Nov 29 '16 at 07:33
  • What about the encoding. Is it an assumption. The 'string' extension methods in Crypto do use `utf8`. But data extenaitn has no mention of encoding. Are you sure about assuming the encoding to be `utf8` – Swapnil Luktuke Nov 29 '16 at 07:45
  • That's a good question. I tried with ASCII encoding. I get no error, but results are scrambled. – vaid Nov 29 '16 at 08:06
  • I have now tried all encoding types in `String.Encoding` and some values give me jibberish while other give me the error I mentioned earlier. – vaid Nov 29 '16 at 08:19
  • please see the bottom of my question, I have added some tests that I've done – vaid Nov 29 '16 at 11:40
  • Please add a hex or Base 65 representation of the source data (*myData*) so we can reproduce what you're doing. – Codo Nov 29 '16 at 11:45
  • how do I do that? I can paste 64 bytes in the question if you want – vaid Nov 29 '16 at 11:48
  • Yes, paste those 64 bytes (preferrably as hex string). But isn't it 500 bytes? – Codo Nov 29 '16 at 11:49
  • Yes it was 500, I tried 64. Still getting different results on my Swift app and my PHP script regarding `Data().md5()` – vaid Nov 29 '16 at 11:52
  • Then paste all values, the soure bytes, the PHP MD5 result, the Swift MD5 result. We need a consistent set of values to reproduce the problem. – Codo Nov 29 '16 at 11:54
  • @Codo I updated my question. Hopefully it's a sufficient and proper sample. – vaid Nov 29 '16 at 12:07
  • This [page](http://www.fileformat.info/tool/hash.htm?hex=FFD8FFE0+00104A46+49460001+01010048+00480000+FFE13572+45786966+00004D4D+002A0000+0008000B+01060003+00000001+00020000+010F0002+00000012+00000092) thinks your PHP code is wrong. Please add the PHP code as well. – Codo Nov 29 '16 at 12:09
  • Added the PHP code, but on what basis does that page assume that my tests are wrong? My test hash string "test" that I use as well gives me the correct MD5 both on PHP and in Swift. Please see my question where I mention this. – vaid Nov 29 '16 at 12:17
  • @Codo you forgot to remove the spaces, now I get the same result as my PHP script regarding the hex bytes: http://www.fileformat.info/tool/hash.htm?hex=FFD8FFE000104A46494600010101004800480000FFE135724578696600004D4D002A00000008000B010600030000000100020000010F00020000001200000092 – vaid Nov 29 '16 at 12:26
  • Oops, sorry. You're right. According to that website, my PHP script does seem to give the wrong MD5. But I don't get it. Because the teststring `test` has the same MD5 value on both Swift and PHP, while my 64 byte sample has different MD5 on Swift and PHP, WHILE at the same time being correct on that site and also correctly moved to a file on disk properly. – vaid Nov 29 '16 at 12:33
  • `$_FILES["file"]["tmp_name"]` is a string (file name) while 'FFD8FFE0...' is binary data (probably the contents of a JPEG file). You are hashing two different things in PHP and Swift. – Codo Nov 29 '16 at 12:53
  • @Codo From what I know, that is not true as `tmp_name` is PHP's weird way of accessing the data in the file `file` inside a `POST`. However, now that you say that, I will try to save the data first, and then MD5 the saved data using `md5_file`. – vaid Nov 29 '16 at 15:18
  • @Codo you are correct. I was wrong. However, I still have not received any solution as to why I was getting the error saying `fatal error: unexpectedly found nil while unwrapping an Optional value`. I solved that by adding `.toHexString()` – vaid Nov 29 '16 at 15:29

3 Answers3

1

The simple answer to your question is:

String(data: someData, encoding: .utf8)

returns nil if someData is not properly UTF8 encoded data. If you try to unwrap nil like this:

String(data: someDate, encoding: .utf8)!

you get:

fatal error: unexpectedly found nil while unwrapping an Optional value

So at it's core, it's got nothing to do with hashing or crypto.

Both the input and the output of MD5 (or any hash algorithm for that matter) are binary data (and not text or strings). So the output of MD5 is not UTF8 encoded data. Thus why the above String initializer always failed.

If you want to display binary data in your console, you need to convert it to a readable representation. The most common ones are hexadecimal digits or Base 64 encoding.

Note: Some crypto libraries allow you to feed string into their hash functions. They will silently convert the string to a binary representation using some character encoding. If the encodings do not match, the hash values do not match across systems and programming languages. So you better try to understand why they really do in the background.

Codo
  • 75,595
  • 17
  • 168
  • 206
0

I use a library called 'CryptoSwift' for creating hashes, as well as encrypting data before I send it/store it. It's very easy to use.

It can be found here https://github.com/krzyzanowskim/CryptoSwift and you can even install it with CocoaPods by adding pod 'CryptoSwift' to your podfile.

Once installed, hashing a Data object is as simple as calling Data.md5()! It really is that easy. It also supports other hashing algorithms such as SHA.

You can then just print the MD5 object and CryptoSwift will convert it to a String for you.

The full docs on creating digests can be found here: https://github.com/krzyzanowskim/CryptoSwift#calculate-digest

Jacob King
  • 6,025
  • 4
  • 27
  • 45
  • I am trying CryptoSwift right now, I get the same error. – vaid Nov 29 '16 at 07:23
  • Where is the Data that you are trying to hash coming from? If it is an encoded string is it possible to try hashing that String directly? – Jacob King Nov 29 '16 at 07:29
  • The data comes from `PHAssetResourceManager.requestData()` which is stored on an external variable. So my app extracts data from an asset, stores that data inside `myData` which resided OUTSIDE `requestData`, and THEN tries to MD5 `myData`. – vaid Nov 29 '16 at 07:32
  • Could you try to MD5 some data, about 512 bytes, mate? Alternatively, have a look at my code here: https://github.com/aidv/AssetManager/blob/master/AssetManager.swift – vaid Nov 29 '16 at 07:35
  • Check out line 110. It returns nothing, and causes no problems, but when trying to print that value, I get the error in both Crypto and CryptoSwift. – vaid Nov 29 '16 at 07:37
  • Hmm if you still haven't found a solution by the time I take lunch I will pull your code and take a look. – Jacob King Nov 29 '16 at 07:45
  • Please see the bottom of my questions, I've added some tests that I have done – vaid Nov 29 '16 at 11:41
  • Can you fixed this issue ? because I also face this issue from last two days – Vinayak Bhor Apr 11 '19 at 12:17
0

Thanks to Jacob King I tried a much simpler MD5 framework called CryptoSwift.

The user Codo inspired me to look deeper in to my PHP script as he suggested that I am not in fact hashing the content of my data, but instead the filename, which is correct.

The original question however was not about which framework to use or suggestions to as why my app and my PHP script return different MD5 values.

The question was originally about why I get the error

fatal error: unexpectedly found nil while unwrapping an Optional value

at the line of code saying

print(" md5 result: " + String(data: myData.md5, encoding: .utf8)!)

So the answer to that is that I should not try to convert the 16 bytes data output of the MD5() function, but instead call a subfunction of MD5() called toHexString().

So the proper line of code should look like the following:

print("md5 result: " + myData.md5().toHexString())

BONUS

My PHP script now contains the following code:

move_uploaded_file($_FILES["file"]["tmp_name"], $target_dir); //save data to disk
$md5_of_data = md5_file ($target_dir); //get MD5 of saved data

BONUS-BONUS The problem and solution is part of a small framework called AssetManager that I'm working on, which can be found here: https://github.com/aidv/AssetManager

vaid
  • 1,390
  • 12
  • 33