Skip to content

Commit

Permalink
pkcs12: add PKCS12#set_mac
Browse files Browse the repository at this point in the history
Add a binding for PKCS12_set_mac() to set MAC parameters and
(re-)calculate MAC for the content.

This allows generating PKCS #12 with consistent MAC parameters with
different OpenSSL versions. OpenSSL 3.0 changed the default hash
function used for HMAC and the KDF from SHA-1 to SHA-256.

Fixes: #772
  • Loading branch information
rhenium committed Nov 13, 2024
1 parent 3fc8972 commit d3e3338
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 0 deletions.
43 changes: 43 additions & 0 deletions ext/openssl/ossl_pkcs12.c
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,48 @@ 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;

rb_scan_args(argc, argv, "13", &pass, &salt, &iter, &md_name);
rb_check_frozen(self);
GetPKCS12(self, p12);

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)
{
Expand All @@ -276,6 +318,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));
Expand Down
42 changes: 42 additions & 0 deletions test/openssl/test_pkcs12.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down

0 comments on commit d3e3338

Please sign in to comment.