Skip to content

Refactor legacy MAC implementations to use internal OMAC context #503

@VladGud

Description

@VladGud

Refactor legacy MAC implementations to use internal OMAC context

Summary

Introduce direct MAC method variants in legacy OMAC implementations that operate on internal OMAC contexts without depending on EVP_MD, EVP_MD_CTX, or CMAC_CTX wrappers.

Problem Description

Current OMAC/CMAC implementations (gost_omac.c, gost_omac_acpkm.c) use CMAC_CTX which internally depends on EVP_MD_CTX:

typedef struct omac_ctx {
    CMAC_CTX *cmac_ctx;  // Wraps EVP_MD_CTX
    size_t dgst_size;
    const char *cipher_name;
    int key_set;
    unsigned char key[32];
} OMAC_CTX;

This creates a dependency chain: Provider → EVP_MD_CTX → OMAC_CTX → CMAC_CTX

Required Changes

1. Extend GOST_digest structure for MAC operations (gost_lcl.h)

MACs in GOST engine are currently represented as GOST_digest (since they produce digest-like outputs). Add direct MAC methods:

struct gost_digest_st {
    // ... existing fields ...
    
    /* NEW: Provider-native MAC direct methods */
    int (*init_direct)(void *mac_data);
    int (*update_direct)(void *mac_data, const void *data, size_t count);
    int (*final_direct)(void *mac_data, unsigned char *mac);
    int (*copy_direct)(void *mac_data_to, const void *mac_data_from);
    void (*cleanup_direct)(void *mac_data);
    int (*ctrl_direct)(void *mac_data, int type, int arg, void *ptr);
};

2. Create internal OMAC context (no CMAC_CTX)

Define a new internal OMAC context that doesn't wrap CMAC_CTX:

// Internal OMAC state (direct cipher-based MAC)
typedef struct omac_direct_ctx_st {
    /* Cipher context for CBC-MAC computation */
    EVP_CIPHER_CTX *cctx;  // TODO: Replace with internal cipher context in future
    
    /* Key material */
    unsigned char key[32];
    
    /* MAC state */
    unsigned char last_block[16];
    int nlast_block;
    
    /* Current MAC accumulator */
    unsigned char mac[16];
    
    /* Cipher parameters */
    size_t dgst_size;
    int block_size;
    const char *cipher_name;
    int key_set;
} OMAC_DIRECT_CTX;

3. Implement direct OMAC operations (gost_omac.c)

Direct initialization:

static int omac_imit_init_direct(OMAC_DIRECT_CTX *c, const char *cipher_name)
{
    memset(c, 0, sizeof(OMAC_DIRECT_CTX));
    c->cipher_name = cipher_name;
    c->key_set = 0;
    c->nlast_block = 0;
    
    switch (OBJ_txt2nid(cipher_name)) {
    case NID_magma_cbc:
        c->dgst_size = 8;
        c->block_size = 8;
        break;
    case NID_grasshopper_cbc:
        c->dgst_size = 16;
        c->block_size = 16;
        break;
    default:
        return 0;
    }
    
    c->cctx = EVP_CIPHER_CTX_new();
    return (c->cctx != NULL);
}

static int magma_imit_init_direct(void *md_data)
{
    return omac_imit_init_direct((OMAC_DIRECT_CTX *)md_data, SN_magma_cbc);
}

static int grasshopper_imit_init_direct(void *md_data)
{
    return omac_imit_init_direct((OMAC_DIRECT_CTX *)md_data, SN_grasshopper_cbc);
}

Direct update:

static int omac_imit_update_direct(OMAC_DIRECT_CTX *c,
                                   const void *data, size_t count)
{
    const unsigned char *in = data;
    size_t bl = c->block_size;
    
    if (!c->key_set)
        return 0;
    
    // Process buffered partial block
    if (c->nlast_block > 0) {
        size_t to_copy = bl - c->nlast_block;
        if (to_copy > count)
            to_copy = count;
        
        memcpy(c->last_block + c->nlast_block, in, to_copy);
        c->nlast_block += to_copy;
        in += to_copy;
        count -= to_copy;
        
        if (c->nlast_block == bl) {
            // Process full block
            EVP_Cipher(c->cctx, c->mac, c->last_block, bl);
            c->nlast_block = 0;
        }
    }
    
    // Process full blocks
    while (count > bl) {
        EVP_Cipher(c->cctx, c->mac, in, bl);
        in += bl;
        count -= bl;
    }
    
    // Buffer remaining partial block
    if (count > 0) {
        memcpy(c->last_block, in, count);
        c->nlast_block = count;
    }
    
    return 1;
}

Direct final:

