From 15821a48e558d595895fc8cf1c9c038d7c455550 Mon Sep 17 00:00:00 2001 From: Pauli Date: Thu, 11 May 2023 17:14:26 +1000 Subject: design proposal: fast param location outline Reviewed-by: Hugo Landau Reviewed-by: Shane Lontis (Merged from https://github.com/openssl/openssl/pull/20940) --- doc/designs/fast-param-find.md | 345 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 345 insertions(+) create mode 100644 doc/designs/fast-param-find.md (limited to 'doc/designs') diff --git a/doc/designs/fast-param-find.md b/doc/designs/fast-param-find.md new file mode 100644 index 0000000000..02460ce4bd --- /dev/null +++ b/doc/designs/fast-param-find.md @@ -0,0 +1,345 @@ +Proposal for OSSL_PARAM futures +=============================== + +Format: + +```perl +{- +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 emited 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 emited 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: + +```c + 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: + +```c +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; +} +``` -- cgit v1.2.3