njs_generate_try_end(njs_vm_t *vm, njs_generator_t *generator,
    njs_parser_node_t *node)
{
    njs_int_t                ret;
    njs_index_t              exit_index;
    const njs_str_t          *dest_label;
    njs_vmcode_finally_t     *finally;
    njs_generator_patch_t    *patch;
    njs_generator_block_t    *block, *try_block, *catch_block;
    njs_generator_try_ctx_t  *ctx;

    ctx = generator->context;

    try_block = ctx->try_block;
    exit_index = try_block->index;
    catch_block = ctx->catch_block;

    njs_generate_code_finally(generator, finally, ctx->exception_index,
                              exit_index, node);

    if (try_block->continuation != NULL
        || (catch_block && catch_block->continuation != NULL))
    {
        dest_label = njs_generate_jump_destination(vm, generator->block,
                                                   "try continue",
                                                   NJS_GENERATOR_LOOP,
                                                   &ctx->try_cont_label,
                                                   &ctx->catch_cont_label);
        if (njs_slow_path(dest_label == NULL)) {
            return NJS_ERROR;
        }

        /*
         * block != NULL is checked
         * by njs_generate_continue_statement()
         */
        block = njs_generate_find_block(vm, generator->block,
                                        NJS_GENERATOR_LOOP, dest_label);

        patch = njs_generate_make_continuation_patch(vm, block, dest_label,
                         njs_code_offset(generator, finally)
                         + offsetof(njs_vmcode_finally_t, continue_offset));
        if (njs_slow_path(patch == NULL)) {
            return NJS_ERROR;
        }
    }

    if (try_block->exit != NULL
        || (catch_block != NULL && catch_block->exit != NULL))
    {
        dest_label = njs_generate_jump_destination(vm, generator->block,
                                                   "try break/return",
                                                   NJS_GENERATOR_ALL
                                                   | NJS_GENERATOR_TRY,
                                                   &ctx->try_exit_label,
                                                   &ctx->catch_exit_label);
        if (njs_slow_path(dest_label == NULL)) {
            return NJS_ERROR;
        }

        /*
         * block can be NULL for "return" instruction in
         * outermost try-catch block.
         */
        block = njs_generate_find_block(vm, generator->block,
                                        NJS_GENERATOR_ALL, dest_label);
        if (block != NULL) {
            patch = njs_generate_make_exit_patch(vm, block, dest_label,
                            njs_code_offset(generator, finally)
                            + offsetof(njs_vmcode_finally_t, break_offset));
            if (njs_slow_path(patch == NULL)) {
                return NJS_ERROR;
            }

        } else {

            block = njs_generate_find_block(vm, generator->block,
                                            NJS_GENERATOR_ALL, &no_label);
            if (block != NULL) {
                patch = njs_generate_make_exit_patch(vm, block, &no_label,
                                njs_code_offset(generator, finally)
                                + offsetof(njs_vmcode_finally_t, break_offset));
                if (njs_slow_path(patch == NULL)) {
                    return NJS_ERROR;
                }
            }
        }
    }

    ret = njs_generate_index_release(vm, generator, ctx->exception_index);
    if (njs_slow_path(ret != NJS_OK)) {
        return ret;
    }

    return njs_generator_stack_pop(vm, generator, ctx);
}


njs_generate_for_update(njs_vm_t *vm, njs_generator_t *generator,
    njs_parser_node_t *node)
{
    njs_int_t                 ret;
    njs_parser_node_t         *condition, *update;
    njs_generator_loop_ctx_t  *ctx;

    ctx = generator->context;

    update = node->right->right->right;
    condition = node->right->left;

    ret = njs_generate_node_index_release(vm, generator, update);
    if (njs_slow_path(ret != NJS_OK)) {
        return ret;
    }

    /* The loop condition. */

    if (condition != NULL) {
        njs_code_set_jump_offset(generator, njs_vmcode_jump_t,
                                 ctx->jump_offset);

        njs_generator_next(generator, njs_generate, condition);

        return njs_generator_after(vm, generator,
                                   njs_queue_first(&generator->stack), node,
                                   njs_generate_for_end, ctx, 0);
    }

    return njs_generate_for_end(vm, generator, node);
}


njs_generate_operation_assignment_end(njs_vm_t *vm, njs_generator_t *generator,
    njs_parser_node_t *node)
{
    njs_int_t              ret;
    njs_parser_node_t      *lvalue, *expr;
    njs_vmcode_3addr_t     *code;
    njs_vmcode_prop_set_t  *prop_set;

    lvalue = node->left;
    expr = node->right;

    njs_generate_code(generator, njs_vmcode_3addr_t, code,
                      node->u.operation, 3, expr);
    code->dst = node->index;
    code->src1 = node->index;
    code->src2 = expr->index;

    njs_generate_code(generator, njs_vmcode_prop_set_t, prop_set,
                      NJS_VMCODE_PROPERTY_SET, 3, expr);
    prop_set->value = node->index;
    prop_set->object = lvalue->left->index;
    prop_set->property = lvalue->right->index;

    ret = njs_generate_children_indexes_release(vm, generator, lvalue);
    if (njs_slow_path(ret != NJS_OK)) {
        return ret;
    }

    return njs_generate_node_index_release_pop(vm, generator, expr);
}


njs_generate_method_call(njs_vm_t *vm, njs_generator_t *generator,
    njs_parser_node_t *node)
{
    njs_int_t          ret;
    njs_parser_node_t  *prop;

    prop = node->left;

    /* Object. */

    njs_generator_next(generator, njs_generate, prop->left);

    ret = njs_generator_after(vm, generator,
                              njs_queue_first(&generator->stack), node,
                              njs_generate_method_call_arguments, NULL, 0);
    if (njs_slow_path(ret != NJS_OK)) {
        return ret;
    }

    /* Method name. */

    return njs_generator_after(vm, generator,
                               njs_queue_first(&generator->stack), prop->right,
                               njs_generate, NULL, 0);
}


njs_generate_typeof_operation_end(njs_vm_t *vm, njs_generator_t *generator,
    njs_parser_node_t *node)
{
    njs_vmcode_2addr_t  *code;

    njs_generate_code(generator, njs_vmcode_2addr_t, code,
                      node->u.operation, 2, node->left);
    code->src = node->left->index;

    node->index = njs_generate_dest_index(vm, generator, node);
    if (njs_slow_path(node->index == NJS_INDEX_ERROR)) {
        return node->index;
    }

    code->dst = node->index;

    njs_debug_generator_code(code);

    return njs_generator_stack_pop(vm, generator, NULL);
}


