Categories
iOS Swift

Hashing data using CryptoKit

So far we have been using CommonCrypto when it has come to creating hashes of data. I even wrote about it some time ago and presented a thin layer on top of it making it more convenient to use. In WWDC’19 Apple presented a new framework called CryptoKit. And of course, it contains functions for hashing data.

SHA512, SHA384, SHA256, SHA1 and MD5

CryptoKit contains separate types for SHA512, SHA384 and SHA256. In addition, there are MD5 and SHA1 but those are considered to be insecure and available only because of backwards compatibility reasons. With CryptoKit, hashing data becomes one line of code.

import CryptoKit
let sourceData = "The quick brown fox jumps over the lazy dog".data(using: .utf8)!
let sha512Digest = SHA512.hash(data: sourceData)
print(sha512Digest) // 07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6
let sha384Digest = SHA384.hash(data: sourceData)
print(sha384Digest) // ca737f1014a48f4c0b6dd43cb177b0afd9e5169367544c494011e3317dbf9a509cb1e5dc1e85a941bbee3d7f2afbc9b1
let sha256Digest = SHA256.hash(data: sourceData)
print(sha256Digest) // d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592
view raw .swift hosted with ❤ by GitHub

In case we do not have the whole data available in memory (e.g. really huge file), new types support creating hash by feeding data in piece by piece (just highlighting here how to use the hasher with incremental data).

let dataPieces = ["The ", "quick ", "brown ", "fox ", "jumps ", "over ", "the ", "lazy ", "dog"].map({ $0.data(using: .utf8)! })
var hasher = SHA512()
dataPieces.forEach { (data) in
hasher.update(data: data)
}
print(hasher.finalize()) // 07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6
view raw .swift hosted with ❤ by GitHub

Apple has an excellent playground describing the common operations developers need when using CryptoKit. Highly recommend to check it out if you need something more than just creating hashes.

Summary

CryptoKit is long waited framework what is easy to use and does not require managing raw pointers what was needed to when using CommonCrypto. It now just takes some time when we can bump deployment targets and forget CommonCrypto.

If this was helpful, please let me know on Mastodon@toomasvahter or Twitter @toomasvahter. Feel free to subscribe to RSS feed. Thank you for reading.

Categories
iOS

Using CCHmac for creating message authentication codes

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);
view raw CCHmac.h hosted with ❤ by GitHub

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)
}
}
view raw CCHmac.swift hosted with ❤ by GitHub

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).

References

Wikipedia: HMAC
man page of CCHmac

Categories
iOS Xcode

Hashing data using CommonCrypto and SHA256

Looking for hashing data using CryptoKit? Please navigate to here.

In this post we will look into how to add CommonCrypto to a Xcode project and how to generate hash using SHA256.

Adding CommonCrypto as a module

Note: Since Swift 4.2, this step is not necessary and manually added CommonCrypto module must be removed.

CommonCrypto library can be added in two ways: importing it in the Objective-C bridging header or by using module maps.
The first option makes sense for projects having both Objective-C and Swift code. In this case Objective-C bridging header is already part of the project and therefore adding #import to the bridging header is the quickest way.
The second option is more suitable for pure Swift projects as it enables to avoid adding the bridging header (although it is up for everyone’s preference).
Adding CommonCrypto as a module consists of two steps. Firstly, we need to create a folder named ‘CommonCrypto’ in the same folder as the Xcode project file is. Then we need to create a file ‘module.map’ and save it to ‘CommonCrypto’ folder.

module CommonCrypto [system] {
header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/CommonCrypto/CommonCrypto.h"
export *
}
view raw module.map hosted with ❤ by GitHub
CommonCryptoModuleInProject

Secondly, we need to open the project in Xcode, then navigating to ‘Build Settings’ of the target and adding $(PROJECT_DIR)/CommonCrypto/ to ‘Import Paths’.

CommonCryptoImportPaths

That’s it, now Xcode knows where the module is located and it can be imported in Swift files.

Hashing data using SHA256 algorithm

CommonCrypto contains a function named CC_SHA256(…) what takes in data, the count of bytes and gives a pointer to the hash of the data. As the function operates on the data, it makes sense to extend Data. We’ll add a function what can be later on extended for adding support for other hashing algorithms as well (see here for other algorithms).

import Foundation
import CommonCrypto
extension Data {
enum Algorithm {
case sha256
var digestLength: Int {
switch self {
case .sha256: return Int(CC_SHA256_DIGEST_LENGTH)
}
}
}
func hash(for algorithm: Algorithm) -> Data {
let hashBytes = UnsafeMutablePointer<UInt8>.allocate(capacity: algorithm.digestLength)
defer { hashBytes.deallocate() }
switch algorithm {
case .sha256:
withUnsafeBytes { (buffer) -> Void in
CC_SHA256(buffer.baseAddress!, CC_LONG(buffer.count), hashBytes)
}
}
return Data(bytes: hashBytes, count: algorithm.digestLength)
}
}
if let someData = "Random string".data(using: .utf8) {
let hash = someData.hash(for: .sha256)
print("hash=\(hash.base64EncodedString()).")
}
// hash=LclzKS0NtZFSuwQF5H2FpTralr75LsjrnE3et2LxkHs=.
view raw Hashing.swift hosted with ❤ by GitHub

Example project can be found on GitHub (CommonCryptoExample).

If this was helpful, please let me know on Mastodon@toomasvahter or Twitter @toomasvahter. Feel free to subscribe to RSS feed. Thank you for reading.