CryptoKit vs CommonCrypto

I am exited about CryptoKit and I tried to replace CommonCrypto with CryptoKit for a simple hash I was doing but the results are different. Can anyone tell me why I get different output from CommonCrypto and CryptoKit in the following code?


    func execute() {
        let password: String = "Apple!Crypt#Test123"
        print("password: \(password)")
        let commonCryptoHashPassword = hashPasswordCommonCrypto(password)
        print("CommonCrypto: \(commonCryptoHashPassword!)")
        let cryptoKitHashPassword = hashPasswordCryptoKit(password)
        print("CryptoKit: \(cryptoKitHashPassword!)")
    }
    
    // CommonCrypto
    func hashPasswordCommonCrypto(_ password: String) -> String? {
        // Create the password hash
        var digest = [UInt8](repeating: 1, count: Int(CC_SHA512_DIGEST_LENGTH))
        CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA512), password, password.count, password, password.count, &digest)
        let passwordHashData = Data(bytes: digest)
        return formatPassword(passwordHashData)
    }
    
    // CrytoKit
    func hashPasswordCryptoKit(_ password: String) -> String? {
        // Create the hash
        let passwordData = Data(password.utf8)
        let passwordHashDigest = SHA512.hash(data: passwordData)
        return formatPassword(passwordHashDigest)
    }
    
    //Format password into readable string
    
    func formatPassword(_ password: Data) -> String {
        var passwordString : String = password.map { String(format: "%02hhx", $0) }.joined()
        // Add a dash after every 8 characters
        var index = 8
        repeat {
            passwordString.insert("-", at: String.Index(encodedOffset: index))
            index = index + 9
        } while index < passwordString.count
        return passwordString
    }
    

    func formatPassword(_ password: SHA512Digest) -> String {
        var passwordString : String = password.map { String(format: "%02hhx", $0) }.joined()
        // Add a dash after every 8 characters
        var index = 8
        repeat {
            passwordString.insert("-", at: String.Index(encodedOffset: index))
            index = index + 9
        } while index < passwordString.count
        return passwordString
    }


Here is the output that I get.


CommonCrypto:

90cc8e53-49ad79dc-a01de5a0-718744e4-a1180758-fb8a9976-970d1dcb-513ecfdf-abdd8aa7-d152bd5c-17e0c924-e3bf9885-102383a2-2f471eb0-add0fd58-7cc21dd9


CryptoKit:

6650240f-d532b1b4-a40a1b96-25e2e64c-6d90d5d1-29a14243-0673e408-d3d3c6ea-41adf755-baccd663-a2be7c66-f221d75b-de45d5fe-0f920c9e-8f7d4a2f-af532b3b

Accepted Reply

You need to use equivalent code to compare the results.


Hmac-SHA512:

// CommonCrypto
func hashHmacSHA512CommonCrypto(_ password: String) -> String? {
    // Create the password hash
    var digest = Data(count: Int(CC_SHA512_DIGEST_LENGTH))
    digest.withUnsafeMutableBytes {mutableBytes in
        CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA512), password, password.utf8.count, password, password.utf8.count, mutableBytes.baseAddress)
    }
    return formatPassword(digest)
}

// CrytoKit
func hashHmacSHA512CryptoKit(_ password: String) -> String? {
    // Create the hash
    let passwordData = password.data(using: .utf8)!
    let symmetricKey = SymmetricKey(data: passwordData)
    let passwordHashDigest = HMAC<SHA512>.authenticationCode(for: passwordData, using: symmetricKey)
    return formatPassword(Data(passwordHashDigest))
}

SHA512:

// CommonCrypto
func hashSha512CommonCrypto(_ password: String) -> String? {
    // Create the password hash
    var digest = Data(count: Int(CC_SHA512_DIGEST_LENGTH))
    digest.withUnsafeMutableBytes {mutableBytes in
        CC_SHA512(password, CC_LONG(password.utf8.count), mutableBytes.bindMemory(to: UInt8.self).baseAddress)
    }
    return formatPassword(digest)
}

