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
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)
}
}
Try this for Swift 3.1 :
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.cString(using: String.Encoding.utf8)
let strLen = Int(self.lengthOfBytes(using: String.Encoding.utf8))
let digestLen = algorithm.digestLength
let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen)
let keyStr = key.cString(using: String.Encoding.utf8)
let keyLen = Int(key.lengthOfBytes(using: String.Encoding.utf8))
CCHmac(algorithm.HMACAlgorithm, keyStr!, keyLen, str!, strLen, result)
let digest = stringFromResult(result: result, length: digestLen)
result.deallocate(capacity: digestLen)
return digest
}
private func stringFromResult(result: UnsafeMutablePointer<CUnsignedChar>, length: Int) -> String {
let hash = NSMutableString()
for i in 0..<length {
hash.appendFormat("%02x", result[i])
}
return String(hash)
}
}
Don't forget add #import <CommonCrypto/CommonHMAC.h>
to Header.h
I wanted to keep things minimal. Avoid the added complexity of creating a generic class that could handle all the different digest types and instead just have a small method I could drop into a class if needed. I also prefer to avoid adding extensions to core classes.
Add the following include to your -Bridging-Header.h
file:
#import <CommonCrypto/CommonHMAC.h>
Then in the class that needs to call CCHmac()
add a private method:
private func hmac(string: NSString, key: NSData) -> NSData {
let keyBytes = UnsafePointer<CUnsignedChar>(key.bytes)
let data = string.cStringUsingEncoding(NSUTF8StringEncoding)
let dataLen = Int(string.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
let digestLen = Int(CC_SHA1_DIGEST_LENGTH)
let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen)
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1), keyBytes, key.length, data, dataLen, result);
return NSData(bytes: result, length: digestLen)
}
If I need a different CCHmacAlgorithm
I would just replace the two constants in that method with the appropriate ones. In my case I needed kCCHmacAlgSHA256
and CC_SHA256_DIGEST_LENGTH
.
Thanks to Jernej Strasner and others for the other answers, I just wanted something simpler for my case.
Swift can figure out how to map to the Obj-C frameworks but not so much for direct C functions. Apple has provided some bindings for stuff like GCD and AudioToolbox, but not everything. It seems CommonCrypto does not have proper bindings yet.
For this case, I would recommend writing your own basic wrappers in Obj-C, then use these wrapper classes in Swift.
For example you could create an HMAC
class in Obj-C:
// This enum is in HMAC.h
typedef NS_ENUM(NSInteger, HMACAlgorithm)
{
SHA1,
MD5,
SHA256,
SHA384,
SHA512,
SHA224
};
// Class methods here
+ (NSData *)calculateWithAlgorithm:(HMACAlgorithm)algorithm forKey:(const void *)key andData:(const void *)data
{
NSInteger digestLength = [self digestLengthForAlgorithm:algorithm];
unsigned char hmac[digestLength];
CCHmac(algorithm, &key, strlen(key), &data, strlen(data), &hmac);
NSData *hmacBytes = [NSData dataWithBytes:hmac length:sizeof(hmac)];
return hmacBytes;
}
+ (NSInteger)digestLengthForAlgorithm:(HMACAlgorithm)algorithm
{
switch (algorithm)
{
case MD5: return CC_MD5_DIGEST_LENGTH;
case SHA1: return CC_SHA1_DIGEST_LENGTH;
case SHA224: return CC_SHA224_DIGEST_LENGTH;
case SHA256: return CC_SHA256_DIGEST_LENGTH;
case SHA384: return CC_SHA384_DIGEST_LENGTH;
case SHA512: return CC_SHA512_DIGEST_LENGTH;
default: return 0;
}
}
Then in Swift:
class SwiftHMAC
{
// Swift will automatically pull the enum from Obj-C
func calculate(algorithm:HMACAlgorithm, key:Byte[], data:Byte[]) -> Byte[]
{
let computedHMAC = HMAC.calculateWithAlgorithm(algorithm, forKey: key, andData: data)
var rawBytes = Byte[](count: computedHMAC.length, repeatedValue: 0)
computedHMAC.getBytes(&rawBytes)
return rawBytes
}
}
Just remember to add #import "HMAC.h"
to your Swift bridging header as well as #import "<##Your-Project-Name##>-Swift.h"
to the Obj-C implementation (.m) file.