krzyzanowskim / OpenSSL

OpenSSL package for SwiftPM, CocoaPod, and Carthage, multiplatform
https://swift.best
Other
910 stars 336 forks source link

EVP_CMAC_init fails but no errors #220

Open thomzon opened 1 month ago

thomzon commented 1 month ago

Hi,

I'm trying to use migrate a CMAC function from OpenSSL 1 to 3, going from the deprecated CMAC API to the EVP_MAC API. Unfortunately I am stuck, I cannot make the EVP_MAC_init call to work. I've tried many different combinations, without success, it always returns 0. Also, ERR_get_error returns 0, so something fails but apparently without adding anything to the error stack.

Here is the function, maybe I'm doing something obviously wrong? Or can there be an issue elsewhere?

    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])
    }
thomzon commented 3 weeks ago

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")
        }
    }