+static int gost_grasshopper_mgm_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr)
+{
+ gost_mgm_ctx *mctx =
+ (gost_mgm_ctx *)EVP_CIPHER_CTX_get_cipher_data(c);
+ unsigned char *buf, *iv;
+ int ivlen, enc;
+
+ switch (type) {
+ case EVP_CTRL_INIT:
+ ivlen = EVP_CIPHER_iv_length(EVP_CIPHER_CTX_cipher(c));
+ iv = EVP_CIPHER_CTX_iv_noconst(c);
+ mctx->key_set = 0;
+ mctx->iv_set = 0;
+ mctx->ivlen = ivlen;
+ mctx->iv = iv;
+ mctx->taglen = -1;
+ mctx->tlstree_mode = TLSTREE_MODE_NONE;
+ return 1;
+
+ case EVP_CTRL_GET_IVLEN:
+ *(int *)ptr = mctx->ivlen;
+ return 1;
+
+ case EVP_CTRL_AEAD_SET_IVLEN:
+ if (arg <= 0)
+ return 0;
+ if ((arg > EVP_MAX_IV_LENGTH) && (arg > mctx->ivlen)) {
+ // TODO: Allocate memory for IV or set error
+ return 0;
+ }
+ mctx->ivlen = arg;
+ return 1;
+
+ case EVP_CTRL_AEAD_SET_TAG:
+ buf = EVP_CIPHER_CTX_buf_noconst(c);
+ enc = EVP_CIPHER_CTX_encrypting(c);
+ if (arg <= 0 || arg != 16 || enc) {
+ GOSTerr(GOST_F_GOST_GRASSHOPPER_MGM_CTRL,
+ GOST_R_INVALID_TAG_LENGTH);
+ return 0;
+ }
+ memcpy(buf, ptr, arg);
+ mctx->taglen = arg;
+ return 1;
+
+ case EVP_CTRL_AEAD_GET_TAG:
+ buf = EVP_CIPHER_CTX_buf_noconst(c);
+ enc = EVP_CIPHER_CTX_encrypting(c);
+ if (arg <= 0 || arg > 16 || !enc || mctx->taglen < 0) {
+ GOSTerr(GOST_F_GOST_GRASSHOPPER_MGM_CTRL,
+ GOST_R_INVALID_TAG_LENGTH);
+ return 0;
+ }
+ memcpy(ptr, buf, arg);
+ return 1;
+
+ case EVP_CTRL_SET_TLSTREE_PARAMS:
+ if (strcmp((char *)ptr, "short") == 0)
+ mctx->tlstree_mode = TLSTREE_MODE_S;
+ else if (strcmp((char *)ptr, "long") == 0)
+ mctx->tlstree_mode = TLSTREE_MODE_L;
+ else {
+ // TODO: set err
+ return 0;
+ }
+ return 1;
+
+ case EVP_CTRL_TLSTREE:
+ {
+ unsigned char newkey[32];
+ if (gost_tlstree(NID_kuznyechik_mgm,
+ mctx->ks.gh_ks.master_key.k.b, newkey,
+ (const unsigned char *)ptr, mctx->tlstree_mode)
+ > 0) {
+ gost_grasshopper_cipher_key(&mctx->ks.gh_ks, newkey);
+ memset(newkey, 0, sizeof(newkey));
+ }
+ }
+ return 1;
+
+ default:
+ return -1;
+ }
+}
+
+static int gost_grasshopper_cipher_ctl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr)
+{