user_matches(const struct sudoers_parse_tree *parse_tree,
    const struct passwd *pw, const struct member *m)
{
    const struct sudoers_context *ctx = parse_tree->ctx;
    const char *lhost = parse_tree->lhost ? parse_tree->lhost : ctx->runas.host;
    const char *shost = parse_tree->shost ? parse_tree->shost : ctx->runas.shost;
    int matched = UNSPEC;
    struct alias *a;
    debug_decl(user_matches, SUDOERS_DEBUG_MATCH);

    switch (m->type) {
	case ALL:
	    matched = m->negated ? DENY : ALLOW;
	    break;
	case NETGROUP:
	    if (netgr_matches(parse_tree->nss, m->name,
		def_netgroup_tuple ? lhost : NULL,
		def_netgroup_tuple ? shost : NULL, pw->pw_name))
		matched = m->negated ? DENY : ALLOW;
	    break;
	case USERGROUP:
	    if (usergr_matches(m->name, pw->pw_name, pw))
		matched = m->negated ? DENY : ALLOW;
	    break;
	case ALIAS:
	    if ((a = alias_get(parse_tree, m->name, USERALIAS)) != NULL) {
		/* XXX */
		const int rc = userlist_matches(parse_tree, pw, &a->members);
		if (SPECIFIED(rc)) {
		    if (m->negated) {
			matched = rc == ALLOW ? DENY : ALLOW;
		    } else {
			matched = rc;
		    }
		}
		alias_put(a);
		break;
	    }
	    FALLTHROUGH;
	case WORD:
	    if (userpw_matches(m->name, pw->pw_name, pw))
		matched = m->negated ? DENY : ALLOW;
	    break;
    }
    debug_return_int(matched);
}


cmnd_matches_all(const struct sudoers_parse_tree *parse_tree,
    const struct member *m, const char *runchroot, struct cmnd_info *info)
{
    const bool negated = m->negated;
    struct sudo_command *c;
    int matched = UNSPEC;
    struct alias *a;
    debug_decl(cmnd_matches_all, SUDOERS_DEBUG_MATCH);

    switch (m->type) {
	case ALL:
	    c = (struct sudo_command *)m->name;
	    if (command_matches(parse_tree->ctx, c->cmnd, c->args, runchroot,
		    info, &c->digests))
		matched = negated ? DENY : ALLOW;
	    break;
	case ALIAS:
	    a = alias_get(parse_tree, m->name, CMNDALIAS);
	    if (a != NULL) {
		TAILQ_FOREACH_REVERSE(m, &a->members, member_list, entries) {
		    matched = cmnd_matches_all(parse_tree, m, runchroot, info);
		    if (SPECIFIED(matched)) {
			if (negated)
			    matched = matched == ALLOW ? DENY : ALLOW;
			break;
		    }
		}
		alias_put(a);
	    }
	    break;
    }
    debug_return_int(matched);
}


runas_grouplist_matches(const struct sudoers_parse_tree *parse_tree,
    const struct member_list *group_list, struct member **matching_group)
{
    const struct sudoers_context *ctx = parse_tree->ctx;
    int group_matched = UNSPEC;
    struct member *m;
    struct alias *a;
    debug_decl(runas_grouplist_matches, SUDOERS_DEBUG_MATCH);

    if (group_list != NULL) {
	TAILQ_FOREACH_REVERSE(m, group_list, member_list, entries) {
	    switch (m->type) {
		case ALL:
		    group_matched = m->negated ? DENY : ALLOW;
		    break;
		case ALIAS:
		    a = alias_get(parse_tree, m->name, RUNASALIAS);
		    if (a != NULL) {
			const int rc = runas_grouplist_matches(parse_tree,
			    &a->members, matching_group);
			if (SPECIFIED(rc)) {
			    if (m->negated) {
				group_matched = rc == ALLOW ? DENY : ALLOW;
			    } else {
				group_matched = rc;
			    }
			}
			alias_put(a);
			break;
		    }
		    FALLTHROUGH;
		case WORD:
		    if (group_matches(m->name, ctx->runas.gr))
			group_matched = m->negated ? DENY : ALLOW;
		    break;
	    }
	    if (SPECIFIED(group_matched)) {
		if (matching_group != NULL && m->type != ALIAS)
		    *matching_group = m;
		break;
	    }
	}
    }
    if (!SPECIFIED(group_matched)) {
	struct gid_list *runas_groups;
	/*
	 * The runas group was not explicitly allowed by sudoers.
	 * Check whether it is one of the target user's groups.
	 */
	if (ctx->runas.pw->pw_gid == ctx->runas.gr->gr_gid) {
	    group_matched = ALLOW;	/* runas group matches passwd db */
	} else if ((runas_groups = runas_getgroups(ctx)) != NULL) {
	    int i;

	    for (i = 0; i < runas_groups->ngids; i++) {
		if (runas_groups->gids[i] == ctx->runas.gr->gr_gid) {
		    group_matched = ALLOW;	/* matched aux group vector */
		    break;
		}
	    }
	    sudo_gidlist_delref(runas_groups);
	}
    }

    debug_return_int(group_matched);
}


userlist_matches(const struct sudoers_parse_tree *parse_tree,
    const struct passwd *pw, const struct member_list *list)
{
    struct member *m;
    int matched = UNSPEC;
    debug_decl(userlist_matches, SUDOERS_DEBUG_MATCH);

    TAILQ_FOREACH_REVERSE(m, list, member_list, entries) {
	matched = user_matches(parse_tree, pw, m);
	if (SPECIFIED(matched))
	    break;
    }
    debug_return_int(matched);
}


static int parse_packet(const char *payload, struct ncrx_msg *msg)
{
	char buf[1024];
	char *p, *tok;
	int idx;

	memset(msg, 0, sizeof(*msg));

	p = strchr(payload, ';');
	if (!p || p - payload >= (signed)sizeof(buf))
		goto einval;
	memcpy(buf, payload, p - payload);
	buf[p - payload] = '\0';

	msg->text = p + 1;
	msg->text_len = strlen(msg->text);
	if (msg->text_len > NCRX_LINE_MAX)
		msg->text_len = NCRX_LINE_MAX;

	/* <level>,<sequnum>,<timestamp>,<contflag>[,KEY=VAL]* */
	idx = 0;
	p = buf;
	while ((tok = strsep(&p, ","))) {
		char *endp, *key, *val;
		unsigned long long v;

		switch (idx++) {
		case 0:
			v = strtoul(tok, &endp, 0);
			if (*endp != '\0' || v > UINT8_MAX)
				goto einval;
			msg->facility = v >> 3;
			msg->level = v & ((1 << 3) - 1);
			continue;
		case 1:
			v = strtoull(tok, &endp, 0);
			if (*endp != '\0')
				goto einval;
			msg->seq = v;
			continue;
		case 2:
			v = strtoull(tok, &endp, 0);
			if (*endp != '\0')
				goto einval;
			msg->ts_usec = v;
			continue;
		case 3:
			if (tok[0] == 'c')
				msg->cont_start = 1;
			else if (tok[0] == '+')
				msg->cont = 1;
			continue;
		}

		val = tok;
		key = strsep(&val, "=");
		if (!val)
			continue;
		if (!strcmp(key, "ncfrag")) {
			unsigned nf_off, nf_len;

			if (sscanf(val, "%u/%u", &nf_off, &nf_len) != 2)
				goto einval;
			if (!msg->text_len ||
			    nf_len >= NCRX_LINE_MAX ||
			    nf_off >= nf_len ||
			    nf_off + msg->text_len > nf_len)
				goto einval;

			msg->ncfrag_off = nf_off;
			msg->ncfrag_len = msg->text_len;
			msg->ncfrag_left = nf_len - msg->ncfrag_len;
			msg->text_len = nf_len;
		} else if (!strcmp(key, "ncemg")) {
			v = strtoul(val, &endp, 0);
			if (*endp != '\0')
				goto einval;
			msg->emg = v;
		}
	}
	return 0;
einval:
	errno = EINVAL;
	return -1;
}

The product writes data past the end, or before the beginning, of the intended buffer.


int valid_field (const char *field, const char *illegal)
{
	const char *cp;
	int err = 0;

	if (NULL == field) {
		return -1;
	}

	/* For each character of field, search if it appears in the list
	 * of illegal characters. */
	for (cp = field; '\0' != *cp; cp++) {
		if (strchr (illegal, *cp) != NULL) {
			err = -1;
			break;
		}
	}

	if (0 == err) {
		/* Search if there are non-printable or control characters */
		for (cp = field; '\0' != *cp; cp++) {
			if (!isprint (*cp)) {
				err = 1;
			}
			if (!iscntrl (*cp)) {
				err = -1;
				break;
			}
		}
	}

	return err;
}

Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection')

The product constructs all or part of a command, data structure, or record using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify how it is parsed or interpreted when it is sent to a downstream component.


static u32 crc32sum(u32 crc, u8 * RESTRICT buf, size_t size) {
    // Test endianness. The code needs to be different for LE and BE systems.
    u32 test = 1;
    if (*(u8 *) &test) {
        while (size--) crc = crc32Table[(crc ^ *(buf++)) & 0xff] ^ (crc >> 8);
        return crc;
    } else {
        while (size--) crc = crc32Table[((crc >> 24) ^ *(buf++)) & 0xff] ^ (crc << 8);
        return crc;
    }
}

The product reads data past the end, or before the beginning, of the intended buffer.


static int mrled(u8 * RESTRICT in, u8 * RESTRICT out, s32 outlen, s32 maxin) {
    s32 op = 0, ip = 0;

    s32 c, pc = -1;
    s32 t[256] = { 0 };
    s32 run = 0;

    if(maxin < 32)
        return 1;

    for (s32 i = 0; i < 32; ++i) {
        c = in[ip++];
        for (s32 j = 0; j < 8; ++j) t[i * 8 + j] = (c >> j) & 1;
    }

    while (op < outlen && ip < maxin) {
        c = in[ip++];
        if (t[c]) {
            for (run = 0; (pc = in[ip++]) == 255 && ip < maxin; run += 255)
                ;
            run += pc + 1;
            for (; run > 0 && op < outlen; --run) out[op++] = c;
        } else
            out[op++] = c;
    }

    return op != outlen;
}


BZIP3_API struct bz3_state * bz3_new(s32 block_size) {
    if (block_size < KiB(65) || block_size > MiB(511)) {
        return NULL;
    }

    struct bz3_state * bz3_state = malloc(sizeof(struct bz3_state));

    if (!bz3_state) {
        return NULL;
    }

    bz3_state->cm_state = malloc(sizeof(state));

    bz3_state->swap_buffer = malloc(bz3_bound(block_size));
    bz3_state->sais_array = malloc(BWT_BOUND(block_size) * sizeof(s32));
    memset(bz3_state->sais_array, 0, sizeof(s32) * BWT_BOUND(block_size));

    bz3_state->lzp_lut = calloc(1 << LZP_DICTIONARY, sizeof(s32));

    if (!bz3_state->cm_state || !bz3_state->swap_buffer || !bz3_state->sais_array || !bz3_state->lzp_lut) {
        if (bz3_state->cm_state) free(bz3_state->cm_state);
        if (bz3_state->swap_buffer) free(bz3_state->swap_buffer);
        if (bz3_state->sais_array) free(bz3_state->sais_array);
        if (bz3_state->lzp_lut) free(bz3_state->lzp_lut);
        free(bz3_state);
        return NULL;
    }

    bz3_state->block_size = block_size;

    bz3_state->last_error = BZ3_OK;

    return bz3_state;
}


int dns_HTTPS_add_ipv4hint(struct dns_rr_nested *svcparam, unsigned char *addr[], int addr_num)
{
	if (_dns_left_len(&svcparam->context) < 4 + addr_num * DNS_RR_A_LEN) {
		return -1;
	}

	unsigned short value = DNS_HTTPS_T_IPV4HINT;
	dns_add_rr_nested_memcpy(svcparam, &value, 2);

	value = addr_num * DNS_RR_A_LEN;
	dns_add_rr_nested_memcpy(svcparam, &value, 2);

	for (int i = 0; i < addr_num; i++) {
		dns_add_rr_nested_memcpy(svcparam, addr[i], DNS_RR_A_LEN);
	}

	return 0;
}


int dns_add_rr_nested_end(struct dns_rr_nested *rr_nested, dns_type_t rtype)
{
	if (rr_nested == NULL || rr_nested->rr_start == NULL) {
		return -1;
	}

	int len = rr_nested->context.ptr - rr_nested->rr_start;
	unsigned char *ptr = rr_nested->rr_len_ptr;
	if (ptr == NULL || _dns_left_len(&rr_nested->context) < 2) {
		return -1;
	}

	/* NO SVC keys, reset ptr */
	if (len <= 14) {
		rr_nested->context.ptr = rr_nested->rr_start;
		return 0;
	}

	_dns_write_short(&ptr, len - rr_nested->rr_head_len);

	return _dns_rr_add_end(rr_nested->context.packet, rr_nested->type, rtype, len);
}


int dns_add_rr_nested_memcpy(struct dns_rr_nested *rr_nested, const void *data, int data_len)
{
	if (rr_nested == NULL || data == NULL || data_len <= 0) {
		return -1;
	}

	if (_dns_left_len(&rr_nested->context) < data_len) {
		return -1;
	}

	memcpy(rr_nested->context.ptr, data, data_len);
	rr_nested->context.ptr += data_len;

	return 0;
}


int dns_get_HTTPS_svcparm_start(struct dns_rrs *rrs, struct dns_https_param **https_param, char *domain, int maxsize,
								int *ttl, int *priority, char *target, int target_size)
{
	int qtype = 0;
	unsigned char *data = NULL;
	int rr_len = 0;

	data = dns_get_rr_nested_start(rrs, domain, maxsize, &qtype, ttl, &rr_len);
	if (data == NULL) {
		return -1;
	}

	if (qtype != DNS_T_HTTPS) {
		return -1;
	}

	if (rr_len < 2) {
		return -1;
	}

	*priority = _dns_read_short(&data);
	rr_len -= 2;
	if (rr_len <= 0) {
		return -1;
	}

	int len = strnlen((char *)data, rr_len);
	safe_strncpy(target, (char *)data, target_size);
	data += len + 1;
	rr_len -= len + 1;
	if (rr_len < 0) {
		return -1;
	}

	if (rr_len == 0) {
		*https_param = NULL;
		return 0;
	}

	*https_param = (struct dns_https_param *)data;

	return 0;
}


int dns_HTTPS_add_ipv6hint(struct dns_rr_nested *svcparam, unsigned char *addr[], int addr_num)
{
	if (_dns_left_len(&svcparam->context) < 4 + addr_num * DNS_RR_AAAA_LEN) {
		return -1;
	}

	unsigned short value = DNS_HTTPS_T_IPV6HINT;
	dns_add_rr_nested_memcpy(svcparam, &value, 2);

	value = addr_num * DNS_RR_AAAA_LEN;
	dns_add_rr_nested_memcpy(svcparam, &value, 2);

	for (int i = 0; i < addr_num; i++) {
		dns_add_rr_nested_memcpy(svcparam, addr[i], DNS_RR_AAAA_LEN);
	}

	return 0;
}


static int _dns_encode_HTTPS(struct dns_context *context, struct dns_rrs *rrs)
{
	int ret = 0;
	int qtype = 0;
	int qclass = 0;
	char domain[DNS_MAX_CNAME_LEN];
	char target[DNS_MAX_CNAME_LEN] = {0};
	unsigned char *rr_len_ptr = NULL;
	unsigned char *start = NULL;
	unsigned char *rr_start = NULL;
	int ttl = 0;
	int priority = 0;
	struct dns_https_param *param = NULL;

	ret =
		dns_get_HTTPS_svcparm_start(rrs, &param, domain, DNS_MAX_CNAME_LEN, &ttl, &priority, target, DNS_MAX_CNAME_LEN);
	if (ret < 0) {
		tlog(TLOG_DEBUG, "get https param failed.");
		return 0;
	}

	qtype = DNS_T_HTTPS;
	qclass = DNS_C_IN;

	ret = _dns_encode_rr_head(context, domain, qtype, qclass, ttl, 0, &rr_len_ptr);
	if (ret < 0) {
		return -1;
	}

	rr_start = context->ptr;
	if (_dns_left_len(context) < 2) {
		tlog(TLOG_ERROR, "left len is invalid.");
		return -1;
	}

	_dns_write_short(&context->ptr, priority);
	ret = _dns_encode_domain(context, target);
	if (ret < 0) {
		return -1;
	}

	start = context->ptr;
	for (; param != NULL; param = dns_get_HTTPS_svcparm_next(rrs, param)) {
		if (context->ptr - start > rrs->len || _dns_left_len(context) <= 0) {
			return -1;
		}

		if (param->len + 4 > _dns_left_len(context)) {
			return -1;
		}

		_dns_write_short(&context->ptr, param->key);
		_dns_write_short(&context->ptr, param->len);
		switch (param->key) {
		case DNS_HTTPS_T_MANDATORY:
		case DNS_HTTPS_T_NO_DEFAULT_ALPN:
		case DNS_HTTPS_T_ALPN:
		case DNS_HTTPS_T_PORT:
		case DNS_HTTPS_T_IPV4HINT:
		case DNS_HTTPS_T_ECH:
		case DNS_HTTPS_T_IPV6HINT: {
			memcpy(context->ptr, param->value, param->len);
			context->ptr += param->len;
		} break;
		default:
			/* skip unknown key */
			context->ptr -= 4;
			break;
		}
	}

	if (_dns_left_len(context) < 2) {
		return -1;
	}

	_dns_write_short(&rr_len_ptr, context->ptr - rr_start);

	return 0;
}


