How can I hash a file on iOS using Quick 3?

I have several files that will live on the server. Users can create these types of files (plists) on the device, which will then be downloaded to the specified server (CloudKit). I would like them to be unique in content (a unique methodology should be resistant to variations in the creation date). I understand that I must use these files to get unique file names for them. My questions:

  • As far as I understand, what I want is a hash function?
  • What function should I use (from CommonCrypto).
  • What do I need - a digest?
  • How do I do this in code? (I assume this should be hashed on an instance of NSData?). My understanding from googling around is that I need a header header, but besides that, using CommonCrypto puzzles me. If there is an easier way to use third-party APIs (Apple), I’m all ears (I want to avoid using third-party code as much as possible).

Many thanks!

+5
source share
3 answers

, . SHA-256 - -, iOS Common Crypto , iPhone 6S SHA256 1 / -. , .

Common Crypto (Swift3)

:

func sha256(string: String) -> Data {
    let messageData = string.data(using:String.Encoding.utf8)!
    var digestData = Data(count: Int(CC_SHA256_DIGEST_LENGTH))

    _ = digestData.withUnsafeMutableBytes {digestBytes in
        messageData.withUnsafeBytes {messageBytes in
            CC_SHA256(messageBytes, CC_LONG(messageData.count), digestBytes)
        }
    }
    return digestData
}
let testString = "testString"
let testHash = sha256(string:testString)
print("testHash: \(testHash.map { String(format: "%02hhx", $0) }.joined())")

let testHashBase64 = testHash.base64EncodedString()
print("testHashBase64: \(testHashBase64)")

:
testHash: 4acf0b39d9c4766709a3689f553ac01ab550545ffa4544dfc0b2cea82fba02a3
testHashBase64: Ss8LOdnEdmcJo2ifVTrAGrVQVF/6RUTfwLLOqC + 6AqM =

. :

#import <CommonCrypto/CommonCrypto.h>

-:

func sha256(data: Data) -> Data {
    var digestData = Data(count: Int(CC_SHA256_DIGEST_LENGTH))

    _ = digestData.withUnsafeMutableBytes {digestBytes in
        data.withUnsafeBytes {messageBytes in
            CC_SHA256(messageBytes, CC_LONG(data.count), digestBytes)
        }
    }
    return digestData
}

let testData: Data = "testString".data(using: .utf8)!
print("testData: \(testData.map { String(format: "%02hhx", $0) }.joined())")
let testHash = sha256(data:testData)
print("testHash: \(testHash.map { String(format: "%02hhx", $0) }.joined())")

:
testData: 74657374537472696e67
testHash: 4acf0b39d9c4766709a3689f553ac01ab550545ffa4544dfc0b2cea82fba02a3

. .

+8

, , :

func sha256(url: URL) -> Data? {
    do {
        let bufferSize = 1024 * 1024
        // Open file for reading:
        let file = try FileHandle(forReadingFrom: url)
        defer {
            file.closeFile()
        }

        // Create and initialize SHA256 context:
        var context = CC_SHA256_CTX()
        CC_SHA256_Init(&context)

        // Read up to `bufferSize` bytes, until EOF is reached, and update SHA256 context:
        while autoreleasepool(invoking: {
            // Read up to `bufferSize` bytes
            let data = file.readData(ofLength: bufferSize)
            if data.count > 0 {
                data.withUnsafeBytes {
                    _ = CC_SHA256_Update(&context, $0, numericCast(data.count))
                }
                // Continue
                return true
            } else {
                // End of file
                return false
            }
        }) { }

        // Compute the SHA256 digest:
        var digest = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
        digest.withUnsafeMutableBytes {
            _ = CC_SHA256_Final($0, &context)
        }

        return digest
    } catch {
        print(error)
        return nil
    }
}

URL fileURL, :

if let digestData = sha256(url: fileURL) {
    let calculatedHash = digestData.map { String(format: "%02hhx", $0) }.joined()
    DDLogDebug(calculatedHash)
}
+4

Starting with Swift 5, @chriswillow's answer is still mostly correct, but there have been some updates in withUnsafeBytes/ withUnsafeMutableBytes. These updates make the methods safer, but at the same time more annoying to use.

For a bit using withUnsafeBytes, use:

_ = data.withUnsafeBytes { bytesFromBuffer -> Int32 in
  guard let rawBytes = bytesFromBuffer.bindMemory(to: UInt8.self).baseAddress else {
    return Int32(kCCMemoryFailure)
  }

  return CC_SHA256_Update(&context, rawBytes, numericCast(data.count))
}

For a bit generating final digest data, use:

var digestData = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
_ = digestData.withUnsafeMutableBytes { bytesFromDigest -> Int32 in
  guard let rawBytes = bytesFromDigest.bindMemory(to: UInt8.self).baseAddress else {
    return Int32(kCCMemoryFailure)
  }

  return CC_SHA256_Final(rawBytes, &context)
}
0
source

Source: https://habr.com/ru/post/1672852/


All Articles