static json_t * generate_credential_fake_from_seed(const char * seed) {
  unsigned char credential_id[64] = {0}, credential_id_b64[129], created_at[32], name_hash[32];
  char * seed_credential_id, * seed_name, * seed_created_at, name[32];
  time_t created_at_t;
  size_t credential_id_len = 64, credential_id_b64_len, name_hash_len = 32, created_at_len = 32;
  json_t * j_return;

  if ((seed_credential_id = msprintf("%s-credential_id", seed)) != NULL) {
    if (generate_digest_raw(digest_SHA512, (unsigned char *)seed_credential_id, o_strlen(seed_credential_id), credential_id, &credential_id_len)) {
      if (o_base64_encode(credential_id, credential_id_len, credential_id_b64, &credential_id_b64_len)) {
        if ((seed_name = msprintf("%s-name", seed)) != NULL) {
          if (generate_digest_raw(digest_SHA256, (unsigned char *)seed_name, o_strlen(seed_name), name_hash, &name_hash_len)) {
            if (name_hash[0]%2) {
              o_strcpy(name, "fido-u2f");
            } else {
              o_strcpy(name, "android-safetynet");
            }
            if ((seed_created_at = msprintf("%s-created_at", seed)) != NULL) {
              if (generate_digest_raw(digest_SHA256, (unsigned char *)seed_created_at, o_strlen(seed_created_at), created_at, &created_at_len)) {
                time(&created_at_t);
                created_at_t -= created_at[0] - (created_at[1] << 8);
                j_return = json_pack("{sis{sssssiss}}",
                                     "result",
                                     G_OK,
                                     "credential",
                                      "credential_id",
                                      credential_id_b64,
                                      "name",
                                      name,
                                      "created_at",
                                      created_at_t,
                                      "status",
                                      "registered");
              } else {
                y_log_message(Y_LOG_LEVEL_ERROR, "generate_credential_fake_from_seed - Error generate_digest_raw for seed_created_at");
                j_return = json_pack("{si}", "result", G_ERROR);
              }
            } else {
              y_log_message(Y_LOG_LEVEL_ERROR, "generate_credential_fake_from_seed - Error allocating resources for seed_created_at");
              j_return = json_pack("{si}", "result", G_ERROR_MEMORY);
            }
            o_free(seed_created_at);
          } else {
            y_log_message(Y_LOG_LEVEL_ERROR, "generate_credential_fake_from_seed - Error generate_digest_raw for seed_name");
            j_return = json_pack("{si}", "result", G_ERROR);
          }
        } else {
          y_log_message(Y_LOG_LEVEL_ERROR, "generate_credential_fake_from_seed - Error allocating resources for seed_name");
          j_return = json_pack("{si}", "result", G_ERROR_MEMORY);
        }
        o_free(seed_name);
      } else {
        y_log_message(Y_LOG_LEVEL_ERROR, "generate_credential_fake_from_seed - Error o_base64_encode for seed_credential_id");
        j_return = json_pack("{si}", "result", G_ERROR);
      }
    } else {
      y_log_message(Y_LOG_LEVEL_ERROR, "generate_credential_fake_from_seed - Error generate_digest_raw for seed_credential_id");
      j_return = json_pack("{si}", "result", G_ERROR);
    }
  } else {
    y_log_message(Y_LOG_LEVEL_ERROR, "generate_credential_fake_from_seed - Error allocating resources for seed_credential_id");
    j_return = json_pack("{si}", "result", G_ERROR_MEMORY);
  }
  o_free(seed_credential_id);
  return j_return;
}


static json_t * check_attestation_fido_u2f(json_t * j_params, unsigned char * credential_id, size_t credential_id_len, unsigned char * cert_x, size_t cert_x_len, unsigned char * cert_y, size_t cert_y_len, cbor_item_t * att_stmt, unsigned char * rpid_hash, size_t rpid_hash_len, const unsigned char * client_data) {
  json_t * j_error = json_array(), * j_return;
  cbor_item_t * key = NULL, * x5c = NULL, * sig = NULL, * att_cert = NULL;
  int i, ret;
  char * message = NULL;
  gnutls_pubkey_t pubkey = NULL;
  gnutls_x509_crt_t cert = NULL;
  gnutls_datum_t cert_dat, data, signature, cert_issued_by;
  unsigned char * data_signed = NULL, client_data_hash[32], cert_export[32], cert_export_b64[64];
  size_t data_signed_offset = 0, client_data_hash_len = 32, cert_export_len = 32, cert_export_b64_len = 0;

  if (j_error != NULL) {
    do {
      if (gnutls_x509_crt_init(&cert)) {
        json_array_append_new(j_error, json_string("check_attestation_fido_u2f - Error gnutls_x509_crt_init"));
        break;
      }
      if (gnutls_pubkey_init(&pubkey)) {
        json_array_append_new(j_error, json_string("check_attestation_fido_u2f - Error gnutls_pubkey_init"));
        break;
      }

      // Step 1
      if (att_stmt == NULL || !cbor_isa_map(att_stmt) || cbor_map_size(att_stmt) != 2) {
        json_array_append_new(j_error, json_string("CBOR map value 'attStmt' invalid format"));
        break;
      }
      for (i=0; i<2; i++) {
        key = cbor_map_handle(att_stmt)[i].key;
        if (cbor_isa_string(key)) {
          if (0 == o_strncmp((const char *)cbor_string_handle(key), "x5c", MIN(o_strlen("x5c"), cbor_string_length(key)))) {
            x5c = cbor_map_handle(att_stmt)[i].value;
          } else if (0 == o_strncmp((const char *)cbor_string_handle(key), "sig", MIN(o_strlen("sig"), cbor_string_length(key)))) {
            sig = cbor_map_handle(att_stmt)[i].value;
          } else {
            message = msprintf("attStmt map element %d key is not valid: '%.*s'", i, cbor_string_length(key), cbor_string_handle(key));
            json_array_append_new(j_error, json_string(message));
            o_free(message);
            break;
          }
        } else {
          message = msprintf("attStmt map element %d key is not a string", i);
          json_array_append_new(j_error, json_string(message));
          o_free(message);
          break;
        }
      }
      if (x5c == NULL || !cbor_isa_array(x5c) || cbor_array_size(x5c) != 1) {
        json_array_append_new(j_error, json_string("CBOR map value 'x5c' invalid format"));
        break;
      }
      att_cert = cbor_array_get(x5c, 0);
      cert_dat.data = cbor_bytestring_handle(att_cert);
      cert_dat.size = cbor_bytestring_length(att_cert);
      if ((ret = gnutls_x509_crt_import(cert, &cert_dat, GNUTLS_X509_FMT_DER)) < 0) {
        json_array_append_new(j_error, json_string("Error importing x509 certificate"));
        y_log_message(Y_LOG_LEVEL_DEBUG, "check_attestation_fido_u2f - Error gnutls_pcert_import_x509_raw: %d", ret);
        break;
      }
      if (json_object_get(j_params, "root-ca-list") != json_null() && validate_certificate_from_root(j_params, cert, x5c) != G_OK) {
        json_array_append_new(j_error, json_string("Unrecognized certificate authority"));
        if (gnutls_x509_crt_get_issuer_dn2(cert, &cert_issued_by) >= 0) {
          message = msprintf("Unrecognized certificate autohority: %.*s", cert_issued_by.size, cert_issued_by.data);
          y_log_message(Y_LOG_LEVEL_DEBUG, "check_attestation_fido_u2f - %s", message);
          o_free(message);
          gnutls_free(cert_issued_by.data);
        } else {
          y_log_message(Y_LOG_LEVEL_DEBUG, "check_attestation_fido_u2f - Unrecognized certificate autohority (unable to get issuer dn)");
        }
        break;
      }
      if ((ret = gnutls_pubkey_import_x509(pubkey, cert, 0)) < 0) {
        json_array_append_new(j_error, json_string("Error importing x509 certificate"));
        y_log_message(Y_LOG_LEVEL_DEBUG, "check_attestation_fido_u2f - Error gnutls_pubkey_import_x509: %d", ret);
        break;
      }
      if ((ret = gnutls_x509_crt_get_key_id(cert, GNUTLS_KEYID_USE_SHA256, cert_export, &cert_export_len)) < 0) {
        json_array_append_new(j_error, json_string("Error exporting x509 certificate"));
        y_log_message(Y_LOG_LEVEL_DEBUG, "check_attestation_fido_u2f - Error gnutls_x509_crt_get_key_id: %d", ret);
        break;
      }
      if (!o_base64_encode(cert_export, cert_export_len, cert_export_b64, &cert_export_b64_len)) {
        json_array_append_new(j_error, json_string("Internal error"));
        y_log_message(Y_LOG_LEVEL_DEBUG, "check_attestation_fido_u2f - Error o_base64_encode cert_export");
        break;
      }
      if (!generate_digest_raw(digest_SHA256, client_data, o_strlen((char *)client_data), client_data_hash, &client_data_hash_len)) {
        json_array_append_new(j_error, json_string("Internal error"));
        y_log_message(Y_LOG_LEVEL_ERROR, "check_attestation_fido_u2f - Error generate_digest_raw client_data");
        break;
      }

      if (sig == NULL || !cbor_isa_bytestring(sig)) {
        json_array_append_new(j_error, json_string("Error sig is not a bytestring"));
        break;
      }

      if ((data_signed = o_malloc(rpid_hash_len+client_data_hash_len+credential_id_len+cert_x_len+cert_y_len+2)) == NULL) {
        y_log_message(Y_LOG_LEVEL_DEBUG, "check_attestation_fido_u2f - Error allocating data_signed");
        json_array_append_new(j_error, json_string("Internal error"));
        break;
      }

      // Build bytestring to verify signature
      data_signed[0] = 0x0;
      data_signed_offset = 1;

      memcpy(data_signed+data_signed_offset, rpid_hash, rpid_hash_len);
      data_signed_offset += rpid_hash_len;

      memcpy(data_signed+data_signed_offset, client_data_hash, client_data_hash_len);
      data_signed_offset+=client_data_hash_len;

      memcpy(data_signed+data_signed_offset, credential_id, credential_id_len);
      data_signed_offset+=credential_id_len;

      data_signed[data_signed_offset] = 0x04;
      data_signed_offset++;

      memcpy(data_signed+data_signed_offset, cert_x, cert_x_len);
      data_signed_offset+=cert_x_len;

      memcpy(data_signed+data_signed_offset, cert_y, cert_y_len);
      data_signed_offset+=cert_y_len;

      // Let's verify sig over data_signed
      data.data = data_signed;
      data.size = data_signed_offset;

      signature.data = cbor_bytestring_handle(sig);
      signature.size = cbor_bytestring_length(sig);

      if (gnutls_pubkey_verify_data2(pubkey, GNUTLS_SIGN_ECDSA_SHA256, 0, &data, &signature)) {
        json_array_append_new(j_error, json_string("Invalid signature"));
      }

    } while (0);
    o_free(data_signed);

    if (json_array_size(j_error)) {
      j_return = json_pack("{sisO}", "result", G_ERROR_PARAM, "error", j_error);
    } else {
      j_return = json_pack("{sis{ss%}}", "result", G_OK, "data", "certificate", cert_export_b64, cert_export_b64_len);
    }
    json_decref(j_error);
    gnutls_pubkey_deinit(pubkey);
    gnutls_x509_crt_deinit(cert);
    if (att_cert != NULL) {
      cbor_decref(&att_cert);
    }

  } else {
    y_log_message(Y_LOG_LEVEL_ERROR, "check_attestation_fido_u2f - Error allocating resources for j_error");
    j_return = json_pack("{si}", "result", G_ERROR);
  }
  return j_return;
}