// CrytoKit
func hashSha512CryptoKit(_ password: String) -> String? {
    // Create the hash
    let passwordData = password.data(using: .utf8)!
    let passwordHashDigest = SHA512.hash(data: passwordData)
    return formatPassword(Data(passwordHashDigest))
}


Testing:

//Format password into readable string

func formatPassword(_ password: Data) -> String {
    var passwordString : String = password.map { String(format: "%02x", $0) }.joined()
    // Add a dash after every 8 characters
    var index = passwordString.index(passwordString.startIndex, offsetBy: 8)
    repeat {
        passwordString.insert("-", at: index)
        passwordString.formIndex(&index, offsetBy: 9)
    } while index < passwordString.endIndex
    return passwordString
}

func execute() {
    let password: String = "Apple!Crypt#Test123"
    print("password: \(password)")
    print("HMAC-SHA512")
    let commonCryptoHmacPassword = hashHmacSHA512CommonCrypto(password)
    print("CommonCrypto: \(commonCryptoHmacPassword!)")
    let cryptoKitHmacPassword = hashHmacSHA512CryptoKit(password)
    print("CryptoKit   : \(cryptoKitHmacPassword!)")
    print("SHA512")
    let commonCryptoSha512Password = hashSha512CommonCrypto(password)
    print("CommonCrypto: \(commonCryptoSha512Password!)")
    let cryptoKitSha512Password = hashSha512CryptoKit(password)
    print("CryptoKit   : \(cryptoKitSha512Password!)")
}

execute()

Output:

password: Apple!Crypt#Test123

HMAC-SHA512

CommonCrypto: 90cc8e53-49ad79dc-a01de5a0-718744e4-a1180758-fb8a9976-970d1dcb-513ecfdf-abdd8aa7-d152bd5c-17e0c924-e3bf9885-102383a2-2f471eb0-add0fd58-7cc21dd9

CryptoKit : 90cc8e53-49ad79dc-a01de5a0-718744e4-a1180758-fb8a9976-970d1dcb-513ecfdf-abdd8aa7-d152bd5c-17e0c924-e3bf9885-102383a2-2f471eb0-add0fd58-7cc21dd9

SHA512

CommonCrypto: 6650240f-d532b1b4-a40a1b96-25e2e64c-6d90d5d1-29a14243-0673e408-d3d3c6ea-41adf755-baccd663-a2be7c66-f221d75b-de45d5fe-0f920c9e-8f7d4a2f-af532b3b

CryptoKit : 6650240f-d532b1b4-a40a1b96-25e2e64c-6d90d5d1-29a14243-0673e408-d3d3c6ea-41adf755-baccd663-a2be7c66-f221d75b-de45d5fe-0f920c9e-8f7d4a2f-af532b3b

Replies

In the CommonCrypto case, you are passing in an existing digest filled with `0x01`s. Try `repeating: 0`.

That value isn't used, it's just a random value to allocate space for the digest that is returned. I had already tried 0, and that or any other value has no effect because it's just overwritten by the digest.

CCHmac takes both a key and data, where the CyrptoKit hash just takes data. In the CCHmac I'm passing the password for both the key and data. I guess the quesion is what do I pass CCHmac for the key?

You need to use equivalent code to compare the results.


Hmac-SHA512:

// CommonCrypto
func hashHmacSHA512CommonCrypto(_ password: String) -> String? {
    // Create the password hash
    var digest = Data(count: Int(CC_SHA512_DIGEST_LENGTH))
    digest.withUnsafeMutableBytes {mutableBytes in
        CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA512), password, password.utf8.count, password, password.utf8.count, mutableBytes.baseAddress)
    }
    return formatPassword(digest)
}