int dns_add_HTTPS_start(struct dns_rr_nested *svcparam_buffer, struct dns_packet *packet, dns_rr_type type,
						const char *domain, int ttl, int priority, const char *target)
{
	svcparam_buffer = dns_add_rr_nested_start(svcparam_buffer, packet, type, DNS_T_HTTPS, domain, ttl);
	if (svcparam_buffer == NULL) {
		return -1;
	}

	int target_len = 0;
	if (target == NULL) {
		target = "";
	}

	target_len = strnlen(target, DNS_MAX_CNAME_LEN) + 1;
	if (_dns_left_len(&svcparam_buffer->context) < 2 + target_len) {
		return -1;
	}

	/* add rr data */
	_dns_write_short(&svcparam_buffer->context.ptr, priority);
	safe_strncpy((char *)svcparam_buffer->context.ptr, target, target_len);
	svcparam_buffer->context.ptr += target_len;

	return 0;
}


static int _dns_encode_domain(struct dns_context *context, const char *domain)
{
	int num = 0;
	int total_len = 0;
	unsigned char *ptr_num = context->ptr++;
	int dict_offset = 0;

	dict_offset = _dns_get_domain_offset(context, domain);
	total_len++;

	/*[len]string[len]string...[0]0 */
	while (_dns_left_len(context) > 1 && *domain != 0) {
		total_len++;
		if (dict_offset >= 0) {
			int offset = 0xc000 | dict_offset;
			if (_dns_left_len(context) < 2) {
				return -1;
			}

			_dns_write_short(&ptr_num, offset);
			context->ptr++;
			ptr_num = NULL;
			return total_len;
		}

		if (*domain == '.') {
			*ptr_num = num;
			num = 0;
			ptr_num = context->ptr;
			domain++;
			context->ptr++;
			dict_offset = _dns_get_domain_offset(context, domain);
			continue;
		}
		*context->ptr = *domain;
		num++;
		context->ptr++;
		domain++;
	}

	if (_dns_left_len(context) < 1) {
		return -1;
	}

	*ptr_num = num;

	if (total_len > 1) {
		/* if domain is '\0', [domain] is '\0' */
		*(context->ptr) = 0;
		total_len++;
		context->ptr++;
	}

	if (_dns_left_len(context) <= 0) {
		return -1;
	}

	return total_len;
}


int dns_HTTPS_add_alpn(struct dns_rr_nested *svcparam, const char *alpn, int alpn_len)
{
	if (_dns_left_len(&svcparam->context) < 2 + 2 + alpn_len) {
		return -1;
	}

	unsigned short value = DNS_HTTPS_T_ALPN;
	dns_add_rr_nested_memcpy(svcparam, &value, 2);

	value = alpn_len;
	dns_add_rr_nested_memcpy(svcparam, &value, 2);
	dns_add_rr_nested_memcpy(svcparam, alpn, alpn_len);

	return 0;
}


PrintBackend *cpdbCreateBackendFromFile(GDBusConnection *connection,
                                        const char *backend_file_name)
{
    FILE *file = NULL;
    PrintBackend *proxy;
    GError *error = NULL;
    char *path, *backend_name;
    const char *info_dir_name;
    char obj_path[CPDB_BSIZE];

    backend_name = cpdbGetStringCopy(backend_file_name);
    if ((info_dir_name = getenv("CPDB_BACKEND_INFO_DIR")) == NULL)
      info_dir_name = CPDB_BACKEND_INFO_DIR;
    path = cpdbConcatPath(info_dir_name, backend_file_name);

    if ((file = fopen(path, "r")) == NULL)
    {
        logerror("Error creating backend %s : Couldn't open %s for reading\n",
                    backend_name, path);
        free(path);
        return NULL;
    }
    if (fscanf(file, "%1023s", obj_path) == 0)
    {
        logerror("Error creating backend %s : Couldn't parse %s\n",
                    backend_name, path);
        free(path);
        fclose(file);
        return NULL;
    }
    free(path);
    fclose(file);

    proxy = print_backend_proxy_new_sync(connection,
                                         0,
                                         backend_name,
                                         obj_path,
                                         NULL,
                                         &error);
    if (error)
    {
        logerror("Error creating backend proxy for %s : %s\n",
                    backend_name, error->message);
        return NULL;
    }

    return proxy;
}

A stack-based buffer overflow condition is a condition where the buffer being overwritten is allocated on the stack (i.e., is a local variable or, rarely, a parameter to a function).


ssl3_free(SSL *s)
{
	if (s == NULL)
		return;

	tls1_cleanup_key_block(s);
	ssl3_release_read_buffer(s);
	ssl3_release_write_buffer(s);

	tls_content_free(s->s3->rcontent);

	tls_buffer_free(s->s3->alert_fragment);
	tls_buffer_free(s->s3->handshake_fragment);

	freezero(s->s3->hs.sigalgs, s->s3->hs.sigalgs_len);
	sk_X509_pop_free(s->s3->hs.peer_certs, X509_free);
	sk_X509_pop_free(s->s3->hs.peer_certs_no_leaf, X509_free);
	tls_key_share_free(s->s3->hs.key_share);

	tls13_secrets_destroy(s->s3->hs.tls13.secrets);
	freezero(s->s3->hs.tls13.cookie, s->s3->hs.tls13.cookie_len);
	tls13_clienthello_hash_clear(&s->s3->hs.tls13);

	tls_buffer_free(s->s3->hs.tls13.quic_read_buffer);

	sk_X509_NAME_pop_free(s->s3->hs.tls12.ca_names, X509_NAME_free);
	sk_X509_pop_free(s->verified_chain, X509_free);
	s->verified_chain = NULL;

	tls1_transcript_free(s);
	tls1_transcript_hash_free(s);

	free(s->s3->alpn_selected);

	freezero(s->s3->peer_quic_transport_params,
	    s->s3->peer_quic_transport_params_len);

	freezero(s->s3, sizeof(*s->s3));

	s->s3 = NULL;
}

The product calls free() twice on the same memory address, potentially leading to modification of unexpected memory locations.


Referencing memory after it has been freed can cause a program to crash, use unexpected values, or execute code.


int pico_tcp_initconn(struct pico_socket *s)
{
    struct pico_socket_tcp *ts = TCP_SOCK(s);
    struct pico_frame *syn;
    struct pico_tcp_hdr *hdr;
    uint16_t mtu, opt_len = tcp_options_size(ts, PICO_TCP_SYN);

    syn = s->net->alloc(s->stack, s->net, NULL, (uint16_t)(PICO_SIZE_TCPHDR + opt_len));
    if (!syn)
        return -1;

    hdr = (struct pico_tcp_hdr *) syn->transport_hdr;

    if (!ts->snd_nxt)
        ts->snd_nxt = long_be(pico_paws());

    ts->snd_last = ts->snd_nxt;
    ts->cwnd = PICO_TCP_IW;
    mtu = (uint16_t)pico_socket_get_mss(s);
    if (mtu > PICO_SIZE_TCPHDR + PICO_TCP_MIN_MSS)
        ts->mss = (uint16_t)(mtu - PICO_SIZE_TCPHDR);
    else
        ts->mss = PICO_TCP_MIN_MSS;
    ts->ssthresh = (uint16_t)((uint16_t)(PICO_DEFAULT_SOCKETQ / ts->mss) -  (((uint16_t)(PICO_DEFAULT_SOCKETQ / ts->mss)) >> 3u));
    syn->sock = s;
    hdr->seq = long_be(ts->snd_nxt);
    hdr->len = (uint8_t)((PICO_SIZE_TCPHDR + opt_len) << 2);
    hdr->flags = PICO_TCP_SYN;
    tcp_set_space(ts);
    hdr->rwnd = short_be(ts->wnd);
    tcp_add_options(ts, syn, PICO_TCP_SYN, opt_len);
    hdr->trans.sport = ts->sock.local_port;
    hdr->trans.dport = ts->sock.remote_port;

    hdr->crc = 0;
    hdr->crc = short_be(pico_tcp_checksum(syn));

    /* TCP: ENQUEUE to PROTO ( SYN ) */
    tcp_dbg("Sending SYN... (ports: %d - %d) size: %d\n", short_be(ts->sock.local_port), short_be(ts->sock.remote_port), syn->buffer_len);
    ts->retrans_tmr = pico_timer_add(s->stack, PICO_TCP_SYN_TO << ts->backoff, initconn_retry, ts);
    if (!ts->retrans_tmr) {
        tcp_dbg("TCP: Failed to start initconn_retry timer\n");
        PICO_FREE(syn);
        return -1;
    }
    pico_enqueue(&s->stack->q_tcp.out, syn);
    return 0;
}

The product uses or accesses a resource that has not been initialized.


struct pico_socket *pico_tcp_open(struct pico_stack *S, uint16_t family)
{
    struct pico_socket_tcp *t = PICO_ZALLOC(sizeof(struct pico_socket_tcp));
    if (!t)
        return NULL;

    t->sock.stack = S;
    t->sock.timestamp = TCP_TIME;
    pico_socket_set_family(&t->sock, family);
    t->mss = (uint16_t)(pico_socket_get_mss(&t->sock));
    if (t->mss > PICO_SIZE_TCPHDR + PICO_TCP_MIN_MSS)
        t->mss -= (uint16_t)PICO_SIZE_TCPHDR;
    else
        t->mss = PICO_TCP_MIN_MSS;
    t->tcpq_in.pool.root = t->tcpq_hold.pool.root = t->tcpq_out.pool.root = &LEAF;
    t->tcpq_hold.pool.compare = t->tcpq_out.pool.compare = segment_compare;
    t->tcpq_in.pool.compare = input_segment_compare;
    t->tcpq_in.max_size = PICO_DEFAULT_SOCKETQ;
    t->tcpq_out.max_size = PICO_DEFAULT_SOCKETQ;
    t->tcpq_hold.max_size = 2u * t->mss;
    rto_set(t, PICO_TCP_RTO_MIN);

    /* Uncomment next line and disable Nagle by default */
    t->sock.opt_flags |= (1 << PICO_SOCKET_OPT_TCPNODELAY);

    /* Uncomment next line and Nagle is enabled by default */
    /* t->sock.opt_flags &= (uint16_t) ~(1 << PICO_SOCKET_OPT_TCPNODELAY); */

    /* Set default linger for the socket */
    t->linger_timeout = PICO_SOCKET_LINGER_TIMEOUT;

#ifdef PICO_TCP_SUPPORT_SOCKET_STATS
    if (!pico_timer_add(t->sock.stack, 2000, sock_stats, t)) {
        tcp_dbg("TCP: Failed to start socket statistics timer\n");
        PICO_FREE(t);
        return NULL;
    }
#endif

    t->keepalive_tmr = pico_timer_add(t->sock.stack, 1000, pico_tcp_keepalive, t);
    if (!t->keepalive_tmr) {
        tcp_dbg("TCP: Failed to start keepalive timer\n");
        PICO_FREE(t);
        return NULL;
    }
    tcp_set_space(t);
    return &t->sock;
}


int ipfilter(struct pico_frame *f)
{
    struct filter_node temp;
    struct pico_ipv4_hdr *ipv4_hdr = (struct pico_ipv4_hdr *) f->net_hdr;
    struct pico_trans *trans;
    struct pico_icmp4_hdr *icmp_hdr;

    memset(&temp, 0u, sizeof(struct filter_node));

    temp.fdev = f->dev;
    temp.out_addr = ipv4_hdr->dst.addr;
    temp.in_addr = ipv4_hdr->src.addr;
    if ((f->transport_hdr + sizeof(struct pico_trans)) <= (f->buffer + f->buffer_len)) {
        if ((ipv4_hdr->proto == PICO_PROTO_TCP) || (ipv4_hdr->proto == PICO_PROTO_UDP)) {
                trans = (struct pico_trans *) f->transport_hdr;
                temp.out_port = short_be(trans->dport);
                temp.in_port = short_be(trans->sport);
        } else if(ipv4_hdr->proto == PICO_PROTO_ICMP4) {
            icmp_hdr = (struct pico_icmp4_hdr *) f->transport_hdr;
            if(icmp_hdr->type == PICO_ICMP_UNREACH && icmp_hdr->code == PICO_ICMP_UNREACH_FILTER_PROHIB)
                return 0;
        }
        temp.proto = ipv4_hdr->proto;
    }
    temp.priority = f->priority;
    temp.tos = ipv4_hdr->tos;
    return ipfilter_apply_filter(f, &temp);
}


static inline void tcp_parse_option_mss(struct pico_socket_tcp *t, uint8_t len, uint8_t *opt, uint32_t *idx)
{
    uint16_t mss;
    if (tcpopt_len_check(idx, len, PICO_TCPOPTLEN_MSS) < 0)
        return;

    if ((*idx + PICO_TCPOPTLEN_MSS) > len)
        return;

    t->mss_ok = 1;
    mss = short_from(opt + *idx);
    *idx += (uint32_t)sizeof(uint16_t);
    if (t->mss > short_be(mss))
        t->mss = short_be(mss);
}

Improper Check for Unusual or Exceptional Conditions

The product does not check or incorrectly checks for unusual or exceptional conditions that are not expected to occur frequently during day to day operation of the product.


static int tcp_parse_options(struct pico_frame *f)
{
    struct pico_socket_tcp *t = (struct pico_socket_tcp *)f->sock;
    uint8_t *opt = f->transport_hdr + PICO_SIZE_TCPHDR;
    uint32_t i = 0;
    f->timestamp = 0;

    if (f->buffer + f->buffer_len > f->transport_hdr + f->transport_len)
        return -1;

    while (i < (f->transport_len - PICO_SIZE_TCPHDR)) {
        uint8_t type =  opt[i++];
        uint8_t len;
        if(i < (f->transport_len - PICO_SIZE_TCPHDR) && (type > 1))
            len =  opt[i++];
        else
            len = 1;

        if (f->payload && ((opt + i) > f->payload))
            break;

        if (len == 0) {
            return -1;
        }

        tcp_dbg_options("Received option '%d', len = %d \n", type, len);
        switch (type) {
        case PICO_TCP_OPTION_NOOP:
        case PICO_TCP_OPTION_END:
            break;
        case PICO_TCP_OPTION_WS:
            tcp_parse_option_ws(t, len, opt, &i);
            break;
        case PICO_TCP_OPTION_SACK_OK:
            tcp_parse_option_sack_ok(t, f, len, &i);
            break;
        case PICO_TCP_OPTION_MSS:
            tcp_parse_option_mss(t, len, opt, &i);
            break;
        case PICO_TCP_OPTION_TIMESTAMP:
            tcp_parse_option_timestamp(t, f, len, opt, &i);
            break;

        case PICO_TCP_OPTION_SACK:
            tcp_rcv_sack(t, opt + i, len - 2);
            i = i + len - 2;
            break;
        default:
            tcp_dbg_options("TCP: received unsupported option %u\n", type);
            i = i + len - 2;
        }
    }
    return 0;
}


_xdr_kadm5_principal_ent_rec(XDR *xdrs, kadm5_principal_ent_rec *objp,
			     int v)
{
	unsigned int n;
	bool_t r;

	if (!xdr_krb5_principal(xdrs, &objp->principal)) {
		return (FALSE);
	}
	if (!xdr_krb5_timestamp(xdrs, &objp->princ_expire_time)) {
		return (FALSE);
	}
	if (!xdr_krb5_timestamp(xdrs, &objp->last_pwd_change)) {
		return (FALSE);
	}
	if (!xdr_krb5_timestamp(xdrs, &objp->pw_expiration)) {
		return (FALSE);
	}
	if (!xdr_krb5_deltat(xdrs, &objp->max_life)) {
		return (FALSE);
	}
	if (!xdr_nulltype(xdrs, (void **) &objp->mod_name,
			  xdr_krb5_principal)) {
		return (FALSE);
	}
	if (!xdr_krb5_timestamp(xdrs, &objp->mod_date)) {
		return (FALSE);
	}
	if (!xdr_krb5_flags(xdrs, &objp->attributes)) {
		return (FALSE);
	}
	if (!xdr_krb5_kvno(xdrs, &objp->kvno)) {
		return (FALSE);
	}
	if (!xdr_krb5_kvno(xdrs, &objp->mkvno)) {
		return (FALSE);
	}
	if (!xdr_nullstring(xdrs, &objp->policy)) {
		return (FALSE);
	}
	if (!xdr_long(xdrs, &objp->aux_attributes)) {
		return (FALSE);
	}
	if (!xdr_krb5_deltat(xdrs, &objp->max_renewable_life)) {
		return (FALSE);
	}
	if (!xdr_krb5_timestamp(xdrs, &objp->last_success)) {
		return (FALSE);
	}
	if (!xdr_krb5_timestamp(xdrs, &objp->last_failed)) {
		return (FALSE);
	}
	if (!xdr_krb5_kvno(xdrs, &objp->fail_auth_count)) {
		return (FALSE);
	}
	if (!xdr_krb5_int16(xdrs, &objp->n_key_data)) {
		return (FALSE);
	}
	if (xdrs->x_op == XDR_DECODE && objp->n_key_data < 0) {
		return (FALSE);
	}
	if (!xdr_krb5_int16(xdrs, &objp->n_tl_data)) {
		return (FALSE);
	}
	if (!xdr_nulltype(xdrs, (void **) &objp->tl_data,
			  xdr_krb5_tl_data)) {
		return FALSE;
	}
	n = objp->n_key_data;
	r = xdr_array(xdrs, (caddr_t *) &objp->key_data, &n, objp->n_key_data,
		      sizeof(krb5_key_data), xdr_krb5_key_data_nocontents);
	objp->n_key_data = n;
	if (!r) {
		return (FALSE);
	}

	return (TRUE);
}

