iedi3
iedi3

Reputation: 41

How to replicate hash_hmac('sha256', $key, $secret_key) function in Swift 4?

I've tried generating the hash_hmac('sha256', $key, $secret_key) php function equivalent in Swift 4 without success, after using libraries like CommonCrypto, CryptoSwift. I need these function for API authentication, using Alamofire library, which is a great library. Since i use Swift 4 the compatibility with other Swift libraries is not so good. Even with CryptoSwift which has the latest version(0.7.1) for Swift 4 i still get a lot of compatibility errors likes enter image description here

Upvotes: 3

Views: 2326

Answers (2)

zaph
zaph

Reputation: 112857

Swift 3/4:

HMAC with MD5, SHA1, SHA224, SHA256, SHA384, SHA512 (Swift 3)

These functions will hash either String or Data input with one of eight cryptographic hash algorithms.

The name parameter specifies the hash function name as a String Supported functions are MD5, SHA1, SHA224, SHA256, SHA384 and SHA512

This example requires Common Crypto
It is necessary to have a bridging header to the project:
#import <CommonCrypto/CommonCrypto.h>
Add the Security.framework to the project.

These functions takes a hash name, message to be hashed, a key and return a digest:


hashName: name of a hash function as String  
message:  message as Data  
key:      key as Data  
returns:  digest as Data  
func hmac(hashName:String, message:Data, key:Data) -> Data? {
    let algos = ["SHA1":   (kCCHmacAlgSHA1,   CC_SHA1_DIGEST_LENGTH),
                 "MD5":    (kCCHmacAlgMD5,    CC_MD5_DIGEST_LENGTH),
                 "SHA224": (kCCHmacAlgSHA224, CC_SHA224_DIGEST_LENGTH),
                 "SHA256": (kCCHmacAlgSHA256, CC_SHA256_DIGEST_LENGTH),
                 "SHA384": (kCCHmacAlgSHA384, CC_SHA384_DIGEST_LENGTH),
                 "SHA512": (kCCHmacAlgSHA512, CC_SHA512_DIGEST_LENGTH)]
    guard let (hashAlgorithm, length) = algos[hashName]  else { return nil }
    var macData = Data(count: Int(length))

    macData.withUnsafeMutableBytes {macBytes in
        message.withUnsafeBytes {messageBytes in
            key.withUnsafeBytes {keyBytes in
                CCHmac(CCHmacAlgorithm(hashAlgorithm),
                       keyBytes,     key.count,
                       messageBytes, message.count,
                       macBytes)
            }
        }
    }
    return macData

}

hashName: name of a hash function as String
message:  message as String
key:      key as String
returns:  digest as Data
func hmac(hashName:String, message:String, key:String) -> Data? {
    let messageData = message.data(using:.utf8)!
    let keyData = key.data(using:.utf8)!
    return hmac(hashName:hashName, message:messageData, key:keyData)
}

hashName: name of a hash function as String  
message:  message as String  
key:      key as Data  
returns:  digest as Data  
func hmac(hashName:String, message:String, key:Data) -> Data? {
    let messageData = message.data(using:.utf8)!
    return hmac(hashName:hashName, message:messageData, key:key)
}

// Examples

let clearString = "clearData0123456"
let keyString   = "keyData8901234562"
let clearData   = clearString.data(using:.utf8)!
let keyData     = keyString.data(using:.utf8)!
print("clearString: \(clearString)")
print("keyString:   \(keyString)")
print("clearData: \(clearData as NSData)")
print("keyData:   \(keyData as NSData)")

let hmacData1 = hmac(hashName:"SHA1", message:clearData, key:keyData)
print("hmacData1: \(hmacData1! as NSData)")

let hmacData2 = hmac(hashName:"SHA1", message:clearString, key:keyString)
print("hmacData2: \(hmacData2! as NSData)")

let hmacData3 = hmac(hashName:"SHA1", message:clearString, key:keyData)
print("hmacData3: \(hmacData3! as NSData)")

Output:

clearString: clearData0123456
keyString:   keyData8901234562
clearData: <636c6561 72446174 61303132 33343536>
keyData:   <6b657944 61746138 39303132 33343536 32>

hmacData1: <bb358f41 79b68c08 8e93191a da7dabbc 138f2ae6>
hmacData2: <bb358f41 79b68c08 8e93191a da7dabbc 138f2ae6>
hmacData3: <bb358f41 79b68c08 8e93191a da7dabbc 138f2ae6>

Upvotes: 2

Antwan van Houdt
Antwan van Houdt

Reputation: 6991

First of all it might be better to go straight for SHA512, SHA is notoriously easy to crack with GPU's, thus upping the memory scale a bit is not a bad idea.

Second, using CommonCrypto it is actually extremely easy to generate HMAC's, this is the implementation that I use:

static func hmac(_ secretKey: inout [UInt8], cipherText: inout [UInt8], algorithm: CommonCrypto.HMACAlgorithm = .sha512) -> [UInt8] {
        var mac = [UInt8](repeating: 0, count: 64)
        CCHmac(algorithm.value, &secretKey, secretKey.count, &cipherText, cipherText.count, &mac)
        return mac
    }

Where the algorithm is defined as such:

enum HMACAlgorithm {
        case sha512

        var value: UInt32 {
            switch(self) {
            case .sha512:
                return UInt32(kCCHmacAlgSHA512)
            }
        }
    }

My cipher text is cipherText+IV in this instance. When you are not using AES-GCM it seems suggested / recommended to HMAC IV+Cipher, but I cannot give you the technical details as to why.

Converting Data or NSData to a byte array:

var byteArray = data.withUnsafeBytes { [UInt8](UnsafeBufferPointer(start: $0, count: data.count) }

The reason for using an array is a substantial performance increase over Data, I don't know what the core team is doing but Data performs worse than NSMutableData even.

Upvotes: 0

Related Questions