openssl/doc/designs/fast-param-find.md

11 KiB

Proposal for OSSL_PARAM futures

Format:

{-
use OpenSSL::paramnames qw(produce_param_handlers);
-}

/*
 * Machine generated parameter handling
 * generated by util/perl/OpenSSL/paramnames.pm
 */
{-
produce_param_handlers(
    'name' => 'kdf_scrypt',
    'functions' => 'both',   # getter or setter being the other options
    'prologue' => "KDF_SCRYPT *ctx = vctx;",
    "static" => "yes",      # "yes" to generate static functions (default) or
                            # "no" to not
    'params' => (
        'KDF_PARAM_PASSWORD' => (
            'type' => 'octet string',
            'access' => 'writeonly',
            'setaction' => qq(
                if (!scrypt_set_membuf(&ctx->pass, &ctx->pass_len, p))
                    return 0;
            ),
        ),

        'KDF_PARAM_SALT' => (
            'type' => 'octet string',
            'access' => 'readwrite',
            'setaction' => qq(
                if (!scrypt_set_membuf(&ctx->salt, &ctx->salt_len, p))
                    return 0;
            ),
            'getaction' => qq(
                p->return_size = ctx->salt_len;
                if (p->data_size >= ctx->salt_len)
                    memcpy(p->data, ctx->salt, p->data_size >= ctx->salt_len);
            ),
        ),

        'KDF_PARAM_SCRYPT_N' => (
            'type' => 'integer',
            'ctype' => 'uint64_t',
            'access' => 'readwrite',
            'field' => "ctx->N",
            'sanitycheck' => "value > 1 && is_power_of_two(value)"
        ),

        'KDF_PARAM_SCRYPT_R' => (
            'type' => 'integer',
            'ctype' => 'uint64_t',
            'access' => 'readwrite',
            'field' => "ctx->r",
            'sanitycheck' => "value >= 1",
        ),

        'KDF_PARAM_SCRYPT_P' => (
            'type' => 'integer',
            'ctype' => 'uint64_t',
            'access' => 'readwrite',
            'field' => "ctx->p",
            'sanitycheck' => "value >= 1",
        ),

        'KDF_PARAM_SCRYPT_MAXMEM' => (
            'type' => 'integer',
            'ctype' => 'uint64_t',
            'access' => 'readwrite',
            'field' => "ctx->maxmem_bytes",
            'sanitycheck' => "value >= 1",
        ),

        'KDF_PARAM_PROPERTIES' => (
            'type' => 'utf8_string',
            'access' => 'readwrite',
            'setaction' => qq(
                if (!set_property_query(ctx, p->data) || !set_digest(ctx))
                    return 0;
            ),
        ),

        'KDF_PARAM_SIZE' => (
            'type' => 'integer',
            'ctype' => 'size_t',
            'access' => 'readonly',
            'field' => "SIZE_MAX",
        ),
    );
);
-}
/* End of generated code */

THe top level attributes are:

  • "name" is the name the functions will derive from e.g. "kdf_scrypt" to this will be appended _[gs]et[_ctx]_params
  • "functions" is the functions to generate. By default both setters and getters but either can be omitted.
  • "prologue" defines some introductory code emitted in the generated functions. Function arguments are: void *vctx, OSSL_PARAM params[] and this can be used to specialise the void pointer or declare locals.
  • "epilogue" defines some post decode code emitted in the generated function
  • "params" defines the parameters both gettable and settable

Within the "params" the fields specify each parameter by label.