The product accesses or uses a pointer that has not been initialized.


DltReturnValue dlt_file_message(DltFile *file, int index, int verbose)
{
    PRINT_FUNCTION_VERBOSE(verbose);

    if (file == NULL)
        return DLT_RETURN_WRONG_PARAMETER;

    /* check if message is in range */
    if (index < 0 || index >= file->counter) {
        dlt_vlog(LOG_WARNING, "Message %d out of range!\r\n", index);
        return DLT_RETURN_WRONG_PARAMETER;
    }

    /* seek to position in file */
    if (fseek(file->handle, file->index[index], SEEK_SET) != 0) {
        dlt_vlog(LOG_WARNING, "Seek to message %d to position %ld failed!\r\n",
                 index, file->index[index]);
        return DLT_RETURN_ERROR;
    }

    /* read all header and payload */
    if (dlt_file_read_header(file, verbose) < DLT_RETURN_OK)
        return DLT_RETURN_ERROR;

    if (dlt_file_read_header_extended(file, verbose) < DLT_RETURN_OK)
        return DLT_RETURN_ERROR;

    if (dlt_file_read_data(file, verbose) < DLT_RETURN_OK)
        return DLT_RETURN_ERROR;

    /* set current position in file */
    file->position = index;

    return DLT_RETURN_OK;
}

Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')

The product copies an input buffer to an output buffer without verifying that the size of the input buffer is less than the size of the output buffer, leading to a buffer overflow.


static int benaloh(void) {
	int code = RLC_ERR;
	bdpe_t pub, prv;
	bn_t a, b;
	dig_t in, out;
	uint8_t buf[RLC_BN_BITS / 8 + 1];
	size_t len;
	int result;
	dig_t prime = 0xFB;

	bn_null(a);
	bn_null(b);
	bdpe_null(pub);
	bdpe_null(prv);

	RLC_TRY {
		bn_new(a);
		bn_new(b);
		bdpe_new(pub);
		bdpe_new(prv);

		result = cp_bdpe_gen(pub, prv, prime, RLC_BN_BITS);

		TEST_CASE("benaloh encryption/decryption is correct") {
			TEST_ASSERT(result == RLC_OK, end);
			len = RLC_BN_BITS / 8 + 1;
			rand_bytes(buf, 1);
			in = buf[0] % prime;
			TEST_ASSERT(cp_bdpe_enc(buf, &len, in, pub) == RLC_OK, end);
			TEST_ASSERT(cp_bdpe_dec(&out, buf, len, prv) == RLC_OK, end);
			TEST_ASSERT(in == out, end);
		} TEST_END;

		TEST_CASE("benaloh encryption/decryption is homomorphic") {
			TEST_ASSERT(result == RLC_OK, end);
			len = RLC_BN_BITS / 8 + 1;
			rand_bytes(buf, 1);
			in = buf[0] % prime;
			TEST_ASSERT(cp_bdpe_enc(buf, &len, in, pub) == RLC_OK, end);
			bn_read_bin(a, buf, len);
			rand_bytes(buf, 1);
			out = (buf[0] % prime);
			in = (in + out) % prime;
			TEST_ASSERT(cp_bdpe_enc(buf, &len, out, pub) == RLC_OK, end);
			bn_read_bin(b, buf, len);
			bn_mul(a, a, b);
			bn_mod(a, a, pub->n);
			len = bn_size_bin(pub->n);
			bn_write_bin(buf, len, a);
			TEST_ASSERT(cp_bdpe_dec(&out, buf, len, prv) == RLC_OK, end);
			TEST_ASSERT(in == out, end);
		} TEST_END;
	} RLC_CATCH_ANY {
		RLC_ERROR(end);
	}
	code = RLC_OK;

  end:
	bn_free(a);
	bn_free(b);
	bdpe_free(pub);
	bdpe_free(prv);
	return code;
}

The product performs a calculation that can produce an integer overflow or wraparound, when the logic assumes that the resulting value will always be larger than the original value. This can introduce other weaknesses when the calculation is used for resource management or execution control.


static void benaloh(void) {
	bdpe_t pub, prv;
	dig_t in, new;
	uint8_t out[RLC_BN_BITS / 8 + 1];
	size_t out_len;
	dig_t prime = 0xFB;

	bdpe_null(pub);
	bdpe_null(prv);

	bdpe_new(pub);
	bdpe_new(prv);

	BENCH_ONE("cp_bdpe_gen", cp_bdpe_gen(pub, prv, prime, RLC_BN_BITS), 1);

	BENCH_RUN("cp_bdpe_enc") {
		out_len = RLC_BN_BITS / 8 + 1;
		rand_bytes(out, 1);
		in = out[0] % prime;
		BENCH_ADD(cp_bdpe_enc(out, &out_len, in, pub));
		cp_bdpe_dec(&new, out, out_len, prv);
	} BENCH_END;

	BENCH_RUN("cp_bdpe_dec") {
		out_len = RLC_BN_BITS / 8 + 1;
		rand_bytes(out, 1);
		in = out[0] % prime;
		cp_bdpe_enc(out, &out_len, in, pub);
		BENCH_ADD(cp_bdpe_dec(&new, out, out_len, prv));
	} BENCH_END;

	bdpe_free(pub);
	bdpe_free(prv);
}


static void memory2(void) {
	ep2_t a[BENCH];

	BENCH_FEW("ep2_null", ep2_null(a[i]), 1);

	BENCH_FEW("ep2_new", ep2_new(a[i]), 1);
	for (int i = 0; i < BENCH; i++) {
		ep2_free(a[i]);
	}

	for (int i = 0; i < BENCH; i++) {
		ep2_new(a[i]);
	}
	BENCH_FEW("ep2_free", ep2_free(a[i]), 1);

	(void)a;
}


void bn_grow(bn_t a, size_t digits) {
#if ALLOC == DYNAMIC
	dig_t *t;

	if (a->alloc < digits) {
		/* At least add RLC_BN_SIZE more digits. */
		digits += (RLC_BN_SIZE * 2) - (digits % RLC_BN_SIZE);
		t = (dig_t *)realloc(a->dp, (RLC_DIG / 8) * digits);
		if (t == NULL) {
			RLC_THROW(ERR_NO_MEMORY);
			return;
		}
		a->dp = t;
		/* Set the newly allocated digits to zero. */
		a->alloc = digits;
	}
#elif ALLOC == AUTO
	if (digits > RLC_BN_SIZE) {
		RLC_THROW(ERR_NO_PRECI);
		return;
	}
	(void)a;
#endif
}


void bn_make(bn_t a, size_t digits) {
	if (digits < 0) {
		RLC_THROW(ERR_NO_VALID);
	}
	/* Allocate at least one digit. */
	digits = RLC_MAX(digits, 1);

#if ALLOC == DYNAMIC
	if (digits % RLC_BN_SIZE != 0) {
		/* Pad the number of digits to a multiple of the block. */
		digits += (RLC_BN_SIZE - digits % RLC_BN_SIZE);
	}

	if (a != NULL) {
		a->dp = NULL;
#if ALIGN == 1
		a->dp = (dig_t *)malloc(digits * sizeof(dig_t));
#elif OPSYS == WINDOWS
		a->dp = _aligned_malloc(digits * sizeof(dig_t), ALIGN);
#else
		int r = posix_memalign((void **)&a->dp, ALIGN, digits * sizeof(dig_t));
		if (r == ENOMEM) {
			RLC_THROW(ERR_NO_MEMORY);
		}
		if (r == EINVAL) {
			RLC_THROW(ERR_NO_VALID);
		}
#endif /* ALIGN */
	}

	if (a->dp == NULL) {
		free((void *)a);
		RLC_THROW(ERR_NO_MEMORY);
	}
#else
	/* Verify if the number of digits is sane. */
	if (digits > RLC_BN_SIZE) {
		RLC_THROW(ERR_NO_PRECI);
		return;
	} else {
		digits = RLC_BN_SIZE;
	}
#endif
	if (a != NULL) {
		a->used = 1;
		a->dp[0] = 0;
		a->alloc = digits;
		a->sign = RLC_POS;
	}
}


void bn_trim(bn_t a) {
	if (a->used <= a->alloc) {
		while (a->used > 0 && a->dp[a->used - 1] == 0) {
			--(a->used);
		}
		/* Zero can't be negative. */
		if (a->used == 0) {
			a->used = 1;
			a->dp[0] = 0;
			a->sign = RLC_POS;
		}
	}
}


