Add support for CMAC
Follow-up to #806.
Implements #802 by adding the OpenSSL::MAC and OpenSSL::MAC::CMAC classes.
The classes are defined if compiled against OpenSSL 3.
Thanks for working on this series.
The base class OpenSSL::MAC doesn't seem to be useful on its own currently. The EVP_MAC API seems quite generic, so I wonder if we can expose the OSSL_PARAM construction part and the EVP_MAC_init() call as a method under OpenSSL::MAC that accepts arbitrary (parameter, value) pairs. That way, OpenSSL::MAC::CMAC (or other MACs supported by EVP_MAC) could be implemented entirely in lib/openssl/mac.rb without requiring a specialized extension method.
Regarding the method naming, is there particular reason for choosing #{,hex,base64}mac instead of #*digest? Since OpenSSL::HMAC already uses #*digest, I think copying it would have less friction, though I agree it might not have been the best choice originally.
The #*mac naming seems redundant, especially when used with the shorthand methods like OpenSSL::MAC::CMAC.mac(ciph, key, data). Another idea would be to use #final taken from the C API, which is what OpenSSL::Cipher does. That said I'm not sure what name I'd give to the variant that returns the hexadecimal string.
Also, we need docs.
Thanks for the second review.
That way,
OpenSSL::MAC::CMAC(or other MACs supported byEVP_MAC) could be implemented entirely inlib/openssl/mac.rbwithout requiring a specialized extension method.
I think we can do it the way you suggest.
However, another approach has occurred to me: OpenSSL::MAC#initialize would also accept key and parameters and call EVP_MAC_init() .
Here, parameters would be an array of objects of the future OpenSSL::Parameter class, which would represent OSSL_PARAM to a certain extent.
I don't currently see which approach is better.
What do you think about these approaches?
Regarding the method naming, is there particular reason for choosing
#{,hex,base64}macinstead of#*digest?
I chose the method names like "mac" because OpenSSL refers to MACs as "MACs," distinguishing them from "digests." That said, I admit these method names are redundant when used with class names like "MAC." I propose "value" as yet another option. This is because the term "MAC value" is frequently seen, and since "value" is a noun, it can be preceded by modifiers.
I leaning towards sticking with the *digest naming here. We'll likely want a variant that returns a hexadecimal string and one-shot methods for OpenSSL::MAC, similar to what the digest or OpenSSL::HMAC already provides. Using "final" (from the C API) or "value" would require inventing a new naming scheme just for OpenSSL::MAC which I prefer not to do.
Digging through the history, it appears OpenSSL::HMAC initially had method names #hmac and #hexhmac in 2002. They were later renamed to #digest and #hexdigest around when the one-shot class methods were added. This seems like a it was a deliberate change.
https://github.com/rhenium/ruby-openssl/commit/9eb38c6de7024e3c9bae02fea182a2a3f0e306c9
That way,
OpenSSL::MAC::CMAC(or other MACs supported byEVP_MAC) could be implemented entirely inlib/openssl/mac.rbwithout requiring a specialized extension method.I think we can do it the way you suggest. However, another approach has occurred to me:
OpenSSL::MAC#initializewould also acceptkeyandparametersand callEVP_MAC_init(). Here,parameterswould be an array of objects of the futureOpenSSL::Parameterclass, which would representOSSL_PARAMto a certain extent. I don't currently see which approach is better. What do you think about these approaches?
FWIW, in #906, I've been working on exposing EVP_KDF that was also added in OpenSSL 3.0 and similarly takes an array of OSSL_PARAM for input. It's not set in stone and the PR is not finished yet, but I'm thinking of an interface to take them at one time as an enumerable of (key, value) tuples.
EVP_KEM mentioned in https://github.com/ruby/openssl/issues/894#issuecomment-2931537934 (not being worked on yet) also takes it.
I'm hoping they can have a consistent behavior.