static int omac_imit_final_direct(OMAC_DIRECT_CTX *c, unsigned char *md)
{
    if (!c->key_set)
        return 0;
    
    size_t bl = c->block_size;
    unsigned char k1[16], k2[16];
    
    // Generate subkeys K1, K2 from current MAC state
    // (CMAC padding logic)
    
    if (c->nlast_block == bl) {
        // Complete block: XOR with K1
        for (int i = 0; i < bl; i++)
            c->last_block[i] ^= k1[i];
    } else {
        // Incomplete block: pad and XOR with K2
        c->last_block[c->nlast_block] = 0x80;
        for (int i = 0; i < bl; i++)
            c->last_block[i] ^= k2[i];
    }
    
    EVP_Cipher(c->cctx, c->mac, c->last_block, bl);
    memcpy(md, c->mac, c->dgst_size);
    
    OPENSSL_cleanse(k1, sizeof(k1));
    OPENSSL_cleanse(k2, sizeof(k2));
    
    return 1;
}

Direct control (key setting):

static int omac_imit_ctrl_direct(OMAC_DIRECT_CTX *c, int type, int arg, void *ptr)
{
    switch (type) {
    case EVP_MD_CTRL_SET_KEY: {
        if (c->key_set)
            return 0;  // Key already set
        
        const EVP_CIPHER *cipher = EVP_get_cipherbyname(c->cipher_name);
        if (!cipher)
            return 0;
        
        if (arg == 32) {
            memcpy(c->key, ptr, 32);
            if (!EVP_EncryptInit_ex(c->cctx, cipher, NULL, c->key, NULL))
                return 0;
            c->key_set = 1;
            return 1;
        }
        return 0;
    }
    case EVP_MD_CTRL_XOF_LEN: {
        // Set MAC output length
        if (c->block_size == 8 && (arg < 1 || arg > 8))
            return 0;
        if (c->block_size == 16 && (arg < 1 || arg > 16))
            return 0;
        c->dgst_size = arg;
        return 1;
    }
    default:
        return 0;
    }
}

Legacy EVP wrappers:

static int omac_imit_update(EVP_MD_CTX *ctx, const void *data, size_t count)
{
    OMAC_DIRECT_CTX *c = EVP_MD_CTX_md_data(ctx);
    return omac_imit_update_direct(c, data, count);
}

static int omac_imit_final(EVP_MD_CTX *ctx, unsigned char *md)
{
    OMAC_DIRECT_CTX *c = EVP_MD_CTX_md_data(ctx);
    return omac_imit_final_direct(c, md);
}

// ... similar for other methods

Update descriptors:

GOST_digest magma_mac_digest = {
    .nid = NID_magma_mac,
    .template = &omac_template_digest,
    .result_size = 8,
    .app_datasize = sizeof(OMAC_DIRECT_CTX),
    
    // Legacy
    .init = magma_imit_init,
    .update = omac_imit_update,
    .final = omac_imit_final,
    .copy = omac_imit_copy,
    .cleanup = omac_imit_cleanup,
    .ctrl = omac_imit_ctrl,
    
    // NEW: Direct
    .init_direct = magma_imit_init_direct,
    .update_direct = omac_imit_update_direct,
    .final_direct = omac_imit_final_direct,
    .copy_direct = omac_imit_copy_direct,
    .cleanup_direct = omac_imit_cleanup_direct,
    .ctrl_direct = omac_imit_ctrl_direct,
};

4. Implement direct CMAC-ACPKM operations (gost_omac_acpkm.c)

Similar approach for ACPKM variant, replacing CMAC_ACPKM_CTX with internal state.

4.1 Remove ENGINE parameter from CMAC_ACPKM_Init

Current signature:

static int CMAC_ACPKM_Init(CMAC_ACPKM_CTX *ctx, const void *key, size_t keylen,
                           const EVP_CIPHER *cipher, ENGINE *impl)

The ENGINE *impl parameter is not used in the function and represents legacy OpenSSL 1.x engine support. Since the refactoring eliminates engine-specific code paths, this parameter should be removed.

Changes:

  1. Remove ENGINE *impl parameter from CMAC_ACPKM_Init signature
  2. Update all call sites to remove the NULL argument passed for impl
  3. Simplify cipher lookup to not depend on engine-specific logic

New signature:

static int CMAC_ACPKM_Init(CMAC_ACPKM_CTX *ctx, const void *key, size_t keylen,
                           const EVP_CIPHER *cipher)

Impact:

  • Removes one level of legacy API dependency
  • Simplifies function signature and implementation
  • Makes it clear that ACPKM MAC operations don't require engine support

Files to Modify

  • gost_lcl.h: Add direct MAC method pointers to GOST_digest
  • gost_omac.c: Implement OMAC_DIRECT_CTX and direct methods
  • gost_omac_acpkm.c: Implement direct ACPKM variants

Acceptance Criteria

  • All MAC implementations have *_direct method variants
  • Direct methods operate on internal OMAC context
  • Legacy EVP methods delegate to direct methods
  • ENGINE-based MAC tests still pass

Testing

  • Run ENGINE MAC tests: make test TESTS=test_mac
  • Test OMAC with different key sizes
  • Test ACPKM meshing behavior

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions