1

I want to calculate hash of an image, first I convert image to data and then with help of this function I will calculate hash of image file (Data), but the generated hash doesn't match with online generator and other language convertors like (Java), even I tried other libraries but i get same result, I think while Im converting to Data something happend to my file so hash doesn't match with other convertors.

but when i calculate a plain text hash, it matches with all online convertor and other languages convertor but its not the same with Image?

generated hash in terminal is different enter image description here

thanks for any help

   func md5(url: URL) {
          let bufferSize = 1024*1024
          do {
            let file = try FileHandle.init(forReadingFrom: url)
              defer {
                  file.closeFile()
              }

              var context = CC_MD5_CTX.init()
              CC_MD5_Init(&context)
              while case let data = file.readData(ofLength: bufferSize), data.count > 0 {
                  data.withUnsafeBytes { (poiner) -> Void in
                      _ = CC_MD5_Update(&context, poiner, CC_LONG(data.count))
                  }
              }

                           // Calculate the MD5 summary
              var digest = Data(count: Int(CC_MD5_DIGEST_LENGTH))
              digest.withUnsafeMutableBytes { (pointer) -> Void in
                  _ = CC_MD5_Final(pointer, &context)
              }
              let result = digest.map { (byte) -> String in
                  String.init(format: "%02hhx", byte)
              }.joined()
              print("result: \(result)")
          } catch let error as Error {
              print("calculation error: \(error.localizedDescription)") // Where is the try, where is the error?
          }
      }
Mahdi
  • 94
  • 10
  • 1
    Should work - are you sure you have correct file? – skaak Mar 08 '21 at 13:00
  • Yes I convert a png image to Data and calculate hash with 2 different library and I will get same result, but online converter and terminal I will receive different hash – Mahdi Mar 08 '21 at 13:10
  • 1
    Show how you convert png to data. Note png != data ... there are comments and other metadata that may differ. This should work if url = original png – skaak Mar 08 '21 at 13:27
  • let image = UIImage(named: "aaaaaaaaa")?.pngData(). but I didnt get what you, about url – Mahdi Mar 08 '21 at 13:30
  • I mean, in your code if you call ```md5 ( url : xxx )``` with xxx the original image it should work. That ```pngData()``` will yield data that omits or adds metadata and so != the file's data which is what the other md5's operate on. – skaak Mar 08 '21 at 13:33
  • I saved image in filemanager(which i convert to data for saving) and generate md5 based on url but still get same result. is there any other way? – Mahdi Mar 08 '21 at 13:36
  • I checked your code on some arbitrary file and it works - md5 calculates and prints correctly. Here also I think the trouble lies in the conversion or saving so if you show that code maybe there is something funny there. Anyhow, to get this to work, do not operate on the image after you've done ```UIImage( named : a ).pngData``` as that implies a conversion that, although the image is the same, is not exactly the same as the data stored in the file. Either check the image file directly or you have to do it differently. But the code works fine! – skaak Mar 08 '21 at 13:39
  • No I dont I work on UIImage( named : "a" ).pngData directly but I didnt find anyway to calculate md5 directly on png image with casting it to Data – Mahdi Mar 08 '21 at 13:43
  • How do you work on image? Do you have it in assets or do you download? Whichever way, you need to get its url and pass that to your md5 func. I think it is closer than you think. – skaak Mar 08 '21 at 13:44
  • for testing it is from asset – Mahdi Mar 08 '21 at 13:46
  • Have a look at this https://stackoverflow.com/questions/21769092/can-i-get-a-nsurl-from-an-xcassets-bundle towards the end is a way to get your image's URL using Swift 5. Use that and see how it goes. – skaak Mar 08 '21 at 13:53
  • I tried those solution and still get the same result, because in all of those code it will convert to Data than save on disk – Mahdi Mar 08 '21 at 13:58
  • Just a little try (it "failed" in my end), but: do the test by comparing `UIImage("a").pngData()` (from an image in xcdataassets) AND the same image but in bundle, with `Bundle.main.url(forResource:withExtension:)` and `Data(contentsOf:)`, the data I got weren't the same, explaining why it failed on your tests. Because it's a wrong test, your first data is wrong. – Larme Mar 08 '21 at 14:09
  • thanks for your reply, I tried same image on online md5 generator and the hash match with the same one terminal, but for me It was different when i read it from url, bundle or directly. the fun part is that a same plain text md5 hash match on every where – Mahdi Mar 08 '21 at 14:14
  • But compare the initial `data` from bundle, url or directly, you'll see. If the starting point, the data to hash is different, why should the result be the same? You can print the data (at least, start/end, and size), they should be different. – Larme Mar 08 '21 at 14:26

1 Answers1

0

Ok, here is a little test. To do this I dragged a png and some random file into the project. They are highlighted in the image below.

Files added to project

Note that I added the files directly to the project, not to assets. That answer I linked to in the comments actually mentions that you can not get the image directly but, as you indicate, it is also saved as png data.

The project is a standard iOS SwiftUI app and I added the code below to the autogenerated ContentView.swift file.

import SwiftUI
import CommonCrypto

struct ContentView: View
{
    var body: some View
    {
        Text("Hello, world!")
            .padding()
            .onAppear
            {
                md5 ( url : Bundle.main.url(forResource: "t", withExtension: "png" ) )
                md5 ( url : Bundle.main.url(forResource: "something", withExtension: "ext" ) )
            }
    }
}

func md5 ( url : URL? ) -> Void
{
    if url == nil
    {
        print ( "Skipping empty url" )
        return
    }
    else
    {
        print ( "Summing \( url )" )
    }

    let bufferSize = 1024*1024
    do {
        let file = try FileHandle.init(forReadingFrom: url!)
        defer {
            file.closeFile()
        }
        
        var context = CC_MD5_CTX.init()
        CC_MD5_Init(&context)
        while case let data = file.readData(ofLength: bufferSize), data.count > 0 {
            data.withUnsafeBytes { (poiner) -> Void in
                _ = CC_MD5_Update(&context, poiner, CC_LONG(data.count))
            }
        }
        
        // Calculate the MD5 summary
        var digest = Data(count: Int(CC_MD5_DIGEST_LENGTH))
        digest.withUnsafeMutableBytes { (pointer) -> Void in
            _ = CC_MD5_Final(pointer, &context)
        }
        let result = digest.map { (byte) -> String in
            String.init(format: "%02hhx", byte)
        }.joined()
        print("result: \(result)")
    } catch let error as Error {
        print("calculation error: \(error.localizedDescription)") // Where is the try, where is the error?
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

This code is pretty much your MD5 function together with some code to test those files added to the project.

Now, the MD5 for the file works but it does not work for the image!

I presume Apple add some stuff to the image. I print the URL and checked - the image is a lot smaller than the original so it is probably some stripped down version with all or most of the metadata removed but this is just a guess.

Anyhow, I share your pain ... Why do these images differ - [img drawInRect:] problem here Apple changes an image that you draw in what appears to be an arbitrary way. I presume these kinds of tweaks are to improve UI or UX but personally I do not appreciate them.

Your md5 function works though fwiw. But you should be careful to test the same file as you do with the other MD5 functions. Thus you should not convert it in any way that would change even just one byte from the original. This is why pngData is a bad idea. But at the same time, I would expect if you copy it into your project that it would stay the same but apparently not.

EDIT

FWIW, if you rename the image to something without extension png and then add it to the project it stays the same. I duplicated and renamed that image to z.z and added that to the project and then the MD5 (and z.z) is the same as the original.

skaak
  • 2,988
  • 1
  • 8
  • 16