diff --git a/ext/openssl/ossl_pkcs12.c b/ext/openssl/ossl_pkcs12.c index 1fcb1a88..f5319783 100644 --- a/ext/openssl/ossl_pkcs12.c +++ b/ext/openssl/ossl_pkcs12.c @@ -251,6 +251,46 @@ ossl_pkcs12_to_der(VALUE self) return str; } +/* + * call-seq: + * pkcs12.set_mac(pass, salt = nil, iter = nil, md_type = nil) + * + * Sets MAC parameters and generates MAC over the PKCS #12 structure. + * + * This method uses HMAC and the PKCS #12 specific password-based KDF as + * specified in the original PKCS #12. + * + * See also the man page PKCS12_set_mac(3). + * + * Added in version 3.3.0. + */ +static VALUE +pkcs12_set_mac(int argc, VALUE *argv, VALUE self) +{ + PKCS12 *p12; + VALUE pass, salt, iter, md_name; + int iter_i = 0; + const EVP_MD *md_type = NULL; + + GetPKCS12(self, p12); + rb_scan_args(argc, argv, "13", &pass, &salt, &iter, &md_name); + StringValue(pass); + if (!NIL_P(salt)) + StringValue(salt); + if (!NIL_P(iter)) + iter_i = NUM2INT(iter); + if (!NIL_P(md_name)) + md_type = ossl_evp_get_digestbyname(md_name); + + if (!PKCS12_set_mac(p12, RSTRING_PTR(pass), RSTRING_LENINT(pass), + !NIL_P(salt) ? (unsigned char *)RSTRING_PTR(salt) : NULL, + !NIL_P(salt) ? RSTRING_LENINT(salt) : 0, + iter_i, md_type)) + ossl_raise(ePKCS12Error, "PKCS12_set_mac"); + + return Qnil; +} + void Init_ossl_pkcs12(void) { @@ -276,6 +316,7 @@ Init_ossl_pkcs12(void) rb_attr(cPKCS12, rb_intern("ca_certs"), 1, 0, Qfalse); rb_define_method(cPKCS12, "initialize", ossl_pkcs12_initialize, -1); rb_define_method(cPKCS12, "to_der", ossl_pkcs12_to_der, 0); + rb_define_method(cPKCS12, "set_mac", pkcs12_set_mac, -1); /* MSIE specific PKCS12 key usage extensions */ rb_define_const(cPKCS12, "KEY_EX", INT2NUM(KEY_EX)); diff --git a/test/openssl/test_pkcs12.rb b/test/openssl/test_pkcs12.rb index faf26c9e..68a23b28 100644 --- a/test/openssl/test_pkcs12.rb +++ b/test/openssl/test_pkcs12.rb @@ -337,6 +337,48 @@ def test_dup ) assert_equal p12.to_der, p12.dup.to_der end + + def test_set_mac_pkcs12kdf + p12 = OpenSSL::PKCS12.create( + "pass", + "name", + @mykey, + @mycert, + nil, + nil, + nil, + nil, + 1234, # mac_iter + nil, + ) + macdata = macdata(p12) + # Depends on the OpenSSL version: SHA256 in OpenSSL >= 3.0 + assert_include ["SHA1", "SHA256"], macdata[:mac_algo] + assert_equal 1234, macdata[:iter] + + p12.set_mac("pass", "macsalt", 2345, "SHA384") + macdata = macdata(p12) + assert_equal "SHA384", macdata[:mac_algo] + assert_equal "macsalt", macdata[:salt] + assert_equal 2345, macdata[:iter] + assert_equal @mykey.to_der, OpenSSL::PKCS12.new(p12.to_der, "pass").key.to_der + end + + private + + def macdata(p12) + # See RFC 7292 + asn1 = OpenSSL::ASN1.decode(p12.to_der) + macdata = asn1.value[2] + mac = macdata.value[0] + mac_algo = mac.value[0].value[0].value + _mac_params = mac.value[0].value[1] + { + mac_algo: mac_algo, + salt: macdata.value[1].value, + iter: macdata.value[2]&.value, + } + end end end