json_t * user_auth_scheme_module_trigger(struct config_module * config, const struct _u_request * http_request, const char * username, json_t * j_scheme_trigger, void * cls) {
  UNUSED(j_scheme_trigger);
  json_t * j_return = NULL, * j_session = config->glewlwyd_module_callback_check_user_session(config, http_request, username), * j_credential, * j_assertion, * j_user_id, * j_credential_fake;
  unsigned char user_id_fake[64];

  if (check_result_value(j_session, G_OK) || json_object_get((json_t *)cls, "session-mandatory") == json_false()) {
    j_credential_fake = generate_credential_fake_list((json_t *)cls, username);
    if (check_result_value(j_credential_fake, G_OK)) {
      j_user_id = get_user_id_from_username(config, (json_t *)cls, username, 0);
      if (check_result_value(j_user_id, G_OK)) {
        j_credential = get_credential_list(config, (json_t *)cls, username, 1);
        if (check_result_value(j_credential, G_OK)) {
          j_assertion = generate_new_assertion(config, (json_t *)cls, username, 0);
          if (check_result_value(j_assertion, G_OK)) {
            j_return = json_pack("{sis{sOsOsOs{sOss}sOsssi}}",
                                "result", G_OK,
                                "response",
                                  "allowCredentials", json_object_get(j_credential, "credential"),
                                  "session", json_object_get(json_object_get(j_assertion, "assertion"), "session"),
                                  "challenge", json_object_get(json_object_get(j_assertion, "assertion"), "challenge"),
                                  "user",
                                    "id", json_object_get(j_user_id, "user_id"),
                                    "name", username,
                                  "rpId", json_object_get((json_t *)cls, "rp-origin"),
                                  "attestation-required", json_object_get((json_t *)cls, "force-fmt-none")==json_true()?"none":"direct",
                                  "timeout", 60000
                                );
            if (json_object_get((json_t *)cls, "session-mandatory") == json_false()) {
              json_array_extend(json_object_get(json_object_get(j_return, "response"), "allowCredentials"), json_object_get(j_credential_fake, "credential"));
            }
          } else if (check_result_value(j_assertion, G_ERROR_UNAUTHORIZED)) {
            j_return = json_pack("{si}", "result", G_ERROR_UNAUTHORIZED);
          } else {
            y_log_message(Y_LOG_LEVEL_ERROR, "user_auth_scheme_module_trigger webauthn - Error register_new_assertion");
            j_return = json_pack("{si}", "result", G_ERROR);
          }
          json_decref(j_assertion);
        } else if (check_result_value(j_credential, G_ERROR_NOT_FOUND)) {
          if (json_object_get((json_t *)cls, "session-mandatory") == json_false()) {
            j_assertion = generate_new_assertion(config, (json_t *)cls, username, 2);
            if (check_result_value(j_assertion, G_OK)) {
              j_return = json_pack("{sis{sOsOsOs{sOss}sO}}",
                                  "result", G_OK,
                                  "response",
                                    "allowCredentials", json_object_get(j_credential_fake, "credential"),
                                    "session", json_object_get(json_object_get(j_assertion, "assertion"), "session"),
                                    "challenge", json_object_get(json_object_get(j_assertion, "assertion"), "challenge"),
                                    "user",
                                      "id", json_object_get(j_user_id, "user_id"),
                                      "name", username,
                                    "rpId", json_object_get((json_t *)cls, "rp-origin")
                                  );
            } else {
              y_log_message(Y_LOG_LEVEL_ERROR, "user_auth_scheme_module_trigger webauthn - Error register_new_assertion");
              j_return = json_pack("{si}", "result", G_ERROR);
            }
            json_decref(j_assertion);
          } else {
            j_return = json_pack("{si}", "result", G_ERROR_UNAUTHORIZED);
          }
        } else {
          y_log_message(Y_LOG_LEVEL_ERROR, "user_auth_scheme_module_trigger webauthn - Error get_credential_list");
          j_return = json_pack("{si}", "result", G_ERROR);
        }
        json_decref(j_credential);
      } else if (check_result_value(j_user_id, G_ERROR_NOT_FOUND)) {
        if (json_object_get((json_t *)cls, "session-mandatory") == json_false()) {
          if (generate_fake_user_id((json_t *)cls, username, user_id_fake) == G_OK) {
            j_assertion = generate_new_assertion(config, (json_t *)cls, username, 2);
            if (check_result_value(j_assertion, G_OK)) {
              j_return = json_pack("{sis{sOsOsOs{ssss}sO}}",
                                  "result", G_OK,
                                  "response",
                                    "allowCredentials", json_object_get(j_credential_fake, "credential"),
                                    "session", json_object_get(json_object_get(j_assertion, "assertion"), "session"),
                                    "challenge", json_object_get(json_object_get(j_assertion, "assertion"), "challenge"),
                                    "user",
                                      "id", user_id_fake,
                                      "name", username,
                                    "rpId", json_object_get((json_t *)cls, "rp-origin")
                                  );
            } else {
              y_log_message(Y_LOG_LEVEL_ERROR, "user_auth_scheme_module_trigger webauthn - Error register_new_assertion");
              j_return = json_pack("{si}", "result", G_ERROR);
            }
            json_decref(j_assertion);
          } else {
            y_log_message(Y_LOG_LEVEL_ERROR, "user_auth_scheme_module_register webauthn - Error generate_fake_user_id");
            j_return = json_pack("{si}", "result", G_ERROR);
          }
        } else {
          j_return = json_pack("{si}", "result", G_ERROR_UNAUTHORIZED);
        }
      } else {
        y_log_message(Y_LOG_LEVEL_ERROR, "user_auth_scheme_module_register webauthn - Error get_user_id_from_username");
        j_return = json_pack("{si}", "result", G_ERROR);
      }
      json_decref(j_user_id);
    } else {
      y_log_message(Y_LOG_LEVEL_ERROR, "user_auth_scheme_module_trigger webauthn - Error generate_credential_fake");
      j_return = json_pack("{si}", "result", G_ERROR);
    }
    json_decref(j_credential_fake);
  } else if (check_result_value(j_session, G_ERROR_UNAUTHORIZED)) {
    j_return = json_pack("{si}", "result", G_ERROR_UNAUTHORIZED);
  } else {
    y_log_message(Y_LOG_LEVEL_ERROR, "user_auth_scheme_module_trigger webauthn - Error glewlwyd_module_callback_check_user_session");
    j_return = json_pack("{si}", "result", G_ERROR);
  }
  json_decref(j_session);
  return j_return;
}


