-
Notifications
You must be signed in to change notification settings - Fork 176
Add support for CMAC #891
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Add support for CMAC #891
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
#include "ossl.h" | ||
|
||
#if OSSL_OPENSSL_PREREQ(3, 0, 0) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe LibreSSL and AWS-LC will eventually implement the |
||
|
||
#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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Technically, I suggest taking a look at |
||
|
||
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 |
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_ */ |
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Comparing MACs generated by different algorithms or parameters would be pointless, but this doesn't prevent it and it's probably not possible unless we store parameters or sensitive keys ourselves, which would be a bad idea. Honestly, I'm not sure if this should be provided by this library. |
||
|
||
def hexmac | ||
mac.unpack1('H*') | ||
end | ||
alias inspect hexmac | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think people would expect |
||
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 |
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please sort them in alphabetical order.