From 5a85aaa6148955e10da433351589d10a5458a3c5 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sat, 21 May 2016 20:52:54 +0900 Subject: wip --- ext/openssl/ossl_cipher.c | 89 ++++++++++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 36 deletions(-) diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c index 09b021d987..bbb738c22a 100644 --- a/ext/openssl/ossl_cipher.c +++ b/ext/openssl/ossl_cipher.c @@ -29,6 +29,15 @@ GetCipher((obj), (ctx)); \ } while (0) + +#define ossl_is_gcm(nid) ((nid) == NID_aes_128_gcm || \ + (nid) == NID_aes_192_gcm || \ + (nid) == NID_aes_256_gcm) + +#define ossl_is_ccm(nid) ((nid) == NID_aes_128_ccm || \ + (nid) == NID_aes_192_ccm || \ + (nid) == NID_aes_256_ccm) + /* * Classes */ @@ -512,12 +521,32 @@ static VALUE ossl_cipher_set_iv(VALUE self, VALUE iv) { EVP_CIPHER_CTX *ctx; + int iv_len, new_len; StringValue(iv); GetCipher(self, ctx); - if (RSTRING_LEN(iv) < EVP_CIPHER_CTX_iv_length(ctx)) - ossl_raise(eCipherError, "iv length too short"); + iv_len = EVP_CIPHER_CTX_iv_length(ctx); + new_len = RSTRING_LENINT(iv); + if (new_len != iv_len) { + int nid = EVP_CIPHER_CTX_nid(ctx); + + /* if the cipher is GCM/CCM mode, the IV length can be changed */ + if (ossl_is_gcm(nid)) { + if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, new_len, NULL)) + ossl_raise(eCipherError, NULL); + } + else if (ossl_is_ccm(nid)) { + if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, new_len, NULL)) + ossl_raise(eCipherError, NULL); + } + else { + if (new_len < iv_len) + ossl_raise(eCipherError, "iv length too short"); + else + rb_warn("iv too long; truncated to %d bytes", iv_len); + } + } if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, (unsigned char *)RSTRING_PTR(iv), -1) != 1) ossl_raise(eCipherError, NULL); @@ -564,29 +593,9 @@ ossl_cipher_set_auth_data(VALUE self, VALUE data) return data; } -#define ossl_is_gcm(nid) (nid) == NID_aes_128_gcm || \ - (nid) == NID_aes_192_gcm || \ - (nid) == NID_aes_256_gcm - -static VALUE -ossl_get_gcm_auth_tag(EVP_CIPHER_CTX *ctx, int len) -{ - unsigned char *tag; - VALUE ret; - - tag = ALLOC_N(unsigned char, len); - - if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, len, tag)) - ossl_raise(eCipherError, "retrieving the authentication tag failed"); - - ret = rb_str_new((const char *) tag, len); - xfree(tag); - return ret; -} - /* * call-seq: - * cipher.auth_tag([ tag_len ] -> string + * cipher.auth_tag([tag_len]) -> string * * Gets the authentication tag generated by Authenticated Encryption Cipher * modes (GCM for example). This tag may be stored along with the ciphertext, @@ -601,7 +610,7 @@ ossl_get_gcm_auth_tag(EVP_CIPHER_CTX *ctx, int len) static VALUE ossl_cipher_get_auth_tag(int argc, VALUE *argv, VALUE self) { - VALUE vtag_len; + VALUE vtag_len, ret; EVP_CIPHER_CTX *ctx; int nid, tag_len; @@ -614,19 +623,21 @@ ossl_cipher_get_auth_tag(int argc, VALUE *argv, VALUE self) GetCipher(self, ctx); nid = EVP_CIPHER_CTX_nid(ctx); + ret = rb_str_new(NULL, tag_len); + if (ossl_is_gcm(nid)) { - return ossl_get_gcm_auth_tag(ctx, tag_len); - } else { + if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, tag_len, RSTRING_PTR(ret))) + ossl_raise(eCipherError, "retrieving the authentication tag failed"); + } + else if (ossl_is_ccm(nid)) { + if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_GET_TAG, tag_len, RSTRING_PTR(ret))) + ossl_raise(eCipherError, "retrieving the authentication tag failed"); + } + else { ossl_raise(eCipherError, "authentication tag not supported by this cipher"); - return Qnil; /* dummy */ } -} -static inline void -ossl_set_gcm_auth_tag(EVP_CIPHER_CTX *ctx, unsigned char *tag, int tag_len) -{ - if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, tag_len, tag)) - ossl_raise(eCipherError, "unable to set GCM tag"); + return ret; } /* @@ -657,8 +668,14 @@ ossl_cipher_set_auth_tag(VALUE self, VALUE vtag) nid = EVP_CIPHER_CTX_nid(ctx); if (ossl_is_gcm(nid)) { - ossl_set_gcm_auth_tag(ctx, tag, tag_len); - } else { + if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, tag_len, tag)) + ossl_raise(eCipherError, "unable to set GCM tag"); + } + else if (ossl_is_ccm(nid)) { + if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, tag_len, tag)) + ossl_raise(eCipherError, "unable to set CCM tag"); + } + else { ossl_raise(eCipherError, "authentication tag not supported by this cipher"); } @@ -681,7 +698,7 @@ ossl_cipher_is_authenticated(VALUE self) GetCipher(self, ctx); nid = EVP_CIPHER_CTX_nid(ctx); - if (ossl_is_gcm(nid)) { + if (ossl_is_gcm(nid) || ossl_is_ccm(nid)) { return Qtrue; } else { return Qfalse; -- cgit v1.2.3