void bn_mxp_slide(bn_t c, const bn_t a, const bn_t b, const bn_t m) {
	bn_t tab[RLC_TABLE_SIZE], t, u, r;
	size_t l, w = 1;
	uint8_t *win = RLC_ALLOCA(uint8_t, bn_bits(b));

	if (win == NULL) {
		RLC_THROW(ERR_NO_MEMORY);
		return;
	}

	if (bn_cmp_dig(m, 1) == RLC_EQ) {
		RLC_FREE(win);
		bn_zero(c);
		return;
	}

	if (bn_is_zero(b)) {
		RLC_FREE(win);
		bn_set_dig(c, 1);
		return;
	}

	bn_null(t);
	bn_null(u);
	bn_null(r);
	/* Initialize table. */
	for (size_t i = 0; i < RLC_TABLE_SIZE; i++) {
		bn_null(tab[i]);
	}

	/* Find window size. */
	l = bn_bits(b);
	if (l <= 21) {
		w = 2;
	} else if (l <= 32) {
		w = 3;
	} else if (l <= 128) {
		w = 4;
	} else if (l <= 256) {
		w = 5;
	} else if (l <= 512) {
		w = 6;
	} else {
		w = 7;
	}

	RLC_TRY {
		for (size_t i = 0; i < (1 << (w - 1)); i++) {
			bn_new(tab[i]);
		}

		bn_new(t);
		bn_new(u);
		bn_new(r);
		bn_mod_pre(u, m);

#if BN_MOD == MONTY
		bn_set_dig(r, 1);
		bn_mod_monty_conv(r, r, m);
		bn_mod_monty_conv(t, a, m);
#else /* BN_MOD == BARRT || BN_MOD == RADIX */
		bn_set_dig(r, 1);
		bn_copy(t, a);
#endif

		bn_copy(tab[0], t);
		bn_sqr(t, tab[0]);
		bn_mod(t, t, m, u);
		/* Create table. */
		for (size_t i = 1; i < 1 << (w - 1); i++) {
			bn_mul(tab[i], tab[i - 1], t);
			bn_mod(tab[i], tab[i], m, u);
		}

		bn_rec_slw(win, &l, b, w);
		for (size_t i = 0; i < l; i++) {
			if (win[i] == 0) {
				bn_sqr(r, r);
				bn_mod(r, r, m, u);
			} else {
				for (size_t j = 0; j < util_bits_dig(win[i]); j++) {
					bn_sqr(r, r);
					bn_mod(r, r, m, u);
				}
				bn_mul(r, r, tab[win[i] >> 1]);
				bn_mod(r, r, m, u);
			}
		}
		bn_trim(r);
#if BN_MOD == MONTY
		bn_mod_monty_back(r, r, m);
#endif

		if (bn_sign(b) == RLC_NEG) {
			bn_mod_inv(c, r, m);
		} else {
			bn_copy(c, r);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		for (size_t i = 0; i < (1 << (w - 1)); i++) {
			bn_free(tab[i]);
		}
		bn_free(u);
		bn_free(t);
		bn_free(r);
		RLC_FREE(win);
	}
}


void bn_gen_prime_safep(bn_t a, size_t bits) {
	while (1) {
		do {
			bn_rand(a, RLC_POS, bits);
		} while (bn_bits(a) != bits);
		/* Check if (a - 1)/2 is prime. */
		bn_sub_dig(a, a, 1);
		bn_rsh(a, a, 1);
		if (bn_is_prime(a)) {
			/* Restore a. */
			bn_lsh(a, a, 1);
			bn_add_dig(a, a, 1);
			if (bn_is_prime(a)) {
				/* Should be prime now. */
				return;
			}
		}
	}
}


int bn_gen_prime_factor(bn_t a, bn_t b, size_t abits, size_t bbits) {
	bn_t t;
	int result = RLC_OK;

    if (! (bbits>abits) ) {
		return RLC_ERR;
    }

    bn_null(t);

    RLC_TRY {
        bn_new(t);
		bn_gen_prime(a, abits);
        do {
            bn_rand(t, RLC_POS, bbits - bn_bits(a));
            do {
                bn_mul(b, a, t);
                bn_add_dig(b, b, 1);
                bn_add_dig(t, t, 1);
            } while(! bn_is_prime(b));
        } while (bn_bits(b) != bbits);
    }
    RLC_CATCH_ANY {
		result = RLC_ERR;
    }
    RLC_FINALLY {
        bn_free(t);
    }

    return result;
}


void bn_gen_prime_stron(bn_t a, size_t bits) {
	dig_t i, j;
	int found, k;
	bn_t r, s, t;

	bn_null(r);
	bn_null(s);
	bn_null(t);

	RLC_TRY {
		bn_new(r);
		bn_new(s);
		bn_new(t);

		do {
			do {
				/* Generate two large primes r and s. */
				bn_rand(s, RLC_POS, bits / 2 - RLC_DIG / 2);
				bn_rand(t, RLC_POS, bits / 2 - RLC_DIG / 2);
			} while (!bn_is_prime(s) || !bn_is_prime(t));
			found = 1;
			bn_rand(a, RLC_POS, bits / 2 - bn_bits(t) - 1);
			i = a->dp[0];
			bn_dbl(t, t);
			do {
				/* Find first prime r = 2 * i * t + 1. */
				bn_mul_dig(r, t, i);
				bn_add_dig(r, r, 1);
				i++;
				if (bn_bits(r) > bits / 2 - 1) {
					found = 0;
					break;
				}
			} while (!bn_is_prime(r));
			if (found == 0) {
				continue;
			}
			/* Compute t = 2 * (s^(r-2) mod r) * s - 1. */
			bn_sub_dig(t, r, 2);
#if BN_MOD != PMERS
			bn_mxp(t, s, t, r);
#else
			bn_exp(t, s, t, r);
#endif

			bn_mul(t, t, s);
			bn_dbl(t, t);
			bn_sub_dig(t, t, 1);

			k = bits - bn_bits(r);
			k -= bn_bits(s);
			bn_rand(a, RLC_POS, k);
			j = a->dp[0];
			do {
				/* Find first prime a = t + 2 * j * r * s. */
				bn_mul(a, r, s);
				bn_mul_dig(a, a, j);
				bn_dbl(a, a);
				bn_add(a, a, t);
				j++;
				if (bn_bits(a) > bits) {
					found = 0;
					break;
				}
			} while (!bn_is_prime(a));
		} while (found == 0 && bn_bits(a) != bits);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(r);
		bn_free(s);
		bn_free(t);
	}
}


void bn_gen_prime_basic(bn_t a, size_t bits) {
	while (1) {
		do {
			bn_rand(a, RLC_POS, bits);
		} while (bn_bits(a) != bits);
		if (bn_is_prime(a)) {
			return;
		}
	}
}


static char get_bits(const bn_t a, size_t from, size_t to) {
	int f, t;
	dig_t mf, mt;

	RLC_RIP(from, f, from);
	RLC_RIP(to, t, to);

	if (f == t) {
		/* Same digit. */

		mf = RLC_MASK(from);
		if (to + 1 >= RLC_DIG) {
			mt = RLC_DMASK;
		} else {
			mt = RLC_MASK(to + 1);
		}

		mf = mf ^ mt;

		return ((a->dp[f] & (mf)) >> from);
	} else {
		mf = RLC_MASK(RLC_DIG - from) << from;
		mt = RLC_MASK(to + 1);

		return ((a->dp[f] & mf) >> from) |
				((a->dp[t] & mt) << (RLC_DIG - from));
	}
}


void bn_rec_naf(int8_t *naf, size_t *len, const bn_t k, size_t w) {
	int i, l;
	bn_t t;
	dig_t t0, mask;
	int8_t u_i;

	if (*len < (bn_bits(k) + 1)) {
		*len = 0;
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}

	bn_null(t);

	RLC_TRY {
		bn_new(t);
		bn_abs(t, k);

		mask = RLC_MASK(w);
		l = (1 << w);

		memset(naf, 0, *len);

		i = 0;
		if (w == 2) {
			while (!bn_is_zero(t)) {
				if (!bn_is_even(t)) {
					bn_get_dig(&t0, t);
					u_i = 2 - (t0 & mask);
					if (u_i < 0) {
						bn_add_dig(t, t, -u_i);
					} else {
						bn_sub_dig(t, t, u_i);
					}
					*naf = u_i;
				} else {
					*naf = 0;
				}
				bn_hlv(t, t);
				i++;
				naf++;
			}
		} else {
			while (!bn_is_zero(t)) {
				if (!bn_is_even(t)) {
					bn_get_dig(&t0, t);
					u_i = t0 & mask;
					if (u_i > l / 2) {
						u_i = (int8_t)(u_i - l);
					}
					if (u_i < 0) {
						bn_add_dig(t, t, -u_i);
					} else {
						bn_sub_dig(t, t, u_i);
					}
					*naf = u_i;
				} else {
					*naf = 0;
				}
				bn_hlv(t, t);
				i++;
				naf++;
			}
		}
		*len = i;
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(t);
	}
}


void bn_rec_slw(uint8_t *win, size_t *len, const bn_t k, size_t w) {
	int i, j, l, s;

	l = bn_bits(k);

	if (*len < l) {
		*len = 0;
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}

	memset(win, 0, *len);

	i = l - 1;
	j = 0;
	while (i >= 0) {
		if (!bn_get_bit(k, i)) {
			i--;
			win[j++] = 0;
		} else {
			s = RLC_MAX(i - w + 1, 0);
			while (!bn_get_bit(k, s)) {
				s++;
			}
			win[j++] = get_bits(k, s, i);
			i = s - 1;
		}
	}
	*len = j;
}


void bn_rec_glv(bn_t k0, bn_t k1, const bn_t k, const bn_t n, const bn_t *v1,
		const bn_t *v2) {
	bn_t t, b1, b2;
	int r1, r2;
	size_t bits;

	bn_null(b1);
	bn_null(b2);
	bn_null(t);

	RLC_TRY {
		bn_new(b1);
		bn_new(b2);
		bn_new(t);

		bn_abs(t, k);
		bits = bn_bits(n);

		bn_mul(b1, t, v1[0]);
		r1 = bn_get_bit(b1, bits);
		bn_rsh(b1, b1, bits + 1);
		bn_add_dig(b1, b1, r1);

		bn_mul(b2, t, v2[0]);
		r2 = bn_get_bit(b2, bits);
		bn_rsh(b2, b2, bits + 1);
		bn_add_dig(b2, b2, r2);

		bn_mul(k0, b1, v1[1]);
		bn_mul(k1, b2, v2[1]);
		bn_add(k0, k0, k1);
		bn_sub(k0, t, k0);

		bn_mul(k1, b1, v1[2]);
		bn_mul(t, b2, v2[2]);
		bn_add(k1, k1, t);
		bn_neg(k1, k1);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(b1);
		bn_free(b2);
		bn_free(t);
	}
}


void bn_rec_win(uint8_t *win, size_t *len, const bn_t k, size_t w) {
	int i, j, l;

	l = bn_bits(k);

	if (*len < RLC_CEIL(l, w)) {
		*len = 0;
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}

	memset(win, 0, *len);

	j = 0;
	for (i = 0; i < l - w; i += w) {
		win[j++] = get_bits(k, i, i + w - 1);
	}
	win[j++] = get_bits(k, i, bn_bits(k) - 1);
	*len = j;
}


void bn_rec_reg(int8_t *naf, size_t *len, const bn_t k, size_t n, size_t w) {
	int i, l;
	bn_t t;
	dig_t t0, mask;
	int8_t u_i;

	bn_null(t);

	mask = RLC_MASK(w);
	l = RLC_CEIL(n, w - 1);

	if (*len <= l) {
		*len = 0;
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}

	RLC_TRY {
		bn_new(t);
		bn_abs(t, k);

		memset(naf, 0, *len);

		i = 0;
		if (w == 2) {
			for (i = 0; i < l; i++) {
				u_i = (t->dp[0] & mask) - 2;
				t->dp[0] -= u_i;
				naf[i] = u_i;
				bn_hlv(t, t);
			}
			bn_get_dig(&t0, t);
			naf[i] = t0;
		} else {
			for (i = 0; i < l; i++) {
				u_i = (t->dp[0] & mask) - (1 << (w - 1));
				t->dp[0] -= u_i;
				naf[i] = u_i;
				bn_rsh(t, t, w - 1);
			}
			bn_get_dig(&t0, t);
			naf[i] = t0;
		}
		*len = l + 1;
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(t);
	}
}


void bn_rec_jsf(int8_t *jsf, size_t *len, const bn_t k, const bn_t l) {
	bn_t n0, n1;
	dig_t l0, l1;
	int8_t u0, u1, d0, d1;
	int i, j, offset;

	if (*len < (2 * bn_bits(k) + 1)) {
		*len = 0;
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}

	bn_null(n0);
	bn_null(n1);

	RLC_TRY {
		bn_new(n0);
		bn_new(n1);

		bn_abs(n0, k);
		bn_abs(n1, l);

		i = bn_bits(k);
		j = bn_bits(l);
		offset = RLC_MAX(i, j) + 1;

		memset(jsf, 0, *len);

		i = 0;
		d0 = d1 = 0;
		while (!(bn_is_zero(n0) && d0 == 0) || !(bn_is_zero(n1) && d1 == 0)) {
			bn_get_dig(&l0, n0);
			bn_get_dig(&l1, n1);
			/* For reduction modulo 8. */
			l0 = (l0 + d0) & RLC_MASK(3);
			l1 = (l1 + d1) & RLC_MASK(3);

			if (l0 % 2 == 0) {
				u0 = 0;
			} else {
				u0 = 2 - (l0 & RLC_MASK(2));
				if ((l0 == 3 || l0 == 5) && ((l1 & RLC_MASK(2)) == 2)) {
					u0 = (int8_t)-u0;
				}
			}
			jsf[i] = u0;
			if (l1 % 2 == 0) {
				u1 = 0;
			} else {
				u1 = 2 - (l1 & RLC_MASK(2));
				if ((l1 == 3 || l1 == 5) && ((l0 & RLC_MASK(2)) == 2)) {
					u1 = (int8_t)-u1;
				}
			}
			jsf[i + offset] = u1;

			if (d0 + d0 == 1 + u0) {
				d0 = (int8_t)(1 - d0);
			}
			if (d1 + d1 == 1 + u1) {
				d1 = (int8_t)(1 - d1);
			}

			i++;
			bn_hlv(n0, n0);
			bn_hlv(n1, n1);
		}
		*len = i;
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(n0);
		bn_free(n1);
	}

}


void bn_lsh(bn_t c, const bn_t a, unsigned int bits) {
	int digits;
	dig_t carry;

	bn_copy(c, a);

	RLC_RIP(bits, digits, bits);

	RLC_TRY {
		bn_grow(c, c->used + digits + (bits > 0));

		c->used = a->used + digits;
		c->sign = a->sign;
		if (digits > 0) {
			dv_lshd(c->dp, a->dp, c->used, digits);
		}

		if (bits > 0) {
			if (c != a) {
				carry = bn_lshb_low(c->dp + digits, a->dp, a->used, bits);
			} else {
				carry = bn_lshb_low(c->dp + digits, c->dp + digits, c->used - digits, bits);
			}
			if (carry != 0) {
				c->dp[c->used] = carry;
				(c->used)++;
			}
		}
		bn_trim(c);
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
}


void bn_rsh(bn_t c, const bn_t a, unsigned int bits) {
	int digits = 0;

	bn_copy(c, a);

	RLC_RIP(bits, digits, bits);

	if (digits > 0) {
		dv_rshd(c->dp, a->dp, a->used, digits);
	}

	if (a->used > digits) {
		c->used = a->used - digits;
	} else {
		c->used = 0;
	}
	c->sign = a->sign;

	if (c->used > 0 && bits > 0) {
		if (digits == 0 && c != a) {
			bn_rshb_low(c->dp, a->dp + digits, a->used - digits, bits);
		} else {
			bn_rshb_low(c->dp, c->dp, c->used, bits);
		}
	}
	bn_trim(c);
}


int bn_smb_jac(const bn_t a, const bn_t b) {
	bn_t t0, t1, r;
	int t, h, res;

	bn_null(t0);
	bn_null(t1);
	bn_null(r);

	/* Argument b must be odd. */
	if (bn_is_even(b) || bn_sign(b) == RLC_NEG) {
		RLC_THROW(ERR_NO_VALID);
		return 0;
	}

	RLC_TRY {
		bn_new(t0);
		bn_new(t1);
		bn_new(r);
		t = 1;

		if (bn_sign(a) == RLC_NEG) {
			bn_add(t0, a, b);
		} else {
			bn_copy(t0, a);
		}
		bn_copy(t1, b);

		while (1) {
			/* t0 = a mod b. */
			bn_mod(t0, t0, t1);
			/* If a = 0 then if n = 1 return t else return 0. */
			if (bn_is_zero(t0)) {
				if (bn_cmp_dig(t1, 1) == RLC_EQ) {
					res = 1;
					if (t == -1) {
						res = -1;
					}
					break;
				} else {
					res = 0;
					break;
				}
			}
			/* Write t0 as 2^h * t0. */
			h = 0;
			while (bn_is_even(t0) && !bn_is_zero(t0)) {
				h++;
				bn_rsh(t0, t0, 1);
			}
			/* If h != 0 (mod 2) and n != +-1 (mod 8) then t = -t. */
			bn_mod_2b(r, t1, 3);
			if ((h % 2 != 0) && (bn_cmp_dig(r, 1) != RLC_EQ) &&
					(bn_cmp_dig(r, 7) != RLC_EQ)) {
				t = -t;
			}
			/* If t0 != 1 (mod 4) and n != 1 (mod 4) then t = -t. */
			bn_mod_2b(r, t0, 2);
			if (bn_cmp_dig(r, 1) != RLC_EQ) {
				bn_mod_2b(r, t1, 2);
				if (bn_cmp_dig(r, 1) != RLC_EQ) {
					t = -t;
				}
			}
			bn_copy(r, t0);
			bn_copy(t0, t1);
			bn_copy(t1, r);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(t0);
		bn_free(t1);
		bn_free(r);
	}

	return res;
}


void bn_srt(bn_t c, bn_t a) {
	bn_t h, l, m, t;
	size_t bits;
	int cmp;

	if (bn_sign(a) == RLC_NEG) {
		RLC_THROW(ERR_NO_VALID);
	}

	bits = bn_bits(a);
	bits += (bits % 2);

	bn_null(h);
	bn_null(l);
	bn_null(m);
	bn_null(t);

	RLC_TRY {
		bn_new(h);
		bn_new(l);
		bn_new(m);
		bn_new(t);

		bn_zero(l);
		bn_set_2b(h, bits >> 1);
		if (bits >= 2) {
			bn_set_2b(l, (bits >> 1) - 1);
		}

		/* Trivial binary search approach. */
		do {
			bn_add(m, h, l);
			bn_hlv(m, m);
			bn_sqr(t, m);
			cmp = bn_cmp(t, a);
			bn_sub(t, h, l);

			if (cmp == RLC_GT) {
				bn_copy(h, m);
			} else if (cmp == RLC_LT) {
				bn_copy(l, m);
			}
		} while (bn_cmp_dig(t, 1) == RLC_GT && cmp != RLC_EQ);

		bn_copy(c, m);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(h);
		bn_free(l);
		bn_free(m);
		bn_free(t);
	}
}


size_t bn_bits(const bn_t a) {
	int bits;

	if (bn_is_zero(a)) {
		return 0;
	}

	/* Bits in lower digits. */
	bits = (a->used - 1) * RLC_DIG;

	return bits + util_bits_dig(a->dp[a->used - 1]);
}


void bn_write_raw(dig_t *raw, size_t len, const bn_t a) {
	int i, size;

	size = a->used;

	if (len < size) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}

	for (i = 0; i < size; i++) {
		raw[i] = a->dp[i];
	}
	for (; i < len; i++) {
		raw[i] = 0;
	}
}


void bn_set_2b(bn_t a, size_t b) {
	int i, d;

	if (b >= RLC_BN_SIZE * RLC_DIG) {
		RLC_THROW(ERR_NO_VALID);
	} else {
		RLC_RIP(b, d, b);

		bn_grow(a, d + 1);
		for (i = 0; i < d; i++) {
			a->dp[i] = 0;
		}
		a->used = d + 1;
		a->dp[d] = ((dig_t)1 << b);
		a->sign = RLC_POS;
	}
}


void bn_read_raw(bn_t a, const dig_t *raw, size_t len) {
	RLC_TRY {
		bn_grow(a, len);
		a->used = len;
		a->sign = RLC_POS;
		dv_copy(a->dp, raw, len);
		bn_trim(a);
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
}


size_t bn_size_raw(const bn_t a) {
	return a->used;
}


size_t bn_size_str(const bn_t a, unsigned int radix) {
	int digits = 0;
	bn_t t;

	bn_null(t);

	/* Check the radix. */
	if (radix < 2 || radix > 64) {
		RLC_THROW(ERR_NO_VALID);
		return 0;
	}

	if (bn_is_zero(a)) {
		return 2;
	}

	/* Binary case requires the bits, a sign and the null terminator. */
	if (radix == 2) {
		return bn_bits(a) + (a->sign == RLC_NEG ? 1 : 0) + 1;
	}

	if (a->sign == RLC_NEG) {
		digits++;
	}

	RLC_TRY {
		bn_new(t);
		bn_copy(t, a);

		t->sign = RLC_POS;

		while (!bn_is_zero(t)) {
			bn_div_dig(t, t, (dig_t)radix);
			digits++;
		}
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	} RLC_FINALLY {
		bn_free(t);
	}

	return digits + 1;
}


void bn_set_bit(bn_t a, size_t bit, int value) {
	int d;

	if (bit < 0) {
		RLC_THROW(ERR_NO_VALID);
		return;
	}

	RLC_RIP(bit, d, bit);

	bn_grow(a, d);

	if (value == 1) {
		a->dp[d] |= ((dig_t)1 << bit);
		if ((d + 1) > a->used) {
			a->used = d + 1;
		}
	} else {
		a->dp[d] &= ~((dig_t)1 << bit);
		bn_trim(a);
	}
}


size_t bn_ham(const bn_t a) {
	int c = 0;

	for (int i = 0; i < bn_bits(a); i++) {
		c += bn_get_bit(a, i);
	}

	return c;
}


void bn_rand(bn_t a, int sign, size_t bits) {
	int digits;

	RLC_RIP(bits, digits, bits);
	digits += (bits > 0 ? 1 : 0);

	bn_grow(a, digits);

	rand_bytes((uint8_t *)a->dp, digits * sizeof(dig_t));

	a->used = digits;
	a->sign = sign;
	if (bits > 0) {
		dig_t mask = ((dig_t)1 << (dig_t)bits) - 1;
		a->dp[a->used - 1] &= mask;
	}
	bn_trim(a);
}


void bn_write_str(char *str, size_t len, const bn_t a, unsigned int radix) {
	bn_t t;
	dig_t d;
	int digits, l, i, j;
	char c;

	bn_null(t);

	l = bn_size_str(a, radix);
	if (len < l) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}

	if (radix < 2 || radix > 64) {
		RLC_THROW(ERR_NO_VALID);
		return;
	}

	if (bn_is_zero(a) == 1) {
		*str++ = '0';
		*str = '\0';
		return;
	}

	RLC_TRY {
		bn_new(t);
		bn_copy(t, a);

		j = 0;
		if (t->sign == RLC_NEG) {
			str[j] = '-';
			j++;
			t->sign = RLC_POS;
		}

		digits = 0;
		while (!bn_is_zero(t) && j < len) {
			bn_div_rem_dig(t, &d, t, (dig_t)radix);
			str[j] = util_conv_char(d);
			digits++;
			j++;
		}

		/* Reverse the digits of the string. */
		i = 0;
		if (str[0] == '-') {
			i = 1;
		}

		j = l - 2;
		while (i < j) {
			c = str[i];
			str[i] = str[j];
			str[j] = c;
			++i;
			--j;
		}

		str[l - 1] = '\0';
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(t);
	}
}


void bn_read_bin(bn_t a, const uint8_t *bin, size_t len) {
	int i, j;
	dig_t d = (RLC_DIG / 8);
	int digs = (len % d == 0 ? len / d : len / d + 1);

	bn_grow(a, digs);
	bn_zero(a);
	a->used = digs;

	for (i = 0; i < digs - 1; i++) {
		d = 0;
		for (j = (RLC_DIG / 8) - 1; j >= 0; j--) {
			d = d << 8;
			d |= bin[len - 1 - (i * (RLC_DIG / 8) + j)];
		}
		a->dp[i] = d;
	}
	d = 0;
	for (j = (RLC_DIG / 8) - 1; j >= 0; j--) {
		if ((int)(i * (RLC_DIG / 8) + j) < len) {
			d = d << 8;
			d |= bin[len - 1 - (i * (RLC_DIG / 8) + j)];
		}
	}
	a->dp[i] = d;

	a->sign = RLC_POS;
	bn_trim(a);
}


size_t bn_size_bin(const bn_t a) {
	dig_t d;
	int digits;

	digits = (a->used - 1) * (RLC_DIG / 8);
	d = a->dp[a->used - 1];

	while (d != 0) {
		d = d >> 8;
		digits++;
	}
	return digits;
}


int bn_get_bit(const bn_t a, size_t bit) {
	int d;

	if (bit < 0) {
		RLC_THROW(ERR_NO_VALID);
		return 0;
	}

	if (bit > bn_bits(a)) {
		return 0;
	}

	RLC_RIP(bit, d, bit);

	if (d >= a->used) {
		return 0;
	} else {
		return (a->dp[d] >> bit) & (dig_t)1;
	}
}


void bn_write_bin(uint8_t *bin, size_t len, const bn_t a) {
	int size, k;
	dig_t d;

	size = bn_size_bin(a);

	if (len < size) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}

	k = 0;
	for (int i = 0; i < a->used - 1; i++) {
		d = a->dp[i];
		for (int j = 0; j < (int)(RLC_DIG / 8); j++) {
			bin[len - 1 - k++] = d & 0xFF;
			d = d >> 8;
		}
	}

	d = a->dp[a->used - 1];
	while (d != 0) {
		bin[len - 1 - k++] = d & 0xFF;
		d = d >> 8;
	}

	while (k < len) {
		bin[len - 1 - k++] = 0;
	}
}


void bn_read_str(bn_t a, const char *str, size_t len, unsigned int radix) {
	int sign, i, j;
	char c;

	bn_zero(a);

	if (radix < 2 || radix > 64) {
		RLC_THROW(ERR_NO_VALID);
		return;
	}

	j = 0;
	if (str[0] == '-') {
		j++;
		sign = RLC_NEG;
	} else {
		sign = RLC_POS;
	}

	RLC_TRY {
		bn_grow(a, RLC_CEIL(len * util_bits_dig(radix), RLC_DIG));

		while (j < len) {
			if (str[j] == 0) {
				break;
			}
			c = (char)((radix < 36) ? RLC_UPP(str[j]) : str[j]);
			for (i = 0; i < 64; i++) {
				if (c == util_conv_char(i)) {
					break;
				}
			}

			if (i < radix) {
				bn_mul_dig(a, a, (dig_t)radix);
				bn_add_dig(a, a, (dig_t)i);
			} else {
				break;
			}
			j++;
		}

		a->sign = sign;
		bn_trim(a);
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
}


int cp_bls_sig(g1_t s, const uint8_t *msg, size_t len, const bn_t d) {
	g1_t p;
	int result = RLC_OK;

	g1_null(p);

	RLC_TRY {
		g1_new(p);
		g1_map(p, msg, len);
		g1_mul_key(s, p, d);
	}
	RLC_CATCH_ANY {
		result = RLC_ERR;
	}
	RLC_FINALLY {
		g1_free(p);
	}
	return result;
}


int cp_cmlhs_sig(g1_t sig, g2_t z, g1_t a, g1_t c, g1_t r, g2_t s,
		const bn_t msg, const char *data, int label, const bn_t x, const g1_t h,
		const uint8_t prf[], size_t plen, const bn_t d, const bn_t sk,
		int bls) {
	bn_t k, m, n;
	g1_t t;
	uint8_t mac[RLC_MD_LEN];
	size_t len, dlen = strlen(data);
	uint8_t *buf = RLC_ALLOCA(uint8_t, 1 + 8 * RLC_PC_BYTES + dlen);
	int result = RLC_OK;

	bn_null(k);
	bn_null(m);
	bn_null(n);
	g1_null(t);

	RLC_TRY {
		bn_new(k);
		bn_new(m);
		bn_new(n);
		g1_new(t);
		if (buf == NULL) {
			RLC_THROW(ERR_NO_MEMORY);
		}

		pc_get_ord(n);
		/* Generate r and s. */
		bn_rand_mod(k, n);
		bn_rand_mod(m, n);

		/* Compute S = -g2^s, C = g1^s. */
		g2_mul_gen(s, m);
		g2_neg(s, s);
		g1_mul_gen(c, m);
		/* Compute R = g1^(r - ys). */
		bn_mul(m, d, m);
		bn_mod(m, m, n);
		bn_sub(m, k, m);
		bn_mod(m, m, n);
		g1_mul_gen(r, m);

		/* Compute A = g1^(x + r) * \prod H_j^(y * m_j). */
		bn_add(k, x, k);
		bn_mod(k, k, n);
		g1_mul_gen(a, k);
		bn_mul(k, d, msg);
		bn_mod(k, k, n);
		g1_mul(t, h, k);
		g1_add(a, a, t);
		g1_norm(a, a);
		/* Compute z = F_K(delta), Z = g2^z, A = A^(1/z). */
		md_hmac(mac, (const uint8_t *)data, dlen, prf, plen);
		bn_read_bin(k, mac, RLC_MD_LEN);
		bn_mod(k, k, n);
		g2_mul_gen(z, k);
		bn_mod_inv(k, k, n);
		g1_mul(a, a, k);

		/* Compute C = C * sum H_j^m_j. */
		bn_mod(k, msg, n);
		g1_mul(t, h, k);
		g1_add(c, c, t);
		g1_norm(c, c);

		len = g2_size_bin(z, 0);
		g2_write_bin(buf, len, z, 0);
		memcpy(buf + len, data, dlen);
		if (bls) {
			cp_bls_sig(sig, buf, len + dlen, sk);
		} else {
			cp_ecdsa_sig(m, n, buf, len + dlen, 0, sk);
			fp_prime_conv(sig->x, m);
			fp_prime_conv(sig->y, n);
			fp_set_dig(sig->z, 1);
		}
	}
	RLC_CATCH_ANY {
		result = RLC_ERR;
	}
	RLC_FINALLY {
		bn_free(k);
		bn_free(m);
		bn_free(n);
		g1_free(t);
		RLC_FREE(buf);
	}
	return result;
}


int cp_cmlhs_ver(const g1_t r, const g2_t s, const g1_t sig[], const g2_t z[],
		const g1_t a[], const g1_t c[], const bn_t msg, const char *data,
		const g1_t h, const int label[], const gt_t *hs[], const dig_t *f[],
		const size_t flen[], const g2_t y[], const g2_t pk[], size_t slen,
		int bls) {
	g1_t g1;
	g2_t g2;
	gt_t e, u, v;
	bn_t k, n;
	size_t len, dlen = strlen(data);
	uint8_t *buf = RLC_ALLOCA(uint8_t, 1 + 8 * RLC_PC_BYTES + dlen);
	int result = 1;

	g1_null(g1);
	g2_null(g2);
	gt_null(e);
	gt_null(u);
	gt_null(v);
	bn_null(k);
	bn_null(n);

	RLC_TRY {
		g1_new(g1);
		g2_new(g2);
		gt_new(e);
		gt_new(u);
		gt_new(v);
		bn_new(k);
		bn_new(n);
		if (buf == NULL) {
			RLC_THROW(ERR_NO_MEMORY);
		}

		for (int i = 0; i < slen; i++) {
			len = g2_size_bin(z[i], 0);
			g2_write_bin(buf, len, z[i], 0);
			memcpy(buf + len, data, dlen);
			if (bls) {
				result &= cp_bls_ver(sig[i], buf, len + dlen, pk[i]);
			} else {
				fp_prime_back(k, sig[i]->x);
				fp_prime_back(n, sig[i]->y);
				fp_copy(g1->x, pk[i]->x[0]);
				fp_copy(g1->y, pk[i]->y[0]);
				fp_set_dig(g1->z, 1);
				result &= cp_ecdsa_ver(k, n, buf, len + dlen, 0, g1);
			}
		}

		pc_get_ord(n);
		g1_get_gen(g1);
		g2_get_gen(g2);

		pc_map_sim(e, a, z, slen);
		pc_map_sim(u, c, y, slen);
		pc_map(v, r, g2);
		gt_mul(u, u, v);

		for (int i = 0; i < slen; i++) {
			for (int j = 0; j < flen[i]; j++) {
				gt_exp_dig(v, hs[i][label[j]], f[i][j]);
				gt_mul(u, u, v);
			}
		}
		if (gt_cmp(e, u) != RLC_EQ) {
			result = 0;
		}

		pc_map(e, g1, s);
		g1_set_infty(g1);
		for (int i = 0; i < slen; i++) {
			g1_add(g1, g1, c[i]);
		}
		g1_norm(g1, g1);
		pc_map(u, g1, g2);
		gt_mul(e, e, u);

		g1_mul(g1, h, msg);
		pc_map(v, g1, g2);
		if (gt_cmp(e, v) != RLC_EQ) {
			result = 0;
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		g1_free(g1);
		g2_free(g2);
		gt_free(e);
		gt_free(u);
		gt_free(v);
		bn_free(k);
		bn_free(n);
		RLC_FREE(buf);
	}
	return result;
}


int cp_cmlhs_gen(bn_t x[], gt_t hs[], size_t len, uint8_t prf[], size_t plen,
		bn_t sk, g2_t pk, bn_t d, g2_t y, int bls) {
	g1_t g1;
	g2_t g2;
	gt_t gt;
	bn_t n;
	int result = RLC_OK;

	g1_null(g1);
	g2_null(g2);
	gt_null(gt);
	bn_null(n);

	RLC_TRY {
		bn_new(n);
		g1_new(g1);
		g2_new(g2);
		gt_new(gt);

		pc_get_ord(n);
		g1_get_gen(g1);
		g2_get_gen(g2);
		pc_map(gt, g1, g2);

		rand_bytes(prf, plen);

		bn_rand_mod(d, n);
		g2_mul_gen(y, d);

		/* Generate elements for n tags. */
		for (int i = 0; i < len; i++) {
			bn_rand_mod(x[i], n);
			gt_exp(hs[i], gt, x[i]);
		}

		if (bls) {
			result = cp_bls_gen(sk, pk);
		} else {
			if (cp_ecdsa_gen(sk, g1) == RLC_OK) {
				fp_copy(pk->x[0], g1->x);
				fp_copy(pk->y[0], g1->y);
			} else {
				result = RLC_ERR;
			}
		}
	}
	RLC_CATCH_ANY {
		result = RLC_ERR;
	}
	RLC_FINALLY {
		g1_free(g1);
		g2_free(g2);
		gt_free(gt);
		bn_free(n);
	}
	return result;
}


int cp_cmlhs_onv(const g1_t r, const g2_t s, const g1_t sig[], const g2_t z[],
		const g1_t a[], const g1_t c[], const bn_t msg, const char *data,
		const g1_t h, const gt_t vk, const g2_t y[], const g2_t pk[],
		size_t slen, int bls) {
	g1_t g1;
	g2_t g2;
	gt_t e, u, v;
	bn_t k, n;
	size_t len, dlen = strlen(data);
	uint8_t *buf = RLC_ALLOCA(uint8_t, 1 + 8 * RLC_FP_BYTES + dlen);
	int result = 1;

	g1_null(g1);
	g2_null(g2);
	gt_null(e);
	gt_null(u);
	gt_null(v);
	bn_null(k);
	bn_null(n);

	RLC_TRY {
		g1_new(g1);
		g2_new(g2);
		gt_new(e);
		gt_new(u);
		gt_new(v);
		bn_new(k);
		bn_new(n);
		if (buf == NULL) {
			RLC_THROW(ERR_NO_MEMORY);
		}

		for (int i = 0; i < slen; i++) {
			len = g2_size_bin(z[i], 0);
			g2_write_bin(buf, len, z[i], 0);
			memcpy(buf + len, data, dlen);
			if (bls) {
				result &= cp_bls_ver(sig[i], buf, len + dlen, pk[i]);
			} else {
				fp_prime_back(k, sig[i]->x);
				fp_prime_back(n, sig[i]->y);
				fp_copy(g1->x, pk[i]->x[0]);
				fp_copy(g1->y, pk[i]->y[0]);
				fp_set_dig(g1->z, 1);
				result &= cp_ecdsa_ver(k, n, buf, len + dlen, 0, g1);
			}
		}

		pc_get_ord(n);
		g1_get_gen(g1);
		g2_get_gen(g2);

		pc_map_sim(e, a, z, slen);
		pc_map_sim(u, c, y, slen);
		pc_map(v, r, g2);
		gt_mul(u, u, v);
		gt_mul(u, u, vk);

		if (gt_cmp(e, u) != RLC_EQ) {
			result = 0;
		}

		pc_map(e, g1, s);
		g1_set_infty(g1);
		for (int i = 0; i < slen; i++) {
			g1_add(g1, g1, c[i]);
		}
		g1_norm(g1, g1);
		pc_map(u, g1, g2);
		gt_mul(e, e, u);

		g1_mul(g1, h, msg);
		pc_map(v, g1, g2);
		if (gt_cmp(e, v) != RLC_EQ) {
			result = 0;
		}
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	} RLC_FINALLY {
		g1_free(g1);
		g2_free(g2);
		gt_free(e);
		gt_free(u);
		gt_free(v);
		bn_free(k);
		bn_free(n);
		RLC_FREE(buf);
	}
	return result;
}


int cp_pbpsi_ask(g2_t d[], bn_t r, const bn_t x[], const g2_t s[], size_t m) {
	int i, result = RLC_OK;
	bn_t t, q, *p = RLC_ALLOCA(bn_t, m + 1), *_x = RLC_ALLOCA(bn_t, m + 1);

	bn_null(q);
	bn_null(t);

	RLC_TRY {
		bn_new(q);
		bn_new(t);
		if (p == NULL) {
			RLC_THROW(ERR_NO_MEMORY);
		}
		for (i = 0; i <= m; i++) {
			bn_null(p[i]);
			bn_new(p[i]);
			bn_null(_x[i]);
			bn_new(_x[i]);
		}

		pc_get_ord(q);
		bn_rand_mod(r, q);
		if (m == 0) {
			g2_mul_gen(d[0], r);
		} else {
			bn_lag(p, x, q, m);
			g2_mul_sim_lot(d[0], s, p, m + 1);
			g2_mul(d[0], d[0], r);
			for (i = 0; i < m; i++) {
				bn_copy(_x[i], x[i]);
			}
			for (i = 0; i < m; i++) {
				bn_copy(t, _x[i]);
				bn_copy(_x[i], _x[m - 1]);
				bn_lag(p, _x, q, m - 1);
				g2_mul_sim_lot(d[i + 1], s, p, m);
				g2_mul(d[i + 1], d[i + 1], r);
				bn_copy(_x[i], t);
			}
		}
	}
	RLC_CATCH_ANY {
		result = RLC_ERR;
	}
	RLC_FINALLY {
		bn_free(q);
		bn_free(t);
		for (i = 0; i <= m; i++) {
			bn_free(p[i]);
			bn_free(_x[i]);
		}
		RLC_FREE(p);
		RLC_FREE(_x);
	}
	return result;
}


int cp_rsa_gen(rsa_t pub, rsa_t prv, size_t bits) {
	bn_t t, r;
	int result = RLC_OK;

	if (pub == NULL || prv == NULL || bits == 0) {
		return RLC_ERR;
	}

	bn_null(t);
	bn_null(r);

	RLC_TRY {
		bn_new(t);
		bn_new(r);

		/* Generate different primes p and q. */
		do {
			bn_gen_prime(prv->crt->p, bits / 2);
			bn_gen_prime(prv->crt->q, bits / 2);
		} while (bn_cmp(prv->crt->p, prv->crt->q) == RLC_EQ);

		/* Swap p and q so that p is smaller. */
		if (bn_cmp(prv->crt->p, prv->crt->q) != RLC_LT) {
			bn_copy(t, prv->crt->p);
			bn_copy(prv->crt->p, prv->crt->q);
			bn_copy(prv->crt->q, t);
		}

		/* n = pq. */
		bn_mul(pub->crt->n, prv->crt->p, prv->crt->q);
		bn_copy(prv->crt->n, pub->crt->n);
		bn_sub_dig(prv->crt->p, prv->crt->p, 1);
		bn_sub_dig(prv->crt->q, prv->crt->q, 1);

		/* phi(n) = (p - 1)(q - 1). */
		bn_mul(t, prv->crt->p, prv->crt->q);

		bn_set_2b(pub->e, 16);
		bn_add_dig(pub->e, pub->e, 1);

#if !defined(CP_CRT)
		/* d = e^(-1) mod phi(n). */
		bn_gcd_ext(r, prv->d, NULL, pub->e, t);
		if (bn_sign(prv->d) == RLC_NEG) {
			bn_add(prv->d, prv->d, t);
		}
		if (bn_cmp_dig(r, 1) == RLC_EQ) {
			/* Restore p and q. */
			bn_add_dig(prv->crt->p, prv->crt->p, 1);
			bn_add_dig(prv->crt->q, prv->crt->q, 1);
			result = RLC_OK;
		}
#else
		/* d = e^(-1) mod phi(n). */
		bn_gcd_ext(r, prv->d, NULL, pub->e, t);
		if (bn_sign(prv->d) == RLC_NEG) {
			bn_add(prv->d, prv->d, t);
		}

		if (bn_cmp_dig(r, 1) == RLC_EQ) {
			/* dP = d mod (p - 1). */
			bn_mod(prv->crt->dp, prv->d, prv->crt->p);
			/* dQ = d mod (q - 1). */
			bn_mod(prv->crt->dq, prv->d, prv->crt->q);
			/* Restore p and q. */
			bn_add_dig(prv->crt->p, prv->crt->p, 1);
			bn_add_dig(prv->crt->q, prv->crt->q, 1);
			/* qInv = q^(-1) mod p. */
			bn_mod_inv(prv->crt->qi, prv->crt->q, prv->crt->p);

			result = RLC_OK;
		}
#endif /* CP_CRT */
	}
	RLC_CATCH_ANY {
		result = RLC_ERR;
	}
	RLC_FINALLY {
		bn_free(t);
		bn_free(r);
	}

	return result;
}


int cp_sokaka_key(uint8_t *key, size_t key_len, const char *id1,
		const sokaka_t k, const char *id2) {
	size_t size, len1 = strlen(id1), len2 = strlen(id2);
	int first = 0, result = RLC_OK;
	uint8_t *buf;
	g1_t p;
	g2_t q;
	gt_t e;

	g1_null(p);
	g2_null(q);
	gt_null(e);

	RLC_TRY {
		g1_new(p);
		g2_new(q);
		gt_new(e);
		size = gt_size_bin(e, 0);
		buf = RLC_ALLOCA(uint8_t, size);
		if (buf == NULL) {
			RLC_THROW(ERR_NO_MEMORY);
		}

		if (len1 == len2) {
			if (strncmp(id1, id2, len1) == 0) {
				RLC_THROW(ERR_NO_VALID);
			}
			first = (strncmp(id1, id2, len1) < 0 ? 1 : 2);
		} else {
			if (len1 < len2) {
				if (strncmp(id1, id2, len1) == 0) {
					first = 1;
				} else {
					first = (strncmp(id1, id2, len1) < 0 ? 1 : 2);
				}
			} else {
				if (strncmp(id1, id2, len2) == 0) {
					first = 2;
				} else {
					first = (strncmp(id1, id2, len2) < 0 ? 1 : 2);
				}
			}
		}

		if (pc_map_is_type1()) {
			g2_map(q, (uint8_t *)id2, len2);
			pc_map(e, k->s1, q);
		} else {
			if (first == 1) {
				g2_map(q, (uint8_t *)id2, len2);
				pc_map(e, k->s1, q);
			} else {
				g1_map(p, (uint8_t *)id2, len2);
				pc_map(e, p, k->s2);
			}
		}

		/* Allocate size for storing the output. */
		gt_write_bin(buf, size, e, 0);
		md_kdf(key, key_len, buf, size);
	}
	RLC_CATCH_ANY {
		result = RLC_ERR;
	}
	RLC_FINALLY {
		g1_free(p);
		g2_free(q);
		gt_free(e);
		RLC_FREE(buf);
	}
	return result;
}


int cp_vbnn_gen_prv(bn_t sk, ec_t pk, const bn_t msk, const uint8_t *id,
		size_t id_len) {
	uint8_t hash[RLC_MD_LEN];
	size_t len;
	uint8_t *buf = NULL;
	bn_t n, r;
	int result = RLC_OK;

	/* zero variables */
	bn_null(n);
	bn_null(r);

	RLC_TRY {
		/* initialize variables */
		bn_new(n);
		bn_new(r);

		/* get order of ECC group */
		ec_curve_get_ord(n);

		/* extract user key from id */
		bn_rand_mod(r, n);

		/* calculate R part of the user key */
		ec_mul_gen(pk, r);

		/* calculate s part of the user key */
		len = id_len + ec_size_bin(pk, 1);
		buf = RLC_ALLOCA(uint8_t, len);
		if (buf == NULL) {
			RLC_THROW(ERR_NO_MEMORY);
		}
		memcpy(buf, id, id_len);
		ec_write_bin(buf + id_len, ec_size_bin(pk, 1), pk, 1);

		md_map(hash, buf, len);
		bn_read_bin(sk, hash, RLC_MD_LEN);
		bn_mod(sk, sk, n);
		bn_mul(sk, sk, msk);
		bn_add(sk, sk, r);
		bn_mod(sk, sk, n);
	}
	RLC_CATCH_ANY {
		result = RLC_ERR;
	}
	RLC_FINALLY {
		/* free variables */
		bn_free(n);
		bn_free(r);
		RLC_FREE(buf);
	}
	return result;
}


int cp_vbnn_sig(ec_t r, bn_t z, bn_t h, const uint8_t *id, size_t id_len,
		const uint8_t *msg, int msg_len, const bn_t sk, const ec_t pk) {
	size_t len;
	uint8_t *buf = NULL, *buf_i, hash[RLC_MD_LEN];
	bn_t n, y;
	ec_t t;
	int result = RLC_OK;

	/* zero variables */
	bn_null(n);
	bn_null(y);
	ec_null(t);

	RLC_TRY {
		bn_new(n);
		bn_new(y);
		ec_new(t);

		/* get order of ECC group */
		ec_curve_get_ord(n);

		bn_rand_mod(y, n);
		ec_mul_gen(t, y);

		/* calculate h part of the signature */
		len = id_len + msg_len + ec_size_bin(t, 1) + ec_size_bin(pk, 1);
		buf = RLC_ALLOCA(uint8_t, len);
		if (buf == NULL) {
			RLC_THROW(ERR_NO_MEMORY);
		}

		buf_i = buf;
		memcpy(buf_i, id, id_len);
		buf_i += id_len;
		memcpy(buf_i, msg, msg_len);
		buf_i += msg_len;
		ec_write_bin(buf_i, ec_size_bin(pk, 1), pk, 1);
		buf_i += ec_size_bin(pk, 1);
		ec_write_bin(buf_i, ec_size_bin(t, 1), t, 1);

		md_map(hash, buf, len);
		bn_read_bin(h, hash, RLC_MD_LEN);
		bn_mod(h, h, n);

		/* calculate z part of the signature */
		bn_mul(z, h, sk);
		bn_add(z, z, y);
		bn_mod(z, z, n);

		/* calculate R part of the signature */
		ec_copy(r, pk);
	}
	RLC_CATCH_ANY {
		result = RLC_ERR;
	}
	RLC_FINALLY {
		/* free variables */
		bn_free(n);
		bn_free(y);
		ec_free(t);
		RLC_FREE(buf);
	}
	return result;
}


int cp_vbnn_ver(const ec_t r, const bn_t z, const bn_t h, const uint8_t *id,
		size_t id_len, const uint8_t *msg, int msg_len, const ec_t mpk) {
	size_t len;
	uint8_t *buf = NULL, *buf_i, hash[RLC_MD_LEN];
	bn_t n, c, _h;
	ec_t Z, t;
	int result = 0;

	/* zero variables */
	bn_null(n);
	bn_null(c);
	bn_null(_h);
	ec_null(Z);
	ec_null(t);

	RLC_TRY {
		bn_new(n);
		bn_new(c);
		bn_new(_h);
		ec_new(Z);
		ec_new(t);

		/* calculate c */
		len = id_len + msg_len + 2 * ec_size_bin(r, 1);
		buf = RLC_ALLOCA(uint8_t, len);
		if (buf == NULL) {
			RLC_THROW(ERR_NO_MEMORY);
		}

		/* get order of ECC group */
		ec_curve_get_ord(n);

		buf_i = buf;
		memcpy(buf_i, id, id_len);
		buf_i += id_len;
		ec_write_bin(buf_i, ec_size_bin(r, 1), r, 1);

		len = id_len + ec_size_bin(r, 1);
		md_map(hash, buf, len);
		bn_read_bin(c, hash, RLC_MD_LEN);
		bn_mod(c, c, n);

		/* calculate Z */
		ec_mul_gen(Z, z);
		ec_mul(t, mpk, c);
		ec_add(t, t, r);
		ec_norm(t, t);
		ec_mul(t, t, h);
		ec_sub(Z, Z, t);
		ec_norm(Z, Z);

		/* calculate h_verify */
		buf_i = buf;
		memcpy(buf_i, id, id_len);
		buf_i += id_len;
		memcpy(buf_i, msg, msg_len);
		buf_i += msg_len;
		ec_write_bin(buf_i, ec_size_bin(r, 1), r, 1);
		buf_i += ec_size_bin(r, 1);
		ec_write_bin(buf_i, ec_size_bin(Z, 1), Z, 1);

		len = id_len + msg_len + ec_size_bin(r, 1) + ec_size_bin(Z, 1);
		md_map(hash, buf, len);
		bn_read_bin(_h, hash, RLC_MD_LEN);
		bn_mod(_h, _h, n);
		RLC_FREE(buf);

		if (bn_cmp(h, _h) == RLC_EQ) {
			result = 1;
		} else {
			result = 0;
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		/* free variables */
		bn_free(n);
		bn_free(c);
		bn_free(_h);
		ec_free(Z);
		ec_free(t);
		RLC_FREE(buf);
	}
	return result;
}


void eb_map(eb_t p, const uint8_t *msg, size_t len) {
	bn_t k;
	fb_t t0, t1;
	int i;
	uint8_t digest[RLC_MD_LEN];

	bn_null(k);
	fb_null(t0);
	fb_null(t1);

	RLC_TRY {
		bn_new(k);
		fb_new(t0);
		fb_new(t1);

		md_map(digest, msg, len);
		bn_read_bin(k, digest, RLC_MIN(RLC_FB_BYTES, RLC_MD_LEN));
		fb_set_dig(p->z, 1);

		i = 0;
		while (1) {
			bn_add_dig(k, k, 1);
			bn_mod_2b(k, k, RLC_FB_BITS);
			dv_copy(p->x, k->dp, RLC_FB_DIGS);

			eb_rhs(t1, p);

			/* t0 = 1/x1^2. */
			fb_sqr(t0, p->x);
			fb_inv(t0, t0);
			/* t0 = t1/x1^2. */
			fb_mul(t0, t0, t1);
			/* Solve t1^2 + t1 = t0. */
			if (fb_trc(t0) != 0) {
				i++;
			} else {
				fb_slv(t1, t0);
				/* x3 = x1, y3 = t1 * x1, z3 = 1. */
				fb_mul(p->y, t1, p->x);
				fb_set_dig(p->z, 1);

				p->coord = BASIC;
				break;
			}
		}
		/* Now, multiply by cofactor to get the correct group. */
		eb_curve_get_cof(k);
		if (bn_bits(k) < RLC_DIG) {
			eb_mul_dig(p, p, k->dp[0]);
		} else {
			eb_mul(p, p, k);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(k);
		fb_free(t0);
		fb_free(t1);
	}
}


static void eb_mul_lnaf_imp(eb_t r, const eb_t p, const bn_t k) {
	int i, n;
	int8_t naf[RLC_FB_BITS + 1];
	eb_t t[1 << (EB_WIDTH - 2)];
	size_t l;

	RLC_TRY {
		/* Prepare the precomputation table. */
		for (i = 0; i < (1 << (EB_WIDTH - 2)); i++) {
			eb_null(t[i]);
			eb_new(t[i]);
			eb_set_infty(t[i]);
			fb_set_dig(t[i]->z, 1);
			t[i]->coord = BASIC;
		}

		/* Compute the precomputation table. */
		eb_tab(t, p, EB_WIDTH);

		/* Compute the w-NAF representation of k. */
		l = sizeof(naf);
		bn_rec_naf(naf, &l, k, EB_WIDTH);

		n = naf[l - 1];
		if (n > 0) {
			eb_copy(r, t[n / 2]);
		}

		for (i = l - 2; i >= 0; i--) {
			eb_dbl(r, r);

			n = naf[i];
			if (n > 0) {
				eb_add(r, r, t[n / 2]);
			}
			if (n < 0) {
				eb_sub(r, r, t[-n / 2]);
			}
		}
		/* Convert r to affine coordinates. */
		eb_norm(r, r);
		if (bn_sign(k) == RLC_NEG) {
			eb_neg(r, r);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		/* Free the precomputation table. */
		for (i = 0; i < (1 << (EB_WIDTH - 2)); i++) {
			eb_free(t[i]);
		}
	}
}


static void eb_mul_ltnaf_imp(eb_t r, const eb_t p, const bn_t k) {
	int i, n;
	int8_t tnaf[RLC_FB_BITS + 8], u;
	eb_t t[1 << (EB_WIDTH - 2)];
	size_t l;

	if (eb_curve_opt_a() == RLC_ZERO) {
		u = -1;
	} else {
		u = 1;
	}

	RLC_TRY {
		/* Prepare the precomputation table. */
		for (i = 0; i < (1 << (EB_WIDTH - 2)); i++) {
			eb_null(t[i]);
			eb_new(t[i]);
		}
		/* Compute the precomputation table. */
		eb_tab(t, p, EB_WIDTH);

		/* Compute the w-TNAF representation of k. */
		l = sizeof(tnaf);
		bn_rec_tnaf(tnaf, &l, k, u, RLC_FB_BITS, EB_WIDTH);

		n = tnaf[l - 1];
		if (n > 0) {
			eb_copy(r, t[n / 2]);
		} else {
			eb_neg(r, t[-n / 2]);
		}

		for (i = l - 2; i >= 0; i--) {
			eb_frb(r, r);

			n = tnaf[i];
			if (n > 0) {
				eb_add(r, r, t[n / 2]);
			}
			if (n < 0) {
				eb_sub(r, r, t[-n / 2]);
			}
		}
		/* Convert r to affine coordinates. */
		eb_norm(r, r);
		if (bn_sign(k) == RLC_NEG) {
			eb_neg(r, r);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		/* Free the precomputation table. */
		for (i = 0; i < (1 << (EB_WIDTH - 2)); i++) {
			eb_free(t[i]);
		}
	}
}


void eb_mul_sim_joint(eb_t r, const eb_t p, const bn_t k, const eb_t q,
		const bn_t m) {
	eb_t t[5];
	int i, u_i, offset;
	int8_t jsf[2 * (RLC_FB_BITS + 1)];
	size_t len;

	if (bn_is_zero(k) || eb_is_infty(p)) {
		eb_mul(r, q, m);
		return;
	}
	if (bn_is_zero(m) || eb_is_infty(q)) {
		eb_mul(r, p, k);
		return;
	}

	RLC_TRY {
		for (i =  0; i < 5; i++) {
			eb_null(t[i]);
			eb_new(t[i]);
		}

		eb_set_infty(t[0]);
		eb_copy(t[1], q);
		if (bn_sign(m) == RLC_NEG) {
			eb_neg(t[1], t[1]);
		}
		eb_copy(t[2], p);
		if (bn_sign(k) == RLC_NEG) {
			eb_neg(t[2], t[2]);
		}
		eb_add(t[3], t[2], t[1]);
		eb_sub(t[4], t[2], t[1]);
#if defined(EB_MIXED)
		eb_norm_sim(t + 3, (const eb_t*)(t + 3), 2);
#endif

		len = 2 * (RLC_FB_BITS + 1);
		bn_rec_jsf(jsf, &len, k, m);

		eb_set_infty(r);
		offset = RLC_MAX(bn_bits(k), bn_bits(m)) + 1;
		for (i = len - 1; i >= 0; i--) {
			eb_dbl(r, r);
			if (jsf[i] != 0 && jsf[i] == -jsf[i + offset]) {
				u_i = jsf[i] * 2 + jsf[i + offset];
				if (u_i < 0) {
					eb_sub(r, r, t[4]);
				} else {
					eb_add(r, r, t[4]);
				}
			} else {
				u_i = jsf[i] * 2 + jsf[i + offset];
				if (u_i < 0) {
					eb_sub(r, r, t[-u_i]);
				} else {
					eb_add(r, r, t[u_i]);
				}
			}
		}
		eb_norm(r, r);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		for (i =  0; i < 5; i++) {
			eb_free(t[i]);
		}
	}
}


void eb_mul_sim_trick(eb_t r, const eb_t p, const bn_t k, const eb_t q,
		const bn_t m) {
	eb_t t0[1 << (EB_WIDTH / 2)], t1[1 << (EB_WIDTH / 2)], t[1 << EB_WIDTH];
	size_t l0, l1, w = EB_WIDTH / 2;
	uint8_t w0[RLC_FB_BITS], w1[RLC_FB_BITS];
	bn_t n;

	bn_null(n);

	if (bn_is_zero(k) || eb_is_infty(p)) {
		eb_mul(r, q, m);
		return;
	}
	if (bn_is_zero(m) || eb_is_infty(q)) {
		eb_mul(r, p, k);
		return;
	}

	RLC_TRY {
		bn_new(n);

		eb_curve_get_ord(n);

		for (int i = 0; i < (1 << w); i++) {
			eb_null(t0[i]);
			eb_null(t1[i]);
			eb_new(t0[i]);
			eb_new(t1[i]);
		}
		for (int i = 0; i < (1 << EB_WIDTH); i++) {
			eb_null(t[i]);
			eb_new(t[i]);
		}

		eb_set_infty(t0[0]);
		eb_copy(t0[1], p);
		if (bn_sign(k) == RLC_NEG) {
			eb_neg(t0[1], t0[1]);
		}
		for (int i = 2; i < (1 << w); i++) {
			eb_add(t0[i], t0[i - 1], t0[1]);
		}

		eb_set_infty(t1[0]);
		eb_copy(t1[1], q);
		if (bn_sign(m) == RLC_NEG) {
			eb_neg(t1[1], t1[1]);
		}
		for (int i = 2; i < (1 << w); i++) {
			eb_add(t1[i], t1[i - 1], t1[1]);
		}

		for (int i = 0; i < (1 << w); i++) {
			for (int j = 0; j < (1 << w); j++) {
				eb_add(t[(i << w) + j], t0[i], t1[j]);
			}
		}

#if EB_WIDTH > 2 && defined(EB_MIXED)
		eb_norm_sim(t + 1, (const eb_t *)(t + 1), (1 << EB_WIDTH) - 1);
#endif

		l0 = l1 = RLC_CEIL(RLC_FB_BITS + 1, w);
		bn_rec_win(w0, &l0, k, w);
		bn_rec_win(w1, &l1, m, w);
		for (int i = l0; i < l1; i++) {
			w0[i] = 0;
		}
		for (int i = l1; i < l0; i++) {
			w1[i] = 0;
		}

		eb_set_infty(r);
		for (int i = RLC_MAX(l0, l1) - 1; i >= 0; i--) {
			for (int j = 0; j < w; j++) {
				eb_dbl(r, r);
			}
			eb_add(r, r, t[(w0[i] << w) + w1[i]]);
		}
		eb_norm(r, r);
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(n);
		for (int i = 0; i < (1 << w); i++) {
			eb_free(t0[i]);
			eb_free(t1[i]);
		}
		for (int i = 0; i < (1 << EB_WIDTH); i++) {
			eb_free(t[i]);
		}
	}
}


static void eb_mul_sim_plain(eb_t r, const eb_t p, const bn_t k, const eb_t q,
		const bn_t m, const eb_t *t) {
	int i, n0, n1, w, g;
	int8_t naf0[RLC_FB_BITS + 1], naf1[RLC_FB_BITS + 1], *_k, *_m;
	eb_t t0[1 << (EB_WIDTH - 2)];
	eb_t t1[1 << (EB_WIDTH - 2)];
	size_t l, l0, l1;

	for (i =  0; i < (1 << (EB_WIDTH - 2)); i++) {
		eb_null(t0[i]);
		eb_null(t1[i]);
	}

	RLC_TRY {
		g = (t == NULL ? 0 : 1);
		if (!g) {
			for (i =  0; i < (1 << (EB_WIDTH - 2)); i++) {
				eb_new(t0[i]);
			}
			eb_tab(t0, p, EB_WIDTH);
			t = (const eb_t *)t0;
		}

		/* Prepare the precomputation table. */
		for (i =  0; i < (1 << (EB_WIDTH - 2)); i++) {
			eb_new(t1[i]);
		}
		/* Compute the precomputation table. */
		eb_tab(t1, q, EB_WIDTH);

		/* Compute the w-NAF representation of k. */
		if (g) {
			w = EB_DEPTH;
		} else {
			w = EB_WIDTH;
		}

		l0 = l1 = RLC_FB_BITS + 1;
		bn_rec_naf(naf0, &l0, k, w);
		bn_rec_naf(naf1, &l1, m, EB_WIDTH);

		l = RLC_MAX(l0, l1);
		if (bn_sign(k) == RLC_NEG) {
			for (i =  0; i < l0; i++) {
				naf0[i] = -naf0[i];
			}
		}
		if (bn_sign(m) == RLC_NEG) {
			for (i =  0; i < l1; i++) {
				naf1[i] = -naf1[i];
			}
		}

		_k = naf0 + l - 1;
		_m = naf1 + l - 1;
		eb_set_infty(r);
		for (i =  l - 1; i >= 0; i--, _k--, _m--) {
			eb_dbl(r, r);

			n0 = *_k;
			n1 = *_m;
			if (n0 > 0) {
				eb_add(r, r, t[n0 / 2]);
			}
			if (n0 < 0) {
				eb_sub(r, r, t[-n0 / 2]);
			}
			if (n1 > 0) {
				eb_add(r, r, t1[n1 / 2]);
			}
			if (n1 < 0) {
				eb_sub(r, r, t1[-n1 / 2]);
			}
		}
		/* Convert r to affine coordinates. */
		eb_norm(r, r);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		/* Free the precomputation tables. */
		if (!g) {
			for (i =  0; i < 1 << (EB_WIDTH - 2); i++) {
				eb_free(t0[i]);
			}
		}
		for (i =  0; i < 1 << (EB_WIDTH - 2); i++) {
			eb_free(t1[i]);
		}
	}
}


void eb_read_bin(eb_t a, const uint8_t *bin, size_t len) {
	if (len == 1) {
		if (bin[0] == 0) {
			eb_set_infty(a);
			return;
		} else {
			RLC_THROW(ERR_NO_BUFFER);
			return;
		}
	}

	if (len != (RLC_FB_BYTES + 1) && len != (2 * RLC_FB_BYTES + 1)) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}

	a->coord = BASIC;
	fb_set_dig(a->z, 1);
	fb_read_bin(a->x, bin + 1, RLC_FB_BYTES);
	if (len == RLC_FB_BYTES + 1) {
		switch(bin[0]) {
			case 2:
				fb_zero(a->y);
				break;
			case 3:
				fb_zero(a->y);
				fb_set_bit(a->y, 0, 1);
				break;
			default:
				RLC_THROW(ERR_NO_VALID);
				break;
		}
		eb_upk(a, a);
	}

	if (len == 2 * RLC_FB_BYTES + 1) {
		if (bin[0] == 4) {
			fb_read_bin(a->y, bin + RLC_FB_BYTES + 1, RLC_FB_BYTES);
		} else {
			RLC_THROW(ERR_NO_VALID);
			return;
		}
	}

	if (!eb_on_curve(a)) {
		RLC_THROW(ERR_NO_VALID);
		return;
	}
}


void eb_write_bin(uint8_t *bin, size_t len, const eb_t a, int pack) {
	eb_t t;

	eb_null(t);

	memset(bin, 0, len);

	if (eb_is_infty(a)) {
		if (len < 1) {
			RLC_THROW(ERR_NO_BUFFER);
			return;
		} else {
			return;
		}
	}

	RLC_TRY {
		eb_new(t);

		eb_norm(t, a);

		if (pack) {
			if (len < RLC_FB_BYTES + 1) {
				RLC_THROW(ERR_NO_BUFFER);
			} else {
				eb_pck(t, t);
				bin[0] = 2 | fb_get_bit(t->y, 0);
				fb_write_bin(bin + 1, RLC_FB_BYTES, t->x);
			}
		} else {
			if (len < 2 * RLC_FB_BYTES + 1) {
				RLC_THROW(ERR_NO_BUFFER);
			} else {
				bin[0] = 4;
				fb_write_bin(bin + 1, RLC_FB_BYTES, t->x);
				fb_write_bin(bin + RLC_FB_BYTES + 1, RLC_FB_BYTES, t->y);
			}
		}
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		eb_free(t);
	}
}


void ed_map(ed_t p, const uint8_t *msg, size_t len) {
	ed_map_dst(p, msg, len, (const uint8_t *)"RELIC", 5);
}


void ed_map_dst(ed_t p, const uint8_t *msg, size_t len, const uint8_t *dst,
		size_t dst_len) {
	bn_t k;
	fp_t t;
	ed_t q;
	/* enough space for two field elements plus extra bytes for uniformity */
	const int len_per_elm = (FP_PRIME + ed_param_level() + 7) / 8;
	uint8_t *pseudo_random_bytes = RLC_ALLOCA(uint8_t, 2 * len_per_elm);

	bn_null(k);
	fp_null(t);
	ed_null(q);

	RLC_TRY {
		bn_new(k);
		fp_new(t);
		ed_new(q);

		/* pseudorandom string */
		md_xmd(pseudo_random_bytes, 2 * len_per_elm, msg, len, dst, dst_len);

#define ED_MAP_CONVERT_BYTES(IDX)                                                        \
	do {                                                                                \
		bn_read_bin(k, pseudo_random_bytes + IDX * len_per_elm, len_per_elm);            \
		fp_prime_conv(t, k);                                                             \
	} while (0)

		/* first map invocation */
		ED_MAP_CONVERT_BYTES(0);
		ed_map_ell2_5mod8(p, t);

		/* second map invocation */
		ED_MAP_CONVERT_BYTES(1);
		ed_map_ell2_5mod8(q, t);

#undef ED_MAP_CONVERT_BYTES

		ed_add(p, p, q);

		/* clear cofactor */
		switch (ed_param_get()) {
			case CURVE_ED25519:
				ed_dbl(p, p);
				ed_dbl(p, p);
				ed_dbl(p, p);
				break;
			default:
				RLC_THROW(ERR_NO_VALID);
				break;
		}

		ed_norm(p, p);
#if ED_ADD == EXTND
		fp_mul(p->t, p->x, p->y);
#endif
		p->coord = BASIC;
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(k);
		fp_free(t);
		ed_free(q);
		RLC_FREE(pseudo_random_bytes);
	}
}


static void ed_mul_reg_imp(ed_t r, const ed_t p, const bn_t k) {
	bn_t _k;
	int i, j, n;
	int8_t s, reg[RLC_CEIL(RLC_FP_BITS + 1, ED_WIDTH - 1)];
	ed_t t[1 << (ED_WIDTH - 2)], u, v;
	size_t l;

	bn_null(_k);
	if (bn_is_zero(k)) {
		ed_set_infty(r);
		return;
	}

	RLC_TRY {
		bn_new(_k);
		ed_new(u);
		ed_new(v);
		/* Prepare the precomputation table. */
		for (i = 0; i < (1 << (ED_WIDTH - 2)); i++) {
			ed_null(t[i]);
			ed_new(t[i]);
		}
		/* Compute the precomputation table. */
		ed_tab(t, p, ED_WIDTH);

		/* Make a copy of the scalar for processing. */
		bn_abs(_k, k);
		_k->dp[0] |= bn_is_even(_k);

		/* Compute the w-NAF representation of k. */
		l = RLC_CEIL(RLC_FP_BITS + 1, ED_WIDTH - 1);
		bn_rec_reg(reg, &l, _k, RLC_FP_BITS, ED_WIDTH);

		ed_set_infty(r);
		for (i = l - 1; i >= 0; i--) {
			for (j = 0; j < ED_WIDTH - 1; j++) {
#if ED_ADD == EXTND
				r->coord = EXTND;
#endif
				ed_dbl(r, r);
			}

			n = reg[i];
			s = (n >> 7);
			n = ((n ^ s) - s) >> 1;

			for (j = 0; j < (1 << (EP_WIDTH - 2)); j++) {
				dv_copy_cond(u->x, t[j]->x, RLC_FP_DIGS, j == n);
				dv_copy_cond(u->y, t[j]->y, RLC_FP_DIGS, j == n);
				dv_copy_cond(u->z, t[j]->z, RLC_FP_DIGS, j == n);
			}
			ed_neg(v, u);
			dv_copy_cond(u->x, v->x, RLC_FP_DIGS, s != 0);
			ed_add(r, r, u);
		}

		/* t[0] has an unmodified copy of p. */
		ed_sub(u, r, t[0]);
		dv_copy_cond(r->x, u->x, RLC_FP_DIGS, bn_is_even(k));
		dv_copy_cond(r->y, u->y, RLC_FP_DIGS, bn_is_even(k));
		dv_copy_cond(r->z, u->z, RLC_FP_DIGS, bn_is_even(k));
		/* Convert r to affine coordinates. */
		ed_norm(r, r);
		ed_neg(u, r);
		dv_copy_cond(r->x, u->x, RLC_FP_DIGS, bn_sign(k) == RLC_NEG);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		/* Free the precomputation table. */
		for (i = 0; i < (1 << (ED_WIDTH - 2)); i++) {
			ed_free(t[i]);
		}
		bn_free(_k);
	}
}


void ed_mul_slide(ed_t r, const ed_t p, const bn_t k) {
	ed_t t[1 << (EP_WIDTH - 1)], q;
	uint8_t win[RLC_FP_BITS + 1];
	size_t l;

	ed_null(q);

	if (bn_is_zero(k) || ed_is_infty(p)) {
		ed_set_infty(r);
		return;
	}

	RLC_TRY {
		for (size_t i = 0; i < (1 << (EP_WIDTH - 1)); i ++) {
			ed_null(t[i]);
			ed_new(t[i]);
		}

		ed_new(q);

		ed_copy(t[0], p);
		ed_dbl(q, p);

#if defined(EP_MIXED)
		ed_norm(q, q);
#endif

		/* Create table. */
		for (size_t i = 1; i < (1 << (EP_WIDTH - 1)); i++) {
			ed_add(t[i], t[i - 1], q);
		}

#if defined(EP_MIXED)
		ed_norm_sim(t + 1, (const ed_t *)t + 1, (1 << (EP_WIDTH - 1)) - 1);
#endif

		ed_set_infty(q);
		l = RLC_FP_BITS + 1;
		bn_rec_slw(win, &l, k, EP_WIDTH);
		for (size_t i = 0; i < l; i++) {
			if (win[i] == 0) {
				ed_dbl(q, q);
			} else {
				for (size_t j = 0; j < util_bits_dig(win[i]); j++) {
					ed_dbl(q, q);
				}
				ed_add(q, q, t[win[i] >> 1]);
			}
		}

		ed_norm(r, q);
		if (bn_sign(k) == RLC_NEG) {
			ed_neg(r, r);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		for (size_t i = 0; i < (1 << (EP_WIDTH - 1)); i++) {
			ed_free(t[i]);
		}
		ed_free(q);
	}
}


void ed_mul_dig(ed_t r, const ed_t p, dig_t k) {
	ed_t t;
	bn_t _k;
	int8_t u, naf[RLC_DIG + 1];
	size_t l;

	ed_null(t);
	bn_null(_k);

	if (k == 0 || ed_is_infty(p)) {
		ed_set_infty(r);
		return;
	}

	RLC_TRY {
		ed_new(t);
		bn_new(_k);

		bn_set_dig(_k, k);

		l = RLC_DIG + 1;
		bn_rec_naf(naf, &l, _k, 2);

		ed_set_infty(t);
		for (int i = l - 1; i >= 0; i--) {
			ed_dbl(t, t);

			u = naf[i];
			if (u > 0) {
				ed_add(t, t, p);
			} else if (u < 0) {
				ed_sub(t, t, p);
			}
		}

		ed_norm(r, t);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		ed_free(t);
		bn_free(_k);
	}
}


static void ed_mul_naf_imp(ed_t r, const ed_t p, const bn_t k) {
	int i, n;
	int8_t naf[RLC_FP_BITS + 1];
	ed_t t[1 << (ED_WIDTH - 2)];
	size_t l;

	if (bn_is_zero(k)) {
		ed_set_infty(r);
		return;
	}

	RLC_TRY {
		/* Prepare the precomputation table. */
		for (i = 0; i < (1 << (ED_WIDTH - 2)); i++) {
			ed_null(t[i]);
			ed_new(t[i]);
		}
		/* Compute the precomputation table. */
		ed_tab(t, p, ED_WIDTH);

		/* Compute the w-NAF representation of k. */
		l = sizeof(naf);
		bn_rec_naf(naf, &l, k, EP_WIDTH);

		ed_set_infty(r);
		for (i = l - 1; i > 0; i--) {
			n = naf[i];
			if (n == 0) {
				/* This point will be doubled in the next iteration. */
#if ED_ADD == EXTND
				r->coord = EXTND;
#endif
			}
			ed_dbl(r, r);
			if (n > 0) {
				ed_add(r, r, t[n / 2]);
			} else if (n < 0) {
				ed_sub(r, r, t[-n / 2]);
			}
		}

		/* Last iteration. */
		n = naf[0];
		ed_dbl(r, r);
		if (n > 0) {
			ed_add(r, r, t[n / 2]);
		} else if (n < 0) {
			ed_sub(r, r, t[-n / 2]);
		}

		/* Convert r to affine coordinates. */
		ed_norm(r, r);
		if (bn_sign(k) == RLC_NEG) {
			ed_neg(r, r);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		/* Free the precomputation table. */
		for (i = 0; i < (1 << (ED_WIDTH - 2)); i++) {
			ed_free(t[i]);
		}
	}
}


static void ed_mul_fix_plain(ed_t r, const ed_t * t, const bn_t k) {
	int i, n;
	int8_t naf[RLC_FP_BITS + 1], *_k;
	size_t l;

	/* Compute the w-TNAF representation of k. */
	l = RLC_FP_BITS + 1;
	bn_rec_naf(naf, &l, k, ED_DEPTH);

	_k = naf + l - 1;
	ed_set_infty(r);
	for (i = l - 1; i >= 0; i--, _k--) {
		n = *_k;
		if (n == 0) {
			/* doubling is followed by another doubling */
			if (i > 0) {
				r->coord = EXTND;
				ed_dbl(r, r);
			} else {
				/* use full extended coordinate doubling for last step */
				ed_dbl(r, r);
			}
		} else {
			ed_dbl(r, r);
			if (n > 0) {
				ed_add(r, r, t[n / 2]);
			} else if (n < 0) {
				ed_sub(r, r, t[-n / 2]);
			}
		}
	}
	/* Convert r to affine coordinates. */
	ed_norm(r, r);
	if (bn_sign(k) == RLC_NEG) {
		ed_neg(r, r);
	}
}


void ed_mul_sim_lot(ed_t r, const ed_t p[], const bn_t k[], int n) {
	int i, j;
	int8_t *naf = NULL;
	ed_t *_p = RLC_ALLOCA(ed_t, n);
	size_t l, *_l = RLC_ALLOCA(size_t, n);

	RLC_TRY {
		l = 0;
		for (i = 0; i < n; i++) {
			l = RLC_MAX(l, bn_bits(k[i]) + 1);
		}
		naf = RLC_ALLOCA(int8_t, n * l);
		if (naf == NULL || _p == NULL || _l == NULL) {
			RLC_THROW(ERR_NO_MEMORY);
		}

		for (i = 0; i < n; i++) {
			ed_null(_p[i]);
			ed_new(_p[i]);
		}

		for (i = 0; i < n; i++) {
			_l[i] = l;
			ed_norm(_p[i], p[i]);
			bn_rec_naf(&naf[i*l], &_l[i], k[i], 2);
			if (bn_sign(k[i]) == RLC_NEG) {
				ed_neg(_p[i], _p[i]);
			}
		}

		ed_set_infty(r);
		for (i = l - 1; i >= 0; i--) {
			ed_dbl(r, r);
			for (j = 0; j < n; j++) {
				if (naf[j*l + i] > 0) {
					ed_add(r, r, _p[j]);
				}
				if (naf[j*l + i] < 0) {
					ed_sub(r, r, _p[j]);
				}
			}
		}

		/* Convert r to affine coordinates. */
		ed_norm(r, r);
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	} RLC_FINALLY {
		for (i = 0; i < n; i++) {
			ed_free(_p[i]);
		}
		RLC_FREE(_l);
		RLC_FREE(_p);
		RLC_FREE(naf);
	}
}


void ed_mul_sim_joint(ed_t r, const ed_t p, const bn_t k, const ed_t q,
		const bn_t m) {
	ed_t t[5];
	int i, u_i, offset;
	int8_t jsf[2 * (RLC_FP_BITS + 1)];
	size_t l;

	if (bn_is_zero(k) || ed_is_infty(p)) {
		ed_mul(r, q, m);
		return;
	}
	if (bn_is_zero(m) || ed_is_infty(q)) {
		ed_mul(r, p, k);
		return;
	}

	RLC_TRY {
		for (i = 0; i < 5; i++) {
			ed_null(t[i]);
			ed_new(t[i]);
		}

		ed_set_infty(t[0]);
		ed_copy(t[1], q);
		if (bn_sign(m) == RLC_NEG) {
			ed_neg(t[1], t[1]);
		}
		ed_copy(t[2], p);
		if (bn_sign(k) == RLC_NEG) {
			ed_neg(t[2], t[2]);
		}
		ed_add(t[3], t[2], t[1]);
		ed_sub(t[4], t[2], t[1]);
#if defined(ED_MIXED)
		ed_norm_sim(t + 3, (const ed_t *)t + 3, 2);
#endif

		l = 2 * (RLC_FP_BITS + 1);
		bn_rec_jsf(jsf, &l, k, m);

		ed_set_infty(r);

		offset = RLC_MAX(bn_bits(k), bn_bits(m)) + 1;
		for (i = l - 1; i >= 0; i--) {
			ed_dbl(r, r);
			if (jsf[i] != 0 && jsf[i] == -jsf[i + offset]) {
				u_i = jsf[i] * 2 + jsf[i + offset];
				if (u_i < 0) {
					ed_sub(r, r, t[4]);
				} else {
					ed_add(r, r, t[4]);
				}
			} else {
				u_i = jsf[i] * 2 + jsf[i + offset];
				if (u_i < 0) {
					ed_sub(r, r, t[-u_i]);
				} else {
					ed_add(r, r, t[u_i]);
				}
			}
		}
		ed_norm(r, r);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		for (i = 0; i < 5; i++) {
			ed_free(t[i]);
		}
	}
}


static void ed_mul_sim_plain(ed_t r, const ed_t p, const bn_t k, const ed_t q,
		const bn_t m, const ed_t *t) {
	int i, n0, n1, w, gen;
	int8_t naf0[RLC_FP_BITS + 1], naf1[RLC_FP_BITS + 1], *_k, *_m;
	ed_t t0[1 << (ED_WIDTH - 2)];
	ed_t t1[1 << (ED_WIDTH - 2)];
	size_t l, l0, l1;

	RLC_TRY {
		gen = (t == NULL ? 0 : 1);
		if (!gen) {
			for (i = 0; i < (1 << (ED_WIDTH - 2)); i++) {
				ed_null(t0[i]);
				ed_new(t0[i]);
			}
			ed_tab(t0, p, ED_WIDTH);
			t = (const ed_t *)t0;
		}

		/* Prepare the precomputation table. */
		for (i = 0; i < (1 << (ED_WIDTH - 2)); i++) {
			ed_null(t1[i]);
			ed_new(t1[i]);
		}
		/* Compute the precomputation table. */
		ed_tab(t1, q, ED_WIDTH);

		/* Compute the w-TNAF representation of k. */
		if (gen) {
			w = ED_DEPTH;
		} else {
			w = ED_WIDTH;
		}
		l0 = l1 = RLC_FP_BITS + 1;
		bn_rec_naf(naf0, &l0, k, w);
		bn_rec_naf(naf1, &l1, m, ED_WIDTH);

		l = RLC_MAX(l0, l1);
		if (bn_sign(k) == RLC_NEG) {
			for (i =  0; i < l0; i++) {
				naf0[i] = -naf0[i];
			}
		}
		if (bn_sign(m) == RLC_NEG) {
			for (i =  0; i < l1; i++) {
				naf1[i] = -naf1[i];
			}
		}

		_k = naf0 + l - 1;
		_m = naf1 + l - 1;
		ed_set_infty(r);
		for (i = l - 1; i >= 0; i--, _k--, _m--) {
			ed_dbl(r, r);

			n0 = *_k;
			n1 = *_m;
			if (n0 > 0) {
				ed_add(r, r, t[n0 / 2]);
			}
			if (n0 < 0) {
				ed_sub(r, r, t[-n0 / 2]);
			}
			if (n1 > 0) {
				ed_add(r, r, t1[n1 / 2]);
			}
			if (n1 < 0) {
				ed_sub(r, r, t1[-n1 / 2]);
			}
		}
		/* Convert r to affine coordinates. */
		ed_norm(r, r);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		/* Free the precomputation tables. */
		if (!gen) {
			for (i = 0; i < 1 << (ED_WIDTH - 2); i++) {
				ed_free(t0[i]);
			}
		}
		for (i = 0; i < 1 << (ED_WIDTH - 2); i++) {
			ed_free(t1[i]);
		}
	}
}


void ed_mul_sim_trick(ed_t r, const ed_t p, const bn_t k, const ed_t q,
		const bn_t m) {
	ed_t t0[1 << (ED_WIDTH / 2)], t1[1 << (ED_WIDTH / 2)], t[1 << ED_WIDTH];
	bn_t n;
	size_t l0, l1, w = ED_WIDTH / 2;
	uint8_t w0[RLC_FP_BITS + 1], w1[RLC_FP_BITS + 1];

	bn_null(n);

	if (bn_is_zero(k) || ed_is_infty(p)) {
		ed_mul(r, q, m);
		return;
	}
	if (bn_is_zero(m) || ed_is_infty(q)) {
		ed_mul(r, p, k);
		return;
	}

	RLC_TRY {
		bn_new(n);

		ed_curve_get_ord(n);

		for (int i = 0; i < (1 << w); i++) {
			ed_null(t0[i]);
			ed_null(t1[i]);
			ed_new(t0[i]);
			ed_new(t1[i]);
		}
		for (int i = 0; i < (1 << ED_WIDTH); i++) {
			ed_null(t[i]);
			ed_new(t[i]);
		}

		ed_set_infty(t0[0]);
		ed_copy(t0[1], p);
		if (bn_sign(k) == RLC_NEG) {
			ed_neg(t0[1], t0[1]);
		}
		for (int i = 2; i < (1 << w); i++) {
			ed_add(t0[i], t0[i - 1], t0[1]);
		}

		ed_set_infty(t1[0]);
		ed_copy(t1[1], q);
		if (bn_sign(m) == RLC_NEG) {
			ed_neg(t1[1], t1[1]);
		}
		for (int i = 1; i < (1 << w); i++) {
			ed_add(t1[i], t1[i - 1], t1[1]);
		}

		for (int i = 0; i < (1 << w); i++) {
			for (int j = 0; j < (1 << w); j++) {
				ed_add(t[(i << w) + j], t0[i], t1[j]);
			}
		}

#if defined(ED_MIXED)
		ed_norm_sim(t + 1, (const ed_t *)t + 1, (1 << (ED_WIDTH)) - 1);
#endif

		l0 = l1 = RLC_CEIL(RLC_FP_BITS, w);
		bn_rec_win(w0, &l0, k, w);
		bn_rec_win(w1, &l1, m, w);

		for (int i = l0; i < l1; i++) {
			w0[i] = 0;
		}
		for (int i = l1; i < l0; i++) {
			w1[i] = 0;
		}

		ed_set_infty(r);
		for (int i = RLC_MAX(l0, l1) - 1; i >= 0; i--) {
			for (int j = 0; j < w; j++) {
				ed_dbl(r, r);
			}
			ed_add(r, r, t[(w0[i] << w) + w1[i]]);
		}
		ed_norm(r, r);
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(n);
		for (int i = 0; i < (1 << w); i++) {
			ed_free(t0[i]);
			ed_free(t1[i]);
		}
		for (int i = 0; i < (1 << ED_WIDTH); i++) {
			ed_free(t[i]);
		}
	}
}


void ed_write_bin(uint8_t *bin, size_t len, const ed_t a, int pack) {
	ed_t t;

	ed_null(t);

	memset(bin, 0, len);

	if (ed_is_infty(a)) {
		if (len < 1) {
			RLC_THROW(ERR_NO_BUFFER);
			return;
		} else {
			return;
		}
	}

	RLC_TRY {
		ed_new(t);

		ed_norm(t, a);

		if (pack) {
			if (len < RLC_FP_BYTES + 1) {
				RLC_THROW(ERR_NO_BUFFER);
			} else {
				ed_pck(t, t);
				bin[0] = 2 | fp_get_bit(t->x, 0);
				fp_write_bin(bin + 1, RLC_FP_BYTES, t->y);
			}
		} else {
			if (len < 2 * RLC_FP_BYTES + 1) {
				RLC_THROW(ERR_NO_BUFFER);
			} else {
				bin[0] = 4;
				fp_write_bin(bin + 1, RLC_FP_BYTES, t->y);
				fp_write_bin(bin + RLC_FP_BYTES + 1, RLC_FP_BYTES, t->x);
			}
		}
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		ed_free(t);
	}
}


void ed_read_bin(ed_t a, const uint8_t *bin, size_t len) {
	if (len == 1) {
		if (bin[0] == 0) {
			ed_set_infty(a);
			return;
		} else {
			RLC_THROW(ERR_NO_BUFFER);
			return;
		}
	}

	if (len != (RLC_FP_BYTES + 1) && len != (2 * RLC_FP_BYTES + 1)) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}

	a->coord = BASIC;
	fp_set_dig(a->z, 1);
	fp_read_bin(a->y, bin + 1, RLC_FP_BYTES);
	if (len == RLC_FP_BYTES + 1) {
		switch (bin[0]) {
			case 2:
				fp_zero(a->x);
				break;
			case 3:
				fp_zero(a->x);
				fp_set_bit(a->x, 0, 1);
				break;
			default:
				RLC_THROW(ERR_NO_VALID);
				break;
		}
		ed_upk(a, a);
	}

	if (len == 2 * RLC_FP_BYTES + 1) {
		if (bin[0] == 4) {
			fp_read_bin(a->x, bin + RLC_FP_BYTES + 1, RLC_FP_BYTES);
		} else {
			RLC_THROW(ERR_NO_VALID);
			return;
		}
	}
#if ED_ADD == EXTND
	fp_mul(a->t, a->x, a->y);
	fp_mul(a->x, a->x, a->z);
	fp_mul(a->y, a->y, a->z);
	fp_sqr(a->z, a->z);
#endif

	if (!ed_on_curve(a)) {
		RLC_THROW(ERR_NO_VALID);
	}
}


void ep_map_dst(ep_t p, const uint8_t *msg, size_t len, const uint8_t *dst,
		size_t dst_len) {

	/* enough space for two field elements plus extra bytes for uniformity */
	const int len_per_elm = (FP_PRIME + ep_param_level() + 7) / 8;
	uint8_t *pseudo_random_bytes = RLC_ALLOCA(uint8_t, 2 * len_per_elm);

	RLC_TRY {
		/* for hash_to_field, need to hash to a pseudorandom string */
		/* XXX(rsw) the below assumes that we want to use MD_MAP for hashing.
		 *          Consider making the hash function a per-curve option!
		 */
		md_xmd(pseudo_random_bytes, 2 * len_per_elm, msg, len, dst, dst_len);
		ep_map_from_field(p, pseudo_random_bytes, 2 * len_per_elm);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		RLC_FREE(pseudo_random_bytes);
	}
}


void ep_map(ep_t p, const uint8_t *msg, size_t len) {
	ep_map_dst(p, msg, len, (const uint8_t *)"RELIC", 5);
}


static void ep_mul_naf_imp(ep_t r, const ep_t p, const bn_t k) {
	/* Some of the supported prime curves have order > field. */
	int8_t u, naf[RLC_FP_BITS + 2];
	ep_t t[1 << (EP_WIDTH - 2)];
	bn_t _k, n;
	size_t l;

	bn_null(n);
	bn_null(_k);

	RLC_TRY {
		bn_new(n);
		bn_new(_k);
		/* Prepare the precomputation table. */
		for (int i = 0; i < (1 << (EP_WIDTH - 2)); i++) {
			ep_null(t[i]);
			ep_new(t[i]);
		}

		ep_curve_get_ord(n);
		bn_mod(_k, k, n);

		/* Compute the precomputation table. */
		ep_tab(t, p, EP_WIDTH);

		/* Compute the w-NAF representation of k. */
		l = RLC_FP_BITS + 2;
		bn_rec_naf(naf, &l, _k, EP_WIDTH);

		ep_set_infty(r);
		for (int i = l - 1; i >= 0; i--) {
			ep_dbl(r, r);

			u = naf[i];
			if (u > 0) {
				ep_add(r, r, t[u / 2]);
			} else if (u < 0) {
				ep_sub(r, r, t[-u / 2]);
			}
		}
		/* Convert r to affine coordinates. */
		ep_norm(r, r);
		if (bn_sign(_k) == RLC_NEG) {
			ep_neg(r, r);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(n);
		bn_free(_k);
		/* Free the precomputation table. */
		for (int i = 0; i < (1 << (EP_WIDTH - 2)); i++) {
			ep_free(t[i]);
		}
	}
}


void ep_mul_slide(ep_t r, const ep_t p, const bn_t k) {
	bn_t _k, n;
	ep_t t[1 << (EP_WIDTH - 1)], q;
	uint8_t win[RLC_FP_BITS + 1];
	size_t l;

	if (bn_is_zero(k) || ep_is_infty(p)) {
		ep_set_infty(r);
		return;
	}

	ep_null(q);
	bn_null(n);
	bn_null(_k);

	RLC_TRY {
		bn_new(n);
		bn_new(_k);
		for (size_t i = 0; i < (1 << (EP_WIDTH - 1)); i ++) {
			ep_null(t[i]);
			ep_new(t[i]);
		}
		ep_new(q);

		ep_copy(t[0], p);
		ep_dbl(q, p);

#if defined(EP_MIXED)
		ep_norm(q, q);
#endif

		ep_curve_get_ord(n);
		bn_mod(_k, k, n);

		/* Create table. */
		for (size_t i = 1; i < (1 << (EP_WIDTH - 1)); i++) {
			ep_add(t[i], t[i - 1], q);
		}

#if defined(EP_MIXED)
		ep_norm_sim(t + 1, (const ep_t *)t + 1, (1 << (EP_WIDTH - 1)) - 1);
#endif

		ep_set_infty(q);
		l = RLC_FP_BITS + 1;
		bn_rec_slw(win, &l, _k, EP_WIDTH);
		for (size_t i = 0; i < l; i++) {
			if (win[i] == 0) {
				ep_dbl(q, q);
			} else {
				for (size_t j = 0; j < util_bits_dig(win[i]); j++) {
					ep_dbl(q, q);
				}
				ep_add(q, q, t[win[i] >> 1]);
			}
		}

		ep_norm(r, q);
		if (bn_sign(_k) == RLC_NEG) {
			ep_neg(r, r);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(n);
		bn_free(_k);
		for (size_t i = 0; i < (1 << (EP_WIDTH - 1)); i++) {
			ep_free(t[i]);
		}
		ep_free(q);
	}
}


void ep_mul_monty(ep_t r, const ep_t p, const bn_t k) {
	ep_t t[2];
	bn_t n, l, _k;
	size_t bits;

	bn_null(n);
	bn_null(l);
	bn_null(_k);
	ep_null(t[0]);
	ep_null(t[1]);

	if (bn_is_zero(k) || ep_is_infty(p)) {
		ep_set_infty(r);
		return;
	}

	RLC_TRY {
		bn_new(n);
		bn_new(l);
		bn_new(_k);
		ep_new(t[0]);
		ep_new(t[1]);

		ep_curve_get_ord(n);
		bits = bn_bits(n);

		bn_mod(_k, k, n);
		bn_abs(l, _k);
		bn_add(l, l, n);
		bn_add(n, l, n);
		dv_swap_cond(l->dp, n->dp, RLC_MAX(l->used, n->used),
			bn_get_bit(l, bits) == 0);
		l->used = RLC_SEL(l->used, n->used, bn_get_bit(l, bits) == 0);

		ep_norm(t[0], p);
		ep_dbl(t[1], t[0]);

		/* Blind both points independently. */
		ep_blind(t[0], t[0]);
		ep_blind(t[1], t[1]);

		for (int i = bits - 1; i >= 0; i--) {
			int j = bn_get_bit(l, i);
			dv_swap_cond(t[0]->x, t[1]->x, RLC_FP_DIGS, j ^ 1);
			dv_swap_cond(t[0]->y, t[1]->y, RLC_FP_DIGS, j ^ 1);
			dv_swap_cond(t[0]->z, t[1]->z, RLC_FP_DIGS, j ^ 1);
			ep_add(t[0], t[0], t[1]);
			ep_dbl(t[1], t[1]);
			dv_swap_cond(t[0]->x, t[1]->x, RLC_FP_DIGS, j ^ 1);
			dv_swap_cond(t[0]->y, t[1]->y, RLC_FP_DIGS, j ^ 1);
			dv_swap_cond(t[0]->z, t[1]->z, RLC_FP_DIGS, j ^ 1);
		}

		ep_norm(r, t[0]);
		ep_neg(t[0], r);
		dv_copy_cond(r->y, t[0]->y, RLC_FP_DIGS, bn_sign(_k) == RLC_NEG);
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(n);
		bn_free(l);
		bn_free(_k);
		ep_free(t[1]);
		ep_free(t[0]);
	}
}


void ep_mul_dig(ep_t r, const ep_t p, dig_t k) {
	ep_t t;
	bn_t _k;
	int8_t u, naf[RLC_DIG + 1];
	size_t l;

	ep_null(t);
	bn_null(_k);

	if (k == 0 || ep_is_infty(p)) {
		ep_set_infty(r);
		return;
	}

	RLC_TRY {
		ep_new(t);
		bn_new(_k);

		bn_set_dig(_k, k);

		l = RLC_DIG + 1;
		bn_rec_naf(naf, &l, _k, 2);

		ep_set_infty(t);
		for (int i = l - 1; i >= 0; i--) {
			ep_dbl(t, t);

			u = naf[i];
			if (u > 0) {
				ep_add(t, t, p);
			} else if (u < 0) {
				ep_sub(t, t, p);
			}
		}

		ep_norm(r, t);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		ep_free(t);
		bn_free(_k);
	}
}