Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ext/openssl/ossl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1040,6 +1040,7 @@ Init_openssl(void)
Init_ossl_engine();
Init_ossl_hmac();
Init_ossl_kdf();
Init_ossl_mac();
Init_ossl_ns_spki();
Init_ossl_ocsp();
Init_ossl_pkcs12();
Expand Down
1 change: 1 addition & 0 deletions ext/openssl/ossl.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ extern VALUE dOSSL;
#include "ossl_digest.h"
#include "ossl_engine.h"
#include "ossl_hmac.h"
#include "ossl_mac.h"
#include "ossl_kdf.h"
#include "ossl_ns_spki.h"
#include "ossl_ocsp.h"
Expand Down
174 changes: 174 additions & 0 deletions ext/openssl/ossl_mac.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#include "ossl.h"

#if OSSL_OPENSSL_PREREQ(3, 0, 0)

#define NewMAC(klass) \
TypedData_Wrap_Struct((klass), &ossl_mac_type, 0)
#define SetMAC(obj, ctx) do { \
if (!(ctx)) \
ossl_raise(rb_eRuntimeError, "MAC wasn't initialized"); \
RTYPEDDATA_DATA(obj) = (ctx); \
} while (0)
#define GetMAC(obj, ctx) do { \
TypedData_Get_Struct((obj), EVP_MAC_CTX, &ossl_mac_type, (ctx)); \
if (!(ctx)) \
ossl_raise(rb_eRuntimeError, "MAC wasn't initialized"); \
} while (0)

/*
* Classes
*/
static VALUE cMAC;
static VALUE cCMAC;
static VALUE eMACError;

/*
* Public
*/

/*
* Private
*/
static void
ossl_mac_free(void *ctx)
{
EVP_MAC_CTX_free(ctx);
}