// CrytoKit
func hashHmacSHA512CryptoKit(_ password: String) -> String? {
    // Create the hash
    let passwordData = password.data(using: .utf8)!
    let symmetricKey = SymmetricKey(data: passwordData)
    let passwordHashDigest = HMAC<SHA512>.authenticationCode(for: passwordData, using: symmetricKey)
    return formatPassword(Data(passwordHashDigest))
}

SHA512:

// CommonCrypto
func hashSha512CommonCrypto(_ password: String) -> String? {
    // Create the password hash
    var digest = Data(count: Int(CC_SHA512_DIGEST_LENGTH))
    digest.withUnsafeMutableBytes {mutableBytes in
        CC_SHA512(password, CC_LONG(password.utf8.count), mutableBytes.bindMemory(to: UInt8.self).baseAddress)
    }
    return formatPassword(digest)
}

// CrytoKit
func hashSha512CryptoKit(_ password: String) -> String? {
    // Create the hash
    let passwordData = password.data(using: .utf8)!
    let passwordHashDigest = SHA512.hash(data: passwordData)
    return formatPassword(Data(passwordHashDigest))
}


Testing:

//Format password into readable string

func formatPassword(_ password: Data) -> String {
    var passwordString : String = password.map { String(format: "%02x", $0) }.joined()
    // Add a dash after every 8 characters
    var index = passwordString.index(passwordString.startIndex, offsetBy: 8)
    repeat {
        passwordString.insert("-", at: index)
        passwordString.formIndex(&index, offsetBy: 9)
    } while index < passwordString.endIndex
    return passwordString
}

func execute() {
    let password: String = "Apple!Crypt#Test123"
    print("password: \(password)")
    print("HMAC-SHA512")
    let commonCryptoHmacPassword = hashHmacSHA512CommonCrypto(password)
    print("CommonCrypto: \(commonCryptoHmacPassword!)")
    let cryptoKitHmacPassword = hashHmacSHA512CryptoKit(password)
    print("CryptoKit   : \(cryptoKitHmacPassword!)")
    print("SHA512")
    let commonCryptoSha512Password = hashSha512CommonCrypto(password)
    print("CommonCrypto: \(commonCryptoSha512Password!)")
    let cryptoKitSha512Password = hashSha512CryptoKit(password)
    print("CryptoKit   : \(cryptoKitSha512Password!)")
}

execute()

Output:

password: Apple!Crypt#Test123

HMAC-SHA512

CommonCrypto: 90cc8e53-49ad79dc-a01de5a0-718744e4-a1180758-fb8a9976-970d1dcb-513ecfdf-abdd8aa7-d152bd5c-17e0c924-e3bf9885-102383a2-2f471eb0-add0fd58-7cc21dd9

CryptoKit : 90cc8e53-49ad79dc-a01de5a0-718744e4-a1180758-fb8a9976-970d1dcb-513ecfdf-abdd8aa7-d152bd5c-17e0c924-e3bf9885-102383a2-2f471eb0-add0fd58-7cc21dd9

SHA512

CommonCrypto: 6650240f-d532b1b4-a40a1b96-25e2e64c-6d90d5d1-29a14243-0673e408-d3d3c6ea-41adf755-baccd663-a2be7c66-f221d75b-de45d5fe-0f920c9e-8f7d4a2f-af532b3b

CryptoKit : 6650240f-d532b1b4-a40a1b96-25e2e64c-6d90d5d1-29a14243-0673e408-d3d3c6ea-41adf755-baccd663-a2be7c66-f221d75b-de45d5fe-0f920c9e-8f7d4a2f-af532b3b

Thank you. I hadn't realized that one was using Hmac-SHA512 and the other SHA512,

Frankly, I was not understanding the difference between Hmac-SHAxxx and SHAxxx until recently...


They have different use cases and you usually do not pass the same thing to data and key of Hmac.

I guess you might not need Hmac for your hasing cases.

I agree. That's why I used SHA512. Unfortunately, this is a legacy app and it was coded with Hmac-SHA512, the password was passed as the key, and there is a lot of data stored that way, so that‘s what I’ll have to continue to use,