Each parameter is then specialised with attributes:

  • "type" is the OSSL_PARAM type

  • "ctype" is the underlying C type (e.g. for an integer parameter size_t could be the C type)

  • "access" is readwrite, readonly or writeonly. This determines if the parameter is a settable, gettable or both

  • "field" is an accessor to the field itself

  • "sanitycheck" is a validation check for the parameter. If present, code will be generated if (!(sanitycheck)) return 0; The local variable var will contain the C value if specified.

  • "setaction" is C code to execute when the parameter is being set. It will define an OSSL_PARAM pointer p to set.

  • "code" set to "no" skips code generation for this parameter, it defaults to "yes" which generates handlers. This is useful when a parameter is duplicated with differenting types (e.g. utf8 string and integer).

  • "published" set to "yes" includes the parameter in the gettable/settable lists. Set to "no" and it isn't included (but will still be processed). It defaults to "yes".

  • Flags include:

    • nostatic: do not make the function static
    • nocode: do not generate code for this parameter
      • This allows, e.g., two different types for a parameter (int & string)
    • unpublished: do not generate this parameter in the gettable/settable list
      • "engine" is the only one like this
    • readonly: create a getter but not a setter
    • writeonly: create a setting but not a getter

The idea is that the gettable and get functions will be simultaneously generated along with fast decoder to look up parameter names quickly.

The getter and setter functions will be pre-populated with some local variable:

    OSSL_PARAM *p; /* The matching parameter */
    type val;      /* The value of the parameter after a get/set call */
                   /* (for C types) */

A worked example for scrypt:

Would generate something along the lines of:

enum kdf_scrypt_ctx_param_e {
    kdf_scrypt_ctx_param_INVALID,
    kdf_scrypt_ctx_param_OSSL_KDF_PARAM_PASSWORD,
    kdf_scrypt_ctx_param_OSSL_KDF_PARAM_PROPERTIES,
    kdf_scrypt_ctx_param_OSSL_KDF_PARAM_SALT,
    kdf_scrypt_ctx_param_OSSL_KDF_PARAM_SCRYPT_MAXMEM,
    kdf_scrypt_ctx_param_OSSL_KDF_PARAM_SCRYPT_N,
    kdf_scrypt_ctx_param_OSSL_KDF_PARAM_SCRYPT_P,
    kdf_scrypt_ctx_param_OSSL_KDF_PARAM_SCRYPT_R,
    kdf_scrypt_ctx_param_OSSL_KDF_PARAM_SIZE
};


static enum kdf_scrypt_ctx_param_e kdf_scrypt_ctx_lookup(const OSSL_PARAM *p) {
    /* magic decoder */
    return kdf_scrypt_ctx_param_INVALID;
}