static json_t * get_assertion_from_session(struct config_module * config, json_t * j_params, const char * username, const char * session, int mock) {
  json_t * j_query, * j_result, * j_return;
  char * username_escaped, * mod_name_escaped, * username_clause, * expiration_clause;
  char * session_hash;
  int res;
  time_t now;

  if (o_strlen(session)) {
    session_hash = generate_hash(config->hash_algorithm, session);
    if (session_hash != NULL) {
      time(&now);
      username_escaped = h_escape_string_with_quotes(config->conn, username);
      mod_name_escaped = h_escape_string_with_quotes(config->conn, json_string_value(json_object_get(j_params, "mod_name")));
      username_clause = msprintf(" = (SELECT gswu_id FROM "G_TABLE_WEBAUTHN_USER" WHERE UPPER(gswu_username) = UPPER(%s) AND gswu_mod_name = %s)", username_escaped, mod_name_escaped);
      if (config->conn->type==HOEL_DB_TYPE_MARIADB) {
        expiration_clause = msprintf("> FROM_UNIXTIME(%u)", (now - (unsigned int)json_integer_value(json_object_get(j_params, "credential-assertion"))));
      } else if (config->conn->type==HOEL_DB_TYPE_PGSQL) {
        expiration_clause = msprintf("> TO_TIMESTAMP(%u)", (now - (unsigned int)json_integer_value(json_object_get(j_params, "credential-assertion"))));
      } else { // HOEL_DB_TYPE_SQLITE
        expiration_clause = msprintf("> %u", (now - (unsigned int)json_integer_value(json_object_get(j_params, "credential-assertion"))));
      }
      j_query = json_pack("{sss[ssss]s{sss{ssss}sis{ssss}si}}",
                          "table",
                          G_TABLE_WEBAUTHN_ASSERTION,
                          "columns",
                            "gswa_id",
                            "gswu_id",
                            "gswa_session_hash AS session_hash",
                            "gswa_challenge_hash AS challenge_hash",
                          "where",
                            "gswa_session_hash",
                            session_hash,
                            "gswu_id",
                              "operator",
                              "raw",
                              "value",
                              username_clause,
                            "gswa_status",
                            0,
                            "gswa_issued_at",
                              "operator",
                              "raw",
                              "value",
                              expiration_clause,
                            "gswa_mock",
                            mock);
      o_free(username_clause);
      o_free(username_escaped);
      o_free(mod_name_escaped);
      o_free(expiration_clause);
      res = h_select(config->conn, j_query, &j_result, NULL);
      json_decref(j_query);
      if (res == H_OK) {
        if (json_array_size(j_result)) {
          j_return = json_pack("{sisO}", "result", G_OK, "assertion", json_array_get(j_result, 0));
        } else {
          j_return = json_pack("{si}", "result", G_ERROR_NOT_FOUND);
        }
        json_decref(j_result);
      } else {
        y_log_message(Y_LOG_LEVEL_ERROR, "get_assertion_from_session - Error executing j_query");
        config->glewlwyd_module_callback_metrics_increment_counter(config, GLWD_METRICS_DATABSE_ERROR, 1, NULL);
        j_return = json_pack("{si}", "result", G_ERROR_DB);
      }
    } else {
      y_log_message(Y_LOG_LEVEL_ERROR, "get_assertion_from_session - Error generate_hash");
      j_return = json_pack("{si}", "result", G_ERROR);
    }
    o_free(session_hash);
  } else {
    j_return = json_pack("{si}", "result", G_ERROR_PARAM);
  }
  return j_return;
}


static int validate_certificate_from_root(json_t * j_params, gnutls_x509_crt_t cert_leaf, cbor_item_t * x5c_array) {
  int ret = G_ERROR_NOT_FOUND, res;
  unsigned int result;
  gnutls_datum_t cert_dat = {NULL, 0}, issuer_dat = {NULL, 0};
  gnutls_x509_trust_list_t tlist = NULL;
  gnutls_x509_crt_t cert_x509[cbor_array_size(x5c_array)+1], root_x509 = NULL;
  json_t * j_cert = NULL;
  cbor_item_t * cbor_cert = NULL;
  size_t index = 0, i = 0, x5c_array_size = cbor_array_size(x5c_array);
  char * issuer;

  for (i=0; i<x5c_array_size+1; i++) {
    cert_x509[i] = NULL;
  }
  if ((res = gnutls_x509_crt_get_issuer_dn2(cert_leaf, &issuer_dat)) >= 0) {
    issuer = o_strndup((const char *)issuer_dat.data, issuer_dat.size);
    json_array_foreach(json_object_get(j_params, "root-ca-array"), index, j_cert) {
      if (0 == o_strcmp(issuer, json_string_value(json_object_get(j_cert, "dn")))) {
        cert_dat.data = (unsigned char *)json_string_value(json_object_get(j_cert, "x509"));
        cert_dat.size = json_string_length(json_object_get(j_cert, "x509"));
        if (!gnutls_x509_crt_init(&root_x509) && !gnutls_x509_crt_import(root_x509, &cert_dat, GNUTLS_X509_FMT_PEM)) {
          cert_x509[0] = cert_leaf;
          for (i=1; i<x5c_array_size; i++) {
            cbor_cert = cbor_array_get(x5c_array, i);
            cert_dat.data = cbor_bytestring_handle(cbor_cert);
            cert_dat.size = cbor_bytestring_length(cbor_cert);
            if (gnutls_x509_crt_init(&cert_x509[i]) < 0 || gnutls_x509_crt_import(cert_x509[i], &cert_dat, GNUTLS_X509_FMT_DER) < 0) {
              y_log_message(Y_LOG_LEVEL_ERROR, "validate_certificate_from_root - Error import chain cert at index %zu", i);
              ret = G_ERROR;
            }
            cbor_decref(&cbor_cert);
          }
          cert_x509[x5c_array_size] = root_x509;
          ret = G_OK;
        } else {
          y_log_message(Y_LOG_LEVEL_ERROR, "validate_certificate_from_root - Error import root cert");
          ret = G_ERROR;
        }
      }
    }
    o_free(issuer);
  } else {
    y_log_message(Y_LOG_LEVEL_ERROR, "validate_certificate_from_root - Error gnutls_x509_crt_get_issuer_dn2: %d", res);
    ret = G_ERROR;
  }
  gnutls_free(issuer_dat.data);

  if (ret == G_OK) {
    if (!gnutls_x509_trust_list_init(&tlist, 0)) {
      if (gnutls_x509_trust_list_add_cas(tlist, &root_x509, 1, 0) >= 0) {
        if (gnutls_x509_trust_list_verify_crt(tlist, cert_x509, 2, 0, &result, NULL) >= 0) {
          if (result) {
            y_log_message(Y_LOG_LEVEL_DEBUG, "validate_certificate_from_root - certificate chain invalid");
            ret = G_ERROR;
          }
        } else {
          y_log_message(Y_LOG_LEVEL_ERROR, "validate_certificate_from_root - Error gnutls_x509_trust_list_verify_crt");
          ret = G_ERROR;
        }
      } else {
        y_log_message(Y_LOG_LEVEL_ERROR, "validate_certificate_from_root - Error gnutls_x509_trust_list_add_cas");
        ret = G_ERROR;
      }
    } else {
      y_log_message(Y_LOG_LEVEL_ERROR, "validate_certificate_from_root - Error gnutls_x509_trust_list_init");
      ret = G_ERROR;
    }
  }
  gnutls_x509_crt_deinit(root_x509);
  for (i=1; i<x5c_array_size; i++) {
    gnutls_x509_crt_deinit(cert_x509[i]);
  }
  gnutls_x509_trust_list_deinit(tlist, 0);
  return ret;
}


