This is a second part of the first post about hashing data using SHA256. Here we will look into CCHmac (Hash-based Message Authentication Code) functions and see how to use it for creating authentication codes what can be used for data integrity checks and authentication of a message.
Message authentication codes
Hash-based message authentication code enables validating messages sent between two parties. For achieving that, the sender will use secret key for creating an authentication code of the message. Then transmits the message and the code to the other party. Then the receiver will use the same secret key (only they both know the key) for creating an authentication code of the received message. If the received authentication code matches with the created one, the receiver can be sure of the message’s integrity.
CCHmac interface
There are 3 functions which are used together for creating an authentication code. These are: CCHmacInit()
, CCHmacUpdate()
and CCHmacFinal()
. Note that the update function can be called multiple times for processing the data in chunks. In addition, there is a convenience function what internally creates CCHmacContext
object and uses listed functions for initialising, updating and finalising the process of creating an authentication code. We will concentrate on the convenience function.
/*! | |
@function CCHmac | |
@abstract Create the Message Authentication Code. | |
@param algorithm HMAC algorithm to perform. | |
@param key Raw key bytes. | |
@param keyLength Count of raw key bytes. It can also be zero. | |
@param data Raw data bytes. | |
@param dataLength Count of data bytes. | |
@param macOut Output buffer allocated by caller. | |
*/ | |
void CCHmac(CCHmacAlgorithm algorithm, const void *key, size_t keyLength, const void *data, size_t dataLength, void *macOut); |
Arguments define the hashing algorithm, secret key, input data and an output buffer. It is important to note that the output buffer needs to be preallocated and the length depends on the chosen algorithm (for example SHA256 requires a buffer with length equal to CC_SHA256_DIGEST_LENGTH).
Data extension for CCHmac in Swift
import Foundation | |
import CommonCrypto | |
extension Data { | |
enum Algorithm { | |
case md5 | |
case sha1 | |
case sha224 | |
case sha256 | |
case sha384 | |
case sha512 | |
var digestLength: Int { | |
switch self { | |
case .md5: return Int(CC_MD5_DIGEST_LENGTH) | |
case .sha1: return Int(CC_SHA1_DIGEST_LENGTH) | |
case .sha224: return Int(CC_SHA224_DIGEST_LENGTH) | |
case .sha256: return Int(CC_SHA256_DIGEST_LENGTH) | |
case .sha384: return Int(CC_SHA384_DIGEST_LENGTH) | |
case .sha512: return Int(CC_SHA512_DIGEST_LENGTH) | |
} | |
} | |
} | |
} | |
extension Data.Algorithm: RawRepresentable { | |
typealias RawValue = Int | |
init?(rawValue: Int) { | |
switch rawValue { | |
case kCCHmacAlgMD5: self = .md5 | |
case kCCHmacAlgSHA1: self = .sha1 | |
case kCCHmacAlgSHA224: self = .sha224 | |
case kCCHmacAlgSHA256: self = .sha256 | |
case kCCHmacAlgSHA384: self = .sha384 | |
case kCCHmacAlgSHA512: self = .sha512 | |
default: return nil | |
} | |
} | |
var rawValue: Int { | |
switch self { | |
case .md5: return kCCHmacAlgMD5 | |
case .sha1: return kCCHmacAlgSHA1 | |
case .sha224: return kCCHmacAlgSHA224 | |
case .sha256: return kCCHmacAlgSHA256 | |
case .sha384: return kCCHmacAlgSHA384 | |
case .sha512: return kCCHmacAlgSHA512 | |
} | |
} | |
} | |
extension Data { | |
func authenticationCode(for algorithm: Algorithm, secretKey: String = "") -> Data { | |
guard let secretKeyData = secretKey.data(using: .utf8) else { fatalError() } | |
return authenticationCode(for: algorithm, secretKey: secretKeyData) | |
} | |
func authenticationCode(for algorithm: Algorithm, secretKey: Data) -> Data { | |
let hashBytes = UnsafeMutablePointer<UInt8>.allocate(capacity: algorithm.digestLength) | |
defer { hashBytes.deallocate() } | |
withUnsafeBytes { (bytes) -> Void in | |
secretKey.withUnsafeBytes { (secretKeyBytes) -> Void in | |
CCHmac(CCHmacAlgorithm(algorithm.rawValue), secretKeyBytes, secretKey.count, bytes, count, hashBytes) | |
} | |
} | |
return Data(bytes: hashBytes, count: algorithm.digestLength) | |
} | |
} |
The method for creating authentication code is func authenticationCode(for algorithm: Algorithm, secretKey: Data) -> Data
what is very easy to use for creating raw message authentication code. It can be converted into string by using Data's base64EncodedString()
.
Example project can be found on GitHub (CommonCryptoExample).