1

I need to re-implement for iOS (swift) a cryptographic operation done for an Android app (kotlin) thanks to bouncycastle's lbrary. The kotlin code is :

val mac = "9D3391051A4E774B".hexStringToByteArray()
val macKey = "89D7B23D500D492FA01DC53B44864AB8".hexStringToByteArray()
val cipheredData = "E77A914D5C94A04B6D8E10BA7A56A015AC2C40167F867A97B6349F29F3100D6D".hexStringToByteArray()

var macBlock = CBCBlockCipherMac(AESEngine(), ISO7816d4Padding())
macBlock.init(KeyParameter(macKey))
macBlock.update(cipheredData, 0, cipheredData.size)
var output = ByteArray(8)
macBlock.doFinal(output, 0)

if(output.toHex() == mac.toHex()) {
    print("equals !!")
} else {
    print("not equals : ${output.toHex()}")
}

This code works, the found mac from the output is the same as the original 'mac' property.

I tried using swift Library CryptoSwift with this code :

let mac = Data(hex: "9D3391051A4E774B")
let macKey = Data(hex: "89D7B23D500D492FA01DC53B44864AB8")
let cipheredData = Data(hex: "E77A914D5C94A04B6D8E10BA7A56A015AC2C40167F867A97B6349F29F3100D6D")

do {
    var output = try CBCMAC(key: macKey.bytes).authenticate(cipheredData.bytes)
    checkOutput(mac: mac, output: output)
} catch {
    debugPrint("Exception \(error)")
}

But this doesn't work. The algorithm behind the CryptoSwift's CBCMAC class is not doing the same think as bouncycastle's CBCBlockCipherMac.

I also tried using apple's CommonCrypto library but there is no CBCMAC authentification, only HMAC. I didn't find any way of doing CBC-MAC authentification easily for iOS plateform.

brice cesarin
  • 105
  • 2
  • 7
  • 1
    LOL, it looks like CBCMAC actually implements CMAC. How can you create a crypto lib if you don't even know the correct names? – Maarten Bodewes Sep 21 '19 at 22:35
  • 1
    CBC-MAC is simply doing CBC encryption with an all zero IV, tossing out the intermediate results and just keeping the last block, using bit padding. That should not be all that hard to implement. – Maarten Bodewes Sep 21 '19 at 22:50
  • I've commented and "upvoted" [your github issue](https://github.com/krzyzanowskim/CryptoSwift/issues/746) about this. – Maarten Bodewes Sep 21 '19 at 23:34
  • Oh, and I've for fun implemented CBC-MAC in Java, getting a result where the leftmost 8 bytes of the 16 byte result are, indeed, `9D3391051A4E774B` so your Android / Kotlin / Bouncy Castle code returns the correct result. It's the CryptoSwift library that is wrong, and that's easily confirmed by looking at the source code. – Maarten Bodewes Sep 22 '19 at 20:24
  • Tank you, I will notice this subject if I create the real CBC-MAC swift class. – brice cesarin Sep 23 '19 at 13:28

1 Answers1

1

I found a solution, developing the real CBC-MAC encryption in CryptoSwift class :

public func authenticate(_ cipheredBytes: Array<UInt8>, padding: Padding, blockSize: Int) throws -> Array<UInt8> {
    var inBytes = cipheredBytes
    bitPadding(to: &inBytes, blockSize: blockSize)
    let blocks = inBytes.chunked(into: blockSize)

    var lastBlockEncryptionResult : [UInt8] = CBCMAC.Zero
    try blocks.forEach { (block) in
        let aes = try AES(key: Array(key), blockMode: CBC(iv: lastBlockEncryptionResult), padding: padding)
        lastBlockEncryptionResult = try aes.encrypt(block)
    }

    return lastBlockEncryptionResult
}

Calling this with my initial parameters gives the answer :

9d3391051a4e774b7572fb9bca51dc51

So the first 8 bits are the good ones.

brice cesarin
  • 105
  • 2
  • 7
  • The first (leftmost) bytes are the correct ones for CBC-MAC with a configured output size of 64 bits, yes. As indicated in my comments and in the GitHub issue, you certainly *can* have an output of 16 bytes and this is usually thought as the default configuration option (which one is more secure is another discussion). – Maarten Bodewes Sep 23 '19 at 16:59
  • Oh, and you might want to use a *time-constant* comparison of the calculated result during verification. It's a bit of a shame if you correctly implement the function and then step in the next possible pitfall :) As for your parameters, for CBC-MAC there is only one type of padding and for AES the block size is known in advance (it's 128 bits / 16 bytes), so you should only use those parameters if required by the framework. – Maarten Bodewes Sep 23 '19 at 17:03
  • That's what I did on my pull request :) https://github.com/krzyzanowskim/CryptoSwift/pull/749 Thank you again for your help – brice cesarin Sep 24 '19 at 21:21