ConversionResult ConvertUTF8toUTF16 (
    const UTF8** sourceStart, const UTF8* sourceEnd,
    UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
	ConversionResult result = conversionOK;
	const UTF8* source = *sourceStart;
	UTF16* target = *targetStart;
	while (source < sourceEnd) {
		UTF32 ch = 0;
		unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
		if (source + extraBytesToRead >= sourceEnd) {
			result = sourceExhausted;
			break;
		}
		/* Do this check whether lenient or strict */
		if (! isLegalUTF8(source, extraBytesToRead+1)) {
			result = sourceIllegal;
			break;
		}
		/*
		 * The cases all fall through. See "Note A" below.
		 */
		switch (extraBytesToRead) {
		case 5:
			ch += *source++;
			ch <<= 6; /* remember, illegal UTF-8 */
		case 4:
			ch += *source++;
			ch <<= 6; /* remember, illegal UTF-8 */
		case 3:
			ch += *source++;
			ch <<= 6;
		case 2:
			ch += *source++;
			ch <<= 6;
		case 1:
			ch += *source++;
			ch <<= 6;
		case 0:
			ch += *source++;
		}
		ch -= offsetsFromUTF8[extraBytesToRead];

		if (target >= targetEnd) {
			source -= (extraBytesToRead+1); /* Back up source pointer! */
			result = targetExhausted;
			break;
		}
		if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
			/* UTF-16 surrogate values are illegal in UTF-32 */
			if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
				if (flags == strictConversion) {
					source -= (extraBytesToRead+1); /* return to the illegal value itself */
					result = sourceIllegal;
					break;
				} else {
					*target++ = UNI_REPLACEMENT_CHAR;
				}
			} else {
				*target++ = (UTF16)ch; /* normal case */
			}
		} else if (ch > UNI_MAX_UTF16) {
			if (flags == strictConversion) {
				result = sourceIllegal;
				source -= (extraBytesToRead+1); /* return to the start */
				break; /* Bail out; shouldn't continue */
			} else {
				*target++ = UNI_REPLACEMENT_CHAR;
			}
		} else {
			/* target is a character in range 0xFFFF - 0x10FFFF. */
			if (target + 1 >= targetEnd) {
				source -= (extraBytesToRead+1); /* Back up source pointer! */
				result = targetExhausted;
				break;
			}
			ch -= halfBase;
			*target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
			*target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
		}
	}
	*sourceStart = source;
	*targetStart = target;
	return result;
}


void txtin_finalize(GF_Filter *filter)
{
	GF_TXTIn *ctx = gf_filter_get_udta(filter);

	ttxtin_reset(ctx);
	if (ctx->bs_w) gf_bs_del(ctx->bs_w);

	if (ctx->text_descs) {
		while (gf_list_count(ctx->text_descs)) {
			GF_PropertyValue *p = gf_list_pop_back(ctx->text_descs);
			gf_free(p->value.data.ptr);
			gf_free(p);
		}
		gf_list_del(ctx->text_descs);
	}
#ifndef GPAC_DISABLE_SWF_IMPORT
	gf_swf_reader_del(ctx->swf_parse);
#endif

	if (ctx->intervals) {
		ttml_reset_intervals(ctx);
		gf_list_del(ctx->intervals);
	}
	if (ctx->ttml_resources) {
		while (gf_list_count(ctx->ttml_resources)) {
			TTMLRes *ires = gf_list_pop_back(ctx->ttml_resources);
			gf_free(ires->data);
			gf_free(ires);
		}
		gf_list_del(ctx->ttml_resources);
	}
	if (ctx->div_nodes_list)
		gf_list_del(ctx->div_nodes_list);

	if (ctx->file_name) gf_free(ctx->file_name);
}


static void CTXLoad_ExecuteConditional(M_Conditional *c, GF_Scene *scene)
{
	GF_List *clist = c->buffer.commandList;
	c->buffer.commandList = NULL;

	gf_sg_command_apply_list(gf_node_get_graph((GF_Node*)c), clist, gf_scene_get_time(scene));

	if (c->buffer.commandList != NULL) {
		while (gf_list_count(clist)) {
			GF_Command *sub_com = (GF_Command *)gf_list_get(clist, 0);
			gf_sg_command_del(sub_com);
			gf_list_rem(clist, 0);
		}
		gf_list_del(clist);
	} else {
		c->buffer.commandList = clist;
	}
}


