Swift hash a string using hash_hmac with ripemd160
As you have already figured out ripemd160
is unsupported in CommonCrypto. Therefore, you need to use some external crypto library. There are (at least) two libraries that you can use in Swift/Objective-C: OpenSSL or Crypto++.
OpenSSL
Here is the code for calculating the hmac hash with ripemd160 using OpenSSL:
+ (NSString *)ripemd160WithKey:(NSString *)aKey andData:(NSString *)aData {
const char* key = [aKey cStringUsingEncoding:NSUTF8StringEncoding];
const char* data = [aData cStringUsingEncoding:NSUTF8StringEncoding];
unsigned char* digest;
digest = HMAC(EVP_ripemd160(), key, (int)strlen(key), (unsigned char*)data, strlen(data), NULL, NULL);
NSMutableString* str = [NSMutableString string];
for(int i = 0; i < RIPEMD160_DIGEST_LENGTH; i++) {
[str appendFormat:@"%02x", (unsigned int)digest[i]];
}
return str;
}
I have setup an example for iOS based on Felix Schulze's OpenSSL for iPhone library that you can also compile for watchOS, macOS or tvOS.
- Clone the repository from my GitHub account.
- Open Terminal and change directory to the repository folder
cd OpenSSL-for-iPhone
- Run
./build-libssl.sh
to download and compile the openSSL sources - Open
OpenSSL-for-iOS.xcodeproj
with Xcode - Run
OpenSSL-for-iOS
on either iOS Simulator or on your phone
As you can see in the ViewController.swift
you just need to call FSOpenSSL.ripemd160(withKey: "test", andData: "string")
with the corresponding parameters. You can play with different values via the UI.
Hope that helps.
Here is a (self-contained) pure Swift 5 implementation of the RIPEMD-160 hash function and the corresponding HMAC-RIPEMD-160 message authentication code. It has been tested with all test vectors on the RIPEMD-160 page (tested on macOS, 32-bit iOS, 64-bit iOS).
The code for Swift 3 can be found in the edit history.
This is a translation of the reference implementation
rmd160.h,
rmd160.c
from the RIPEMD-160 page from C to Swift. I translated the helper macros and the compress()
function (the "heart" of the algorithm) as "verbatim" as possible. This allowed me to
copy/paste large C code segments and make only minor adjustments for Swift.
The update()
and finalize()
methods provide a streaming interface, similar to that
of the CommonCrypto functions.
RIPEMD160.swift:
import Foundation
public struct RIPEMD160 {
private var MDbuf: (UInt32, UInt32, UInt32, UInt32, UInt32)
private var buffer: Data
private var count: Int64 // Total # of bytes processed.
public init() {
MDbuf = (0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0)
buffer = Data()
count = 0
}
private mutating func compress(_ X: UnsafePointer<UInt32>) {
// *** Helper functions (originally macros in rmd160.h) ***
/* ROL(x, n) cyclically rotates x over n bits to the left */
/* x must be of an unsigned 32 bits type and 0 <= n < 32. */
func ROL(_ x: UInt32, _ n: UInt32) -> UInt32 {
return (x << n) | ( x >> (32 - n))
}
/* the five basic functions F(), G() and H() */
func F(_ x: UInt32, _ y: UInt32, _ z: UInt32) -> UInt32 {
return x ^ y ^ z
}
func G(_ x: UInt32, _ y: UInt32, _ z: UInt32) -> UInt32 {
return (x & y) | (~x & z)
}
func H(_ x: UInt32, _ y: UInt32, _ z: UInt32) -> UInt32 {
return (x | ~y) ^ z
}
func I(_ x: UInt32, _ y: UInt32, _ z: UInt32) -> UInt32 {
return (x & z) | (y & ~z)
}
func J(_ x: UInt32, _ y: UInt32, _ z: UInt32) -> UInt32 {
return x ^ (y | ~z)
}
/* the ten basic operations FF() through III() */
func FF(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
a = a &+ F(b, c, d) &+ x
a = ROL(a, s) &+ e
c = ROL(c, 10)
}
func GG(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
a = a &+ G(b, c, d) &+ x &+ 0x5a827999
a = ROL(a, s) &+ e
c = ROL(c, 10)
}
func HH(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
a = a &+ H(b, c, d) &+ x &+ 0x6ed9eba1
a = ROL(a, s) &+ e
c = ROL(c, 10)
}
func II(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
a = a &+ I(b, c, d) &+ x &+ 0x8f1bbcdc
a = ROL(a, s) &+ e
c = ROL(c, 10)
}
func JJ(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
a = a &+ J(b, c, d) &+ x &+ 0xa953fd4e
a = ROL(a, s) &+ e
c = ROL(c, 10)
}
func FFF(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
a = a &+ F(b, c, d) &+ x
a = ROL(a, s) &+ e
c = ROL(c, 10)
}
func GGG(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
a = a &+ G(b, c, d) &+ x &+ 0x7a6d76e9
a = ROL(a, s) &+ e
c = ROL(c, 10)
}
func HHH(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
a = a &+ H(b, c, d) &+ x &+ 0x6d703ef3
a = ROL(a, s) &+ e
c = ROL(c, 10)
}
func III(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
a = a &+ I(b, c, d) &+ x &+ 0x5c4dd124
a = ROL(a, s) &+ e
c = ROL(c, 10)
}
func JJJ(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
a = a &+ J(b, c, d) &+ x &+ 0x50a28be6
a = ROL(a, s) &+ e
c = ROL(c, 10)
}
// *** The function starts here ***
var (aa, bb, cc, dd, ee) = MDbuf
var (aaa, bbb, ccc, ddd, eee) = MDbuf
/* round 1 */
FF(&aa, bb, &cc, dd, ee, X[ 0], 11)
FF(&ee, aa, &bb, cc, dd, X[ 1], 14)
FF(&dd, ee, &aa, bb, cc, X[ 2], 15)
FF(&cc, dd, &ee, aa, bb, X[ 3], 12)
FF(&bb, cc, &dd, ee, aa, X[ 4], 5)
FF(&aa, bb, &cc, dd, ee, X[ 5], 8)
FF(&ee, aa, &bb, cc, dd, X[ 6], 7)
FF(&dd, ee, &aa, bb, cc, X[ 7], 9)
FF(&cc, dd, &ee, aa, bb, X[ 8], 11)
FF(&bb, cc, &dd, ee, aa, X[ 9], 13)
FF(&aa, bb, &cc, dd, ee, X[10], 14)
FF(&ee, aa, &bb, cc, dd, X[11], 15)
FF(&dd, ee, &aa, bb, cc, X[12], 6)
FF(&cc, dd, &ee, aa, bb, X[13], 7)
FF(&bb, cc, &dd, ee, aa, X[14], 9)
FF(&aa, bb, &cc, dd, ee, X[15], 8)
/* round 2 */
GG(&ee, aa, &bb, cc, dd, X[ 7], 7)
GG(&dd, ee, &aa, bb, cc, X[ 4], 6)
GG(&cc, dd, &ee, aa, bb, X[13], 8)
GG(&bb, cc, &dd, ee, aa, X[ 1], 13)
GG(&aa, bb, &cc, dd, ee, X[10], 11)
GG(&ee, aa, &bb, cc, dd, X[ 6], 9)
GG(&dd, ee, &aa, bb, cc, X[15], 7)
GG(&cc, dd, &ee, aa, bb, X[ 3], 15)
GG(&bb, cc, &dd, ee, aa, X[12], 7)
GG(&aa, bb, &cc, dd, ee, X[ 0], 12)
GG(&ee, aa, &bb, cc, dd, X[ 9], 15)
GG(&dd, ee, &aa, bb, cc, X[ 5], 9)
GG(&cc, dd, &ee, aa, bb, X[ 2], 11)
GG(&bb, cc, &dd, ee, aa, X[14], 7)
GG(&aa, bb, &cc, dd, ee, X[11], 13)
GG(&ee, aa, &bb, cc, dd, X[ 8], 12)
/* round 3 */
HH(&dd, ee, &aa, bb, cc, X[ 3], 11)
HH(&cc, dd, &ee, aa, bb, X[10], 13)
HH(&bb, cc, &dd, ee, aa, X[14], 6)
HH(&aa, bb, &cc, dd, ee, X[ 4], 7)
HH(&ee, aa, &bb, cc, dd, X[ 9], 14)
HH(&dd, ee, &aa, bb, cc, X[15], 9)
HH(&cc, dd, &ee, aa, bb, X[ 8], 13)
HH(&bb, cc, &dd, ee, aa, X[ 1], 15)
HH(&aa, bb, &cc, dd, ee, X[ 2], 14)
HH(&ee, aa, &bb, cc, dd, X[ 7], 8)
HH(&dd, ee, &aa, bb, cc, X[ 0], 13)
HH(&cc, dd, &ee, aa, bb, X[ 6], 6)
HH(&bb, cc, &dd, ee, aa, X[13], 5)
HH(&aa, bb, &cc, dd, ee, X[11], 12)
HH(&ee, aa, &bb, cc, dd, X[ 5], 7)
HH(&dd, ee, &aa, bb, cc, X[12], 5)
/* round 4 */
II(&cc, dd, &ee, aa, bb, X[ 1], 11)
II(&bb, cc, &dd, ee, aa, X[ 9], 12)
II(&aa, bb, &cc, dd, ee, X[11], 14)
II(&ee, aa, &bb, cc, dd, X[10], 15)
II(&dd, ee, &aa, bb, cc, X[ 0], 14)
II(&cc, dd, &ee, aa, bb, X[ 8], 15)
II(&bb, cc, &dd, ee, aa, X[12], 9)
II(&aa, bb, &cc, dd, ee, X[ 4], 8)
II(&ee, aa, &bb, cc, dd, X[13], 9)
II(&dd, ee, &aa, bb, cc, X[ 3], 14)
II(&cc, dd, &ee, aa, bb, X[ 7], 5)
II(&bb, cc, &dd, ee, aa, X[15], 6)
II(&aa, bb, &cc, dd, ee, X[14], 8)
II(&ee, aa, &bb, cc, dd, X[ 5], 6)
II(&dd, ee, &aa, bb, cc, X[ 6], 5)
II(&cc, dd, &ee, aa, bb, X[ 2], 12)
/* round 5 */
JJ(&bb, cc, &dd, ee, aa, X[ 4], 9)
JJ(&aa, bb, &cc, dd, ee, X[ 0], 15)
JJ(&ee, aa, &bb, cc, dd, X[ 5], 5)
JJ(&dd, ee, &aa, bb, cc, X[ 9], 11)
JJ(&cc, dd, &ee, aa, bb, X[ 7], 6)
JJ(&bb, cc, &dd, ee, aa, X[12], 8)
JJ(&aa, bb, &cc, dd, ee, X[ 2], 13)
JJ(&ee, aa, &bb, cc, dd, X[10], 12)
JJ(&dd, ee, &aa, bb, cc, X[14], 5)
JJ(&cc, dd, &ee, aa, bb, X[ 1], 12)
JJ(&bb, cc, &dd, ee, aa, X[ 3], 13)
JJ(&aa, bb, &cc, dd, ee, X[ 8], 14)
JJ(&ee, aa, &bb, cc, dd, X[11], 11)
JJ(&dd, ee, &aa, bb, cc, X[ 6], 8)
JJ(&cc, dd, &ee, aa, bb, X[15], 5)
JJ(&bb, cc, &dd, ee, aa, X[13], 6)
/* parallel round 1 */
JJJ(&aaa, bbb, &ccc, ddd, eee, X[ 5], 8)
JJJ(&eee, aaa, &bbb, ccc, ddd, X[14], 9)
JJJ(&ddd, eee, &aaa, bbb, ccc, X[ 7], 9)
JJJ(&ccc, ddd, &eee, aaa, bbb, X[ 0], 11)
JJJ(&bbb, ccc, &ddd, eee, aaa, X[ 9], 13)
JJJ(&aaa, bbb, &ccc, ddd, eee, X[ 2], 15)
JJJ(&eee, aaa, &bbb, ccc, ddd, X[11], 15)
JJJ(&ddd, eee, &aaa, bbb, ccc, X[ 4], 5)
JJJ(&ccc, ddd, &eee, aaa, bbb, X[13], 7)
JJJ(&bbb, ccc, &ddd, eee, aaa, X[ 6], 7)
JJJ(&aaa, bbb, &ccc, ddd, eee, X[15], 8)
JJJ(&eee, aaa, &bbb, ccc, ddd, X[ 8], 11)
JJJ(&ddd, eee, &aaa, bbb, ccc, X[ 1], 14)
JJJ(&ccc, ddd, &eee, aaa, bbb, X[10], 14)
JJJ(&bbb, ccc, &ddd, eee, aaa, X[ 3], 12)
JJJ(&aaa, bbb, &ccc, ddd, eee, X[12], 6)
/* parallel round 2 */
III(&eee, aaa, &bbb, ccc, ddd, X[ 6], 9)
III(&ddd, eee, &aaa, bbb, ccc, X[11], 13)
III(&ccc, ddd, &eee, aaa, bbb, X[ 3], 15)
III(&bbb, ccc, &ddd, eee, aaa, X[ 7], 7)
III(&aaa, bbb, &ccc, ddd, eee, X[ 0], 12)
III(&eee, aaa, &bbb, ccc, ddd, X[13], 8)
III(&ddd, eee, &aaa, bbb, ccc, X[ 5], 9)
III(&ccc, ddd, &eee, aaa, bbb, X[10], 11)
III(&bbb, ccc, &ddd, eee, aaa, X[14], 7)
III(&aaa, bbb, &ccc, ddd, eee, X[15], 7)
III(&eee, aaa, &bbb, ccc, ddd, X[ 8], 12)
III(&ddd, eee, &aaa, bbb, ccc, X[12], 7)
III(&ccc, ddd, &eee, aaa, bbb, X[ 4], 6)
III(&bbb, ccc, &ddd, eee, aaa, X[ 9], 15)
III(&aaa, bbb, &ccc, ddd, eee, X[ 1], 13)
III(&eee, aaa, &bbb, ccc, ddd, X[ 2], 11)
/* parallel round 3 */
HHH(&ddd, eee, &aaa, bbb, ccc, X[15], 9)
HHH(&ccc, ddd, &eee, aaa, bbb, X[ 5], 7)
HHH(&bbb, ccc, &ddd, eee, aaa, X[ 1], 15)
HHH(&aaa, bbb, &ccc, ddd, eee, X[ 3], 11)
HHH(&eee, aaa, &bbb, ccc, ddd, X[ 7], 8)
HHH(&ddd, eee, &aaa, bbb, ccc, X[14], 6)
HHH(&ccc, ddd, &eee, aaa, bbb, X[ 6], 6)
HHH(&bbb, ccc, &ddd, eee, aaa, X[ 9], 14)
HHH(&aaa, bbb, &ccc, ddd, eee, X[11], 12)
HHH(&eee, aaa, &bbb, ccc, ddd, X[ 8], 13)
HHH(&ddd, eee, &aaa, bbb, ccc, X[12], 5)
HHH(&ccc, ddd, &eee, aaa, bbb, X[ 2], 14)
HHH(&bbb, ccc, &ddd, eee, aaa, X[10], 13)
HHH(&aaa, bbb, &ccc, ddd, eee, X[ 0], 13)
HHH(&eee, aaa, &bbb, ccc, ddd, X[ 4], 7)
HHH(&ddd, eee, &aaa, bbb, ccc, X[13], 5)
/* parallel round 4 */
GGG(&ccc, ddd, &eee, aaa, bbb, X[ 8], 15)
GGG(&bbb, ccc, &ddd, eee, aaa, X[ 6], 5)
GGG(&aaa, bbb, &ccc, ddd, eee, X[ 4], 8)
GGG(&eee, aaa, &bbb, ccc, ddd, X[ 1], 11)
GGG(&ddd, eee, &aaa, bbb, ccc, X[ 3], 14)
GGG(&ccc, ddd, &eee, aaa, bbb, X[11], 14)
GGG(&bbb, ccc, &ddd, eee, aaa, X[15], 6)
GGG(&aaa, bbb, &ccc, ddd, eee, X[ 0], 14)
GGG(&eee, aaa, &bbb, ccc, ddd, X[ 5], 6)
GGG(&ddd, eee, &aaa, bbb, ccc, X[12], 9)
GGG(&ccc, ddd, &eee, aaa, bbb, X[ 2], 12)
GGG(&bbb, ccc, &ddd, eee, aaa, X[13], 9)
GGG(&aaa, bbb, &ccc, ddd, eee, X[ 9], 12)
GGG(&eee, aaa, &bbb, ccc, ddd, X[ 7], 5)
GGG(&ddd, eee, &aaa, bbb, ccc, X[10], 15)
GGG(&ccc, ddd, &eee, aaa, bbb, X[14], 8)
/* parallel round 5 */
FFF(&bbb, ccc, &ddd, eee, aaa, X[12] , 8)
FFF(&aaa, bbb, &ccc, ddd, eee, X[15] , 5)
FFF(&eee, aaa, &bbb, ccc, ddd, X[10] , 12)
FFF(&ddd, eee, &aaa, bbb, ccc, X[ 4] , 9)
FFF(&ccc, ddd, &eee, aaa, bbb, X[ 1] , 12)
FFF(&bbb, ccc, &ddd, eee, aaa, X[ 5] , 5)
FFF(&aaa, bbb, &ccc, ddd, eee, X[ 8] , 14)
FFF(&eee, aaa, &bbb, ccc, ddd, X[ 7] , 6)
FFF(&ddd, eee, &aaa, bbb, ccc, X[ 6] , 8)
FFF(&ccc, ddd, &eee, aaa, bbb, X[ 2] , 13)
FFF(&bbb, ccc, &ddd, eee, aaa, X[13] , 6)
FFF(&aaa, bbb, &ccc, ddd, eee, X[14] , 5)
FFF(&eee, aaa, &bbb, ccc, ddd, X[ 0] , 15)
FFF(&ddd, eee, &aaa, bbb, ccc, X[ 3] , 13)
FFF(&ccc, ddd, &eee, aaa, bbb, X[ 9] , 11)
FFF(&bbb, ccc, &ddd, eee, aaa, X[11] , 11)
/* combine results */
MDbuf = (MDbuf.1 &+ cc &+ ddd,
MDbuf.2 &+ dd &+ eee,
MDbuf.3 &+ ee &+ aaa,
MDbuf.4 &+ aa &+ bbb,
MDbuf.0 &+ bb &+ ccc)
}
public mutating func update(data: Data) {
var X = [UInt32](repeating: 0, count: 16)
var pos = data.startIndex
var length = data.count
// Process remaining bytes from last call:
if buffer.count > 0 && buffer.count + length >= 64 {
let amount = 64 - buffer.count
buffer.append(data[..<amount])
X.withUnsafeMutableBytes {
_ = buffer.copyBytes(to: $0)
}
compress(X)
pos += amount
length -= amount
}
// Process 64 byte chunks:
while length >= 64 {
X.withUnsafeMutableBytes {
_ = data[pos..<pos+64].copyBytes(to: $0)
}
compress(X)
pos += 64
length -= 64
}
// Save remaining unprocessed bytes:
buffer = data[pos...]
count += Int64(data.count)
}
public mutating func finalize() -> Data {
var X = [UInt32](repeating: 0, count: 16)
/* append the bit m_n == 1 */
buffer.append(0x80)
X.withUnsafeMutableBytes {
_ = buffer.copyBytes(to: $0)
}
if (count & 63) > 55 {
/* length goes to next block */
compress(X)
X = [UInt32](repeating: 0, count: 16)
}
/* append length in bits */
let lswlen = UInt32(truncatingIfNeeded: count)
let mswlen = UInt32(UInt64(count) >> 32)
X[14] = lswlen << 3
X[15] = (lswlen >> 29) | (mswlen << 3)
compress(X)
buffer = Data()
let result = [MDbuf.0, MDbuf.1, MDbuf.2, MDbuf.3, MDbuf.4]
return result.withUnsafeBytes { Data($0) }
}
}
And here are "one-shot" functions to compute the hash of a message (given as Data
or as String
):
RIPEMD160-Ext.swift:
import Foundation
public extension RIPEMD160 {
static func hash(message: Data) -> Data {
var md = RIPEMD160()
md.update(data: message)
return md.finalize()
}
static func hash(message: String) -> Data {
return RIPEMD160.hash(message: message.data(using: .utf8)!)
}
}
Finally, the corresponding message authentication code. The algorithm is taken from the pseudo-code at Wikipedia: Hash-based message authentication code:
RIPEMD160-HMAC.swift:
import Foundation
public extension RIPEMD160 {
static func hmac(key: Data, message: Data) -> Data {
var key = key
key.count = 64 // Truncate to 64 bytes or fill-up with zeros.
let outerKeyPad = Data(key.map { $0 ^ 0x5c })
let innerKeyPad = Data(key.map { $0 ^ 0x36 })
var innerMd = RIPEMD160()
innerMd.update(data: innerKeyPad)
innerMd.update(data: message)
var outerMd = RIPEMD160()
outerMd.update(data: outerKeyPad)
outerMd.update(data: innerMd.finalize())
return outerMd.finalize()
}
static func hmac(key: Data, message: String) -> Data {
return RIPEMD160.hmac(key: key, message: message.data(using: .utf8)!)
}
static func hmac(key: String, message: String) -> Data {
return RIPEMD160.hmac(key: key.data(using: .utf8)!, message: message)
}
}
Examples
To print the message digest as a hex-encoded string, the following method from How to convert Data to hex string in swift can be used:
extension Data {
func hexEncodedString() -> String {
return map { String(format: "%02hhx", $0) }.joined()
}
}
Example 1:
let msg = "Hello World"
let hash = RIPEMD160.hash(message: msg).hexEncodedString()
print(hash) // a830d7beb04eb7549ce990fb7dc962e499a27230
Example 2 (your example):
let hmac = RIPEMD160.hmac(key: "test", message: "string").hexEncodedString()
print(hmac) // 37241f2513c60ae4d9b3b8d0d30517445f451fa5