swiftopensslcmac

CMAC with Swift OpenSSL 3 fails at init


I'm trying to compute a CMAC using OpenSSL 3 EVP_MAC APIs in Swift, and I am stuck at the EVP_MAC_init call, which always fails. I've tried many different combinations, without success.

If anyone managed to do this, please let me know, it's making me crazy as also the ERR_get_error() call gives me nothing, also returns 0 after the failure.

Here is the function:

static func generateAESCMAC(key: [UInt8], message: [UInt8] ) throws -> [UInt8] {
    guard let lib = OSSL_LIB_CTX_new()
    else {
        throw OpenSSLError.cmac("Cannot setup lib ctx")
    }
    defer { OSSL_LIB_CTX_free(lib) }
    
    guard let macAlgo = EVP_MAC_fetch(lib, "CMAC", nil)
    else {
        throw OpenSSLError.cmac("Cannot fetch CMAC algo")
    }
    defer { EVP_MAC_free(macAlgo) }

    guard let ctx = EVP_MAC_CTX_new(macAlgo)
    else {
        throw OpenSSLError.cmac("Cannot create CMAC ctx")
    }
    defer { EVP_MAC_CTX_free(ctx) }

    var params: [OSSL_PARAM] = []
    var cipherString = "aes-128-cbc".cString(using: .utf8)!
    let cipherParam = OSSL_PARAM_construct_utf8_string(
        OSSL_MAC_PARAM_CIPHER,
        &cipherString,
        0
    )
    params.append(cipherParam)
    params.append(OSSL_PARAM_construct_end())
    var keyValue = key
    guard EVP_MAC_init(ctx, &keyValue, key.count, params) == 1
    else {
        throw OpenSSLError.cmac("Cannot init CMAC")
    }
    
    guard EVP_MAC_update(ctx, message, message.count) == 1
    else {
        throw OpenSSLError.cmac("Cannot update CMAC")
    }
    
    var cmacResult = [UInt8](repeating: 0, count: Int(EVP_MAX_MD_SIZE))
    var cmacLength: size_t = 0
    guard EVP_MAC_final(ctx, &cmacResult, &cmacLength, cmacResult.count) == 1
    else {
        throw OpenSSLError.cmac("Cannot final CMAC")
    }
    
    return Array(cmacResult[0..<cmacLength])
}

Solution

  • Found the solution, handling of the OSSL_PARAM was not OK memory wise, resulting in an invalid OSSL_PARAM. That does not explain why the init call never generated an error, it should probably have when called with an invalid parameter list.

    The following code works:

    static func generateAESCMAC(key: [UInt8], message: [UInt8]) throws -> [UInt8] {
        guard let lib = OSSL_LIB_CTX_new()
        else {
            throw OpenSSLError.cmac("Cannot setup lib ctx")
        }
        defer { OSSL_LIB_CTX_free(lib) }
        
        guard let macAlgo = EVP_MAC_fetch(lib, "CMAC", nil)
        else {
            throw OpenSSLError.cmac("Cannot fetch CMAC algo")
        }
        defer { EVP_MAC_free(macAlgo) }
    
        guard let ctx = EVP_MAC_CTX_new(macAlgo)
        else {
            throw OpenSSLError.cmac("Cannot create CMAC ctx")
        }
        defer { EVP_MAC_CTX_free(ctx) }
    
        var cipherString = "AES-128-CBC".cString(using: .utf8)!
        try OSSL_MAC_PARAM_CIPHER.withCString { cipherKey in
            try cipherString.withUnsafeMutableBytes { cipherBytes in
                let cipherParam = OSSL_PARAM_construct_utf8_string(
                    cipherKey,
                    cipherBytes.baseAddress,
                    0
                )
                let params = [cipherParam, OSSL_PARAM_construct_end()]
                guard EVP_MAC_init(ctx, key, key.count, params) == 1
                else {
                    throw OpenSSLError.cmac("Cannot init CMAC")
                }
            }
        }
                
        guard EVP_MAC_update(ctx, message, message.count) == 1
        else {
            throw OpenSSLError.cmac("Cannot update CMAC")
        }
        
        var cmacResult = [UInt8](repeating: 0, count: Int(EVP_MAX_MD_SIZE))
        var cmacLength: size_t = 0
        
        if EVP_MAC_final(ctx, &cmacResult, &cmacLength, cmacResult.count) == 1 {
            return Array(cmacResult[0..<cmacLength])
        } else {
            throw OpenSSLError.cmac("Cannot final CMAC")
        }
    }