#else
const GF_FilterRegister *txtin_register(GF_FilterSession *session)
{
	return NULL;


static void txtin_probe_duration(GF_TXTIn *ctx)
{
	GF_Fraction64 dur;
	dur.num = 0;

	if (ctx->fmt == GF_TXTIN_MODE_SWF_SVG) {
#ifndef GPAC_DISABLE_SWF_IMPORT
		u32 frame_count, frame_rate;
		gf_swf_get_duration(ctx->swf_parse, &frame_rate, &frame_count);
		if (frame_count) {
			GF_Fraction64 tdur;
			tdur.num = frame_count;
			tdur.den = frame_rate;
			gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DURATION, &PROP_FRAC64(tdur));
			gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_PLAYBACK_MODE, &PROP_UINT(GF_PLAYBACK_MODE_FASTFORWARD ) );
		}
#endif
		return;
	}
	if ((ctx->fmt == GF_TXTIN_MODE_SRT) || (ctx->fmt == GF_TXTIN_MODE_WEBVTT)  || (ctx->fmt == GF_TXTIN_MODE_SUB)) {
		u64 pos = gf_ftell(ctx->src);
		gf_fseek(ctx->src, 0, SEEK_SET);
		while (!gf_feof(ctx->src)) {
			u64 end;
			char szLine[2048];
			char *sOK = gf_text_get_utf8_line(szLine, 2048, ctx->src, ctx->unicode_type);
			if (!sOK) break;
			REM_TRAIL_MARKS(szLine, "\r\n\t ")

			if (ctx->fmt == GF_TXTIN_MODE_SUB) {
				char szText[2048];
				u32 sframe, eframe;
				if (sscanf(szLine, "{%d}{%d}%2047s", &sframe, &eframe, szText) == 3) {
					if (ctx->fps.den)
						end = 1000 * eframe * ctx->fps.num / ctx->fps.den;
					else
						end = 1000 * eframe / 25;
					if (end > (u64) dur.num) dur.num = (s64) end;
				}
			} else {
				u32 eh, em, es, ems;
				char *start = strstr(szLine, "-->");
				if (!start) continue;
				while (start[0] && ((start[0] == ' ') || (start[0] == '\t'))) start++;

				if (sscanf(start, "%u:%u:%u,%u", &eh, &em, &es, &ems) != 4) {
					eh = 0;
					if (sscanf(szLine, "%u:%u,%u", &em, &es, &ems) != 3) {
						continue;
					}
				}
				end = (3600*eh + 60*em + es)*1000 + ems;
				if (end > (u64) dur.num) dur.num = (s64) end;
			}
		}
		gf_fseek(ctx->src, pos, SEEK_SET);
		if (dur.num) {
			dur.den = 1000;
			gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DURATION, &PROP_FRAC64(dur));
			gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_PLAYBACK_MODE, &PROP_UINT(GF_PLAYBACK_MODE_FASTFORWARD ) );
		}
		return;
	}
	if ((ctx->fmt == GF_TXTIN_MODE_TTXT) || (ctx->fmt == GF_TXTIN_MODE_TEXML)) {
		u32 i=0;
		GF_XMLNode *node, *root = gf_xml_dom_get_root(ctx->parser);
		while ((node = gf_list_enum(root->content, &i))) {
			u32 j;
			u64 duration;
			GF_XMLAttribute *att;
			if (node->type) {
				continue;
			}
			/*sample text*/
			if ((ctx->fmt == GF_TXTIN_MODE_TTXT) && strcmp(node->name, "TextSample")) continue;
			else if ((ctx->fmt == GF_TXTIN_MODE_TEXML) && strcmp(node->name, "sample")) continue;

			j=0;
			while ( (att=(GF_XMLAttribute*)gf_list_enum(node->attributes, &j))) {
				u32 h, m, s, ms;
				u64 ts=0;
				if (ctx->fmt == GF_TXTIN_MODE_TTXT) {
					if (strcmp(att->name, "sampleTime")) continue;

					if (sscanf(att->value, "%u:%u:%u.%u", &h, &m, &s, &ms) == 4) {
						ts = (h*3600 + m*60 + s)*1000 + ms;
					} else {
						ts = (u32) (atof(att->value) * 1000);
					}
					if (ts > (u64) dur.num) dur.num = (s64) ts;
				} else {
					if (strcmp(att->name, "duration")) continue;
					duration = atoi(att->value);
					dur.num += (s32) ( (1000 * duration) / ctx->txml_timescale);
				}
			}
		}
		if (dur.num) {
			dur.den = 1000;
			gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DURATION, &PROP_FRAC64(dur));
			gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_PLAYBACK_MODE, &PROP_UINT(GF_PLAYBACK_MODE_FASTFORWARD ) );
		}
		return;
	}

	if (ctx->fmt == GF_TXTIN_MODE_TTML) {
		u32 i=0, k=0;
		GF_XMLNode *div_node;

		while ((div_node = gf_list_enum(ctx->div_nodes_list, &k))) {
			GF_XMLNode *node;
			while ((node = gf_list_enum(div_node->content, &i))) {
				GF_XMLNode *p_node;
				GF_XMLAttribute *att;
				u32 h, m, s, ms, p_idx=0;
				u64 ts_end=0;
				h = m = s = ms = 0;
				while ( (att = (GF_XMLAttribute*)gf_list_enum(node->attributes, &p_idx))) {
					if (strcmp(att->name, "end")) continue;

					if (sscanf(att->value, "%u:%u:%u.%u", &h, &m, &s, &ms) == 4) {
						ts_end = (h*3600 + m*60+s)*1000+ms;
					} else if (sscanf(att->value, "%u:%u:%u", &h, &m, &s) == 3) {
						ts_end = (h*3600 + m*60+s)*1000;
					}
				}
				//or under a <span>
				p_idx = 0;
				while ( (p_node = (GF_XMLNode*)gf_list_enum(node->content, &p_idx))) {
					u32 span_idx = 0;
					while ( (att = (GF_XMLAttribute*)gf_list_enum(p_node->attributes, &span_idx))) {
						if (strcmp(att->name, "end")) continue;
						if (sscanf(att->value, "%u:%u:%u.%u", &h, &m, &s, &ms) == 4) {
							ts_end = (h*3600 + m*60+s)*1000+ms;
						} else if (sscanf(att->value, "%u:%u:%u", &h, &m, &s) == 3) {
							ts_end = (h*3600 + m*60+s)*1000;
						}
					}
				}
				if (ts_end > (u64) dur.num) dur.num = (s64) ts_end;
			}
		}
		if (dur.num) {
			dur.den = 1000;
			gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DURATION, &PROP_FRAC64(dur));
			gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_PLAYBACK_MODE, &PROP_UINT(GF_PLAYBACK_MODE_FASTFORWARD ) );
		}
		return;
	}
	GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[TXTIn] Duration probing not supported for format %d\n", ctx->fmt));
}


u32 gf_utf8_wcslen (const unsigned short *s)
{
	const unsigned short* ptr;
	for (ptr = s; *ptr != (unsigned short)'\0'; ptr++) {
	}
	return (u32) (ptr - s);
}


s32 gf_text_get_utf_type(FILE *in_src)
{
	u32 read;
	unsigned char BOM[5];
	read = (u32) gf_fread(BOM, 5, in_src);
	if ((s32) read < 1)
		return -1;

	if ((BOM[0]==0xFF) && (BOM[1]==0xFE)) {
		/*UTF32 not supported*/
		if (!BOM[2] && !BOM[3]) return -1;
		gf_fseek(in_src, 2, SEEK_SET);
		return 3;
	}
	if ((BOM[0]==0xFE) && (BOM[1]==0xFF)) {
		/*UTF32 not supported*/
		if (!BOM[2] && !BOM[3]) return -1;
		gf_fseek(in_src, 2, SEEK_SET);
		return 2;
	} else if ((BOM[0]==0xEF) && (BOM[1]==0xBB) && (BOM[2]==0xBF)) {
		gf_fseek(in_src, 3, SEEK_SET);
		return 1;
	}
	if (BOM[0]<0x80) {
		gf_fseek(in_src, 0, SEEK_SET);
		return 0;
	}
	return -1;
}


static GF_Err swf_svg_add_iso_sample(void *user, const u8 *data, u32 length, u64 timestamp, Bool isRap)
{
	GF_FilterPacket *pck;
	u8 *pck_data;
	GF_TXTIn *ctx = (GF_TXTIn *)user;

	if (ctx->seek_state==2) {
		Double ts = (Double) timestamp;
		ts/=1000;
		if (ts<ctx->start_range) return GF_OK;
		ctx->seek_state = 0;
	}

	pck = gf_filter_pck_new_alloc(ctx->opid, length, &pck_data);
	if (pck) {
		memcpy(pck_data, data, length);
		gf_filter_pck_set_cts(pck, (u64) (ctx->timescale*timestamp/1000) );
		gf_filter_pck_set_sap(pck, isRap ? GF_FILTER_SAP_1 : GF_FILTER_SAP_NONE);
		gf_filter_pck_set_framing(pck, GF_TRUE, GF_FALSE);

		gf_filter_pck_send(pck);
	}

	if (gf_filter_pid_would_block(ctx->opid))
		ctx->do_suspend = GF_TRUE;
	return GF_OK;
}


static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr)
{
	struct xfrm_dump_info *sp = ptr;
	struct xfrm_userpolicy_info *p;
	struct sk_buff *in_skb = sp->in_skb;
	struct sk_buff *skb = sp->out_skb;
	struct nlmsghdr *nlh;
	int err;

	nlh = nlmsg_put(skb, NETLINK_CB(in_skb).pid, sp->nlmsg_seq,
			XFRM_MSG_NEWPOLICY, sizeof(*p), sp->nlmsg_flags);
	if (nlh == NULL)
		return -EMSGSIZE;

	p = nlmsg_data(nlh);
	copy_to_user_policy(xp, p, dir);
	err = copy_to_user_tmpl(xp, skb);
	if (!err)
		err = copy_to_user_sec_ctx(xp, skb);
	if (!err)
		err = copy_to_user_policy_type(xp->type, skb);
	if (!err)
		err = xfrm_mark_put(skb, &xp->mark);
	if (err) {
		nlmsg_cancel(skb, nlh);
		return err;
	}
	nlmsg_end(skb, nlh);
	return 0;
}