static const rb_data_type_t ossl_mac_type = {
"OpenSSL/MAC",
{
0, ossl_mac_free,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};

static VALUE
ossl_mac_alloc(VALUE klass)
{
return NewMAC(klass);
}

static VALUE
ossl_mac_initialize(VALUE self, VALUE algorithm)
{
EVP_MAC *mac;
EVP_MAC_CTX *ctx;

mac = EVP_MAC_fetch(NULL, StringValueCStr(algorithm), NULL);
if (!mac)
ossl_raise(eMACError, "EVP_MAC_fetch");
ctx = EVP_MAC_CTX_new(mac);
if (!ctx) {
EVP_MAC_free(mac);
ossl_raise(eMACError, "EVP_MAC_CTX_new");
}
SetMAC(self, ctx);

return self;
}

static VALUE
ossl_cmac_initialize(VALUE self, VALUE cipher, VALUE key)
{
EVP_MAC_CTX *ctx;
VALUE algorithm;
OSSL_PARAM params[2];

algorithm = rb_str_new_literal("CMAC");
rb_call_super(1, &algorithm);

GetMAC(self, ctx);
StringValue(key);
params[0] = OSSL_PARAM_construct_utf8_string("cipher", StringValueCStr(cipher), 0);
params[1] = OSSL_PARAM_construct_end();
if (EVP_MAC_init(ctx, (unsigned char *)RSTRING_PTR(key), RSTRING_LEN(key), params) != 1)
ossl_raise(eMACError, "EVP_MAC_init");

return self;
}

static VALUE
ossl_mac_copy(VALUE self, VALUE other)
{
EVP_MAC_CTX *ctx1, *ctx2;

rb_check_frozen(self);
if (self == other)
return self;

GetMAC(other, ctx1);
ctx2 = EVP_MAC_CTX_dup(ctx1);
if (!ctx2)
ossl_raise(eMACError, "EVP_MAC_CTX_dup");
SetMAC(self, ctx2);

return self;
}

static VALUE
ossl_mac_update(VALUE self, VALUE chunk)
{
EVP_MAC_CTX *ctx;

GetMAC(self, ctx);
StringValue(chunk);
if (EVP_MAC_update(ctx, (unsigned char *)RSTRING_PTR(chunk), RSTRING_LEN(chunk)) != 1)
ossl_raise(eMACError, "EVP_MAC_update");

return self;
}

static VALUE
ossl_mac_mac(VALUE self)
{
VALUE ret;
EVP_MAC_CTX *ctx1, *ctx2;
size_t len;

GetMAC(self, ctx1);
if (EVP_MAC_final(ctx1, NULL, &len, 0) != 1)
ossl_raise(eMACError, "EVP_MAC_final");
ret = rb_str_new(NULL, len);
ctx2 = EVP_MAC_CTX_dup(ctx1);
if (!ctx2)
ossl_raise(eMACError, "EVP_MAC_CTX_dup");
if (EVP_MAC_final(ctx2, (unsigned char *)RSTRING_PTR(ret), &len, RSTRING_LEN(ret)) != 1) {
EVP_MAC_CTX_free(ctx2);
ossl_raise(eMACError, "EVP_MAC_final");
}
EVP_MAC_CTX_free(ctx2);

return ret;
}

/*
* INIT
*/
void
Init_ossl_mac(void)
{
#if 0
mOSSL = rb_define_module("OpenSSL");
eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
#endif

cMAC = rb_define_class_under(mOSSL, "MAC", rb_cObject);
rb_define_alloc_func(cMAC, ossl_mac_alloc);
rb_define_method(cMAC, "initialize", ossl_mac_initialize, 1);
rb_define_method(cMAC, "initialize_copy", ossl_mac_copy, 1);
rb_define_method(cMAC, "update", ossl_mac_update, 1);
rb_define_alias(cMAC, "<<", "update");
rb_define_method(cMAC, "mac", ossl_mac_mac, 0);

cCMAC = rb_define_class_under(cMAC, "CMAC", cMAC);
rb_define_method(cCMAC, "initialize", ossl_cmac_initialize, 2);

eMACError = rb_define_class_under(mOSSL, "MACError", eOSSLError);
}
#else
void
Init_ossl_mac(void)
{
}
#endif
6 changes: 6 additions & 0 deletions ext/openssl/ossl_mac.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#if !defined(_OSSL_MAC_H_)
#define _OSSL_MAC_H_

void Init_ossl_mac(void);

#endif /* _OSSL_MAC_H_ */
1 change: 1 addition & 0 deletions lib/openssl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
require_relative 'openssl/cipher'
require_relative 'openssl/digest'
require_relative 'openssl/hmac'
require_relative 'openssl/mac'
require_relative 'openssl/x509'
require_relative 'openssl/ssl'
require_relative 'openssl/pkcs5'
Expand Down
36 changes: 36 additions & 0 deletions lib/openssl/mac.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# frozen_string_literal: true

module OpenSSL
if defined?(MAC)
class MAC
def ==(other)
return false unless self.class === other
return false unless self.mac.bytesize == other.mac.bytesize

OpenSSL.fixed_length_secure_compare(self.mac, other.mac)
end

def hexmac
mac.unpack1('H*')
end
alias inspect hexmac
alias to_s hexmac

def base64mac
[mac].pack('m0')
end

class CMAC < MAC
class << self
def mac(cipher, key, message)
cmac = new(cipher, key)
cmac << message
cmac.send(__callee__)
end
alias hexmac mac
alias base64mac mac
end
end
end
end
end
34 changes: 34 additions & 0 deletions test/openssl/test_cmac.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# frozen_string_literal: true
require_relative "utils"

if defined?(OpenSSL::MAC::CMAC)

class OpenSSL::TestCMAC < OpenSSL::TestCase
def test_cmac
cmac = OpenSSL::MAC::CMAC.new("AES-128-CBC", ["2b7e151628aed2a6abf7158809cf4f3c"].pack("H*"))
cmac.update(["6bc1bee22e409f96e93d7e117393172a"].pack("H*"))
assert_equal ["070a16b46b4d4144f79bdd9dd04a287c"].pack("H*"), cmac.mac
assert_equal "070a16b46b4d4144f79bdd9dd04a287c", cmac.hexmac
assert_equal "BwoWtGtNQUT3m92d0EoofA==", cmac.base64mac
end

def test_dup
cmac1 = OpenSSL::MAC::CMAC.new("AES-192-CBC", ["8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"].pack("H*"))
cmac2 = cmac1.dup
assert_equal cmac2, cmac1

cmac1.update("message")
assert_not_equal cmac2, cmac1

cmac2.update("message")
assert_equal cmac2, cmac1
end

def test_class_methods
assert_equal ["28a7023f452e8f82bd4bf28d8c37c35c"].pack("H*"), OpenSSL::MAC::CMAC.mac("AES-256-CBC", ["603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"].pack("H*"), ["6bc1bee22e409f96e93d7e117393172a"].pack("H*"))
assert_equal "28a7023f452e8f82bd4bf28d8c37c35c", OpenSSL::MAC::CMAC.hexmac("AES-256-CBC", ["603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"].pack("H*"), ["6bc1bee22e409f96e93d7e117393172a"].pack("H*"))
assert_equal "KKcCP0Uuj4K9S/KNjDfDXA==", OpenSSL::MAC::CMAC.base64mac("AES-256-CBC", ["603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"].pack("H*"), ["6bc1bee22e409f96e93d7e117393172a"].pack("H*"))
end
end

end