macoshashswifthmacsha1commoncrypto

CommonHMAC in Swift


I'm trying to create a HMAC SHA-1 hash of a string in Swift but can't figure out how to interact with the APIs as it doesn't seem to be importing the CommonCrypto framework. I've tried various different forms of "import CommonCrypto" and creating a bridging header file but none of it made a difference.

The odd thing is that if I create an Objective-C class, I'm able to interact with APIs without any problems, so this seems to be unique to Swift.

Also if anyone could tell me what the equivalent of uint8_t digest[CC_SHA1_DIGEST_LENGTH] is in Swift I'd be very grateful


Solution

  • You can do it in Swift. Just make sure you add #import <CommonCrypto/CommonHMAC.h> to the bridging Objective-C bridging header.

    Update: For Swift 4 see a much better solution using the Swift Package Manager here: https://github.com/jernejstrasner/SwiftCrypto

    enum CryptoAlgorithm {
        case MD5, SHA1, SHA224, SHA256, SHA384, SHA512
    
        var HMACAlgorithm: CCHmacAlgorithm {
            var result: Int = 0
            switch self {
            case .MD5:      result = kCCHmacAlgMD5
            case .SHA1:     result = kCCHmacAlgSHA1
            case .SHA224:   result = kCCHmacAlgSHA224
            case .SHA256:   result = kCCHmacAlgSHA256
            case .SHA384:   result = kCCHmacAlgSHA384
            case .SHA512:   result = kCCHmacAlgSHA512
            }
            return CCHmacAlgorithm(result)
        }
    
        var digestLength: Int {
            var result: Int32 = 0
            switch self {
            case .MD5:      result = CC_MD5_DIGEST_LENGTH
            case .SHA1:     result = CC_SHA1_DIGEST_LENGTH
            case .SHA224:   result = CC_SHA224_DIGEST_LENGTH
            case .SHA256:   result = CC_SHA256_DIGEST_LENGTH
            case .SHA384:   result = CC_SHA384_DIGEST_LENGTH
            case .SHA512:   result = CC_SHA512_DIGEST_LENGTH
            }
            return Int(result)
        }
    }
    
    extension String {
    
        func hmac(algorithm: CryptoAlgorithm, key: String) -> String {
            let str = self.cStringUsingEncoding(NSUTF8StringEncoding)
            let strLen = Int(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
            let digestLen = algorithm.digestLength
            let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen)
            let keyStr = key.cStringUsingEncoding(NSUTF8StringEncoding)
            let keyLen = Int(key.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
    
            CCHmac(algorithm.HMACAlgorithm, keyStr!, keyLen, str!, strLen, result)
    
            let digest = stringFromResult(result, length: digestLen)
    
            result.dealloc(digestLen)
    
            return digest
        }
    
        private func stringFromResult(result: UnsafeMutablePointer<CUnsignedChar>, length: Int) -> String {
            var hash = NSMutableString()
            for i in 0..<length {
                hash.appendFormat("%02x", result[i])
            }
            return String(hash)
        }
    
    }