static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c)
{
	struct xfrm_aevent_id *id;
	struct nlmsghdr *nlh;
	int err;

	nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_NEWAE, sizeof(*id), 0);
	if (nlh == NULL)
		return -EMSGSIZE;

	id = nlmsg_data(nlh);
	memcpy(&id->sa_id.daddr, &x->id.daddr,sizeof(x->id.daddr));
	id->sa_id.spi = x->id.spi;
	id->sa_id.family = x->props.family;
	id->sa_id.proto = x->id.proto;
	memcpy(&id->saddr, &x->props.saddr,sizeof(x->props.saddr));
	id->reqid = x->props.reqid;
	id->flags = c->data.aevent;

	if (x->replay_esn) {
		err = nla_put(skb, XFRMA_REPLAY_ESN_VAL,
			      xfrm_replay_state_esn_len(x->replay_esn),
			      x->replay_esn);
	} else {
		err = nla_put(skb, XFRMA_REPLAY_VAL, sizeof(x->replay),
			      &x->replay);
	}
	if (err)
		goto out_cancel;
	err = nla_put(skb, XFRMA_LTIME_VAL, sizeof(x->curlft), &x->curlft);
	if (err)
		goto out_cancel;

	if (id->flags & XFRM_AE_RTHR) {
		err = nla_put_u32(skb, XFRMA_REPLAY_THRESH, x->replay_maxdiff);
		if (err)
			goto out_cancel;
	}
	if (id->flags & XFRM_AE_ETHR) {
		err = nla_put_u32(skb, XFRMA_ETIMER_THRESH,
				  x->replay_maxage * 10 / HZ);
		if (err)
			goto out_cancel;
	}
	err = xfrm_mark_put(skb, &x->mark);
	if (err)
		goto out_cancel;

	return nlmsg_end(skb, nlh);

out_cancel:
	nlmsg_cancel(skb, nlh);
	return err;
}


static int copy_to_user_state_extra(struct xfrm_state *x,
				    struct xfrm_usersa_info *p,
				    struct sk_buff *skb)
{
	int ret = 0;

	copy_to_user_state(x, p);

	if (x->coaddr) {
		ret = nla_put(skb, XFRMA_COADDR, sizeof(*x->coaddr), x->coaddr);
		if (ret)
			goto out;
	}
	if (x->lastused) {
		ret = nla_put_u64(skb, XFRMA_LASTUSED, x->lastused);
		if (ret)
			goto out;
	}
	if (x->aead) {
		ret = nla_put(skb, XFRMA_ALG_AEAD, aead_len(x->aead), x->aead);
		if (ret)
			goto out;
	}
	if (x->aalg) {
		ret = copy_to_user_auth(x->aalg, skb);
		if (!ret)
			ret = nla_put(skb, XFRMA_ALG_AUTH_TRUNC,
				      xfrm_alg_auth_len(x->aalg), x->aalg);
		if (ret)
			goto out;
	}
	if (x->ealg) {
		ret = nla_put(skb, XFRMA_ALG_CRYPT, xfrm_alg_len(x->ealg), x->ealg);
		if (ret)
			goto out;
	}
	if (x->calg) {
		ret = nla_put(skb, XFRMA_ALG_COMP, sizeof(*(x->calg)), x->calg);
		if (ret)
			goto out;
	}
	if (x->encap) {
		ret = nla_put(skb, XFRMA_ENCAP, sizeof(*x->encap), x->encap);
		if (ret)
			goto out;
	}
	if (x->tfcpad) {
		ret = nla_put_u32(skb, XFRMA_TFCPAD, x->tfcpad);
		if (ret)
			goto out;
	}
	ret = xfrm_mark_put(skb, &x->mark);
	if (ret)
		goto out;
	if (x->replay_esn) {
		ret = nla_put(skb, XFRMA_REPLAY_ESN_VAL,
			      xfrm_replay_state_esn_len(x->replay_esn),
			      x->replay_esn);
		if (ret)
			goto out;
	}
	if (x->security)
		ret = copy_sec_ctx(x->security, skb);
out:
	return ret;
}


static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_event *c)
{
	int len = nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr);
	struct net *net = xp_net(xp);
	struct xfrm_userpolicy_info *p;
	struct xfrm_userpolicy_id *id;
	struct nlmsghdr *nlh;
	struct sk_buff *skb;
	int headlen, err;

	headlen = sizeof(*p);
	if (c->event == XFRM_MSG_DELPOLICY) {
		len += nla_total_size(headlen);
		headlen = sizeof(*id);
	}
	len += userpolicy_type_attrsize();
	len += nla_total_size(sizeof(struct xfrm_mark));
	len += NLMSG_ALIGN(headlen);

	skb = nlmsg_new(len, GFP_ATOMIC);
	if (skb == NULL)
		return -ENOMEM;

	nlh = nlmsg_put(skb, c->pid, c->seq, c->event, headlen, 0);
	err = -EMSGSIZE;
	if (nlh == NULL)
		goto out_free_skb;

	p = nlmsg_data(nlh);
	if (c->event == XFRM_MSG_DELPOLICY) {
		struct nlattr *attr;

		id = nlmsg_data(nlh);
		memset(id, 0, sizeof(*id));
		id->dir = dir;
		if (c->data.byid)
			id->index = xp->index;
		else
			memcpy(&id->sel, &xp->selector, sizeof(id->sel));

		attr = nla_reserve(skb, XFRMA_POLICY, sizeof(*p));
		err = -EMSGSIZE;
		if (attr == NULL)
			goto out_free_skb;

		p = nla_data(attr);
	}

	copy_to_user_policy(xp, p, dir);
	err = copy_to_user_tmpl(xp, skb);
	if (!err)
		err = copy_to_user_policy_type(xp->type, skb);
	if (!err)
		err = xfrm_mark_put(skb, &xp->mark);
	if (err)
		goto out_free_skb;

	nlmsg_end(skb, nlh);

	return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC);

out_free_skb:
	kfree_skb(skb);
	return err;
}


void skcipher_geniv_free(struct crypto_instance *inst)
{
	crypto_drop_skcipher(crypto_instance_ctx(inst));
	kfree(inst);
}


int blkcipher_walk_phys(struct blkcipher_desc *desc,
			struct blkcipher_walk *walk)
{
	walk->flags |= BLKCIPHER_WALK_PHYS;
	walk->blocksize = crypto_blkcipher_blocksize(desc->tfm);
	return blkcipher_walk_first(desc, walk);
}


static void crypto_givcipher_show(struct seq_file *m, struct crypto_alg *alg)
{
	struct ablkcipher_alg *ablkcipher = &alg->cra_ablkcipher;

	seq_printf(m, "type         : givcipher\n");
	seq_printf(m, "async        : %s\n", alg->cra_flags & CRYPTO_ALG_ASYNC ?
					     "yes" : "no");
	seq_printf(m, "blocksize    : %u\n", alg->cra_blocksize);
	seq_printf(m, "min keysize  : %u\n", ablkcipher->min_keysize);
	seq_printf(m, "max keysize  : %u\n", ablkcipher->max_keysize);
	seq_printf(m, "ivsize       : %u\n", ablkcipher->ivsize);
	seq_printf(m, "geniv        : %s\n", ablkcipher->geniv ?: "<built-in>");
}


static int ablkcipher_walk_next(struct ablkcipher_request *req,
				struct ablkcipher_walk *walk)
{
	struct crypto_tfm *tfm = req->base.tfm;
	unsigned int alignmask, bsize, n;
	void *src, *dst;
	int err;

	alignmask = crypto_tfm_alg_alignmask(tfm);
	n = walk->total;
	if (unlikely(n < crypto_tfm_alg_blocksize(tfm))) {
		req->base.flags |= CRYPTO_TFM_RES_BAD_BLOCK_LEN;
		return ablkcipher_walk_done(req, walk, -EINVAL);
	}

	walk->flags &= ~ABLKCIPHER_WALK_SLOW;
	src = dst = NULL;

	bsize = min(walk->blocksize, n);
	n = scatterwalk_clamp(&walk->in, n);
	n = scatterwalk_clamp(&walk->out, n);

	if (n < bsize ||
	    !scatterwalk_aligned(&walk->in, alignmask) ||
	    !scatterwalk_aligned(&walk->out, alignmask)) {
		err = ablkcipher_next_slow(req, walk, bsize, alignmask,
					   &src, &dst);
		goto set_phys_lowmem;
	}

	walk->nbytes = n;