static int kdf_scrypt_set_ctx_params(void *vctx, const OSSL_PARAM params[])
{
    const OSSL_PARAM *p;
    KDF_SCRYPT *ctx = vctx;

    if (params == NULL)
        return 1;

    for (p = params; p->key != NULL; p++) {
        switch (kdf_scrypt_ctx_lookup(p)) {
        default:
            break;

        case kdf_scrypt_ctx_param_OSSL_KDF_PARAM_PASSWORD:
            if (!scrypt_set_membuf(&ctx->pass, &ctx->pass_len, p))
                return 0;
            break;

        case kdf_scrypt_ctx_param_OSSL_KDF_PARAM_SALT:
            if (!scrypt_set_membuf(&ctx->salt, &ctx->salt_len, p))
                return 0;
            break;

        case kdf_scrypt_ctx_param_OSSL_KDF_PARAM_SCRYPT_N: {
            uint64_t value;

            if (!OSSL_PARAM_get_uint64(p, &value) {
                if (!(value > 1 && is_power_of_two(u64_value)))
                    return 0;
                ctx->N = value;
            }
            break;
        }

        case kdf_scrypt_ctx_param_OSSL_KDF_PARAM_SCRYPT_R: {
            uint64_t value;

            if (!OSSL_PARAM_get_uint64(p, &value) {
                if (!(value >= 1))
                    return 0;
                ctx->r = value;
            }
            break;
        }

        case kdf_scrypt_ctx_param_OSSL_KDF_PARAM_SCRYPT_P: {
            uint64_t value;

            if (!OSSL_PARAM_get_uint64(p, &value) {
                if (!(value >= 1))
                    return 0;
                ctx->p = value;
            }
            break;
        }

        case kdf_scrypt_ctx_param_OSSL_KDF_PARAM_SCRYPT_MAXMEM: {
            uint64_t value;

            if (!OSSL_PARAM_get_uint64(p, &value) {
                if (!(value >= 1))
                    return 0;
                ctx->p = value;
            }
            break;
        }

        case kdf_scrypt_ctx_param_OSSL_KDF_PARAM_PROPERTIES:
            if (p != NULL) {
                if (p->data_type != OSSL_PARAM_UTF8_STRING) {
                    if (!set_property_query(ctx, p->data) || !set_digest(ctx))
                        return 0;
                    }
            }
        }
    }

    return 1;
}

static const OSSL_PARAM *kdf_scrypt_settable_ctx_params(ossl_unused void *ctx,
                                                        ossl_unused void *p_ctx)
{
    static const OSSL_PARAM known_settable_ctx_params[] = {
        OSSL_PARAM_octet_string(OSSL_KDF_PARAM_PASSWORD, NULL, 0),
        OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0),
        OSSL_PARAM_uint64(OSSL_KDF_PARAM_SCRYPT_N, NULL),
        OSSL_PARAM_uint32(OSSL_KDF_PARAM_SCRYPT_R, NULL),
        OSSL_PARAM_uint32(OSSL_KDF_PARAM_SCRYPT_P, NULL),
        OSSL_PARAM_uint64(OSSL_KDF_PARAM_SCRYPT_MAXMEM, NULL),
        OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0),
        OSSL_PARAM_END
    };
    return known_settable_ctx_params;
}

static int kdf_scrypt_get_ctx_params(void *vctx, OSSL_PARAM params[])
{
    const OSSL_PARAM *p;
    KDF_SCRYPT *ctx = vctx;

    if (params == NULL)
        return 1;

    for (p = params; p->key != NULL; p++) {
        switch (kdf_scrypt_ctx_lookup(p)) {
        default:
            break;

        case kdf_scrypt_ctx_param_OSSL_KDF_PARAM_PASSWORD:
            if (!scrypt_set_membuf(&ctx->pass, &ctx->pass_len, p))
                return 0;
            break;

        case kdf_scrypt_ctx_param_OSSL_KDF_PARAM_SALT:
            p->return_size = ctx->salt_len;
            if (p->data_size >= ctx->salt_len)
                memcpy(p->data, ctx->salt, ctx->salt_len);
            break;

        case kdf_scrypt_ctx_param_OSSL_KDF_PARAM_SCRYPT_N: {
            if (!OSSL_PARAM_set_uint64(p, &ctx->N)
                return 0;
            break;
        }

        case kdf_scrypt_ctx_param_OSSL_KDF_PARAM_SCRYPT_R: {
            if (!OSSL_PARAM_set_uint64(p, &ctx->r)
                return 0;
            break;
        }

        case kdf_scrypt_ctx_param_OSSL_KDF_PARAM_SCRYPT_P: {
            if (!OSSL_PARAM_set_uint64(p, &ctx->p)
                return 0;
            break;
        }

        case kdf_scrypt_ctx_param_OSSL_KDF_PARAM_SCRYPT_MAXMEM: {
            if (!OSSL_PARAM_set_uint64(p, &ctx->maxmem)
                return 0;
            break;
        }

        case kdf_scrypt_ctx_param_OSSL_KDF_PARAM_PROPERTIES:
            if (p->data_type != OSSL_PARAM_UTF8_STRING) {
                if (!set_property_query(ctx, p->data) || !set_digest(ctx))
                    return 0;
            }
            break;

        case kdf_scrypt_ctx_param_KDF_PARAM_SIZE:
            if (!OSSL_PARAM_set_size_t(p, SIZE_MAX))
                return 0;
            break;
        }
    }
    return 1;
}

static const OSSL_PARAM *kdf_scrypt_gettable_ctx_params(ossl_unused void *ctx,
                                                        ossl_unused void *p_ctx)
{
    static const OSSL_PARAM known_gettable_ctx_params[] = {
        OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL),
        OSSL_PARAM_END
    };
    return known_gettable_ctx_params;
}