	return ablkcipher_next_fast(req, walk);

set_phys_lowmem:
	if (err >= 0) {
		walk->src.page = virt_to_page(src);
		walk->dst.page = virt_to_page(dst);
		walk->src.offset = ((unsigned long)src & (PAGE_SIZE - 1));
		walk->dst.offset = ((unsigned long)dst & (PAGE_SIZE - 1));
	}

	return err;
}


static int aead_null_givencrypt(struct aead_givcrypt_request *req)
{
	return crypto_aead_encrypt(&req->areq);
}


static void crypto_rng_show(struct seq_file *m, struct crypto_alg *alg)
{
	seq_printf(m, "type         : rng\n");
	seq_printf(m, "seedsize     : %u\n", alg->cra_rng.seedsize);
}


static int setkey_unaligned(struct crypto_tfm *tfm, const u8 *key,
			    unsigned int keylen)
{
	struct blkcipher_alg *cipher = &tfm->__crt_alg->cra_blkcipher;
	unsigned long alignmask = crypto_tfm_alg_alignmask(tfm);
	int ret;
	u8 *buffer, *alignbuffer;
	unsigned long absize;

	absize = keylen + alignmask;
	buffer = kmalloc(absize, GFP_ATOMIC);
	if (!buffer)
		return -ENOMEM;

	alignbuffer = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1);
	memcpy(alignbuffer, key, keylen);
	ret = cipher->setkey(tfm, alignbuffer, keylen);
	memset(alignbuffer, 0, keylen);
	kfree(buffer);
	return ret;
}


ModuleExport size_t RegisterPNGImage(void)
{
  char
    version[MagickPathExtent];

  MagickInfo
    *entry;

  static const char
    *PNGNote=
    {
      "See http://www.libpng.org/ for details about the PNG format."
    },

    *JNGNote=
    {
      "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
      "format."
    },

    *MNGNote=
    {
      "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
      "format."
    };

  *version='\0';

#if defined(PNG_LIBPNG_VER_STRING)
  (void) ConcatenateMagickString(version,"libpng ",MagickPathExtent);
  (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,
   MagickPathExtent);

  if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
    {
      (void) ConcatenateMagickString(version,",",MagickPathExtent);
      (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
            MagickPathExtent);
    }
#endif

  entry=AcquireMagickInfo("PNG","MNG","Multiple-image Network Graphics");
  entry->flags|=CoderSeekableStreamFlag;  /* To do: eliminate this. */

#if defined(MAGICKCORE_PNG_DELEGATE)
  entry->decoder=(DecodeImageHandler *) ReadMNGImage;
  entry->encoder=(EncodeImageHandler *) WriteMNGImage;
#endif

  entry->magick=(IsImageFormatHandler *) IsMNG;

  if (*version != '\0')
    entry->version=ConstantString(version);

  entry->mime_type=ConstantString("video/x-mng");
  entry->note=ConstantString(MNGNote);
  (void) RegisterMagickInfo(entry);

  entry=AcquireMagickInfo("PNG","PNG","Portable Network Graphics");

#if defined(MAGICKCORE_PNG_DELEGATE)
  entry->decoder=(DecodeImageHandler *) ReadPNGImage;
  entry->encoder=(EncodeImageHandler *) WritePNGImage;
#endif

  entry->magick=(IsImageFormatHandler *) IsPNG;
  entry->flags^=CoderAdjoinFlag;
  entry->mime_type=ConstantString("image/png");

  if (*version != '\0')
    entry->version=ConstantString(version);

  entry->note=ConstantString(PNGNote);
  (void) RegisterMagickInfo(entry);

  entry=AcquireMagickInfo("PNG","PNG8",
    "8-bit indexed with optional binary transparency");

#if defined(MAGICKCORE_PNG_DELEGATE)
  entry->decoder=(DecodeImageHandler *) ReadPNGImage;
  entry->encoder=(EncodeImageHandler *) WritePNGImage;
#endif

  entry->magick=(IsImageFormatHandler *) IsPNG;
  entry->flags^=CoderAdjoinFlag;
  entry->mime_type=ConstantString("image/png");
  (void) RegisterMagickInfo(entry);

  entry=AcquireMagickInfo("PNG","PNG24",
    "opaque or binary transparent 24-bit RGB");
  *version='\0';

#if defined(ZLIB_VERSION)
  (void) ConcatenateMagickString(version,"zlib ",MagickPathExtent);
  (void) ConcatenateMagickString(version,ZLIB_VERSION,MagickPathExtent);

  if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
    {
      (void) ConcatenateMagickString(version,",",MagickPathExtent);
      (void) ConcatenateMagickString(version,zlib_version,MagickPathExtent);
    }
#endif

  if (*version != '\0')
    entry->version=ConstantString(version);

#if defined(MAGICKCORE_PNG_DELEGATE)
  entry->decoder=(DecodeImageHandler *) ReadPNGImage;
  entry->encoder=(EncodeImageHandler *) WritePNGImage;
#endif

  entry->magick=(IsImageFormatHandler *) IsPNG;
  entry->flags^=CoderAdjoinFlag;
  entry->mime_type=ConstantString("image/png");
  (void) RegisterMagickInfo(entry);

  entry=AcquireMagickInfo("PNG","PNG32","opaque or transparent 32-bit RGBA");

#if defined(MAGICKCORE_PNG_DELEGATE)
  entry->decoder=(DecodeImageHandler *) ReadPNGImage;
  entry->encoder=(EncodeImageHandler *) WritePNGImage;
#endif

  entry->magick=(IsImageFormatHandler *) IsPNG;
  entry->flags^=CoderAdjoinFlag;
  entry->mime_type=ConstantString("image/png");
  (void) RegisterMagickInfo(entry);

  entry=AcquireMagickInfo("PNG","PNG48",
    "opaque or binary transparent 48-bit RGB");

#if defined(MAGICKCORE_PNG_DELEGATE)
  entry->decoder=(DecodeImageHandler *) ReadPNGImage;
  entry->encoder=(EncodeImageHandler *) WritePNGImage;
#endif

  entry->magick=(IsImageFormatHandler *) IsPNG;
  entry->flags^=CoderAdjoinFlag;
  entry->mime_type=ConstantString("image/png");
  (void) RegisterMagickInfo(entry);

  entry=AcquireMagickInfo("PNG","PNG64","opaque or transparent 64-bit RGBA");

#if defined(MAGICKCORE_PNG_DELEGATE)
  entry->decoder=(DecodeImageHandler *) ReadPNGImage;
  entry->encoder=(EncodeImageHandler *) WritePNGImage;
#endif

  entry->magick=(IsImageFormatHandler *) IsPNG;
  entry->flags^=CoderAdjoinFlag;
  entry->mime_type=ConstantString("image/png");
  (void) RegisterMagickInfo(entry);

  entry=AcquireMagickInfo("PNG","PNG00",
    "PNG inheriting bit-depth, color-type from original, if possible");

#if defined(MAGICKCORE_PNG_DELEGATE)
  entry->decoder=(DecodeImageHandler *) ReadPNGImage;
  entry->encoder=(EncodeImageHandler *) WritePNGImage;
#endif

  entry->magick=(IsImageFormatHandler *) IsPNG;
  entry->flags^=CoderAdjoinFlag;
  entry->mime_type=ConstantString("image/png");
  (void) RegisterMagickInfo(entry);

  entry=AcquireMagickInfo("PNG","JNG","JPEG Network Graphics");

#if defined(JNG_SUPPORTED)
#if defined(MAGICKCORE_PNG_DELEGATE)
  entry->decoder=(DecodeImageHandler *) ReadJNGImage;
  entry->encoder=(EncodeImageHandler *) WriteJNGImage;
#endif
#endif

  entry->magick=(IsImageFormatHandler *) IsJNG;
  entry->flags^=CoderAdjoinFlag;
  entry->mime_type=ConstantString("image/x-jng");
  entry->note=ConstantString(JNGNote);
  (void) RegisterMagickInfo(entry);

#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
  ping_semaphore=AcquireSemaphoreInfo();
#endif

  return(MagickImageCoderSignature);
}