Compare commits

...

8 Commits

Author SHA1 Message Date
Laan Tungir
2e8ce777d8 v0.0.37 - Add interactive multi-transport selection menu at startup — users can select one or more transports (unix, qrexec bridge, TCP) from a menu instead of memorizing CLI flags 2026-07-11 11:31:21 -04:00
Laan Tungir
8ebdb50789 v0.0.36 - Update README with qrexec bridge docs, update start_nsigner.sh to support qrexec mode and extra args, add setup scripts 2026-07-11 11:09:41 -04:00
Laan Tungir
a39baed82b v0.0.35 - Update README with qrexec bridge docs, update start_nsigner.sh to support qrexec mode and extra args 2026-07-11 11:08:04 -04:00
Laan Tungir
d15eebb80f v0.0.34 - Add qrexec bridge subcommand and --bridge-source-trusted flag for persistent-signer qrexec transport 2026-07-11 09:37:51 -04:00
Laan Tungir
fd622dfd60 server: ignore SIGPIPE so abrupt client disconnects don't kill the signer
The attended signer only handled SIGINT/SIGTERM. A client that disconnects
abruptly (e.g. nostr_core_lib's nsigner client reconnects per request and
closes the previous connection) could cause a write to the closed socket to
raise SIGPIPE, terminating the whole signer process after one request.

Ignore SIGPIPE process-wide so writes to closed sockets return EPIPE instead.
Verified end-to-end with nostr_core_lib note_poster --signer unix:nsigner-test
posting a kind-1 to relay.laantungir.net.
2026-07-08 09:46:42 -04:00
Laan Tungir
c847179ba4 v0.0.33 - Implement in-place mnemonic entry grid with staged prefix selection and fix stage-3 candidate rendering 2026-06-09 10:29:15 -04:00
Laan Tungir
69c46ab2a7 v0.0.32 - Fix KB2040 Python WebUSB handshake and robust frame reassembly for get-public-key 2026-06-08 20:24:02 -04:00
Laan Tungir
6fd09f521a v0.0.31 - Harden LVGL screen transitions with widget pointer resets and null guards 2026-05-28 09:22:42 -04:00
168 changed files with 75159 additions and 619 deletions

3
.gitignore vendored
View File

@@ -6,4 +6,7 @@ build/
resources/
.test_mnemonic
Trash/
node_modules/
package.json
package-lock.json

View File

@@ -1,5 +1,5 @@
CC := gcc
CFLAGS := -Wall -Wextra -std=c99 -Os -ffunction-sections -fdata-sections -Isrc -Iresources/nostr_core_lib -Iresources/nostr_core_lib/nostr_core -Iresources/nostr_core_lib/cjson -Iresources/tui_continuous
CFLAGS := -Wall -Wextra -std=c99 -Os -ffunction-sections -fdata-sections -DNOSTR_ENABLE_NSIGNER_CLIENT=1 -Isrc -Iresources/nostr_core_lib -Iresources/nostr_core_lib/nostr_core -Iresources/nostr_core_lib/cjson -Iresources/tui_continuous
LDFLAGS := -Wl,--gc-sections resources/nostr_core_lib/libnostr_core_x64.a -lz -ldl -lpthread -lm -lssl -lcrypto -lcurl -lsecp256k1
SRC_DIR := src
@@ -42,6 +42,7 @@ TEST_QREXEC_AUTH_TARGET := $(BUILD_DIR)/test_qrexec_auth
TEST_MNEMONIC_INPUT_TARGET := $(BUILD_DIR)/test_mnemonic_input
EXAMPLE_GET_PUBLIC_KEY_TARGET := $(BUILD_DIR)/example_get_public_key_client
EXAMPLE_SIGN_EVENT_TARGET := $(BUILD_DIR)/example_sign_event_client
EXAMPLE_GET_PUBKEY_TCP_TARGET := $(BUILD_DIR)/example_get_pubkey_tcp
.PHONY: all lib dev static static-debug static-arm64 firmware-feather test test-integration test-mnemonic test-mnemonic-input test-role test-selector test-enforcement test-dispatcher test-policy test-socket-name test-auth-envelope test-qrexec-auth examples test-client clean
@@ -108,7 +109,7 @@ test-qrexec-auth: $(TEST_QREXEC_AUTH_TARGET) $(TARGET_DEV)
test-client: examples
examples: $(EXAMPLE_GET_PUBLIC_KEY_TARGET) $(EXAMPLE_SIGN_EVENT_TARGET)
examples: $(EXAMPLE_GET_PUBLIC_KEY_TARGET) $(EXAMPLE_SIGN_EVENT_TARGET) $(EXAMPLE_GET_PUBKEY_TCP_TARGET)
$(TEST_MNEMONIC_TARGET): $(TEST_DIR)/test_mnemonic.c $(SRC_DIR)/mnemonic.c $(SRC_DIR)/secure_mem.c
@mkdir -p $(BUILD_DIR)
@@ -138,9 +139,9 @@ $(TEST_POLICY_TARGET): $(TEST_DIR)/test_policy.c $(SRC_DIR)/policy.c
@mkdir -p $(BUILD_DIR)
$(CC) $(CFLAGS) $(TEST_DIR)/test_policy.c $(SRC_DIR)/policy.c -o $(TEST_POLICY_TARGET) $(LDFLAGS)
$(TEST_INTEGRATION_TARGET): $(TEST_DIR)/test_integration.c $(CLIENT_DIR)/nsigner_client.c $(SRC_DIR)/auth_envelope.c
$(TEST_INTEGRATION_TARGET): $(TEST_DIR)/test_integration.c
@mkdir -p $(BUILD_DIR)
$(CC) $(CFLAGS) -I$(CLIENT_DIR) $(TEST_DIR)/test_integration.c $(CLIENT_DIR)/nsigner_client.c $(SRC_DIR)/auth_envelope.c -o $(TEST_INTEGRATION_TARGET) $(LDFLAGS)
$(CC) $(CFLAGS) $(TEST_DIR)/test_integration.c -o $(TEST_INTEGRATION_TARGET) $(LDFLAGS)
$(TEST_SOCKET_NAME_TARGET): $(TEST_DIR)/test_socket_name.c $(SRC_DIR)/socket_name.c
@mkdir -p $(BUILD_DIR)
@@ -154,13 +155,17 @@ $(TEST_QREXEC_AUTH_TARGET): $(TEST_DIR)/test_qrexec_auth.c $(SRC_DIR)/auth_envel
@mkdir -p $(BUILD_DIR)
$(CC) $(CFLAGS) $(TEST_DIR)/test_qrexec_auth.c $(SRC_DIR)/auth_envelope.c -o $(TEST_QREXEC_AUTH_TARGET) $(LDFLAGS)
$(EXAMPLE_GET_PUBLIC_KEY_TARGET): $(EXAMPLES_DIR)/get_public_key_client.c $(CLIENT_DIR)/nsigner_client.c $(SRC_DIR)/auth_envelope.c
$(EXAMPLE_GET_PUBLIC_KEY_TARGET): $(EXAMPLES_DIR)/get_public_key_client.c
@mkdir -p $(BUILD_DIR)
$(CC) $(CFLAGS) -I$(CLIENT_DIR) $(EXAMPLES_DIR)/get_public_key_client.c $(CLIENT_DIR)/nsigner_client.c $(SRC_DIR)/auth_envelope.c -o $(EXAMPLE_GET_PUBLIC_KEY_TARGET) $(LDFLAGS)
$(CC) $(CFLAGS) $(EXAMPLES_DIR)/get_public_key_client.c -o $(EXAMPLE_GET_PUBLIC_KEY_TARGET) $(LDFLAGS)
$(EXAMPLE_SIGN_EVENT_TARGET): $(EXAMPLES_DIR)/sign_event_client.c $(CLIENT_DIR)/nsigner_client.c $(SRC_DIR)/auth_envelope.c
$(EXAMPLE_SIGN_EVENT_TARGET): $(EXAMPLES_DIR)/sign_event_client.c
@mkdir -p $(BUILD_DIR)
$(CC) $(CFLAGS) -I$(CLIENT_DIR) $(EXAMPLES_DIR)/sign_event_client.c $(CLIENT_DIR)/nsigner_client.c $(SRC_DIR)/auth_envelope.c -o $(EXAMPLE_SIGN_EVENT_TARGET) $(LDFLAGS)
$(CC) $(CFLAGS) $(EXAMPLES_DIR)/sign_event_client.c -o $(EXAMPLE_SIGN_EVENT_TARGET) $(LDFLAGS)
$(EXAMPLE_GET_PUBKEY_TCP_TARGET): $(EXAMPLES_DIR)/get_pubkey_tcp.c
@mkdir -p $(BUILD_DIR)
$(CC) $(CFLAGS) $(EXAMPLES_DIR)/get_pubkey_tcp.c -o $(EXAMPLE_GET_PUBKEY_TCP_TARGET) $(LDFLAGS)
clean:
rm -rf $(BUILD_DIR)

View File

@@ -247,6 +247,8 @@ Discovery:
- `nsigner --listen stdio` runs one framed JSON-RPC request/response over stdin/stdout.
- `nsigner --listen qrexec` is the same stdio framing mode, but caller identity can be derived from `QREXEC_REMOTE_DOMAIN` (displayed as `qubes:<source-vm>`).
- `nsigner --listen tcp:IPv4:PORT` or `tcp:[IPv6]:PORT` enables TCP listening for non-AF_UNIX clients (for example `tcp:127.0.0.1:8080`, `tcp:[::]:8080`, or `tcp:[fd00::1234]:8080`).
- `nsigner bridge --to <socket-name>` is a stateless relay for Qubes qrexec: reads one framed request from stdin, forwards it to a persistent signer's abstract unix socket, and relays the response to stdout. Used as the `qubes.NsignerRpc` service entrypoint. See [§8.3](#83-qubes-os-qube) and [`plans/qrexec_persistent_bridge.md`](plans/qrexec_persistent_bridge.md).
- `--bridge-source-trusted` (unix listener only): marks the socket as a trusted bridge endpoint. Each connection sends a framed `{"qrexec_source":"<vm>"}` preamble before the request, and the caller identity is composed as `qubes:<vm>` — matching the native qrexec identity path. This enables a persistent signer (mnemonic in mlock'd RAM) to receive qrexec-routed requests without spawning a fresh process per call.
### 7.2 ESP32 MCU: TinyUSB composite (CDC + WebUSB)
@@ -283,7 +285,43 @@ MCU target reuses mnemonic/role/selector/enforcement/dispatcher core and swaps t
### 8.3 Qubes OS qube
Qubes deployment runs `n_signer` in a dedicated signer qube as a foreground process under explicit user session control. Transport binding is platform-specific, but lifecycle and memory-only state model are unchanged.
Qubes deployment runs `n_signer` in a dedicated signer qube (e.g. `nostr_signer`) as a foreground process under explicit user session control. The mnemonic lives only in mlock'd RAM in that qube — a compromised agent in a caller qube cannot read it (hypervisor-enforced memory isolation).
Two transport paths are supported:
**FIPS/TCP** — the signer listens on `tcp:[::]:8080` and FIPS carries traffic between qubes as an IPv6 mesh substrate. See [`documents/FIPS_DEPLOYMENT.md`](documents/FIPS_DEPLOYMENT.md).
**Qubes qrexec bridge** (recommended for no-network deployments) — a persistent signer listens on an abstract unix socket, and a stateless `nsigner bridge` relay (the `qubes.NsignerRpc` qrexec service) forwards one request per qrexec invocation. No network, no FIPS — pure intra-host IPC. Caller identity is `qubes:<source-vm>` (from `QREXEC_REMOTE_DOMAIN`), relayed via a trusted preamble. See [`plans/qrexec_persistent_bridge.md`](plans/qrexec_persistent_bridge.md) for the full design.
#### Qrexec bridge setup
**In the signer qube** (`nostr_signer`):
```bash
# Install nsigner and the qrexec service
bash setup_signer_qube.sh # from packaging/qubes/
# Start the persistent signer (mnemonic entered at terminal, in mlock'd RAM)
~/.local/bin/nsigner --listen unix --socket-name nsigner --bridge-source-trusted
```
**In dom0**:
```bash
# Install policy and tag the signer qube
bash setup_dom0.sh nostr_signer # from packaging/qubes/
```
The dom0 policy allows trusted caller qubes without a popup (memory isolation is the real security boundary) and asks for confirmation from any other qube. The signer's own approval prompt at the `nostr_signer` terminal is the operation-level gate.
**From a caller qube**:
```bash
# JavaScript example (uses qrexec-client-vm, no auth envelope needed)
node examples/n_signer_qube_example_qrexec.js nostr_signer
```
Setup scripts and policy are in [`packaging/qubes/`](packaging/qubes/). See also [`documents/QUBES_OS.md`](documents/QUBES_OS.md) and [`documents/qubes_client_examples.md`](documents/qubes_client_examples.md).
## 9. Usage
@@ -319,6 +357,18 @@ TCP transport mode (no TUI; serves requests until terminated):
nsigner --listen tcp:[::]:8080
```
Qrexec bridge mode (stateless relay to a persistent signer's unix socket; used as the `qubes.NsignerRpc` service):
```bash
nsigner bridge --to nsigner
```
Persistent signer for qrexec bridge (unix listener with trusted source-qube preamble):
```bash
nsigner --listen unix --socket-name nsigner --bridge-source-trusted
```
### 9.2 Send a request (client mode)
From another terminal, target the signer by its socket name:
@@ -414,6 +464,11 @@ These items are designed and worth doing, but are not in the current implementat
- [`documents/CLIENT_IMPLEMENTATION.md`](documents/CLIENT_IMPLEMENTATION.md): client integration contract and framing behavior
- [`documents/QUBES_OS.md`](documents/QUBES_OS.md): Qubes OS deployment/integration checklist for dedicated signer qubes
- [`documents/FIPS_DEPLOYMENT.md`](documents/FIPS_DEPLOYMENT.md): Tier-1 FIPS deployment runbook using loopback TCP listener
- [`plans/qrexec_persistent_bridge.md`](plans/qrexec_persistent_bridge.md): design for the qrexec → unix-socket bridge transport (persistent signer, no mnemonic on disk)
- [`packaging/qubes/`](packaging/qubes/): qrexec service script, dom0 policy, and setup scripts for Qubes deployment
- [`examples/n_signer_qube_example_qrexec.js`](examples/n_signer_qube_example_qrexec.js): JavaScript caller example using qrexec (no network, no auth envelope)
- [`examples/n_signer_qube_example_fips.js`](examples/n_signer_qube_example_fips.js): JavaScript caller example using FIPS/TCP with auth envelope
- [`examples/get_pubkey_tcp.c`](examples/get_pubkey_tcp.c): C caller example using TCP transport with auth envelope
- [`plans/nsigner.md`](plans/nsigner.md): implementation plan and sequencing
- [`plans/seed_phrase_uses.md`](plans/seed_phrase_uses.md): seed phrase domain/use catalog and caveats
- [`firmware/feather_s3_tft`](firmware/feather_s3_tft): Feather ESP32-S3 Reverse TFT firmware (TinyUSB composite CDC + WebUSB signer PoC)

View File

@@ -1,47 +1,44 @@
# n_signer C Reference Client
# n_signer C Client — migrated to `nostr_core_lib`
This directory provides a minimal copy/paste-friendly C client for `n_signer`.
The hand-rolled `nsigner_client.{c,h}` that previously lived in this directory
has been **removed**. n_signer now uses the shared, transport-pluggable client
stack that lives in [`nostr_core_lib`](../resources/nostr_core_lib):
## Files
- [`nostr_core/nsigner_transport.{h,c}`](../resources/nostr_core_lib/nostr_core/nsigner_transport.h) —
pluggable transport vtable (unix-abstract / tcp / usb-cdc serial / fds) + discovery.
- [`nostr_core/nsigner_client.{h,c}`](../resources/nostr_core_lib/nostr_core/nsigner_client.h) —
heap-allocated client: 4-byte length-prefixed framing, kind-27235 auth envelope
(self-contained), JSON-RPC verbs, cJSON result parsing, RPC error mapping.
- [`nostr_core/nostr_signer.{h,c}`](../resources/nostr_core_lib/nostr_core/nostr_signer.h) —
high-level `nostr_signer_t` abstraction (local + remote backends, 6 verbs).
- `nsigner_client.h` / `nsigner_client.c`:
- Unix abstract socket connect helper
- length-prefixed frame send/receive
- generic request API
- helpers for `get_public_key` and `sign_event`
- optional TCP auth envelope attachment via `auth_envelope_build_for_request()`
This is the single source of truth for the n_signer wire contract. See
[`nostr_core/NSIGNER_INTEGRATION.md`](../resources/nostr_core_lib/nostr_core/NSIGNER_INTEGRATION.md)
for the full integration contract.
## API at a glance
## What moved where
```c
nsigner_client_t client;
nsigner_client_init(&client);
| Old (`client/`) | New (`nostr_core_lib`) |
|---|---|
| `nsigner_client_t` (stack) | `nsigner_client_t*` (heap) or `nostr_signer_t*` |
| `nsigner_client_init` / `connect_unix` / `close` | `nsigner_transport_open_unix` + `nsigner_client_new` / `nsigner_client_free` |
| `nsigner_client_get_public_key` | `nostr_signer_get_public_key` or `nsigner_client_call(..., "get_public_key", ...)` |
| `nsigner_client_sign_event` | `nostr_signer_sign_event` or `nsigner_client_call(..., "sign_event", ...)` |
| `nsigner_client_set_auth` | `nsigner_client_set_auth` or `nostr_signer_nsigner_set_auth` |
| `nsigner_client_request` / `request_raw` | `nsigner_client_call` (returns parsed cJSON result) |
nsigner_client_connect_unix(&client, "nsigner", 5000);
## Consumers (updated)
char *resp = NULL;
nsigner_client_get_public_key(&client, "1", "", &resp);
free(resp);
- [`examples/get_public_key_client.c`](../examples/get_public_key_client.c) — uses `nsigner_transport_open_unix` + `nsigner_client_new` + `nsigner_client_call`.
- [`examples/sign_event_client.c`](../examples/sign_event_client.c) — same pattern.
- [`tests/test_integration.c`](../tests/test_integration.c) — same pattern; cJSON-based assertions.
nsigner_client_close(&client);
```
The `nsigner ... client '<json>'` subcommand in [`src/main.c`](../src/main.c) is
unaffected — it has its own raw framing pass-through and never used this directory.
## Auth envelope usage
## Why
If transport requires auth envelopes (for TCP mode), set a private key once:
```c
unsigned char privkey[32] = { /* caller key */ };
nsigner_client_set_auth(&client, privkey, "example-client");
```
Subsequent requests automatically include an `auth` object.
## Ownership rules
- Any `out_response_json` returned by the API must be freed by caller using `free()`.
- `nsigner_client_close()` only closes the socket; it does not free the client struct itself.
## License
Source files in this directory use SPDX `0BSD` headers for permissive reuse in external projects.
Per [`plans/nsigner_integration_plan.md`](../resources/nostr_core_lib/plans/nsigner_integration_plan.md)
(Phase 7): retire per-project hand-rolled clients in favor of the shared module
in `nostr_core_lib`, so the wire contract has one implementation and downstream
projects get unix/tcp/serial/fds transports for free.

View File

@@ -1,369 +0,0 @@
/*
* SPDX-License-Identifier: 0BSD
*/
#define _GNU_SOURCE
#include "nsigner_client.h"
#include <arpa/inet.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <time.h>
#include <unistd.h>
#include <cJSON.h>
#include "auth_envelope.h"
#define NSIGNER_CLIENT_MAX_FRAME (65536U)
static void client_set_error(nsigner_client_t *client, const char *fmt, ...) {
va_list ap;
if (client == NULL) {
return;
}
va_start(ap, fmt);
vsnprintf(client->last_error, sizeof(client->last_error), fmt, ap);
va_end(ap);
}
static int write_full(int fd, const void *buf, size_t len) {
const unsigned char *p = (const unsigned char *)buf;
size_t off = 0;
while (off < len) {
ssize_t n = write(fd, p + off, len - off);
if (n < 0) {
if (errno == EINTR) {
continue;
}
return -1;
}
off += (size_t)n;
}
return 0;
}
static int read_full(int fd, void *buf, size_t len) {
unsigned char *p = (unsigned char *)buf;
size_t off = 0;
while (off < len) {
ssize_t n = read(fd, p + off, len - off);
if (n == 0) {
return -1;
}
if (n < 0) {
if (errno == EINTR) {
continue;
}
return -1;
}
off += (size_t)n;
}
return 0;
}
static int send_framed(int fd, const char *payload) {
uint32_t len;
uint32_t be_len;
if (payload == NULL) {
return -1;
}
len = (uint32_t)strlen(payload);
be_len = htonl(len);
if (write_full(fd, &be_len, sizeof(be_len)) != 0) {
return -1;
}
if (write_full(fd, payload, len) != 0) {
return -1;
}
return 0;
}
static int recv_framed(int fd, char **out_payload) {
uint32_t be_len;
uint32_t len;
char *payload;
if (out_payload == NULL) {
return -1;
}
*out_payload = NULL;
if (read_full(fd, &be_len, sizeof(be_len)) != 0) {
return -1;
}
len = ntohl(be_len);
if (len == 0 || len > NSIGNER_CLIENT_MAX_FRAME) {
return -1;
}
payload = (char *)malloc((size_t)len + 1U);
if (payload == NULL) {
return -1;
}
if (read_full(fd, payload, len) != 0) {
free(payload);
return -1;
}
payload[len] = '\0';
*out_payload = payload;
return 0;
}
static int sleep_ms(int ms) {
struct timespec ts;
ts.tv_sec = ms / 1000;
ts.tv_nsec = (long)(ms % 1000) * 1000000L;
return nanosleep(&ts, NULL);
}
void nsigner_client_init(nsigner_client_t *client) {
if (client == NULL) {
return;
}
memset(client, 0, sizeof(*client));
client->fd = -1;
client->timeout_ms = 5000;
}
void nsigner_client_close(nsigner_client_t *client) {
if (client == NULL) {
return;
}
if (client->fd >= 0) {
close(client->fd);
client->fd = -1;
}
}
int nsigner_client_connect_unix(nsigner_client_t *client,
const char *socket_name,
int timeout_ms) {
struct sockaddr_un addr;
socklen_t addr_len;
int elapsed = 0;
if (client == NULL || socket_name == NULL || socket_name[0] == '\0') {
return -1;
}
timeout_ms = (timeout_ms > 0) ? timeout_ms : 5000;
nsigner_client_close(client);
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
addr.sun_path[0] = '\0';
strncpy(&addr.sun_path[1], socket_name, sizeof(addr.sun_path) - 2);
addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
addr_len = (socklen_t)(sizeof(sa_family_t) + 1 + strlen(socket_name));
while (elapsed < timeout_ms) {
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) {
client_set_error(client, "socket failed: %s", strerror(errno));
return -1;
}
if (connect(fd, (struct sockaddr *)&addr, addr_len) == 0) {
client->fd = fd;
client->timeout_ms = timeout_ms;
client_set_error(client, "ok");
return 0;
}
close(fd);
sleep_ms(100);
elapsed += 100;
}
client_set_error(client, "connect timeout: %s", strerror(errno));
return -1;
}
int nsigner_client_set_auth(nsigner_client_t *client,
const unsigned char privkey[32],
const char *label) {
if (client == NULL || privkey == NULL) {
return -1;
}
memcpy(client->auth_privkey, privkey, 32);
client->auth_enabled = 1;
if (label == NULL) {
label = "";
}
strncpy(client->auth_label, label, sizeof(client->auth_label) - 1);
client->auth_label[sizeof(client->auth_label) - 1] = '\0';
return 0;
}
const char *nsigner_client_last_error(const nsigner_client_t *client) {
if (client == NULL) {
return "invalid_client";
}
return client->last_error;
}
int nsigner_client_request_raw(nsigner_client_t *client,
const char *request_json,
char **out_response_json) {
char *response_json = NULL;
if (client == NULL || request_json == NULL || out_response_json == NULL) {
return -1;
}
if (client->fd < 0) {
client_set_error(client, "not connected");
return -1;
}
*out_response_json = NULL;
if (send_framed(client->fd, request_json) != 0) {
client_set_error(client, "send failed");
return -1;
}
if (recv_framed(client->fd, &response_json) != 0) {
client_set_error(client, "receive failed");
return -1;
}
*out_response_json = response_json;
client_set_error(client, "ok");
return 0;
}
int nsigner_client_request(nsigner_client_t *client,
const char *id,
const char *method,
const cJSON *params,
char **out_response_json) {
cJSON *root = NULL;
cJSON *params_copy = NULL;
cJSON *auth = NULL;
char *request_json = NULL;
int rc = -1;
if (client == NULL || id == NULL || method == NULL || out_response_json == NULL) {
return -1;
}
root = cJSON_CreateObject();
if (root == NULL) {
client_set_error(client, "out of memory");
goto cleanup;
}
cJSON_AddStringToObject(root, "id", id);
cJSON_AddStringToObject(root, "method", method);
if (params != NULL) {
params_copy = cJSON_Duplicate((cJSON *)params, 1);
} else {
params_copy = cJSON_CreateArray();
}
if (params_copy == NULL) {
client_set_error(client, "failed to create params");
goto cleanup;
}
cJSON_AddItemToObject(root, "params", params_copy);
if (client->auth_enabled) {
if (auth_envelope_build_for_request(id,
method,
params_copy,
client->auth_privkey,
client->auth_label,
time(NULL),
&auth) != 0) {
client_set_error(client, "failed to build auth envelope");
goto cleanup;
}
cJSON_AddItemToObject(root, "auth", auth);
auth = NULL;
}
request_json = cJSON_PrintUnformatted(root);
if (request_json == NULL) {
client_set_error(client, "failed to serialize request");
goto cleanup;
}
rc = nsigner_client_request_raw(client, request_json, out_response_json);
cleanup:
cJSON_Delete(root);
cJSON_Delete(auth);
free(request_json);
return rc;
}
int nsigner_client_get_public_key(nsigner_client_t *client,
const char *id,
const char *selector,
char **out_response_json) {
cJSON *params = cJSON_CreateArray();
int rc;
if (params == NULL) {
return -1;
}
cJSON_AddItemToArray(params, cJSON_CreateString((selector != NULL) ? selector : ""));
rc = nsigner_client_request(client, id, "get_public_key", params, out_response_json);
cJSON_Delete(params);
return rc;
}
int nsigner_client_sign_event(nsigner_client_t *client,
const char *id,
const char *event_json,
const char *role,
char **out_response_json) {
cJSON *params = cJSON_CreateArray();
cJSON *opts = cJSON_CreateObject();
int rc;
if (params == NULL || opts == NULL || event_json == NULL) {
cJSON_Delete(params);
cJSON_Delete(opts);
return -1;
}
cJSON_AddItemToArray(params, cJSON_CreateString(event_json));
if (role != NULL && role[0] != '\0') {
cJSON_AddStringToObject(opts, "role", role);
}
cJSON_AddItemToArray(params, opts);
rc = nsigner_client_request(client, id, "sign_event", params, out_response_json);
cJSON_Delete(params);
return rc;
}

View File

@@ -1,56 +0,0 @@
/*
* SPDX-License-Identifier: 0BSD
*/
#ifndef NSIGNER_CLIENT_H
#define NSIGNER_CLIENT_H
#include <stddef.h>
#include <stdint.h>
#include <cJSON.h>
typedef struct {
int fd;
int timeout_ms;
unsigned char auth_privkey[32];
int auth_enabled;
char auth_label[64];
char last_error[128];
} nsigner_client_t;
void nsigner_client_init(nsigner_client_t *client);
void nsigner_client_close(nsigner_client_t *client);
int nsigner_client_connect_unix(nsigner_client_t *client,
const char *socket_name,
int timeout_ms);
int nsigner_client_set_auth(nsigner_client_t *client,
const unsigned char privkey[32],
const char *label);
const char *nsigner_client_last_error(const nsigner_client_t *client);
int nsigner_client_request_raw(nsigner_client_t *client,
const char *request_json,
char **out_response_json);
int nsigner_client_request(nsigner_client_t *client,
const char *id,
const char *method,
const cJSON *params,
char **out_response_json);
int nsigner_client_get_public_key(nsigner_client_t *client,
const char *id,
const char *selector,
char **out_response_json);
int nsigner_client_sign_event(nsigner_client_t *client,
const char *id,
const char *event_json,
const char *role,
char **out_response_json);
#endif

View File

@@ -160,6 +160,58 @@ Includes:
- missing policy -> deny path
- missing `QREXEC_REMOTE_DOMAIN` -> fallback identity path
## 5.5 USB keyboard firmware workflows in a PCI-USB-controller qube
When a full USB controller is assigned directly to a qube via PCI passthrough, HID input does **not** flow through `sys-usb` input proxy policy. In this setup, keyboard-class USB blocking is usually caused by `usbguard` inside the owning qube.
### Scope check (dom0)
```bash
qvm-prefs AI klass
```
- `StandaloneVM`: service state changes in the qube root filesystem persist.
- `AppVM`: root filesystem state resets at reboot; use `/rw/config/rc.local` for per-qube persistence.
### Disable usbguard immediately (inside AI qube)
```bash
sudo systemctl disable --now usbguard
sudo systemctl mask usbguard
```
### Persist behavior across reboot
- **StandaloneVM**: the mask persists as-is.
- **AppVM**: persist with `/rw/config/rc.local`:
```bash
sudo tee /rw/config/rc.local >/dev/null <<'EOF'
#!/bin/sh
systemctl stop usbguard 2>/dev/null || true
systemctl mask usbguard 2>/dev/null || true
EOF
sudo chmod +x /rw/config/rc.local
```
### Restart + verify
```bash
# dom0
qvm-shutdown --wait AI && qvm-start AI
# inside AI qube
systemctl is-enabled usbguard
lsusb
ls /dev/ttyACM*
```
Expected result: `usbguard` is `masked`, the keyboard-class device enumerates, and serial/WebUSB workflows remain usable.
### Security trade-off
Disabling `usbguard` removes an important BadUSB defense for that qube. Keep this configuration limited to a dedicated hardware-development qube and avoid using it for general browsing or untrusted USB devices.
---
## 6. Security requirements

195
examples/get_pubkey_tcp.c Normal file
View File

@@ -0,0 +1,195 @@
/*
* get_pubkey_tcp.c — connect to a running n_signer over TCP and call
* get_public_key for nostr_index 0 and 1, printing both hex pubkey and
* bech32 npub for each.
*
* This is a cross-qube test client for Qubes OS: the signer runs in the
* nostr_signer qube listening on tcp:[::]:8080, and this client runs in
* a different qube connecting to the signer's FIPS address.
*
* Usage:
* ./get_pubkey_tcp <host> <port>
* ./get_pubkey_tcp npub1xxx...fips 8080
*
* If no arguments are given, defaults to localhost:8080.
*
* Output: for each index, prints:
* index 0: hex=<64 hex chars> npub=npub1...
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "nostr_common.h"
#include "nsigner_transport.h"
#include "nsigner_client.h"
#include "nip019.h"
#include "../cjson/cJSON.h"
/* Demo caller key (32 bytes). Replace with your stable caller key in real use. */
static const unsigned char DEMO_PRIVKEY[32] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32
};
static int hex_to_bytes(const char *hex, unsigned char *out, size_t out_len) {
size_t len = strlen(hex);
if (len != out_len * 2) {
return -1;
}
for (size_t i = 0; i < out_len; i++) {
unsigned int byte;
if (sscanf(hex + 2 * i, "%2x", &byte) != 1) {
return -1;
}
out[i] = (unsigned char)byte;
}
return 0;
}
static int query_pubkey(const char *host, int port, int nostr_index,
char *out_hex, size_t hex_size, char *out_npub, size_t npub_size) {
(void)npub_size; /* npub buffer size is enforced by nostr_key_to_bech32 output */
nsigner_transport_t *transport = NULL;
nsigner_client_t *client = NULL;
cJSON *params = NULL;
cJSON *opts = NULL;
cJSON *result = NULL;
const char *hex_pubkey = NULL;
unsigned char pubkey_bytes[32];
int rc = -1;
transport = nsigner_transport_open_tcp(host, port, 10000);
if (transport == NULL) {
fprintf(stderr, "connect failed: cannot open TCP transport to %s:%d\n", host, port);
goto cleanup;
}
client = nsigner_client_new(transport);
if (client == NULL) {
fprintf(stderr, "connect failed: cannot create nsigner client\n");
transport->close(transport);
goto cleanup;
}
transport = NULL; /* owned by client now */
/* Set auth envelope — required for TCP listeners */
if (nsigner_client_set_auth(client, DEMO_PRIVKEY, "get_pubkey_tcp") != NOSTR_SUCCESS) {
fprintf(stderr, "failed to set auth envelope\n");
goto cleanup;
}
/* params: [{"nostr_index": N}] */
params = cJSON_CreateArray();
if (params == NULL) {
fprintf(stderr, "out of memory\n");
goto cleanup;
}
opts = cJSON_CreateObject();
if (opts == NULL) {
fprintf(stderr, "out of memory\n");
goto cleanup;
}
cJSON_AddNumberToObject(opts, "nostr_index", nostr_index);
cJSON_AddItemToArray(params, opts);
opts = NULL;
if (nsigner_client_call(client, "get_public_key", params, &result) != NOSTR_SUCCESS) {
fprintf(stderr, "request failed for index %d: %s\n", nostr_index,
nsigner_client_last_error(client));
params = NULL; /* nsigner_client_call took ownership even on failure */
goto cleanup;
}
params = NULL; /* nsigner_client_call took ownership */
if (!cJSON_IsString(result)) {
fprintf(stderr, "index %d: unexpected result type\n", nostr_index);
goto cleanup;
}
hex_pubkey = result->valuestring;
if (strlen(hex_pubkey) != 64) {
fprintf(stderr, "index %d: unexpected pubkey length: %zu\n", nostr_index, strlen(hex_pubkey));
goto cleanup;
}
strncpy(out_hex, hex_pubkey, hex_size - 1);
out_hex[hex_size - 1] = '\0';
/* Convert hex pubkey to npub (bech32) */
if (hex_to_bytes(hex_pubkey, pubkey_bytes, 32) != 0) {
fprintf(stderr, "index %d: failed to parse hex pubkey\n", nostr_index);
goto cleanup;
}
if (nostr_key_to_bech32(pubkey_bytes, "npub", out_npub) != NOSTR_SUCCESS) {
fprintf(stderr, "index %d: failed to convert to npub\n", nostr_index);
goto cleanup;
}
rc = 0;
cleanup:
cJSON_Delete(opts);
cJSON_Delete(params);
cJSON_Delete(result);
nsigner_client_free(client);
return rc;
}
int main(int argc, char **argv) {
const char *host = "127.0.0.1";
int port = 8080;
char hex0[65], npub0[128];
char hex1[65], npub1[128];
int failures = 0;
if (argc > 1) {
host = argv[1];
}
if (argc > 2) {
port = atoi(argv[2]);
if (port <= 0 || port > 65535) {
fprintf(stderr, "Invalid port: %s\n", argv[2]);
return 1;
}
}
if (nostr_init() != NOSTR_SUCCESS) {
fprintf(stderr, "failed to initialize crypto subsystem\n");
return 1;
}
printf("Connecting to n_signer at %s:%d\n", host, port);
printf("Querying get_public_key for nostr_index 0 and 1...\n\n");
/* Query index 0 */
hex0[0] = '\0';
npub0[0] = '\0';
if (query_pubkey(host, port, 0, hex0, sizeof(hex0), npub0, sizeof(npub0)) == 0) {
printf("index 0: hex=%s npub=%s\n", hex0, npub0);
} else {
printf("index 0: FAILED\n");
failures++;
}
/* Query index 1 */
hex1[0] = '\0';
npub1[0] = '\0';
if (query_pubkey(host, port, 1, hex1, sizeof(hex1), npub1, sizeof(npub1)) == 0) {
printf("index 1: hex=%s npub=%s\n", hex1, npub1);
} else {
printf("index 1: FAILED\n");
failures++;
}
nostr_cleanup();
if (failures > 0) {
printf("\n%d query(s) failed\n", failures);
return 1;
}
printf("\nAll queries succeeded\n");
return 0;
}

View File

@@ -1,32 +1,100 @@
/*
* get_public_key_client.c — connect to a running n_signer over its abstract
* UNIX socket and call get_public_key, using the shared nsigner client from
* nostr_core_lib (nostr_core/nsigner_client.h + nostr_core/nsigner_transport.h).
*
* Usage: ./get_public_key_client [socket_name]
*
* Output: the raw JSON-RPC response string, e.g.
* {"id":"1","result":"<pubkey hex>"}
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../client/nsigner_client.h"
#include "nostr_common.h"
#include "nsigner_transport.h"
#include "nsigner_client.h"
#include "../cjson/cJSON.h"
int main(int argc, char **argv) {
const char *socket_name = "nsigner";
nsigner_client_t client;
char *response = NULL;
nsigner_transport_t *transport = NULL;
nsigner_client_t *client = NULL;
cJSON *params = NULL;
cJSON *result = NULL;
cJSON *response = NULL;
char *response_json = NULL;
int rc = 1;
if (argc > 1 && argv[1] != NULL && argv[1][0] != '\0') {
socket_name = argv[1];
}
nsigner_client_init(&client);
if (nsigner_client_connect_unix(&client, socket_name, 5000) != 0) {
fprintf(stderr, "connect failed: %s\n", nsigner_client_last_error(&client));
if (nostr_init() != NOSTR_SUCCESS) {
fprintf(stderr, "failed to initialize crypto subsystem\n");
return 1;
}
if (nsigner_client_get_public_key(&client, "example-1", "", &response) != 0) {
fprintf(stderr, "request failed: %s\n", nsigner_client_last_error(&client));
nsigner_client_close(&client);
return 1;
transport = nsigner_transport_open_unix(socket_name, 5000);
if (transport == NULL) {
fprintf(stderr, "connect failed: cannot open unix transport @%s\n", socket_name);
goto cleanup;
}
printf("%s\n", response);
free(response);
nsigner_client_close(&client);
return 0;
client = nsigner_client_new(transport);
if (client == NULL) {
fprintf(stderr, "connect failed: cannot create nsigner client\n");
/* nsigner_client_new takes ownership of transport on success only */
transport->close(transport);
goto cleanup;
}
transport = NULL; /* owned by client now */
/* params: [] (empty array — server picks the default role) */
params = cJSON_CreateArray();
if (params == NULL) {
fprintf(stderr, "out of memory\n");
goto cleanup;
}
if (nsigner_client_call(client, "get_public_key", params, &result) != NOSTR_SUCCESS) {
fprintf(stderr, "request failed: %s\n", nsigner_client_last_error(client));
goto cleanup;
}
params = NULL; /* nsigner_client_call takes ownership of params */
/*
* Reconstruct a JSON-RPC response string so the CLI output stays
* backward-compatible with the old client example:
* {"id":"<id>","result":"<pubkey hex>"}
* nsigner_client_call returns only the parsed `result` element and does
* not expose the server-assigned id, so we emit a minimal envelope.
*/
response = cJSON_CreateObject();
if (response == NULL) {
fprintf(stderr, "out of memory\n");
goto cleanup;
}
cJSON_AddStringToObject(response, "id", "1");
cJSON_AddItemReferenceToObject(response, "result", result);
response_json = cJSON_PrintUnformatted(response);
if (response_json == NULL) {
fprintf(stderr, "failed to serialize response\n");
goto cleanup;
}
printf("%s\n", response_json);
rc = 0;
cleanup:
free(response_json);
cJSON_Delete(response);
cJSON_Delete(result);
cJSON_Delete(params);
nsigner_client_free(client); /* also closes/frees the transport */
nostr_cleanup();
return rc;
}

View File

@@ -0,0 +1,214 @@
#!/usr/bin/env python3
"""
KB2040 Hidden Signer host client (WebUSB/vendor interface via pyusb).
Protocol:
4-byte big-endian length + UTF-8 JSON body.
Default target:
VID:PID 239a:8104
Examples:
python3 examples/kb2040_hidden_signer_client.py status
python3 examples/kb2040_hidden_signer_client.py set-mnemonic --mnemonic "abandon ... about"
python3 examples/kb2040_hidden_signer_client.py get-public-key
python3 examples/kb2040_hidden_signer_client.py sign-event --event '{"kind":1,"content":"hello"}'
"""
import argparse
import json
import struct
import sys
import time
try:
import usb.core
import usb.util
except ImportError:
print("Missing pyusb. Install with: pip install pyusb", file=sys.stderr)
sys.exit(2)
def find_vendor_interface(dev):
cfg = dev.get_active_configuration()
for itf in cfg:
if itf.bInterfaceClass == 0xFF:
ep_out = None
ep_in = None
for ep in itf:
direction = usb.util.endpoint_direction(ep.bEndpointAddress)
if direction == usb.util.ENDPOINT_OUT:
ep_out = ep
elif direction == usb.util.ENDPOINT_IN:
ep_in = ep
if ep_out is not None and ep_in is not None:
return itf, ep_out, ep_in
return None, None, None
def open_device(vid: int, pid: int):
dev = usb.core.find(idVendor=vid, idProduct=pid)
if dev is None:
raise RuntimeError(f"Device {vid:04x}:{pid:04x} not found")
try:
dev.set_configuration()
except usb.core.USBError:
pass
itf, ep_out, ep_in = find_vendor_interface(dev)
if itf is None:
raise RuntimeError("Vendor/WebUSB interface (class 0xFF) not found")
itf_num = itf.bInterfaceNumber
try:
if dev.is_kernel_driver_active(itf_num):
dev.detach_kernel_driver(itf_num)
except (NotImplementedError, usb.core.USBError):
pass
usb.util.claim_interface(dev, itf_num)
# WebUSB-style connect handshake (CDC SET_CONTROL_LINE_STATE / request 0x22).
# Firmware gates IN transfers on this "connected" state.
try:
dev.ctrl_transfer(0x21, 0x22, 0x0001, itf_num, None)
except usb.core.USBError:
# Some stacks may not require/implement this; continue and let RPC I/O decide.
pass
return dev, itf_num, ep_out, ep_in
def close_device(dev, itf_num: int):
try:
usb.util.release_interface(dev, itf_num)
except usb.core.USBError:
pass
_rx_remainder = bytearray()
def send_frame(ep_out, payload: bytes):
frame = struct.pack(">I", len(payload)) + payload
ep_out.write(frame)
def recv_exact(ep_in, n: int, timeout_ms: int):
global _rx_remainder
out = bytearray()
# Consume previously over-read bytes first.
if _rx_remainder:
take = min(n, len(_rx_remainder))
out.extend(_rx_remainder[:take])
del _rx_remainder[:take]
deadline = time.time() + max(1.0, timeout_ms / 1000.0)
packet_size = max(64, int(getattr(ep_in, "wMaxPacketSize", 64)))
while len(out) < n and time.time() < deadline:
chunk = ep_in.read(packet_size, timeout=timeout_ms)
out.extend(bytes(chunk))
if len(out) < n:
raise TimeoutError(f"Timeout while reading {n} bytes (got {len(out)})")
if len(out) > n:
_rx_remainder.extend(out[n:])
del out[n:]
return bytes(out)
def recv_frame(ep_in, timeout_ms: int):
hdr = recv_exact(ep_in, 4, timeout_ms)
body_len = struct.unpack(">I", hdr)[0]
if body_len > 64 * 1024:
raise ValueError(f"Refusing oversized frame: {body_len}")
body = recv_exact(ep_in, body_len, timeout_ms)
return body
def rpc(dev_args, method: str, params: dict):
global _rx_remainder
dev, itf_num, ep_out, ep_in = open_device(dev_args.vid, dev_args.pid)
try:
_rx_remainder.clear()
req = {"method": method}
req.update(params)
payload = json.dumps(req, separators=(",", ":")).encode("utf-8")
send_frame(ep_out, payload)
resp = recv_frame(ep_in, dev_args.timeout_ms)
return json.loads(resp.decode("utf-8", errors="replace"))
finally:
close_device(dev, itf_num)
def cmd_status(args):
print(json.dumps(rpc(args, "get_status", {}), indent=2))
def cmd_set_mnemonic(args):
print(json.dumps(rpc(args, "set_mnemonic", {"mnemonic": args.mnemonic}), indent=2))
def cmd_set_auto_approve(args):
value = args.value.lower() in ("1", "true", "yes", "on")
print(json.dumps(rpc(args, "set_auto_approve", {"value": value}), indent=2))
def cmd_get_public_key(args):
print(json.dumps(rpc(args, "get_public_key", {}), indent=2))
def cmd_sign_event(args):
event = json.loads(args.event)
print(json.dumps(rpc(args, "sign_event", {"event": event}), indent=2))
def build_parser():
p = argparse.ArgumentParser()
p.add_argument("--vid", type=lambda s: int(s, 16), default=0x239A, help="USB VID in hex (default: 239A)")
p.add_argument("--pid", type=lambda s: int(s, 16), default=0x8104, help="USB PID in hex (default: 8104)")
p.add_argument("--timeout-ms", type=int, default=3000, help="USB read timeout in ms")
sub = p.add_subparsers(dest="cmd", required=True)
s = sub.add_parser("status")
s.set_defaults(func=cmd_status)
s = sub.add_parser("set-mnemonic")
s.add_argument("--mnemonic", required=True)
s.set_defaults(func=cmd_set_mnemonic)
s = sub.add_parser("set-auto-approve")
s.add_argument("--value", required=True, help="true|false")
s.set_defaults(func=cmd_set_auto_approve)
s = sub.add_parser("get-public-key")
s.set_defaults(func=cmd_get_public_key)
s = sub.add_parser("sign-event")
s.add_argument("--event", required=True, help="JSON object string")
s.set_defaults(func=cmd_sign_event)
return p
def main():
parser = build_parser()
args = parser.parse_args()
try:
args.func(args)
return 0
except Exception as e:
print(f"ERROR: {e}", file=sys.stderr)
return 1
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,257 @@
#!/usr/bin/env node
/**
* n_signer_qube_example.js — connect to a running n_signer over TCP (FIPS mesh)
* and call get_public_key for nostr_index 0 and 1, printing both hex pubkey
* and bech32 npub for each.
*
* This is a cross-qube test client for Qubes OS: the signer runs in the
* nostr_signer qube listening on tcp:[::]:8080, and this client runs in
* a different qube connecting to the signer's FIPS address.
*
* Usage:
* node n_signer_qube_example.js [host] [port]
* node n_signer_qube_example.js fd56:d7c3:f605:719d:15b:18a0:fb06:982f 8080
*
* If no arguments are given, defaults to localhost:8080.
*
* Protocol:
* - 4-byte big-endian length prefix + JSON payload (TCP framing)
* - JSON-RPC: {"id":"...","method":"get_public_key","params":[{"nostr_index":N}],"auth":{...}}
* - Auth envelope: kind 27235 Nostr event with nsigner_rpc, nsigner_method,
* nsigner_body_hash tags. body_hash = sha256(json.dumps(params))
* - Response: {"id":"...","result":"<pubkey hex>"} or {"id":"...","error":{...}}
*/
const net = require("net");
const crypto = require("crypto");
const secp = require("@noble/secp256k1");
// @noble/secp256k1 v3 requires us to provide sync sha256/hmacSha256
secp.hashes.sha256 = (msg) => new Uint8Array(crypto.createHash("sha256").update(msg).digest());
secp.hashes.hmacSha256 = (key, msg) =>
new Uint8Array(crypto.createHmac("sha256", key).update(msg).digest());
const { schnorr } = secp;
/** Convert a Uint8Array to a hex string. */
function bytesToHex(bytes) {
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
}
/** Convert a hex string to a Uint8Array. */
function hexToBytes(hex) {
const arr = new Uint8Array(hex.length / 2);
for (let i = 0; i < arr.length; i++) {
arr[i] = parseInt(hex.substr(i * 2, 2), 16);
}
return arr;
}
// Demo caller key (32 bytes). Replace with your stable caller key in real use.
const DEMO_PRIVKEY = new Uint8Array(Array.from({ length: 32 }, (_, i) => i + 1));
/**
* Compute the body hash for a params array.
* This must match n_signer's hashing: sha256 of canonical JSON (no spaces).
*/
function computeBodyHash(params) {
const canonical = JSON.stringify(params);
return crypto.createHash("sha256").update(canonical).digest("hex");
}
/**
* Build a kind-27235 auth envelope for the given method and params.
* Returns the auth object to include in the JSON-RPC request.
*/
function buildAuthEnvelope(privkey, method, params, requestId) {
const bodyHash = computeBodyHash(params);
const pubkeyHex = bytesToHex(schnorr.getPublicKey(privkey));
const createdAt = Math.floor(Date.now() / 1000);
// nsigner_rpc tag value MUST match the JSON-RPC request "id" field
const tags = [
["nsigner_rpc", requestId],
["nsigner_method", method],
["nsigner_body_hash", bodyHash],
];
const content = "js-example";
// Serialize the event for signing: [0, pubkey, created_at, kind, tags, content]
const serialized = JSON.stringify([
0,
pubkeyHex,
createdAt,
27235,
tags,
content,
]);
const eventId = crypto.createHash("sha256").update(serialized).digest("hex");
const sig = bytesToHex(schnorr.sign(hexToBytes(eventId), privkey, new Uint8Array(32)));
return {
id: eventId,
pubkey: pubkeyHex,
created_at: createdAt,
kind: 27235,
tags: tags,
content: content,
sig: sig,
};
}
/**
* Send a framed JSON-RPC request over a TCP socket and receive the response.
* Framing: 4-byte big-endian length prefix + JSON payload.
*/
function sendRequest(socket, request) {
return new Promise((resolve, reject) => {
const payload = Buffer.from(JSON.stringify(request), "utf8");
const header = Buffer.alloc(4);
header.writeUInt32BE(payload.length, 0);
socket.write(Buffer.concat([header, payload]));
let headerBuf = Buffer.alloc(0);
let bodyBuf = Buffer.alloc(0);
let bodyLen = 0;
let state = "header";
const onData = (chunk) => {
if (state === "header") {
headerBuf = Buffer.concat([headerBuf, chunk]);
if (headerBuf.length >= 4) {
bodyLen = headerBuf.readUInt32BE(0);
const remaining = headerBuf.subarray(4);
headerBuf = Buffer.alloc(0);
state = "body";
if (remaining.length > 0) {
bodyBuf = Buffer.concat([bodyBuf, remaining]);
}
if (bodyBuf.length >= bodyLen) {
finish();
}
}
} else if (state === "body") {
bodyBuf = Buffer.concat([bodyBuf, chunk]);
if (bodyBuf.length >= bodyLen) {
finish();
}
}
};
function finish() {
socket.off("data", onData);
socket.off("error", onError);
const body = bodyBuf.subarray(0, bodyLen).toString("utf8");
try {
resolve(JSON.parse(body));
} catch (e) {
reject(new Error(`Failed to parse response: ${e.message}`));
}
}
function onError(err) {
socket.off("data", onData);
reject(err);
}
socket.on("data", onData);
socket.on("error", onError);
});
}
/**
* Query get_public_key for a given nostr_index.
* Opens a fresh TCP connection for each request (n_signer handles one request
* per connection).
*/
async function getPublicKey(host, port, nostrIndex, privkey) {
const params = [{ nostr_index: nostrIndex }];
const requestId = String(nostrIndex);
const auth = buildAuthEnvelope(privkey, "get_public_key", params, requestId);
const request = {
id: requestId,
method: "get_public_key",
params: params,
auth: auth,
};
return new Promise((resolve, reject) => {
const socket = new net.Socket();
socket.setTimeout(15000);
socket.connect(port, host, async () => {
try {
const response = await sendRequest(socket, request);
socket.destroy();
resolve(response);
} catch (e) {
socket.destroy();
reject(e);
}
});
socket.on("timeout", () => {
socket.destroy();
reject(new Error("Connection timed out"));
});
socket.on("error", (err) => {
reject(err);
});
});
}
/**
* Convert a 32-byte hex pubkey to bech32 npub format (NIP-19).
*/
function hexToNpub(pubkeyHex) {
const { nip19 } = require("nostr-tools");
return nip19.npubEncode(pubkeyHex);
}
async function main() {
const host = process.argv[2] || "127.0.0.1";
const port = parseInt(process.argv[3] || "8080", 10);
console.log(`Connecting to n_signer at ${host}:${port}`);
console.log("Querying get_public_key for nostr_index 0 and 1...\n");
let failures = 0;
for (const index of [0, 1]) {
// Small delay between requests to avoid auth nonce collision
// (the auth envelope uses created_at as part of the nonce)
if (index > 0) await new Promise((r) => setTimeout(r, 1100));
try {
const response = await getPublicKey(host, port, index, DEMO_PRIVKEY);
if (response.error) {
console.log(`index ${index}: ERROR: ${JSON.stringify(response.error)}`);
failures++;
continue;
}
const pubkeyHex = response.result;
const npub = hexToNpub(pubkeyHex);
console.log(`index ${index}: hex=${pubkeyHex} npub=${npub}`);
} catch (e) {
console.log(`index ${index}: FAILED - ${e.message}`);
failures++;
}
}
if (failures > 0) {
console.log(`\n${failures} query(s) failed`);
process.exit(1);
}
console.log("\nAll queries succeeded");
}
main().catch((e) => {
console.error(e);
process.exit(1);
});

View File

@@ -0,0 +1,155 @@
#!/usr/bin/env node
/**
* n_signer_qube_example_qrexec.js — connect to n_signer in the nostr_signer
* qube via Qubes qrexec (qubes.NsignerRpc service) and call get_public_key
* for nostr_index 0 and 1, printing both hex pubkey and bech32 npub.
*
* This uses Qubes OS's built-in secure IPC (qrexec) instead of FIPS TCP.
* No network connectivity is required — all traffic stays within the host.
*
* Prerequisites:
* - The qubes.NsignerRpc service must be installed in the nostr_signer qube
* (see packaging/qubes/install-service.sh)
* - The qrexec policy must be installed in dom0
* (see packaging/qubes/install-policy.sh)
* - The nostr_signer qube must be tagged with 'nsigner-signer'
* (qvm-tags nostr_signer add nsigner-signer)
* - A mnemonic file must exist at /home/user/.nsigner_mnemonic in the
* nostr_signer qube
*
* Usage:
* node n_signer_qube_example_qrexec.js [target_qube]
* node n_signer_qube_example_qrexec.js nostr_signer
*
* If no argument is given, defaults to "nostr_signer".
*
* Protocol:
* - qrexec-client-vm spawns the qubes.NsignerRpc service in the target qube
* - The service runs: nsigner --listen qrexec --allow-all --mnemonic-fd 3
* - We send one framed JSON-RPC request via stdin, receive one framed response via stdout
* - Framing: 4-byte big-endian length prefix + JSON payload
* - Auth: not required in qrexec mode (caller identity is qubes:<source-vm>)
*/
const { spawn } = require("child_process");
const { nip19 } = require("nostr-tools");
/**
* Frame a JSON-RPC request: 4-byte big-endian length + JSON payload.
*/
function frameRequest(obj) {
const payload = Buffer.from(JSON.stringify(obj), "utf8");
const header = Buffer.alloc(4);
header.writeUInt32BE(payload.length, 0);
return Buffer.concat([header, payload]);
}
/**
* Parse a framed response from the qrexec stdout buffer.
*/
function parseFramedResponse(buf) {
if (buf.length < 4) {
throw new Error("short response (missing frame header)");
}
const len = buf.readUInt32BE(0);
const payload = buf.subarray(4, 4 + len);
if (payload.length !== len) {
throw new Error(`short response payload: expected ${len}, got ${payload.length}`);
}
return JSON.parse(payload.toString("utf8"));
}
/**
* Call nsigner via qrexec. Sends one framed request, receives one framed response.
* Each call spawns a fresh qrexec-client-vm process (one request per invocation).
*/
function callNsignerQrexec(targetQube, request) {
return new Promise((resolve, reject) => {
const framed = frameRequest(request);
const proc = spawn("qrexec-client-vm", [targetQube, "qubes.NsignerRpc"], {
stdio: ["pipe", "pipe", "pipe"],
});
const stdoutChunks = [];
const stderrChunks = [];
proc.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
proc.stderr.on("data", (chunk) => stderrChunks.push(chunk));
proc.on("error", (err) => {
reject(new Error(`failed to spawn qrexec-client-vm: ${err.message}`));
});
proc.on("close", (code) => {
if (code !== 0) {
const stderr = Buffer.concat(stderrChunks).toString("utf8");
reject(new Error(`qrexec-client-vm exited with code ${code}: ${stderr.trim()}`));
return;
}
try {
const response = parseFramedResponse(Buffer.concat(stdoutChunks));
resolve(response);
} catch (e) {
reject(new Error(`failed to parse response: ${e.message}`));
}
});
// Send the framed request and close stdin
proc.stdin.write(framed);
proc.stdin.end();
});
}
/**
* Query get_public_key for a given nostr_index via qrexec.
* No auth envelope needed — qrexec mode uses qubes:<source-vm> as caller identity.
*/
async function getPublicKey(targetQube, nostrIndex) {
const request = {
id: String(nostrIndex),
method: "get_public_key",
params: [{ nostr_index: nostrIndex }],
};
return callNsignerQrexec(targetQube, request);
}
async function main() {
const targetQube = process.argv[2] || "nostr_signer";
console.log(`Calling n_signer in qube "${targetQube}" via qrexec...`);
console.log("Querying get_public_key for nostr_index 0 and 1...\n");
let failures = 0;
for (const index of [0, 1]) {
try {
const response = await getPublicKey(targetQube, index);
if (response.error) {
console.log(`index ${index}: ERROR: ${JSON.stringify(response.error)}`);
failures++;
continue;
}
const pubkeyHex = response.result;
const npub = nip19.npubEncode(pubkeyHex);
console.log(`index ${index}: hex=${pubkeyHex} npub=${npub}`);
} catch (e) {
console.log(`index ${index}: FAILED - ${e.message}`);
failures++;
}
}
if (failures > 0) {
console.log(`\n${failures} query(s) failed`);
process.exit(1);
}
console.log("\nAll queries succeeded");
}
main().catch((e) => {
console.error(e);
process.exit(1);
});

View File

@@ -1,33 +1,114 @@
/*
* sign_event_client.c — connect to a running n_signer over its abstract
* UNIX socket and call sign_event, using the shared nsigner client from
* nostr_core_lib (nostr_core/nsigner_client.h + nostr_core/nsigner_transport.h).
*
* Usage: ./sign_event_client [socket_name]
*
* Output: the raw JSON-RPC response string, e.g.
* {"id":"1","result":"{...signed event...}"}
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../client/nsigner_client.h"
#include "nostr_common.h"
#include "nsigner_transport.h"
#include "nsigner_client.h"
#include "../cjson/cJSON.h"
int main(int argc, char **argv) {
const char *socket_name = "nsigner";
const char *event_json = "{\"kind\":1,\"content\":\"hello from client example\",\"tags\":[],\"created_at\":1700000000}";
nsigner_client_t client;
char *response = NULL;
const char *role = "main";
nsigner_transport_t *transport = NULL;
nsigner_client_t *client = NULL;
cJSON *params = NULL;
cJSON *opts = NULL;
cJSON *result = NULL;
cJSON *response = NULL;
char *response_json = NULL;
int rc = 1;
if (argc > 1 && argv[1] != NULL && argv[1][0] != '\0') {
socket_name = argv[1];
}
nsigner_client_init(&client);
if (nsigner_client_connect_unix(&client, socket_name, 5000) != 0) {
fprintf(stderr, "connect failed: %s\n", nsigner_client_last_error(&client));
if (nostr_init() != NOSTR_SUCCESS) {
fprintf(stderr, "failed to initialize crypto subsystem\n");
return 1;
}
if (nsigner_client_sign_event(&client, "example-2", event_json, "main", &response) != 0) {
fprintf(stderr, "request failed: %s\n", nsigner_client_last_error(&client));
nsigner_client_close(&client);
return 1;
transport = nsigner_transport_open_unix(socket_name, 5000);
if (transport == NULL) {
fprintf(stderr, "connect failed: cannot open unix transport @%s\n", socket_name);
goto cleanup;
}
printf("%s\n", response);
free(response);
nsigner_client_close(&client);
return 0;
client = nsigner_client_new(transport);
if (client == NULL) {
fprintf(stderr, "connect failed: cannot create nsigner client\n");
transport->close(transport);
goto cleanup;
}
transport = NULL; /* owned by client now */
/* params: [ "<event_json>", {"role":"main"} ] */
params = cJSON_CreateArray();
if (params == NULL) {
fprintf(stderr, "out of memory\n");
goto cleanup;
}
cJSON_AddItemToArray(params, cJSON_CreateString(event_json));
opts = cJSON_CreateObject();
if (opts == NULL) {
fprintf(stderr, "out of memory\n");
goto cleanup;
}
if (role != NULL && role[0] != '\0') {
cJSON_AddStringToObject(opts, "role", role);
}
cJSON_AddItemToArray(params, opts);
opts = NULL; /* owned by params now */
if (nsigner_client_call(client, "sign_event", params, &result) != NOSTR_SUCCESS) {
fprintf(stderr, "request failed: %s\n", nsigner_client_last_error(client));
goto cleanup;
}
params = NULL; /* nsigner_client_call takes ownership of params */
/*
* Reconstruct a JSON-RPC response string for backward-compatible CLI
* output: {"id":"1","result":"<signed event json>"}
* The server returns the signed event as a JSON string (not an object),
* so result is a cJSON string here.
*/
response = cJSON_CreateObject();
if (response == NULL) {
fprintf(stderr, "out of memory\n");
goto cleanup;
}
cJSON_AddStringToObject(response, "id", "1");
cJSON_AddItemReferenceToObject(response, "result", result);
response_json = cJSON_PrintUnformatted(response);
if (response_json == NULL) {
fprintf(stderr, "failed to serialize response\n");
goto cleanup;
}
printf("%s\n", response_json);
rc = 0;
cleanup:
free(response_json);
cJSON_Delete(response);
cJSON_Delete(result);
cJSON_Delete(params);
cJSON_Delete(opts);
nsigner_client_free(client); /* also closes/frees the transport */
nostr_cleanup();
return rc;
}

View File

@@ -277,6 +277,72 @@ size_t mnemonic_words_for_letter(char letter, size_t *out_start)
return s_letter_count[idx];
}
size_t mnemonic_words_with_prefix(const char *prefix, uint16_t *out_indices, size_t max_out)
{
size_t start = 0;
size_t count = 0;
size_t written = 0;
if (prefix == NULL || prefix[0] == '\0') {
return 0;
}
count = mnemonic_words_for_letter(prefix[0], &start);
if (count == 0) {
return 0;
}
for (size_t i = 0; i < count; ++i) {
size_t idx = start + i;
const char *word = BIP39_WORDLIST[idx];
if (strncmp(word, prefix, strlen(prefix)) == 0) {
if (out_indices != NULL && written < max_out) {
out_indices[written] = (uint16_t)idx;
}
written++;
}
}
return written;
}
bool mnemonic_letter_extends_prefix(const char *prefix, size_t prefix_len, char next_letter)
{
char probe[16];
size_t start = 0;
size_t count = 0;
if (next_letter >= 'A' && next_letter <= 'Z') {
next_letter = (char)(next_letter - 'A' + 'a');
}
if (next_letter < 'a' || next_letter > 'z') {
return false;
}
if (prefix == NULL || prefix_len >= sizeof(probe) - 1) {
return false;
}
memcpy(probe, prefix, prefix_len);
probe[prefix_len] = next_letter;
probe[prefix_len + 1] = '\0';
count = mnemonic_words_for_letter(probe[0], &start);
if (count == 0) {
return false;
}
for (size_t i = 0; i < count; ++i) {
const char *word = BIP39_WORDLIST[start + i];
if (strncmp(word, probe, prefix_len + 1) == 0) {
return true;
}
}
return false;
}
const char *mnemonic_word_at(size_t index)
{
if (index >= 2048) {

View File

@@ -1,5 +1,6 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
@@ -7,4 +8,6 @@ int generate_mnemonic_12(char *out, size_t out_len);
int mnemonic_to_seed(const char *mnemonic, uint8_t seed[64]);
int mnemonic_validate(const char *mnemonic);
size_t mnemonic_words_for_letter(char letter, size_t *out_start);
size_t mnemonic_words_with_prefix(const char *prefix, uint16_t *out_indices, size_t max_out);
bool mnemonic_letter_extends_prefix(const char *prefix, size_t prefix_len, char next_letter);
const char *mnemonic_word_at(size_t index);

View File

@@ -13,10 +13,15 @@
#include "ili9341.h"
#include "lvgl.h"
#include "mnemonic.h"
#include "secure_mem.h"
#include "touch.h"
#define UI_TICK_MS 2
#define UI_DRAW_LINES 20
#define UI_MNEMONIC_WORDS 12
#define UI_BIP39_WORD_MAX 9
#define UI_SUGGESTION_STORE_MAX 64
#define UI_SUGGESTION_VISIBLE_MAX 32
static const char *TAG = "ui_lvgl";
@@ -30,7 +35,21 @@ static bool s_done = false;
static bool s_have_mnemonic = false;
static char s_selected_mnemonic[256];
static lv_obj_t *s_status_label = NULL;
static lv_obj_t *s_entry_ta = NULL;
static char s_committed_words[UI_MNEMONIC_WORDS][UI_BIP39_WORD_MAX];
static size_t s_word_count = 0;
static char s_prefix[UI_BIP39_WORD_MAX];
static size_t s_prefix_len = 0;
static bool s_checksum_override = false;
static lv_obj_t *s_entry_words_label = NULL;
static lv_obj_t *s_entry_prefix_label = NULL;
static lv_obj_t *s_suggestion_row = NULL;
static lv_obj_t *s_key_matrix = NULL;
static lv_obj_t *s_checksum_panel = NULL;
static uint16_t s_match_indices[UI_SUGGESTION_STORE_MAX];
static size_t s_match_total = 0;
static ui_approval_decision_t s_approval_decision = UI_APPROVAL_TIMEOUT;
static lv_obj_t *s_approval_countdown = NULL;
@@ -41,6 +60,18 @@ static int64_t s_approval_deadline_us = 0;
static char s_event_lines[UI_EVENT_LOG_MAX][UI_EVENT_LINE_MAX];
static int s_event_count = 0;
static void reset_screen_widget_refs(void)
{
s_status_label = NULL;
s_entry_words_label = NULL;
s_entry_prefix_label = NULL;
s_suggestion_row = NULL;
s_key_matrix = NULL;
s_checksum_panel = NULL;
s_approval_countdown = NULL;
s_match_total = 0;
}
static void style_screen(lv_obj_t *scr)
{
lv_obj_set_style_bg_opa(scr, LV_OPA_COVER, 0);
@@ -62,31 +93,24 @@ static void style_button(lv_obj_t *btn)
lv_obj_set_style_text_color(btn, lv_color_hex(0xFF0000), LV_STATE_FOCUSED);
}
static void style_text_area(lv_obj_t *ta)
static void style_key_matrix(lv_obj_t *km)
{
lv_obj_set_style_bg_opa(ta, LV_OPA_COVER, 0);
lv_obj_set_style_bg_color(ta, lv_color_hex(0x000000), 0);
lv_obj_set_style_border_width(ta, 2, 0);
lv_obj_set_style_border_color(ta, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_text_color(ta, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_text_color(ta, lv_color_hex(0xFF0000), LV_PART_CURSOR);
}
lv_obj_set_style_bg_opa(km, LV_OPA_COVER, 0);
lv_obj_set_style_bg_color(km, lv_color_hex(0x000000), 0);
lv_obj_set_style_border_width(km, 1, 0);
lv_obj_set_style_border_color(km, lv_color_hex(0xFFFFFF), 0);
static void style_keyboard(lv_obj_t *kb)
{
lv_obj_set_style_bg_opa(kb, LV_OPA_COVER, 0);
lv_obj_set_style_bg_color(kb, lv_color_hex(0x000000), 0);
lv_obj_set_style_border_width(kb, 1, 0);
lv_obj_set_style_border_color(kb, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_bg_opa(km, LV_OPA_COVER, LV_PART_ITEMS);
lv_obj_set_style_bg_color(km, lv_color_hex(0x000000), LV_PART_ITEMS);
lv_obj_set_style_border_color(km, lv_color_hex(0xFFFFFF), LV_PART_ITEMS);
lv_obj_set_style_border_width(km, 1, LV_PART_ITEMS);
lv_obj_set_style_text_color(km, lv_color_hex(0xFFFFFF), LV_PART_ITEMS);
lv_obj_set_style_bg_opa(kb, LV_OPA_COVER, LV_PART_ITEMS);
lv_obj_set_style_bg_color(kb, lv_color_hex(0x000000), LV_PART_ITEMS);
lv_obj_set_style_border_color(kb, lv_color_hex(0xFFFFFF), LV_PART_ITEMS);
lv_obj_set_style_border_width(kb, 1, LV_PART_ITEMS);
lv_obj_set_style_text_color(kb, lv_color_hex(0xFFFFFF), LV_PART_ITEMS);
lv_obj_set_style_border_color(km, lv_color_hex(0xFF0000), LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_set_style_text_color(km, lv_color_hex(0xFF0000), LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_set_style_border_color(kb, lv_color_hex(0xFF0000), LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_set_style_text_color(kb, lv_color_hex(0xFF0000), LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_set_style_text_color(km, lv_color_hex(0x505050), LV_PART_ITEMS | LV_STATE_DISABLED);
lv_obj_set_style_border_color(km, lv_color_hex(0x303030), LV_PART_ITEMS | LV_STATE_DISABLED);
}
static void lvgl_tick_cb(void *arg)
@@ -198,85 +222,443 @@ static void on_generate(lv_event_t *e)
}
static void build_main_menu(void);
static void refresh_entry_ui(void);
static bool verify_or_prompt_checksum(void);
static void on_back_to_menu(lv_event_t *e)
static const char *s_key_map[] = {
"q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "\n",
"a", "s", "d", "f", "g", "h", "j", "k", "l", "\n",
"z", "x", "c", "v", "b", "n", "m", "<", "",
};
static void clear_entry_state(void)
{
(void)e;
build_main_menu();
secure_memzero(s_committed_words, sizeof(s_committed_words));
secure_memzero(s_prefix, sizeof(s_prefix));
s_word_count = 0;
s_prefix_len = 0;
s_checksum_override = false;
s_match_total = 0;
}
static void on_use_entry(lv_event_t *e)
static bool join_committed_words(char *out, size_t out_len)
{
size_t cursor = 0;
if (out == NULL || out_len == 0) {
return false;
}
out[0] = '\0';
for (size_t i = 0; i < s_word_count; ++i) {
size_t wl = strlen(s_committed_words[i]);
if (cursor != 0) {
if (cursor + 1 >= out_len) {
out[0] = '\0';
return false;
}
out[cursor++] = ' ';
}
if (cursor + wl >= out_len) {
out[0] = '\0';
return false;
}
memcpy(out + cursor, s_committed_words[i], wl);
cursor += wl;
out[cursor] = '\0';
}
return true;
}
static void commit_word(const char *word)
{
if (word == NULL || s_word_count >= UI_MNEMONIC_WORDS) {
return;
}
memset(s_committed_words[s_word_count], 0, UI_BIP39_WORD_MAX);
strncpy(s_committed_words[s_word_count], word, UI_BIP39_WORD_MAX - 1);
s_word_count++;
secure_memzero(s_prefix, sizeof(s_prefix));
s_prefix_len = 0;
s_checksum_override = false;
}
static void on_checksum_use_anyway(lv_event_t *e)
{
(void)e;
if (s_entry_ta == NULL) {
return;
}
const char *txt = lv_textarea_get_text(s_entry_ta);
if (txt == NULL || txt[0] == '\0') {
set_status("Mnemonic is empty");
return;
}
if (mnemonic_validate(txt) != 0) {
set_status("Invalid mnemonic");
char joined[256];
memset(joined, 0, sizeof(joined));
if (!join_committed_words(joined, sizeof(joined))) {
set_status("Mnemonic buffer error");
secure_memzero(joined, sizeof(joined));
return;
}
memset(s_selected_mnemonic, 0, sizeof(s_selected_mnemonic));
strncpy(s_selected_mnemonic, txt, sizeof(s_selected_mnemonic) - 1);
strncpy(s_selected_mnemonic, joined, sizeof(s_selected_mnemonic) - 1);
s_have_mnemonic = true;
s_done = true;
s_checksum_override = true;
secure_memzero(joined, sizeof(joined));
}
static void on_checksum_edit_words(lv_event_t *e)
{
(void)e;
if (s_checksum_panel != NULL) {
lv_obj_del(s_checksum_panel);
s_checksum_panel = NULL;
}
set_status("Checksum mismatch: edit or use anyway");
refresh_entry_ui();
}
static bool verify_or_prompt_checksum(void)
{
char joined[256];
if (s_key_matrix == NULL || s_entry_words_label == NULL || s_entry_prefix_label == NULL) {
return false;
}
if (s_word_count != UI_MNEMONIC_WORDS || s_prefix_len != 0) {
set_status("Need exactly 12 complete words");
return false;
}
memset(joined, 0, sizeof(joined));
if (!join_committed_words(joined, sizeof(joined))) {
set_status("Mnemonic buffer error");
secure_memzero(joined, sizeof(joined));
return false;
}
if (mnemonic_validate(joined) == 0) {
if (s_checksum_panel != NULL) {
lv_obj_del(s_checksum_panel);
s_checksum_panel = NULL;
}
set_status("Checksum valid");
memset(s_selected_mnemonic, 0, sizeof(s_selected_mnemonic));
strncpy(s_selected_mnemonic, joined, sizeof(s_selected_mnemonic) - 1);
s_have_mnemonic = true;
s_done = true;
secure_memzero(joined, sizeof(joined));
return true;
}
if (s_checksum_panel != NULL) {
lv_obj_del(s_checksum_panel);
s_checksum_panel = NULL;
}
s_checksum_panel = lv_obj_create(lv_scr_act());
lv_obj_set_size(s_checksum_panel, DISPLAY_WIDTH - 20, 82);
lv_obj_align(s_checksum_panel, LV_ALIGN_TOP_MID, 0, 78);
lv_obj_set_style_bg_color(s_checksum_panel, lv_color_hex(0x000000), 0);
lv_obj_set_style_border_color(s_checksum_panel, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_border_width(s_checksum_panel, 2, 0);
lv_obj_set_style_radius(s_checksum_panel, 6, 0);
lv_obj_set_style_pad_all(s_checksum_panel, 6, 0);
lv_obj_t *txt = lv_label_create(s_checksum_panel);
lv_label_set_text(txt, "Checksum invalid. Use anyway?");
lv_obj_set_style_text_color(txt, lv_color_hex(0xFF0000), 0);
lv_obj_align(txt, LV_ALIGN_TOP_MID, 0, 0);
lv_obj_t *use_btn = lv_btn_create(s_checksum_panel);
lv_obj_set_size(use_btn, 128, 30);
lv_obj_align(use_btn, LV_ALIGN_BOTTOM_LEFT, 4, -2);
style_button(use_btn);
lv_obj_set_style_border_color(use_btn, lv_color_hex(0xFF0000), 0);
lv_obj_add_event_cb(use_btn, on_checksum_use_anyway, LV_EVENT_RELEASED, NULL);
lv_obj_t *use_lbl = lv_label_create(use_btn);
lv_label_set_text(use_lbl, "Use Anyway");
lv_obj_center(use_lbl);
lv_obj_t *edit_btn = lv_btn_create(s_checksum_panel);
lv_obj_set_size(edit_btn, 128, 30);
lv_obj_align(edit_btn, LV_ALIGN_BOTTOM_RIGHT, -4, -2);
style_button(edit_btn);
lv_obj_add_event_cb(edit_btn, on_checksum_edit_words, LV_EVENT_RELEASED, NULL);
lv_obj_t *edit_lbl = lv_label_create(edit_btn);
lv_label_set_text(edit_lbl, "Edit Words");
lv_obj_center(edit_lbl);
set_status("Checksum invalid");
secure_memzero(joined, sizeof(joined));
return false;
}
static void on_back_to_menu(lv_event_t *e)
{
(void)e;
if (s_checksum_panel != NULL) {
lv_obj_del(s_checksum_panel);
s_checksum_panel = NULL;
}
clear_entry_state();
build_main_menu();
}
static void on_suggestion_tap(lv_event_t *e)
{
const char *word = (const char *)lv_event_get_user_data(e);
if (s_suggestion_row == NULL || word == NULL || s_word_count >= UI_MNEMONIC_WORDS) {
return;
}
commit_word(word);
refresh_entry_ui();
if (s_word_count == UI_MNEMONIC_WORDS) {
(void)verify_or_prompt_checksum();
}
}
static void entry_backspace_step(void)
{
if (s_prefix_len > 0) {
s_prefix_len--;
s_prefix[s_prefix_len] = '\0';
} else if (s_word_count > 0) {
s_word_count--;
memset(s_prefix, 0, sizeof(s_prefix));
strncpy(s_prefix, s_committed_words[s_word_count], sizeof(s_prefix) - 1);
s_prefix_len = strlen(s_prefix);
secure_memzero(s_committed_words[s_word_count], sizeof(s_committed_words[s_word_count]));
}
s_checksum_override = false;
if (s_checksum_panel != NULL) {
lv_obj_del(s_checksum_panel);
s_checksum_panel = NULL;
}
refresh_entry_ui();
}
static void on_key_matrix(lv_event_t *e)
{
lv_obj_t *obj = lv_event_get_target(e);
if (s_key_matrix == NULL || obj != s_key_matrix) {
return;
}
uint16_t idx = lv_btnmatrix_get_selected_btn(obj);
const char *txt = lv_btnmatrix_get_btn_text(obj, idx);
if (txt == NULL || txt[0] == '\0' || txt[1] != '\0') {
return;
}
if (txt[0] == '<') {
entry_backspace_step();
return;
}
if (s_word_count >= UI_MNEMONIC_WORDS) {
set_status("Already have 12 words");
return;
}
if (s_prefix_len + 1 >= sizeof(s_prefix)) {
set_status("Word too long");
return;
}
s_prefix[s_prefix_len++] = txt[0];
s_prefix[s_prefix_len] = '\0';
s_checksum_override = false;
refresh_entry_ui();
if (s_match_total == 1) {
const char *word = mnemonic_word_at(s_match_indices[0]);
if (word != NULL) {
commit_word(word);
refresh_entry_ui();
if (s_word_count == UI_MNEMONIC_WORDS) {
(void)verify_or_prompt_checksum();
}
}
}
}
static void refresh_entry_ui(void)
{
size_t shown = 0;
char words_line[256];
char prefix_line[64];
if (s_checksum_panel != NULL) {
lv_obj_del(s_checksum_panel);
s_checksum_panel = NULL;
}
memset(words_line, 0, sizeof(words_line));
(void)join_committed_words(words_line, sizeof(words_line));
if (s_entry_words_label != NULL) {
if (s_word_count == 0) {
lv_label_set_text(s_entry_words_label, "(no words selected)");
} else {
lv_label_set_text(s_entry_words_label, words_line);
}
}
snprintf(prefix_line, sizeof(prefix_line), "Word %u/12 prefix: %s",
(unsigned)(s_word_count + 1),
(s_prefix_len > 0) ? s_prefix : "-");
if (s_entry_prefix_label != NULL) {
lv_label_set_text(s_entry_prefix_label, prefix_line);
}
if (s_suggestion_row != NULL) {
lv_obj_clean(s_suggestion_row);
if (s_prefix_len > 0 && s_word_count < UI_MNEMONIC_WORDS) {
s_match_total = mnemonic_words_with_prefix(s_prefix, s_match_indices, UI_SUGGESTION_STORE_MAX);
shown = (s_match_total > UI_SUGGESTION_VISIBLE_MAX) ? UI_SUGGESTION_VISIBLE_MAX : s_match_total;
for (size_t i = 0; i < shown; ++i) {
const char *word = mnemonic_word_at(s_match_indices[i]);
lv_obj_t *btn;
lv_obj_t *lbl;
if (word == NULL) {
continue;
}
btn = lv_btn_create(s_suggestion_row);
lv_obj_set_size(btn, 84, 26);
style_button(btn);
lv_obj_add_event_cb(btn, on_suggestion_tap, LV_EVENT_RELEASED, (void *)word);
lbl = lv_label_create(btn);
lv_label_set_text(lbl, word);
lv_obj_center(lbl);
}
if (s_match_total > shown) {
char more[24];
lv_obj_t *m = lv_label_create(s_suggestion_row);
snprintf(more, sizeof(more), "+%u more", (unsigned)(s_match_total - shown));
lv_label_set_text(m, more);
lv_obj_set_style_text_color(m, lv_color_hex(0xFFFFFF), 0);
}
if (s_match_total == 0) {
set_status("No matches");
} else {
set_status("");
}
} else {
s_match_total = 0;
}
}
if (s_key_matrix != NULL) {
const uint16_t matrix_btn_count = 27;
for (uint16_t i = 0; i < matrix_btn_count; ++i) {
const char *txt = lv_btnmatrix_get_btn_text(s_key_matrix, i);
bool enabled = false;
if (txt == NULL || txt[0] == '\0' || txt[1] != '\0') {
continue;
}
if (txt[0] == '<') {
enabled = (s_prefix_len > 0 || s_word_count > 0);
} else if (s_word_count >= UI_MNEMONIC_WORDS) {
enabled = false;
} else if (s_prefix_len == 0) {
size_t start = 0;
enabled = (mnemonic_words_for_letter(txt[0], &start) > 0);
} else {
enabled = mnemonic_letter_extends_prefix(s_prefix, s_prefix_len, txt[0]);
}
if (enabled) {
lv_btnmatrix_clear_btn_ctrl(s_key_matrix, i, LV_BTNMATRIX_CTRL_DISABLED);
} else {
lv_btnmatrix_set_btn_ctrl(s_key_matrix, i, LV_BTNMATRIX_CTRL_DISABLED);
}
}
}
}
static void build_entry_screen(void)
{
lv_obj_t *scr = lv_scr_act();
lv_obj_t *title;
lv_obj_t *close_btn;
lv_obj_t *close_lbl;
reset_screen_widget_refs();
lv_obj_clean(scr);
style_screen(scr);
clear_entry_state();
lv_obj_t *title = lv_label_create(scr);
title = lv_label_create(scr);
lv_label_set_text(title, "Enter Mnemonic");
lv_obj_set_style_text_font(title, &lv_font_montserrat_14, 0);
lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0);
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 6);
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 4);
s_entry_ta = lv_textarea_create(scr);
lv_obj_set_size(s_entry_ta, DISPLAY_WIDTH - 16, 70);
lv_obj_align(s_entry_ta, LV_ALIGN_TOP_MID, 0, 32);
lv_textarea_set_placeholder_text(s_entry_ta, "12 words separated by spaces");
lv_textarea_set_one_line(s_entry_ta, false);
style_text_area(s_entry_ta);
s_entry_words_label = lv_label_create(scr);
lv_obj_set_width(s_entry_words_label, DISPLAY_WIDTH - 16);
lv_label_set_long_mode(s_entry_words_label, LV_LABEL_LONG_WRAP);
lv_label_set_text(s_entry_words_label, "(no words selected)");
lv_obj_set_style_text_color(s_entry_words_label, lv_color_hex(0xFFFFFF), 0);
lv_obj_align(s_entry_words_label, LV_ALIGN_TOP_LEFT, 8, 26);
lv_obj_t *kb = lv_keyboard_create(scr);
lv_obj_set_size(kb, DISPLAY_WIDTH, 110);
lv_obj_align(kb, LV_ALIGN_BOTTOM_MID, 0, 0);
lv_keyboard_set_textarea(kb, s_entry_ta);
style_keyboard(kb);
lv_obj_t *use_btn = lv_btn_create(scr);
lv_obj_set_size(use_btn, 150, 30);
lv_obj_align(use_btn, LV_ALIGN_TOP_LEFT, 8, 110);
lv_obj_add_event_cb(use_btn, on_use_entry, LV_EVENT_RELEASED, NULL);
style_button(use_btn);
lv_obj_t *use_lbl = lv_label_create(use_btn);
lv_label_set_text(use_lbl, "Use Mnemonic");
lv_obj_center(use_lbl);
lv_obj_t *back_btn = lv_btn_create(scr);
lv_obj_set_size(back_btn, 150, 30);
lv_obj_align(back_btn, LV_ALIGN_TOP_RIGHT, -8, 110);
lv_obj_add_event_cb(back_btn, on_back_to_menu, LV_EVENT_RELEASED, NULL);
style_button(back_btn);
lv_obj_t *back_lbl = lv_label_create(back_btn);
lv_label_set_text(back_lbl, "Back");
lv_obj_center(back_lbl);
s_entry_prefix_label = lv_label_create(scr);
lv_label_set_text(s_entry_prefix_label, "Word 1/12 prefix: -");
lv_obj_set_style_text_color(s_entry_prefix_label, lv_color_hex(0xFFFFFF), 0);
lv_obj_align(s_entry_prefix_label, LV_ALIGN_TOP_LEFT, 8, 66);
s_status_label = lv_label_create(scr);
lv_label_set_text(s_status_label, "");
lv_obj_set_style_text_color(s_status_label, lv_color_hex(0xFF0000), 0);
lv_obj_align(s_status_label, LV_ALIGN_TOP_LEFT, 8, 144);
lv_obj_align(s_status_label, LV_ALIGN_TOP_LEFT, 8, 82);
s_suggestion_row = lv_obj_create(scr);
lv_obj_set_size(s_suggestion_row, DISPLAY_WIDTH - 12, 30);
lv_obj_align(s_suggestion_row, LV_ALIGN_TOP_MID, 0, 96);
lv_obj_set_style_bg_color(s_suggestion_row, lv_color_hex(0x000000), 0);
lv_obj_set_style_border_color(s_suggestion_row, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_border_width(s_suggestion_row, 1, 0);
lv_obj_set_style_pad_all(s_suggestion_row, 2, 0);
lv_obj_set_scroll_dir(s_suggestion_row, LV_DIR_HOR);
lv_obj_set_flex_flow(s_suggestion_row, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(s_suggestion_row, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
close_btn = lv_btn_create(scr);
lv_obj_set_size(close_btn, 42, 30);
lv_obj_align(close_btn, LV_ALIGN_TOP_RIGHT, -4, 2);
style_button(close_btn);
lv_obj_add_event_cb(close_btn, on_back_to_menu, LV_EVENT_CLICKED, NULL);
close_lbl = lv_label_create(close_btn);
lv_label_set_text(close_lbl, "X");
lv_obj_center(close_lbl);
s_key_matrix = lv_btnmatrix_create(scr);
lv_obj_set_size(s_key_matrix, DISPLAY_WIDTH - 16, 100);
lv_obj_align(s_key_matrix, LV_ALIGN_TOP_MID, 0, 132);
lv_btnmatrix_set_map(s_key_matrix, s_key_map);
lv_obj_add_event_cb(s_key_matrix, on_key_matrix, LV_EVENT_VALUE_CHANGED, NULL);
style_key_matrix(s_key_matrix);
lv_obj_set_style_pad_all(s_key_matrix, 0, 0);
lv_obj_set_style_pad_gap(s_key_matrix, 0, LV_PART_ITEMS);
lv_obj_set_style_border_width(s_key_matrix, 1, LV_PART_ITEMS);
refresh_entry_ui();
}
static void on_enter(lv_event_t *e)
@@ -316,6 +698,7 @@ static void on_use_viewed(lv_event_t *e)
static void build_view_screen(void)
{
lv_obj_t *scr = lv_scr_act();
reset_screen_widget_refs();
lv_obj_clean(scr);
style_screen(scr);
@@ -370,6 +753,7 @@ static void on_view(lv_event_t *e)
static void build_main_menu(void)
{
lv_obj_t *scr = lv_scr_act();
reset_screen_widget_refs();
lv_obj_clean(scr);
style_screen(scr);
@@ -424,6 +808,7 @@ void ui_show_approval_prompt(const char *caller_pubkey_hex, int kind, const char
}
lv_obj_t *scr = lv_scr_act();
reset_screen_widget_refs();
lv_obj_clean(scr);
style_screen(scr);
@@ -535,6 +920,7 @@ ui_approval_decision_t ui_wait_for_approval(uint32_t timeout_ms)
static void show_event_log_screen(void)
{
lv_obj_t *scr = lv_scr_act();
reset_screen_widget_refs();
lv_obj_clean(scr);
style_screen(scr);
@@ -579,6 +965,7 @@ void ui_show_idle(const char *npub_full, const char *mnemonic_full, const char *
}
lv_obj_t *scr = lv_scr_act();
reset_screen_widget_refs();
lv_obj_clean(scr);
style_screen(scr);

View File

@@ -0,0 +1,77 @@
// Proof-of-concept: KB2040 as composite HID + WebUSB using Adafruit TinyUSB
//
// Board/IDE requirements:
// - Board: Adafruit KB2040 (RP2040)
// - Tools -> USB Stack: Adafruit TinyUSB
// - Library: Adafruit TinyUSB Library
#include <Adafruit_TinyUSB.h>
// HID report descriptor: Consumer Control for media keys
static const uint8_t desc_hid_report[] = {
TUD_HID_REPORT_DESC_CONSUMER(HID_REPORT_ID(1))
};
Adafruit_USBD_HID usb_hid;
Adafruit_USBD_WebUSB usb_web;
WEBUSB_URL_DEF(landing_url, 1 /* https */, "example.com/nsigner/kb2040");
static uint32_t last_hid_ms = 0;
static uint8_t rx_buf[64];
void setup() {
// WebUSB vendor interface
usb_web.setLandingPage(&landing_url);
usb_web.setStringDescriptor("n_signer WebUSB");
usb_web.begin();
// HID interface (media keys)
usb_hid.setPollInterval(2);
usb_hid.setReportDescriptor(desc_hid_report, sizeof(desc_hid_report));
usb_hid.setStringDescriptor("KB2040 Media");
usb_hid.begin();
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
// Wait for host enumeration
while (!TinyUSBDevice.mounted()) {
delay(1);
}
digitalWrite(LED_BUILTIN, HIGH);
}
void loop() {
// Demonstrate HID activity: send volume-up every 5 seconds
if (millis() - last_hid_ms >= 5000) {
last_hid_ms = millis();
if (usb_hid.ready()) {
uint16_t key = HID_USAGE_CONSUMER_VOLUME_INCREMENT;
usb_hid.sendReport16(1, key);
delay(30);
usb_hid.sendReport16(1, 0);
digitalWrite(LED_BUILTIN, LOW);
delay(80);
digitalWrite(LED_BUILTIN, HIGH);
}
}
// Demonstrate WebUSB activity: echo back received bytes with prefix
if (usb_web.available()) {
uint32_t n = usb_web.read(rx_buf, sizeof(rx_buf));
if (n > 0) {
usb_web.write((const uint8_t *)"KB2040:", 7);
usb_web.write(rx_buf, n);
usb_web.flush();
for (int i = 0; i < 2; i++) {
digitalWrite(LED_BUILTIN, LOW);
delay(50);
digitalWrite(LED_BUILTIN, HIGH);
delay(50);
}
}
}
}

View File

@@ -0,0 +1,340 @@
# KB2040 Hidden Signer — Display Screens
Reference for the on-device UI so we can iterate on screen designs.
- Panel: **SSD1327 128x128 grayscale OLED** (I2C, STEMMA QT, addr `0x3D`).
- Rendering source: [`display.cpp`](./display.cpp), driven by call sites in [`kb2040_hidden_signer.ino`](./kb2040_hidden_signer.ino).
- Grayscale levels in use:
- dim (headers / outlines): `0x04`
- bar fill: `0x08`
- bright text: `0x0F`
- Each screen historically ended with a bottom **status line** at `y=118`, drawn by `drawStatusLine()`. The status-line writes are currently commented out in the sketch (they were being overwritten by debug/diagnostic prints), so that line now stays effectively empty unless re-enabled.
The boxes below are schematic (the panel is 128x128 px, not character-aligned). They show layout intent, relative position, and the text/labels actually printed.
---
## 1. Volume View
Source: `drawVolumeView()` in [`display.cpp`](./display.cpp:60).
Top-level "media mode" screen. Header switches between `MODE: MEDIA` / `MODE: SIGNER`.
```
+--------------------------------+
| MODE: MEDIA | <- tiny, dim (size 1)
| |
| VOL | <- size 2, dim
| |
| |
| #### #### |
| ## ## # ## | <- "50" centered, size 6
| ## ## # ## | (huge digits)
| ## ## # ## |
| #### #### |
| |
| +------------------------+ | <- volume bar outline
| |########## | | fill = volume %
| +------------------------+ |
| |
| [status line] | <- currently disabled
+--------------------------------+
```
---
## 2. Signer Menu
Source: `drawSignerMenu()` in [`display.cpp`](./display.cpp:102).
Two items; `>` marks the current selection (shown here on index 0).
```
+--------------------------------+
| SIGNER MENU | <- size 1, dim
| |
| |
| > Generate new | <- size 2, selected
| |
| |
| Enter mnemonic | <- size 2
| |
| |
| |
| |
| [status line] | <- currently disabled
+--------------------------------+
```
---
## 3. Enter Mnemonic (in-place 2-column grid)
Source (planned): `drawEnterMnemonic()` in [`display.cpp`](./display.cpp).
Replaces the old separate `PICK FIRST LETTER` modal + `WORD PICK` modal with a
single grid that looks like the Generated Phrase screen but is filled in by the
user. Header reads `ENTER MNEMONIC`.
Layout: 2 columns x 6 rows (slots 1-6 left, 7-12 right), same geometry as the
Generated Phrase screen. All 12 numbers are shown; words are blank until entered.
A cursor marks the active slot (starts at slot 1).
```
+--------------------------------+
| ENTER MNEMONIC | <- size 1, dim
|> 1 m 7 | <- cursor '>' on active slot
| 2 8 | slot 1 building: "m"
| 3 9 |
| 4 10 |
| 5 11 |
| 6 12 |
| |
| back letter | <- footer legend (stage-dependent)
+--------------------------------+
```
### Per-word 3-stage entry (in the active slot)
Only the letters chosen so far are shown; a full word appears only at stage 3.
| Stage | Scroll wheel cycles | Active slot shows | PLAY does |
| ----- | ---------------------------------- | ----------------- | -------------------------- |
| 1 | first letter `a`..`z` | `m` | lock letter 1 -> stage 2 |
| 2 | valid 2nd letters for letter 1 | `mo` | lock letter 2 -> stage 3 |
| 3 | words matching first two letters | `mosaic` | select word -> next slot |
- After slot 12's word is selected: build phrase, run
`mnemonic_validate_basic()`, then go to Signer Ready (or INVALID modal).
### BACK behavior (context-sensitive erase)
`BACK` = the NEXT button (PREV stays unused here), consistent with other entry
screens.
| Position | BACK action |
| -------------------------------- | --------------------------------------------- |
| Stage 3 (choosing word) | erase 2nd letter -> return to stage 2 |
| Stage 2 (choosing 2nd letter) | erase 1st letter -> return to stage 1 |
| Stage 1, slot N > 1 | clear previous slot's word, move cursor back to slot N-1 at stage 1 |
| Stage 1, slot 1 | exit to Signer Menu |
### Footer legend (stage-dependent)
| Stage | PREV (left) | PLAY (center) | NEXT (right) |
| ------- | ----------- | ------------- | ------------ |
| Stage 1 | `-` | `letter` | `back` |
| Stage 2 | `-` | `letter` | `back` |
| Stage 3 | `-` | `select` | `back` |
### New wordlist helpers required (in `mnemonic_kb.cpp` / `.h`)
- `mnemonic_second_letters(char first, char *out, uint8_t *count)`
-> set of valid 2nd letters given the 1st letter.
- `mnemonic_prefix_range(char first, char second, uint16_t *start, uint16_t *count)`
-> wordlist index range for all words starting with those two letters.
(`mnemonic_letter_range()` already provides the first-letter range and can back
the stage-1 -> stage-2 transition.)
---
## 4. Mnemonic Page (Generated Phrase)
Source: `drawMnemonicPage()` in [`display.cpp`](./display.cpp:152).
Shows generated phrase in one screen: 2 columns × 6 rows (all 12 words, no spacer rows).
```
+--------------------------------+
| GENERATED PHRASE | <- size 1, dim
| 1 abandon 7 issue | <- size 1, tight rows
| 2 ability 8 jungle |
| 3 able 9 kitchen |
| 4 about 10 lemon |
| 5 above 11 mosquito |
| 6 absent 12 napkin |
| |
| PLAY confirm NEXT cancel | <- size 1
| |
| [status line] | <- currently disabled
+--------------------------------+
```
---
## 5. Message (generic 2-line modal)
Source: `drawMessage()` in [`display.cpp`](./display.cpp:193).
Generic title + 2 lines. Reused for several prompts.
Example — **Approval prompt** (title `APPROVAL`):
```
+--------------------------------+
| APPROVAL | <- size 1, dim
| |
| |
| PLAY=Allow | <- size 2
| |
| PREV=Deny | <- size 2
| |
| |
| |
| |
| |
| [status line] | <- currently disabled
+--------------------------------+
```
Other current uses of this same screen:
- `PICK FIRST LETTER` with lines `First letter` / `Letter: a`
- `INVALID` with `Bad mnemonic` / `NEXT to return`
- `ERROR` with `Generate failed` / `Try again`
---
## 6. Signer Ready
Source: `drawSignerReady()` in [`display.cpp`](./display.cpp:210).
Shown while waiting for the host to request signing.
```
+--------------------------------+
| SIGNER READY | <- size 1, dim
| |
| +------------------------+ |
| | Awaiting | | <- size 2
| | | |
| | host | | <- size 2
| +------------------------+ |
| |
| NEXT/PLAY: menu | <- size 1
| |
| [status line] | <- currently disabled
+--------------------------------+
```
---
## Notes / observations for redesign
- The bottom status line frequently **duplicated** the in-body hint text (e.g. Word Pick showed `ENC scroll PLAY select` both in the body and in the status line). With status writes disabled, that redundancy is gone.
- `PICK FIRST LETTER` reuses the generic Message screen rather than having a dedicated layout — a candidate for a purpose-built screen.
- All hint text (`ENC` / `PLAY` / `NEXT` / `PREV`) refers to the physical controls: rotary encoder + the PLAY/NEXT/PREV buttons.
```
---
## Screen ↔ render-function map
| Screen | Render function | display.cpp |
| --------------- | --------------------- | ----------- |
| Volume View | `drawVolumeView()` | [60](./display.cpp:60) |
| Signer Menu | `drawSignerMenu()` | [102](./display.cpp:102) |
| Word Pick | `drawWordPick()` | [123](./display.cpp:123) |
| Mnemonic Page | `drawMnemonicPage()` | [152](./display.cpp:152) |
| Message (modal) | `drawMessage()` | [193](./display.cpp:193) |
| Signer Ready | `drawSignerReady()` | [210](./display.cpp:210) |
| Status line | `drawStatusLine()` | [53](./display.cpp:53) |
---
# PROPOSAL: three-zone button legend footer
The three physical buttons sit directly below the screen in this order:
```
PREV PLAY/PAUSE NEXT
^ ^ ^
(left) (center) (right)
```
Idea: replace the free-text status line with a fixed **3-zone legend** along the
bottom of every screen. Each zone shows a single short verb for what that button
does on the current screen — left-aligned, center-aligned, right-aligned so each
label sits over its physical button. No need to print the button name; position
implies it. An empty zone means that button does nothing on that screen.
### Layout math (size 1 font, 6 px/char on a 128 px panel)
- Left zone: left-aligned at `x = 2`.
- Center zone: centered around `x = 64` (use `getTextBounds` to center).
- Right zone: right-aligned to `x = 126` (compute `x = 126 - width`).
- Budget ~7 chars per zone worst case (7 x 6 = 42 px x 3 = 126 px). Keep verbs
<= 7 chars (`select`, `confirm`, `cancel`, `back`, `deny`, `allow`, `next`,
`prev`) to guarantee no overlap.
- Optional: a thin divider line above the legend at `y ~= 112`, and 1 px vertical
ticks between zones to reinforce the 3-button mapping.
```
+--------------------------------+
| ...screen body... |
| |
| -------------------------------| <- optional divider (y~112)
| prev confirm next | <- 3 zones (left/center/right)
+--------------------------------+
^PREV ^PLAY ^NEXT (physical buttons below glass)
```
### Per-screen legend mapping (derived from `signer_mode_tick()`)
Encoder still scrolls/selects on every screen; the legend only describes the 3
buttons. `-` = button does nothing on that screen.
| Screen / state | PREV (left) | PLAY (center) | NEXT (right) |
| -------------------------------------- | ----------- | ------------- | ------------ |
| Volume / Media view | `prev` | `play` | `next` |
| Signer Menu (`SIGNER_UI_MENU`) | `-` | `select` | `-` |
| Generated Phrase (`GENERATE_REVIEW`) | `cancel` | `confirm` | `new` |
| Signer Ready (`READY`) | `-` | `-` | `menu` |
| Letter Pick (`ENTER_LETTER`) | `-` | `confirm` | `back` |
| Word Pick (`ENTER_PICK`) | `-` | `select` | `back` |
| Approval prompt (Message modal) | `deny` | `allow` | `-` |
Notes:
- `PREV` is currently unused in most signer entry screens; it is meaningful in
Media view (`prev` track), the Approval prompt (`deny`), and now the Generated
Phrase screen (`cancel`).
### Behavior change required: Generated Phrase (`SIGNER_UI_GENERATE_REVIEW`)
The legend wording above implies a **functional change** in
[`signer_mode_tick()`](./kb2040_hidden_signer.ino:657), not just labels:
| Button | Current behavior | Proposed behavior |
| ------ | --------------------------- | ------------------------------------------ |
| PREV | (unused) | `cancel` -> return to Signer Menu |
| PLAY | confirm -> save seed | `confirm` -> save seed (unchanged) |
| NEXT | cancel -> Signer Menu | `new` -> regenerate a fresh 12-word phrase and refresh this screen |
Implementation detail for `new`:
- On `s_evt_next_pressed`, call `generate_mnemonic_12(s_generated_mnemonic, ...)`
again, then `signer_enter_review_screen()` to redraw with the new words.
- On `s_evt_prev_pressed`, call `signer_enter_menu_screen()` (the old NEXT path).
> Dependency check (resolved): `s_evt_prev_pressed` already exists and is wired
> the same way as PLAY/NEXT — declared at [line 112](./kb2040_hidden_signer.ino:112),
> reset in [`update_button_events()`](./kb2040_hidden_signer.ino:560), set on PREV
> press at [line 581](./kb2040_hidden_signer.ino:581). No new plumbing needed.
### What this replaces on each screen
- Word Pick body lines `ENC scroll PLAY select` / `NEXT back` -> removed; the
encoder action stays implicit, and PLAY/NEXT move to the footer legend.
- Mnemonic Page body line `PLAY confirm NEXT cancel` -> moves to the footer
legend (frees a body row).
- Signer Ready body line `NEXT/PLAY: menu` -> becomes a single `menu` in the
NEXT zone.
- Approval modal `PLAY=Allow` / `PREV=Deny` body lines -> footer legend
(`deny` left / `allow` center).
### Implementation sketch
1. Add a helper `drawButtonLegend(const char *left, const char *center, const char *right)`
in [`display.cpp`](./display.cpp) that renders the 3 aligned zones (plus the
optional divider/ticks) at the bottom.
2. Give each screen a small static legend (or pass it in) and call
`drawButtonLegend(...)` instead of `drawStatusLine()`.
3. Remove the now-redundant in-body hint rows listed above.
4. Keep `drawStatusLine()` available (commented/disabled) for transient debug.
### Open design decisions (need your call)
1. **NEXT "back" wording**: use the word `back` vs a glyph like `<` / `<<`.
2. **Divider/ticks**: include the thin divider line + inter-zone ticks, or keep
it text-only for a cleaner look.
3. **Empty zones**: render nothing, or a subtle `·` placeholder so the user can
tell the button is intentionally inactive.
4. **Media view**: apply the same legend (`prev` / `play` / `next`), or leave the
Volume view visually as-is since its meaning is already obvious.

View File

@@ -0,0 +1,100 @@
# KB2040 Hidden Signer (HID + WebUSB)
This directory contains an Arduino firmware scaffold that implements the `kb2040_hidden_signer` plan on top of a TinyUSB composite device.
- Firmware: [`kb2040_hidden_signer.ino`](./kb2040_hidden_signer.ino)
- Host client: [`examples/kb2040_hidden_signer_client.py`](../../examples/kb2040_hidden_signer_client.py)
## What is implemented
1. Composite USB device with two active interfaces:
- HID Consumer Control (media keys)
- Vendor/WebUSB transport (framed JSON)
2. Default media mode:
- Play/Pause, Next, Previous buttons emit HID consumer usages
- Rotary encoder emits volume up/down
3. Hidden mode toggle:
- Hold PLAY + NEXT for 3 seconds to toggle media/signer mode
4. WebUSB framed transport:
- 4-byte big-endian length + JSON payload
5. Signer RPC scaffold methods:
- `ping`
- `get_status`
- `set_mnemonic`
- `set_auto_approve`
- `get_public_key` (deterministic placeholder pubkey)
- `sign_event` (approval gate + placeholder signature)
## What remains to reach production n_signer parity
1. Replace placeholder crypto with real `n_signer` core integration:
- mnemonic -> seed -> key derivation
- secp256k1 schnorr signing
- full auth envelope verification
2. Replace naive JSON parsing with robust parsing (e.g. `ArduinoJson` or cJSON port).
3. Wire exact `keyboard_modules` pin mapping and display abstraction.
4. Add secure memory wiping and persistent policy storage if required.
## Arduino setup (IDE)
1. Install Adafruit board index in Arduino IDE.
2. Install **Adafruit RP2040** boards package.
3. Select board **Adafruit KB2040**.
4. Set **Tools -> USB Stack -> Adafruit TinyUSB**.
5. Install library **Adafruit TinyUSB Library**.
6. Open and flash [`kb2040_hidden_signer.ino`](./kb2040_hidden_signer.ino).
## Arduino CLI flashing (recommended repeatable command)
Use this exact command when flashing from CLI:
```bash
arduino-cli compile --upload -p /dev/ttyACM0 \
--fqbn rp2040:rp2040:adafruit_kb2040:usbstack=tinyusb \
firmware/kb2040_hidden_signer
```
Notes:
- `usbstack=tinyusb` is **required** for this firmware (TinyUSB composite device).
- If you omit that option, build fails with `TinyUSB is not selected`.
- If `/dev/ttyACM0` changes, find the current port with:
```bash
arduino-cli board list
```
## Host validation
Install dependency:
```bash
pip install pyusb
```
Basic dual-interface verification:
```bash
python3 tests/test_kb2040_dual.py
```
Signer RPC flow (WebUSB/vendor interface):
```bash
python3 examples/kb2040_hidden_signer_client.py status
python3 examples/kb2040_hidden_signer_client.py set-mnemonic --mnemonic "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
python3 examples/kb2040_hidden_signer_client.py get-public-key
python3 examples/kb2040_hidden_signer_client.py sign-event --event '{"kind":1,"content":"hello"}'
```
## Pin mapping
Current defaults in [`kb2040_hidden_signer.ino`](./kb2040_hidden_signer.ino):
- `PIN_BTN_PLAY = 2`
- `PIN_BTN_NEXT = 3`
- `PIN_BTN_PREV = 4`
- `PIN_ENC_A = 5`
- `PIN_ENC_B = 6`
- `PIN_ENC_SW = 7`
Update these constants to match your physical `keyboard_modules` build.

View File

@@ -0,0 +1,467 @@
#include "display.h"
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1327.h>
namespace {
static constexpr uint8_t kDisplayWidth = 128;
static constexpr uint8_t kDisplayHeight = 128;
static constexpr uint8_t kDefaultI2cAddr = 0x3D;
static constexpr uint8_t kGrayDim = 0x04;
static constexpr uint8_t kGrayBar = 0x08;
static constexpr uint8_t kGrayText = 0x0F;
Adafruit_SSD1327 g_display(kDisplayWidth, kDisplayHeight, &Wire, -1, 400000, 100000);
bool g_displayPresent = false;
bool g_dirty = true;
display_view_t g_view = DISPLAY_VIEW_VOLUME;
uint8_t g_volume = 50;
bool g_signerMode = false;
char g_status[24] = "BOOT";
uint8_t g_menuSelected = 0;
uint8_t g_mnemonicPage = 0;
char g_mnemonic[256] = "";
char g_msgTitle[18] = "";
char g_msgLine1[22] = "";
char g_msgLine2[22] = "";
char g_enterSlots[12][16] = {{0}};
uint8_t g_enterActiveSlot = 0;
uint8_t g_enterStage = 1;
char g_enterBuilding[16] = "";
uint8_t g_enterCursorPos = 0;
enum signer_screen_t {
SIGNER_SCREEN_MENU = 0,
SIGNER_SCREEN_ENTER_GRID = 1,
SIGNER_SCREEN_MNEMONIC_PAGE = 2,
SIGNER_SCREEN_MESSAGE = 3,
SIGNER_SCREEN_READY = 4,
};
signer_screen_t g_signerScreen = SIGNER_SCREEN_MENU;
uint8_t clampVolume(uint8_t v) {
return (v > 100) ? 100 : v;
}
void drawStatusLine() {
g_display.setTextSize(1);
g_display.setTextColor(kGrayText);
g_display.setCursor(2, 118);
g_display.print(g_status);
}
void drawButtonLegend(const char *left, const char *center, const char *right) {
g_display.setTextSize(1);
g_display.setTextColor(kGrayText);
g_display.setTextWrap(false);
if (left && left[0] != '\0') {
g_display.setCursor(2, 118);
g_display.print(left);
}
int16_t x1 = 0;
int16_t y1 = 0;
uint16_t w = 0;
uint16_t h = 0;
if (center && center[0] != '\0') {
g_display.getTextBounds(center, 0, 0, &x1, &y1, &w, &h);
const int16_t x = static_cast<int16_t>((kDisplayWidth - w) / 2);
g_display.setCursor(x, 118);
g_display.print(center);
}
if (right && right[0] != '\0') {
g_display.getTextBounds(right, 0, 0, &x1, &y1, &w, &h);
const int16_t x = static_cast<int16_t>(126 - w);
g_display.setCursor(x, 118);
g_display.print(right);
}
}
void drawVolumeView() {
g_display.setTextWrap(false);
g_display.setTextColor(kGrayDim);
g_display.setTextSize(2);
g_display.setCursor(40, 18);
g_display.print("VOL");
char volBuf[4];
snprintf(volBuf, sizeof(volBuf), "%u", g_volume);
int16_t x1 = 0;
int16_t y1 = 0;
uint16_t w = 0;
uint16_t h = 0;
g_display.setTextSize(6);
g_display.getTextBounds(volBuf, 0, 0, &x1, &y1, &w, &h);
const int16_t x = static_cast<int16_t>((kDisplayWidth - w) / 2);
const int16_t y = 42;
g_display.setTextColor(kGrayText);
g_display.setCursor(x, y);
g_display.print(volBuf);
const int16_t barX = 8;
const int16_t barY = 102;
const int16_t barW = kDisplayWidth - 16;
const int16_t barH = 8;
g_display.drawRect(barX, barY, barW, barH, kGrayDim);
const int16_t fillW = static_cast<int16_t>((static_cast<uint32_t>(barW - 2) * g_volume) / 100U);
if (fillW > 0) {
g_display.fillRect(barX + 1, barY + 1, fillW, barH - 2, kGrayBar);
}
drawButtonLegend("prev", "play", "next");
}
void drawSignerMenu() {
g_display.setTextWrap(false);
g_display.setTextSize(1);
g_display.setTextColor(kGrayDim);
g_display.setCursor(2, 2);
g_display.print("SIGNER MENU");
const char *items[2] = {"Generate new", "Enter mnemonic"};
g_display.setTextColor(kGrayText);
g_display.setTextSize(2);
for (uint8_t i = 0; i < 2; ++i) {
g_display.setCursor(6, 28 + i * 24);
g_display.print((i == g_menuSelected) ? ">" : " ");
g_display.setCursor(20, 28 + i * 24);
g_display.print(items[i]);
}
drawButtonLegend("", "select", "");
}
void drawEnterMnemonic() {
g_display.setTextWrap(false);
g_display.setTextSize(1);
g_display.setTextColor(kGrayDim);
g_display.setCursor(2, 2);
g_display.print("ENTER MNEMONIC");
g_display.setTextColor(kGrayText);
for (uint8_t i = 0; i < 12; ++i) {
const uint8_t row = (i < 6) ? i : (i - 6);
const int16_t x = (i < 6) ? 0 : 64;
const int16_t y = static_cast<int16_t>(18 + row * 9);
const bool isActive = (i == g_enterActiveSlot);
const char cursor = isActive ? '>' : ' ';
const char *slotWord = g_enterSlots[i];
if (!slotWord) {
slotWord = "";
}
char activeBuf[16];
const char *shownWord = slotWord;
if (isActive) {
const char *src = g_enterBuilding;
if (!src) {
src = "";
}
if (g_enterStage < 3) {
size_t srcLen = strlen(src);
if (srcLen > 14) {
srcLen = 14;
}
size_t pos = g_enterCursorPos;
if (pos > srcLen) {
pos = srcLen;
}
size_t out = 0;
for (size_t j = 0; j < pos && out < sizeof(activeBuf) - 1; ++j) {
activeBuf[out++] = src[j];
}
if (out < sizeof(activeBuf) - 1) {
activeBuf[out++] = '_';
}
for (size_t j = pos; j < srcLen && out < sizeof(activeBuf) - 1; ++j) {
activeBuf[out++] = src[j];
}
activeBuf[out] = '\0';
shownWord = activeBuf;
} else if (src[0] != '\0') {
shownWord = src;
}
}
char lineBuf[22];
snprintf(lineBuf, sizeof(lineBuf), "%c%2u %s", cursor, (unsigned)(i + 1), shownWord);
g_display.setCursor(x, y);
g_display.print(lineBuf);
}
if (g_enterStage == 3) {
drawButtonLegend("", "select", "back");
} else {
drawButtonLegend("", "letter", "back");
}
}
void drawMnemonicPage() {
g_display.setTextWrap(false);
g_display.setTextSize(1);
g_display.setTextColor(kGrayDim);
g_display.setCursor(2, 2);
g_display.print("GENERATED PHRASE");
char copy[256];
strncpy(copy, g_mnemonic, sizeof(copy) - 1);
copy[sizeof(copy) - 1] = '\0';
char *words[12] = {nullptr};
uint8_t count = 0;
char *saveptr = nullptr;
char *tok = strtok_r(copy, " ", &saveptr);
while (tok && count < 12) {
words[count++] = tok;
tok = strtok_r(nullptr, " ", &saveptr);
}
g_display.setTextColor(kGrayText);
for (uint8_t i = 0; i < count; ++i) {
const uint8_t row = (i < 6) ? i : (i - 6);
const int16_t x = (i < 6) ? 0 : 64;
const int16_t y = static_cast<int16_t>(18 + row * 9);
char lineBuf[20];
snprintf(lineBuf, sizeof(lineBuf), "%2u %s", (unsigned)(i + 1), words[i]);
g_display.setCursor(x, y);
g_display.print(lineBuf);
}
drawButtonLegend("cancel", "confirm", "new");
}
void drawMessage() {
g_display.setTextWrap(false);
g_display.setTextSize(1);
g_display.setTextColor(kGrayDim);
g_display.setCursor(2, 2);
g_display.print(g_msgTitle);
g_display.setTextSize(2);
g_display.setTextColor(kGrayText);
g_display.setCursor(6, 34);
g_display.print(g_msgLine1);
g_display.setCursor(6, 62);
g_display.print(g_msgLine2);
const char *left = "";
const char *center = "";
const char *right = "";
if (strcmp(g_msgTitle, "APPROVAL") == 0) {
left = "deny";
center = "allow";
} else if (strcmp(g_msgTitle, "INVALID") == 0) {
center = "select";
right = "back";
} else if (strcmp(g_msgTitle, "ERROR") == 0) {
center = "select";
}
drawButtonLegend(left, center, right);
}
void drawSignerReady() {
g_display.setTextWrap(false);
g_display.setTextSize(1);
g_display.setTextColor(kGrayDim);
g_display.setCursor(2, 2);
g_display.print("SIGNER READY");
g_display.drawRect(4, 22, 120, 62, kGrayDim);
g_display.setTextSize(2);
g_display.setTextColor(kGrayText);
g_display.setCursor(16, 36);
g_display.print("Awaiting");
g_display.setCursor(30, 58);
g_display.print("host");
drawButtonLegend("", "", "menu");
}
void renderFrame() {
if (!g_displayPresent) {
return;
}
g_display.clearDisplay();
if (g_view == DISPLAY_VIEW_VOLUME) {
drawVolumeView();
} else {
switch (g_signerScreen) {
case SIGNER_SCREEN_MENU:
drawSignerMenu();
break;
case SIGNER_SCREEN_ENTER_GRID:
drawEnterMnemonic();
break;
case SIGNER_SCREEN_MNEMONIC_PAGE:
drawMnemonicPage();
break;
case SIGNER_SCREEN_MESSAGE:
drawMessage();
break;
case SIGNER_SCREEN_READY:
drawSignerReady();
break;
default:
drawMessage();
break;
}
}
g_display.display();
}
void copy_text(char *dst, size_t dst_sz, const char *src) {
if (!dst || dst_sz == 0) {
return;
}
if (!src) {
dst[0] = '\0';
return;
}
strncpy(dst, src, dst_sz - 1);
dst[dst_sz - 1] = '\0';
}
} // namespace
void display_init() {
Wire.setClock(400000);
Wire.begin();
Wire.beginTransmission(kDefaultI2cAddr);
const uint8_t probeErr = Wire.endTransmission();
if (probeErr != 0) {
g_displayPresent = false;
return;
}
g_displayPresent = g_display.begin(kDefaultI2cAddr, true);
if (!g_displayPresent) {
return;
}
g_dirty = true;
renderFrame();
}
bool display_is_present() {
return g_displayPresent;
}
void display_set_view(display_view_t view) {
if (g_view == view) {
return;
}
g_view = view;
g_dirty = true;
}
void display_set_mode(bool signer_mode) {
if (g_signerMode == signer_mode) {
return;
}
g_signerMode = signer_mode;
g_dirty = true;
}
void display_set_volume(uint8_t volume) {
const uint8_t v = clampVolume(volume);
if (v == g_volume) {
return;
}
g_volume = v;
g_dirty = true;
}
void display_show_signer_menu(uint8_t selected_index) {
g_signerScreen = SIGNER_SCREEN_MENU;
g_menuSelected = selected_index > 1 ? 1 : selected_index;
g_dirty = true;
}
void display_show_enter_mnemonic(const char *const slots[12], uint8_t active_slot, uint8_t stage,
const char *building_text, uint8_t cursor_pos) {
g_signerScreen = SIGNER_SCREEN_ENTER_GRID;
g_enterActiveSlot = (active_slot > 11) ? 11 : active_slot;
g_enterStage = stage;
g_enterCursorPos = cursor_pos;
copy_text(g_enterBuilding, sizeof(g_enterBuilding), building_text);
for (uint8_t i = 0; i < 12; ++i) {
const char *src = "";
if (slots && slots[i]) {
src = slots[i];
}
copy_text(g_enterSlots[i], sizeof(g_enterSlots[i]), src);
}
g_dirty = true;
}
void display_show_mnemonic_page(const char *mnemonic, uint8_t page_index) {
g_signerScreen = SIGNER_SCREEN_MNEMONIC_PAGE;
g_mnemonicPage = page_index;
copy_text(g_mnemonic, sizeof(g_mnemonic), mnemonic);
g_dirty = true;
}
void display_show_message(const char *title, const char *line1, const char *line2) {
g_signerScreen = SIGNER_SCREEN_MESSAGE;
copy_text(g_msgTitle, sizeof(g_msgTitle), title);
copy_text(g_msgLine1, sizeof(g_msgLine1), line1);
copy_text(g_msgLine2, sizeof(g_msgLine2), line2);
g_dirty = true;
}
void display_show_signer_ready() {
g_signerScreen = SIGNER_SCREEN_READY;
g_dirty = true;
}
// Note: status-line updates are currently disabled at call sites in
// kb2040_hidden_signer.ino by commenting out display_set_status(...)
// invocations to keep the bottom status line from being overwritten.
void display_set_status(const char *status) {
if (!status) {
return;
}
copy_text(g_status, sizeof(g_status), status);
g_dirty = true;
}
void display_tick() {
if (!g_displayPresent || !g_dirty) {
return;
}
renderFrame();
g_dirty = false;
}

View File

@@ -0,0 +1,38 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
enum display_view_t {
DISPLAY_VIEW_VOLUME = 0,
DISPLAY_VIEW_SIGNER = 1,
};
// Initialize the SSD1327 OLED on KB2040 STEMMA QT (Wire = Wire0, GPIO12/13).
void display_init();
// Returns true when the display was detected and initialized successfully.
bool display_is_present();
// Select top-level display view.
void display_set_view(display_view_t view);
// Set current mode text for volume view header.
void display_set_mode(bool signer_mode);
// Set local volume value shown on-screen. Input is clamped to 0..100.
void display_set_volume(uint8_t volume);
// Signer UI rendering helpers.
void display_show_signer_menu(uint8_t selected_index);
void display_show_enter_mnemonic(const char *const slots[12], uint8_t active_slot, uint8_t stage,
const char *building_text, uint8_t cursor_pos);
void display_show_mnemonic_page(const char *mnemonic, uint8_t page_index);
void display_show_message(const char *title, const char *line1, const char *line2);
void display_show_signer_ready();
// Set short status text line (truncated to fit).
void display_set_status(const char *status);
// Non-blocking tick; renders only when state changed.
void display_tick();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,246 @@
#include "mnemonic_kb.h"
#include <Arduino.h>
#include <string.h>
#include "mnemonic_wordlist.h"
namespace {
static bool s_rng_seeded = false;
void ensure_rng_seeded() {
if (s_rng_seeded) {
return;
}
uint32_t seed = micros() ^ (millis() << 16);
for (int i = 0; i < 16; ++i) {
seed ^= ((uint32_t)analogRead(A0) & 0x3FFu) << (i % 22);
delayMicroseconds(50);
}
randomSeed(seed);
s_rng_seeded = true;
}
bool append_word(char* out, size_t out_len, size_t* cursor, const char* word) {
const size_t wlen = strlen(word);
if (*cursor >= out_len) {
return false;
}
if (*cursor != 0) {
if (*cursor + 1 >= out_len) {
return false;
}
out[*cursor] = ' ';
(*cursor)++;
}
if (*cursor + wlen >= out_len) {
return false;
}
memcpy(out + *cursor, word, wlen);
*cursor += wlen;
out[*cursor] = '\0';
return true;
}
} // namespace
bool mnemonic_letter_range(char letter, uint16_t *out_start, uint16_t *out_count) {
if (!out_start || !out_count) {
return false;
}
if (letter >= 'A' && letter <= 'Z') {
letter = (char)(letter - 'A' + 'a');
}
if (letter < 'a' || letter > 'z') {
return false;
}
int first = -1;
int last = -1;
for (int i = 0; i < 2048; ++i) {
const char c = BIP39_WORDLIST[i][0];
if (c == letter) {
if (first < 0) first = i;
last = i;
} else if (first >= 0) {
break;
}
}
if (first < 0 || last < first) {
return false;
}
*out_start = (uint16_t)first;
*out_count = (uint16_t)(last - first + 1);
return true;
}
bool mnemonic_second_letters(char first, char *out, uint8_t *count) {
if (!out || !count) {
return false;
}
*count = 0;
if (first >= 'A' && first <= 'Z') {
first = (char)(first - 'A' + 'a');
}
if (first < 'a' || first > 'z') {
return false;
}
uint16_t start = 0;
uint16_t range_count = 0;
if (!mnemonic_letter_range(first, &start, &range_count) || range_count == 0) {
return false;
}
bool seen[26] = {false};
uint8_t found = 0;
for (uint16_t i = 0; i < range_count; ++i) {
const char* w = BIP39_WORDLIST[start + i];
char c = w[1];
if (c >= 'A' && c <= 'Z') {
c = (char)(c - 'A' + 'a');
}
if (c < 'a' || c > 'z') {
continue;
}
const uint8_t idx = (uint8_t)(c - 'a');
if (!seen[idx]) {
seen[idx] = true;
out[found++] = c;
}
}
*count = found;
return found > 0;
}
bool mnemonic_prefix_range(char first, char second, uint16_t *out_start, uint16_t *out_count) {
if (!out_start || !out_count) {
return false;
}
if (first >= 'A' && first <= 'Z') {
first = (char)(first - 'A' + 'a');
}
if (second >= 'A' && second <= 'Z') {
second = (char)(second - 'A' + 'a');
}
if (first < 'a' || first > 'z' || second < 'a' || second > 'z') {
return false;
}
uint16_t start = 0;
uint16_t range_count = 0;
if (!mnemonic_letter_range(first, &start, &range_count) || range_count == 0) {
return false;
}
int first_match = -1;
int last_match = -1;
for (uint16_t i = 0; i < range_count; ++i) {
const char* w = BIP39_WORDLIST[start + i];
char c = w[1];
if (c >= 'A' && c <= 'Z') {
c = (char)(c - 'A' + 'a');
}
if (c == second) {
if (first_match < 0) {
first_match = (int)(start + i);
}
last_match = (int)(start + i);
} else if (first_match >= 0) {
break;
}
}
if (first_match < 0 || last_match < first_match) {
return false;
}
*out_start = (uint16_t)first_match;
*out_count = (uint16_t)(last_match - first_match + 1);
return true;
}
const char* mnemonic_word_at(uint16_t index) {
if (index >= 2048) {
return nullptr;
}
return BIP39_WORDLIST[index];
}
int mnemonic_word_index(const char* word) {
if (!word || !word[0]) {
return -1;
}
for (int i = 0; i < 2048; ++i) {
if (strcmp(word, BIP39_WORDLIST[i]) == 0) {
return i;
}
}
return -1;
}
int generate_mnemonic_12(char* out, size_t out_len) {
if (!out || out_len == 0) {
return -1;
}
out[0] = '\0';
ensure_rng_seeded();
size_t cursor = 0;
for (int i = 0; i < 12; ++i) {
const uint16_t idx = (uint16_t)random(0, 2048);
const char* word = mnemonic_word_at(idx);
if (!word) {
return -1;
}
if (!append_word(out, out_len, &cursor, word)) {
return -1;
}
}
return 0;
}
bool mnemonic_validate_basic(const char* mnemonic) {
if (!mnemonic || !mnemonic[0]) {
return false;
}
char buf[256];
strncpy(buf, mnemonic, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0';
int count = 0;
char* saveptr = nullptr;
char* tok = strtok_r(buf, " ", &saveptr);
while (tok) {
if (mnemonic_word_index(tok) < 0) {
return false;
}
++count;
tok = strtok_r(nullptr, " ", &saveptr);
}
return count == 12;
}

View File

@@ -0,0 +1,13 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
const char* mnemonic_word_at(uint16_t index);
int mnemonic_word_index(const char* word);
bool mnemonic_letter_range(char letter, uint16_t *out_start, uint16_t *out_count);
bool mnemonic_second_letters(char first, char *out, uint8_t *count);
bool mnemonic_prefix_range(char first, char second, uint16_t *out_start, uint16_t *out_count);
int generate_mnemonic_12(char* out, size_t out_len);
bool mnemonic_validate_basic(const char* mnemonic);

View File

@@ -0,0 +1,261 @@
#pragma once
/* BIP-39 English wordlist (2048 words) */
static const char *BIP39_WORDLIST[2048] = {
"abandon", "ability", "able", "about", "above", "absent", "absorb", "abstract",
"absurd", "abuse", "access", "accident", "account", "accuse", "achieve", "acid",
"acoustic", "acquire", "across", "act", "action", "actor", "actress", "actual",
"adapt", "add", "addict", "address", "adjust", "admit", "adult", "advance",
"advice", "aerobic", "affair", "afford", "afraid", "again", "age", "agent",
"agree", "ahead", "aim", "air", "airport", "aisle", "alarm", "album",
"alcohol", "alert", "alien", "all", "alley", "allow", "almost", "alone",
"alpha", "already", "also", "alter", "always", "amateur", "amazing", "among",
"amount", "amused", "analyst", "anchor", "ancient", "anger", "angle", "angry",
"animal", "ankle", "announce", "annual", "another", "answer", "antenna", "antique",
"anxiety", "any", "apart", "apology", "appear", "apple", "approve", "april",
"arch", "arctic", "area", "arena", "argue", "arm", "armed", "armor",
"army", "around", "arrange", "arrest", "arrive", "arrow", "art", "artefact",
"artist", "artwork", "ask", "aspect", "assault", "asset", "assist", "assume",
"asthma", "athlete", "atom", "attack", "attend", "attitude", "attract", "auction",
"audit", "august", "aunt", "author", "auto", "autumn", "average", "avocado",
"avoid", "awake", "aware", "away", "awesome", "awful", "awkward", "axis",
"baby", "bachelor", "bacon", "badge", "bag", "balance", "balcony", "ball",
"bamboo", "banana", "banner", "bar", "barely", "bargain", "barrel", "base",
"basic", "basket", "battle", "beach", "bean", "beauty", "because", "become",
"beef", "before", "begin", "behave", "behind", "believe", "below", "belt",
"bench", "benefit", "best", "betray", "better", "between", "beyond", "bicycle",
"bid", "bike", "bind", "biology", "bird", "birth", "bitter", "black",
"blade", "blame", "blanket", "blast", "bleak", "bless", "blind", "blood",
"blossom", "blouse", "blue", "blur", "blush", "board", "boat", "body",
"boil", "bomb", "bone", "bonus", "book", "boost", "border", "boring",
"borrow", "boss", "bottom", "bounce", "box", "boy", "bracket", "brain",
"brand", "brass", "brave", "bread", "breeze", "brick", "bridge", "brief",
"bright", "bring", "brisk", "broccoli", "broken", "bronze", "broom", "brother",
"brown", "brush", "bubble", "buddy", "budget", "buffalo", "build", "bulb",
"bulk", "bullet", "bundle", "bunker", "burden", "burger", "burst", "bus",
"business", "busy", "butter", "buyer", "buzz", "cabbage", "cabin", "cable",
"cactus", "cage", "cake", "call", "calm", "camera", "camp", "can",
"canal", "cancel", "candy", "cannon", "canoe", "canvas", "canyon", "capable",
"capital", "captain", "car", "carbon", "card", "cargo", "carpet", "carry",
"cart", "case", "cash", "casino", "castle", "casual", "cat", "catalog",
"catch", "category", "cattle", "caught", "cause", "caution", "cave", "ceiling",
"celery", "cement", "census", "century", "cereal", "certain", "chair", "chalk",
"champion", "change", "chaos", "chapter", "charge", "chase", "chat", "cheap",
"check", "cheese", "chef", "cherry", "chest", "chicken", "chief", "child",
"chimney", "choice", "choose", "chronic", "chuckle", "chunk", "churn", "cigar",
"cinnamon", "circle", "citizen", "city", "civil", "claim", "clap", "clarify",
"claw", "clay", "clean", "clerk", "clever", "click", "client", "cliff",
"climb", "clinic", "clip", "clock", "clog", "close", "cloth", "cloud",
"clown", "club", "clump", "cluster", "clutch", "coach", "coast", "coconut",
"code", "coffee", "coil", "coin", "collect", "color", "column", "combine",
"come", "comfort", "comic", "common", "company", "concert", "conduct", "confirm",
"congress", "connect", "consider", "control", "convince", "cook", "cool", "copper",
"copy", "coral", "core", "corn", "correct", "cost", "cotton", "couch",
"country", "couple", "course", "cousin", "cover", "coyote", "crack", "cradle",
"craft", "cram", "crane", "crash", "crater", "crawl", "crazy", "cream",
"credit", "creek", "crew", "cricket", "crime", "crisp", "critic", "crop",
"cross", "crouch", "crowd", "crucial", "cruel", "cruise", "crumble", "crunch",
"crush", "cry", "crystal", "cube", "culture", "cup", "cupboard", "curious",
"current", "curtain", "curve", "cushion", "custom", "cute", "cycle", "dad",
"damage", "damp", "dance", "danger", "daring", "dash", "daughter", "dawn",
"day", "deal", "debate", "debris", "decade", "december", "decide", "decline",
"decorate", "decrease", "deer", "defense", "define", "defy", "degree", "delay",
"deliver", "demand", "demise", "denial", "dentist", "deny", "depart", "depend",
"deposit", "depth", "deputy", "derive", "describe", "desert", "design", "desk",
"despair", "destroy", "detail", "detect", "develop", "device", "devote", "diagram",
"dial", "diamond", "diary", "dice", "diesel", "diet", "differ", "digital",
"dignity", "dilemma", "dinner", "dinosaur", "direct", "dirt", "disagree", "discover",
"disease", "dish", "dismiss", "disorder", "display", "distance", "divert", "divide",
"divorce", "dizzy", "doctor", "document", "dog", "doll", "dolphin", "domain",
"donate", "donkey", "donor", "door", "dose", "double", "dove", "draft",
"dragon", "drama", "drastic", "draw", "dream", "dress", "drift", "drill",
"drink", "drip", "drive", "drop", "drum", "dry", "duck", "dumb",
"dune", "during", "dust", "dutch", "duty", "dwarf", "dynamic", "eager",
"eagle", "early", "earn", "earth", "easily", "east", "easy", "echo",
"ecology", "economy", "edge", "edit", "educate", "effort", "egg", "eight",
"either", "elbow", "elder", "electric", "elegant", "element", "elephant", "elevator",
"elite", "else", "embark", "embody", "embrace", "emerge", "emotion", "employ",
"empower", "empty", "enable", "enact", "end", "endless", "endorse", "enemy",
"energy", "enforce", "engage", "engine", "enhance", "enjoy", "enlist", "enough",
"enrich", "enroll", "ensure", "enter", "entire", "entry", "envelope", "episode",
"equal", "equip", "era", "erase", "erode", "erosion", "error", "erupt",
"escape", "essay", "essence", "estate", "eternal", "ethics", "evidence", "evil",
"evoke", "evolve", "exact", "example", "excess", "exchange", "excite", "exclude",
"excuse", "execute", "exercise", "exhaust", "exhibit", "exile", "exist", "exit",
"exotic", "expand", "expect", "expire", "explain", "expose", "express", "extend",
"extra", "eye", "eyebrow", "fabric", "face", "faculty", "fade", "faint",
"faith", "fall", "false", "fame", "family", "famous", "fan", "fancy",
"fantasy", "farm", "fashion", "fat", "fatal", "father", "fatigue", "fault",
"favorite", "feature", "february", "federal", "fee", "feed", "feel", "female",
"fence", "festival", "fetch", "fever", "few", "fiber", "fiction", "field",
"figure", "file", "film", "filter", "final", "find", "fine", "finger",
"finish", "fire", "firm", "first", "fiscal", "fish", "fit", "fitness",
"fix", "flag", "flame", "flash", "flat", "flavor", "flee", "flight",
"flip", "float", "flock", "floor", "flower", "fluid", "flush", "fly",
"foam", "focus", "fog", "foil", "fold", "follow", "food", "foot",
"force", "forest", "forget", "fork", "fortune", "forum", "forward", "fossil",
"foster", "found", "fox", "fragile", "frame", "frequent", "fresh", "friend",
"fringe", "frog", "front", "frost", "frown", "frozen", "fruit", "fuel",
"fun", "funny", "furnace", "fury", "future", "gadget", "gain", "galaxy",
"gallery", "game", "gap", "garage", "garbage", "garden", "garlic", "garment",
"gas", "gasp", "gate", "gather", "gauge", "gaze", "general", "genius",
"genre", "gentle", "genuine", "gesture", "ghost", "giant", "gift", "giggle",
"ginger", "giraffe", "girl", "give", "glad", "glance", "glare", "glass",
"glide", "glimpse", "globe", "gloom", "glory", "glove", "glow", "glue",
"goat", "goddess", "gold", "good", "goose", "gorilla", "gospel", "gossip",
"govern", "gown", "grab", "grace", "grain", "grant", "grape", "grass",
"gravity", "great", "green", "grid", "grief", "grit", "grocery", "group",
"grow", "grunt", "guard", "guess", "guide", "guilt", "guitar", "gun",
"gym", "habit", "hair", "half", "hammer", "hamster", "hand", "happy",
"harbor", "hard", "harsh", "harvest", "hat", "have", "hawk", "hazard",
"head", "health", "heart", "heavy", "hedgehog", "height", "hello", "helmet",
"help", "hen", "hero", "hidden", "high", "hill", "hint", "hip",
"hire", "history", "hobby", "hockey", "hold", "hole", "holiday", "hollow",
"home", "honey", "hood", "hope", "horn", "horror", "horse", "hospital",
"host", "hotel", "hour", "hover", "hub", "huge", "human", "humble",
"humor", "hundred", "hungry", "hunt", "hurdle", "hurry", "hurt", "husband",
"hybrid", "ice", "icon", "idea", "identify", "idle", "ignore", "ill",
"illegal", "illness", "image", "imitate", "immense", "immune", "impact", "impose",
"improve", "impulse", "inch", "include", "income", "increase", "index", "indicate",
"indoor", "industry", "infant", "inflict", "inform", "inhale", "inherit", "initial",
"inject", "injury", "inmate", "inner", "innocent", "input", "inquiry", "insane",
"insect", "inside", "inspire", "install", "intact", "interest", "into", "invest",
"invite", "involve", "iron", "island", "isolate", "issue", "item", "ivory",
"jacket", "jaguar", "jar", "jazz", "jealous", "jeans", "jelly", "jewel",
"job", "join", "joke", "journey", "joy", "judge", "juice", "jump",
"jungle", "junior", "junk", "just", "kangaroo", "keen", "keep", "ketchup",
"key", "kick", "kid", "kidney", "kind", "kingdom", "kiss", "kit",
"kitchen", "kite", "kitten", "kiwi", "knee", "knife", "knock", "know",
"lab", "label", "labor", "ladder", "lady", "lake", "lamp", "language",
"laptop", "large", "later", "latin", "laugh", "laundry", "lava", "law",
"lawn", "lawsuit", "layer", "lazy", "leader", "leaf", "learn", "leave",
"lecture", "left", "leg", "legal", "legend", "leisure", "lemon", "lend",
"length", "lens", "leopard", "lesson", "letter", "level", "liar", "liberty",
"library", "license", "life", "lift", "light", "like", "limb", "limit",
"link", "lion", "liquid", "list", "little", "live", "lizard", "load",
"loan", "lobster", "local", "lock", "logic", "lonely", "long", "loop",
"lottery", "loud", "lounge", "love", "loyal", "lucky", "luggage", "lumber",
"lunar", "lunch", "luxury", "lyrics", "machine", "mad", "magic", "magnet",
"maid", "mail", "main", "major", "make", "mammal", "man", "manage",
"mandate", "mango", "mansion", "manual", "maple", "marble", "march", "margin",
"marine", "market", "marriage", "mask", "mass", "master", "match", "material",
"math", "matrix", "matter", "maximum", "maze", "meadow", "mean", "measure",
"meat", "mechanic", "medal", "media", "melody", "melt", "member", "memory",
"mention", "menu", "mercy", "merge", "merit", "merry", "mesh", "message",
"metal", "method", "middle", "midnight", "milk", "million", "mimic", "mind",
"minimum", "minor", "minute", "miracle", "mirror", "misery", "miss", "mistake",
"mix", "mixed", "mixture", "mobile", "model", "modify", "mom", "moment",
"monitor", "monkey", "monster", "month", "moon", "moral", "more", "morning",
"mosquito", "mother", "motion", "motor", "mountain", "mouse", "move", "movie",
"much", "muffin", "mule", "multiply", "muscle", "museum", "mushroom", "music",
"must", "mutual", "myself", "mystery", "myth", "naive", "name", "napkin",
"narrow", "nasty", "nation", "nature", "near", "neck", "need", "negative",
"neglect", "neither", "nephew", "nerve", "nest", "net", "network", "neutral",
"never", "news", "next", "nice", "night", "noble", "noise", "nominee",
"noodle", "normal", "north", "nose", "notable", "note", "nothing", "notice",
"novel", "now", "nuclear", "number", "nurse", "nut", "oak", "obey",
"object", "oblige", "obscure", "observe", "obtain", "obvious", "occur", "ocean",
"october", "odor", "off", "offer", "office", "often", "oil", "okay",
"old", "olive", "olympic", "omit", "once", "one", "onion", "online",
"only", "open", "opera", "opinion", "oppose", "option", "orange", "orbit",
"orchard", "order", "ordinary", "organ", "orient", "original", "orphan", "ostrich",
"other", "outdoor", "outer", "output", "outside", "oval", "oven", "over",
"own", "owner", "oxygen", "oyster", "ozone", "pact", "paddle", "page",
"pair", "palace", "palm", "panda", "panel", "panic", "panther", "paper",
"parade", "parent", "park", "parrot", "party", "pass", "patch", "path",
"patient", "patrol", "pattern", "pause", "pave", "payment", "peace", "peanut",
"pear", "peasant", "pelican", "pen", "penalty", "pencil", "people", "pepper",
"perfect", "permit", "person", "pet", "phone", "photo", "phrase", "physical",
"piano", "picnic", "picture", "piece", "pig", "pigeon", "pill", "pilot",
"pink", "pioneer", "pipe", "pistol", "pitch", "pizza", "place", "planet",
"plastic", "plate", "play", "please", "pledge", "pluck", "plug", "plunge",
"poem", "poet", "point", "polar", "pole", "police", "pond", "pony",
"pool", "popular", "portion", "position", "possible", "post", "potato", "pottery",
"poverty", "powder", "power", "practice", "praise", "predict", "prefer", "prepare",
"present", "pretty", "prevent", "price", "pride", "primary", "print", "priority",
"prison", "private", "prize", "problem", "process", "produce", "profit", "program",
"project", "promote", "proof", "property", "prosper", "protect", "proud", "provide",
"public", "pudding", "pull", "pulp", "pulse", "pumpkin", "punch", "pupil",
"puppy", "purchase", "purity", "purpose", "purse", "push", "put", "puzzle",
"pyramid", "quality", "quantum", "quarter", "question", "quick", "quit", "quiz",
"quote", "rabbit", "raccoon", "race", "rack", "radar", "radio", "rail",
"rain", "raise", "rally", "ramp", "ranch", "random", "range", "rapid",
"rare", "rate", "rather", "raven", "raw", "razor", "ready", "real",
"reason", "rebel", "rebuild", "recall", "receive", "recipe", "record", "recycle",
"reduce", "reflect", "reform", "refuse", "region", "regret", "regular", "reject",
"relax", "release", "relief", "rely", "remain", "remember", "remind", "remove",
"render", "renew", "rent", "reopen", "repair", "repeat", "replace", "report",
"require", "rescue", "resemble", "resist", "resource", "response", "result", "retire",
"retreat", "return", "reunion", "reveal", "review", "reward", "rhythm", "rib",
"ribbon", "rice", "rich", "ride", "ridge", "rifle", "right", "rigid",
"ring", "riot", "ripple", "risk", "ritual", "rival", "river", "road",
"roast", "robot", "robust", "rocket", "romance", "roof", "rookie", "room",
"rose", "rotate", "rough", "round", "route", "royal", "rubber", "rude",
"rug", "rule", "run", "runway", "rural", "sad", "saddle", "sadness",
"safe", "sail", "salad", "salmon", "salon", "salt", "salute", "same",
"sample", "sand", "satisfy", "satoshi", "sauce", "sausage", "save", "say",
"scale", "scan", "scare", "scatter", "scene", "scheme", "school", "science",
"scissors", "scorpion", "scout", "scrap", "screen", "script", "scrub", "sea",
"search", "season", "seat", "second", "secret", "section", "security", "seed",
"seek", "segment", "select", "sell", "seminar", "senior", "sense", "sentence",
"series", "service", "session", "settle", "setup", "seven", "shadow", "shaft",
"shallow", "share", "shed", "shell", "sheriff", "shield", "shift", "shine",
"ship", "shiver", "shock", "shoe", "shoot", "shop", "short", "shoulder",
"shove", "shrimp", "shrug", "shuffle", "shy", "sibling", "sick", "side",
"siege", "sight", "sign", "silent", "silk", "silly", "silver", "similar",
"simple", "since", "sing", "siren", "sister", "situate", "six", "size",
"skate", "sketch", "ski", "skill", "skin", "skirt", "skull", "slab",
"slam", "sleep", "slender", "slice", "slide", "slight", "slim", "slogan",
"slot", "slow", "slush", "small", "smart", "smile", "smoke", "smooth",
"snack", "snake", "snap", "sniff", "snow", "soap", "soccer", "social",
"sock", "soda", "soft", "solar", "soldier", "solid", "solution", "solve",
"someone", "song", "soon", "sorry", "sort", "soul", "sound", "soup",
"source", "south", "space", "spare", "spatial", "spawn", "speak", "special",
"speed", "spell", "spend", "sphere", "spice", "spider", "spike", "spin",
"spirit", "split", "spoil", "sponsor", "spoon", "sport", "spot", "spray",
"spread", "spring", "spy", "square", "squeeze", "squirrel", "stable", "stadium",
"staff", "stage", "stairs", "stamp", "stand", "start", "state", "stay",
"steak", "steel", "stem", "step", "stereo", "stick", "still", "sting",
"stock", "stomach", "stone", "stool", "story", "stove", "strategy", "street",
"strike", "strong", "struggle", "student", "stuff", "stumble", "style", "subject",
"submit", "subway", "success", "such", "sudden", "suffer", "sugar", "suggest",
"suit", "summer", "sun", "sunny", "sunset", "super", "supply", "supreme",
"sure", "surface", "surge", "surprise", "surround", "survey", "suspect", "sustain",
"swallow", "swamp", "swap", "swarm", "swear", "sweet", "swift", "swim",
"swing", "switch", "sword", "symbol", "symptom", "syrup", "system", "table",
"tackle", "tag", "tail", "talent", "talk", "tank", "tape", "target",
"task", "taste", "tattoo", "taxi", "teach", "team", "tell", "ten",
"tenant", "tennis", "tent", "term", "test", "text", "thank", "that",
"theme", "then", "theory", "there", "they", "thing", "this", "thought",
"three", "thrive", "throw", "thumb", "thunder", "ticket", "tide", "tiger",
"tilt", "timber", "time", "tiny", "tip", "tired", "tissue", "title",
"toast", "tobacco", "today", "toddler", "toe", "together", "toilet", "token",
"tomato", "tomorrow", "tone", "tongue", "tonight", "tool", "tooth", "top",
"topic", "topple", "torch", "tornado", "tortoise", "toss", "total", "tourist",
"toward", "tower", "town", "toy", "track", "trade", "traffic", "tragic",
"train", "transfer", "trap", "trash", "travel", "tray", "treat", "tree",
"trend", "trial", "tribe", "trick", "trigger", "trim", "trip", "trophy",
"trouble", "truck", "true", "truly", "trumpet", "trust", "truth", "try",
"tube", "tuition", "tumble", "tuna", "tunnel", "turkey", "turn", "turtle",
"twelve", "twenty", "twice", "twin", "twist", "two", "type", "typical",
"ugly", "umbrella", "unable", "unaware", "uncle", "uncover", "under", "undo",
"unfair", "unfold", "unhappy", "uniform", "unique", "unit", "universe", "unknown",
"unlock", "until", "unusual", "unveil", "update", "upgrade", "uphold", "upon",
"upper", "upset", "urban", "urge", "usage", "use", "used", "useful",
"useless", "usual", "utility", "vacant", "vacuum", "vague", "valid", "valley",
"valve", "van", "vanish", "vapor", "various", "vast", "vault", "vehicle",
"velvet", "vendor", "venture", "venue", "verb", "verify", "version", "very",
"vessel", "veteran", "viable", "vibrant", "vicious", "victory", "video", "view",
"village", "vintage", "violin", "virtual", "virus", "visa", "visit", "visual",
"vital", "vivid", "vocal", "voice", "void", "volcano", "volume", "vote",
"voyage", "wage", "wagon", "wait", "walk", "wall", "walnut", "want",
"warfare", "warm", "warrior", "wash", "wasp", "waste", "water", "wave",
"way", "wealth", "weapon", "wear", "weasel", "weather", "web", "wedding",
"weekend", "weird", "welcome", "west", "wet", "whale", "what", "wheat",
"wheel", "when", "where", "whip", "whisper", "wide", "width", "wife",
"wild", "will", "win", "window", "wine", "wing", "wink", "winner",
"winter", "wire", "wisdom", "wise", "wish", "witness", "wolf", "woman",
"wonder", "wood", "wool", "word", "work", "world", "worry", "worth",
"wrap", "wreck", "wrestle", "wrist", "write", "wrong", "yard", "year",
"yellow", "you", "young", "youth", "zebra", "zero", "zone", "zoo"
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,306 @@
/*
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef cJSON__h
#define cJSON__h
#ifdef __cplusplus
extern "C"
{
#endif
#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
#define __WINDOWS__
#endif
#ifdef __WINDOWS__
/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options:
CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
For *nix builds that support visibility attribute, you can define similar behavior by
setting default visibility to hidden by adding
-fvisibility=hidden (for gcc)
or
-xldscope=hidden (for sun cc)
to CFLAGS
then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
*/
#define CJSON_CDECL __cdecl
#define CJSON_STDCALL __stdcall
/* export symbols by default, this is necessary for copy pasting the C and header file */
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_EXPORT_SYMBOLS
#endif
#if defined(CJSON_HIDE_SYMBOLS)
#define CJSON_PUBLIC(type) type CJSON_STDCALL
#elif defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
#elif defined(CJSON_IMPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
#endif
#else /* !__WINDOWS__ */
#define CJSON_CDECL
#define CJSON_STDCALL
#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
#else
#define CJSON_PUBLIC(type) type
#endif
#endif
/* project version */
#define CJSON_VERSION_MAJOR 1
#define CJSON_VERSION_MINOR 7
#define CJSON_VERSION_PATCH 18
#include <stddef.h>
/* cJSON Types: */
#define cJSON_Invalid (0)
#define cJSON_False (1 << 0)
#define cJSON_True (1 << 1)
#define cJSON_NULL (1 << 2)
#define cJSON_Number (1 << 3)
#define cJSON_String (1 << 4)
#define cJSON_Array (1 << 5)
#define cJSON_Object (1 << 6)
#define cJSON_Raw (1 << 7) /* raw json */
#define cJSON_IsReference 256
#define cJSON_StringIsConst 512
/* The cJSON structure: */
typedef struct cJSON
{
/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *next;
struct cJSON *prev;
/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
struct cJSON *child;
/* The type of the item, as above. */
int type;
/* The item's string, if type==cJSON_String and type == cJSON_Raw */
char *valuestring;
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
int valueint;
/* The item's number, if type==cJSON_Number */
double valuedouble;
/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
char *string;
} cJSON;
typedef struct cJSON_Hooks
{
/* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
void *(CJSON_CDECL *malloc_fn)(size_t sz);
void (CJSON_CDECL *free_fn)(void *ptr);
} cJSON_Hooks;
typedef int cJSON_bool;
/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
* This is to prevent stack overflows. */
#ifndef CJSON_NESTING_LIMIT
#define CJSON_NESTING_LIMIT 1000
#endif
/* Limits the length of circular references can be before cJSON rejects to parse them.
* This is to prevent stack overflows. */
#ifndef CJSON_CIRCULAR_LIMIT
#define CJSON_CIRCULAR_LIMIT 10000
#endif
/* returns the version of cJSON as a string */
CJSON_PUBLIC(const char*) cJSON_Version(void);
/* Supply malloc, realloc and free functions to cJSON */
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
/* Render a cJSON entity to text for transfer/storage. */
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. */
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
/* Delete a cJSON entity and all subentities. */
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
/* Returns the number of items in an array (or object). */
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
/* Get item "string" from object. Case insensitive. */
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
/* Check item type and return its value */
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);
/* These functions check the type of an item */
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
/* These calls create a cJSON item of the appropriate type. */
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
/* raw json */
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
/* Create a string where valuestring references a string so
* it will not be freed by cJSON_Delete */
CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
/* Create an object/array that only references it's elements so
* they will not be freed by cJSON_Delete */
CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
/* These utilities create an Array of count items.
* The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
/* Append item to the specified array/object. */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
* WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
* writing to `item->string` */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
/* Remove/Detach items from Arrays/Objects. */
CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
/* Update array items. */
CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
/* Duplicate a cJSON item */
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
* need to be released. With recurse!=0, it will duplicate any children connected to the item.
* The item->next and ->prev pointers are always zero on return from Duplicate. */
/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
* case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
* The input pointer json cannot point to a read-only address area, such as a string constant,
* but should point to a readable and writable address area. */
CJSON_PUBLIC(void) cJSON_Minify(char *json);
/* Helper functions for creating and adding items to an object at the same time.
* They return the added item or NULL on failure. */
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
/* helper for the cJSON_SetNumberValue macro */
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/
#define cJSON_SetBoolValue(object, boolValue) ( \
(object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \
(object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \
cJSON_Invalid\
)
/* Macro for iterating over an array or object */
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
CJSON_PUBLIC(void) cJSON_free(void *object);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,320 @@
#include "../../secp256k1/include/secp256k1.h"
#include "../../secp256k1/include/secp256k1_schnorrsig.h"
#include "../../secp256k1/include/secp256k1_ecdh.h"
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include "../nostr_platform.h"
/*
* PRIVATE INTERNAL FUNCTIONS - NOT EXPORTED
* These functions are for internal library use only.
*/
/** Opaque data structure that holds a parsed and valid public key.
* Guaranteed to be 64 bytes in size, and can be safely copied/moved.
*/
typedef struct nostr_secp256k1_pubkey {
unsigned char data[64];
} nostr_secp256k1_pubkey;
/** Opaque data structure that holds a parsed keypair.
* Guaranteed to be 96 bytes in size, and can be safely copied/moved.
*/
typedef struct nostr_secp256k1_keypair {
unsigned char data[96];
} nostr_secp256k1_keypair;
/** Opaque data structure that holds a parsed x-only public key.
* Guaranteed to be 64 bytes in size, and can be safely copied/moved.
*/
typedef struct nostr_secp256k1_xonly_pubkey {
unsigned char data[64];
} nostr_secp256k1_xonly_pubkey;
// Global context for secp256k1 operations
static secp256k1_context* g_ctx = NULL;
int nostr_secp256k1_context_create(void) {
if (g_ctx != NULL) {
return 1; // Already initialized
}
g_ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
if (g_ctx == NULL) {
return 0;
}
// Add randomization to the context using platform entropy.
unsigned char randomize[32];
if (nostr_platform_random(randomize, sizeof(randomize)) != 0) {
secp256k1_context_destroy(g_ctx);
g_ctx = NULL;
return 0;
}
if (!secp256k1_context_randomize(g_ctx, randomize)) {
secp256k1_context_destroy(g_ctx);
g_ctx = NULL;
return 0;
}
return 1;
}
void nostr_secp256k1_context_destroy(void) {
if (g_ctx != NULL) {
secp256k1_context_destroy(g_ctx);
g_ctx = NULL;
}
}
int nostr_secp256k1_ec_seckey_verify(const unsigned char *seckey) {
if (g_ctx == NULL || seckey == NULL) {
return 0;
}
return secp256k1_ec_seckey_verify(g_ctx, seckey);
}
int nostr_secp256k1_ec_pubkey_create(nostr_secp256k1_pubkey *pubkey, const unsigned char *seckey) {
if (g_ctx == NULL || pubkey == NULL || seckey == NULL) {
return 0;
}
secp256k1_pubkey internal_pubkey;
if (!secp256k1_ec_pubkey_create(g_ctx, &internal_pubkey, seckey)) {
return 0;
}
// Copy the internal representation to our wrapper
memcpy(pubkey->data, &internal_pubkey, sizeof(secp256k1_pubkey));
return 1;
}
int nostr_secp256k1_keypair_create(nostr_secp256k1_keypair *keypair, const unsigned char *seckey) {
if (g_ctx == NULL || keypair == NULL || seckey == NULL) {
return 0;
}
secp256k1_keypair internal_keypair;
if (!secp256k1_keypair_create(g_ctx, &internal_keypair, seckey)) {
return 0;
}
// Copy the internal representation to our wrapper
memcpy(keypair->data, &internal_keypair, sizeof(secp256k1_keypair));
return 1;
}
int nostr_secp256k1_keypair_xonly_pub(nostr_secp256k1_xonly_pubkey *pubkey, const nostr_secp256k1_keypair *keypair) {
if (g_ctx == NULL || pubkey == NULL || keypair == NULL) {
return 0;
}
secp256k1_keypair internal_keypair;
secp256k1_xonly_pubkey internal_xonly;
// Copy from our wrapper to internal representation
memcpy(&internal_keypair, keypair->data, sizeof(secp256k1_keypair));
if (!secp256k1_keypair_xonly_pub(g_ctx, &internal_xonly, NULL, &internal_keypair)) {
return 0;
}
// Copy the internal representation to our wrapper
memcpy(pubkey->data, &internal_xonly, sizeof(secp256k1_xonly_pubkey));
return 1;
}
int nostr_secp256k1_xonly_pubkey_parse(nostr_secp256k1_xonly_pubkey *pubkey, const unsigned char *input32) {
if (g_ctx == NULL || pubkey == NULL || input32 == NULL) {
return 0;
}
secp256k1_xonly_pubkey internal_xonly;
if (!secp256k1_xonly_pubkey_parse(g_ctx, &internal_xonly, input32)) {
return 0;
}
// Copy the internal representation to our wrapper
memcpy(pubkey->data, &internal_xonly, sizeof(secp256k1_xonly_pubkey));
return 1;
}
int nostr_secp256k1_xonly_pubkey_serialize(unsigned char *output32, const nostr_secp256k1_xonly_pubkey *pubkey) {
if (g_ctx == NULL || output32 == NULL || pubkey == NULL) {
return 0;
}
secp256k1_xonly_pubkey internal_xonly;
// Copy from our wrapper to internal representation
memcpy(&internal_xonly, pubkey->data, sizeof(secp256k1_xonly_pubkey));
return secp256k1_xonly_pubkey_serialize(g_ctx, output32, &internal_xonly);
}
int nostr_secp256k1_schnorrsig_sign32(unsigned char *sig64, const unsigned char *msghash32, const nostr_secp256k1_keypair *keypair, const unsigned char *aux_rand32) {
if (g_ctx == NULL || sig64 == NULL || msghash32 == NULL || keypair == NULL) {
return 0;
}
secp256k1_keypair internal_keypair;
// Copy from our wrapper to internal representation
memcpy(&internal_keypair, keypair->data, sizeof(secp256k1_keypair));
return secp256k1_schnorrsig_sign32(g_ctx, sig64, msghash32, &internal_keypair, aux_rand32);
}
int nostr_secp256k1_schnorrsig_verify(const unsigned char *sig64, const unsigned char *msghash32, const nostr_secp256k1_xonly_pubkey *pubkey) {
if (g_ctx == NULL || sig64 == NULL || msghash32 == NULL || pubkey == NULL) {
return 0;
}
secp256k1_xonly_pubkey internal_xonly;
// Copy from our wrapper to internal representation
memcpy(&internal_xonly, pubkey->data, sizeof(secp256k1_xonly_pubkey));
return secp256k1_schnorrsig_verify(g_ctx, sig64, msghash32, 32, &internal_xonly);
}
int nostr_secp256k1_ec_pubkey_serialize_compressed(unsigned char *output, const nostr_secp256k1_pubkey *pubkey) {
if (g_ctx == NULL || output == NULL || pubkey == NULL) {
return 0;
}
secp256k1_pubkey internal_pubkey;
size_t outputlen = 33;
// Copy from our wrapper to internal representation
memcpy(&internal_pubkey, pubkey->data, sizeof(secp256k1_pubkey));
return secp256k1_ec_pubkey_serialize(g_ctx, output, &outputlen, &internal_pubkey, SECP256K1_EC_COMPRESSED);
}
int nostr_secp256k1_ec_seckey_tweak_add(unsigned char *seckey, const unsigned char *tweak) {
if (g_ctx == NULL || seckey == NULL || tweak == NULL) {
return 0;
}
return secp256k1_ec_seckey_tweak_add(g_ctx, seckey, tweak);
}
int nostr_secp256k1_ec_pubkey_parse(nostr_secp256k1_pubkey *pubkey, const unsigned char *input, size_t inputlen) {
if (g_ctx == NULL || pubkey == NULL || input == NULL) {
return 0;
}
secp256k1_pubkey internal_pubkey;
if (!secp256k1_ec_pubkey_parse(g_ctx, &internal_pubkey, input, inputlen)) {
return 0;
}
// Copy the internal representation to our wrapper
memcpy(pubkey->data, &internal_pubkey, sizeof(secp256k1_pubkey));
return 1;
}
int nostr_secp256k1_ecdh(unsigned char *result, const nostr_secp256k1_pubkey *pubkey, const unsigned char *seckey, void *hashfp, void *data) {
if (g_ctx == NULL || result == NULL || pubkey == NULL || seckey == NULL) {
return 0;
}
secp256k1_pubkey internal_pubkey;
// Copy from our wrapper to internal representation
memcpy(&internal_pubkey, pubkey->data, sizeof(secp256k1_pubkey));
return secp256k1_ecdh(g_ctx, result, &internal_pubkey, seckey, hashfp, data);
}
int nostr_secp256k1_ec_pubkey_tweak_mul(nostr_secp256k1_pubkey* pubkey, const unsigned char* tweak32) {
if (g_ctx == NULL || pubkey == NULL || tweak32 == NULL) {
return 0;
}
secp256k1_pubkey internal_pubkey;
memcpy(&internal_pubkey, pubkey->data, sizeof(secp256k1_pubkey));
if (!secp256k1_ec_pubkey_tweak_mul(g_ctx, &internal_pubkey, tweak32)) {
return 0;
}
memcpy(pubkey->data, &internal_pubkey, sizeof(secp256k1_pubkey));
return 1;
}
int nostr_secp256k1_ec_pubkey_negate(nostr_secp256k1_pubkey* pubkey) {
if (g_ctx == NULL || pubkey == NULL) {
return 0;
}
secp256k1_pubkey internal_pubkey;
memcpy(&internal_pubkey, pubkey->data, sizeof(secp256k1_pubkey));
if (!secp256k1_ec_pubkey_negate(g_ctx, &internal_pubkey)) {
return 0;
}
memcpy(pubkey->data, &internal_pubkey, sizeof(secp256k1_pubkey));
return 1;
}
int nostr_secp256k1_ec_pubkey_combine(nostr_secp256k1_pubkey* out,
const nostr_secp256k1_pubkey* const* ins,
size_t n) {
if (g_ctx == NULL || out == NULL || ins == NULL || n == 0) {
return 0;
}
secp256k1_pubkey* parsed = (secp256k1_pubkey*)calloc(n, sizeof(secp256k1_pubkey));
const secp256k1_pubkey** ptrs = (const secp256k1_pubkey**)calloc(n, sizeof(secp256k1_pubkey*));
if (!parsed || !ptrs) {
free(parsed);
free(ptrs);
return 0;
}
for (size_t i = 0; i < n; i++) {
if (!ins[i]) {
free(parsed);
free(ptrs);
return 0;
}
memcpy(&parsed[i], ins[i]->data, sizeof(secp256k1_pubkey));
ptrs[i] = &parsed[i];
}
secp256k1_pubkey combined;
int ok = secp256k1_ec_pubkey_combine(g_ctx, &combined, ptrs, n);
free(parsed);
free(ptrs);
if (!ok) {
return 0;
}
memcpy(out->data, &combined, sizeof(secp256k1_pubkey));
return 1;
}
int nostr_secp256k1_get_random_bytes(unsigned char *buf, size_t len) {
if (buf == NULL || len == 0) {
return 0;
}
if (nostr_platform_random(buf, len) != 0) {
return 0;
}
return 1;
}

View File

@@ -0,0 +1,345 @@
/*
* NOSTR Core Library - NIP-001: Basic Protocol Flow
*
* Event creation, signing, serialization and core protocol functions
*/
#include "nip001.h"
#include "utils.h"
#include "../cjson/cJSON.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "../nostr_core/nostr_common.h"
// Forward declarations for crypto functions (private API)
// These functions are implemented in crypto/ but not exposed through public headers
typedef struct {
unsigned char data[64];
} nostr_secp256k1_xonly_pubkey;
int nostr_secp256k1_xonly_pubkey_parse(nostr_secp256k1_xonly_pubkey* pubkey, const unsigned char* input32);
int nostr_secp256k1_schnorrsig_verify(const unsigned char* sig64, const unsigned char* msg32, const nostr_secp256k1_xonly_pubkey* pubkey);
// Declare utility functions
void nostr_bytes_to_hex(const unsigned char* bytes, size_t len, char* hex);
int nostr_hex_to_bytes(const char* hex, unsigned char* bytes, size_t len);
int nostr_sha256(const unsigned char* data, size_t len, unsigned char* hash);
int nostr_ec_public_key_from_private_key(const unsigned char* private_key, unsigned char* public_key);
int nostr_ec_sign(const unsigned char* private_key, const unsigned char* hash, unsigned char* signature);
/**
* Create and sign a NOSTR event
*/
cJSON* nostr_create_and_sign_event(int kind, const char* content, cJSON* tags, const unsigned char* private_key, time_t timestamp) {
if (!private_key) {
return NULL;
}
if (!content) {
content = ""; // Default to empty content
}
// Convert private key to public key
unsigned char public_key[32];
if (nostr_ec_public_key_from_private_key(private_key, public_key) != 0) {
return NULL;
}
// Convert public key to hex
char pubkey_hex[65];
nostr_bytes_to_hex(public_key, 32, pubkey_hex);
// Create event structure
cJSON* event = cJSON_CreateObject();
if (!event) {
return NULL;
}
// Use provided timestamp or current time if timestamp is 0
time_t event_time = (timestamp == 0) ? time(NULL) : timestamp;
cJSON_AddStringToObject(event, "pubkey", pubkey_hex);
cJSON_AddNumberToObject(event, "created_at", (double)event_time);
cJSON_AddNumberToObject(event, "kind", kind);
// Add tags (copy provided tags or create empty array)
if (tags) {
cJSON_AddItemToObject(event, "tags", cJSON_Duplicate(tags, 1));
} else {
cJSON_AddItemToObject(event, "tags", cJSON_CreateArray());
}
cJSON_AddStringToObject(event, "content", content);
// ============================================================================
// INLINE SERIALIZATION AND SIGNING LOGIC
// ============================================================================
// Get event fields for serialization
cJSON* pubkey_item = cJSON_GetObjectItem(event, "pubkey");
cJSON* created_at_item = cJSON_GetObjectItem(event, "created_at");
cJSON* kind_item = cJSON_GetObjectItem(event, "kind");
cJSON* tags_item = cJSON_GetObjectItem(event, "tags");
cJSON* content_item = cJSON_GetObjectItem(event, "content");
if (!pubkey_item || !created_at_item || !kind_item || !tags_item || !content_item) {
cJSON_Delete(event);
return NULL;
}
// Create serialization array: [0, pubkey, created_at, kind, tags, content]
cJSON* serialize_array = cJSON_CreateArray();
if (!serialize_array) {
cJSON_Delete(event);
return NULL;
}
cJSON_AddItemToArray(serialize_array, cJSON_CreateNumber(0));
cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(pubkey_item, 1));
cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(created_at_item, 1));
cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(kind_item, 1));
cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(tags_item, 1));
cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(content_item, 1));
char* serialize_string = cJSON_PrintUnformatted(serialize_array);
cJSON_Delete(serialize_array);
if (!serialize_string) {
cJSON_Delete(event);
return NULL;
}
// Hash the serialized event
unsigned char event_hash[32];
if (nostr_sha256((const unsigned char*)serialize_string, strlen(serialize_string), event_hash) != 0) {
free(serialize_string);
cJSON_Delete(event);
return NULL;
}
// Convert hash to hex for event ID
char event_id[65];
nostr_bytes_to_hex(event_hash, 32, event_id);
// Sign the hash using ECDSA
unsigned char signature[64];
if (nostr_ec_sign(private_key, event_hash, signature) != 0) {
free(serialize_string);
cJSON_Delete(event);
return NULL;
}
// Convert signature to hex
char sig_hex[129];
nostr_bytes_to_hex(signature, 64, sig_hex);
// Add ID and signature to the event
cJSON_AddStringToObject(event, "id", event_id);
cJSON_AddStringToObject(event, "sig", sig_hex);
free(serialize_string);
return event;
}
/**
* Validate the structure of a NOSTR event
* Checks required fields, types, and basic format validation
*/
int nostr_validate_event_structure(cJSON* event) {
if (!event || !cJSON_IsObject(event)) {
return NOSTR_ERROR_EVENT_INVALID_STRUCTURE;
}
// Check required fields exist
cJSON* id_item = cJSON_GetObjectItem(event, "id");
cJSON* pubkey_item = cJSON_GetObjectItem(event, "pubkey");
cJSON* created_at_item = cJSON_GetObjectItem(event, "created_at");
cJSON* kind_item = cJSON_GetObjectItem(event, "kind");
cJSON* tags_item = cJSON_GetObjectItem(event, "tags");
cJSON* content_item = cJSON_GetObjectItem(event, "content");
cJSON* sig_item = cJSON_GetObjectItem(event, "sig");
if (!id_item || !pubkey_item || !created_at_item || !kind_item ||
!tags_item || !content_item || !sig_item) {
return NOSTR_ERROR_EVENT_INVALID_STRUCTURE;
}
// Validate field types
if (!cJSON_IsString(id_item)) return NOSTR_ERROR_EVENT_INVALID_ID;
if (!cJSON_IsString(pubkey_item)) return NOSTR_ERROR_EVENT_INVALID_PUBKEY;
if (!cJSON_IsNumber(created_at_item)) return NOSTR_ERROR_EVENT_INVALID_CREATED_AT;
if (!cJSON_IsNumber(kind_item)) return NOSTR_ERROR_EVENT_INVALID_KIND;
if (!cJSON_IsArray(tags_item)) return NOSTR_ERROR_EVENT_INVALID_TAGS;
if (!cJSON_IsString(content_item)) return NOSTR_ERROR_EVENT_INVALID_CONTENT;
if (!cJSON_IsString(sig_item)) return NOSTR_ERROR_EVENT_INVALID_SIGNATURE;
// Validate hex string lengths
const char* id_str = cJSON_GetStringValue(id_item);
const char* pubkey_str = cJSON_GetStringValue(pubkey_item);
const char* sig_str = cJSON_GetStringValue(sig_item);
if (!id_str || strlen(id_str) != 64) return NOSTR_ERROR_EVENT_INVALID_ID;
if (!pubkey_str || strlen(pubkey_str) != 64) return NOSTR_ERROR_EVENT_INVALID_PUBKEY;
if (!sig_str || strlen(sig_str) != 128) return NOSTR_ERROR_EVENT_INVALID_SIGNATURE;
// Validate hex characters (lowercase)
for (int i = 0; i < 64; i++) {
char c = id_str[i];
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) {
return NOSTR_ERROR_EVENT_INVALID_ID;
}
c = pubkey_str[i];
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) {
return NOSTR_ERROR_EVENT_INVALID_PUBKEY;
}
}
// Validate signature hex characters (lowercase) - 128 characters
for (int i = 0; i < 128; i++) {
char c = sig_str[i];
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) {
return NOSTR_ERROR_EVENT_INVALID_SIGNATURE;
}
}
// Validate created_at is a valid timestamp (positive number)
double created_at = cJSON_GetNumberValue(created_at_item);
if (created_at < 0) return NOSTR_ERROR_EVENT_INVALID_CREATED_AT;
// Validate kind is valid (0-65535)
double kind = cJSON_GetNumberValue(kind_item);
if (kind < 0 || kind > 65535 || kind != (int)kind) {
return NOSTR_ERROR_EVENT_INVALID_KIND;
}
// Validate tags array structure (array of arrays of strings)
cJSON* tag_item;
cJSON_ArrayForEach(tag_item, tags_item) {
if (!cJSON_IsArray(tag_item)) {
return NOSTR_ERROR_EVENT_INVALID_TAGS;
}
cJSON* tag_element;
cJSON_ArrayForEach(tag_element, tag_item) {
if (!cJSON_IsString(tag_element)) {
return NOSTR_ERROR_EVENT_INVALID_TAGS;
}
}
}
return NOSTR_SUCCESS;
}
/**
* Verify the cryptographic signature of a NOSTR event
* Validates event ID and signature according to NIP-01
*/
int nostr_verify_event_signature(cJSON* event) {
if (!event) {
return NOSTR_ERROR_INVALID_INPUT;
}
// Get event fields
cJSON* id_item = cJSON_GetObjectItem(event, "id");
cJSON* pubkey_item = cJSON_GetObjectItem(event, "pubkey");
cJSON* created_at_item = cJSON_GetObjectItem(event, "created_at");
cJSON* kind_item = cJSON_GetObjectItem(event, "kind");
cJSON* tags_item = cJSON_GetObjectItem(event, "tags");
cJSON* content_item = cJSON_GetObjectItem(event, "content");
cJSON* sig_item = cJSON_GetObjectItem(event, "sig");
if (!id_item || !pubkey_item || !created_at_item || !kind_item ||
!tags_item || !content_item || !sig_item) {
return NOSTR_ERROR_EVENT_INVALID_STRUCTURE;
}
// Create serialization array: [0, pubkey, created_at, kind, tags, content]
cJSON* serialize_array = cJSON_CreateArray();
if (!serialize_array) {
return NOSTR_ERROR_MEMORY_FAILED;
}
cJSON_AddItemToArray(serialize_array, cJSON_CreateNumber(0));
cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(pubkey_item, 1));
cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(created_at_item, 1));
cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(kind_item, 1));
cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(tags_item, 1));
cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(content_item, 1));
char* serialize_string = cJSON_PrintUnformatted(serialize_array);
cJSON_Delete(serialize_array);
if (!serialize_string) {
return NOSTR_ERROR_MEMORY_FAILED;
}
// Hash the serialized event
unsigned char event_hash[32];
if (nostr_sha256((const unsigned char*)serialize_string, strlen(serialize_string), event_hash) != 0) {
free(serialize_string);
return NOSTR_ERROR_CRYPTO_FAILED;
}
// Convert hash to hex for event ID verification
char calculated_id[65];
nostr_bytes_to_hex(event_hash, 32, calculated_id);
// Compare with provided event ID
const char* provided_id = cJSON_GetStringValue(id_item);
if (!provided_id || strcmp(calculated_id, provided_id) != 0) {
free(serialize_string);
return NOSTR_ERROR_EVENT_INVALID_ID;
}
// Verify signature
const char* pubkey_str = cJSON_GetStringValue(pubkey_item);
const char* sig_str = cJSON_GetStringValue(sig_item);
if (!pubkey_str || !sig_str) {
free(serialize_string);
return NOSTR_ERROR_EVENT_INVALID_STRUCTURE;
}
// Convert hex strings to bytes
unsigned char pubkey_bytes[32];
unsigned char sig_bytes[64];
if (nostr_hex_to_bytes(pubkey_str, pubkey_bytes, 32) != 0 ||
nostr_hex_to_bytes(sig_str, sig_bytes, 64) != 0) {
free(serialize_string);
return NOSTR_ERROR_CRYPTO_FAILED;
}
// Parse the public key into secp256k1 format
nostr_secp256k1_xonly_pubkey xonly_pubkey;
if (!nostr_secp256k1_xonly_pubkey_parse(&xonly_pubkey, pubkey_bytes)) {
free(serialize_string);
return NOSTR_ERROR_EVENT_INVALID_PUBKEY;
}
// Verify Schnorr signature
if (!nostr_secp256k1_schnorrsig_verify(sig_bytes, event_hash, &xonly_pubkey)) {
free(serialize_string);
return NOSTR_ERROR_EVENT_INVALID_SIGNATURE;
}
free(serialize_string);
return NOSTR_SUCCESS;
}
/**
* Complete validation of a NOSTR event
* Performs both structure and cryptographic validation
*/
int nostr_validate_event(cJSON* event) {
// First validate structure (fast check)
int structure_result = nostr_validate_event_structure(event);
if (structure_result != NOSTR_SUCCESS) {
return structure_result;
}
// Then verify signature (expensive check)
return nostr_verify_event_signature(event);
}

View File

@@ -0,0 +1,23 @@
/*
* NOSTR Core Library - NIP-001: Basic Protocol Flow
*
* Event creation, signing, serialization and core protocol functions
*/
#ifndef NIP001_H
#define NIP001_H
#include <stdint.h>
#include <time.h>
#include "../cjson/cJSON.h"
// Function declarations
cJSON* nostr_create_and_sign_event(int kind, const char* content, cJSON* tags, const unsigned char* private_key, time_t timestamp);
// Event validation functions
int nostr_validate_event_structure(cJSON* event);
int nostr_verify_event_signature(cJSON* event);
int nostr_validate_event(cJSON* event);
#endif // NIP001_H

View File

@@ -0,0 +1,104 @@
/*
* NOSTR Core Library - NIP-006: Key Derivation from Mnemonic
*/
#include "nip006.h"
#include "utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include "../nostr_core/nostr_common.h"
#include "nostr_platform.h"
int nostr_generate_keypair(unsigned char* private_key, unsigned char* public_key) {
if (!private_key || !public_key) {
return NOSTR_ERROR_INVALID_INPUT;
}
if (nostr_platform_random(private_key, 32) != 0) {
return NOSTR_ERROR_IO_FAILED;
}
// Validate private key
if (nostr_ec_private_key_verify(private_key) != 0) {
return NOSTR_ERROR_CRYPTO_FAILED;
}
// Generate public key from private key (already x-only for NOSTR)
if (nostr_ec_public_key_from_private_key(private_key, public_key) != 0) {
return NOSTR_ERROR_CRYPTO_FAILED;
}
return NOSTR_SUCCESS;
}
int nostr_generate_mnemonic_and_keys(char* mnemonic, size_t mnemonic_size,
int account, unsigned char* private_key,
unsigned char* public_key) {
if (!mnemonic || mnemonic_size < 256 || !private_key || !public_key) {
return NOSTR_ERROR_INVALID_INPUT;
}
// Generate entropy for 12-word mnemonic
unsigned char entropy[16];
if (nostr_platform_random(entropy, sizeof(entropy)) != 0) {
return NOSTR_ERROR_IO_FAILED;
}
// Generate mnemonic from entropy
if (nostr_bip39_mnemonic_from_bytes(entropy, sizeof(entropy), mnemonic) != 0) {
return NOSTR_ERROR_CRYPTO_FAILED;
}
// Derive keys from the generated mnemonic
return nostr_derive_keys_from_mnemonic(mnemonic, account, private_key, public_key);
}
int nostr_derive_keys_from_mnemonic(const char* mnemonic, int account,
unsigned char* private_key, unsigned char* public_key) {
if (!mnemonic || !private_key || !public_key) {
return NOSTR_ERROR_INVALID_INPUT;
}
// Validate mnemonic
if (nostr_bip39_mnemonic_validate(mnemonic) != 0) {
return NOSTR_ERROR_INVALID_INPUT;
}
// Convert mnemonic to seed
unsigned char seed[64];
if (nostr_bip39_mnemonic_to_seed(mnemonic, "", seed, sizeof(seed)) != 0) {
return NOSTR_ERROR_CRYPTO_FAILED;
}
// Derive master key from seed
nostr_hd_key_t master_key;
if (nostr_bip32_key_from_seed(seed, sizeof(seed), &master_key) != 0) {
return NOSTR_ERROR_CRYPTO_FAILED;
}
// NIP-06 path: m/44'/1237'/account'/0/0
nostr_hd_key_t derived_key;
uint32_t path[] = {
0x80000000 + 44, // 44' (hardened)
0x80000000 + 1237, // 1237' (hardened)
0x80000000 + account, // account' (hardened)
0, // 0 (not hardened)
0 // 0 (not hardened)
};
if (nostr_bip32_derive_path(&master_key, path, sizeof(path) / sizeof(path[0]), &derived_key) != 0) {
return NOSTR_ERROR_CRYPTO_FAILED;
}
// Extract private key and public key
memcpy(private_key, derived_key.private_key, 32);
memcpy(public_key, derived_key.public_key + 1, 32); // Remove compression prefix for x-only
return NOSTR_SUCCESS;
}
// Note: nostr_detect_input_type, nostr_decode_nsec, and nostr_decode_npub
// are implemented in NIP-019 to avoid multiple definitions

View File

@@ -0,0 +1,30 @@
/*
* NOSTR Core Library - NIP-006: Key Derivation from Mnemonic
*/
#ifndef NIP006_H
#define NIP006_H
#include "nip001.h"
#include <stdint.h>
// Input type detection
typedef enum {
NOSTR_INPUT_UNKNOWN = 0,
NOSTR_INPUT_NSEC_HEX,
NOSTR_INPUT_NSEC_BECH32,
NOSTR_INPUT_MNEMONIC
} nostr_input_type_t;
// Function declarations
int nostr_generate_keypair(unsigned char* private_key, unsigned char* public_key);
int nostr_generate_mnemonic_and_keys(char* mnemonic, size_t mnemonic_size,
int account, unsigned char* private_key,
unsigned char* public_key);
int nostr_derive_keys_from_mnemonic(const char* mnemonic, int account,
unsigned char* private_key, unsigned char* public_key);
nostr_input_type_t nostr_detect_input_type(const char* input);
int nostr_decode_nsec(const char* input, unsigned char* private_key);
int nostr_decode_npub(const char* input, unsigned char* public_key);
#endif // NIP006_H

View File

@@ -0,0 +1,265 @@
/*
* NOSTR Core Library - NIP-019: Bech32-encoded Entities
*/
#include "nip019.h"
#include "utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "../nostr_core/nostr_common.h"
#define BECH32_CONST 1
static const char bech32_charset[] = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
static const int8_t bech32_charset_rev[128] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
};
static uint32_t bech32_polymod_step(uint32_t pre) {
uint8_t b = pre >> 25;
return ((pre & 0x1FFFFFF) << 5) ^
(-((b >> 0) & 1) & 0x3b6a57b2UL) ^
(-((b >> 1) & 1) & 0x26508e6dUL) ^
(-((b >> 2) & 1) & 0x1ea119faUL) ^
(-((b >> 3) & 1) & 0x3d4233ddUL) ^
(-((b >> 4) & 1) & 0x2a1462b3UL);
}
static int convert_bits(uint8_t *out, size_t *outlen, int outbits, const uint8_t *in, size_t inlen, int inbits, int pad) {
uint32_t val = 0;
int bits = 0;
uint32_t maxv = (((uint32_t)1) << outbits) - 1;
*outlen = 0;
while (inlen--) {
val = (val << inbits) | *(in++);
bits += inbits;
while (bits >= outbits) {
bits -= outbits;
out[(*outlen)++] = (val >> bits) & maxv;
}
}
if (pad) {
if (bits) {
out[(*outlen)++] = (val << (outbits - bits)) & maxv;
}
} else if (((val << (outbits - bits)) & maxv) || bits >= inbits) {
return 0;
}
return 1;
}
static int bech32_encode(char *output, const char *hrp, const uint8_t *data, size_t data_len) {
uint32_t chk = 1;
size_t i, hrp_len = strlen(hrp);
for (i = 0; i < hrp_len; ++i) {
int ch = hrp[i];
if (ch < 33 || ch > 126) return 0;
if (ch >= 'A' && ch <= 'Z') return 0;
chk = bech32_polymod_step(chk) ^ (ch >> 5);
}
chk = bech32_polymod_step(chk);
for (i = 0; i < hrp_len; ++i) {
chk = bech32_polymod_step(chk) ^ (hrp[i] & 0x1f);
*(output++) = hrp[i];
}
*(output++) = '1';
for (i = 0; i < data_len; ++i) {
if (*data >> 5) return 0;
chk = bech32_polymod_step(chk) ^ (*data);
*(output++) = bech32_charset[*(data++)];
}
for (i = 0; i < 6; ++i) {
chk = bech32_polymod_step(chk);
}
chk ^= BECH32_CONST;
for (i = 0; i < 6; ++i) {
*(output++) = bech32_charset[(chk >> ((5 - i) * 5)) & 0x1f];
}
*output = 0;
return 1;
}
static int bech32_decode(const char* input, const char* hrp, unsigned char* data, size_t* data_len) {
if (!input || !hrp || !data || !data_len) {
return 0;
}
size_t input_len = strlen(input);
size_t hrp_len = strlen(hrp);
if (input_len < hrp_len + 7) return 0;
if (strncmp(input, hrp, hrp_len) != 0) return 0;
if (input[hrp_len] != '1') return 0;
const char* data_part = input + hrp_len + 1;
size_t data_part_len = input_len - hrp_len - 1;
uint8_t values[256];
for (size_t i = 0; i < data_part_len; i++) {
unsigned char c = (unsigned char)data_part[i];
if (c >= 128) return 0;
int8_t val = bech32_charset_rev[c];
if (val == -1) return 0;
values[i] = (uint8_t)val;
}
if (data_part_len < 6) return 0;
uint32_t chk = 1;
for (size_t i = 0; i < hrp_len; i++) {
chk = bech32_polymod_step(chk) ^ (hrp[i] >> 5);
}
chk = bech32_polymod_step(chk);
for (size_t i = 0; i < hrp_len; i++) {
chk = bech32_polymod_step(chk) ^ (hrp[i] & 0x1f);
}
for (size_t i = 0; i < data_part_len; i++) {
chk = bech32_polymod_step(chk) ^ values[i];
}
if (chk != BECH32_CONST) return 0;
size_t payload_len = data_part_len - 6;
size_t decoded_len;
if (!convert_bits(data, &decoded_len, 8, values, payload_len, 5, 0)) {
return 0;
}
*data_len = decoded_len;
return 1;
}
int nostr_key_to_bech32(const unsigned char* key, const char* hrp, char* output) {
if (!key || !hrp || !output) {
return NOSTR_ERROR_INVALID_INPUT;
}
uint8_t conv[64];
size_t conv_len;
if (!convert_bits(conv, &conv_len, 5, key, 32, 8, 1)) {
return NOSTR_ERROR_CRYPTO_FAILED;
}
if (!bech32_encode(output, hrp, conv, conv_len)) {
return NOSTR_ERROR_CRYPTO_FAILED;
}
return NOSTR_SUCCESS;
}
nostr_input_type_t nostr_detect_input_type(const char* input) {
if (!input || strlen(input) == 0) {
return NOSTR_INPUT_UNKNOWN;
}
size_t len = strlen(input);
// Check for bech32 nsec
if (len > 5 && strncmp(input, "nsec1", 5) == 0) {
return NOSTR_INPUT_NSEC_BECH32;
}
// Check for hex nsec (64 characters, all hex)
if (len == 64) {
int is_hex = 1;
for (size_t i = 0; i < len; i++) {
if (!isxdigit((unsigned char)input[i])) {
is_hex = 0;
break;
}
}
if (is_hex) {
return NOSTR_INPUT_NSEC_HEX;
}
}
// Check for mnemonic (space-separated words)
int word_count = 0;
char temp[1024];
strncpy(temp, input, sizeof(temp) - 1);
temp[sizeof(temp) - 1] = '\0';
char* token = strtok(temp, " ");
while (token != NULL) {
word_count++;
token = strtok(NULL, " ");
}
// BIP39 mnemonics are typically 12, 18, or 24 words
if (word_count >= 12 && word_count <= 24) {
return NOSTR_INPUT_MNEMONIC;
}
return NOSTR_INPUT_UNKNOWN;
}
int nostr_decode_nsec(const char* input, unsigned char* private_key) {
if (!input || !private_key) {
return NOSTR_ERROR_INVALID_INPUT;
}
nostr_input_type_t type = nostr_detect_input_type(input);
if (type == NOSTR_INPUT_NSEC_HEX) {
if (nostr_hex_to_bytes(input, private_key, 32) != NOSTR_SUCCESS) {
return NOSTR_ERROR_INVALID_INPUT;
}
} else if (type == NOSTR_INPUT_NSEC_BECH32) {
size_t decoded_len;
if (!bech32_decode(input, "nsec", private_key, &decoded_len)) {
return NOSTR_ERROR_INVALID_INPUT;
}
if (decoded_len != 32) {
return NOSTR_ERROR_INVALID_INPUT;
}
} else {
return NOSTR_ERROR_INVALID_INPUT;
}
// TODO: Add private key validation if crypto functions are available
return NOSTR_SUCCESS;
}
int nostr_decode_npub(const char* input, unsigned char* public_key) {
if (!input || !public_key) {
return NOSTR_ERROR_INVALID_INPUT;
}
nostr_input_type_t type = nostr_detect_input_type(input);
if (type == NOSTR_INPUT_NSEC_HEX) { // Actually public key hex
if (nostr_hex_to_bytes(input, public_key, 32) != NOSTR_SUCCESS) {
return NOSTR_ERROR_INVALID_INPUT;
}
} else if (strncmp(input, "npub1", 4) == 0) { // Bech32 npub
size_t decoded_len;
if (!bech32_decode(input, "npub", public_key, &decoded_len)) {
return NOSTR_ERROR_INVALID_INPUT;
}
if (decoded_len != 32) {
return NOSTR_ERROR_INVALID_INPUT;
}
} else {
return NOSTR_ERROR_INVALID_INPUT;
}
return NOSTR_SUCCESS;
}

View File

@@ -0,0 +1,17 @@
/*
* NOSTR Core Library - NIP-019: Bech32-encoded Entities
*/
#ifndef NIP019_H
#define NIP019_H
#include "nip001.h"
#include "nip006.h" // For nostr_input_type_t enum
// Function declarations
int nostr_key_to_bech32(const unsigned char* key, const char* hrp, char* output);
nostr_input_type_t nostr_detect_input_type(const char* input);
int nostr_decode_nsec(const char* input, unsigned char* private_key);
int nostr_decode_npub(const char* input, unsigned char* public_key);
#endif // NIP019_H

View File

@@ -0,0 +1,106 @@
/*
* NOSTR Core Library - Common Utilities
*
* Common functions and utilities shared across the library
*/
#include "nostr_common.h"
#include "utils.h"
/**
* Convert error code to human-readable string
* Handles all error codes defined in nostr_common.h
*/
const char* nostr_strerror(int error_code) {
switch (error_code) {
case NOSTR_SUCCESS: return "Success";
case NOSTR_ERROR_INVALID_INPUT: return "Invalid input";
case NOSTR_ERROR_CRYPTO_FAILED: return "Cryptographic operation failed";
case NOSTR_ERROR_MEMORY_FAILED: return "Memory allocation failed";
case NOSTR_ERROR_IO_FAILED: return "I/O operation failed";
case NOSTR_ERROR_NETWORK_FAILED: return "Network operation failed";
case NOSTR_ERROR_NIP04_INVALID_FORMAT: return "NIP-04 invalid format";
case NOSTR_ERROR_NIP04_DECRYPT_FAILED: return "NIP-04 decryption failed";
case NOSTR_ERROR_NIP04_BUFFER_TOO_SMALL: return "NIP-04 buffer too small";
case NOSTR_ERROR_NIP44_INVALID_FORMAT: return "NIP-44: Invalid format";
case NOSTR_ERROR_NIP44_DECRYPT_FAILED: return "NIP-44: Decryption failed";
case NOSTR_ERROR_NIP44_BUFFER_TOO_SMALL: return "NIP-44: Buffer too small";
case NOSTR_ERROR_NIP05_INVALID_IDENTIFIER: return "NIP-05: Invalid identifier format";
case NOSTR_ERROR_NIP05_HTTP_FAILED: return "NIP-05: HTTP request failed";
case NOSTR_ERROR_NIP05_JSON_PARSE_FAILED: return "NIP-05: JSON parsing failed";
case NOSTR_ERROR_NIP05_NAME_NOT_FOUND: return "NIP-05: Name not found in .well-known";
case NOSTR_ERROR_NIP05_PUBKEY_MISMATCH: return "NIP-05: Public key mismatch";
case NOSTR_ERROR_EVENT_INVALID_STRUCTURE: return "Event has invalid structure";
case NOSTR_ERROR_EVENT_INVALID_ID: return "Event has invalid ID";
case NOSTR_ERROR_EVENT_INVALID_PUBKEY: return "Event has invalid public key";
case NOSTR_ERROR_EVENT_INVALID_SIGNATURE: return "Event has invalid signature";
case NOSTR_ERROR_EVENT_INVALID_CREATED_AT: return "Event has invalid timestamp";
case NOSTR_ERROR_EVENT_INVALID_KIND: return "Event has invalid kind";
case NOSTR_ERROR_EVENT_INVALID_TAGS: return "Event has invalid tags";
case NOSTR_ERROR_EVENT_INVALID_CONTENT: return "Event has invalid content";
case NOSTR_ERROR_NIP13_INSUFFICIENT: return "NIP-13: Insufficient PoW difficulty";
case NOSTR_ERROR_NIP13_NO_NONCE_TAG: return "NIP-13: Missing nonce tag";
case NOSTR_ERROR_NIP13_INVALID_NONCE_TAG: return "NIP-13: Invalid nonce tag format";
case NOSTR_ERROR_NIP13_TARGET_MISMATCH: return "NIP-13: Target difficulty mismatch";
case NOSTR_ERROR_NIP13_CALCULATION: return "NIP-13: PoW calculation error";
case NOSTR_ERROR_NIP42_INVALID_CHALLENGE: return "NIP-42: Invalid challenge";
case NOSTR_ERROR_NIP42_CHALLENGE_EXPIRED: return "NIP-42: Challenge expired";
case NOSTR_ERROR_NIP42_AUTH_EVENT_INVALID: return "NIP-42: Authentication event invalid";
case NOSTR_ERROR_NIP42_URL_MISMATCH: return "NIP-42: Relay URL mismatch";
case NOSTR_ERROR_NIP42_TIME_TOLERANCE: return "NIP-42: Timestamp outside tolerance";
case NOSTR_ERROR_NIP42_NOT_AUTHENTICATED: return "NIP-42: Client not authenticated";
case NOSTR_ERROR_NIP42_INVALID_MESSAGE_FORMAT: return "NIP-42: Invalid message format";
case NOSTR_ERROR_NIP42_CHALLENGE_TOO_SHORT: return "NIP-42: Challenge too short";
case NOSTR_ERROR_NIP42_CHALLENGE_TOO_LONG: return "NIP-42: Challenge too long";
case NOSTR_ERROR_NIP46_INVALID_BUNKER_URL: return "NIP-46: Invalid bunker URL";
case NOSTR_ERROR_NIP46_INVALID_NOSTRCONNECT: return "NIP-46: Invalid nostrconnect URL";
case NOSTR_ERROR_NIP46_INVALID_REQUEST: return "NIP-46: Invalid request payload";
case NOSTR_ERROR_NIP46_INVALID_RESPONSE: return "NIP-46: Invalid response payload";
case NOSTR_ERROR_NIP46_ENCRYPTION_FAILED: return "NIP-46: Encryption failed";
case NOSTR_ERROR_NIP46_DECRYPTION_FAILED: return "NIP-46: Decryption failed";
case NOSTR_ERROR_NIP46_CONNECTION_FAILED: return "NIP-46: Connection failed";
case NOSTR_ERROR_NIP46_TIMEOUT: return "NIP-46: Timeout";
case NOSTR_ERROR_NIP46_SECRET_MISMATCH: return "NIP-46: Secret mismatch";
case NOSTR_ERROR_NIP46_UNKNOWN_METHOD: return "NIP-46: Unknown method";
case NOSTR_ERROR_NIP46_AUTH_CHALLENGE: return "NIP-46: Auth challenge required";
case NOSTR_ERROR_NIP46_NOT_CONNECTED: return "NIP-46: Not connected";
case NOSTR_ERROR_NIP60_INVALID_WALLET: return "NIP-60: Invalid wallet event";
case NOSTR_ERROR_NIP60_INVALID_TOKEN: return "NIP-60: Invalid token event";
case NOSTR_ERROR_NIP60_INVALID_HISTORY: return "NIP-60: Invalid history event";
case NOSTR_ERROR_NIP60_INVALID_QUOTE: return "NIP-60: Invalid quote event";
case NOSTR_ERROR_NIP60_DECRYPT_FAILED: return "NIP-60: Decryption failed";
case NOSTR_ERROR_NIP60_INVALID_PROOFS: return "NIP-60: Invalid proofs payload";
case NOSTR_ERROR_NIP60_INSUFFICIENT_FUNDS: return "NIP-60: Insufficient funds";
case NOSTR_ERROR_NIP61_INVALID_INFO: return "NIP-61: Invalid info event";
case NOSTR_ERROR_NIP61_INVALID_NUTZAP: return "NIP-61: Invalid nutzap event";
case NOSTR_ERROR_NIP61_MINT_MISMATCH: return "NIP-61: Mint mismatch";
case NOSTR_ERROR_NIP61_PUBKEY_MISMATCH: return "NIP-61: Pubkey mismatch";
case NOSTR_ERROR_NIP61_VERIFICATION_FAILED: return "NIP-61: Verification failed";
case NOSTR_ERROR_CASHU_HTTP_FAILED: return "Cashu: HTTP request failed";
case NOSTR_ERROR_CASHU_JSON_PARSE_FAILED: return "Cashu: JSON parsing failed";
case NOSTR_ERROR_CASHU_MINT_ERROR: return "Cashu: Mint returned an error";
case NOSTR_ERROR_CASHU_QUOTE_NOT_PAID: return "Cashu: Quote not paid";
case NOSTR_ERROR_CASHU_QUOTE_EXPIRED: return "Cashu: Quote expired";
case NOSTR_ERROR_CASHU_PROOFS_SPENT: return "Cashu: One or more proofs are already spent";
case NOSTR_ERROR_CASHU_CRYPTO_FAILED: return "Cashu: Cryptographic operation failed";
case NOSTR_ERROR_CASHU_INVALID_KEYSET: return "Cashu: Invalid keyset";
default: return "Unknown error";
}
}
/**
* Initialize the NOSTR library
*/
int nostr_init(void) {
if (nostr_crypto_init() != 0) {
return NOSTR_ERROR_CRYPTO_FAILED;
}
return NOSTR_SUCCESS;
}
/**
* Cleanup the NOSTR library
*/
void nostr_cleanup(void) {
nostr_crypto_cleanup();
}

View File

@@ -0,0 +1,131 @@
/*
* NOSTR Core - Common Definitions
* Shared error constants and basic types for the modular NOSTR library
*/
#ifndef NOSTR_COMMON_H
#define NOSTR_COMMON_H
#include <stddef.h>
#include <stdint.h>
// Return codes
#define NOSTR_SUCCESS 0
#define NOSTR_ERROR_INVALID_INPUT -1
#define NOSTR_ERROR_CRYPTO_FAILED -2
#define NOSTR_ERROR_MEMORY_FAILED -3
#define NOSTR_ERROR_IO_FAILED -4
#define NOSTR_ERROR_NETWORK_FAILED -5
#define NOSTR_ERROR_NIP04_INVALID_FORMAT -10
#define NOSTR_ERROR_NIP04_DECRYPT_FAILED -11
#define NOSTR_ERROR_NIP04_BUFFER_TOO_SMALL -12
#define NOSTR_ERROR_NIP44_INVALID_FORMAT -13
#define NOSTR_ERROR_NIP44_DECRYPT_FAILED -14
#define NOSTR_ERROR_NIP44_BUFFER_TOO_SMALL -15
#define NOSTR_ERROR_NIP05_INVALID_IDENTIFIER -16
#define NOSTR_ERROR_NIP05_HTTP_FAILED -17
#define NOSTR_ERROR_NIP05_JSON_PARSE_FAILED -18
#define NOSTR_ERROR_NIP05_NAME_NOT_FOUND -19
#define NOSTR_ERROR_NIP05_PUBKEY_MISMATCH -20
#define NOSTR_ERROR_EVENT_INVALID_STRUCTURE -30
#define NOSTR_ERROR_EVENT_INVALID_ID -31
#define NOSTR_ERROR_EVENT_INVALID_PUBKEY -32
#define NOSTR_ERROR_EVENT_INVALID_SIGNATURE -33
#define NOSTR_ERROR_EVENT_INVALID_CREATED_AT -34
#define NOSTR_ERROR_EVENT_INVALID_KIND -35
#define NOSTR_ERROR_EVENT_INVALID_TAGS -36
#define NOSTR_ERROR_EVENT_INVALID_CONTENT -37
// Authentication Rules System Error Codes
#define NOSTR_ERROR_AUTH_RULES_DISABLED -50
#define NOSTR_ERROR_AUTH_RULES_DENIED -51
#define NOSTR_ERROR_AUTH_RULES_DB_FAILED -52
#define NOSTR_ERROR_AUTH_RULES_INVALID_RULE -53
#define NOSTR_ERROR_AUTH_RULES_CACHE_FAILED -54
#define NOSTR_ERROR_AUTH_RULES_BACKEND_NOT_FOUND -55
// NIP-13 PoW-specific error codes
#define NOSTR_ERROR_NIP13_INSUFFICIENT -100
#define NOSTR_ERROR_NIP13_NO_NONCE_TAG -101
#define NOSTR_ERROR_NIP13_INVALID_NONCE_TAG -102
#define NOSTR_ERROR_NIP13_TARGET_MISMATCH -103
#define NOSTR_ERROR_NIP13_CALCULATION -104
// NIP-42 Authentication-specific error codes
#define NOSTR_ERROR_NIP42_INVALID_CHALLENGE -200
#define NOSTR_ERROR_NIP42_CHALLENGE_EXPIRED -201
#define NOSTR_ERROR_NIP42_AUTH_EVENT_INVALID -202
#define NOSTR_ERROR_NIP42_URL_MISMATCH -203
#define NOSTR_ERROR_NIP42_TIME_TOLERANCE -204
#define NOSTR_ERROR_NIP42_NOT_AUTHENTICATED -205
#define NOSTR_ERROR_NIP42_INVALID_MESSAGE_FORMAT -206
#define NOSTR_ERROR_NIP42_CHALLENGE_TOO_SHORT -207
#define NOSTR_ERROR_NIP42_CHALLENGE_TOO_LONG -208
// NIP-46 Remote Signing error codes
#define NOSTR_ERROR_NIP46_INVALID_BUNKER_URL -300
#define NOSTR_ERROR_NIP46_INVALID_NOSTRCONNECT -301
#define NOSTR_ERROR_NIP46_INVALID_REQUEST -302
#define NOSTR_ERROR_NIP46_INVALID_RESPONSE -303
#define NOSTR_ERROR_NIP46_ENCRYPTION_FAILED -304
#define NOSTR_ERROR_NIP46_DECRYPTION_FAILED -305
#define NOSTR_ERROR_NIP46_CONNECTION_FAILED -306
#define NOSTR_ERROR_NIP46_TIMEOUT -307
#define NOSTR_ERROR_NIP46_SECRET_MISMATCH -308
#define NOSTR_ERROR_NIP46_UNKNOWN_METHOD -309
#define NOSTR_ERROR_NIP46_AUTH_CHALLENGE -310
#define NOSTR_ERROR_NIP46_NOT_CONNECTED -311
// NIP-60 Cashu Wallet error codes
#define NOSTR_ERROR_NIP60_INVALID_WALLET -400
#define NOSTR_ERROR_NIP60_INVALID_TOKEN -401
#define NOSTR_ERROR_NIP60_INVALID_HISTORY -402
#define NOSTR_ERROR_NIP60_INVALID_QUOTE -403
#define NOSTR_ERROR_NIP60_DECRYPT_FAILED -404
#define NOSTR_ERROR_NIP60_INVALID_PROOFS -405
#define NOSTR_ERROR_NIP60_INSUFFICIENT_FUNDS -406
// NIP-61 Nutzap error codes
#define NOSTR_ERROR_NIP61_INVALID_INFO -410
#define NOSTR_ERROR_NIP61_INVALID_NUTZAP -411
#define NOSTR_ERROR_NIP61_MINT_MISMATCH -412
#define NOSTR_ERROR_NIP61_PUBKEY_MISMATCH -413
#define NOSTR_ERROR_NIP61_VERIFICATION_FAILED -414
// Cashu Mint client error codes
#define NOSTR_ERROR_CASHU_HTTP_FAILED -420
#define NOSTR_ERROR_CASHU_JSON_PARSE_FAILED -421
#define NOSTR_ERROR_CASHU_MINT_ERROR -422
#define NOSTR_ERROR_CASHU_QUOTE_NOT_PAID -423
#define NOSTR_ERROR_CASHU_QUOTE_EXPIRED -424
#define NOSTR_ERROR_CASHU_PROOFS_SPENT -425
#define NOSTR_ERROR_CASHU_CRYPTO_FAILED -426
#define NOSTR_ERROR_CASHU_INVALID_KEYSET -427
// Constants
#define NOSTR_PRIVATE_KEY_SIZE 32
#define NOSTR_PUBLIC_KEY_SIZE 32
#define NOSTR_HEX_KEY_SIZE 65 // 64 + null terminator
#define NOSTR_BECH32_KEY_SIZE 100
#define NOSTR_MAX_CONTENT_SIZE 2048
#define NOSTR_MAX_URL_SIZE 256
#define NIP05_DEFAULT_TIMEOUT 10
// NIP-04 Constants
#define NOSTR_NIP04_MAX_PLAINTEXT_SIZE 1048576 // 1MB
#define NOSTR_NIP04_MAX_ENCRYPTED_SIZE 22369621 // ~21.3MB (accounts for base64 overhead + IV)
// NIP-44 Constants
#define NOSTR_NIP44_MAX_PLAINTEXT_SIZE 65535 // 64KB - 1 (NIP-44 spec compliant)
// Forward declaration for cJSON (to avoid requiring cJSON.h in header)
struct cJSON;
// Function declarations
const char* nostr_strerror(int error_code);
// Library initialization functions
int nostr_init(void);
void nostr_cleanup(void);
#endif // NOSTR_COMMON_H

View File

@@ -0,0 +1,20 @@
#ifndef NOSTR_PLATFORM_H
#define NOSTR_PLATFORM_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* Fill buffer with cryptographically secure random bytes.
* Returns 0 on success, -1 on failure.
*/
int nostr_platform_random(unsigned char *buf, size_t len);
#ifdef __cplusplus
}
#endif
#endif /* NOSTR_PLATFORM_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,189 @@
/*
* NOSTR Core Library - Utilities
*
* General utility functions used across multiple NIPs
*/
#ifndef NOSTR_UTILS_H
#define NOSTR_UTILS_H
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// =============================================================================
// UTILITY FUNCTIONS
// =============================================================================
// Convert bytes to hexadecimal string
void nostr_bytes_to_hex(const unsigned char *bytes, size_t len, char *hex);
// Convert hexadecimal string to bytes
int nostr_hex_to_bytes(const char *hex, unsigned char *bytes, size_t len);
// Base64 encoding function
size_t base64_encode(const unsigned char *data, size_t len, char *output,
size_t output_size);
// Base64 decoding function
size_t base64_decode(const char *input, unsigned char *output);
// =============================================================================
// CORE CRYPTO FUNCTIONS
// =============================================================================
// Initialize crypto subsystem
int nostr_crypto_init(void);
// Cleanup crypto subsystem
void nostr_crypto_cleanup(void);
// SHA-256 hash function
int nostr_sha256(const unsigned char *data, size_t len, unsigned char *hash);
// =============================================================================
// STREAMING SHA-256 FUNCTIONS
// =============================================================================
// SHA-256 streaming context
typedef struct {
uint32_t state[8]; // Current hash state
unsigned char buffer[64]; // Input buffer for incomplete blocks
uint64_t bitlen; // Total bits processed
size_t buflen; // Current buffer length
} nostr_sha256_ctx_t;
// Initialize SHA-256 streaming context
int nostr_sha256_init(nostr_sha256_ctx_t* ctx);
// Update SHA-256 context with new data
int nostr_sha256_update(nostr_sha256_ctx_t* ctx, const unsigned char* data, size_t len);
// Finalize SHA-256 and output hash
int nostr_sha256_final(nostr_sha256_ctx_t* ctx, unsigned char* hash);
// Stream SHA-256 hash of a file
int nostr_sha256_file_stream(const char* filename, unsigned char* hash);
// HMAC-SHA256
int nostr_hmac_sha256(const unsigned char *key, size_t key_len,
const unsigned char *data, size_t data_len,
unsigned char *output);
// HMAC-SHA512
int nostr_hmac_sha512(const unsigned char *key, size_t key_len,
const unsigned char *data, size_t data_len,
unsigned char *output);
// PBKDF2 with HMAC-SHA512
int nostr_pbkdf2_hmac_sha512(const unsigned char *password, size_t password_len,
const unsigned char *salt, size_t salt_len,
int iterations, unsigned char *output,
size_t output_len);
// SHA-512 implementation (for testing)
int nostr_sha512(const unsigned char *data, size_t len, unsigned char *hash);
// =============================================================================
// SECP256K1 ELLIPTIC CURVE FUNCTIONS
// =============================================================================
// Verify private key is valid
int nostr_ec_private_key_verify(const unsigned char *private_key);
// Generate public key from private key
int nostr_ec_public_key_from_private_key(const unsigned char *private_key,
unsigned char *public_key);
// Sign data with ECDSA
int nostr_ec_sign(const unsigned char *private_key, const unsigned char *hash,
unsigned char *signature);
// RFC 6979 deterministic nonce generation
int nostr_rfc6979_generate_k(const unsigned char *private_key,
const unsigned char *message_hash,
unsigned char *k_out);
int nostr_schnorr_sign(const unsigned char* private_key,
const unsigned char* hash,
unsigned char* signature);
// =============================================================================
// HKDF KEY DERIVATION FUNCTIONS
// =============================================================================
// HKDF Extract step
int nostr_hkdf_extract(const unsigned char *salt, size_t salt_len,
const unsigned char *ikm, size_t ikm_len,
unsigned char *prk);
// HKDF Expand step
int nostr_hkdf_expand(const unsigned char *prk, size_t prk_len,
const unsigned char *info, size_t info_len,
unsigned char *okm, size_t okm_len);
// HKDF (Extract + Expand)
int nostr_hkdf(const unsigned char *salt, size_t salt_len,
const unsigned char *ikm, size_t ikm_len,
const unsigned char *info, size_t info_len, unsigned char *okm,
size_t okm_len);
// ECDH shared secret computation (for debugging)
int ecdh_shared_secret(const unsigned char *private_key,
const unsigned char *public_key_x,
unsigned char *shared_secret);
// =============================================================================
// BIP39 MNEMONIC FUNCTIONS
// =============================================================================
// Generate mnemonic from entropy
int nostr_bip39_mnemonic_from_bytes(const unsigned char *entropy,
size_t entropy_len, char *mnemonic);
// Validate mnemonic
int nostr_bip39_mnemonic_validate(const char *mnemonic);
// Convert mnemonic to seed
int nostr_bip39_mnemonic_to_seed(const char *mnemonic, const char *passphrase,
unsigned char *seed, size_t seed_len);
// =============================================================================
// BIP32 HD WALLET FUNCTIONS
// =============================================================================
typedef struct {
unsigned char private_key[32];
unsigned char public_key[33];
unsigned char chain_code[32];
uint32_t depth;
uint32_t parent_fingerprint;
uint32_t child_number;
} nostr_hd_key_t;
// Create master key from seed
int nostr_bip32_key_from_seed(const unsigned char *seed, size_t seed_len,
nostr_hd_key_t *master_key);
// Derive child key from parent
int nostr_bip32_derive_child(const nostr_hd_key_t *parent_key,
uint32_t child_number, nostr_hd_key_t *child_key);
// Derive key from path
int nostr_bip32_derive_path(const nostr_hd_key_t *master_key,
const uint32_t *path, size_t path_len,
nostr_hd_key_t *derived_key);
#ifdef __cplusplus
}
#endif
#endif // NOSTR_UTILS_H

View File

@@ -0,0 +1,79 @@
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "../nostr_core/nostr_platform.h"
#if defined(ARDUINO)
#include <Arduino.h>
#endif
#if defined(__has_include)
# if __has_include("pico/rand.h")
# include "pico/rand.h"
# define NOSTR_HAVE_PICO_RAND 1
# endif
# if __has_include("hardware/adc.h")
# include "hardware/adc.h"
# define NOSTR_HAVE_PICO_ADC 1
# endif
#endif
static uint32_t xorshift32(uint32_t *state) {
uint32_t x = *state ? *state : 0xA5A5A5A5u;
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
*state = x;
return x;
}
int nostr_platform_random(unsigned char *buf, size_t len) {
if (!buf) {
return -1;
}
size_t i = 0;
#if defined(NOSTR_HAVE_PICO_RAND)
while (i + 4 <= len) {
uint32_t r = get_rand_32();
memcpy(buf + i, &r, 4);
i += 4;
}
if (i < len) {
uint32_t r = get_rand_32();
memcpy(buf + i, &r, len - i);
}
return 0;
#else
uint32_t seed = 0x13579BDFu;
#if defined(ARDUINO)
seed ^= (uint32_t)micros();
seed ^= ((uint32_t)millis() << 16);
seed ^= (uint32_t)(uintptr_t)&seed;
#endif
#if defined(NOSTR_HAVE_PICO_ADC)
adc_init();
adc_set_temp_sensor_enabled(true);
adc_select_input(4);
for (int k = 0; k < 16; ++k) {
seed ^= ((uint32_t)adc_read() << ((k & 3) * 8));
}
#endif
while (i < len) {
uint32_t r = xorshift32(&seed);
#if defined(ARDUINO)
r ^= (uint32_t)micros();
#endif
size_t take = (len - i >= 4) ? 4 : (len - i);
memcpy(buf + i, &r, take);
i += take;
}
return 0;
#endif
}

View File

@@ -0,0 +1,929 @@
#ifndef SECP256K1_H
#define SECP256K1_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
/** Unless explicitly stated all pointer arguments must not be NULL.
*
* The following rules specify the order of arguments in API calls:
*
* 1. Context pointers go first, followed by output arguments, combined
* output/input arguments, and finally input-only arguments.
* 2. Array lengths always immediately follow the argument whose length
* they describe, even if this violates rule 1.
* 3. Within the OUT/OUTIN/IN groups, pointers to data that is typically generated
* later go first. This means: signatures, public nonces, secret nonces,
* messages, public keys, secret keys, tweaks.
* 4. Arguments that are not data pointers go last, from more complex to less
* complex: function pointers, algorithm names, messages, void pointers,
* counts, flags, booleans.
* 5. Opaque data pointers follow the function pointer they are to be passed to.
*/
/** Opaque data structure that holds context information
*
* The primary purpose of context objects is to store randomization data for
* enhanced protection against side-channel leakage. This protection is only
* effective if the context is randomized after its creation. See
* secp256k1_context_create for creation of contexts and
* secp256k1_context_randomize for randomization.
*
* A secondary purpose of context objects is to store pointers to callback
* functions that the library will call when certain error states arise. See
* secp256k1_context_set_error_callback as well as
* secp256k1_context_set_illegal_callback for details. Future library versions
* may use context objects for additional purposes.
*
* A constructed context can safely be used from multiple threads
* simultaneously, but API calls that take a non-const pointer to a context
* need exclusive access to it. In particular this is the case for
* secp256k1_context_destroy, secp256k1_context_preallocated_destroy,
* and secp256k1_context_randomize.
*
* Regarding randomization, either do it once at creation time (in which case
* you do not need any locking for the other calls), or use a read-write lock.
*/
typedef struct secp256k1_context_struct secp256k1_context;
/** Opaque data structure that holds a parsed and valid public key.
*
* The exact representation of data inside is implementation defined and not
* guaranteed to be portable between different platforms or versions. It is
* however guaranteed to be 64 bytes in size, and can be safely copied/moved.
* If you need to convert to a format suitable for storage or transmission,
* use secp256k1_ec_pubkey_serialize and secp256k1_ec_pubkey_parse. To
* compare keys, use secp256k1_ec_pubkey_cmp.
*/
typedef struct secp256k1_pubkey {
unsigned char data[64];
} secp256k1_pubkey;
/** Opaque data structure that holds a parsed ECDSA signature.
*
* The exact representation of data inside is implementation defined and not
* guaranteed to be portable between different platforms or versions. It is
* however guaranteed to be 64 bytes in size, and can be safely copied/moved.
* If you need to convert to a format suitable for storage, transmission, or
* comparison, use the secp256k1_ecdsa_signature_serialize_* and
* secp256k1_ecdsa_signature_parse_* functions.
*/
typedef struct secp256k1_ecdsa_signature {
unsigned char data[64];
} secp256k1_ecdsa_signature;
/** A pointer to a function to deterministically generate a nonce.
*
* Returns: 1 if a nonce was successfully generated. 0 will cause signing to fail.
* Out: nonce32: pointer to a 32-byte array to be filled by the function.
* In: msg32: the 32-byte message hash being verified (will not be NULL)
* key32: pointer to a 32-byte secret key (will not be NULL)
* algo16: pointer to a 16-byte array describing the signature
* algorithm (will be NULL for ECDSA for compatibility).
* data: Arbitrary data pointer that is passed through.
* attempt: how many iterations we have tried to find a nonce.
* This will almost always be 0, but different attempt values
* are required to result in a different nonce.
*
* Except for test cases, this function should compute some cryptographic hash of
* the message, the algorithm, the key and the attempt.
*/
typedef int (*secp256k1_nonce_function)(
unsigned char *nonce32,
const unsigned char *msg32,
const unsigned char *key32,
const unsigned char *algo16,
void *data,
unsigned int attempt
);
# if !defined(SECP256K1_GNUC_PREREQ)
# if defined(__GNUC__)&&defined(__GNUC_MINOR__)
# define SECP256K1_GNUC_PREREQ(_maj,_min) \
((__GNUC__<<16)+__GNUC_MINOR__>=((_maj)<<16)+(_min))
# else
# define SECP256K1_GNUC_PREREQ(_maj,_min) 0
# endif
# endif
/* When this header is used at build-time the SECP256K1_BUILD define needs to be set
* to correctly setup export attributes and nullness checks. This is normally done
* by secp256k1.c but to guard against this header being included before secp256k1.c
* has had a chance to set the define (e.g. via test harnesses that just includes
* secp256k1.c) we set SECP256K1_NO_BUILD when this header is processed without the
* BUILD define so this condition can be caught.
*/
#ifndef SECP256K1_BUILD
# define SECP256K1_NO_BUILD
#endif
/* Symbol visibility. */
#if !defined(SECP256K1_API) && defined(SECP256K1_NO_API_VISIBILITY_ATTRIBUTES)
/* The user has requested that we don't specify visibility attributes in
* the public API.
*
* Since all our non-API declarations use the static qualifier, this means
* that the user can use -fvisibility=<value> to set the visibility of the
* API symbols. For instance, -fvisibility=hidden can be useful *even for
* the API symbols*, e.g., when building a static library which is linked
* into a shared library, and the latter should not re-export the
* libsecp256k1 API.
*
* While visibility is a concept that applies only to shared libraries,
* setting visibility will still make a difference when building a static
* library: the visibility settings will be stored in the static library,
* solely for the potential case that the static library will be linked into
* a shared library. In that case, the stored visibility settings will
* resurface and be honored for the shared library. */
# define SECP256K1_API extern
#endif
#if !defined(SECP256K1_API)
# if defined(SECP256K1_BUILD)
/* On Windows, assume a shared library only if explicitly requested.
* 1. If using Libtool, it defines DLL_EXPORT automatically.
* 2. In other cases, SECP256K1_DLL_EXPORT must be defined. */
# if defined(_WIN32) && (defined(SECP256K1_DLL_EXPORT) || defined(DLL_EXPORT))
/* GCC for Windows (e.g., MinGW) accepts the __declspec syntax for
* MSVC compatibility. A __declspec declaration implies (but is not
* exactly equivalent to) __attribute__ ((visibility("default"))),
* and so we actually want __declspec even on GCC, see "Microsoft
* Windows Function Attributes" in the GCC manual and the
* recommendations in https://gcc.gnu.org/wiki/Visibility . */
# define SECP256K1_API extern __declspec(dllexport)
/* Avoid __attribute__ ((visibility("default"))) on Windows to get rid
* of warnings when compiling with -flto due to a bug in GCC, see
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116478 . */
# elif !defined(_WIN32) && defined (__GNUC__) && (__GNUC__ >= 4)
# define SECP256K1_API extern __attribute__ ((visibility("default")))
# else
# define SECP256K1_API extern
# endif
# else
/* On Windows, SECP256K1_STATIC must be defined when consuming
* libsecp256k1 as a static library. Note that SECP256K1_STATIC is a
* "consumer-only" macro, and it has no meaning when building
* libsecp256k1. */
# if defined(_WIN32) && !defined(SECP256K1_STATIC)
# define SECP256K1_API extern __declspec(dllimport)
# else
# define SECP256K1_API extern
# endif
# endif
#endif
/* Warning attributes
* NONNULL is not used if SECP256K1_BUILD is set to avoid the compiler optimizing out
* some paranoid null checks. */
# if defined(__GNUC__) && SECP256K1_GNUC_PREREQ(3, 4)
# define SECP256K1_WARN_UNUSED_RESULT __attribute__ ((__warn_unused_result__))
# else
# define SECP256K1_WARN_UNUSED_RESULT
# endif
# if !defined(SECP256K1_BUILD) && defined(__GNUC__) && SECP256K1_GNUC_PREREQ(3, 4)
# define SECP256K1_ARG_NONNULL(_x) __attribute__ ((__nonnull__(_x)))
# else
# define SECP256K1_ARG_NONNULL(_x)
# endif
/* Attribute for marking functions, types, and variables as deprecated */
#if !defined(SECP256K1_BUILD) && defined(__has_attribute)
# if __has_attribute(__deprecated__)
# define SECP256K1_DEPRECATED(_msg) __attribute__ ((__deprecated__(_msg)))
# else
# define SECP256K1_DEPRECATED(_msg)
# endif
#else
# define SECP256K1_DEPRECATED(_msg)
#endif
/* All flags' lower 8 bits indicate what they're for. Do not use directly. */
#define SECP256K1_FLAGS_TYPE_MASK ((1 << 8) - 1)
#define SECP256K1_FLAGS_TYPE_CONTEXT (1 << 0)
#define SECP256K1_FLAGS_TYPE_COMPRESSION (1 << 1)
/* The higher bits contain the actual data. Do not use directly. */
#define SECP256K1_FLAGS_BIT_CONTEXT_VERIFY (1 << 8)
#define SECP256K1_FLAGS_BIT_CONTEXT_SIGN (1 << 9)
#define SECP256K1_FLAGS_BIT_CONTEXT_DECLASSIFY (1 << 10)
#define SECP256K1_FLAGS_BIT_COMPRESSION (1 << 8)
/** Context flags to pass to secp256k1_context_create, secp256k1_context_preallocated_size, and
* secp256k1_context_preallocated_create. */
#define SECP256K1_CONTEXT_NONE (SECP256K1_FLAGS_TYPE_CONTEXT)
/** Deprecated context flags. These flags are treated equivalent to SECP256K1_CONTEXT_NONE. */
#define SECP256K1_CONTEXT_VERIFY (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_VERIFY)
#define SECP256K1_CONTEXT_SIGN (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN)
/* Testing flag. Do not use. */
#define SECP256K1_CONTEXT_DECLASSIFY (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_DECLASSIFY)
/** Flag to pass to secp256k1_ec_pubkey_serialize. */
#define SECP256K1_EC_COMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION)
#define SECP256K1_EC_UNCOMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION)
/** Prefix byte used to tag various encoded curvepoints for specific purposes */
#define SECP256K1_TAG_PUBKEY_EVEN 0x02
#define SECP256K1_TAG_PUBKEY_ODD 0x03
#define SECP256K1_TAG_PUBKEY_UNCOMPRESSED 0x04
#define SECP256K1_TAG_PUBKEY_HYBRID_EVEN 0x06
#define SECP256K1_TAG_PUBKEY_HYBRID_ODD 0x07
/** A built-in constant secp256k1 context object with static storage duration, to be
* used in conjunction with secp256k1_selftest.
*
* This context object offers *only limited functionality* , i.e., it cannot be used
* for API functions that perform computations involving secret keys, e.g., signing
* and public key generation. If this restriction applies to a specific API function,
* it is mentioned in its documentation. See secp256k1_context_create if you need a
* full context object that supports all functionality offered by the library.
*
* It is highly recommended to call secp256k1_selftest before using this context.
*/
SECP256K1_API const secp256k1_context * const secp256k1_context_static;
/** Deprecated alias for secp256k1_context_static. */
SECP256K1_API const secp256k1_context * const secp256k1_context_no_precomp
SECP256K1_DEPRECATED("Use secp256k1_context_static instead");
/** Perform basic self tests (to be used in conjunction with secp256k1_context_static)
*
* This function performs self tests that detect some serious usage errors and
* similar conditions, e.g., when the library is compiled for the wrong endianness.
* This is a last resort measure to be used in production. The performed tests are
* very rudimentary and are not intended as a replacement for running the test
* binaries.
*
* It is highly recommended to call this before using secp256k1_context_static.
* It is not necessary to call this function before using a context created with
* secp256k1_context_create (or secp256k1_context_preallocated_create), which will
* take care of performing the self tests.
*
* If the tests fail, this function will call the default error callback to abort the
* program (see secp256k1_context_set_error_callback).
*/
SECP256K1_API void secp256k1_selftest(void);
/** Create a secp256k1 context object (in dynamically allocated memory).
*
* This function uses malloc to allocate memory. It is guaranteed that malloc is
* called at most once for every call of this function. If you need to avoid dynamic
* memory allocation entirely, see secp256k1_context_static and the functions in
* secp256k1_preallocated.h.
*
* Returns: pointer to a newly created context object.
* In: flags: Always set to SECP256K1_CONTEXT_NONE (see below).
*
* The only valid non-deprecated flag in recent library versions is
* SECP256K1_CONTEXT_NONE, which will create a context sufficient for all functionality
* offered by the library. All other (deprecated) flags will be treated as equivalent
* to the SECP256K1_CONTEXT_NONE flag. Though the flags parameter primarily exists for
* historical reasons, future versions of the library may introduce new flags.
*
* If the context is intended to be used for API functions that perform computations
* involving secret keys, e.g., signing and public key generation, then it is highly
* recommended to call secp256k1_context_randomize on the context before calling
* those API functions. This will provide enhanced protection against side-channel
* leakage, see secp256k1_context_randomize for details.
*
* Do not create a new context object for each operation, as construction and
* randomization can take non-negligible time.
*/
SECP256K1_API secp256k1_context *secp256k1_context_create(
unsigned int flags
) SECP256K1_WARN_UNUSED_RESULT;
/** Copy a secp256k1 context object (into dynamically allocated memory).
*
* This function uses malloc to allocate memory. It is guaranteed that malloc is
* called at most once for every call of this function. If you need to avoid dynamic
* memory allocation entirely, see the functions in secp256k1_preallocated.h.
*
* Cloning secp256k1_context_static is not possible, and should not be emulated by
* the caller (e.g., using memcpy). Create a new context instead.
*
* Returns: pointer to a newly created context object.
* Args: ctx: pointer to a context to copy (not secp256k1_context_static).
*/
SECP256K1_API secp256k1_context *secp256k1_context_clone(
const secp256k1_context *ctx
) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT;
/** Destroy a secp256k1 context object (created in dynamically allocated memory).
*
* The context pointer may not be used afterwards.
*
* The context to destroy must have been created using secp256k1_context_create
* or secp256k1_context_clone. If the context has instead been created using
* secp256k1_context_preallocated_create or secp256k1_context_preallocated_clone, the
* behaviour is undefined. In that case, secp256k1_context_preallocated_destroy must
* be used instead.
*
* Args: ctx: pointer to a context to destroy, constructed using
* secp256k1_context_create or secp256k1_context_clone
* (i.e., not secp256k1_context_static).
*/
SECP256K1_API void secp256k1_context_destroy(
secp256k1_context *ctx
) SECP256K1_ARG_NONNULL(1);
/** Set a callback function to be called when an illegal argument is passed to
* an API call. It will only trigger for violations that are mentioned
* explicitly in the header.
*
* The philosophy is that these shouldn't be dealt with through a specific
* return value, as calling code should not have branches to deal with the case
* that this code itself is broken.
*
* On the other hand, during debug stage, one would want to be informed about
* such mistakes, and the default (crashing) may be inadvisable. Should this
* callback return instead of crashing, the return value and output arguments
* of the API function call are undefined. Moreover, the same API call may
* trigger the callback again in this case.
*
* When this function has not been called (or called with fun==NULL), then the
* default callback will be used. The library provides a default callback which
* writes the message to stderr and calls abort. This default callback can be
* replaced at link time if the preprocessor macro
* USE_EXTERNAL_DEFAULT_CALLBACKS is defined, which is the case if the build
* has been configured with --enable-external-default-callbacks (GNU Autotools) or
* -DSECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS=ON (CMake). Then the
* following two symbols must be provided to link against:
* - void secp256k1_default_illegal_callback_fn(const char *message, void *data);
* - void secp256k1_default_error_callback_fn(const char *message, void *data);
* The library may call a default callback even before a proper callback data
* pointer could have been set using secp256k1_context_set_illegal_callback or
* secp256k1_context_set_error_callback, e.g., when the creation of a context
* fails. In this case, the corresponding default callback will be called with
* the data pointer argument set to NULL.
*
* Args: ctx: pointer to a context object.
* In: fun: pointer to a function to call when an illegal argument is
* passed to the API, taking a message and an opaque pointer.
* (NULL restores the default callback.)
* data: the opaque pointer to pass to fun above, must be NULL for the
* default callback.
*
* See also secp256k1_context_set_error_callback.
*/
SECP256K1_API void secp256k1_context_set_illegal_callback(
secp256k1_context *ctx,
void (*fun)(const char *message, void *data),
const void *data
) SECP256K1_ARG_NONNULL(1);
/** Set a callback function to be called when an internal consistency check
* fails.
*
* The default callback writes an error message to stderr and calls abort
* to abort the program.
*
* This can only trigger in case of a hardware failure, miscompilation,
* memory corruption, serious bug in the library, or other error that would
* result in undefined behaviour. It will not trigger due to mere
* incorrect usage of the API (see secp256k1_context_set_illegal_callback
* for that). After this callback returns, anything may happen, including
* crashing.
*
* Args: ctx: pointer to a context object.
* In: fun: pointer to a function to call when an internal error occurs,
* taking a message and an opaque pointer (NULL restores the
* default callback, see secp256k1_context_set_illegal_callback
* for details).
* data: the opaque pointer to pass to fun above, must be NULL for the
* default callback.
*
* See also secp256k1_context_set_illegal_callback.
*/
SECP256K1_API void secp256k1_context_set_error_callback(
secp256k1_context *ctx,
void (*fun)(const char *message, void *data),
const void *data
) SECP256K1_ARG_NONNULL(1);
/** A pointer to a function implementing SHA256's internal compression function.
*
* This function processes one or more contiguous 64-byte message blocks and
* updates the internal SHA256 state accordingly. The function is not responsible
* for counting consumed blocks or bytes, nor for performing padding.
*
* In/Out: state: pointer to eight 32-bit words representing the current internal state;
* the state is updated in place.
* In: blocks64: pointer to concatenation of n_blocks blocks, of 64 bytes each.
* no alignment guarantees are made for this pointer.
* n_blocks: number of contiguous 64-byte blocks to process.
*/
typedef void (*secp256k1_sha256_compression_function)(
uint32_t *state,
const unsigned char *blocks64,
size_t n_blocks
);
/**
* Set a callback function to override the internal SHA256 compression function.
*
* This installs a function to replace the built-in block-compression
* step used by the library's internal SHA256 implementation.
* The provided callback must exactly implement the effect of n_blocks
* repeated applications of the SHA256 compression function.
*
* This API exists to support environments that wish to route the
* SHA256 compression step through a hardware-accelerated or otherwise
* specialized implementation. It is NOT meant for replacing SHA256
* with a different hash function.
*
* Args: ctx: pointer to a context object.
* In: fn_compression: pointer to a function implementing the compression function;
* passing NULL restores the default implementation.
*/
SECP256K1_API void secp256k1_context_set_sha256_compression(
secp256k1_context *ctx,
secp256k1_sha256_compression_function fn_compression
) SECP256K1_ARG_NONNULL(1);
/** Parse a variable-length public key into the pubkey object.
*
* Returns: 1 if the public key was fully valid.
* 0 if the public key could not be parsed or is invalid.
* Args: ctx: pointer to a context object.
* Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to a
* parsed version of input. If not, its value is undefined.
* In: input: pointer to a serialized public key
* inputlen: length of the array pointed to by input
*
* This function supports parsing compressed (33 bytes, header byte 0x02 or
* 0x03), uncompressed (65 bytes, header byte 0x04), or hybrid (65 bytes, header
* byte 0x06 or 0x07) format public keys.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_parse(
const secp256k1_context *ctx,
secp256k1_pubkey *pubkey,
const unsigned char *input,
size_t inputlen
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Serialize a pubkey object into a serialized byte sequence.
*
* Returns: 1 always.
* Args: ctx: pointer to a context object.
* Out: output: pointer to a 65-byte (if compressed==0) or 33-byte (if
* compressed==1) byte array to place the serialized key
* in.
* In/Out: outputlen: pointer to an integer which is initially set to the
* size of output, and is overwritten with the written
* size.
* In: pubkey: pointer to a secp256k1_pubkey containing an
* initialized public key.
* flags: SECP256K1_EC_COMPRESSED if serialization should be in
* compressed format, otherwise SECP256K1_EC_UNCOMPRESSED.
*/
SECP256K1_API int secp256k1_ec_pubkey_serialize(
const secp256k1_context *ctx,
unsigned char *output,
size_t *outputlen,
const secp256k1_pubkey *pubkey,
unsigned int flags
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Compare two public keys using lexicographic (of compressed serialization) order
*
* Returns: <0 if the first public key is less than the second
* >0 if the first public key is greater than the second
* 0 if the two public keys are equal
* Args: ctx: pointer to a context object
* In: pubkey1: first public key to compare
* pubkey2: second public key to compare
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_cmp(
const secp256k1_context *ctx,
const secp256k1_pubkey *pubkey1,
const secp256k1_pubkey *pubkey2
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Sort public keys using lexicographic (of compressed serialization) order
*
* Returns: 0 if the arguments are invalid. 1 otherwise.
*
* Args: ctx: pointer to a context object
* In: pubkeys: array of pointers to pubkeys to sort
* n_pubkeys: number of elements in the pubkeys array
*/
SECP256K1_API int secp256k1_ec_pubkey_sort(
const secp256k1_context *ctx,
const secp256k1_pubkey **pubkeys,
size_t n_pubkeys
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
/** Parse an ECDSA signature in compact (64 bytes) format.
*
* Returns: 1 when the signature could be parsed, 0 otherwise.
* Args: ctx: pointer to a context object
* Out: sig: pointer to a signature object
* In: input64: pointer to the 64-byte array to parse
*
* The signature must consist of a 32-byte big endian R value, followed by a
* 32-byte big endian S value. If R or S fall outside of [0..order-1], the
* encoding is invalid. R and S with value 0 are allowed in the encoding.
*
* After the call, sig will always be initialized. If parsing failed or R or
* S are zero, the resulting sig value is guaranteed to fail verification for
* any message and public key.
*/
SECP256K1_API int secp256k1_ecdsa_signature_parse_compact(
const secp256k1_context *ctx,
secp256k1_ecdsa_signature *sig,
const unsigned char *input64
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Parse a DER ECDSA signature.
*
* Returns: 1 when the signature could be parsed, 0 otherwise.
* Args: ctx: pointer to a context object
* Out: sig: pointer to a signature object
* In: input: pointer to the signature to be parsed
* inputlen: the length of the array pointed to be input
*
* This function will accept any valid DER encoded signature, even if the
* encoded numbers are out of range.
*
* After the call, sig will always be initialized. If parsing failed or the
* encoded numbers are out of range, signature verification with it is
* guaranteed to fail for every message and public key.
*/
SECP256K1_API int secp256k1_ecdsa_signature_parse_der(
const secp256k1_context *ctx,
secp256k1_ecdsa_signature *sig,
const unsigned char *input,
size_t inputlen
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Serialize an ECDSA signature in DER format.
*
* Returns: 1 if enough space was available to serialize, 0 otherwise
* Args: ctx: pointer to a context object
* Out: output: pointer to an array to store the DER serialization
* In/Out: outputlen: pointer to a length integer. Initially, this integer
* should be set to the length of output. After the call
* it will be set to the length of the serialization (even
* if 0 was returned).
* In: sig: pointer to an initialized signature object
*/
SECP256K1_API int secp256k1_ecdsa_signature_serialize_der(
const secp256k1_context *ctx,
unsigned char *output,
size_t *outputlen,
const secp256k1_ecdsa_signature *sig
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Serialize an ECDSA signature in compact (64 byte) format.
*
* Returns: 1
* Args: ctx: pointer to a context object
* Out: output64: pointer to a 64-byte array to store the compact serialization
* In: sig: pointer to an initialized signature object
*
* See secp256k1_ecdsa_signature_parse_compact for details about the encoding.
*/
SECP256K1_API int secp256k1_ecdsa_signature_serialize_compact(
const secp256k1_context *ctx,
unsigned char *output64,
const secp256k1_ecdsa_signature *sig
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Verify an ECDSA signature.
*
* Returns: 1: correct signature
* 0: incorrect or unparseable signature
* Args: ctx: pointer to a context object
* In: sig: the signature being verified.
* msghash32: the 32-byte message hash being verified.
* The verifier must make sure to apply a cryptographic
* hash function to the message by itself and not accept an
* msghash32 value directly. Otherwise, it would be easy to
* create a "valid" signature without knowledge of the
* secret key. See also
* https://bitcoin.stackexchange.com/a/81116/35586 for more
* background on this topic.
* pubkey: pointer to an initialized public key to verify with.
*
* To avoid accepting malleable signatures, only ECDSA signatures in lower-S
* form are accepted.
*
* If you need to accept ECDSA signatures from sources that do not obey this
* rule, apply secp256k1_ecdsa_signature_normalize to the signature prior to
* verification, but be aware that doing so results in malleable signatures.
*
* For details, see the comments for that function.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify(
const secp256k1_context *ctx,
const secp256k1_ecdsa_signature *sig,
const unsigned char *msghash32,
const secp256k1_pubkey *pubkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Convert a signature to a normalized lower-S form.
*
* Returns: 1 if sigin was not normalized, 0 if it already was.
* Args: ctx: pointer to a context object
* Out: sigout: pointer to a signature to fill with the normalized form,
* or copy if the input was already normalized. (can be NULL if
* you're only interested in whether the input was already
* normalized).
* In: sigin: pointer to a signature to check/normalize (can be identical to sigout)
*
* With ECDSA a third-party can forge a second distinct signature of the same
* message, given a single initial signature, but without knowing the key. This
* is done by negating the S value modulo the order of the curve, 'flipping'
* the sign of the random point R which is not included in the signature.
*
* Forgery of the same message isn't universally problematic, but in systems
* where message malleability or uniqueness of signatures is important this can
* cause issues. This forgery can be blocked by all verifiers forcing signers
* to use a normalized form.
*
* The lower-S form reduces the size of signatures slightly on average when
* variable length encodings (such as DER) are used and is cheap to verify,
* making it a good choice. Security of always using lower-S is assured because
* anyone can trivially modify a signature after the fact to enforce this
* property anyway.
*
* The lower S value is always between 0x1 and
* 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
* inclusive.
*
* No other forms of ECDSA malleability are known and none seem likely, but
* there is no formal proof that ECDSA, even with this additional restriction,
* is free of other malleability. Commonly used serialization schemes will also
* accept various non-unique encodings, so care should be taken when this
* property is required for an application.
*
* The secp256k1_ecdsa_sign function will by default create signatures in the
* lower-S form, and secp256k1_ecdsa_verify will not accept others. In case
* signatures come from a system that cannot enforce this property,
* secp256k1_ecdsa_signature_normalize must be called before verification.
*/
SECP256K1_API int secp256k1_ecdsa_signature_normalize(
const secp256k1_context *ctx,
secp256k1_ecdsa_signature *sigout,
const secp256k1_ecdsa_signature *sigin
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3);
/** An implementation of RFC6979 (using HMAC-SHA256) as nonce generation function.
* If a data pointer is passed, it is assumed to be a pointer to 32 bytes of
* extra entropy.
*/
SECP256K1_API const secp256k1_nonce_function secp256k1_nonce_function_rfc6979;
/** A default safe nonce generation function (currently equal to secp256k1_nonce_function_rfc6979). */
SECP256K1_API const secp256k1_nonce_function secp256k1_nonce_function_default;
/** Create an ECDSA signature.
*
* Returns: 1: signature created
* 0: the nonce generation function failed, or the secret key was invalid.
* Args: ctx: pointer to a context object (not secp256k1_context_static).
* Out: sig: pointer to an array where the signature will be placed.
* In: msghash32: the 32-byte message hash being signed.
* seckey: pointer to a 32-byte secret key.
* noncefp: pointer to a nonce generation function. If NULL,
* secp256k1_nonce_function_default is used.
* ndata: pointer to arbitrary data used by the nonce generation function
* (can be NULL). If it is non-NULL and
* secp256k1_nonce_function_default is used, then ndata must be a
* pointer to 32-bytes of additional data.
*
* The created signature is always in lower-S form. See
* secp256k1_ecdsa_signature_normalize for more details.
*/
SECP256K1_API int secp256k1_ecdsa_sign(
const secp256k1_context *ctx,
secp256k1_ecdsa_signature *sig,
const unsigned char *msghash32,
const unsigned char *seckey,
secp256k1_nonce_function noncefp,
const void *ndata
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Verify an elliptic curve secret key.
*
* A secret key is valid if it is not 0 and less than the secp256k1 curve order
* when interpreted as an integer (most significant byte first). The
* probability of choosing a 32-byte string uniformly at random which is an
* invalid secret key is negligible. However, if it does happen it should
* be assumed that the randomness source is severely broken and there should
* be no retry.
*
* Returns: 1: secret key is valid
* 0: secret key is invalid
* Args: ctx: pointer to a context object.
* In: seckey: pointer to a 32-byte secret key.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_verify(
const secp256k1_context *ctx,
const unsigned char *seckey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
/** Compute the public key for a secret key.
*
* Returns: 1: secret was valid, public key stores.
* 0: secret was invalid, try again.
* Args: ctx: pointer to a context object (not secp256k1_context_static).
* Out: pubkey: pointer to the created public key.
* In: seckey: pointer to a 32-byte secret key.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create(
const secp256k1_context *ctx,
secp256k1_pubkey *pubkey,
const unsigned char *seckey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Negates a secret key in place.
*
* Returns: 0 if the given secret key is invalid according to
* secp256k1_ec_seckey_verify. 1 otherwise
* Args: ctx: pointer to a context object
* In/Out: seckey: pointer to the 32-byte secret key to be negated. If the
* secret key is invalid according to
* secp256k1_ec_seckey_verify, this function returns 0 and
* seckey will be set to some unspecified value.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_negate(
const secp256k1_context *ctx,
unsigned char *seckey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
/** Negates a public key in place.
*
* Returns: 1 always
* Args: ctx: pointer to a context object
* In/Out: pubkey: pointer to the public key to be negated.
*/
SECP256K1_API int secp256k1_ec_pubkey_negate(
const secp256k1_context *ctx,
secp256k1_pubkey *pubkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
/** Tweak a secret key by adding tweak to it.
*
* Returns: 0 if the arguments are invalid or the resulting secret key would be
* invalid (only when the tweak is the negation of the secret key). 1
* otherwise.
* Args: ctx: pointer to a context object.
* In/Out: seckey: pointer to a 32-byte secret key. If the secret key is
* invalid according to secp256k1_ec_seckey_verify, this
* function returns 0. seckey will be set to some unspecified
* value if this function returns 0.
* In: tweak32: pointer to a 32-byte tweak, which must be valid according to
* secp256k1_ec_seckey_verify or 32 zero bytes. For uniformly
* random 32-byte tweaks, the chance of being invalid is
* negligible (around 1 in 2^128).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_add(
const secp256k1_context *ctx,
unsigned char *seckey,
const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Tweak a public key by adding tweak times the generator to it.
*
* Returns: 0 if the arguments are invalid or the resulting public key would be
* invalid (only when the tweak is the negation of the corresponding
* secret key). 1 otherwise.
* Args: ctx: pointer to a context object.
* In/Out: pubkey: pointer to a public key object. pubkey will be set to an
* invalid value if this function returns 0.
* In: tweak32: pointer to a 32-byte tweak, which must be valid according to
* secp256k1_ec_seckey_verify or 32 zero bytes. For uniformly
* random 32-byte tweaks, the chance of being invalid is
* negligible (around 1 in 2^128).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add(
const secp256k1_context *ctx,
secp256k1_pubkey *pubkey,
const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Tweak a secret key by multiplying it by a tweak.
*
* Returns: 0 if the arguments are invalid. 1 otherwise.
* Args: ctx: pointer to a context object.
* In/Out: seckey: pointer to a 32-byte secret key. If the secret key is
* invalid according to secp256k1_ec_seckey_verify, this
* function returns 0. seckey will be set to some unspecified
* value if this function returns 0.
* In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid according to
* secp256k1_ec_seckey_verify, this function returns 0. For
* uniformly random 32-byte arrays the chance of being invalid
* is negligible (around 1 in 2^128).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_mul(
const secp256k1_context *ctx,
unsigned char *seckey,
const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Tweak a public key by multiplying it by a tweak value.
*
* Returns: 0 if the arguments are invalid. 1 otherwise.
* Args: ctx: pointer to a context object.
* In/Out: pubkey: pointer to a public key object. pubkey will be set to an
* invalid value if this function returns 0.
* In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid according to
* secp256k1_ec_seckey_verify, this function returns 0. For
* uniformly random 32-byte arrays the chance of being invalid
* is negligible (around 1 in 2^128).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul(
const secp256k1_context *ctx,
secp256k1_pubkey *pubkey,
const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Randomizes the context to provide enhanced protection against side-channel leakage.
*
* Returns: 1: randomization successful
* 0: error
* Args: ctx: pointer to a context object (not secp256k1_context_static).
* In: seed32: pointer to a 32-byte random seed (NULL resets to initial state).
*
* While secp256k1 code is written and tested to be constant-time no matter what
* secret values are, it is possible that a compiler may output code which is not,
* and also that the CPU may not emit the same radio frequencies or draw the same
* amount of power for all values. Randomization of the context shields against
* side-channel observations which aim to exploit secret-dependent behaviour in
* certain computations which involve secret keys.
*
* It is highly recommended to call this function on contexts returned from
* secp256k1_context_create or secp256k1_context_clone (or from the corresponding
* functions in secp256k1_preallocated.h) before using these contexts to call API
* functions that perform computations involving secret keys, e.g., signing and
* public key generation. It is possible to call this function more than once on
* the same context, and doing so before every few computations involving secret
* keys is recommended as a defense-in-depth measure. Randomization of the static
* context secp256k1_context_static is not supported.
*
* Currently, the random seed is mainly used for blinding multiplications of a
* secret scalar with the elliptic curve base point. Multiplications of this
* kind are performed by exactly those API functions which are documented to
* require a context that is not secp256k1_context_static. As a rule of thumb,
* these are all functions which take a secret key (or a keypair) as an input.
* A notable exception to that rule is the ECDH module, which relies on a different
* kind of elliptic curve point multiplication and thus does not benefit from
* enhanced protection against side-channel leakage currently.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize(
secp256k1_context *ctx,
const unsigned char *seed32
) SECP256K1_ARG_NONNULL(1);
/** Add a number of public keys together.
*
* Returns: 1: the sum of the public keys is valid.
* 0: the sum of the public keys is not valid.
* Args: ctx: pointer to a context object.
* Out: out: pointer to a public key object for placing the resulting public key.
* In: ins: pointer to array of pointers to public keys.
* n: the number of public keys to add together (must be at least 1).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine(
const secp256k1_context *ctx,
secp256k1_pubkey *out,
const secp256k1_pubkey * const *ins,
size_t n
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Compute a tagged hash as defined in BIP-340.
*
* This is useful for creating a message hash and achieving domain separation
* through an application-specific tag. This function returns
* SHA256(SHA256(tag)||SHA256(tag)||msg). Therefore, tagged hash
* implementations optimized for a specific tag can precompute the SHA256 state
* after hashing the tag hashes.
*
* Returns: 1 always.
* Args: ctx: pointer to a context object
* Out: hash32: pointer to a 32-byte array to store the resulting hash
* In: tag: pointer to an array containing the tag
* taglen: length of the tag array
* msg: pointer to an array containing the message
* msglen: length of the message array
*/
SECP256K1_API int secp256k1_tagged_sha256(
const secp256k1_context *ctx,
unsigned char *hash32,
const unsigned char *tag,
size_t taglen,
const unsigned char *msg,
size_t msglen
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5);
#ifdef __cplusplus
}
#endif
#endif /* SECP256K1_H */

View File

@@ -0,0 +1,63 @@
#ifndef SECP256K1_ECDH_H
#define SECP256K1_ECDH_H
#include "secp256k1.h"
#ifdef __cplusplus
extern "C" {
#endif
/** A pointer to a function that hashes an EC point to obtain an ECDH secret
*
* Returns: 1 if the point was successfully hashed.
* 0 will cause secp256k1_ecdh to fail and return 0.
* Other return values are not allowed, and the behaviour of
* secp256k1_ecdh is undefined for other return values.
* Out: output: pointer to an array to be filled by the function
* In: x32: pointer to a 32-byte x coordinate
* y32: pointer to a 32-byte y coordinate
* data: arbitrary data pointer that is passed through
*/
typedef int (*secp256k1_ecdh_hash_function)(
unsigned char *output,
const unsigned char *x32,
const unsigned char *y32,
void *data
);
/** An implementation of SHA256 hash function that applies to compressed public key.
* Populates the output parameter with 32 bytes. */
SECP256K1_API const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_sha256;
/** A default ECDH hash function (currently equal to secp256k1_ecdh_hash_function_sha256).
* Populates the output parameter with 32 bytes. */
SECP256K1_API const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_default;
/** Compute an EC Diffie-Hellman secret in constant time
*
* Returns: 1: exponentiation was successful
* 0: scalar was invalid (zero or overflow) or hashfp returned 0
* Args: ctx: pointer to a context object.
* Out: output: pointer to an array to be filled by hashfp.
* In: pubkey: pointer to a secp256k1_pubkey containing an initialized public key.
* seckey: a 32-byte scalar with which to multiply the point.
* hashfp: pointer to a hash function. If NULL,
* secp256k1_ecdh_hash_function_sha256 is used
* (in which case, 32 bytes will be written to output).
* data: arbitrary data pointer that is passed through to hashfp
* (can be NULL for secp256k1_ecdh_hash_function_sha256).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh(
const secp256k1_context *ctx,
unsigned char *output,
const secp256k1_pubkey *pubkey,
const unsigned char *seckey,
secp256k1_ecdh_hash_function hashfp,
void *data
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
#ifdef __cplusplus
}
#endif
#endif /* SECP256K1_ECDH_H */

View File

@@ -0,0 +1,200 @@
#ifndef SECP256K1_ELLSWIFT_H
#define SECP256K1_ELLSWIFT_H
#include "secp256k1.h"
#ifdef __cplusplus
extern "C" {
#endif
/* This module provides an implementation of ElligatorSwift as well as a
* version of x-only ECDH using it (including compatibility with BIP324).
*
* ElligatorSwift is described in https://eprint.iacr.org/2022/759 by
* Chavez-Saab, Rodriguez-Henriquez, and Tibouchi. It permits encoding
* uniformly chosen public keys as 64-byte arrays which are indistinguishable
* from uniformly random arrays.
*
* Let f be the function from pairs of field elements to point X coordinates,
* defined as follows (all operations modulo p = 2^256 - 2^32 - 977)
* f(u,t):
* - Let C = 0xa2d2ba93507f1df233770c2a797962cc61f6d15da14ecd47d8d27ae1cd5f852,
* a square root of -3.
* - If u=0, set u=1 instead.
* - If t=0, set t=1 instead.
* - If u^3 + t^2 + 7 = 0, multiply t by 2.
* - Let X = (u^3 + 7 - t^2) / (2 * t)
* - Let Y = (X + t) / (C * u)
* - Return the first in [u + 4 * Y^2, (-X/Y - u) / 2, (X/Y - u) / 2] that is an
* X coordinate on the curve (at least one of them is, for any u and t).
*
* Then an ElligatorSwift encoding of x consists of the 32-byte big-endian
* encodings of field elements u and t concatenated, where f(u,t) = x.
* The encoding algorithm is described in the paper, and effectively picks a
* uniformly random pair (u,t) among those which encode x.
*
* If the Y coordinate is relevant, it is given the same parity as t.
*
* Changes w.r.t. the paper:
* - The u=0, t=0, and u^3+t^2+7=0 conditions result in decoding to the point
* at infinity in the paper. Here they are remapped to finite points.
* - The paper uses an additional encoding bit for the parity of y. Here the
* parity of t is used (negating t does not affect the decoded x coordinate,
* so this is possible).
*
* For mathematical background about the scheme, see the doc/ellswift.md file.
*/
/** A pointer to a function used by secp256k1_ellswift_xdh to hash the shared X
* coordinate along with the encoded public keys to a uniform shared secret.
*
* Returns: 1 if a shared secret was successfully computed.
* 0 will cause secp256k1_ellswift_xdh to fail and return 0.
* Other return values are not allowed, and the behaviour of
* secp256k1_ellswift_xdh is undefined for other return values.
* Out: output: pointer to an array to be filled by the function
* In: x32: pointer to the 32-byte serialized X coordinate
* of the resulting shared point (will not be NULL)
* ell_a64: pointer to the 64-byte encoded public key of party A
* (will not be NULL)
* ell_b64: pointer to the 64-byte encoded public key of party B
* (will not be NULL)
* data: arbitrary data pointer that is passed through
*/
typedef int (*secp256k1_ellswift_xdh_hash_function)(
unsigned char *output,
const unsigned char *x32,
const unsigned char *ell_a64,
const unsigned char *ell_b64,
void *data
);
/** An implementation of an secp256k1_ellswift_xdh_hash_function which uses
* SHA256(prefix64 || ell_a64 || ell_b64 || x32), where prefix64 is the 64-byte
* array pointed to by data. */
SECP256K1_API const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_prefix;
/** An implementation of an secp256k1_ellswift_xdh_hash_function compatible with
* BIP324. It returns H_tag(ell_a64 || ell_b64 || x32), where H_tag is the
* BIP340 tagged hash function with tag "bip324_ellswift_xonly_ecdh". Equivalent
* to secp256k1_ellswift_xdh_hash_function_prefix with prefix64 set to
* SHA256("bip324_ellswift_xonly_ecdh")||SHA256("bip324_ellswift_xonly_ecdh").
* The data argument is ignored. */
SECP256K1_API const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_bip324;
/** Construct a 64-byte ElligatorSwift encoding of a given pubkey.
*
* Returns: 1 always.
* Args: ctx: pointer to a context object
* Out: ell64: pointer to a 64-byte array to be filled
* In: pubkey: pointer to a secp256k1_pubkey containing an
* initialized public key
* rnd32: pointer to 32 bytes of randomness
*
* It is recommended that rnd32 consists of 32 uniformly random bytes, not
* known to any adversary trying to detect whether public keys are being
* encoded, though 16 bytes of randomness (padded to an array of 32 bytes,
* e.g., with zeros) suffice to make the result indistinguishable from
* uniform. The randomness in rnd32 must not be a deterministic function of
* the pubkey (it can be derived from the private key, though).
*
* It is not guaranteed that the computed encoding is stable across versions
* of the library, even if all arguments to this function (including rnd32)
* are the same.
*
* This function runs in variable time.
*/
SECP256K1_API int secp256k1_ellswift_encode(
const secp256k1_context *ctx,
unsigned char *ell64,
const secp256k1_pubkey *pubkey,
const unsigned char *rnd32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Decode a 64-bytes ElligatorSwift encoded public key.
*
* Returns: always 1
* Args: ctx: pointer to a context object
* Out: pubkey: pointer to a secp256k1_pubkey that will be filled
* In: ell64: pointer to a 64-byte array to decode
*
* This function runs in variable time.
*/
SECP256K1_API int secp256k1_ellswift_decode(
const secp256k1_context *ctx,
secp256k1_pubkey *pubkey,
const unsigned char *ell64
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Compute an ElligatorSwift public key for a secret key.
*
* Returns: 1: secret was valid, public key was stored.
* 0: secret was invalid, try again.
* Args: ctx: pointer to a context object (not secp256k1_context_static)
* Out: ell64: pointer to a 64-byte array to receive the ElligatorSwift
* public key
* In: seckey32: pointer to a 32-byte secret key
* auxrnd32: (optional) pointer to 32 bytes of randomness
*
* Constant time in seckey and auxrnd32, but not in the resulting public key.
*
* It is recommended that auxrnd32 contains 32 uniformly random bytes, though
* it is optional (and does result in encodings that are indistinguishable from
* uniform even without any auxrnd32). It differs from the (mandatory) rnd32
* argument to secp256k1_ellswift_encode in this regard.
*
* This function can be used instead of calling secp256k1_ec_pubkey_create
* followed by secp256k1_ellswift_encode. It is safer, as it uses the secret
* key as entropy for the encoding (supplemented with auxrnd32, if provided).
*
* Like secp256k1_ellswift_encode, this function does not guarantee that the
* computed encoding is stable across versions of the library, even if all
* arguments (including auxrnd32) are the same.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ellswift_create(
const secp256k1_context *ctx,
unsigned char *ell64,
const unsigned char *seckey32,
const unsigned char *auxrnd32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Given a private key, and ElligatorSwift public keys sent in both directions,
* compute a shared secret using x-only Elliptic Curve Diffie-Hellman (ECDH).
*
* Returns: 1: shared secret was successfully computed
* 0: secret was invalid or hashfp returned 0
* Args: ctx: pointer to a context object.
* Out: output: pointer to an array to be filled by hashfp.
* In: ell_a64: pointer to the 64-byte encoded public key of party A
* (will not be NULL)
* ell_b64: pointer to the 64-byte encoded public key of party B
* (will not be NULL)
* seckey32: pointer to our 32-byte secret key
* party: boolean indicating which party we are: zero if we are
* party A, non-zero if we are party B. seckey32 must be
* the private key corresponding to that party's ell_?64.
* This correspondence is not checked.
* hashfp: pointer to a hash function.
* data: arbitrary data pointer passed through to hashfp.
*
* Constant time in seckey32.
*
* This function is more efficient than decoding the public keys, and performing
* ECDH on them.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ellswift_xdh(
const secp256k1_context *ctx,
unsigned char *output,
const unsigned char *ell_a64,
const unsigned char *ell_b64,
const unsigned char *seckey32,
int party,
secp256k1_ellswift_xdh_hash_function hashfp,
void *data
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7);
#ifdef __cplusplus
}
#endif
#endif /* SECP256K1_ELLSWIFT_H */

View File

@@ -0,0 +1,250 @@
#ifndef SECP256K1_EXTRAKEYS_H
#define SECP256K1_EXTRAKEYS_H
#include "secp256k1.h"
#ifdef __cplusplus
extern "C" {
#endif
/** Opaque data structure that holds a parsed and valid "x-only" public key.
* An x-only pubkey encodes a point whose Y coordinate is even. It is
* serialized using only its X coordinate (32 bytes). See BIP-340 for more
* information about x-only pubkeys.
*
* The exact representation of data inside is implementation defined and not
* guaranteed to be portable between different platforms or versions. It is
* however guaranteed to be 64 bytes in size, and can be safely copied/moved.
* If you need to convert to a format suitable for storage, transmission, use
* use secp256k1_xonly_pubkey_serialize and secp256k1_xonly_pubkey_parse. To
* compare keys, use secp256k1_xonly_pubkey_cmp.
*/
typedef struct secp256k1_xonly_pubkey {
unsigned char data[64];
} secp256k1_xonly_pubkey;
/** Opaque data structure that holds a keypair consisting of a secret and a
* public key.
*
* The exact representation of data inside is implementation defined and not
* guaranteed to be portable between different platforms or versions. It is
* however guaranteed to be 96 bytes in size, and can be safely copied/moved.
*/
typedef struct secp256k1_keypair {
unsigned char data[96];
} secp256k1_keypair;
/** Parse a 32-byte sequence into a xonly_pubkey object.
*
* Returns: 1 if the public key was fully valid.
* 0 if the public key could not be parsed or is invalid.
*
* Args: ctx: pointer to a context object.
* Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to a
* parsed version of input. If not, it's set to an invalid value.
* In: input32: pointer to a serialized xonly_pubkey.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_parse(
const secp256k1_context *ctx,
secp256k1_xonly_pubkey *pubkey,
const unsigned char *input32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Serialize an xonly_pubkey object into a 32-byte sequence.
*
* Returns: 1 always.
*
* Args: ctx: pointer to a context object.
* Out: output32: pointer to a 32-byte array to place the serialized key in.
* In: pubkey: pointer to a secp256k1_xonly_pubkey containing an initialized public key.
*/
SECP256K1_API int secp256k1_xonly_pubkey_serialize(
const secp256k1_context *ctx,
unsigned char *output32,
const secp256k1_xonly_pubkey *pubkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Compare two x-only public keys using lexicographic order
*
* Returns: <0 if the first public key is less than the second
* >0 if the first public key is greater than the second
* 0 if the two public keys are equal
* Args: ctx: pointer to a context object.
* In: pubkey1: first public key to compare
* pubkey2: second public key to compare
*/
SECP256K1_API int secp256k1_xonly_pubkey_cmp(
const secp256k1_context *ctx,
const secp256k1_xonly_pubkey *pk1,
const secp256k1_xonly_pubkey *pk2
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Converts a secp256k1_pubkey into a secp256k1_xonly_pubkey.
*
* Returns: 1 always.
*
* Args: ctx: pointer to a context object.
* Out: xonly_pubkey: pointer to an x-only public key object for placing the converted public key.
* pk_parity: Ignored if NULL. Otherwise, pointer to an integer that
* will be set to 1 if the point encoded by xonly_pubkey is
* the negation of the pubkey and set to 0 otherwise.
* In: pubkey: pointer to a public key that is converted.
*/
SECP256K1_API int secp256k1_xonly_pubkey_from_pubkey(
const secp256k1_context *ctx,
secp256k1_xonly_pubkey *xonly_pubkey,
int *pk_parity,
const secp256k1_pubkey *pubkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
/** Tweak an x-only public key by adding the generator multiplied with tweak32
* to it.
*
* Note that the resulting point can not in general be represented by an x-only
* pubkey because it may have an odd Y coordinate. Instead, the output_pubkey
* is a normal secp256k1_pubkey.
*
* Returns: 0 if the arguments are invalid or the resulting public key would be
* invalid (only when the tweak is the negation of the corresponding
* secret key). 1 otherwise.
*
* Args: ctx: pointer to a context object.
* Out: output_pubkey: pointer to a public key to store the result. Will be set
* to an invalid value if this function returns 0.
* In: internal_pubkey: pointer to an x-only pubkey to apply the tweak to.
* tweak32: pointer to a 32-byte tweak, which must be valid
* according to secp256k1_ec_seckey_verify or 32 zero
* bytes. For uniformly random 32-byte tweaks, the chance of
* being invalid is negligible (around 1 in 2^128).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_add(
const secp256k1_context *ctx,
secp256k1_pubkey *output_pubkey,
const secp256k1_xonly_pubkey *internal_pubkey,
const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Checks that a tweaked pubkey is the result of calling
* secp256k1_xonly_pubkey_tweak_add with internal_pubkey and tweak32.
*
* The tweaked pubkey is represented by its 32-byte x-only serialization and
* its pk_parity, which can both be obtained by converting the result of
* tweak_add to a secp256k1_xonly_pubkey.
*
* Note that this alone does _not_ verify that the tweaked pubkey is a
* commitment. If the tweak is not chosen in a specific way, the tweaked pubkey
* can easily be the result of a different internal_pubkey and tweak.
*
* Returns: 0 if the arguments are invalid or the tweaked pubkey is not the
* result of tweaking the internal_pubkey with tweak32. 1 otherwise.
* Args: ctx: pointer to a context object.
* In: tweaked_pubkey32: pointer to a serialized xonly_pubkey.
* tweaked_pk_parity: the parity of the tweaked pubkey (whose serialization
* is passed in as tweaked_pubkey32). This must match the
* pk_parity value that is returned when calling
* secp256k1_xonly_pubkey with the tweaked pubkey, or
* this function will fail.
* internal_pubkey: pointer to an x-only public key object to apply the tweak to.
* tweak32: pointer to a 32-byte tweak.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_add_check(
const secp256k1_context *ctx,
const unsigned char *tweaked_pubkey32,
int tweaked_pk_parity,
const secp256k1_xonly_pubkey *internal_pubkey,
const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
/** Compute the keypair for a valid secret key.
*
* See the documentation of `secp256k1_ec_seckey_verify` for more information
* about the validity of secret keys.
*
* Returns: 1: secret key is valid
* 0: secret key is invalid
* Args: ctx: pointer to a context object (not secp256k1_context_static).
* Out: keypair: pointer to the created keypair.
* In: seckey: pointer to a 32-byte secret key.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_create(
const secp256k1_context *ctx,
secp256k1_keypair *keypair,
const unsigned char *seckey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Get the secret key from a keypair.
*
* Returns: 1 always.
* Args: ctx: pointer to a context object.
* Out: seckey: pointer to a 32-byte buffer for the secret key.
* In: keypair: pointer to a keypair.
*/
SECP256K1_API int secp256k1_keypair_sec(
const secp256k1_context *ctx,
unsigned char *seckey,
const secp256k1_keypair *keypair
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Get the public key from a keypair.
*
* Returns: 1 always.
* Args: ctx: pointer to a context object.
* Out: pubkey: pointer to a pubkey object, set to the keypair public key.
* In: keypair: pointer to a keypair.
*/
SECP256K1_API int secp256k1_keypair_pub(
const secp256k1_context *ctx,
secp256k1_pubkey *pubkey,
const secp256k1_keypair *keypair
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Get the x-only public key from a keypair.
*
* This is the same as calling secp256k1_keypair_pub and then
* secp256k1_xonly_pubkey_from_pubkey.
*
* Returns: 1 always.
* Args: ctx: pointer to a context object.
* Out: pubkey: pointer to an xonly_pubkey object, set to the keypair
* public key after converting it to an xonly_pubkey.
* pk_parity: Ignored if NULL. Otherwise, pointer to an integer that will be set to the
* pk_parity argument of secp256k1_xonly_pubkey_from_pubkey.
* In: keypair: pointer to a keypair.
*/
SECP256K1_API int secp256k1_keypair_xonly_pub(
const secp256k1_context *ctx,
secp256k1_xonly_pubkey *pubkey,
int *pk_parity,
const secp256k1_keypair *keypair
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
/** Tweak a keypair by adding tweak32 to the secret key and updating the public
* key accordingly.
*
* Calling this function and then secp256k1_keypair_pub results in the same
* public key as calling secp256k1_keypair_xonly_pub and then
* secp256k1_xonly_pubkey_tweak_add.
*
* Returns: 0 if the arguments are invalid or the resulting keypair would be
* invalid (only when the tweak is the negation of the keypair's
* secret key). 1 otherwise.
*
* Args: ctx: pointer to a context object.
* In/Out: keypair: pointer to a keypair to apply the tweak to. Will be set to
* an invalid value if this function returns 0.
* In: tweak32: pointer to a 32-byte tweak, which must be valid according to
* secp256k1_ec_seckey_verify or 32 zero bytes. For uniformly
* random 32-byte tweaks, the chance of being invalid is
* negligible (around 1 in 2^128).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_tweak_add(
const secp256k1_context *ctx,
secp256k1_keypair *keypair,
const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
#ifdef __cplusplus
}
#endif
#endif /* SECP256K1_EXTRAKEYS_H */

View File

@@ -0,0 +1,588 @@
#ifndef SECP256K1_MUSIG_H
#define SECP256K1_MUSIG_H
#include "secp256k1_extrakeys.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
/** This module implements BIP 327 "MuSig2 for BIP340-compatible
* Multi-Signatures"
* (https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki)
* v1.0.0. You can find an example demonstrating the musig module in
* examples/musig.c.
*
* The module also supports BIP 341 ("Taproot") public key tweaking.
*
* It is recommended to read the documentation in this include file carefully.
* Further notes on API usage can be found in doc/musig.md
*
* Since the first version of MuSig is essentially replaced by MuSig2, we use
* MuSig, musig and MuSig2 synonymously unless noted otherwise.
*/
/** Opaque data structures
*
* The exact representation of data inside the opaque data structures is
* implementation defined and not guaranteed to be portable between different
* platforms or versions. With the exception of `secp256k1_musig_secnonce`, the
* data structures can be safely copied/moved. If you need to convert to a
* format suitable for storage, transmission, or comparison, use the
* corresponding serialization and parsing functions.
*/
/** Opaque data structure that caches information about public key aggregation.
*
* Guaranteed to be 197 bytes in size. No serialization and parsing functions
* (yet).
*/
typedef struct secp256k1_musig_keyagg_cache {
unsigned char data[197];
} secp256k1_musig_keyagg_cache;
/** Opaque data structure that holds a signer's _secret_ nonce.
*
* Guaranteed to be 132 bytes in size.
*
* WARNING: This structure MUST NOT be copied or read or written to directly. A
* signer who is online throughout the whole process and can keep this
* structure in memory can use the provided API functions for a safe standard
* workflow.
*
* Copying this data structure can result in nonce reuse which will leak the
* secret signing key.
*/
typedef struct secp256k1_musig_secnonce {
unsigned char data[132];
} secp256k1_musig_secnonce;
/** Opaque data structure that holds a signer's public nonce.
*
* Guaranteed to be 132 bytes in size. Serialized and parsed with
* `musig_pubnonce_serialize` and `musig_pubnonce_parse`.
*/
typedef struct secp256k1_musig_pubnonce {
unsigned char data[132];
} secp256k1_musig_pubnonce;
/** Opaque data structure that holds an aggregate public nonce.
*
* Guaranteed to be 132 bytes in size. Serialized and parsed with
* `musig_aggnonce_serialize` and `musig_aggnonce_parse`.
*/
typedef struct secp256k1_musig_aggnonce {
unsigned char data[132];
} secp256k1_musig_aggnonce;
/** Opaque data structure that holds a MuSig session.
*
* This structure is not required to be kept secret for the signing protocol to
* be secure. Guaranteed to be 133 bytes in size. No serialization and parsing
* functions (yet).
*/
typedef struct secp256k1_musig_session {
unsigned char data[133];
} secp256k1_musig_session;
/** Opaque data structure that holds a partial MuSig signature.
*
* Guaranteed to be 36 bytes in size. Serialized and parsed with
* `musig_partial_sig_serialize` and `musig_partial_sig_parse`.
*/
typedef struct secp256k1_musig_partial_sig {
unsigned char data[36];
} secp256k1_musig_partial_sig;
/** Parse a signer's public nonce.
*
* Returns: 1 when the nonce could be parsed, 0 otherwise.
* Args: ctx: pointer to a context object
* Out: nonce: pointer to a nonce object
* In: in66: pointer to the 66-byte nonce to be parsed
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubnonce_parse(
const secp256k1_context *ctx,
secp256k1_musig_pubnonce *nonce,
const unsigned char *in66
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Serialize a signer's public nonce
*
* Returns: 1 always
* Args: ctx: pointer to a context object
* Out: out66: pointer to a 66-byte array to store the serialized nonce
* In: nonce: pointer to the nonce
*/
SECP256K1_API int secp256k1_musig_pubnonce_serialize(
const secp256k1_context *ctx,
unsigned char *out66,
const secp256k1_musig_pubnonce *nonce
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Parse an aggregate public nonce.
*
* Returns: 1 when the nonce could be parsed, 0 otherwise.
* Args: ctx: pointer to a context object
* Out: nonce: pointer to a nonce object
* In: in66: pointer to the 66-byte nonce to be parsed
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_aggnonce_parse(
const secp256k1_context *ctx,
secp256k1_musig_aggnonce *nonce,
const unsigned char *in66
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Serialize an aggregate public nonce
*
* Returns: 1 always
* Args: ctx: pointer to a context object
* Out: out66: pointer to a 66-byte array to store the serialized nonce
* In: nonce: pointer to the nonce
*/
SECP256K1_API int secp256k1_musig_aggnonce_serialize(
const secp256k1_context *ctx,
unsigned char *out66,
const secp256k1_musig_aggnonce *nonce
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Parse a MuSig partial signature.
*
* Returns: 1 when the signature could be parsed, 0 otherwise.
* Args: ctx: pointer to a context object
* Out: sig: pointer to a signature object
* In: in32: pointer to the 32-byte signature to be parsed
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_parse(
const secp256k1_context *ctx,
secp256k1_musig_partial_sig *sig,
const unsigned char *in32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Serialize a MuSig partial signature
*
* Returns: 1 always
* Args: ctx: pointer to a context object
* Out: out32: pointer to a 32-byte array to store the serialized signature
* In: sig: pointer to the signature
*/
SECP256K1_API int secp256k1_musig_partial_sig_serialize(
const secp256k1_context *ctx,
unsigned char *out32,
const secp256k1_musig_partial_sig *sig
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Computes an aggregate public key and uses it to initialize a keyagg_cache
*
* Different orders of `pubkeys` result in different `agg_pk`s.
*
* Before aggregating, the pubkeys can be sorted with `secp256k1_ec_pubkey_sort`
* which ensures the same `agg_pk` result for the same multiset of pubkeys.
* This is useful to do before `pubkey_agg`, such that the order of pubkeys
* does not affect the aggregate public key.
*
* Returns: 0 if the arguments are invalid, 1 otherwise
* Args: ctx: pointer to a context object
* Out: agg_pk: the MuSig-aggregated x-only public key. If you do not need it,
* this arg can be NULL.
* keyagg_cache: if non-NULL, pointer to a musig_keyagg_cache struct that
* is required for signing (or observing the signing session
* and verifying partial signatures).
* In: pubkeys: input array of pointers to public keys to aggregate. The order
* is important; a different order will result in a different
* aggregate public key.
* n_pubkeys: length of pubkeys array. Must be greater than 0.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_agg(
const secp256k1_context *ctx,
secp256k1_xonly_pubkey *agg_pk,
secp256k1_musig_keyagg_cache *keyagg_cache,
const secp256k1_pubkey * const *pubkeys,
size_t n_pubkeys
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(4);
/** Obtain the aggregate public key from a keyagg_cache.
*
* This is only useful if you need the non-xonly public key, in particular for
* plain (non-xonly) tweaking or batch-verifying multiple key aggregations
* (not implemented).
*
* Returns: 0 if the arguments are invalid, 1 otherwise
* Args: ctx: pointer to a context object
* Out: agg_pk: the MuSig-aggregated public key.
* In: keyagg_cache: pointer to a `musig_keyagg_cache` struct initialized by
* `musig_pubkey_agg`
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_get(
const secp256k1_context *ctx,
secp256k1_pubkey *agg_pk,
const secp256k1_musig_keyagg_cache *keyagg_cache
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Apply plain "EC" tweaking to a public key in a given keyagg_cache by adding
* the generator multiplied with `tweak32` to it. This is useful for deriving
* child keys from an aggregate public key via BIP 32 where `tweak32` is set to
* a hash as defined in BIP 32.
*
* Callers are responsible for deriving `tweak32` in a way that does not reduce
* the security of MuSig (for example, by following BIP 32).
*
* The tweaking method is the same as `secp256k1_ec_pubkey_tweak_add`. So after
* the following pseudocode buf and buf2 have identical contents (absent
* earlier failures).
*
* secp256k1_musig_pubkey_agg(..., keyagg_cache, pubkeys, ...)
* secp256k1_musig_pubkey_get(..., agg_pk, keyagg_cache)
* secp256k1_musig_pubkey_ec_tweak_add(..., output_pk, tweak32, keyagg_cache)
* secp256k1_ec_pubkey_serialize(..., buf, ..., output_pk, ...)
* secp256k1_ec_pubkey_tweak_add(..., agg_pk, tweak32)
* secp256k1_ec_pubkey_serialize(..., buf2, ..., agg_pk, ...)
*
* This function is required if you want to _sign_ for a tweaked aggregate key.
* If you are only computing a public key but not intending to create a
* signature for it, use `secp256k1_ec_pubkey_tweak_add` instead.
*
* Returns: 0 if the arguments are invalid, 1 otherwise
* Args: ctx: pointer to a context object
* Out: output_pubkey: pointer to a public key to store the result. Will be set
* to an invalid value if this function returns 0. If you
* do not need it, this arg can be NULL.
* In/Out: keyagg_cache: pointer to a `musig_keyagg_cache` struct initialized by
* `musig_pubkey_agg`
* In: tweak32: pointer to a 32-byte tweak. The tweak is valid if it passes
* `secp256k1_ec_seckey_verify` and is not equal to the
* secret key corresponding to the public key represented
* by keyagg_cache or its negation. For uniformly random
* 32-byte arrays the chance of being invalid is
* negligible (around 1 in 2^128).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_ec_tweak_add(
const secp256k1_context *ctx,
secp256k1_pubkey *output_pubkey,
secp256k1_musig_keyagg_cache *keyagg_cache,
const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Apply x-only tweaking to a public key in a given keyagg_cache by adding the
* generator multiplied with `tweak32` to it. This is useful for creating
* Taproot outputs where `tweak32` is set to a TapTweak hash as defined in BIP
* 341.
*
* Callers are responsible for deriving `tweak32` in a way that does not reduce
* the security of MuSig (for example, by following Taproot BIP 341).
*
* The tweaking method is the same as `secp256k1_xonly_pubkey_tweak_add`. So in
* the following pseudocode xonly_pubkey_tweak_add_check (absent earlier
* failures) returns 1.
*
* secp256k1_musig_pubkey_agg(..., agg_pk, keyagg_cache, pubkeys, ...)
* secp256k1_musig_pubkey_xonly_tweak_add(..., output_pk, keyagg_cache, tweak32)
* secp256k1_xonly_pubkey_serialize(..., buf, output_pk)
* secp256k1_xonly_pubkey_tweak_add_check(..., buf, ..., agg_pk, tweak32)
*
* This function is required if you want to _sign_ for a tweaked aggregate key.
* If you are only computing a public key but not intending to create a
* signature for it, use `secp256k1_xonly_pubkey_tweak_add` instead.
*
* Returns: 0 if the arguments are invalid, 1 otherwise
* Args: ctx: pointer to a context object
* Out: output_pubkey: pointer to a public key to store the result. Will be set
* to an invalid value if this function returns 0. If you
* do not need it, this arg can be NULL.
* In/Out: keyagg_cache: pointer to a `musig_keyagg_cache` struct initialized by
* `musig_pubkey_agg`
* In: tweak32: pointer to a 32-byte tweak. The tweak is valid if it passes
* `secp256k1_ec_seckey_verify` and is not equal to the
* secret key corresponding to the public key represented
* by keyagg_cache or its negation. For uniformly random
* 32-byte arrays the chance of being invalid is
* negligible (around 1 in 2^128).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_xonly_tweak_add(
const secp256k1_context *ctx,
secp256k1_pubkey *output_pubkey,
secp256k1_musig_keyagg_cache *keyagg_cache,
const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Starts a signing session by generating a nonce
*
* This function outputs a secret nonce that will be required for signing and a
* corresponding public nonce that is intended to be sent to other signers.
*
* MuSig differs from regular Schnorr signing in that implementers _must_ take
* special care to not reuse a nonce. This can be ensured by following these rules:
*
* 1. Each call to this function must have a UNIQUE session_secrand32 that must
* NOT BE REUSED in subsequent calls to this function and must be KEPT
* SECRET (even from other signers).
* 2. If you already know the seckey, message or aggregate public key
* cache, they can be optionally provided to derive the nonce and increase
* misuse-resistance. The extra_input32 argument can be used to provide
* additional data that does not repeat in normal scenarios, such as the
* current time.
* 3. Avoid copying (or serializing) the secnonce. This reduces the possibility
* that it is used more than once for signing.
*
* If you don't have access to good randomness for session_secrand32, but you
* have access to a non-repeating counter, then see
* secp256k1_musig_nonce_gen_counter.
*
* Remember that nonce reuse will leak the secret key!
* Note that using the same seckey for multiple MuSig sessions is fine.
*
* Returns: 0 if the arguments are invalid and 1 otherwise
* Args: ctx: pointer to a context object (not secp256k1_context_static)
* Out: secnonce: pointer to a structure to store the secret nonce
* pubnonce: pointer to a structure to store the public nonce
* In/Out:
* session_secrand32: a 32-byte session_secrand32 as explained above. Must be unique to this
* call to secp256k1_musig_nonce_gen and must be uniformly
* random. If the function call is successful, the
* session_secrand32 buffer is invalidated to prevent reuse.
* In:
* seckey: the 32-byte secret key that will later be used for signing, if
* already known (can be NULL)
* pubkey: public key of the signer creating the nonce. The secnonce
* output of this function cannot be used to sign for any
* other public key. While the public key should correspond
* to the provided seckey, a mismatch will not cause the
* function to return 0.
* msg32: the 32-byte message that will later be signed, if already known
* (can be NULL)
* keyagg_cache: pointer to the keyagg_cache that was used to create the aggregate
* (and potentially tweaked) public key if already known
* (can be NULL)
* extra_input32: an optional 32-byte array that is input to the nonce
* derivation function (can be NULL)
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_nonce_gen(
const secp256k1_context *ctx,
secp256k1_musig_secnonce *secnonce,
secp256k1_musig_pubnonce *pubnonce,
unsigned char *session_secrand32,
const unsigned char *seckey,
const secp256k1_pubkey *pubkey,
const unsigned char *msg32,
const secp256k1_musig_keyagg_cache *keyagg_cache,
const unsigned char *extra_input32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(6);
/** Alternative way to generate a nonce and start a signing session
*
* This function outputs a secret nonce that will be required for signing and a
* corresponding public nonce that is intended to be sent to other signers.
*
* This function differs from `secp256k1_musig_nonce_gen` by accepting a
* non-repeating counter value instead of a secret random value. This requires
* that a secret key is provided to `secp256k1_musig_nonce_gen_counter`
* (through the keypair argument), as opposed to `secp256k1_musig_nonce_gen`
* where the seckey argument is optional.
*
* MuSig differs from regular Schnorr signing in that implementers _must_ take
* special care to not reuse a nonce. This can be ensured by following these rules:
*
* 1. The nonrepeating_cnt argument must be a counter value that never repeats,
* i.e., you must never call `secp256k1_musig_nonce_gen_counter` twice with
* the same keypair and nonrepeating_cnt value. For example, this implies
* that if the same keypair is used with `secp256k1_musig_nonce_gen_counter`
* on multiple devices, none of the devices should have the same counter
* value as any other device.
* 2. If the seckey, message or aggregate public key cache is already available
* at this stage, any of these can be optionally provided, in which case
* they will be used in the derivation of the nonce and increase
* misuse-resistance. The extra_input32 argument can be used to provide
* additional data that does not repeat in normal scenarios, such as the
* current time.
* 3. Avoid copying (or serializing) the secnonce. This reduces the possibility
* that it is used more than once for signing.
*
* Remember that nonce reuse will leak the secret key!
* Note that using the same keypair for multiple MuSig sessions is fine.
*
* Returns: 0 if the arguments are invalid and 1 otherwise
* Args: ctx: pointer to a context object (not secp256k1_context_static)
* Out: secnonce: pointer to a structure to store the secret nonce
* pubnonce: pointer to a structure to store the public nonce
* In:
* nonrepeating_cnt: the value of a counter as explained above. Must be
* unique to this call to secp256k1_musig_nonce_gen.
* keypair: keypair of the signer creating the nonce. The secnonce
* output of this function cannot be used to sign for any
* other keypair.
* msg32: the 32-byte message that will later be signed, if already known
* (can be NULL)
* keyagg_cache: pointer to the keyagg_cache that was used to create the aggregate
* (and potentially tweaked) public key if already known
* (can be NULL)
* extra_input32: an optional 32-byte array that is input to the nonce
* derivation function (can be NULL)
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_nonce_gen_counter(
const secp256k1_context *ctx,
secp256k1_musig_secnonce *secnonce,
secp256k1_musig_pubnonce *pubnonce,
uint64_t nonrepeating_cnt,
const secp256k1_keypair *keypair,
const unsigned char *msg32,
const secp256k1_musig_keyagg_cache *keyagg_cache,
const unsigned char *extra_input32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5);
/** Aggregates the nonces of all signers into a single nonce
*
* This can be done by an untrusted party to reduce the communication
* between signers. Instead of everyone sending nonces to everyone else, there
* can be one party receiving all nonces, aggregating the nonces with this
* function and then sending only the aggregate nonce back to the signers.
*
* If the aggregator does not compute the aggregate nonce correctly, the final
* signature will be invalid.
*
* Returns: 0 if the arguments are invalid, 1 otherwise
* Args: ctx: pointer to a context object
* Out: aggnonce: pointer to an aggregate public nonce object for
* musig_nonce_process
* In: pubnonces: array of pointers to public nonces sent by the
* signers
* n_pubnonces: number of elements in the pubnonces array. Must be
* greater than 0.
*/
SECP256K1_API int secp256k1_musig_nonce_agg(
const secp256k1_context *ctx,
secp256k1_musig_aggnonce *aggnonce,
const secp256k1_musig_pubnonce * const *pubnonces,
size_t n_pubnonces
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Takes the aggregate nonce and creates a session that is required for signing
* and verification of partial signatures.
*
* Returns: 0 if the arguments are invalid, 1 otherwise
* Args: ctx: pointer to a context object
* Out: session: pointer to a struct to store the session
* In: aggnonce: pointer to an aggregate public nonce object that is the
* output of musig_nonce_agg
* msg32: the 32-byte message to sign
* keyagg_cache: pointer to the keyagg_cache that was used to create the
* aggregate (and potentially tweaked) pubkey
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_nonce_process(
const secp256k1_context *ctx,
secp256k1_musig_session *session,
const secp256k1_musig_aggnonce *aggnonce,
const unsigned char *msg32,
const secp256k1_musig_keyagg_cache *keyagg_cache
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
/** Produces a partial signature
*
* This function overwrites the given secnonce with zeros and will abort if given a
* secnonce that is all zeros. This is a best effort attempt to protect against nonce
* reuse. However, this is of course easily defeated if the secnonce has been
* copied (or serialized). Remember that nonce reuse will leak the secret key!
*
* For signing to succeed, the secnonce provided to this function must have
* been generated for the provided keypair. This means that when signing for a
* keypair consisting of a seckey and pubkey, the secnonce must have been
* created by calling musig_nonce_gen with that pubkey. Otherwise, the
* illegal_callback is called.
*
* This function does not verify the output partial signature, deviating from
* the BIP 327 specification. It is recommended to verify the output partial
* signature with `secp256k1_musig_partial_sig_verify` to prevent random or
* adversarially provoked computation errors.
*
* Returns: 0 if the arguments are invalid or the provided secnonce has already
* been used for signing, 1 otherwise
* Args: ctx: pointer to a context object
* Out: partial_sig: pointer to struct to store the partial signature
* In/Out: secnonce: pointer to the secnonce struct created in
* musig_nonce_gen that has been never used in a
* partial_sign call before and has been created for the
* keypair
* In: keypair: pointer to keypair to sign the message with
* keyagg_cache: pointer to the keyagg_cache that was output when the
* aggregate public key for this session
* session: pointer to the session that was created with
* musig_nonce_process
*/
SECP256K1_API int secp256k1_musig_partial_sign(
const secp256k1_context *ctx,
secp256k1_musig_partial_sig *partial_sig,
secp256k1_musig_secnonce *secnonce,
const secp256k1_keypair *keypair,
const secp256k1_musig_keyagg_cache *keyagg_cache,
const secp256k1_musig_session *session
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);
/** Verifies an individual signer's partial signature
*
* The signature is verified for a specific signing session. In order to avoid
* accidentally verifying a signature from a different or non-existing signing
* session, you must ensure the following:
* 1. The `keyagg_cache` argument is identical to the one used to create the
* `session` with `musig_nonce_process`.
* 2. The `pubkey` argument must be identical to the one sent by the signer
* before aggregating it with `musig_pubkey_agg` to create the
* `keyagg_cache`.
* 3. The `pubnonce` argument must be identical to the one sent by the signer
* before aggregating it with `musig_nonce_agg` and using the result to
* create the `session` with `musig_nonce_process`.
*
* It is not required to call this function in regular MuSig sessions, because
* if any partial signature does not verify, the final signature will not
* verify either, so the problem will be caught. However, this function
* provides the ability to identify which specific partial signature fails
* verification.
*
* Returns: 0 if the arguments are invalid or the partial signature does not
* verify, 1 otherwise
* Args ctx: pointer to a context object
* In: partial_sig: pointer to partial signature to verify, sent by
* the signer associated with `pubnonce` and `pubkey`
* pubnonce: public nonce of the signer in the signing session
* pubkey: public key of the signer in the signing session
* keyagg_cache: pointer to the keyagg_cache that was output when the
* aggregate public key for this signing session
* session: pointer to the session that was created with
* `musig_nonce_process`
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_verify(
const secp256k1_context *ctx,
const secp256k1_musig_partial_sig *partial_sig,
const secp256k1_musig_pubnonce *pubnonce,
const secp256k1_pubkey *pubkey,
const secp256k1_musig_keyagg_cache *keyagg_cache,
const secp256k1_musig_session *session
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);
/** Aggregates partial signatures
*
* Returns: 0 if the arguments are invalid, 1 otherwise (which does NOT mean
* the resulting signature verifies).
* Args: ctx: pointer to a context object
* Out: sig64: complete (but possibly invalid) Schnorr signature
* In: session: pointer to the session that was created with
* musig_nonce_process
* partial_sigs: array of pointers to partial signatures to aggregate
* n_sigs: number of elements in the partial_sigs array. Must be
* greater than 0.
*/
SECP256K1_API int secp256k1_musig_partial_sig_agg(
const secp256k1_context *ctx,
unsigned char *sig64,
const secp256k1_musig_session *session,
const secp256k1_musig_partial_sig * const *partial_sigs,
size_t n_sigs
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,134 @@
#ifndef SECP256K1_PREALLOCATED_H
#define SECP256K1_PREALLOCATED_H
#include "secp256k1.h"
#ifdef __cplusplus
extern "C" {
#endif
/* The module provided by this header file is intended for settings in which it
* is not possible or desirable to rely on dynamic memory allocation. It provides
* functions for creating, cloning, and destroying secp256k1 context objects in a
* contiguous fixed-size block of memory provided by the caller.
*
* Context objects created by functions in this module can be used like contexts
* objects created by functions in secp256k1.h, i.e., they can be passed to any
* API function that expects a context object (see secp256k1.h for details). The
* only exception is that context objects created by functions in this module
* must be destroyed using secp256k1_context_preallocated_destroy (in this
* module) instead of secp256k1_context_destroy (in secp256k1.h).
*
* It is guaranteed that functions in this module will not call malloc or its
* friends realloc, calloc, and free.
*/
/** Determine the memory size of a secp256k1 context object to be created in
* caller-provided memory.
*
* The purpose of this function is to determine how much memory must be provided
* to secp256k1_context_preallocated_create.
*
* Returns: the required size of the caller-provided memory block
* In: flags: which parts of the context to initialize.
*/
SECP256K1_API size_t secp256k1_context_preallocated_size(
unsigned int flags
) SECP256K1_WARN_UNUSED_RESULT;
/** Create a secp256k1 context object in caller-provided memory.
*
* The caller must provide a pointer to a rewritable contiguous block of memory
* of size at least secp256k1_context_preallocated_size(flags) bytes, suitably
* aligned to hold an object of any type.
*
* The block of memory is exclusively owned by the created context object during
* the lifetime of this context object, which begins with the call to this
* function and ends when a call to secp256k1_context_preallocated_destroy
* (which destroys the context object again) returns. During the lifetime of the
* context object, the caller is obligated not to access this block of memory,
* i.e., the caller may not read or write the memory, e.g., by copying the memory
* contents to a different location or trying to create a second context object
* in the memory. In simpler words, the prealloc pointer (or any pointer derived
* from it) should not be used during the lifetime of the context object.
*
* Returns: pointer to newly created context object.
* In: prealloc: pointer to a rewritable contiguous block of memory of
* size at least secp256k1_context_preallocated_size(flags)
* bytes, as detailed above.
* flags: which parts of the context to initialize.
*
* See secp256k1_context_create (in secp256k1.h) for further details.
*
* See also secp256k1_context_randomize (in secp256k1.h)
* and secp256k1_context_preallocated_destroy.
*/
SECP256K1_API secp256k1_context *secp256k1_context_preallocated_create(
void *prealloc,
unsigned int flags
) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT;
/** Determine the memory size of a secp256k1 context object to be copied into
* caller-provided memory.
*
* Returns: the required size of the caller-provided memory block.
* In: ctx: pointer to a context to copy.
*/
SECP256K1_API size_t secp256k1_context_preallocated_clone_size(
const secp256k1_context *ctx
) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT;
/** Copy a secp256k1 context object into caller-provided memory.
*
* The caller must provide a pointer to a rewritable contiguous block of memory
* of size at least secp256k1_context_preallocated_size(flags) bytes, suitably
* aligned to hold an object of any type.
*
* The block of memory is exclusively owned by the created context object during
* the lifetime of this context object, see the description of
* secp256k1_context_preallocated_create for details.
*
* Cloning secp256k1_context_static is not possible, and should not be emulated by
* the caller (e.g., using memcpy). Create a new context instead.
*
* Returns: pointer to a newly created context object.
* Args: ctx: pointer to a context to copy (not secp256k1_context_static).
* In: prealloc: pointer to a rewritable contiguous block of memory of
* size at least secp256k1_context_preallocated_size(flags)
* bytes, as detailed above.
*/
SECP256K1_API secp256k1_context *secp256k1_context_preallocated_clone(
const secp256k1_context *ctx,
void *prealloc
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_WARN_UNUSED_RESULT;
/** Destroy a secp256k1 context object that has been created in
* caller-provided memory.
*
* The context pointer may not be used afterwards.
*
* The context to destroy must have been created using
* secp256k1_context_preallocated_create or secp256k1_context_preallocated_clone.
* If the context has instead been created using secp256k1_context_create or
* secp256k1_context_clone, the behaviour is undefined. In that case,
* secp256k1_context_destroy must be used instead.
*
* If required, it is the responsibility of the caller to deallocate the block
* of memory properly after this function returns, e.g., by calling free on the
* preallocated pointer given to secp256k1_context_preallocated_create or
* secp256k1_context_preallocated_clone.
*
* Args: ctx: pointer to a context to destroy, constructed using
* secp256k1_context_preallocated_create or
* secp256k1_context_preallocated_clone
* (i.e., not secp256k1_context_static).
*/
SECP256K1_API void secp256k1_context_preallocated_destroy(
secp256k1_context *ctx
) SECP256K1_ARG_NONNULL(1);
#ifdef __cplusplus
}
#endif
#endif /* SECP256K1_PREALLOCATED_H */

View File

@@ -0,0 +1,123 @@
#ifndef SECP256K1_RECOVERY_H
#define SECP256K1_RECOVERY_H
#include "secp256k1.h"
#ifdef __cplusplus
extern "C" {
#endif
/** Opaque data structure that holds a parsed ECDSA signature,
* supporting pubkey recovery.
*
* The exact representation of data inside is implementation defined and not
* guaranteed to be portable between different platforms or versions. It is
* however guaranteed to be 65 bytes in size, and can be safely copied/moved.
* If you need to convert to a format suitable for storage or transmission, use
* the secp256k1_ecdsa_signature_serialize_* and
* secp256k1_ecdsa_signature_parse_* functions.
*
* Furthermore, it is guaranteed that identical signatures (including their
* recoverability) will have identical representation, so they can be
* memcmp'ed.
*/
typedef struct secp256k1_ecdsa_recoverable_signature {
unsigned char data[65];
} secp256k1_ecdsa_recoverable_signature;
/** Parse a compact ECDSA signature (64 bytes + recovery id).
*
* Returns: 1 when the signature could be parsed, 0 otherwise
* Args: ctx: pointer to a context object
* Out: sig: pointer to a signature object
* In: input64: pointer to a 64-byte compact signature
* recid: the recovery id (0, 1, 2 or 3)
*/
SECP256K1_API int secp256k1_ecdsa_recoverable_signature_parse_compact(
const secp256k1_context *ctx,
secp256k1_ecdsa_recoverable_signature *sig,
const unsigned char *input64,
int recid
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Convert a recoverable signature into a normal signature.
*
* Returns: 1
* Args: ctx: pointer to a context object.
* Out: sig: pointer to a normal signature.
* In: sigin: pointer to a recoverable signature.
*/
SECP256K1_API int secp256k1_ecdsa_recoverable_signature_convert(
const secp256k1_context *ctx,
secp256k1_ecdsa_signature *sig,
const secp256k1_ecdsa_recoverable_signature *sigin
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Serialize an ECDSA signature in compact format (64 bytes + recovery id).
*
* Returns: 1
* Args: ctx: pointer to a context object.
* Out: output64: pointer to a 64-byte array of the compact signature.
* recid: pointer to an integer to hold the recovery id.
* In: sig: pointer to an initialized signature object.
*/
SECP256K1_API int secp256k1_ecdsa_recoverable_signature_serialize_compact(
const secp256k1_context *ctx,
unsigned char *output64,
int *recid,
const secp256k1_ecdsa_recoverable_signature *sig
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Create a recoverable ECDSA signature.
*
* Returns: 1: signature created
* 0: the nonce generation function failed, or the secret key was invalid.
* Args: ctx: pointer to a context object (not secp256k1_context_static).
* Out: sig: pointer to an array where the signature will be placed.
* In: msghash32: the 32-byte message hash being signed.
* seckey: pointer to a 32-byte secret key.
* noncefp: pointer to a nonce generation function. If NULL,
* secp256k1_nonce_function_default is used.
* ndata: pointer to arbitrary data used by the nonce generation function
* (can be NULL for secp256k1_nonce_function_default).
*/
SECP256K1_API int secp256k1_ecdsa_sign_recoverable(
const secp256k1_context *ctx,
secp256k1_ecdsa_recoverable_signature *sig,
const unsigned char *msghash32,
const unsigned char *seckey,
secp256k1_nonce_function noncefp,
const void *ndata
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Recover an ECDSA public key from a signature.
*
* Successful public key recovery guarantees that the signature, after normalization,
* passes `secp256k1_ecdsa_verify`. Thus, explicit verification is not necessary.
*
* However, a recoverable signature that successfully passes `secp256k1_ecdsa_recover`,
* when converted to a non-recoverable signature (using
* `secp256k1_ecdsa_recoverable_signature_convert`), is not guaranteed to be
* normalized and thus not guaranteed to pass `secp256k1_ecdsa_verify`. If a
* normalized signature is required, call `secp256k1_ecdsa_signature_normalize`
* after `secp256k1_ecdsa_recoverable_signature_convert`.
*
* Returns: 1: public key successfully recovered
* 0: otherwise.
* Args: ctx: pointer to a context object.
* Out: pubkey: pointer to the recovered public key.
* In: sig: pointer to initialized signature that supports pubkey recovery.
* msghash32: the 32-byte message hash assumed to be signed.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_recover(
const secp256k1_context *ctx,
secp256k1_pubkey *pubkey,
const secp256k1_ecdsa_recoverable_signature *sig,
const unsigned char *msghash32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
#ifdef __cplusplus
}
#endif
#endif /* SECP256K1_RECOVERY_H */

View File

@@ -0,0 +1,190 @@
#ifndef SECP256K1_SCHNORRSIG_H
#define SECP256K1_SCHNORRSIG_H
#include "secp256k1.h"
#include "secp256k1_extrakeys.h"
#ifdef __cplusplus
extern "C" {
#endif
/** This module implements a variant of Schnorr signatures compliant with
* Bitcoin Improvement Proposal 340 "Schnorr Signatures for secp256k1"
* (https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki).
*/
/** A pointer to a function to deterministically generate a nonce.
*
* Same as secp256k1_nonce function with the exception of accepting an
* additional pubkey argument and not requiring an attempt argument. The pubkey
* argument can protect signature schemes with key-prefixed challenge hash
* inputs against reusing the nonce when signing with the wrong precomputed
* pubkey.
*
* Returns: 1 if a nonce was successfully generated. 0 will cause signing to
* return an error.
* Out: nonce32: pointer to a 32-byte array to be filled by the function
* In: msg: the message being verified. Is NULL if and only if msglen
* is 0.
* msglen: the length of the message
* key32: pointer to a 32-byte secret key (will not be NULL)
* xonly_pk32: the 32-byte serialized xonly pubkey corresponding to key32
* (will not be NULL)
* algo: pointer to an array describing the signature
* algorithm (will not be NULL)
* algolen: the length of the algo array
* data: arbitrary data pointer that is passed through
*
* Except for test cases, this function should compute some cryptographic hash of
* the message, the key, the pubkey, the algorithm description, and data.
*/
typedef int (*secp256k1_nonce_function_hardened)(
unsigned char *nonce32,
const unsigned char *msg,
size_t msglen,
const unsigned char *key32,
const unsigned char *xonly_pk32,
const unsigned char *algo,
size_t algolen,
void *data
);
/** An implementation of the nonce generation function as defined in Bitcoin
* Improvement Proposal 340 "Schnorr Signatures for secp256k1"
* (https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki).
*
* If a data pointer is passed, it is assumed to be a pointer to 32 bytes of
* auxiliary random data as defined in BIP-340. If the data pointer is NULL,
* the nonce derivation procedure follows BIP-340 by setting the auxiliary
* random data to zero. The algo argument must be non-NULL, otherwise the
* function will fail and return 0. The hash will be tagged with algo.
* Therefore, to create BIP-340 compliant signatures, algo must be set to
* "BIP0340/nonce" and algolen to 13.
*/
SECP256K1_API const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340;
/** Data structure that contains additional arguments for schnorrsig_sign_custom.
*
* A schnorrsig_extraparams structure object can be initialized correctly by
* setting it to SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT.
*
* Members:
* magic: set to SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC at initialization
* and has no other function than making sure the object is
* initialized.
* noncefp: pointer to a nonce generation function. If NULL,
* secp256k1_nonce_function_bip340 is used
* ndata: pointer to arbitrary data used by the nonce generation function
* (can be NULL). If it is non-NULL and
* secp256k1_nonce_function_bip340 is used, then ndata must be a
* pointer to 32-byte auxiliary randomness as per BIP-340.
*/
typedef struct secp256k1_schnorrsig_extraparams {
unsigned char magic[4];
secp256k1_nonce_function_hardened noncefp;
void *ndata;
} secp256k1_schnorrsig_extraparams;
#define SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC { 0xda, 0x6f, 0xb3, 0x8c }
#define SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT {\
SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC,\
NULL,\
NULL\
}
/** Create a Schnorr signature.
*
* Does _not_ strictly follow BIP-340 because it does not verify the resulting
* signature. Instead, you can manually use secp256k1_schnorrsig_verify and
* abort if it fails.
*
* This function only signs 32-byte messages. If you have messages of a
* different size (or the same size but without a context-specific tag
* prefix), it is recommended to create a 32-byte message hash with
* secp256k1_tagged_sha256 and then sign the hash. Tagged hashing allows
* providing an context-specific tag for domain separation. This prevents
* signatures from being valid in multiple contexts by accident.
*
* Returns 1 on success, 0 on failure.
* Args: ctx: pointer to a context object (not secp256k1_context_static).
* Out: sig64: pointer to a 64-byte array to store the serialized signature.
* In: msg32: the 32-byte message being signed.
* keypair: pointer to an initialized keypair.
* aux_rand32: 32 bytes of fresh randomness. While recommended to provide
* this, it is only supplemental to security and can be NULL. A
* NULL argument is treated the same as an all-zero one. See
* BIP-340 "Default Signing" for a full explanation of this
* argument and for guidance if randomness is expensive.
*/
SECP256K1_API int secp256k1_schnorrsig_sign32(
const secp256k1_context *ctx,
unsigned char *sig64,
const unsigned char *msg32,
const secp256k1_keypair *keypair,
const unsigned char *aux_rand32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Same as secp256k1_schnorrsig_sign32, but DEPRECATED. Will be removed in
* future versions. */
SECP256K1_API int secp256k1_schnorrsig_sign(
const secp256k1_context *ctx,
unsigned char *sig64,
const unsigned char *msg32,
const secp256k1_keypair *keypair,
const unsigned char *aux_rand32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4)
SECP256K1_DEPRECATED("Use secp256k1_schnorrsig_sign32 instead");
/** Create a Schnorr signature with a more flexible API.
*
* Same arguments as secp256k1_schnorrsig_sign except that it allows signing
* variable length messages and accepts a pointer to an extraparams object that
* allows customizing signing by passing additional arguments.
*
* Equivalent to secp256k1_schnorrsig_sign32(..., aux_rand32) if msglen is 32
* and extraparams is initialized as follows:
* ```
* secp256k1_schnorrsig_extraparams extraparams = SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT;
* extraparams.ndata = (unsigned char*)aux_rand32;
* ```
*
* Returns 1 on success, 0 on failure.
* Args: ctx: pointer to a context object (not secp256k1_context_static).
* Out: sig64: pointer to a 64-byte array to store the serialized signature.
* In: msg: the message being signed. Can only be NULL if msglen is 0.
* msglen: length of the message.
* keypair: pointer to an initialized keypair.
* extraparams: pointer to an extraparams object (can be NULL).
*/
SECP256K1_API int secp256k1_schnorrsig_sign_custom(
const secp256k1_context *ctx,
unsigned char *sig64,
const unsigned char *msg,
size_t msglen,
const secp256k1_keypair *keypair,
secp256k1_schnorrsig_extraparams *extraparams
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(5);
/** Verify a Schnorr signature.
*
* Returns: 1: correct signature
* 0: incorrect signature
* Args: ctx: pointer to a context object.
* In: sig64: pointer to the 64-byte signature to verify.
* msg: the message being verified. Can only be NULL if msglen is 0.
* msglen: length of the message
* pubkey: pointer to an x-only public key to verify with
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify(
const secp256k1_context *ctx,
const unsigned char *sig64,
const unsigned char *msg,
size_t msglen,
const secp256k1_xonly_pubkey *pubkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(5);
#ifdef __cplusplus
}
#endif
#endif /* SECP256K1_SCHNORRSIG_H */

View File

@@ -0,0 +1,229 @@
add_library(secp256k1)
set_property(TARGET secp256k1 PROPERTY PUBLIC_HEADER
${PROJECT_SOURCE_DIR}/include/secp256k1.h
${PROJECT_SOURCE_DIR}/include/secp256k1_preallocated.h
)
# Processing must be done in a topological sorting of the dependency graph
# (dependent module first).
if(SECP256K1_ENABLE_MODULE_ELLSWIFT)
add_compile_definitions(ENABLE_MODULE_ELLSWIFT=1)
set_property(TARGET secp256k1 APPEND PROPERTY PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/include/secp256k1_ellswift.h)
endif()
if(SECP256K1_ENABLE_MODULE_MUSIG)
if(DEFINED SECP256K1_ENABLE_MODULE_SCHNORRSIG AND NOT SECP256K1_ENABLE_MODULE_SCHNORRSIG)
message(FATAL_ERROR "Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the musig module.")
endif()
set(SECP256K1_ENABLE_MODULE_SCHNORRSIG ON)
add_compile_definitions(ENABLE_MODULE_MUSIG=1)
set_property(TARGET secp256k1 APPEND PROPERTY PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/include/secp256k1_musig.h)
endif()
if(SECP256K1_ENABLE_MODULE_SCHNORRSIG)
if(DEFINED SECP256K1_ENABLE_MODULE_EXTRAKEYS AND NOT SECP256K1_ENABLE_MODULE_EXTRAKEYS)
message(FATAL_ERROR "Module dependency error: You have disabled the extrakeys module explicitly, but it is required by the schnorrsig module.")
endif()
set(SECP256K1_ENABLE_MODULE_EXTRAKEYS ON)
add_compile_definitions(ENABLE_MODULE_SCHNORRSIG=1)
set_property(TARGET secp256k1 APPEND PROPERTY PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/include/secp256k1_schnorrsig.h)
endif()
if(SECP256K1_ENABLE_MODULE_EXTRAKEYS)
add_compile_definitions(ENABLE_MODULE_EXTRAKEYS=1)
set_property(TARGET secp256k1 APPEND PROPERTY PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/include/secp256k1_extrakeys.h)
endif()
if(SECP256K1_ENABLE_MODULE_RECOVERY)
add_compile_definitions(ENABLE_MODULE_RECOVERY=1)
set_property(TARGET secp256k1 APPEND PROPERTY PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/include/secp256k1_recovery.h)
endif()
if(SECP256K1_ENABLE_MODULE_ECDH)
add_compile_definitions(ENABLE_MODULE_ECDH=1)
set_property(TARGET secp256k1 APPEND PROPERTY PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/include/secp256k1_ecdh.h)
endif()
add_library(secp256k1_precomputed OBJECT EXCLUDE_FROM_ALL
precomputed_ecmult.c
precomputed_ecmult_gen.c
)
# Add objects explicitly rather than linking to the object libs to keep them
# from being exported.
target_sources(secp256k1 PRIVATE secp256k1.c $<TARGET_OBJECTS:secp256k1_precomputed>)
if(NOT SECP256K1_ENABLE_API_VISIBILITY_ATTRIBUTES)
target_compile_definitions(secp256k1 PRIVATE SECP256K1_NO_API_VISIBILITY_ATTRIBUTES)
endif()
# Create a helper lib that parent projects can use to link secp256k1 into a
# static lib.
add_library(secp256k1_objs INTERFACE)
target_sources(secp256k1_objs INTERFACE $<TARGET_OBJECTS:secp256k1> $<TARGET_OBJECTS:secp256k1_precomputed>)
add_library(secp256k1_asm INTERFACE)
if(SECP256K1_ASM STREQUAL "arm32")
add_library(secp256k1_asm_arm OBJECT EXCLUDE_FROM_ALL)
target_sources(secp256k1_asm_arm PUBLIC
asm/field_10x26_arm.s
)
target_sources(secp256k1 PRIVATE $<TARGET_OBJECTS:secp256k1_asm_arm>)
target_sources(secp256k1_objs INTERFACE $<TARGET_OBJECTS:secp256k1_asm_arm>)
target_link_libraries(secp256k1_asm INTERFACE secp256k1_asm_arm)
endif()
if(WIN32)
# Define our export symbol only for shared libs.
set_target_properties(secp256k1 PROPERTIES DEFINE_SYMBOL SECP256K1_DLL_EXPORT)
target_compile_definitions(secp256k1 INTERFACE $<$<NOT:$<BOOL:${BUILD_SHARED_LIBS}>>:SECP256K1_STATIC>)
endif()
# Object libs don't know if they're being built for a shared or static lib.
# Grab the PIC property from secp256k1 which knows.
get_target_property(use_pic secp256k1 POSITION_INDEPENDENT_CODE)
set_target_properties(secp256k1_precomputed PROPERTIES POSITION_INDEPENDENT_CODE ${use_pic})
# Add the include path for parent projects so that they don't have to manually add it.
target_include_directories(secp256k1 INTERFACE
$<BUILD_INTERFACE:$<$<NOT:$<BOOL:${PROJECT_IS_TOP_LEVEL}>>:${PROJECT_SOURCE_DIR}/include>>
)
set_target_properties(secp256k1_objs PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "$<TARGET_PROPERTY:secp256k1,INTERFACE_COMPILE_DEFINITIONS>"
INTERFACE_INCLUDE_DIRECTORIES "$<TARGET_PROPERTY:secp256k1,INTERFACE_INCLUDE_DIRECTORIES>"
)
# This emulates Libtool to make sure Libtool and CMake agree on the ABI version,
# see below "Calculate the version variables" in autotools-aux/ltmain.sh.
math(EXPR ${PROJECT_NAME}_soversion "${${PROJECT_NAME}_LIB_VERSION_CURRENT} - ${${PROJECT_NAME}_LIB_VERSION_AGE}")
set_target_properties(secp256k1 PROPERTIES
SOVERSION ${${PROJECT_NAME}_soversion}
)
if(CMAKE_SYSTEM_NAME MATCHES "^(Linux|FreeBSD)$")
set_target_properties(secp256k1 PROPERTIES
VERSION ${${PROJECT_NAME}_soversion}.${${PROJECT_NAME}_LIB_VERSION_AGE}.${${PROJECT_NAME}_LIB_VERSION_REVISION}
)
elseif(APPLE)
math(EXPR ${PROJECT_NAME}_compatibility_version "${${PROJECT_NAME}_LIB_VERSION_CURRENT} + 1")
set_target_properties(secp256k1 PROPERTIES
MACHO_COMPATIBILITY_VERSION ${${PROJECT_NAME}_compatibility_version}
MACHO_CURRENT_VERSION ${${PROJECT_NAME}_compatibility_version}.${${PROJECT_NAME}_LIB_VERSION_REVISION}
)
unset(${PROJECT_NAME}_compatibility_version)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
set(${PROJECT_NAME}_windows "secp256k1")
if(MSVC)
set(${PROJECT_NAME}_windows "${PROJECT_NAME}")
endif()
set_target_properties(secp256k1 PROPERTIES
ARCHIVE_OUTPUT_NAME "${${PROJECT_NAME}_windows}"
RUNTIME_OUTPUT_NAME "${${PROJECT_NAME}_windows}-${${PROJECT_NAME}_soversion}"
)
unset(${PROJECT_NAME}_windows)
endif()
unset(${PROJECT_NAME}_soversion)
if(SECP256K1_BUILD_BENCHMARK)
add_executable(bench bench.c)
target_link_libraries(bench secp256k1)
add_executable(bench_internal bench_internal.c)
target_link_libraries(bench_internal secp256k1_precomputed secp256k1_asm)
add_executable(bench_ecmult bench_ecmult.c)
target_link_libraries(bench_ecmult secp256k1_precomputed secp256k1_asm)
endif()
if(SECP256K1_BUILD_TESTS)
include(CheckIncludeFile)
check_include_file(sys/types.h HAVE_SYS_TYPES_H)
check_include_file(sys/wait.h HAVE_SYS_WAIT_H)
check_include_file(unistd.h HAVE_UNISTD_H)
set(TEST_DEFINITIONS "")
if(HAVE_SYS_TYPES_H AND HAVE_SYS_WAIT_H AND HAVE_UNISTD_H)
list(APPEND TEST_DEFINITIONS SUPPORTS_CONCURRENCY=1)
endif()
function(add_executable_and_tests exe_name verify_definition)
add_executable(${exe_name} tests.c)
target_link_libraries(${exe_name} secp256k1_precomputed secp256k1_asm)
target_compile_definitions(${exe_name} PRIVATE ${verify_definition} ${TEST_DEFINITIONS})
include(DiscoverTests)
discover_tests(${exe_name}
DISCOVERY_ARGS "--list_tests"
DISCOVERY_MATCH "^\\t\\\\[ *[0-9]+\\\\] ([^ ].*)$"
TEST_NAME_REPLACEMENT "secp256k1.${exe_name}.\\\\1"
TEST_ARGS_REPLACEMENT "--target=\\\\1 --log=1"
PROPERTIES
LABELS "secp256k1_${exe_name}"
)
endfunction()
add_executable_and_tests(noverify_tests "")
if(NOT CMAKE_BUILD_TYPE STREQUAL "Coverage")
add_executable_and_tests(tests VERIFY)
endif()
unset(TEST_DEFINITIONS)
endif()
if(SECP256K1_BUILD_EXHAUSTIVE_TESTS)
# Note: do not include secp256k1_precomputed in exhaustive_tests (it uses runtime-generated tables).
add_executable(exhaustive_tests tests_exhaustive.c)
target_link_libraries(exhaustive_tests secp256k1_asm)
target_compile_definitions(exhaustive_tests PRIVATE $<$<NOT:$<CONFIG:Coverage>>:VERIFY>)
add_test(NAME secp256k1.exhaustive_tests COMMAND exhaustive_tests)
set_tests_properties(secp256k1.exhaustive_tests PROPERTIES
LABELS secp256k1_exhaustive
)
endif()
if(SECP256K1_BUILD_CTIME_TESTS)
add_executable(ctime_tests ctime_tests.c)
target_link_libraries(ctime_tests secp256k1)
endif()
if(SECP256K1_INSTALL)
include(GNUInstallDirs)
target_include_directories(secp256k1 INTERFACE
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
install(TARGETS secp256k1
EXPORT ${PROJECT_NAME}-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install(EXPORT ${PROJECT_NAME}-targets
FILE ${PROJECT_NAME}-targets.cmake
NAMESPACE ${PROJECT_NAME}::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
)
include(CMakePackageConfigHelpers)
configure_package_config_file(
${PROJECT_SOURCE_DIR}/cmake/config.cmake.in
${PROJECT_NAME}-config.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
NO_SET_AND_CHECK_MACRO
)
write_basic_package_version_file(${PROJECT_NAME}-config-version.cmake
COMPATIBILITY SameMinorVersion
)
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
)
include(GeneratePkgConfigFile)
generate_pkg_config_file(${PROJECT_SOURCE_DIR}/libsecp256k1.pc.in)
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
)
endif()

View File

@@ -0,0 +1,916 @@
@ vim: set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab syntax=armasm:
/***********************************************************************
* Copyright (c) 2014 Wladimir J. van der Laan *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
/*
ARM implementation of field_10x26 inner loops.
Note:
- To avoid unnecessary loads and make use of available registers, two
'passes' have every time been interleaved, with the odd passes accumulating c' and d'
which will be added to c and d respectively in the even passes
*/
.syntax unified
@ eabi attributes - see readelf -A
.eabi_attribute 24, 1 @ Tag_ABI_align_needed = 8-byte
.eabi_attribute 25, 1 @ Tag_ABI_align_preserved = 8-byte, except leaf SP
.text
@ Field constants
.set field_R0, 0x3d10
.set field_R1, 0x400
.set field_not_M, 0xfc000000 @ ~M = ~0x3ffffff
.align 2
.global secp256k1_fe_mul_inner
.type secp256k1_fe_mul_inner, %function
.hidden secp256k1_fe_mul_inner
@ Arguments:
@ r0 r Restrict: can overlap with a, not with b
@ r1 a
@ r2 b
@ Stack (total 4+10*4 = 44)
@ sp + #0 saved 'r' pointer
@ sp + #4 + 4*X t0,t1,t2,t3,t4,t5,t6,t7,u8,t9
secp256k1_fe_mul_inner:
stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r14}
sub sp, sp, #48 @ frame=44 + alignment
str r0, [sp, #0] @ save result address, we need it only at the end
/******************************************
* Main computation code.
******************************************
Allocation:
r0,r14,r7,r8 scratch
r1 a (pointer)
r2 b (pointer)
r3:r4 c
r5:r6 d
r11:r12 c'
r9:r10 d'
Note: do not write to r[] here, it may overlap with a[]
*/
/* A - interleaved with B */
ldr r7, [r1, #0*4] @ a[0]
ldr r8, [r2, #9*4] @ b[9]
ldr r0, [r1, #1*4] @ a[1]
umull r5, r6, r7, r8 @ d = a[0] * b[9]
ldr r14, [r2, #8*4] @ b[8]
umull r9, r10, r0, r8 @ d' = a[1] * b[9]
ldr r7, [r1, #2*4] @ a[2]
umlal r5, r6, r0, r14 @ d += a[1] * b[8]
ldr r8, [r2, #7*4] @ b[7]
umlal r9, r10, r7, r14 @ d' += a[2] * b[8]
ldr r0, [r1, #3*4] @ a[3]
umlal r5, r6, r7, r8 @ d += a[2] * b[7]
ldr r14, [r2, #6*4] @ b[6]
umlal r9, r10, r0, r8 @ d' += a[3] * b[7]
ldr r7, [r1, #4*4] @ a[4]
umlal r5, r6, r0, r14 @ d += a[3] * b[6]
ldr r8, [r2, #5*4] @ b[5]
umlal r9, r10, r7, r14 @ d' += a[4] * b[6]
ldr r0, [r1, #5*4] @ a[5]
umlal r5, r6, r7, r8 @ d += a[4] * b[5]
ldr r14, [r2, #4*4] @ b[4]
umlal r9, r10, r0, r8 @ d' += a[5] * b[5]
ldr r7, [r1, #6*4] @ a[6]
umlal r5, r6, r0, r14 @ d += a[5] * b[4]
ldr r8, [r2, #3*4] @ b[3]
umlal r9, r10, r7, r14 @ d' += a[6] * b[4]
ldr r0, [r1, #7*4] @ a[7]
umlal r5, r6, r7, r8 @ d += a[6] * b[3]
ldr r14, [r2, #2*4] @ b[2]
umlal r9, r10, r0, r8 @ d' += a[7] * b[3]
ldr r7, [r1, #8*4] @ a[8]
umlal r5, r6, r0, r14 @ d += a[7] * b[2]
ldr r8, [r2, #1*4] @ b[1]
umlal r9, r10, r7, r14 @ d' += a[8] * b[2]
ldr r0, [r1, #9*4] @ a[9]
umlal r5, r6, r7, r8 @ d += a[8] * b[1]
ldr r14, [r2, #0*4] @ b[0]
umlal r9, r10, r0, r8 @ d' += a[9] * b[1]
ldr r7, [r1, #0*4] @ a[0]
umlal r5, r6, r0, r14 @ d += a[9] * b[0]
@ r7,r14 used in B
bic r0, r5, field_not_M @ t9 = d & M
str r0, [sp, #4 + 4*9]
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
/* B */
umull r3, r4, r7, r14 @ c = a[0] * b[0]
adds r5, r5, r9 @ d += d'
adc r6, r6, r10
bic r0, r5, field_not_M @ u0 = d & M
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u0 * R0
umlal r3, r4, r0, r14
bic r14, r3, field_not_M @ t0 = c & M
str r14, [sp, #4 + 0*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u0 * R1
umlal r3, r4, r0, r14
/* C - interleaved with D */
ldr r7, [r1, #0*4] @ a[0]
ldr r8, [r2, #2*4] @ b[2]
ldr r14, [r2, #1*4] @ b[1]
umull r11, r12, r7, r8 @ c' = a[0] * b[2]
ldr r0, [r1, #1*4] @ a[1]
umlal r3, r4, r7, r14 @ c += a[0] * b[1]
ldr r8, [r2, #0*4] @ b[0]
umlal r11, r12, r0, r14 @ c' += a[1] * b[1]
ldr r7, [r1, #2*4] @ a[2]
umlal r3, r4, r0, r8 @ c += a[1] * b[0]
ldr r14, [r2, #9*4] @ b[9]
umlal r11, r12, r7, r8 @ c' += a[2] * b[0]
ldr r0, [r1, #3*4] @ a[3]
umlal r5, r6, r7, r14 @ d += a[2] * b[9]
ldr r8, [r2, #8*4] @ b[8]
umull r9, r10, r0, r14 @ d' = a[3] * b[9]
ldr r7, [r1, #4*4] @ a[4]
umlal r5, r6, r0, r8 @ d += a[3] * b[8]
ldr r14, [r2, #7*4] @ b[7]
umlal r9, r10, r7, r8 @ d' += a[4] * b[8]
ldr r0, [r1, #5*4] @ a[5]
umlal r5, r6, r7, r14 @ d += a[4] * b[7]
ldr r8, [r2, #6*4] @ b[6]
umlal r9, r10, r0, r14 @ d' += a[5] * b[7]
ldr r7, [r1, #6*4] @ a[6]
umlal r5, r6, r0, r8 @ d += a[5] * b[6]
ldr r14, [r2, #5*4] @ b[5]
umlal r9, r10, r7, r8 @ d' += a[6] * b[6]
ldr r0, [r1, #7*4] @ a[7]
umlal r5, r6, r7, r14 @ d += a[6] * b[5]
ldr r8, [r2, #4*4] @ b[4]
umlal r9, r10, r0, r14 @ d' += a[7] * b[5]
ldr r7, [r1, #8*4] @ a[8]
umlal r5, r6, r0, r8 @ d += a[7] * b[4]
ldr r14, [r2, #3*4] @ b[3]
umlal r9, r10, r7, r8 @ d' += a[8] * b[4]
ldr r0, [r1, #9*4] @ a[9]
umlal r5, r6, r7, r14 @ d += a[8] * b[3]
ldr r8, [r2, #2*4] @ b[2]
umlal r9, r10, r0, r14 @ d' += a[9] * b[3]
umlal r5, r6, r0, r8 @ d += a[9] * b[2]
bic r0, r5, field_not_M @ u1 = d & M
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u1 * R0
umlal r3, r4, r0, r14
bic r14, r3, field_not_M @ t1 = c & M
str r14, [sp, #4 + 1*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u1 * R1
umlal r3, r4, r0, r14
/* D */
adds r3, r3, r11 @ c += c'
adc r4, r4, r12
adds r5, r5, r9 @ d += d'
adc r6, r6, r10
bic r0, r5, field_not_M @ u2 = d & M
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u2 * R0
umlal r3, r4, r0, r14
bic r14, r3, field_not_M @ t2 = c & M
str r14, [sp, #4 + 2*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u2 * R1
umlal r3, r4, r0, r14
/* E - interleaved with F */
ldr r7, [r1, #0*4] @ a[0]
ldr r8, [r2, #4*4] @ b[4]
umull r11, r12, r7, r8 @ c' = a[0] * b[4]
ldr r8, [r2, #3*4] @ b[3]
umlal r3, r4, r7, r8 @ c += a[0] * b[3]
ldr r7, [r1, #1*4] @ a[1]
umlal r11, r12, r7, r8 @ c' += a[1] * b[3]
ldr r8, [r2, #2*4] @ b[2]
umlal r3, r4, r7, r8 @ c += a[1] * b[2]
ldr r7, [r1, #2*4] @ a[2]
umlal r11, r12, r7, r8 @ c' += a[2] * b[2]
ldr r8, [r2, #1*4] @ b[1]
umlal r3, r4, r7, r8 @ c += a[2] * b[1]
ldr r7, [r1, #3*4] @ a[3]
umlal r11, r12, r7, r8 @ c' += a[3] * b[1]
ldr r8, [r2, #0*4] @ b[0]
umlal r3, r4, r7, r8 @ c += a[3] * b[0]
ldr r7, [r1, #4*4] @ a[4]
umlal r11, r12, r7, r8 @ c' += a[4] * b[0]
ldr r8, [r2, #9*4] @ b[9]
umlal r5, r6, r7, r8 @ d += a[4] * b[9]
ldr r7, [r1, #5*4] @ a[5]
umull r9, r10, r7, r8 @ d' = a[5] * b[9]
ldr r8, [r2, #8*4] @ b[8]
umlal r5, r6, r7, r8 @ d += a[5] * b[8]
ldr r7, [r1, #6*4] @ a[6]
umlal r9, r10, r7, r8 @ d' += a[6] * b[8]
ldr r8, [r2, #7*4] @ b[7]
umlal r5, r6, r7, r8 @ d += a[6] * b[7]
ldr r7, [r1, #7*4] @ a[7]
umlal r9, r10, r7, r8 @ d' += a[7] * b[7]
ldr r8, [r2, #6*4] @ b[6]
umlal r5, r6, r7, r8 @ d += a[7] * b[6]
ldr r7, [r1, #8*4] @ a[8]
umlal r9, r10, r7, r8 @ d' += a[8] * b[6]
ldr r8, [r2, #5*4] @ b[5]
umlal r5, r6, r7, r8 @ d += a[8] * b[5]
ldr r7, [r1, #9*4] @ a[9]
umlal r9, r10, r7, r8 @ d' += a[9] * b[5]
ldr r8, [r2, #4*4] @ b[4]
umlal r5, r6, r7, r8 @ d += a[9] * b[4]
bic r0, r5, field_not_M @ u3 = d & M
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u3 * R0
umlal r3, r4, r0, r14
bic r14, r3, field_not_M @ t3 = c & M
str r14, [sp, #4 + 3*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u3 * R1
umlal r3, r4, r0, r14
/* F */
adds r3, r3, r11 @ c += c'
adc r4, r4, r12
adds r5, r5, r9 @ d += d'
adc r6, r6, r10
bic r0, r5, field_not_M @ u4 = d & M
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u4 * R0
umlal r3, r4, r0, r14
bic r14, r3, field_not_M @ t4 = c & M
str r14, [sp, #4 + 4*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u4 * R1
umlal r3, r4, r0, r14
/* G - interleaved with H */
ldr r7, [r1, #0*4] @ a[0]
ldr r8, [r2, #6*4] @ b[6]
ldr r14, [r2, #5*4] @ b[5]
umull r11, r12, r7, r8 @ c' = a[0] * b[6]
ldr r0, [r1, #1*4] @ a[1]
umlal r3, r4, r7, r14 @ c += a[0] * b[5]
ldr r8, [r2, #4*4] @ b[4]
umlal r11, r12, r0, r14 @ c' += a[1] * b[5]
ldr r7, [r1, #2*4] @ a[2]
umlal r3, r4, r0, r8 @ c += a[1] * b[4]
ldr r14, [r2, #3*4] @ b[3]
umlal r11, r12, r7, r8 @ c' += a[2] * b[4]
ldr r0, [r1, #3*4] @ a[3]
umlal r3, r4, r7, r14 @ c += a[2] * b[3]
ldr r8, [r2, #2*4] @ b[2]
umlal r11, r12, r0, r14 @ c' += a[3] * b[3]
ldr r7, [r1, #4*4] @ a[4]
umlal r3, r4, r0, r8 @ c += a[3] * b[2]
ldr r14, [r2, #1*4] @ b[1]
umlal r11, r12, r7, r8 @ c' += a[4] * b[2]
ldr r0, [r1, #5*4] @ a[5]
umlal r3, r4, r7, r14 @ c += a[4] * b[1]
ldr r8, [r2, #0*4] @ b[0]
umlal r11, r12, r0, r14 @ c' += a[5] * b[1]
ldr r7, [r1, #6*4] @ a[6]
umlal r3, r4, r0, r8 @ c += a[5] * b[0]
ldr r14, [r2, #9*4] @ b[9]
umlal r11, r12, r7, r8 @ c' += a[6] * b[0]
ldr r0, [r1, #7*4] @ a[7]
umlal r5, r6, r7, r14 @ d += a[6] * b[9]
ldr r8, [r2, #8*4] @ b[8]
umull r9, r10, r0, r14 @ d' = a[7] * b[9]
ldr r7, [r1, #8*4] @ a[8]
umlal r5, r6, r0, r8 @ d += a[7] * b[8]
ldr r14, [r2, #7*4] @ b[7]
umlal r9, r10, r7, r8 @ d' += a[8] * b[8]
ldr r0, [r1, #9*4] @ a[9]
umlal r5, r6, r7, r14 @ d += a[8] * b[7]
ldr r8, [r2, #6*4] @ b[6]
umlal r9, r10, r0, r14 @ d' += a[9] * b[7]
umlal r5, r6, r0, r8 @ d += a[9] * b[6]
bic r0, r5, field_not_M @ u5 = d & M
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u5 * R0
umlal r3, r4, r0, r14
bic r14, r3, field_not_M @ t5 = c & M
str r14, [sp, #4 + 5*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u5 * R1
umlal r3, r4, r0, r14
/* H */
adds r3, r3, r11 @ c += c'
adc r4, r4, r12
adds r5, r5, r9 @ d += d'
adc r6, r6, r10
bic r0, r5, field_not_M @ u6 = d & M
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u6 * R0
umlal r3, r4, r0, r14
bic r14, r3, field_not_M @ t6 = c & M
str r14, [sp, #4 + 6*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u6 * R1
umlal r3, r4, r0, r14
/* I - interleaved with J */
ldr r8, [r2, #8*4] @ b[8]
ldr r7, [r1, #0*4] @ a[0]
ldr r14, [r2, #7*4] @ b[7]
umull r11, r12, r7, r8 @ c' = a[0] * b[8]
ldr r0, [r1, #1*4] @ a[1]
umlal r3, r4, r7, r14 @ c += a[0] * b[7]
ldr r8, [r2, #6*4] @ b[6]
umlal r11, r12, r0, r14 @ c' += a[1] * b[7]
ldr r7, [r1, #2*4] @ a[2]
umlal r3, r4, r0, r8 @ c += a[1] * b[6]
ldr r14, [r2, #5*4] @ b[5]
umlal r11, r12, r7, r8 @ c' += a[2] * b[6]
ldr r0, [r1, #3*4] @ a[3]
umlal r3, r4, r7, r14 @ c += a[2] * b[5]
ldr r8, [r2, #4*4] @ b[4]
umlal r11, r12, r0, r14 @ c' += a[3] * b[5]
ldr r7, [r1, #4*4] @ a[4]
umlal r3, r4, r0, r8 @ c += a[3] * b[4]
ldr r14, [r2, #3*4] @ b[3]
umlal r11, r12, r7, r8 @ c' += a[4] * b[4]
ldr r0, [r1, #5*4] @ a[5]
umlal r3, r4, r7, r14 @ c += a[4] * b[3]
ldr r8, [r2, #2*4] @ b[2]
umlal r11, r12, r0, r14 @ c' += a[5] * b[3]
ldr r7, [r1, #6*4] @ a[6]
umlal r3, r4, r0, r8 @ c += a[5] * b[2]
ldr r14, [r2, #1*4] @ b[1]
umlal r11, r12, r7, r8 @ c' += a[6] * b[2]
ldr r0, [r1, #7*4] @ a[7]
umlal r3, r4, r7, r14 @ c += a[6] * b[1]
ldr r8, [r2, #0*4] @ b[0]
umlal r11, r12, r0, r14 @ c' += a[7] * b[1]
ldr r7, [r1, #8*4] @ a[8]
umlal r3, r4, r0, r8 @ c += a[7] * b[0]
ldr r14, [r2, #9*4] @ b[9]
umlal r11, r12, r7, r8 @ c' += a[8] * b[0]
ldr r0, [r1, #9*4] @ a[9]
umlal r5, r6, r7, r14 @ d += a[8] * b[9]
ldr r8, [r2, #8*4] @ b[8]
umull r9, r10, r0, r14 @ d' = a[9] * b[9]
umlal r5, r6, r0, r8 @ d += a[9] * b[8]
bic r0, r5, field_not_M @ u7 = d & M
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u7 * R0
umlal r3, r4, r0, r14
bic r14, r3, field_not_M @ t7 = c & M
str r14, [sp, #4 + 7*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u7 * R1
umlal r3, r4, r0, r14
/* J */
adds r3, r3, r11 @ c += c'
adc r4, r4, r12
adds r5, r5, r9 @ d += d'
adc r6, r6, r10
bic r0, r5, field_not_M @ u8 = d & M
str r0, [sp, #4 + 8*4]
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u8 * R0
umlal r3, r4, r0, r14
/******************************************
* compute and write back result
******************************************
Allocation:
r0 r
r3:r4 c
r5:r6 d
r7 t0
r8 t1
r9 t2
r11 u8
r12 t9
r1,r2,r10,r14 scratch
Note: do not read from a[] after here, it may overlap with r[]
*/
ldr r0, [sp, #0]
add r1, sp, #4 + 3*4 @ r[3..7] = t3..7, r11=u8, r12=t9
ldmia r1, {r2,r7,r8,r9,r10,r11,r12}
add r1, r0, #3*4
stmia r1, {r2,r7,r8,r9,r10}
bic r2, r3, field_not_M @ r[8] = c & M
str r2, [r0, #8*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u8 * R1
umlal r3, r4, r11, r14
movw r14, field_R0 @ c += d * R0
umlal r3, r4, r5, r14
adds r3, r3, r12 @ c += t9
adc r4, r4, #0
add r1, sp, #4 + 0*4 @ r7,r8,r9 = t0,t1,t2
ldmia r1, {r7,r8,r9}
ubfx r2, r3, #0, #22 @ r[9] = c & (M >> 4)
str r2, [r0, #9*4]
mov r3, r3, lsr #22 @ c >>= 22
orr r3, r3, r4, asl #10
mov r4, r4, lsr #22
movw r14, field_R1 << 4 @ c += d * (R1 << 4)
umlal r3, r4, r5, r14
movw r14, field_R0 >> 4 @ d = c * (R0 >> 4) + t0 (64x64 multiply+add)
umull r5, r6, r3, r14 @ d = c.lo * (R0 >> 4)
adds r5, r5, r7 @ d.lo += t0
mla r6, r14, r4, r6 @ d.hi += c.hi * (R0 >> 4)
adc r6, r6, 0 @ d.hi += carry
bic r2, r5, field_not_M @ r[0] = d & M
str r2, [r0, #0*4]
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R1 >> 4 @ d += c * (R1 >> 4) + t1 (64x64 multiply+add)
umull r1, r2, r3, r14 @ tmp = c.lo * (R1 >> 4)
adds r5, r5, r8 @ d.lo += t1
adc r6, r6, #0 @ d.hi += carry
adds r5, r5, r1 @ d.lo += tmp.lo
mla r2, r14, r4, r2 @ tmp.hi += c.hi * (R1 >> 4)
adc r6, r6, r2 @ d.hi += carry + tmp.hi
bic r2, r5, field_not_M @ r[1] = d & M
str r2, [r0, #1*4]
mov r5, r5, lsr #26 @ d >>= 26 (ignore hi)
orr r5, r5, r6, asl #6
add r5, r5, r9 @ d += t2
str r5, [r0, #2*4] @ r[2] = d
add sp, sp, #48
ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc}
.size secp256k1_fe_mul_inner, .-secp256k1_fe_mul_inner
.align 2
.global secp256k1_fe_sqr_inner
.type secp256k1_fe_sqr_inner, %function
.hidden secp256k1_fe_sqr_inner
@ Arguments:
@ r0 r Can overlap with a
@ r1 a
@ Stack (total 4+10*4 = 44)
@ sp + #0 saved 'r' pointer
@ sp + #4 + 4*X t0,t1,t2,t3,t4,t5,t6,t7,u8,t9
secp256k1_fe_sqr_inner:
stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r14}
sub sp, sp, #48 @ frame=44 + alignment
str r0, [sp, #0] @ save result address, we need it only at the end
/******************************************
* Main computation code.
******************************************
Allocation:
r0,r14,r2,r7,r8 scratch
r1 a (pointer)
r3:r4 c
r5:r6 d
r11:r12 c'
r9:r10 d'
Note: do not write to r[] here, it may overlap with a[]
*/
/* A interleaved with B */
ldr r0, [r1, #1*4] @ a[1]*2
ldr r7, [r1, #0*4] @ a[0]
mov r0, r0, asl #1
ldr r14, [r1, #9*4] @ a[9]
umull r3, r4, r7, r7 @ c = a[0] * a[0]
ldr r8, [r1, #8*4] @ a[8]
mov r7, r7, asl #1
umull r5, r6, r7, r14 @ d = a[0]*2 * a[9]
ldr r7, [r1, #2*4] @ a[2]*2
umull r9, r10, r0, r14 @ d' = a[1]*2 * a[9]
ldr r14, [r1, #7*4] @ a[7]
umlal r5, r6, r0, r8 @ d += a[1]*2 * a[8]
mov r7, r7, asl #1
ldr r0, [r1, #3*4] @ a[3]*2
umlal r9, r10, r7, r8 @ d' += a[2]*2 * a[8]
ldr r8, [r1, #6*4] @ a[6]
umlal r5, r6, r7, r14 @ d += a[2]*2 * a[7]
mov r0, r0, asl #1
ldr r7, [r1, #4*4] @ a[4]*2
umlal r9, r10, r0, r14 @ d' += a[3]*2 * a[7]
ldr r14, [r1, #5*4] @ a[5]
mov r7, r7, asl #1
umlal r5, r6, r0, r8 @ d += a[3]*2 * a[6]
umlal r9, r10, r7, r8 @ d' += a[4]*2 * a[6]
umlal r5, r6, r7, r14 @ d += a[4]*2 * a[5]
umlal r9, r10, r14, r14 @ d' += a[5] * a[5]
bic r0, r5, field_not_M @ t9 = d & M
str r0, [sp, #4 + 9*4]
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
/* B */
adds r5, r5, r9 @ d += d'
adc r6, r6, r10
bic r0, r5, field_not_M @ u0 = d & M
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u0 * R0
umlal r3, r4, r0, r14
bic r14, r3, field_not_M @ t0 = c & M
str r14, [sp, #4 + 0*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u0 * R1
umlal r3, r4, r0, r14
/* C interleaved with D */
ldr r0, [r1, #0*4] @ a[0]*2
ldr r14, [r1, #1*4] @ a[1]
mov r0, r0, asl #1
ldr r8, [r1, #2*4] @ a[2]
umlal r3, r4, r0, r14 @ c += a[0]*2 * a[1]
mov r7, r8, asl #1 @ a[2]*2
umull r11, r12, r14, r14 @ c' = a[1] * a[1]
ldr r14, [r1, #9*4] @ a[9]
umlal r11, r12, r0, r8 @ c' += a[0]*2 * a[2]
ldr r0, [r1, #3*4] @ a[3]*2
ldr r8, [r1, #8*4] @ a[8]
umlal r5, r6, r7, r14 @ d += a[2]*2 * a[9]
mov r0, r0, asl #1
ldr r7, [r1, #4*4] @ a[4]*2
umull r9, r10, r0, r14 @ d' = a[3]*2 * a[9]
ldr r14, [r1, #7*4] @ a[7]
umlal r5, r6, r0, r8 @ d += a[3]*2 * a[8]
mov r7, r7, asl #1
ldr r0, [r1, #5*4] @ a[5]*2
umlal r9, r10, r7, r8 @ d' += a[4]*2 * a[8]
ldr r8, [r1, #6*4] @ a[6]
mov r0, r0, asl #1
umlal r5, r6, r7, r14 @ d += a[4]*2 * a[7]
umlal r9, r10, r0, r14 @ d' += a[5]*2 * a[7]
umlal r5, r6, r0, r8 @ d += a[5]*2 * a[6]
umlal r9, r10, r8, r8 @ d' += a[6] * a[6]
bic r0, r5, field_not_M @ u1 = d & M
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u1 * R0
umlal r3, r4, r0, r14
bic r14, r3, field_not_M @ t1 = c & M
str r14, [sp, #4 + 1*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u1 * R1
umlal r3, r4, r0, r14
/* D */
adds r3, r3, r11 @ c += c'
adc r4, r4, r12
adds r5, r5, r9 @ d += d'
adc r6, r6, r10
bic r0, r5, field_not_M @ u2 = d & M
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u2 * R0
umlal r3, r4, r0, r14
bic r14, r3, field_not_M @ t2 = c & M
str r14, [sp, #4 + 2*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u2 * R1
umlal r3, r4, r0, r14
/* E interleaved with F */
ldr r7, [r1, #0*4] @ a[0]*2
ldr r0, [r1, #1*4] @ a[1]*2
ldr r14, [r1, #2*4] @ a[2]
mov r7, r7, asl #1
ldr r8, [r1, #3*4] @ a[3]
ldr r2, [r1, #4*4]
umlal r3, r4, r7, r8 @ c += a[0]*2 * a[3]
mov r0, r0, asl #1
umull r11, r12, r7, r2 @ c' = a[0]*2 * a[4]
mov r2, r2, asl #1 @ a[4]*2
umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[3]
ldr r8, [r1, #9*4] @ a[9]
umlal r3, r4, r0, r14 @ c += a[1]*2 * a[2]
ldr r0, [r1, #5*4] @ a[5]*2
umlal r11, r12, r14, r14 @ c' += a[2] * a[2]
ldr r14, [r1, #8*4] @ a[8]
mov r0, r0, asl #1
umlal r5, r6, r2, r8 @ d += a[4]*2 * a[9]
ldr r7, [r1, #6*4] @ a[6]*2
umull r9, r10, r0, r8 @ d' = a[5]*2 * a[9]
mov r7, r7, asl #1
ldr r8, [r1, #7*4] @ a[7]
umlal r5, r6, r0, r14 @ d += a[5]*2 * a[8]
umlal r9, r10, r7, r14 @ d' += a[6]*2 * a[8]
umlal r5, r6, r7, r8 @ d += a[6]*2 * a[7]
umlal r9, r10, r8, r8 @ d' += a[7] * a[7]
bic r0, r5, field_not_M @ u3 = d & M
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u3 * R0
umlal r3, r4, r0, r14
bic r14, r3, field_not_M @ t3 = c & M
str r14, [sp, #4 + 3*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u3 * R1
umlal r3, r4, r0, r14
/* F */
adds r3, r3, r11 @ c += c'
adc r4, r4, r12
adds r5, r5, r9 @ d += d'
adc r6, r6, r10
bic r0, r5, field_not_M @ u4 = d & M
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u4 * R0
umlal r3, r4, r0, r14
bic r14, r3, field_not_M @ t4 = c & M
str r14, [sp, #4 + 4*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u4 * R1
umlal r3, r4, r0, r14
/* G interleaved with H */
ldr r7, [r1, #0*4] @ a[0]*2
ldr r0, [r1, #1*4] @ a[1]*2
mov r7, r7, asl #1
ldr r8, [r1, #5*4] @ a[5]
ldr r2, [r1, #6*4] @ a[6]
umlal r3, r4, r7, r8 @ c += a[0]*2 * a[5]
ldr r14, [r1, #4*4] @ a[4]
mov r0, r0, asl #1
umull r11, r12, r7, r2 @ c' = a[0]*2 * a[6]
ldr r7, [r1, #2*4] @ a[2]*2
umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[5]
mov r7, r7, asl #1
ldr r8, [r1, #3*4] @ a[3]
umlal r3, r4, r0, r14 @ c += a[1]*2 * a[4]
mov r0, r2, asl #1 @ a[6]*2
umlal r11, r12, r7, r14 @ c' += a[2]*2 * a[4]
ldr r14, [r1, #9*4] @ a[9]
umlal r3, r4, r7, r8 @ c += a[2]*2 * a[3]
ldr r7, [r1, #7*4] @ a[7]*2
umlal r11, r12, r8, r8 @ c' += a[3] * a[3]
mov r7, r7, asl #1
ldr r8, [r1, #8*4] @ a[8]
umlal r5, r6, r0, r14 @ d += a[6]*2 * a[9]
umull r9, r10, r7, r14 @ d' = a[7]*2 * a[9]
umlal r5, r6, r7, r8 @ d += a[7]*2 * a[8]
umlal r9, r10, r8, r8 @ d' += a[8] * a[8]
bic r0, r5, field_not_M @ u5 = d & M
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u5 * R0
umlal r3, r4, r0, r14
bic r14, r3, field_not_M @ t5 = c & M
str r14, [sp, #4 + 5*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u5 * R1
umlal r3, r4, r0, r14
/* H */
adds r3, r3, r11 @ c += c'
adc r4, r4, r12
adds r5, r5, r9 @ d += d'
adc r6, r6, r10
bic r0, r5, field_not_M @ u6 = d & M
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u6 * R0
umlal r3, r4, r0, r14
bic r14, r3, field_not_M @ t6 = c & M
str r14, [sp, #4 + 6*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u6 * R1
umlal r3, r4, r0, r14
/* I interleaved with J */
ldr r7, [r1, #0*4] @ a[0]*2
ldr r0, [r1, #1*4] @ a[1]*2
mov r7, r7, asl #1
ldr r8, [r1, #7*4] @ a[7]
ldr r2, [r1, #8*4] @ a[8]
umlal r3, r4, r7, r8 @ c += a[0]*2 * a[7]
ldr r14, [r1, #6*4] @ a[6]
mov r0, r0, asl #1
umull r11, r12, r7, r2 @ c' = a[0]*2 * a[8]
ldr r7, [r1, #2*4] @ a[2]*2
umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[7]
ldr r8, [r1, #5*4] @ a[5]
umlal r3, r4, r0, r14 @ c += a[1]*2 * a[6]
ldr r0, [r1, #3*4] @ a[3]*2
mov r7, r7, asl #1
umlal r11, r12, r7, r14 @ c' += a[2]*2 * a[6]
ldr r14, [r1, #4*4] @ a[4]
mov r0, r0, asl #1
umlal r3, r4, r7, r8 @ c += a[2]*2 * a[5]
mov r2, r2, asl #1 @ a[8]*2
umlal r11, r12, r0, r8 @ c' += a[3]*2 * a[5]
umlal r3, r4, r0, r14 @ c += a[3]*2 * a[4]
umlal r11, r12, r14, r14 @ c' += a[4] * a[4]
ldr r8, [r1, #9*4] @ a[9]
umlal r5, r6, r2, r8 @ d += a[8]*2 * a[9]
@ r8 will be used in J
bic r0, r5, field_not_M @ u7 = d & M
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u7 * R0
umlal r3, r4, r0, r14
bic r14, r3, field_not_M @ t7 = c & M
str r14, [sp, #4 + 7*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u7 * R1
umlal r3, r4, r0, r14
/* J */
adds r3, r3, r11 @ c += c'
adc r4, r4, r12
umlal r5, r6, r8, r8 @ d += a[9] * a[9]
bic r0, r5, field_not_M @ u8 = d & M
str r0, [sp, #4 + 8*4]
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u8 * R0
umlal r3, r4, r0, r14
/******************************************
* compute and write back result
******************************************
Allocation:
r0 r
r3:r4 c
r5:r6 d
r7 t0
r8 t1
r9 t2
r11 u8
r12 t9
r1,r2,r10,r14 scratch
Note: do not read from a[] after here, it may overlap with r[]
*/
ldr r0, [sp, #0]
add r1, sp, #4 + 3*4 @ r[3..7] = t3..7, r11=u8, r12=t9
ldmia r1, {r2,r7,r8,r9,r10,r11,r12}
add r1, r0, #3*4
stmia r1, {r2,r7,r8,r9,r10}
bic r2, r3, field_not_M @ r[8] = c & M
str r2, [r0, #8*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u8 * R1
umlal r3, r4, r11, r14
movw r14, field_R0 @ c += d * R0
umlal r3, r4, r5, r14
adds r3, r3, r12 @ c += t9
adc r4, r4, #0
add r1, sp, #4 + 0*4 @ r7,r8,r9 = t0,t1,t2
ldmia r1, {r7,r8,r9}
ubfx r2, r3, #0, #22 @ r[9] = c & (M >> 4)
str r2, [r0, #9*4]
mov r3, r3, lsr #22 @ c >>= 22
orr r3, r3, r4, asl #10
mov r4, r4, lsr #22
movw r14, field_R1 << 4 @ c += d * (R1 << 4)
umlal r3, r4, r5, r14
movw r14, field_R0 >> 4 @ d = c * (R0 >> 4) + t0 (64x64 multiply+add)
umull r5, r6, r3, r14 @ d = c.lo * (R0 >> 4)
adds r5, r5, r7 @ d.lo += t0
mla r6, r14, r4, r6 @ d.hi += c.hi * (R0 >> 4)
adc r6, r6, 0 @ d.hi += carry
bic r2, r5, field_not_M @ r[0] = d & M
str r2, [r0, #0*4]
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R1 >> 4 @ d += c * (R1 >> 4) + t1 (64x64 multiply+add)
umull r1, r2, r3, r14 @ tmp = c.lo * (R1 >> 4)
adds r5, r5, r8 @ d.lo += t1
adc r6, r6, #0 @ d.hi += carry
adds r5, r5, r1 @ d.lo += tmp.lo
mla r2, r14, r4, r2 @ tmp.hi += c.hi * (R1 >> 4)
adc r6, r6, r2 @ d.hi += carry + tmp.hi
bic r2, r5, field_not_M @ r[1] = d & M
str r2, [r0, #1*4]
mov r5, r5, lsr #26 @ d >>= 26 (ignore hi)
orr r5, r5, r6, asl #6
add r5, r5, r9 @ d += t2
str r5, [r0, #2*4] @ r[2] = d
add sp, sp, #48
ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc}
.size secp256k1_fe_sqr_inner, .-secp256k1_fe_sqr_inner
.section .note.GNU-stack,"",%progbits

View File

@@ -0,0 +1,87 @@
/***********************************************************************
* Copyright (c) 2020 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_ASSUMPTIONS_H
#define SECP256K1_ASSUMPTIONS_H
#include <limits.h>
#include "util.h"
#if defined(SECP256K1_INT128_NATIVE)
#include "int128_native.h"
#endif
/* This library, like most software, relies on a number of compiler implementation defined (but not undefined)
behaviours. Although the behaviours we require are essentially universal we test them specifically here to
reduce the odds of experiencing an unwelcome surprise.
*/
#if defined(__has_attribute)
# if __has_attribute(__unavailable__)
__attribute__((__unavailable__("Don't call this function. It only exists because STATIC_ASSERT cannot be used outside a function.")))
# endif
#endif
static void secp256k1_assumption_checker(void) {
/* Bytes are 8 bits. */
STATIC_ASSERT(CHAR_BIT == 8);
/* No integer promotion for uint32_t. This ensures that we can multiply uintXX_t values where XX >= 32
without signed overflow, which would be undefined behaviour. */
STATIC_ASSERT(UINT_MAX <= UINT32_MAX);
/* Conversions from unsigned to signed outside of the bounds of the signed type are
implementation-defined. Verify that they function as reinterpreting the lower
bits of the input in two's complement notation. Do this for conversions:
- from uint(N)_t to int(N)_t with negative result
- from uint(2N)_t to int(N)_t with negative result
- from int(2N)_t to int(N)_t with negative result
- from int(2N)_t to int(N)_t with positive result */
/* To int8_t. */
STATIC_ASSERT(((int8_t)(uint8_t)0xAB == (int8_t)-(int8_t)0x55));
STATIC_ASSERT((int8_t)(uint16_t)0xABCD == (int8_t)-(int8_t)0x33);
STATIC_ASSERT((int8_t)(int16_t)(uint16_t)0xCDEF == (int8_t)(uint8_t)0xEF);
STATIC_ASSERT((int8_t)(int16_t)(uint16_t)0x9234 == (int8_t)(uint8_t)0x34);
/* To int16_t. */
STATIC_ASSERT((int16_t)(uint16_t)0xBCDE == (int16_t)-(int16_t)0x4322);
STATIC_ASSERT((int16_t)(uint32_t)0xA1B2C3D4 == (int16_t)-(int16_t)0x3C2C);
STATIC_ASSERT((int16_t)(int32_t)(uint32_t)0xC1D2E3F4 == (int16_t)(uint16_t)0xE3F4);
STATIC_ASSERT((int16_t)(int32_t)(uint32_t)0x92345678 == (int16_t)(uint16_t)0x5678);
/* To int32_t. */
STATIC_ASSERT((int32_t)(uint32_t)0xB2C3D4E5 == (int32_t)-(int32_t)0x4D3C2B1B);
STATIC_ASSERT((int32_t)(uint64_t)0xA123B456C789D012ULL == (int32_t)-(int32_t)0x38762FEE);
STATIC_ASSERT((int32_t)(int64_t)(uint64_t)0xC1D2E3F4A5B6C7D8ULL == (int32_t)(uint32_t)0xA5B6C7D8);
STATIC_ASSERT((int32_t)(int64_t)(uint64_t)0xABCDEF0123456789ULL == (int32_t)(uint32_t)0x23456789);
/* To int64_t. */
STATIC_ASSERT((int64_t)(uint64_t)0xB123C456D789E012ULL == (int64_t)-(int64_t)0x4EDC3BA928761FEEULL);
#if defined(SECP256K1_INT128_NATIVE)
STATIC_ASSERT((int64_t)(((uint128_t)0xA1234567B8901234ULL << 64) + 0xC5678901D2345678ULL) == (int64_t)-(int64_t)0x3A9876FE2DCBA988ULL);
STATIC_ASSERT(((int64_t)(int128_t)(((uint128_t)0xB1C2D3E4F5A6B7C8ULL << 64) + 0xD9E0F1A2B3C4D5E6ULL)) == (int64_t)(uint64_t)0xD9E0F1A2B3C4D5E6ULL);
STATIC_ASSERT(((int64_t)(int128_t)(((uint128_t)0xABCDEF0123456789ULL << 64) + 0x0123456789ABCDEFULL)) == (int64_t)(uint64_t)0x0123456789ABCDEFULL);
/* To int128_t. */
STATIC_ASSERT((int128_t)(((uint128_t)0xB1234567C8901234ULL << 64) + 0xD5678901E2345678ULL) == (int128_t)(-(int128_t)0x8E1648B3F50E80DCULL * 0x8E1648B3F50E80DDULL + 0x5EA688D5482F9464ULL));
#endif
/* Right shift on negative signed values is implementation defined. Verify that it
acts as a right shift in two's complement with sign extension (i.e duplicating
the top bit into newly added bits). */
STATIC_ASSERT((((int8_t)0xE8) >> 2) == (int8_t)(uint8_t)0xFA);
STATIC_ASSERT((((int16_t)0xE9AC) >> 4) == (int16_t)(uint16_t)0xFE9A);
STATIC_ASSERT((((int32_t)0x937C918A) >> 9) == (int32_t)(uint32_t)0xFFC9BE48);
STATIC_ASSERT((((int64_t)0xA8B72231DF9CF4B9ULL) >> 19) == (int64_t)(uint64_t)0xFFFFF516E4463BF3ULL);
#if defined(SECP256K1_INT128_NATIVE)
STATIC_ASSERT((((int128_t)(((uint128_t)0xCD833A65684A0DBCULL << 64) + 0xB349312F71EA7637ULL)) >> 39) == (int128_t)(((uint128_t)0xFFFFFFFFFF9B0674ULL << 64) + 0xCAD0941B79669262ULL));
#endif
/* This function is not supposed to be called. */
VERIFY_CHECK(0);
}
#endif /* SECP256K1_ASSUMPTIONS_H */

View File

@@ -0,0 +1,174 @@
/***********************************************************************
* Copyright (c) 2014 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_BENCH_H
#define SECP256K1_BENCH_H
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "tests_common.h"
#define FP_EXP (6)
#define FP_MULT (1000000LL)
/* Format fixed point number. */
static void print_number(const int64_t x) {
int64_t x_abs, y;
int c, i, rounding, g; /* g = integer part size, c = fractional part size */
size_t ptr;
char buffer[30];
if (x == INT64_MIN) {
/* Prevent UB. */
printf("ERR");
return;
}
x_abs = x < 0 ? -x : x;
/* Determine how many decimals we want to show (more than FP_EXP makes no
* sense). */
y = x_abs;
c = 0;
while (y > 0LL && y < 100LL * FP_MULT && c < FP_EXP) {
y *= 10LL;
c++;
}
/* Round to 'c' decimals. */
y = x_abs;
rounding = 0;
for (i = c; i < FP_EXP; ++i) {
rounding = (y % 10) >= 5;
y /= 10;
}
y += rounding;
/* Format and print the number. */
ptr = sizeof(buffer) - 1;
buffer[ptr] = 0;
g = 0;
if (c != 0) { /* non zero fractional part */
for (i = 0; i < c; ++i) {
buffer[--ptr] = '0' + (y % 10);
y /= 10;
}
} else if (c == 0) { /* fractional part is 0 */
buffer[--ptr] = '0';
}
buffer[--ptr] = '.';
do {
buffer[--ptr] = '0' + (y % 10);
y /= 10;
g++;
} while (y != 0);
if (x < 0) {
buffer[--ptr] = '-';
g++;
}
printf("%5.*s", g, &buffer[ptr]); /* Prints integer part */
printf("%-*s", FP_EXP, &buffer[ptr + g]); /* Prints fractional part */
}
static void run_benchmark(char *name, void (*benchmark)(void*, int), void (*setup)(void*), void (*teardown)(void*, int), void* data, int count, int iter) {
int i;
int64_t min = INT64_MAX;
int64_t sum = 0;
int64_t max = 0;
for (i = 0; i < count; i++) {
int64_t begin, total;
if (setup != NULL) {
setup(data);
}
begin = gettime_i64();
benchmark(data, iter);
total = gettime_i64() - begin;
if (teardown != NULL) {
teardown(data, iter);
}
if (total < min) {
min = total;
}
if (total > max) {
max = total;
}
sum += total;
}
/* ',' is used as a column delimiter */
printf("%-30s, ", name);
print_number(min * FP_MULT / iter);
printf(" , ");
print_number(((sum * FP_MULT) / count) / iter);
printf(" , ");
print_number(max * FP_MULT / iter);
printf("\n");
}
static int have_flag(int argc, char** argv, char *flag) {
char** argm = argv + argc;
argv++;
while (argv != argm) {
if (strcmp(*argv, flag) == 0) {
return 1;
}
argv++;
}
return 0;
}
/* takes an array containing the arguments that the user is allowed to enter on the command-line
returns:
- 1 if the user entered an invalid argument
- 0 if all the user entered arguments are valid */
static int have_invalid_args(int argc, char** argv, char** valid_args, size_t n) {
size_t i;
int found_valid;
char** argm = argv + argc;
argv++;
while (argv != argm) {
found_valid = 0;
for (i = 0; i < n; i++) {
if (strcmp(*argv, valid_args[i]) == 0) {
found_valid = 1; /* user entered a valid arg from the list */
break;
}
}
if (found_valid == 0) {
return 1; /* invalid arg found */
}
argv++;
}
return 0;
}
static int get_iters(int default_iters) {
char* env = getenv("SECP256K1_BENCH_ITERS");
if (env) {
char* endptr;
long int iters = strtol(env, &endptr, 0);
if (*endptr != '\0' || iters <= 0) {
printf("Error: Value of SECP256K1_BENCH_ITERS is not a positive integer: %s\n\n", env);
return 0;
}
return iters;
} else {
return default_iters;
}
}
static void print_output_table_header_row(void) {
char* bench_str = "Benchmark"; /* left justified */
char* min_str = " Min(us) "; /* center alignment */
char* avg_str = " Avg(us) ";
char* max_str = " Max(us) ";
printf("%-30s,%-15s,%-15s,%-15s\n", bench_str, min_str, avg_str, max_str);
printf("\n");
}
#endif /* SECP256K1_BENCH_H */

View File

@@ -0,0 +1,117 @@
/***********************************************************************
* Copyright (c) 2022 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
/* The code here is inspired by Kris Kwiatkowski's approach in
* https://github.com/kriskwiatkowski/pqc/blob/main/src/common/ct_check.h
* to provide a general interface for memory-checking mechanisms, primarily
* for constant-time checking.
*/
/* These macros are defined by this header file:
*
* - SECP256K1_CHECKMEM_ENABLED:
* - 1 if memory-checking integration is available, 0 otherwise.
* This is just a compile-time macro. Use the next macro to check it is actually
* available at runtime.
* - SECP256K1_CHECKMEM_RUNNING():
* - Acts like a function call, returning 1 if memory checking is available
* at runtime.
* - SECP256K1_CHECKMEM_CHECK(p, len):
* - Assert or otherwise fail in case the len-byte memory block pointed to by p is
* not considered entirely defined.
* - SECP256K1_CHECKMEM_CHECK_VERIFY(p, len):
* - Like SECP256K1_CHECKMEM_CHECK, but only works in VERIFY mode.
* - SECP256K1_CHECKMEM_UNDEFINE(p, len):
* - marks the len-byte memory block pointed to by p as undefined data (secret data,
* in the context of constant-time checking).
* - SECP256K1_CHECKMEM_DEFINE(p, len):
* - marks the len-byte memory pointed to by p as defined data (public data, in the
* context of constant-time checking).
* - SECP256K1_CHECKMEM_MSAN_DEFINE(p, len):
* - Like SECP256K1_CHECKMEM_DEFINE, but applies only to memory_sanitizer.
*
*/
#ifndef SECP256K1_CHECKMEM_H
#define SECP256K1_CHECKMEM_H
/* Define a statement-like macro that ignores the arguments. */
#define SECP256K1_CHECKMEM_NOOP(p, len) do { (void)(p); (void)(len); } while(0)
/* If compiling under msan, map the SECP256K1_CHECKMEM_* functionality to msan.
* Choose this preferentially, even when VALGRIND is defined, as msan-compiled
* binaries can't be run under valgrind anyway. */
#if defined(__has_feature)
# if __has_feature(memory_sanitizer)
# include <sanitizer/msan_interface.h>
# define SECP256K1_CHECKMEM_ENABLED 1
# if defined(__clang__) && ((__clang_major__ == 21 && __clang_minor__ >= 1) || __clang_major__ >= 22)
# define SECP256K1_CHECKMEM_UNDEFINE(p, len) do { \
/* Work around https://github.com/llvm/llvm-project/issues/160094 */ \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wuninitialized-const-pointer\"") \
__msan_allocated_memory((p), (len)); \
_Pragma("clang diagnostic pop") \
} while(0)
# else
# define SECP256K1_CHECKMEM_UNDEFINE(p, len) __msan_allocated_memory((p), (len))
# endif
# define SECP256K1_CHECKMEM_DEFINE(p, len) __msan_unpoison((p), (len))
# define SECP256K1_CHECKMEM_MSAN_DEFINE(p, len) __msan_unpoison((p), (len))
# define SECP256K1_CHECKMEM_CHECK(p, len) __msan_check_mem_is_initialized((p), (len))
# define SECP256K1_CHECKMEM_RUNNING() (1)
# endif
#endif
#if !defined SECP256K1_CHECKMEM_MSAN_DEFINE
# define SECP256K1_CHECKMEM_MSAN_DEFINE(p, len) SECP256K1_CHECKMEM_NOOP((p), (len))
#endif
/* If valgrind integration is desired (through the VALGRIND define), implement the
* SECP256K1_CHECKMEM_* macros using valgrind. */
#if !defined SECP256K1_CHECKMEM_ENABLED
# if defined VALGRIND
# include <stddef.h>
# if defined(__clang__) && defined(__APPLE__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wreserved-identifier"
# elif defined(__GNUC__) && (__GNUC__ >= 15)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wtrailing-whitespace"
# endif
# include <valgrind/memcheck.h>
# if defined(__clang__) && defined(__APPLE__)
# pragma clang diagnostic pop
# elif defined(__GNUC__) && (__GNUC__ >= 15)
# pragma GCC diagnostic pop
# endif
# define SECP256K1_CHECKMEM_ENABLED 1
# define SECP256K1_CHECKMEM_UNDEFINE(p, len) VALGRIND_MAKE_MEM_UNDEFINED((p), (len))
# define SECP256K1_CHECKMEM_DEFINE(p, len) VALGRIND_MAKE_MEM_DEFINED((p), (len))
# define SECP256K1_CHECKMEM_CHECK(p, len) VALGRIND_CHECK_MEM_IS_DEFINED((p), (len))
/* VALGRIND_MAKE_MEM_DEFINED returns 0 iff not running on memcheck.
* This is more precise than the RUNNING_ON_VALGRIND macro, which
* checks for valgrind in general instead of memcheck specifically. */
# define SECP256K1_CHECKMEM_RUNNING() (VALGRIND_MAKE_MEM_DEFINED(NULL, 0) != 0)
# endif
#endif
/* As a fall-back, map these macros to dummy statements. */
#if !defined SECP256K1_CHECKMEM_ENABLED
# define SECP256K1_CHECKMEM_ENABLED 0
# define SECP256K1_CHECKMEM_UNDEFINE(p, len) SECP256K1_CHECKMEM_NOOP((p), (len))
# define SECP256K1_CHECKMEM_DEFINE(p, len) SECP256K1_CHECKMEM_NOOP((p), (len))
# define SECP256K1_CHECKMEM_CHECK(p, len) SECP256K1_CHECKMEM_NOOP((p), (len))
# define SECP256K1_CHECKMEM_RUNNING() (0)
#endif
#if defined VERIFY
#define SECP256K1_CHECKMEM_CHECK_VERIFY(p, len) SECP256K1_CHECKMEM_CHECK((p), (len))
#else
#define SECP256K1_CHECKMEM_CHECK_VERIFY(p, len) SECP256K1_CHECKMEM_NOOP((p), (len))
#endif
#endif /* SECP256K1_CHECKMEM_H */

View File

@@ -0,0 +1,21 @@
/***********************************************************************
* Copyright (c) 2013, 2014 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_ECDSA_H
#define SECP256K1_ECDSA_H
#include <stddef.h>
#include "scalar.h"
#include "group.h"
#include "ecmult.h"
static int secp256k1_ecdsa_sig_parse(secp256k1_scalar *r, secp256k1_scalar *s, const unsigned char *sig, size_t size);
static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, size_t *size, const secp256k1_scalar *r, const secp256k1_scalar *s);
static int secp256k1_ecdsa_sig_verify(const secp256k1_scalar* r, const secp256k1_scalar* s, const secp256k1_ge *pubkey, const secp256k1_scalar *message);
static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar* r, secp256k1_scalar* s, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid);
#endif /* SECP256K1_ECDSA_H */

View File

@@ -0,0 +1,312 @@
/***********************************************************************
* Copyright (c) 2013-2015 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_ECDSA_IMPL_H
#define SECP256K1_ECDSA_IMPL_H
#include "scalar.h"
#include "field.h"
#include "group.h"
#include "ecmult.h"
#include "ecmult_gen.h"
#include "ecdsa.h"
/** Group order for secp256k1 defined as 'n' in "Standards for Efficient Cryptography" (SEC2) 2.7.1
* $ sage -c 'load("secp256k1_params.sage"); print(hex(N))'
* 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
*/
static const secp256k1_fe secp256k1_ecdsa_const_order_as_fe = SECP256K1_FE_CONST(
0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL,
0xBAAEDCE6UL, 0xAF48A03BUL, 0xBFD25E8CUL, 0xD0364141UL
);
/** Difference between field and order, values 'p' and 'n' values defined in
* "Standards for Efficient Cryptography" (SEC2) 2.7.1.
* $ sage -c 'load("secp256k1_params.sage"); print(hex(P-N))'
* 0x14551231950b75fc4402da1722fc9baee
*/
static const secp256k1_fe secp256k1_ecdsa_const_p_minus_order = SECP256K1_FE_CONST(
0, 0, 0, 1, 0x45512319UL, 0x50B75FC4UL, 0x402DA172UL, 0x2FC9BAEEUL
);
static int secp256k1_der_read_len(size_t *len, const unsigned char **sigp, const unsigned char *sigend) {
size_t lenleft;
unsigned char b1;
VERIFY_CHECK(len != NULL);
*len = 0;
if (*sigp >= sigend) {
return 0;
}
b1 = *((*sigp)++);
if (b1 == 0xFF) {
/* X.690-0207 8.1.3.5.c the value 0xFF shall not be used. */
return 0;
}
if ((b1 & 0x80) == 0) {
/* X.690-0207 8.1.3.4 short form length octets */
*len = b1;
return 1;
}
if (b1 == 0x80) {
/* Indefinite length is not allowed in DER. */
return 0;
}
/* X.690-207 8.1.3.5 long form length octets */
lenleft = b1 & 0x7F; /* lenleft is at least 1 */
if (lenleft > (size_t)(sigend - *sigp)) {
return 0;
}
if (**sigp == 0) {
/* Not the shortest possible length encoding. */
return 0;
}
if (lenleft > sizeof(size_t)) {
/* The resulting length would exceed the range of a size_t, so
* it is certainly longer than the passed array size. */
return 0;
}
while (lenleft > 0) {
*len = (*len << 8) | **sigp;
(*sigp)++;
lenleft--;
}
if (*len > (size_t)(sigend - *sigp)) {
/* Result exceeds the length of the passed array.
(Checking this is the responsibility of the caller but it
can't hurt do it here, too.) */
return 0;
}
if (*len < 128) {
/* Not the shortest possible length encoding. */
return 0;
}
return 1;
}
static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char **sig, const unsigned char *sigend) {
int overflow = 0;
unsigned char ra[32] = {0};
size_t rlen;
if (*sig == sigend || **sig != 0x02) {
/* Not a primitive integer (X.690-0207 8.3.1). */
return 0;
}
(*sig)++;
if (secp256k1_der_read_len(&rlen, sig, sigend) == 0) {
return 0;
}
if (rlen == 0 || rlen > (size_t)(sigend - *sig)) {
/* Exceeds bounds or not at least length 1 (X.690-0207 8.3.1). */
return 0;
}
if (**sig == 0x00 && rlen > 1 && (((*sig)[1]) & 0x80) == 0x00) {
/* Excessive 0x00 padding. */
return 0;
}
if (**sig == 0xFF && rlen > 1 && (((*sig)[1]) & 0x80) == 0x80) {
/* Excessive 0xFF padding. */
return 0;
}
if ((**sig & 0x80) == 0x80) {
/* Negative. */
overflow = 1;
}
/* There is at most one leading zero byte:
* if there were two leading zero bytes, we would have failed and returned 0
* because of excessive 0x00 padding already. */
if (rlen > 0 && **sig == 0) {
/* Skip leading zero byte */
rlen--;
(*sig)++;
}
if (rlen > 32) {
overflow = 1;
}
if (!overflow) {
if (rlen) memcpy(ra + 32 - rlen, *sig, rlen);
secp256k1_scalar_set_b32(r, ra, &overflow);
}
if (overflow) {
secp256k1_scalar_set_int(r, 0);
}
(*sig) += rlen;
return 1;
}
static int secp256k1_ecdsa_sig_parse(secp256k1_scalar *rr, secp256k1_scalar *rs, const unsigned char *sig, size_t size) {
const unsigned char *sigend = sig + size;
size_t rlen;
if (sig == sigend || *(sig++) != 0x30) {
/* The encoding doesn't start with a constructed sequence (X.690-0207 8.9.1). */
return 0;
}
if (secp256k1_der_read_len(&rlen, &sig, sigend) == 0) {
return 0;
}
if (rlen != (size_t)(sigend - sig)) {
/* Tuple exceeds bounds or garage after tuple. */
return 0;
}
if (!secp256k1_der_parse_integer(rr, &sig, sigend)) {
return 0;
}
if (!secp256k1_der_parse_integer(rs, &sig, sigend)) {
return 0;
}
if (sig != sigend) {
/* Trailing garbage inside tuple. */
return 0;
}
return 1;
}
static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, size_t *size, const secp256k1_scalar* ar, const secp256k1_scalar* as) {
unsigned char r[33] = {0}, s[33] = {0};
unsigned char *rp = r, *sp = s;
size_t lenR = 33, lenS = 33;
secp256k1_scalar_get_b32(&r[1], ar);
secp256k1_scalar_get_b32(&s[1], as);
while (lenR > 1 && rp[0] == 0 && rp[1] < 0x80) { lenR--; rp++; }
while (lenS > 1 && sp[0] == 0 && sp[1] < 0x80) { lenS--; sp++; }
if (*size < 6+lenS+lenR) {
*size = 6 + lenS + lenR;
return 0;
}
*size = 6 + lenS + lenR;
sig[0] = 0x30;
sig[1] = 4 + lenS + lenR;
sig[2] = 0x02;
sig[3] = lenR;
memcpy(sig+4, rp, lenR);
sig[4+lenR] = 0x02;
sig[5+lenR] = lenS;
memcpy(sig+lenR+6, sp, lenS);
return 1;
}
static int secp256k1_ecdsa_sig_verify(const secp256k1_scalar *sigr, const secp256k1_scalar *sigs, const secp256k1_ge *pubkey, const secp256k1_scalar *message) {
unsigned char c[32];
secp256k1_scalar sn, u1, u2;
#if !defined(EXHAUSTIVE_TEST_ORDER)
int range;
secp256k1_fe xr;
#endif
secp256k1_gej pubkeyj;
secp256k1_gej pr;
if (secp256k1_scalar_is_zero(sigr) || secp256k1_scalar_is_zero(sigs)) {
return 0;
}
secp256k1_scalar_inverse_var(&sn, sigs);
secp256k1_scalar_mul(&u1, &sn, message);
secp256k1_scalar_mul(&u2, &sn, sigr);
secp256k1_gej_set_ge(&pubkeyj, pubkey);
secp256k1_ecmult(&pr, &pubkeyj, &u2, &u1);
if (secp256k1_gej_is_infinity(&pr)) {
return 0;
}
#if defined(EXHAUSTIVE_TEST_ORDER)
{
secp256k1_scalar computed_r;
secp256k1_ge pr_ge;
secp256k1_ge_set_gej(&pr_ge, &pr);
secp256k1_fe_normalize(&pr_ge.x);
secp256k1_fe_get_b32(c, &pr_ge.x);
secp256k1_scalar_set_b32(&computed_r, c, NULL);
return secp256k1_scalar_eq(sigr, &computed_r);
}
#else
/* Interpret sigr as a field element xr */
secp256k1_scalar_get_b32(c, sigr);
range = secp256k1_fe_set_b32_limit(&xr, c);
#ifdef VERIFY
/* We know that c is in range; it comes from a scalar. */
VERIFY_CHECK(range);
#else
(void)range;
#endif
/** We now have the recomputed R point in pr, and its claimed x coordinate (modulo n)
* in xr. Naively, we would extract the x coordinate from pr (requiring a inversion modulo p),
* compute the remainder modulo n, and compare it to xr. However:
*
* xr == X(pr) mod n
* <=> exists h. (xr + h * n < p && xr + h * n == X(pr))
* [Since 2 * n > p, h can only be 0 or 1]
* <=> (xr == X(pr)) || (xr + n < p && xr + n == X(pr))
* [In Jacobian coordinates, X(pr) is pr.x / pr.z^2 mod p]
* <=> (xr == pr.x / pr.z^2 mod p) || (xr + n < p && xr + n == pr.x / pr.z^2 mod p)
* [Multiplying both sides of the equations by pr.z^2 mod p]
* <=> (xr * pr.z^2 mod p == pr.x) || (xr + n < p && (xr + n) * pr.z^2 mod p == pr.x)
*
* Thus, we can avoid the inversion, but we have to check both cases separately.
* secp256k1_gej_eq_x implements the (xr * pr.z^2 mod p == pr.x) test.
*/
if (secp256k1_gej_eq_x_var(&xr, &pr)) {
/* xr * pr.z^2 mod p == pr.x, so the signature is valid. */
return 1;
}
if (secp256k1_fe_cmp_var(&xr, &secp256k1_ecdsa_const_p_minus_order) >= 0) {
/* xr + n >= p, so we can skip testing the second case. */
return 0;
}
secp256k1_fe_add(&xr, &secp256k1_ecdsa_const_order_as_fe);
if (secp256k1_gej_eq_x_var(&xr, &pr)) {
/* (xr + n) * pr.z^2 mod p == pr.x, so the signature is valid. */
return 1;
}
return 0;
#endif
}
static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid) {
unsigned char b[32];
secp256k1_gej rp;
secp256k1_ge r;
secp256k1_scalar n;
int overflow = 0;
int high;
secp256k1_ecmult_gen(ctx, &rp, nonce);
secp256k1_ge_set_gej(&r, &rp);
secp256k1_fe_normalize(&r.x);
secp256k1_fe_normalize(&r.y);
secp256k1_fe_get_b32(b, &r.x);
secp256k1_scalar_set_b32(sigr, b, &overflow);
if (recid) {
/* The overflow condition is cryptographically unreachable as hitting it requires finding the discrete log
* of some P where P.x >= order, and only 1 in about 2^127 points meet this criteria.
*/
*recid = (overflow << 1) | secp256k1_fe_is_odd(&r.y);
}
secp256k1_scalar_mul(&n, sigr, seckey);
secp256k1_scalar_add(&n, &n, message);
secp256k1_scalar_inverse(sigs, nonce);
secp256k1_scalar_mul(sigs, sigs, &n);
secp256k1_scalar_clear(&n);
secp256k1_gej_clear(&rp);
secp256k1_ge_clear(&r);
high = secp256k1_scalar_is_high(sigs);
secp256k1_scalar_cond_negate(sigs, high);
if (recid) {
*recid ^= high;
}
/* P.x = order is on the curve, so technically sig->r could end up being zero, which would be an invalid signature.
* This is cryptographically unreachable as hitting it requires finding the discrete log of P.x = N.
*/
return (int)(!secp256k1_scalar_is_zero(sigr)) & (int)(!secp256k1_scalar_is_zero(sigs));
}
#endif /* SECP256K1_ECDSA_IMPL_H */

View File

@@ -0,0 +1,28 @@
/***********************************************************************
* Copyright (c) 2013, 2014 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_ECKEY_H
#define SECP256K1_ECKEY_H
#include <stddef.h>
#include "group.h"
#include "scalar.h"
#include "ecmult.h"
#include "ecmult_gen.h"
static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char *pub, size_t size);
/** Serialize a group element (that is not allowed to be infinity) to a compressed public key (33 bytes). */
static void secp256k1_eckey_pubkey_serialize33(secp256k1_ge *elem, unsigned char *pub33);
/** Serialize a group element (that is not allowed to be infinity) to an uncompressed public key (65 bytes). */
static void secp256k1_eckey_pubkey_serialize65(secp256k1_ge *elem, unsigned char *pub65);
static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar *key, const secp256k1_scalar *tweak);
static int secp256k1_eckey_pubkey_tweak_add(secp256k1_ge *key, const secp256k1_scalar *tweak);
static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar *key, const secp256k1_scalar *tweak);
static int secp256k1_eckey_pubkey_tweak_mul(secp256k1_ge *key, const secp256k1_scalar *tweak);
#endif /* SECP256K1_ECKEY_H */

View File

@@ -0,0 +1,94 @@
/***********************************************************************
* Copyright (c) 2013, 2014 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_ECKEY_IMPL_H
#define SECP256K1_ECKEY_IMPL_H
#include "eckey.h"
#include "util.h"
#include "scalar.h"
#include "field.h"
#include "group.h"
#include "ecmult_gen.h"
static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char *pub, size_t size) {
if (size == 33 && (pub[0] == SECP256K1_TAG_PUBKEY_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_ODD)) {
secp256k1_fe x;
return secp256k1_fe_set_b32_limit(&x, pub+1) && secp256k1_ge_set_xo_var(elem, &x, pub[0] == SECP256K1_TAG_PUBKEY_ODD);
} else if (size == 65 && (pub[0] == SECP256K1_TAG_PUBKEY_UNCOMPRESSED || pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_ODD)) {
secp256k1_fe x, y;
if (!secp256k1_fe_set_b32_limit(&x, pub+1) || !secp256k1_fe_set_b32_limit(&y, pub+33)) {
return 0;
}
secp256k1_ge_set_xy(elem, &x, &y);
if ((pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_ODD) &&
secp256k1_fe_is_odd(&y) != (pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_ODD)) {
return 0;
}
return secp256k1_ge_is_valid_var(elem);
} else {
return 0;
}
}
static void secp256k1_eckey_pubkey_serialize33(secp256k1_ge *elem, unsigned char *pub33) {
VERIFY_CHECK(!secp256k1_ge_is_infinity(elem));
secp256k1_fe_normalize_var(&elem->x);
secp256k1_fe_normalize_var(&elem->y);
pub33[0] = secp256k1_fe_is_odd(&elem->y) ? SECP256K1_TAG_PUBKEY_ODD : SECP256K1_TAG_PUBKEY_EVEN;
secp256k1_fe_get_b32(&pub33[1], &elem->x);
}
static void secp256k1_eckey_pubkey_serialize65(secp256k1_ge *elem, unsigned char *pub65) {
VERIFY_CHECK(!secp256k1_ge_is_infinity(elem));
secp256k1_fe_normalize_var(&elem->x);
secp256k1_fe_normalize_var(&elem->y);
pub65[0] = SECP256K1_TAG_PUBKEY_UNCOMPRESSED;
secp256k1_fe_get_b32(&pub65[1], &elem->x);
secp256k1_fe_get_b32(&pub65[33], &elem->y);
}
static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar *key, const secp256k1_scalar *tweak) {
secp256k1_scalar_add(key, key, tweak);
return !secp256k1_scalar_is_zero(key);
}
static int secp256k1_eckey_pubkey_tweak_add(secp256k1_ge *key, const secp256k1_scalar *tweak) {
secp256k1_gej pt;
secp256k1_gej_set_ge(&pt, key);
secp256k1_ecmult(&pt, &pt, &secp256k1_scalar_one, tweak);
if (secp256k1_gej_is_infinity(&pt)) {
return 0;
}
secp256k1_ge_set_gej(key, &pt);
return 1;
}
static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar *key, const secp256k1_scalar *tweak) {
int ret;
ret = !secp256k1_scalar_is_zero(tweak);
secp256k1_scalar_mul(key, key, tweak);
return ret;
}
static int secp256k1_eckey_pubkey_tweak_mul(secp256k1_ge *key, const secp256k1_scalar *tweak) {
secp256k1_gej pt;
if (secp256k1_scalar_is_zero(tweak)) {
return 0;
}
secp256k1_gej_set_ge(&pt, key);
secp256k1_ecmult(&pt, &pt, tweak, NULL);
secp256k1_ge_set_gej(key, &pt);
return 1;
}
#endif /* SECP256K1_ECKEY_IMPL_H */

View File

@@ -0,0 +1,64 @@
/***********************************************************************
* Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_ECMULT_H
#define SECP256K1_ECMULT_H
#include "group.h"
#include "scalar.h"
#include "scratch.h"
#ifndef ECMULT_WINDOW_SIZE
# define ECMULT_WINDOW_SIZE 15
# ifdef DEBUG_CONFIG
# pragma message DEBUG_CONFIG_MSG("ECMULT_WINDOW_SIZE undefined, assuming default value")
# endif
#endif
#ifdef DEBUG_CONFIG
# pragma message DEBUG_CONFIG_DEF(ECMULT_WINDOW_SIZE)
#endif
/* No one will ever need more than a window size of 24. The code might
* be correct for larger values of ECMULT_WINDOW_SIZE but this is not
* tested.
*
* The following limitations are known, and there are probably more:
* If WINDOW_G > 27 and size_t has 32 bits, then the code is incorrect
* because the size of the memory object that we allocate (in bytes)
* will not fit in a size_t.
* If WINDOW_G > 31 and int has 32 bits, then the code is incorrect
* because certain expressions will overflow.
*/
#if ECMULT_WINDOW_SIZE < 2 || ECMULT_WINDOW_SIZE > 24
# error Set ECMULT_WINDOW_SIZE to an integer in range [2..24].
#endif
/** The number of entries a table with precomputed multiples needs to have. */
#define ECMULT_TABLE_SIZE(w) ((size_t)1 << ((w)-2))
/** Double multiply: R = na*A + ng*G
*
* Passing NULL as ng is equivalent to the zero scalar but a tiny bit faster.
*/
static void secp256k1_ecmult(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng);
typedef int (secp256k1_ecmult_multi_callback)(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data);
/**
* Multi-multiply: R = inp_g_sc * G + sum_i ni * Ai.
* Chooses the right algorithm for a given number of points and scratch space
* size. Resets and overwrites the given scratch space. If the points do not
* fit in the scratch space the algorithm is repeatedly run with batches of
* points. If no scratch space is given then a simple algorithm is used that
* simply multiplies the points with the corresponding scalars and adds them up.
* Returns: 1 on success (including when inp_g_sc is NULL and n is 0)
* 0 if there is not enough scratch space for a single point or
* callback returns 0
*/
static int secp256k1_ecmult_multi_var(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n);
#endif /* SECP256K1_ECMULT_H */

View File

@@ -0,0 +1,16 @@
/*****************************************************************************************************
* Copyright (c) 2013, 2014, 2017, 2021 Pieter Wuille, Andrew Poelstra, Jonas Nick, Russell O'Connor *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php. *
*****************************************************************************************************/
#ifndef SECP256K1_ECMULT_COMPUTE_TABLE_H
#define SECP256K1_ECMULT_COMPUTE_TABLE_H
/* Construct table of all odd multiples of gen in range 1..(2**(window_g-1)-1). */
static void secp256k1_ecmult_compute_table(secp256k1_ge_storage* table, int window_g, const secp256k1_gej* gen);
/* Like secp256k1_ecmult_compute_table, but one for both gen and gen*2^128. */
static void secp256k1_ecmult_compute_two_tables(secp256k1_ge_storage* table, secp256k1_ge_storage* table_128, int window_g, const secp256k1_ge* gen);
#endif /* SECP256K1_ECMULT_COMPUTE_TABLE_H */

View File

@@ -0,0 +1,49 @@
/*****************************************************************************************************
* Copyright (c) 2013, 2014, 2017, 2021 Pieter Wuille, Andrew Poelstra, Jonas Nick, Russell O'Connor *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php. *
*****************************************************************************************************/
#ifndef SECP256K1_ECMULT_COMPUTE_TABLE_IMPL_H
#define SECP256K1_ECMULT_COMPUTE_TABLE_IMPL_H
#include "ecmult_compute_table.h"
#include "group_impl.h"
#include "field_impl.h"
#include "ecmult.h"
#include "util.h"
static void secp256k1_ecmult_compute_table(secp256k1_ge_storage* table, int window_g, const secp256k1_gej* gen) {
secp256k1_gej gj;
secp256k1_ge ge, dgen;
size_t j;
gj = *gen;
secp256k1_ge_set_gej_var(&ge, &gj);
secp256k1_ge_to_storage(&table[0], &ge);
secp256k1_gej_double_var(&gj, gen, NULL);
secp256k1_ge_set_gej_var(&dgen, &gj);
for (j = 1; j < ECMULT_TABLE_SIZE(window_g); ++j) {
secp256k1_gej_set_ge(&gj, &ge);
secp256k1_gej_add_ge_var(&gj, &gj, &dgen, NULL);
secp256k1_ge_set_gej_var(&ge, &gj);
secp256k1_ge_to_storage(&table[j], &ge);
}
}
/* Like secp256k1_ecmult_compute_table, but one for both gen and gen*2^128. */
static void secp256k1_ecmult_compute_two_tables(secp256k1_ge_storage* table, secp256k1_ge_storage* table_128, int window_g, const secp256k1_ge* gen) {
secp256k1_gej gj;
int i;
secp256k1_gej_set_ge(&gj, gen);
secp256k1_ecmult_compute_table(table, window_g, &gj);
for (i = 0; i < 128; ++i) {
secp256k1_gej_double_var(&gj, &gj, NULL);
}
secp256k1_ecmult_compute_table(table_128, window_g, &gj);
}
#endif /* SECP256K1_ECMULT_COMPUTE_TABLE_IMPL_H */

View File

@@ -0,0 +1,38 @@
/***********************************************************************
* Copyright (c) 2015 Andrew Poelstra *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_ECMULT_CONST_H
#define SECP256K1_ECMULT_CONST_H
#include "scalar.h"
#include "group.h"
/**
* Multiply: R = q*A (in constant-time for q)
*/
static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q);
/**
* Same as secp256k1_ecmult_const, but takes in an x coordinate of the base point
* only, specified as fraction n/d (numerator/denominator). Only the x coordinate of the result is
* returned.
*
* If known_on_curve is 0, a verification is performed that n/d is a valid X
* coordinate, and 0 is returned if not. Otherwise, 1 is returned.
*
* d being NULL is interpreted as d=1. If non-NULL, d must not be zero. q must not be zero.
*
* Constant time in the value of q, but not any other inputs.
*/
static int secp256k1_ecmult_const_xonly(
secp256k1_fe *r,
const secp256k1_fe *n,
const secp256k1_fe *d,
const secp256k1_scalar *q,
int known_on_curve
);
#endif /* SECP256K1_ECMULT_CONST_H */

View File

@@ -0,0 +1,402 @@
/***********************************************************************
* Copyright (c) 2015, 2022 Pieter Wuille, Andrew Poelstra *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_ECMULT_CONST_IMPL_H
#define SECP256K1_ECMULT_CONST_IMPL_H
#include "scalar.h"
#include "group.h"
#include "ecmult_const.h"
#include "ecmult_impl.h"
#if defined(EXHAUSTIVE_TEST_ORDER)
/* We need 2^ECMULT_CONST_GROUP_SIZE - 1 to be less than EXHAUSTIVE_TEST_ORDER, because
* the tables cannot have infinities in them (this breaks the effective-affine technique's
* z-ratio tracking) */
# if EXHAUSTIVE_TEST_ORDER == 199
# define ECMULT_CONST_GROUP_SIZE 4
# elif EXHAUSTIVE_TEST_ORDER == 13
# define ECMULT_CONST_GROUP_SIZE 3
# elif EXHAUSTIVE_TEST_ORDER == 7
# define ECMULT_CONST_GROUP_SIZE 2
# else
# error "Unknown EXHAUSTIVE_TEST_ORDER"
# endif
#else
/* Group size 4 or 5 appears optimal. */
# define ECMULT_CONST_GROUP_SIZE 5
#endif
#define ECMULT_CONST_TABLE_SIZE (1L << (ECMULT_CONST_GROUP_SIZE - 1))
#define ECMULT_CONST_GROUPS ((129 + ECMULT_CONST_GROUP_SIZE - 1) / ECMULT_CONST_GROUP_SIZE)
#define ECMULT_CONST_BITS (ECMULT_CONST_GROUPS * ECMULT_CONST_GROUP_SIZE)
/** Fill a table 'pre' with precomputed odd multiples of a.
*
* The resulting point set is brought to a single constant Z denominator, stores the X and Y
* coordinates as ge points in pre, and stores the global Z in globalz.
*
* 'pre' must be an array of size ECMULT_CONST_TABLE_SIZE.
*/
static void secp256k1_ecmult_const_odd_multiples_table_globalz(secp256k1_ge *pre, secp256k1_fe *globalz, const secp256k1_gej *a) {
secp256k1_fe zr[ECMULT_CONST_TABLE_SIZE];
secp256k1_ecmult_odd_multiples_table(ECMULT_CONST_TABLE_SIZE, pre, zr, globalz, a);
secp256k1_ge_table_set_globalz(ECMULT_CONST_TABLE_SIZE, pre, zr);
}
/* Given a table 'pre' with odd multiples of a point, put in r the signed-bit multiplication of n with that point.
*
* For example, if ECMULT_CONST_GROUP_SIZE is 4, then pre is expected to contain 8 entries:
* [1*P, 3*P, 5*P, 7*P, 9*P, 11*P, 13*P, 15*P]. n is then expected to be a 4-bit integer (range 0-15), and its
* bits are interpreted as signs of powers of two to look up.
*
* For example, if n=4, which is 0100 in binary, which is interpreted as [- + - -], so the looked up value is
* [ -(2^3) + (2^2) - (2^1) - (2^0) ]*P = -7*P. Every valid n translates to an odd number in range [-15,15],
* which means we just need to look up one of the precomputed values, and optionally negate it.
*/
#define ECMULT_CONST_TABLE_GET_GE(r,pre,n) do { \
unsigned int m = 0; \
/* If the top bit of n is 0, we want the negation. */ \
volatile unsigned int negative = ((n) >> (ECMULT_CONST_GROUP_SIZE - 1)) ^ 1; \
/* Let n[i] be the i-th bit of n, then the index is
* sum(cnot(n[i]) * 2^i, i=0..l-2)
* where cnot(b) = b if n[l-1] = 1 and 1 - b otherwise.
* For example, if n = 4, in binary 0100, the index is 3, in binary 011.
*
* Proof:
* Let
* x = sum((2*n[i] - 1)*2^i, i=0..l-1)
* = 2*sum(n[i] * 2^i, i=0..l-1) - 2^l + 1
* be the value represented by n.
* The index is (x - 1)/2 if x > 0 and -(x + 1)/2 otherwise.
* Case x > 0:
* n[l-1] = 1
* index = sum(n[i] * 2^i, i=0..l-1) - 2^(l-1)
* = sum(n[i] * 2^i, i=0..l-2)
* Case x <= 0:
* n[l-1] = 0
* index = -(2*sum(n[i] * 2^i, i=0..l-1) - 2^l + 2)/2
* = 2^(l-1) - 1 - sum(n[i] * 2^i, i=0..l-1)
* = sum((1 - n[i]) * 2^i, i=0..l-2)
*/ \
unsigned int index = ((unsigned int)(-negative) ^ n) & ((1U << (ECMULT_CONST_GROUP_SIZE - 1)) - 1U); \
secp256k1_fe neg_y; \
VERIFY_CHECK((n) < (1U << ECMULT_CONST_GROUP_SIZE)); \
VERIFY_CHECK(index < (1U << (ECMULT_CONST_GROUP_SIZE - 1))); \
/* Unconditionally set r->x = (pre)[m].x and r->y = (pre)[m].y because it's either the correct one
* or will get replaced in the later iterations, this is needed to make sure `r` is initialized. */ \
secp256k1_ge_set_xy((r), &(pre)[m].x, &(pre)[m].y); \
for (m = 1; m < ECMULT_CONST_TABLE_SIZE; m++) { \
/* This loop is used to avoid secret data in array indices. See
* the comment in ecmult_gen_impl.h for rationale. */ \
secp256k1_fe_cmov(&(r)->x, &(pre)[m].x, m == index); \
secp256k1_fe_cmov(&(r)->y, &(pre)[m].y, m == index); \
} \
secp256k1_fe_negate(&neg_y, &(r)->y, 1); \
secp256k1_fe_cmov(&(r)->y, &neg_y, negative); \
} while(0)
/* For K as defined in the comment of secp256k1_ecmult_const, we have several precomputed
* formulas/constants.
* - in exhaustive test mode, we give an explicit expression to compute it at compile time: */
#ifdef EXHAUSTIVE_TEST_ORDER
static const secp256k1_scalar secp256k1_ecmult_const_K = ((SECP256K1_SCALAR_CONST(0, 0, 0, (1U << (ECMULT_CONST_BITS - 128)) - 2U, 0, 0, 0, 0) + EXHAUSTIVE_TEST_ORDER - 1U) * (1U + EXHAUSTIVE_TEST_LAMBDA)) % EXHAUSTIVE_TEST_ORDER;
/* - for the real secp256k1 group we have constants for various ECMULT_CONST_BITS values. */
#elif ECMULT_CONST_BITS == 129
/* For GROUP_SIZE = 1,3. */
static const secp256k1_scalar secp256k1_ecmult_const_K = SECP256K1_SCALAR_CONST(0xac9c52b3ul, 0x3fa3cf1ful, 0x5ad9e3fdul, 0x77ed9ba4ul, 0xa880b9fcul, 0x8ec739c2ul, 0xe0cfc810ul, 0xb51283ceul);
#elif ECMULT_CONST_BITS == 130
/* For GROUP_SIZE = 2,5. */
static const secp256k1_scalar secp256k1_ecmult_const_K = SECP256K1_SCALAR_CONST(0xa4e88a7dul, 0xcb13034eul, 0xc2bdd6bful, 0x7c118d6bul, 0x589ae848ul, 0x26ba29e4ul, 0xb5c2c1dcul, 0xde9798d9ul);
#elif ECMULT_CONST_BITS == 132
/* For GROUP_SIZE = 4,6 */
static const secp256k1_scalar secp256k1_ecmult_const_K = SECP256K1_SCALAR_CONST(0x76b1d93dul, 0x0fae3c6bul, 0x3215874bul, 0x94e93813ul, 0x7937fe0dul, 0xb66bcaaful, 0xb3749ca5ul, 0xd7b6171bul);
#else
# error "Unknown ECMULT_CONST_BITS"
#endif
static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q) {
/* The approach below combines the signed-digit logic from Mike Hamburg's
* "Fast and compact elliptic-curve cryptography" (https://eprint.iacr.org/2012/309)
* Section 3.3, with the GLV endomorphism.
*
* The idea there is to interpret the bits of a scalar as signs (1 = +, 0 = -), and compute a
* point multiplication in that fashion. Let v be an n-bit non-negative integer (0 <= v < 2^n),
* and v[i] its i'th bit (so v = sum(v[i] * 2^i, i=0..n-1)). Then define:
*
* C_l(v, A) = sum((2*v[i] - 1) * 2^i*A, i=0..l-1)
*
* Then it holds that C_l(v, A) = sum((2*v[i] - 1) * 2^i*A, i=0..l-1)
* = (2*sum(v[i] * 2^i, i=0..l-1) + 1 - 2^l) * A
* = (2*v + 1 - 2^l) * A
*
* Thus, one can compute q*A as C_256((q + 2^256 - 1) / 2, A). This is the basis for the
* paper's signed-digit multi-comb algorithm for multiplication using a precomputed table.
*
* It is appealing to try to combine this with the GLV optimization: the idea that a scalar
* s can be written as s1 + lambda*s2, where lambda is a curve-specific constant such that
* lambda*A is easy to compute, and where s1 and s2 are small. In particular we have the
* secp256k1_scalar_split_lambda function which performs such a split with the resulting s1
* and s2 in range (-2^128, 2^128) mod n. This does work, but is uninteresting:
*
* To compute q*A:
* - Let s1, s2 = split_lambda(q)
* - Let R1 = C_256((s1 + 2^256 - 1) / 2, A)
* - Let R2 = C_256((s2 + 2^256 - 1) / 2, lambda*A)
* - Return R1 + R2
*
* The issue is that while s1 and s2 are small-range numbers, (s1 + 2^256 - 1) / 2 (mod n)
* and (s2 + 2^256 - 1) / 2 (mod n) are not, undoing the benefit of the splitting.
*
* To make it work, we want to modify the input scalar q first, before splitting, and then only
* add a 2^128 offset of the split results (so that they end up in the single 129-bit range
* [0,2^129]). A slightly smaller offset would work due to the bounds on the split, but we pick
* 2^128 for simplicity. Let s be the scalar fed to split_lambda, and f(q) the function to
* compute it from q:
*
* To compute q*A:
* - Compute s = f(q)
* - Let s1, s2 = split_lambda(s)
* - Let v1 = s1 + 2^128 (mod n)
* - Let v2 = s2 + 2^128 (mod n)
* - Let R1 = C_l(v1, A)
* - Let R2 = C_l(v2, lambda*A)
* - Return R1 + R2
*
* l will thus need to be at least 129, but we may overshoot by a few bits (see
* further), so keep it as a variable.
*
* To solve for s, we reason:
* q*A = R1 + R2
* <=> q*A = C_l(s1 + 2^128, A) + C_l(s2 + 2^128, lambda*A)
* <=> q*A = (2*(s1 + 2^128) + 1 - 2^l) * A + (2*(s2 + 2^128) + 1 - 2^l) * lambda*A
* <=> q*A = (2*(s1 + s2*lambda) + (2^129 + 1 - 2^l) * (1 + lambda)) * A
* <=> q = 2*(s1 + s2*lambda) + (2^129 + 1 - 2^l) * (1 + lambda) (mod n)
* <=> q = 2*s + (2^129 + 1 - 2^l) * (1 + lambda) (mod n)
* <=> s = (q + (2^l - 2^129 - 1) * (1 + lambda)) / 2 (mod n)
* <=> f(q) = (q + K) / 2 (mod n)
* where K = (2^l - 2^129 - 1)*(1 + lambda) (mod n)
*
* We will process the computation of C_l(v1, A) and C_l(v2, lambda*A) in groups of
* ECMULT_CONST_GROUP_SIZE, so we set l to the smallest multiple of ECMULT_CONST_GROUP_SIZE
* that is not less than 129; this equals ECMULT_CONST_BITS.
*/
/* The offset to add to s1 and s2 to make them non-negative. Equal to 2^128. */
static const secp256k1_scalar S_OFFSET = SECP256K1_SCALAR_CONST(0, 0, 0, 1, 0, 0, 0, 0);
secp256k1_scalar s, v1, v2;
secp256k1_ge pre_a[ECMULT_CONST_TABLE_SIZE];
secp256k1_ge pre_a_lam[ECMULT_CONST_TABLE_SIZE];
secp256k1_fe global_z;
int group, i;
/* We're allowed to be non-constant time in the point, and the code below (in particular,
* secp256k1_ecmult_const_odd_multiples_table_globalz) cannot deal with infinity in a
* constant-time manner anyway. */
if (secp256k1_ge_is_infinity(a)) {
secp256k1_gej_set_infinity(r);
return;
}
/* Compute v1 and v2. */
secp256k1_scalar_add(&s, q, &secp256k1_ecmult_const_K);
secp256k1_scalar_half(&s, &s);
secp256k1_scalar_split_lambda(&v1, &v2, &s);
secp256k1_scalar_add(&v1, &v1, &S_OFFSET);
secp256k1_scalar_add(&v2, &v2, &S_OFFSET);
#ifdef VERIFY
/* Verify that v1 and v2 are in range [0, 2^129-1]. */
for (i = 129; i < 256; ++i) {
VERIFY_CHECK(secp256k1_scalar_get_bits_limb32(&v1, i, 1) == 0);
VERIFY_CHECK(secp256k1_scalar_get_bits_limb32(&v2, i, 1) == 0);
}
#endif
/* Calculate odd multiples of A and A*lambda.
* All multiples are brought to the same Z 'denominator', which is stored
* in global_z. Due to secp256k1' isomorphism we can do all operations pretending
* that the Z coordinate was 1, use affine addition formulae, and correct
* the Z coordinate of the result once at the end.
*/
secp256k1_gej_set_ge(r, a);
secp256k1_ecmult_const_odd_multiples_table_globalz(pre_a, &global_z, r);
for (i = 0; i < ECMULT_CONST_TABLE_SIZE; i++) {
secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]);
}
/* Next, we compute r = C_l(v1, A) + C_l(v2, lambda*A).
*
* We proceed in groups of ECMULT_CONST_GROUP_SIZE bits, operating on that many bits
* at a time, from high in v1, v2 to low. Call these bits1 (from v1) and bits2 (from v2).
*
* Now note that ECMULT_CONST_TABLE_GET_GE(&t, pre_a, bits1) loads into t a point equal
* to C_{ECMULT_CONST_GROUP_SIZE}(bits1, A), and analogously for pre_lam_a / bits2.
* This means that all we need to do is add these looked up values together, multiplied
* by 2^(ECMULT_GROUP_SIZE * group).
*/
for (group = ECMULT_CONST_GROUPS - 1; group >= 0; --group) {
/* Using the _var get_bits function is ok here, since it's only variable in offset and count, not in the scalar. */
unsigned int bits1 = secp256k1_scalar_get_bits_var(&v1, group * ECMULT_CONST_GROUP_SIZE, ECMULT_CONST_GROUP_SIZE);
unsigned int bits2 = secp256k1_scalar_get_bits_var(&v2, group * ECMULT_CONST_GROUP_SIZE, ECMULT_CONST_GROUP_SIZE);
secp256k1_ge t;
int j;
ECMULT_CONST_TABLE_GET_GE(&t, pre_a, bits1);
if (group == ECMULT_CONST_GROUPS - 1) {
/* Directly set r in the first iteration. */
secp256k1_gej_set_ge(r, &t);
} else {
/* Shift the result so far up. */
for (j = 0; j < ECMULT_CONST_GROUP_SIZE; ++j) {
secp256k1_gej_double(r, r);
}
secp256k1_gej_add_ge(r, r, &t);
}
ECMULT_CONST_TABLE_GET_GE(&t, pre_a_lam, bits2);
secp256k1_gej_add_ge(r, r, &t);
}
/* Map the result back to the secp256k1 curve from the isomorphic curve. */
secp256k1_fe_mul(&r->z, &r->z, &global_z);
}
static int secp256k1_ecmult_const_xonly(secp256k1_fe* r, const secp256k1_fe *n, const secp256k1_fe *d, const secp256k1_scalar *q, int known_on_curve) {
/* This algorithm is a generalization of Peter Dettman's technique for
* avoiding the square root in a random-basepoint x-only multiplication
* on a Weierstrass curve:
* https://mailarchive.ietf.org/arch/msg/cfrg/7DyYY6gg32wDgHAhgSb6XxMDlJA/
*
*
* === Background: the effective affine technique ===
*
* Let phi_u be the isomorphism that maps (x, y) on secp256k1 curve y^2 = x^3 + 7 to
* x' = u^2*x, y' = u^3*y on curve y'^2 = x'^3 + u^6*7. This new curve has the same order as
* the original (it is isomorphic), but moreover, has the same addition/doubling formulas, as
* the curve b=7 coefficient does not appear in those formulas (or at least does not appear in
* the formulas implemented in this codebase, both affine and Jacobian). See also Example 9.5.2
* in https://www.math.auckland.ac.nz/~sgal018/crypto-book/ch9.pdf.
*
* This means any linear combination of secp256k1 points can be computed by applying phi_u
* (with non-zero u) on all input points (including the generator, if used), computing the
* linear combination on the isomorphic curve (using the same group laws), and then applying
* phi_u^{-1} to get back to secp256k1.
*
* Switching to Jacobian coordinates, note that phi_u applied to (X, Y, Z) is simply
* (X, Y, Z/u). Thus, if we want to compute (X1, Y1, Z) + (X2, Y2, Z), with identical Z
* coordinates, we can use phi_Z to transform it to (X1, Y1, 1) + (X2, Y2, 1) on an isomorphic
* curve where the affine addition formula can be used instead.
* If (X3, Y3, Z3) = (X1, Y1) + (X2, Y2) on that curve, then our answer on secp256k1 is
* (X3, Y3, Z3*Z).
*
* This is the effective affine technique: if we have a linear combination of group elements
* to compute, and all those group elements have the same Z coordinate, we can simply pretend
* that all those Z coordinates are 1, perform the computation that way, and then multiply the
* original Z coordinate back in.
*
* The technique works on any a=0 short Weierstrass curve. It is possible to generalize it to
* other curves too, but there the isomorphic curves will have different 'a' coefficients,
* which typically does affect the group laws.
*
*
* === Avoiding the square root for x-only point multiplication ===
*
* In this function, we want to compute the X coordinate of q*(n/d, y), for
* y = sqrt((n/d)^3 + 7). Its negation would also be a valid Y coordinate, but by convention
* we pick whatever sqrt returns (which we assume to be a deterministic function).
*
* Let g = y^2*d^3 = n^3 + 7*d^3. This also means y = sqrt(g/d^3).
* Further let v = sqrt(d*g), which must exist as d*g = y^2*d^4 = (y*d^2)^2.
*
* The input point (n/d, y) also has Jacobian coordinates:
*
* (n/d, y, 1)
* = (n/d * v^2, y * v^3, v)
* = (n/d * d*g, y * sqrt(d^3*g^3), v)
* = (n/d * d*g, sqrt(y^2 * d^3*g^3), v)
* = (n*g, sqrt(g/d^3 * d^3*g^3), v)
* = (n*g, sqrt(g^4), v)
* = (n*g, g^2, v)
*
* It is easy to verify that both (n*g, g^2, v) and its negation (n*g, -g^2, v) have affine X
* coordinate n/d, and this holds even when the square root function doesn't have a
* deterministic sign. We choose the (n*g, g^2, v) version.
*
* Now switch to the effective affine curve using phi_v, where the input point has coordinates
* (n*g, g^2). Compute (X, Y, Z) = q * (n*g, g^2) there.
*
* Back on secp256k1, that means q * (n*g, g^2, v) = (X, Y, v*Z). This last point has affine X
* coordinate X / (v^2*Z^2) = X / (d*g*Z^2). Determining the affine Y coordinate would involve
* a square root, but as long as we only care about the resulting X coordinate, no square root
* is needed anywhere in this computation.
*/
secp256k1_fe g, i;
secp256k1_ge p;
secp256k1_gej rj;
/* Compute g = (n^3 + B*d^3). */
secp256k1_fe_sqr(&g, n);
secp256k1_fe_mul(&g, &g, n);
if (d) {
secp256k1_fe b;
VERIFY_CHECK(!secp256k1_fe_normalizes_to_zero(d));
secp256k1_fe_sqr(&b, d);
VERIFY_CHECK(SECP256K1_B <= 8); /* magnitude of b will be <= 8 after the next call */
secp256k1_fe_mul_int(&b, SECP256K1_B);
secp256k1_fe_mul(&b, &b, d);
secp256k1_fe_add(&g, &b);
if (!known_on_curve) {
/* We need to determine whether (n/d)^3 + 7 is square.
*
* is_square((n/d)^3 + 7)
* <=> is_square(((n/d)^3 + 7) * d^4)
* <=> is_square((n^3 + 7*d^3) * d)
* <=> is_square(g * d)
*/
secp256k1_fe c;
secp256k1_fe_mul(&c, &g, d);
if (!secp256k1_fe_is_square_var(&c)) return 0;
}
} else {
secp256k1_fe_add_int(&g, SECP256K1_B);
if (!known_on_curve) {
/* g at this point equals x^3 + 7. Test if it is square. */
if (!secp256k1_fe_is_square_var(&g)) return 0;
}
}
SECP256K1_FE_VERIFY_MAGNITUDE(&g, 2);
/* Compute base point P = (n*g, g^2), the effective affine version of
* (n*g, g^2, v), which has corresponding affine X coordinate n/d. */
{
secp256k1_fe x, y;
secp256k1_fe_mul(&x, &g, n);
secp256k1_fe_sqr(&y, &g);
secp256k1_ge_set_xy(&p, &x, &y);
}
/* Perform x-only EC multiplication of P with q. */
VERIFY_CHECK(!secp256k1_scalar_is_zero(q));
secp256k1_ecmult_const(&rj, &p, q);
VERIFY_CHECK(!secp256k1_gej_is_infinity(&rj));
/* The resulting (X, Y, Z) point on the effective-affine isomorphic curve corresponds to
* (X, Y, Z*v) on the secp256k1 curve. The affine version of that has X coordinate
* (X / (Z^2*d*g)). */
secp256k1_fe_sqr(&i, &rj.z);
secp256k1_fe_mul(&i, &i, &g);
if (d) secp256k1_fe_mul(&i, &i, d);
secp256k1_fe_inv(&i, &i);
secp256k1_fe_mul(r, &rj.x, &i);
return 1;
}
#endif /* SECP256K1_ECMULT_CONST_IMPL_H */

View File

@@ -0,0 +1,144 @@
/***********************************************************************
* Copyright (c) Pieter Wuille, Peter Dettman *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_ECMULT_GEN_H
#define SECP256K1_ECMULT_GEN_H
#include "hash.h"
#include "scalar.h"
#include "group.h"
/* Configuration parameters for the signed-digit multi-comb algorithm:
*
* - COMB_BLOCKS is the number of blocks the input is split into. Each
* has a corresponding table.
* - COMB_TEETH is the number of bits simultaneously covered by one table.
* - COMB_RANGE is the number of bits in supported scalars. For production
* purposes, only 256 is reasonable, but smaller numbers are supported for
* exhaustive test mode.
*
* The comb's spacing (COMB_SPACING), or the distance between the teeth,
* is defined as ceil(COMB_RANGE / (COMB_BLOCKS * COMB_TEETH)). Each block covers
* COMB_SPACING * COMB_TEETH consecutive bits in the input.
*
* The size of the precomputed table is COMB_BLOCKS * (1 << (COMB_TEETH - 1))
* secp256k1_ge_storages.
*
* The number of point additions equals COMB_BLOCKS * COMB_SPACING. Each point
* addition involves a cmov from (1 << (COMB_TEETH - 1)) table entries and a
* conditional negation.
*
* The number of point doublings is COMB_SPACING - 1. */
#if defined(EXHAUSTIVE_TEST_ORDER)
/* We need to control these values for exhaustive tests because
* the table cannot have infinities in them (secp256k1_ge_storage
* doesn't support infinities) */
# undef COMB_BLOCKS
# undef COMB_TEETH
# if EXHAUSTIVE_TEST_ORDER == 7
# define COMB_RANGE 3
# define COMB_BLOCKS 1
# define COMB_TEETH 2
# elif EXHAUSTIVE_TEST_ORDER == 13
# define COMB_RANGE 4
# define COMB_BLOCKS 1
# define COMB_TEETH 2
# elif EXHAUSTIVE_TEST_ORDER == 199
# define COMB_RANGE 8
# define COMB_BLOCKS 2
# define COMB_TEETH 3
# else
# error "Unknown exhaustive test order"
# endif
# if (COMB_RANGE >= 32) || ((EXHAUSTIVE_TEST_ORDER >> (COMB_RANGE - 1)) != 1)
# error "COMB_RANGE != ceil(log2(EXHAUSTIVE_TEST_ORDER+1))"
# endif
#else /* !defined(EXHAUSTIVE_TEST_ORDER) */
# define COMB_RANGE 256
#endif /* defined(EXHAUSTIVE_TEST_ORDER) */
/* Use (11, 6) as default configuration, which results in a 22 kB table. */
#ifndef COMB_BLOCKS
# define COMB_BLOCKS 11
# ifdef DEBUG_CONFIG
# pragma message DEBUG_CONFIG_MSG("COMB_BLOCKS undefined, assuming default value")
# endif
#endif
#ifndef COMB_TEETH
# define COMB_TEETH 6
# ifdef DEBUG_CONFIG
# pragma message DEBUG_CONFIG_MSG("COMB_TEETH undefined, assuming default value")
# endif
#endif
/* Use ceil(COMB_RANGE / (COMB_BLOCKS * COMB_TEETH)) as COMB_SPACING. */
#define COMB_SPACING CEIL_DIV(COMB_RANGE, COMB_BLOCKS * COMB_TEETH)
/* Range checks on the parameters. */
/* The remaining COMB_* parameters are derived values, don't modify these. */
/* - The number of bits covered by all the blocks; must be at least COMB_RANGE. */
#define COMB_BITS (COMB_BLOCKS * COMB_TEETH * COMB_SPACING)
/* - The number of entries per table. */
#define COMB_POINTS (1 << (COMB_TEETH - 1))
/* Sanity checks. */
#if !(1 <= COMB_BLOCKS && COMB_BLOCKS <= 256)
# error "COMB_BLOCKS must be in the range [1, 256]"
#endif
#if !(1 <= COMB_TEETH && COMB_TEETH <= 8)
# error "COMB_TEETH must be in the range [1, 8]"
#endif
#if COMB_BITS < COMB_RANGE
# error "COMB_BLOCKS * COMB_TEETH * COMB_SPACING is too low"
#endif
/* These last 2 checks are not strictly required, but prevent gratuitously inefficient
* configurations. Note that they compare with 256 rather than COMB_RANGE, so they do
* permit somewhat excessive values for the exhaustive test case, where testing with
* suboptimal parameters may be desirable. */
#if (COMB_BLOCKS - 1) * COMB_TEETH * COMB_SPACING >= 256
# error "COMB_BLOCKS can be reduced"
#endif
#if COMB_BLOCKS * (COMB_TEETH - 1) * COMB_SPACING >= 256
# error "COMB_TEETH can be reduced"
#endif
#ifdef DEBUG_CONFIG
# pragma message DEBUG_CONFIG_DEF(COMB_RANGE)
# pragma message DEBUG_CONFIG_DEF(COMB_BLOCKS)
# pragma message DEBUG_CONFIG_DEF(COMB_TEETH)
# pragma message DEBUG_CONFIG_DEF(COMB_SPACING)
#endif
typedef struct {
/* Whether the context has been built. */
int built;
/* Values chosen such that
*
* n*G == comb(n + scalar_offset, G/2) + ge_offset.
*
* This expression lets us use scalar blinding and optimize the comb precomputation. See
* ecmult_gen_impl.h for more details. */
secp256k1_scalar scalar_offset;
secp256k1_ge ge_offset;
/* Factor used for projective blinding. This value is used to rescale the Z
* coordinate of the first table lookup. */
secp256k1_fe proj_blind;
} secp256k1_ecmult_gen_context;
static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context* ctx, const secp256k1_hash_ctx *hash_ctx);
static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context* ctx);
/** Multiply with the generator: R = a*G */
static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context* ctx, secp256k1_gej *r, const secp256k1_scalar *a);
static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const secp256k1_hash_ctx *hash_ctx, const unsigned char *seed32);
#endif /* SECP256K1_ECMULT_GEN_H */

View File

@@ -0,0 +1,14 @@
/***********************************************************************
* Copyright (c) Pieter Wuille, Gregory Maxwell *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_ECMULT_GEN_COMPUTE_TABLE_H
#define SECP256K1_ECMULT_GEN_COMPUTE_TABLE_H
#include "ecmult_gen.h"
static void secp256k1_ecmult_gen_compute_table(secp256k1_ge_storage* table, const secp256k1_ge* gen, int blocks, int teeth, int spacing);
#endif /* SECP256K1_ECMULT_GEN_COMPUTE_TABLE_H */

View File

@@ -0,0 +1,108 @@
/***********************************************************************
* Copyright (c) Pieter Wuille, Gregory Maxwell, Peter Dettman *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_ECMULT_GEN_COMPUTE_TABLE_IMPL_H
#define SECP256K1_ECMULT_GEN_COMPUTE_TABLE_IMPL_H
#include "ecmult_gen_compute_table.h"
#include "group_impl.h"
#include "field_impl.h"
#include "scalar_impl.h"
#include "ecmult_gen.h"
#include "util.h"
static void secp256k1_ecmult_gen_compute_table(secp256k1_ge_storage* table, const secp256k1_ge* gen, int blocks, int teeth, int spacing) {
size_t points = ((size_t)1) << (teeth - 1);
size_t points_total = points * blocks;
secp256k1_ge* prec = checked_malloc(&default_error_callback, points_total * sizeof(*prec));
secp256k1_gej* ds = checked_malloc(&default_error_callback, teeth * sizeof(*ds));
secp256k1_gej* vs = checked_malloc(&default_error_callback, points_total * sizeof(*vs));
secp256k1_gej u;
size_t vs_pos = 0;
secp256k1_scalar half;
int block, i;
VERIFY_CHECK(points_total > 0);
/* u is the running power of two times gen we're working with, initially gen/2. */
secp256k1_scalar_half(&half, &secp256k1_scalar_one);
secp256k1_gej_set_infinity(&u);
for (i = 255; i >= 0; --i) {
/* Use a very simple multiplication ladder to avoid dependency on ecmult. */
secp256k1_gej_double_var(&u, &u, NULL);
if (secp256k1_scalar_get_bits_limb32(&half, i, 1)) {
secp256k1_gej_add_ge_var(&u, &u, gen, NULL);
}
}
#ifdef VERIFY
{
/* Verify that u*2 = gen. */
secp256k1_gej double_u;
secp256k1_gej_double_var(&double_u, &u, NULL);
VERIFY_CHECK(secp256k1_gej_eq_ge_var(&double_u, gen));
}
#endif
for (block = 0; block < blocks; ++block) {
int tooth;
/* Here u = 2^(block*teeth*spacing) * gen/2. */
secp256k1_gej sum;
secp256k1_gej_set_infinity(&sum);
for (tooth = 0; tooth < teeth; ++tooth) {
/* Here u = 2^((block*teeth + tooth)*spacing) * gen/2. */
/* Make sum = sum(2^((block*teeth + t)*spacing), t=0..tooth) * gen/2. */
secp256k1_gej_add_var(&sum, &sum, &u, NULL);
/* Make u = 2^((block*teeth + tooth)*spacing + 1) * gen/2. */
secp256k1_gej_double_var(&u, &u, NULL);
/* Make ds[tooth] = u = 2^((block*teeth + tooth)*spacing + 1) * gen/2. */
ds[tooth] = u;
/* Make u = 2^((block*teeth + tooth + 1)*spacing) * gen/2, unless at the end. */
if (block + tooth != blocks + teeth - 2) {
int bit_off;
for (bit_off = 1; bit_off < spacing; ++bit_off) {
secp256k1_gej_double_var(&u, &u, NULL);
}
}
}
/* Now u = 2^((block*teeth + teeth)*spacing) * gen/2
* = 2^((block+1)*teeth*spacing) * gen/2 */
/* Next, compute the table entries for block number block in Jacobian coordinates.
* The entries will occupy vs[block*points + i] for i=0..points-1.
* We start by computing the first (i=0) value corresponding to all summed
* powers of two times G being negative. */
secp256k1_gej_neg(&vs[vs_pos++], &sum);
/* And then teeth-1 times "double" the range of i values for which the table
* is computed: in each iteration, double the table by taking an existing
* table entry and adding ds[tooth]. */
for (tooth = 0; tooth < teeth - 1; ++tooth) {
size_t stride = ((size_t)1) << tooth;
size_t index;
for (index = 0; index < stride; ++index, ++vs_pos) {
secp256k1_gej_add_var(&vs[vs_pos], &vs[vs_pos - stride], &ds[tooth], NULL);
}
}
}
VERIFY_CHECK(vs_pos == points_total);
/* Convert all points simultaneously from secp256k1_gej to secp256k1_ge. */
secp256k1_ge_set_all_gej_var(prec, vs, points_total);
/* Convert all points from secp256k1_ge to secp256k1_ge_storage output. */
for (block = 0; block < blocks; ++block) {
size_t index;
for (index = 0; index < points; ++index) {
VERIFY_CHECK(!secp256k1_ge_is_infinity(&prec[block * points + index]));
secp256k1_ge_to_storage(&table[block * points + index], &prec[block * points + index]);
}
}
/* Free memory. */
free(vs);
free(ds);
free(prec);
}
#endif /* SECP256K1_ECMULT_GEN_COMPUTE_TABLE_IMPL_H */

View File

@@ -0,0 +1,341 @@
/***********************************************************************
* Copyright (c) Pieter Wuille, Gregory Maxwell, Peter Dettman *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_ECMULT_GEN_IMPL_H
#define SECP256K1_ECMULT_GEN_IMPL_H
#include "util.h"
#include "scalar.h"
#include "group.h"
#include "ecmult_gen.h"
#include "hash_impl.h"
#include "precomputed_ecmult_gen.h"
static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx, const secp256k1_hash_ctx *hash_ctx) {
secp256k1_ecmult_gen_blind(ctx, hash_ctx, NULL);
ctx->built = 1;
}
static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_context* ctx) {
return ctx->built;
}
static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context *ctx) {
ctx->built = 0;
secp256k1_scalar_clear(&ctx->scalar_offset);
secp256k1_ge_clear(&ctx->ge_offset);
secp256k1_fe_clear(&ctx->proj_blind);
}
/* Compute the scalar (2^COMB_BITS - 1) / 2, the difference between the gn argument to
* secp256k1_ecmult_gen, and the scalar whose encoding the table lookup bits are drawn
* from (before applying blinding). */
static void secp256k1_ecmult_gen_scalar_diff(secp256k1_scalar* diff) {
int i;
/* Compute scalar -1/2. */
secp256k1_scalar neghalf;
secp256k1_scalar_half(&neghalf, &secp256k1_scalar_one);
secp256k1_scalar_negate(&neghalf, &neghalf);
/* Compute offset = 2^(COMB_BITS - 1). */
*diff = secp256k1_scalar_one;
for (i = 0; i < COMB_BITS - 1; ++i) {
secp256k1_scalar_add(diff, diff, diff);
}
/* The result is the sum 2^(COMB_BITS - 1) + (-1/2). */
secp256k1_scalar_add(diff, diff, &neghalf);
}
static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp256k1_gej *r, const secp256k1_scalar *gn) {
uint32_t comb_off;
secp256k1_ge add;
secp256k1_fe neg;
secp256k1_ge_storage adds;
secp256k1_scalar d;
/* Array of uint32_t values large enough to store COMB_BITS bits. Only the bottom
* 8 are ever nonzero, but having the zero padding at the end if COMB_BITS>256
* avoids the need to deal with out-of-bounds reads from a scalar. */
uint32_t recoded[(COMB_BITS + 31) >> 5] = {0};
int first = 1, i;
memset(&adds, 0, sizeof(adds));
/* We want to compute R = gn*G.
*
* To blind the scalar used in the computation, we rewrite this to be
* R = (gn - b)*G + b*G, with a blinding value b determined by the context.
*
* The multiplication (gn-b)*G will be performed using a signed-digit multi-comb (see Section
* 3.3 of "Fast and compact elliptic-curve cryptography" by Mike Hamburg,
* https://eprint.iacr.org/2012/309).
*
* Let comb(s, P) = sum((2*s[i]-1)*2^i*P for i=0..COMB_BITS-1), where s[i] is the i'th bit of
* the binary representation of scalar s. So the s[i] values determine whether -2^i*P (s[i]=0)
* or +2^i*P (s[i]=1) are added together. COMB_BITS is at least 256, so all bits of s are
* covered. By manipulating:
*
* comb(s, P) = sum((2*s[i]-1)*2^i*P for i=0..COMB_BITS-1)
* <=> comb(s, P) = sum((2*s[i]-1)*2^i for i=0..COMB_BITS-1) * P
* <=> comb(s, P) = (2*sum(s[i]*2^i for i=0..COMB_BITS-1) - sum(2^i for i=0..COMB_BITS-1)) * P
* <=> comb(s, P) = (2*s - (2^COMB_BITS - 1)) * P
*
* If we wanted to compute (gn-b)*G as comb(s, G), it would need to hold that
*
* (gn - b) * G = (2*s - (2^COMB_BITS - 1)) * G
* <=> s = (gn - b + (2^COMB_BITS - 1))/2 (mod order)
*
* We use an alternative here that avoids the modular division by two: instead we compute
* (gn-b)*G as comb(d, G/2). For that to hold it must be the case that
*
* (gn - b) * G = (2*d - (2^COMB_BITS - 1)) * (G/2)
* <=> d = gn - b + (2^COMB_BITS - 1)/2 (mod order)
*
* Adding precomputation, our final equations become:
*
* ctx->scalar_offset = (2^COMB_BITS - 1)/2 - b (mod order)
* ctx->ge_offset = b*G
* d = gn + ctx->scalar_offset (mod order)
* R = comb(d, G/2) + ctx->ge_offset
*
* comb(d, G/2) function is then computed by summing + or - 2^(i-1)*G, for i=0..COMB_BITS-1,
* depending on the value of the bits d[i] of the binary representation of scalar d.
*/
/* Compute the scalar d = (gn + ctx->scalar_offset). */
secp256k1_scalar_add(&d, &ctx->scalar_offset, gn);
/* Convert to recoded array. */
for (i = 0; i < 8 && i < ((COMB_BITS + 31) >> 5); ++i) {
recoded[i] = secp256k1_scalar_get_bits_limb32(&d, 32 * i, 32);
}
secp256k1_scalar_clear(&d);
/* In secp256k1_ecmult_gen_prec_table we have precomputed sums of the
* (2*d[i]-1) * 2^(i-1) * G points, for various combinations of i positions.
* We rewrite our equation in terms of these table entries.
*
* Let mask(b) = sum(2^((b*COMB_TEETH + t)*COMB_SPACING) for t=0..COMB_TEETH-1),
* with b ranging from 0 to COMB_BLOCKS-1. So for example with COMB_BLOCKS=11,
* COMB_TEETH=6, COMB_SPACING=4, we would have:
* mask(0) = 2^0 + 2^4 + 2^8 + 2^12 + 2^16 + 2^20,
* mask(1) = 2^24 + 2^28 + 2^32 + 2^36 + 2^40 + 2^44,
* mask(2) = 2^48 + 2^52 + 2^56 + 2^60 + 2^64 + 2^68,
* ...
* mask(10) = 2^240 + 2^244 + 2^248 + 2^252 + 2^256 + 2^260
*
* We will split up the bits d[i] using these masks. Specifically, each mask is
* used COMB_SPACING times, with different shifts:
*
* d = (d & mask(0)<<0) + (d & mask(1)<<0) + ... + (d & mask(COMB_BLOCKS-1)<<0) +
* (d & mask(0)<<1) + (d & mask(1)<<1) + ... + (d & mask(COMB_BLOCKS-1)<<1) +
* ...
* (d & mask(0)<<(COMB_SPACING-1)) + ...
*
* Now define table(b, m) = (m - mask(b)/2) * G, and we will precompute these values for
* b=0..COMB_BLOCKS-1, and for all values m which (d & mask(b)) can take (so m can take on
* 2^COMB_TEETH distinct values).
*
* If m=(d & mask(b)), then table(b, m) is the sum of 2^i * (2*d[i]-1) * G/2, with i
* iterating over the set bits in mask(b). In our example, table(2, 2^48 + 2^56 + 2^68)
* would equal (2^48 - 2^52 + 2^56 - 2^60 - 2^64 + 2^68) * G/2.
*
* With that, we can rewrite comb(d, G/2) as:
*
* 2^0 * (table(0, d>>0 & mask(0)) + ... + table(COMB_BLOCKS-1, d>>0 & mask(COMP_BLOCKS-1)))
* + 2^1 * (table(0, d>>1 & mask(0)) + ... + table(COMB_BLOCKS-1, d>>1 & mask(COMP_BLOCKS-1)))
* + 2^2 * (table(0, d>>2 & mask(0)) + ... + table(COMB_BLOCKS-1, d>>2 & mask(COMP_BLOCKS-1)))
* + ...
* + 2^(COMB_SPACING-1) * (table(0, d>>(COMB_SPACING-1) & mask(0)) + ...)
*
* Or more generically as
*
* sum(2^i * sum(table(b, d>>i & mask(b)), b=0..COMB_BLOCKS-1), i=0..COMB_SPACING-1)
*
* This is implemented using an outer loop that runs in reverse order over the lines of this
* equation, which in each iteration runs an inner loop that adds the terms of that line and
* then doubles the result before proceeding to the next line.
*
* In pseudocode:
* c = infinity
* for comb_off in range(COMB_SPACING - 1, -1, -1):
* for block in range(COMB_BLOCKS):
* c += table(block, (d >> comb_off) & mask(block))
* if comb_off > 0:
* c = 2*c
* return c
*
* This computes c = comb(d, G/2), and thus finally R = c + ctx->ge_offset. Note that it would
* be possible to apply an initial offset instead of a final offset (moving ge_offset to take
* the place of infinity above), but the chosen approach allows using (in a future improvement)
* an incomplete addition formula for most of the multiplication.
*
* The last question is how to implement the table(b, m) function. For any value of b,
* m=(d & mask(b)) can only take on at most 2^COMB_TEETH possible values (the last one may have
* fewer as there mask(b) may exceed the curve order). So we could create COMB_BLOCK tables
* which contain a value for each such m value.
*
* Now note that if m=(d & mask(b)), then flipping the relevant bits of m results in negating
* the result of table(b, m). This is because table(b,m XOR mask(b)) = table(b, mask(b) - m) =
* (mask(b) - m - mask(b)/2)*G = (-m + mask(b)/2)*G = -(m - mask(b)/2)*G = -table(b, m).
* Because of this it suffices to only store the first half of the m values for every b. If an
* entry from the second half is needed, we look up its bit-flipped version instead, and negate
* it.
*
* secp256k1_ecmult_gen_prec_table[b][index] stores the table(b, m) entries. Index
* is the relevant mask(b) bits of m packed together without gaps. */
/* Outer loop: iterate over comb_off from COMB_SPACING - 1 down to 0. */
comb_off = COMB_SPACING - 1;
while (1) {
uint32_t block;
uint32_t bit_pos = comb_off;
/* Inner loop: for each block, add table entries to the result. */
for (block = 0; block < COMB_BLOCKS; ++block) {
/* Gather the mask(block)-selected bits of d into bits. They're packed:
* bits[tooth] = d[(block*COMB_TEETH + tooth)*COMB_SPACING + comb_off]. */
uint32_t bits = 0, sign, abs, index, tooth;
/* Instead of reading individual bits here to construct the bits variable,
* build up the result by xoring rotated reads together. In every iteration,
* one additional bit is made correct, starting at the bottom. The bits
* above that contain junk. This reduces leakage by avoiding computations
* on variables that can have only a low number of possible values (e.g.,
* just two values when reading a single bit into a variable.) See:
* https://www.usenix.org/system/files/conference/usenixsecurity18/sec18-alam.pdf
*/
for (tooth = 0; tooth < COMB_TEETH; ++tooth) {
/* Construct bitdata s.t. the bottom bit is the bit we'd like to read.
*
* We could just set bitdata = recoded[bit_pos >> 5] >> (bit_pos & 0x1f)
* but this would simply discard the bits that fall off at the bottom,
* and thus, for example, bitdata could still have only two values if we
* happen to shift by exactly 31 positions. We use a rotation instead,
* which ensures that bitdata doesn't lose entropy. This relies on the
* rotation being atomic, i.e., the compiler emitting an actual rot
* instruction. */
uint32_t bitdata = secp256k1_rotr32(recoded[bit_pos >> 5], bit_pos & 0x1f);
/* Clear the bit at position tooth, but sssh, don't tell clang. */
uint32_t volatile vmask = ~(1 << tooth);
bits &= vmask;
/* Write the bit into position tooth (and junk into higher bits). */
bits ^= bitdata << tooth;
bit_pos += COMB_SPACING;
}
/* If the top bit of bits is 1, flip them all (corresponding to looking up
* the negated table value), and remember to negate the result in sign. */
sign = (bits >> (COMB_TEETH - 1)) & 1;
abs = (bits ^ -sign) & (COMB_POINTS - 1);
VERIFY_CHECK(sign == 0 || sign == 1);
VERIFY_CHECK(abs < COMB_POINTS);
/** This uses a conditional move to avoid any secret data in array indexes.
* _Any_ use of secret indexes has been demonstrated to result in timing
* sidechannels, even when the cache-line access patterns are uniform.
* See also:
* "A word of warning", CHES 2013 Rump Session, by Daniel J. Bernstein and Peter Schwabe
* (https://cryptojedi.org/peter/data/chesrump-20130822.pdf) and
* "Cache Attacks and Countermeasures: the Case of AES", RSA 2006,
* by Dag Arne Osvik, Adi Shamir, and Eran Tromer
* (https://eprint.iacr.org/2005/271.pdf)
*/
for (index = 0; index < COMB_POINTS; ++index) {
secp256k1_ge_storage_cmov(&adds, &secp256k1_ecmult_gen_prec_table[block][index], index == abs);
}
/* Set add=adds or add=-adds, in constant time, based on sign. */
secp256k1_ge_from_storage(&add, &adds);
secp256k1_fe_negate(&neg, &add.y, 1);
secp256k1_fe_cmov(&add.y, &neg, sign);
/* Add the looked up and conditionally negated value to r. */
if (EXPECT(first, 0)) {
/* If this is the first table lookup, we can skip addition. */
secp256k1_gej_set_ge(r, &add);
/* Give the entry a random Z coordinate to blind intermediary results. */
secp256k1_gej_rescale(r, &ctx->proj_blind);
first = 0;
} else {
secp256k1_gej_add_ge(r, r, &add);
}
}
/* Double the result, except in the last iteration. */
if (comb_off-- == 0) break;
secp256k1_gej_double(r, r);
}
/* Correct for the scalar_offset added at the start (ge_offset = b*G, while b was
* subtracted from the input scalar gn). */
secp256k1_gej_add_ge(r, r, &ctx->ge_offset);
/* Cleanup. */
secp256k1_fe_clear(&neg);
secp256k1_ge_clear(&add);
secp256k1_memclear_explicit(&adds, sizeof(adds));
secp256k1_memclear_explicit(&recoded, sizeof(recoded));
}
/* Setup blinding values for secp256k1_ecmult_gen. */
static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const secp256k1_hash_ctx *hash_ctx, const unsigned char *seed32) {
secp256k1_scalar b;
secp256k1_scalar diff;
secp256k1_gej gb;
secp256k1_fe f;
unsigned char nonce32[32];
secp256k1_rfc6979_hmac_sha256 rng;
unsigned char keydata[64];
/* Compute the (2^COMB_BITS - 1)/2 term once. */
secp256k1_ecmult_gen_scalar_diff(&diff);
if (seed32 == NULL) {
/* When seed is NULL, reset the final point and blinding value. */
secp256k1_ge_neg(&ctx->ge_offset, &secp256k1_ge_const_g);
secp256k1_scalar_add(&ctx->scalar_offset, &secp256k1_scalar_one, &diff);
ctx->proj_blind = secp256k1_fe_one;
return;
}
/* The prior blinding value (if not reset) is chained forward by including it in the hash. */
secp256k1_scalar_get_b32(keydata, &ctx->scalar_offset);
/** Using a CSPRNG allows a failure free interface, avoids needing large amounts of random data,
* and guards against weak or adversarial seeds. This is a simpler and safer interface than
* asking the caller for blinding values directly and expecting them to retry on failure.
*/
VERIFY_CHECK(seed32 != NULL);
memcpy(keydata + 32, seed32, 32);
secp256k1_rfc6979_hmac_sha256_initialize(hash_ctx, &rng, keydata, 64);
secp256k1_memclear_explicit(keydata, sizeof(keydata));
/* Compute projective blinding factor (cannot be 0). */
secp256k1_rfc6979_hmac_sha256_generate(hash_ctx, &rng, nonce32, 32);
secp256k1_fe_set_b32_mod(&f, nonce32);
secp256k1_fe_cmov(&f, &secp256k1_fe_one, secp256k1_fe_normalizes_to_zero(&f));
ctx->proj_blind = f;
/* For a random blinding value b, set scalar_offset=diff-b, ge_offset=bG */
secp256k1_rfc6979_hmac_sha256_generate(hash_ctx, &rng, nonce32, 32);
secp256k1_scalar_set_b32(&b, nonce32, NULL);
/* The blinding value cannot be zero, as that would mean ge_offset = infinity,
* which secp256k1_gej_add_ge cannot handle. */
secp256k1_scalar_cmov(&b, &secp256k1_scalar_one, secp256k1_scalar_is_zero(&b));
secp256k1_rfc6979_hmac_sha256_finalize(&rng);
secp256k1_ecmult_gen(ctx, &gb, &b);
secp256k1_scalar_negate(&b, &b);
secp256k1_scalar_add(&ctx->scalar_offset, &b, &diff);
secp256k1_ge_set_gej(&ctx->ge_offset, &gb);
/* Clean up. */
secp256k1_memclear_explicit(nonce32, sizeof(nonce32));
secp256k1_scalar_clear(&b);
secp256k1_gej_clear(&gb);
secp256k1_fe_clear(&f);
secp256k1_rfc6979_hmac_sha256_clear(&rng);
}
#endif /* SECP256K1_ECMULT_GEN_IMPL_H */

View File

@@ -0,0 +1,869 @@
/******************************************************************************
* Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra, Jonas Nick *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php. *
******************************************************************************/
#ifndef SECP256K1_ECMULT_IMPL_H
#define SECP256K1_ECMULT_IMPL_H
#include <string.h>
#include <stdint.h>
#include "util.h"
#include "group.h"
#include "scalar.h"
#include "ecmult.h"
#include "precomputed_ecmult.h"
#if defined(EXHAUSTIVE_TEST_ORDER)
/* We need to lower these values for exhaustive tests because
* the tables cannot have infinities in them (this breaks the
* affine-isomorphism stuff which tracks z-ratios) */
# if EXHAUSTIVE_TEST_ORDER > 128
# define WINDOW_A 5
# elif EXHAUSTIVE_TEST_ORDER > 8
# define WINDOW_A 4
# else
# define WINDOW_A 2
# endif
#else
/* optimal for 128-bit and 256-bit exponents. */
# define WINDOW_A 5
/** Larger values for ECMULT_WINDOW_SIZE result in possibly better
* performance at the cost of an exponentially larger precomputed
* table. The exact table size is
* (1 << (WINDOW_G - 2)) * sizeof(secp256k1_ge_storage) bytes,
* where sizeof(secp256k1_ge_storage) is typically 64 bytes but can
* be larger due to platform-specific padding and alignment.
* Two tables of this size are used (due to the endomorphism
* optimization).
*/
#endif
#define WNAF_BITS 128
#define WNAF_SIZE_BITS(bits, w) CEIL_DIV(bits, w)
#define WNAF_SIZE(w) WNAF_SIZE_BITS(WNAF_BITS, w)
/* The number of objects allocated on the scratch space for ecmult_multi algorithms */
#define PIPPENGER_SCRATCH_OBJECTS 6
#define STRAUSS_SCRATCH_OBJECTS 5
#define PIPPENGER_MAX_BUCKET_WINDOW 12
/* Minimum number of points for which pippenger_wnaf is faster than strauss wnaf */
#define ECMULT_PIPPENGER_THRESHOLD 88
#define ECMULT_MAX_POINTS_PER_BATCH 5000000
/** Fill a table 'pre_a' with precomputed odd multiples of a.
* pre_a will contain [1*a,3*a,...,(2*n-1)*a], so it needs space for n group elements.
* zr needs space for n field elements.
*
* Although pre_a is an array of _ge rather than _gej, it actually represents elements
* in Jacobian coordinates with their z coordinates omitted. The omitted z-coordinates
* can be recovered using z and zr. Using the notation z(b) to represent the omitted
* z coordinate of b:
* - z(pre_a[n-1]) = 'z'
* - z(pre_a[i-1]) = z(pre_a[i]) / zr[i] for n > i > 0
*
* Lastly the zr[0] value, which isn't used above, is set so that:
* - a.z = z(pre_a[0]) / zr[0]
*/
static void secp256k1_ecmult_odd_multiples_table(size_t n, secp256k1_ge *pre_a, secp256k1_fe *zr, secp256k1_fe *z, const secp256k1_gej *a) {
secp256k1_gej d, ai;
secp256k1_ge d_ge;
size_t i;
VERIFY_CHECK(!secp256k1_gej_is_infinity(a));
secp256k1_gej_double_var(&d, a, NULL);
/*
* Perform the additions using an isomorphic curve Y^2 = X^3 + 7*C^6 where C := d.z.
* The isomorphism, phi, maps a secp256k1 point (x, y) to the point (x*C^2, y*C^3) on the other curve.
* In Jacobian coordinates phi maps (x, y, z) to (x*C^2, y*C^3, z) or, equivalently to (x, y, z/C).
*
* phi(x, y, z) = (x*C^2, y*C^3, z) = (x, y, z/C)
* d_ge := phi(d) = (d.x, d.y, 1)
* ai := phi(a) = (a.x*C^2, a.y*C^3, a.z)
*
* The group addition functions work correctly on these isomorphic curves.
* In particular phi(d) is easy to represent in affine coordinates under this isomorphism.
* This lets us use the faster secp256k1_gej_add_ge_var group addition function that we wouldn't be able to use otherwise.
*/
secp256k1_ge_set_xy(&d_ge, &d.x, &d.y);
secp256k1_ge_set_gej_zinv(&pre_a[0], a, &d.z);
secp256k1_gej_set_ge(&ai, &pre_a[0]);
ai.z = a->z;
/* pre_a[0] is the point (a.x*C^2, a.y*C^3, a.z*C) which is equivalent to a.
* Set zr[0] to C, which is the ratio between the omitted z(pre_a[0]) value and a.z.
*/
zr[0] = d.z;
for (i = 1; i < n; i++) {
secp256k1_gej_add_ge_var(&ai, &ai, &d_ge, &zr[i]);
secp256k1_ge_set_xy(&pre_a[i], &ai.x, &ai.y);
}
/* Multiply the last z-coordinate by C to undo the isomorphism.
* Since the z-coordinates of the pre_a values are implied by the zr array of z-coordinate ratios,
* undoing the isomorphism here undoes the isomorphism for all pre_a values.
*/
secp256k1_fe_mul(z, &ai.z, &d.z);
}
SECP256K1_INLINE static void secp256k1_ecmult_table_verify(int n, int w) {
(void)n;
(void)w;
VERIFY_CHECK(((n) & 1) == 1);
VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1));
VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1));
}
SECP256K1_INLINE static void secp256k1_ecmult_table_get_ge(secp256k1_ge *r, const secp256k1_ge *pre, int n, int w) {
secp256k1_ecmult_table_verify(n,w);
if (n > 0) {
*r = pre[(n-1)/2];
} else {
*r = pre[(-n-1)/2];
secp256k1_fe_negate(&(r->y), &(r->y), 1);
}
}
SECP256K1_INLINE static void secp256k1_ecmult_table_get_ge_lambda(secp256k1_ge *r, const secp256k1_ge *pre, const secp256k1_fe *x, int n, int w) {
secp256k1_ecmult_table_verify(n,w);
if (n > 0) {
secp256k1_ge_set_xy(r, &x[(n-1)/2], &pre[(n-1)/2].y);
} else {
secp256k1_ge_set_xy(r, &x[(-n-1)/2], &pre[(-n-1)/2].y);
secp256k1_fe_negate(&(r->y), &(r->y), 1);
}
}
SECP256K1_INLINE static void secp256k1_ecmult_table_get_ge_storage(secp256k1_ge *r, const secp256k1_ge_storage *pre, int n, int w) {
secp256k1_ecmult_table_verify(n,w);
if (n > 0) {
secp256k1_ge_from_storage(r, &pre[(n-1)/2]);
} else {
secp256k1_ge_from_storage(r, &pre[(-n-1)/2]);
secp256k1_fe_negate(&(r->y), &(r->y), 1);
}
}
/** Convert a number to WNAF notation. The number becomes represented by sum(2^i * wnaf[i], i=0..bits),
* with the following guarantees:
* - each wnaf[i] is either 0, or an odd integer between -(1<<(w-1) - 1) and (1<<(w-1) - 1)
* - two non-zero entries in wnaf are separated by at least w-1 zeroes.
* - the number of set values in wnaf is returned. This number is at most 256, and at most one more
* than the number of bits in the (absolute value) of the input.
*/
static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a, int w) {
secp256k1_scalar s;
int last_set_bit = -1;
int bit = 0;
int sign = 1;
int carry = 0;
VERIFY_CHECK(wnaf != NULL);
VERIFY_CHECK(0 <= len && len <= 256);
VERIFY_CHECK(a != NULL);
VERIFY_CHECK(2 <= w && w <= 31);
for (bit = 0; bit < len; bit++) {
wnaf[bit] = 0;
}
s = *a;
if (secp256k1_scalar_get_bits_limb32(&s, 255, 1)) {
secp256k1_scalar_negate(&s, &s);
sign = -1;
}
bit = 0;
while (bit < len) {
int now;
int word;
if (secp256k1_scalar_get_bits_limb32(&s, bit, 1) == (unsigned int)carry) {
bit++;
continue;
}
now = w;
if (now > len - bit) {
now = len - bit;
}
word = secp256k1_scalar_get_bits_var(&s, bit, now) + carry;
carry = (word >> (w-1)) & 1;
word -= carry << w;
wnaf[bit] = sign * word;
last_set_bit = bit;
bit += now;
}
#ifdef VERIFY
{
int verify_bit = bit;
VERIFY_CHECK(carry == 0);
while (verify_bit < 256) {
VERIFY_CHECK(secp256k1_scalar_get_bits_limb32(&s, verify_bit, 1) == 0);
verify_bit++;
}
}
#endif
return last_set_bit + 1;
}
/* Same as secp256k1_ecmult_wnaf, but stores to int8_t array. Requires w <= 8. */
static int secp256k1_ecmult_wnaf_small(int8_t *wnaf, int len, const secp256k1_scalar *a, int w) {
int wnaf_tmp[256];
int ret, i;
VERIFY_CHECK(2 <= w && w <= 8);
ret = secp256k1_ecmult_wnaf(wnaf_tmp, len, a, w);
for (i = 0; i < len; i++) {
wnaf[i] = (int8_t)wnaf_tmp[i];
}
return ret;
}
struct secp256k1_strauss_point_state {
int8_t wnaf_na_1[129];
int8_t wnaf_na_lam[129];
int bits_na_1;
int bits_na_lam;
};
struct secp256k1_strauss_state {
/* aux is used to hold z-ratios, and then used to hold pre_a[i].x * BETA values. */
secp256k1_fe* aux;
secp256k1_ge* pre_a;
struct secp256k1_strauss_point_state* ps;
};
static void secp256k1_ecmult_strauss_wnaf(const struct secp256k1_strauss_state *state, secp256k1_gej *r, size_t num, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) {
secp256k1_ge tmpa;
secp256k1_fe Z;
/* Split G factors. */
secp256k1_scalar ng_1, ng_128;
int wnaf_ng_1[129];
int bits_ng_1 = 0;
int wnaf_ng_128[129];
int bits_ng_128 = 0;
int i;
int bits = 0;
size_t np;
size_t no = 0;
secp256k1_fe_set_int(&Z, 1);
for (np = 0; np < num; ++np) {
secp256k1_gej tmp;
secp256k1_scalar na_1, na_lam;
if (secp256k1_scalar_is_zero(&na[np]) || secp256k1_gej_is_infinity(&a[np])) {
continue;
}
/* split na into na_1 and na_lam (where na = na_1 + na_lam*lambda, and na_1 and na_lam are ~128 bit) */
secp256k1_scalar_split_lambda(&na_1, &na_lam, &na[np]);
/* build wnaf representation for na_1 and na_lam. */
state->ps[no].bits_na_1 = secp256k1_ecmult_wnaf_small(state->ps[no].wnaf_na_1, 129, &na_1, WINDOW_A);
state->ps[no].bits_na_lam = secp256k1_ecmult_wnaf_small(state->ps[no].wnaf_na_lam, 129, &na_lam, WINDOW_A);
VERIFY_CHECK(state->ps[no].bits_na_1 <= 129);
VERIFY_CHECK(state->ps[no].bits_na_lam <= 129);
if (state->ps[no].bits_na_1 > bits) {
bits = state->ps[no].bits_na_1;
}
if (state->ps[no].bits_na_lam > bits) {
bits = state->ps[no].bits_na_lam;
}
/* Calculate odd multiples of a.
* All multiples are brought to the same Z 'denominator', which is stored
* in Z. Due to secp256k1' isomorphism we can do all operations pretending
* that the Z coordinate was 1, use affine addition formulae, and correct
* the Z coordinate of the result once at the end.
* The exception is the precomputed G table points, which are actually
* affine. Compared to the base used for other points, they have a Z ratio
* of 1/Z, so we can use secp256k1_gej_add_zinv_var, which uses the same
* isomorphism to efficiently add with a known Z inverse.
*/
tmp = a[np];
if (no) {
secp256k1_gej_rescale(&tmp, &Z);
}
secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->pre_a + no * ECMULT_TABLE_SIZE(WINDOW_A), state->aux + no * ECMULT_TABLE_SIZE(WINDOW_A), &Z, &tmp);
if (no) secp256k1_fe_mul(state->aux + no * ECMULT_TABLE_SIZE(WINDOW_A), state->aux + no * ECMULT_TABLE_SIZE(WINDOW_A), &(a[np].z));
++no;
}
/* Bring them to the same Z denominator. */
if (no) {
secp256k1_ge_table_set_globalz(ECMULT_TABLE_SIZE(WINDOW_A) * no, state->pre_a, state->aux);
}
for (np = 0; np < no; ++np) {
size_t j;
for (j = 0; j < ECMULT_TABLE_SIZE(WINDOW_A); j++) {
secp256k1_fe_mul(&state->aux[np * ECMULT_TABLE_SIZE(WINDOW_A) + j], &state->pre_a[np * ECMULT_TABLE_SIZE(WINDOW_A) + j].x, &secp256k1_const_beta);
}
}
if (ng) {
/* split ng into ng_1 and ng_128 (where gn = gn_1 + gn_128*2^128, and gn_1 and gn_128 are ~128 bit) */
secp256k1_scalar_split_128(&ng_1, &ng_128, ng);
/* Build wnaf representation for ng_1 and ng_128 */
bits_ng_1 = secp256k1_ecmult_wnaf(wnaf_ng_1, 129, &ng_1, WINDOW_G);
bits_ng_128 = secp256k1_ecmult_wnaf(wnaf_ng_128, 129, &ng_128, WINDOW_G);
if (bits_ng_1 > bits) {
bits = bits_ng_1;
}
if (bits_ng_128 > bits) {
bits = bits_ng_128;
}
}
secp256k1_gej_set_infinity(r);
for (i = bits - 1; i >= 0; i--) {
int n;
secp256k1_gej_double_var(r, r, NULL);
for (np = 0; np < no; ++np) {
if (i < state->ps[np].bits_na_1 && (n = state->ps[np].wnaf_na_1[i])) {
secp256k1_ecmult_table_get_ge(&tmpa, state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A);
secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
}
if (i < state->ps[np].bits_na_lam && (n = state->ps[np].wnaf_na_lam[i])) {
secp256k1_ecmult_table_get_ge_lambda(&tmpa, state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), state->aux + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A);
secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
}
}
if (i < bits_ng_1 && (n = wnaf_ng_1[i])) {
secp256k1_ecmult_table_get_ge_storage(&tmpa, secp256k1_pre_g, n, WINDOW_G);
secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z);
}
if (i < bits_ng_128 && (n = wnaf_ng_128[i])) {
secp256k1_ecmult_table_get_ge_storage(&tmpa, secp256k1_pre_g_128, n, WINDOW_G);
secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z);
}
}
if (!secp256k1_gej_is_infinity(r)) {
secp256k1_fe_mul(&r->z, &r->z, &Z);
}
}
static void secp256k1_ecmult(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) {
secp256k1_fe aux[ECMULT_TABLE_SIZE(WINDOW_A)];
secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)];
struct secp256k1_strauss_point_state ps[1];
struct secp256k1_strauss_state state;
state.aux = aux;
state.pre_a = pre_a;
state.ps = ps;
secp256k1_ecmult_strauss_wnaf(&state, r, 1, a, na, ng);
}
static size_t secp256k1_strauss_scratch_size(size_t n_points) {
static const size_t point_size = (sizeof(secp256k1_ge) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar);
return n_points*point_size;
}
static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) {
secp256k1_gej* points;
secp256k1_scalar* scalars;
struct secp256k1_strauss_state state;
size_t i;
const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch);
secp256k1_gej_set_infinity(r);
if (inp_g_sc == NULL && n_points == 0) {
return 1;
}
/* We allocate STRAUSS_SCRATCH_OBJECTS objects on the scratch space. If these
* allocations change, make sure to update the STRAUSS_SCRATCH_OBJECTS
* constant and strauss_scratch_size accordingly. */
points = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_gej));
scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_scalar));
state.aux = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe));
state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(struct secp256k1_strauss_point_state));
if (points == NULL || scalars == NULL || state.aux == NULL || state.pre_a == NULL || state.ps == NULL) {
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 0;
}
for (i = 0; i < n_points; i++) {
secp256k1_ge point;
if (!cb(&scalars[i], &point, i+cb_offset, cbdata)) {
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 0;
}
secp256k1_gej_set_ge(&points[i], &point);
}
secp256k1_ecmult_strauss_wnaf(&state, r, n_points, points, scalars, inp_g_sc);
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 1;
}
/* Wrapper for secp256k1_ecmult_multi_func interface */
static int secp256k1_ecmult_strauss_batch_single(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
return secp256k1_ecmult_strauss_batch(error_callback, scratch, r, inp_g_sc, cb, cbdata, n, 0);
}
static size_t secp256k1_strauss_max_points(const secp256k1_callback* error_callback, secp256k1_scratch *scratch) {
return secp256k1_scratch_max_allocation(error_callback, scratch, STRAUSS_SCRATCH_OBJECTS) / secp256k1_strauss_scratch_size(1);
}
/** Convert a number to WNAF notation.
* The number becomes represented by sum(2^{wi} * wnaf[i], i=0..WNAF_SIZE(w)+1) - return_val.
* It has the following guarantees:
* - each wnaf[i] is either 0 or an odd integer between -(1 << w) and (1 << w)
* - the number of words set is always WNAF_SIZE(w)
* - the returned skew is 0 or 1
*/
static int secp256k1_wnaf_fixed(int *wnaf, const secp256k1_scalar *s, int w) {
int skew = 0;
int pos;
int max_pos;
int last_w;
const secp256k1_scalar *work = s;
if (secp256k1_scalar_is_zero(s)) {
for (pos = 0; pos < WNAF_SIZE(w); pos++) {
wnaf[pos] = 0;
}
return 0;
}
if (secp256k1_scalar_is_even(s)) {
skew = 1;
}
wnaf[0] = secp256k1_scalar_get_bits_var(work, 0, w) + skew;
/* Compute last window size. Relevant when window size doesn't divide the
* number of bits in the scalar */
last_w = WNAF_BITS - (WNAF_SIZE(w) - 1) * w;
/* Store the position of the first nonzero word in max_pos to allow
* skipping leading zeros when calculating the wnaf. */
for (pos = WNAF_SIZE(w) - 1; pos > 0; pos--) {
int val = secp256k1_scalar_get_bits_var(work, pos * w, pos == WNAF_SIZE(w)-1 ? last_w : w);
if(val != 0) {
break;
}
wnaf[pos] = 0;
}
max_pos = pos;
pos = 1;
while (pos <= max_pos) {
int val = secp256k1_scalar_get_bits_var(work, pos * w, pos == WNAF_SIZE(w)-1 ? last_w : w);
if ((val & 1) == 0) {
wnaf[pos - 1] -= (1 << w);
wnaf[pos] = (val + 1);
} else {
wnaf[pos] = val;
}
/* Set a coefficient to zero if it is 1 or -1 and the proceeding digit
* is strictly negative or strictly positive respectively. Only change
* coefficients at previous positions because above code assumes that
* wnaf[pos - 1] is odd.
*/
if (pos >= 2 && ((wnaf[pos - 1] == 1 && wnaf[pos - 2] < 0) || (wnaf[pos - 1] == -1 && wnaf[pos - 2] > 0))) {
if (wnaf[pos - 1] == 1) {
wnaf[pos - 2] += 1 << w;
} else {
wnaf[pos - 2] -= 1 << w;
}
wnaf[pos - 1] = 0;
}
++pos;
}
return skew;
}
struct secp256k1_pippenger_point_state {
int skew_na;
size_t input_pos;
};
struct secp256k1_pippenger_state {
int *wnaf_na;
struct secp256k1_pippenger_point_state* ps;
};
/*
* pippenger_wnaf computes the result of a multi-point multiplication as
* follows: The scalars are brought into wnaf with n_wnaf elements each. Then
* for every i < n_wnaf, first each point is added to a "bucket" corresponding
* to the point's wnaf[i]. Second, the buckets are added together such that
* r += 1*bucket[0] + 3*bucket[1] + 5*bucket[2] + ...
*/
static int secp256k1_ecmult_pippenger_wnaf(secp256k1_gej *buckets, int bucket_window, struct secp256k1_pippenger_state *state, secp256k1_gej *r, const secp256k1_scalar *sc, const secp256k1_ge *pt, size_t num) {
size_t n_wnaf = WNAF_SIZE(bucket_window+1);
size_t np;
size_t no = 0;
int i;
for (np = 0; np < num; ++np) {
if (secp256k1_scalar_is_zero(&sc[np]) || secp256k1_ge_is_infinity(&pt[np])) {
continue;
}
state->ps[no].input_pos = np;
state->ps[no].skew_na = secp256k1_wnaf_fixed(&state->wnaf_na[no*n_wnaf], &sc[np], bucket_window+1);
no++;
}
secp256k1_gej_set_infinity(r);
if (no == 0) {
return 1;
}
for (i = n_wnaf - 1; i >= 0; i--) {
secp256k1_gej running_sum;
int j;
size_t buc;
for (buc = 0; buc < ECMULT_TABLE_SIZE(bucket_window+2); buc++) {
secp256k1_gej_set_infinity(&buckets[buc]);
}
for (np = 0; np < no; ++np) {
int n = state->wnaf_na[np*n_wnaf + i];
struct secp256k1_pippenger_point_state point_state = state->ps[np];
secp256k1_ge tmp;
if (i == 0) {
/* correct for wnaf skew */
int skew = point_state.skew_na;
if (skew) {
secp256k1_ge_neg(&tmp, &pt[point_state.input_pos]);
secp256k1_gej_add_ge_var(&buckets[0], &buckets[0], &tmp, NULL);
}
}
if (n > 0) {
buc = (n - 1)/2;
secp256k1_gej_add_ge_var(&buckets[buc], &buckets[buc], &pt[point_state.input_pos], NULL);
} else if (n < 0) {
buc = -(n + 1)/2;
secp256k1_ge_neg(&tmp, &pt[point_state.input_pos]);
secp256k1_gej_add_ge_var(&buckets[buc], &buckets[buc], &tmp, NULL);
}
}
for (j = 0; j < bucket_window; j++) {
secp256k1_gej_double_var(r, r, NULL);
}
secp256k1_gej_set_infinity(&running_sum);
/* Accumulate the sum: bucket[0] + 3*bucket[1] + 5*bucket[2] + 7*bucket[3] + ...
* = bucket[0] + bucket[1] + bucket[2] + bucket[3] + ...
* + 2 * (bucket[1] + 2*bucket[2] + 3*bucket[3] + ...)
* using an intermediate running sum:
* running_sum = bucket[0] + bucket[1] + bucket[2] + ...
*
* The doubling is done implicitly by deferring the final window doubling (of 'r').
*/
for (buc = ECMULT_TABLE_SIZE(bucket_window+2) - 1; buc > 0; buc--) {
secp256k1_gej_add_var(&running_sum, &running_sum, &buckets[buc], NULL);
secp256k1_gej_add_var(r, r, &running_sum, NULL);
}
secp256k1_gej_add_var(&running_sum, &running_sum, &buckets[0], NULL);
secp256k1_gej_double_var(r, r, NULL);
secp256k1_gej_add_var(r, r, &running_sum, NULL);
}
return 1;
}
/**
* Returns optimal bucket_window (number of bits of a scalar represented by a
* set of buckets) for a given number of points.
*/
static int secp256k1_pippenger_bucket_window(size_t n) {
if (n <= 1) {
return 1;
} else if (n <= 4) {
return 2;
} else if (n <= 20) {
return 3;
} else if (n <= 57) {
return 4;
} else if (n <= 136) {
return 5;
} else if (n <= 235) {
return 6;
} else if (n <= 1260) {
return 7;
} else if (n <= 4420) {
return 9;
} else if (n <= 7880) {
return 10;
} else if (n <= 16050) {
return 11;
} else {
return PIPPENGER_MAX_BUCKET_WINDOW;
}
}
/**
* Returns the maximum optimal number of points for a bucket_window.
*/
static size_t secp256k1_pippenger_bucket_window_inv(int bucket_window) {
switch(bucket_window) {
case 1: return 1;
case 2: return 4;
case 3: return 20;
case 4: return 57;
case 5: return 136;
case 6: return 235;
case 7: return 1260;
case 8: return 1260;
case 9: return 4420;
case 10: return 7880;
case 11: return 16050;
case PIPPENGER_MAX_BUCKET_WINDOW: return SIZE_MAX;
}
return 0;
}
SECP256K1_INLINE static void secp256k1_ecmult_endo_split(secp256k1_scalar *s1, secp256k1_scalar *s2, secp256k1_ge *p1, secp256k1_ge *p2) {
secp256k1_scalar tmp = *s1;
secp256k1_scalar_split_lambda(s1, s2, &tmp);
secp256k1_ge_mul_lambda(p2, p1);
if (secp256k1_scalar_is_high(s1)) {
secp256k1_scalar_negate(s1, s1);
secp256k1_ge_neg(p1, p1);
}
if (secp256k1_scalar_is_high(s2)) {
secp256k1_scalar_negate(s2, s2);
secp256k1_ge_neg(p2, p2);
}
}
/**
* Returns the scratch size required for a given number of points (excluding
* base point G) without considering alignment.
*/
static size_t secp256k1_pippenger_scratch_size(size_t n_points, int bucket_window) {
size_t entries = 2*n_points + 2;
size_t entry_size = sizeof(secp256k1_ge) + sizeof(secp256k1_scalar) + sizeof(struct secp256k1_pippenger_point_state) + (WNAF_SIZE(bucket_window+1)+1)*sizeof(int);
return (sizeof(secp256k1_gej) << bucket_window) + sizeof(struct secp256k1_pippenger_state) + entries * entry_size;
}
static int secp256k1_ecmult_pippenger_batch(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) {
const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch);
/* Use 2(n+1) with the endomorphism, when calculating batch
* sizes. The reason for +1 is that we add the G scalar to the list of
* other scalars. */
size_t entries = 2*n_points + 2;
secp256k1_ge *points;
secp256k1_scalar *scalars;
secp256k1_gej *buckets;
struct secp256k1_pippenger_state *state_space;
size_t idx = 0;
size_t point_idx = 0;
int bucket_window;
secp256k1_gej_set_infinity(r);
if (inp_g_sc == NULL && n_points == 0) {
return 1;
}
bucket_window = secp256k1_pippenger_bucket_window(n_points);
/* We allocate PIPPENGER_SCRATCH_OBJECTS objects on the scratch space. If
* these allocations change, make sure to update the
* PIPPENGER_SCRATCH_OBJECTS constant and pippenger_scratch_size
* accordingly. */
points = (secp256k1_ge *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*points));
scalars = (secp256k1_scalar *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*scalars));
state_space = (struct secp256k1_pippenger_state *) secp256k1_scratch_alloc(error_callback, scratch, sizeof(*state_space));
if (points == NULL || scalars == NULL || state_space == NULL) {
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 0;
}
state_space->ps = (struct secp256k1_pippenger_point_state *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*state_space->ps));
state_space->wnaf_na = (int *) secp256k1_scratch_alloc(error_callback, scratch, entries*(WNAF_SIZE(bucket_window+1)) * sizeof(int));
buckets = (secp256k1_gej *) secp256k1_scratch_alloc(error_callback, scratch, ((size_t)1 << bucket_window) * sizeof(*buckets));
if (state_space->ps == NULL || state_space->wnaf_na == NULL || buckets == NULL) {
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 0;
}
if (inp_g_sc != NULL) {
scalars[0] = *inp_g_sc;
points[0] = secp256k1_ge_const_g;
idx++;
secp256k1_ecmult_endo_split(&scalars[0], &scalars[1], &points[0], &points[1]);
idx++;
}
while (point_idx < n_points) {
if (!cb(&scalars[idx], &points[idx], point_idx + cb_offset, cbdata)) {
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 0;
}
idx++;
secp256k1_ecmult_endo_split(&scalars[idx - 1], &scalars[idx], &points[idx - 1], &points[idx]);
idx++;
point_idx++;
}
secp256k1_ecmult_pippenger_wnaf(buckets, bucket_window, state_space, r, scalars, points, idx);
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 1;
}
/* Wrapper for secp256k1_ecmult_multi_func interface */
static int secp256k1_ecmult_pippenger_batch_single(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
return secp256k1_ecmult_pippenger_batch(error_callback, scratch, r, inp_g_sc, cb, cbdata, n, 0);
}
/**
* Returns the maximum number of points in addition to G that can be used with
* a given scratch space. The function ensures that fewer points may also be
* used.
*/
static size_t secp256k1_pippenger_max_points(const secp256k1_callback* error_callback, secp256k1_scratch *scratch) {
size_t max_alloc = secp256k1_scratch_max_allocation(error_callback, scratch, PIPPENGER_SCRATCH_OBJECTS);
int bucket_window;
size_t res = 0;
for (bucket_window = 1; bucket_window <= PIPPENGER_MAX_BUCKET_WINDOW; bucket_window++) {
size_t n_points;
size_t max_points = secp256k1_pippenger_bucket_window_inv(bucket_window);
size_t space_for_points;
size_t space_overhead;
size_t entry_size = sizeof(secp256k1_ge) + sizeof(secp256k1_scalar) + sizeof(struct secp256k1_pippenger_point_state) + (WNAF_SIZE(bucket_window+1)+1)*sizeof(int);
entry_size = 2*entry_size;
space_overhead = (sizeof(secp256k1_gej) << bucket_window) + entry_size + sizeof(struct secp256k1_pippenger_state);
if (space_overhead > max_alloc) {
break;
}
space_for_points = max_alloc - space_overhead;
n_points = space_for_points/entry_size;
n_points = n_points > max_points ? max_points : n_points;
if (n_points > res) {
res = n_points;
}
if (n_points < max_points) {
/* A larger bucket_window may support even more points. But if we
* would choose that then the caller couldn't safely use any number
* smaller than what this function returns */
break;
}
}
return res;
}
/* Computes ecmult_multi by simply multiplying and adding each point. Does not
* require a scratch space */
static int secp256k1_ecmult_multi_simple_var(secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points) {
size_t point_idx;
secp256k1_gej tmpj;
secp256k1_gej_set_infinity(r);
secp256k1_gej_set_infinity(&tmpj);
/* r = inp_g_sc*G */
secp256k1_ecmult(r, &tmpj, &secp256k1_scalar_zero, inp_g_sc);
for (point_idx = 0; point_idx < n_points; point_idx++) {
secp256k1_ge point;
secp256k1_gej pointj;
secp256k1_scalar scalar;
if (!cb(&scalar, &point, point_idx, cbdata)) {
return 0;
}
/* r += scalar*point */
secp256k1_gej_set_ge(&pointj, &point);
secp256k1_ecmult(&tmpj, &pointj, &scalar, NULL);
secp256k1_gej_add_var(r, r, &tmpj, NULL);
}
return 1;
}
/* Compute the number of batches and the batch size given the maximum batch size and the
* total number of points */
static int secp256k1_ecmult_multi_batch_size_helper(size_t *n_batches, size_t *n_batch_points, size_t max_n_batch_points, size_t n) {
if (max_n_batch_points == 0) {
return 0;
}
if (max_n_batch_points > ECMULT_MAX_POINTS_PER_BATCH) {
max_n_batch_points = ECMULT_MAX_POINTS_PER_BATCH;
}
if (n == 0) {
*n_batches = 0;
*n_batch_points = 0;
return 1;
}
/* Compute ceil(n/max_n_batch_points) and ceil(n/n_batches) */
*n_batches = CEIL_DIV(n, max_n_batch_points);
*n_batch_points = CEIL_DIV(n, *n_batches);
return 1;
}
typedef int (*secp256k1_ecmult_multi_func)(const secp256k1_callback* error_callback, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t);
static int secp256k1_ecmult_multi_var(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
size_t i;
int (*f)(const secp256k1_callback* error_callback, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t, size_t);
size_t n_batches;
size_t n_batch_points;
secp256k1_gej_set_infinity(r);
if (inp_g_sc == NULL && n == 0) {
return 1;
} else if (n == 0) {
secp256k1_ecmult(r, r, &secp256k1_scalar_zero, inp_g_sc);
return 1;
}
if (scratch == NULL) {
return secp256k1_ecmult_multi_simple_var(r, inp_g_sc, cb, cbdata, n);
}
/* Compute the batch sizes for Pippenger's algorithm given a scratch space. If it's greater than
* a threshold use Pippenger's algorithm. Otherwise use Strauss' algorithm.
* As a first step check if there's enough space for Pippenger's algo (which requires less space
* than Strauss' algo) and if not, use the simple algorithm. */
if (!secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, secp256k1_pippenger_max_points(error_callback, scratch), n)) {
return secp256k1_ecmult_multi_simple_var(r, inp_g_sc, cb, cbdata, n);
}
if (n_batch_points >= ECMULT_PIPPENGER_THRESHOLD) {
f = secp256k1_ecmult_pippenger_batch;
} else {
if (!secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, secp256k1_strauss_max_points(error_callback, scratch), n)) {
return secp256k1_ecmult_multi_simple_var(r, inp_g_sc, cb, cbdata, n);
}
f = secp256k1_ecmult_strauss_batch;
}
for(i = 0; i < n_batches; i++) {
size_t nbp = n < n_batch_points ? n : n_batch_points;
size_t offset = n_batch_points*i;
secp256k1_gej tmp;
if (!f(error_callback, scratch, &tmp, i == 0 ? inp_g_sc : NULL, cb, cbdata, nbp, offset)) {
return 0;
}
secp256k1_gej_add_var(r, r, &tmp, NULL);
n -= nbp;
}
return 1;
}
#endif /* SECP256K1_ECMULT_IMPL_H */

View File

@@ -0,0 +1,351 @@
/***********************************************************************
* Copyright (c) 2013, 2014 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_FIELD_H
#define SECP256K1_FIELD_H
#include "util.h"
/* This file defines the generic interface for working with secp256k1_fe
* objects, which represent field elements (integers modulo 2^256 - 2^32 - 977).
*
* The actual definition of the secp256k1_fe type depends on the chosen field
* implementation; see the field_5x52.h and field_10x26.h files for details.
*
* All secp256k1_fe objects have implicit properties that determine what
* operations are permitted on it. These are purely a function of what
* secp256k1_fe_ operations are applied on it, generally (implicitly) fixed at
* compile time, and do not depend on the chosen field implementation. Despite
* that, what these properties actually entail for the field representation
* values depends on the chosen field implementation. These properties are:
* - magnitude: an integer in [0,32]
* - normalized: 0 or 1; normalized=1 implies magnitude <= 1.
*
* In VERIFY mode, they are materialized explicitly as fields in the struct,
* allowing run-time verification of these properties. In that case, the field
* implementation also provides a secp256k1_fe_verify routine to verify that
* these fields match the run-time value and perform internal consistency
* checks. */
#ifdef VERIFY
# define SECP256K1_FE_VERIFY_FIELDS \
int magnitude; \
int normalized;
#else
# define SECP256K1_FE_VERIFY_FIELDS
#endif
#if defined(SECP256K1_WIDEMUL_INT128)
#include "field_5x52.h"
#elif defined(SECP256K1_WIDEMUL_INT64)
#include "field_10x26.h"
#else
#error "Please select wide multiplication implementation"
#endif
#ifdef VERIFY
/* Magnitude and normalized value for constants. */
#define SECP256K1_FE_VERIFY_CONST(d7, d6, d5, d4, d3, d2, d1, d0) \
/* Magnitude is 0 for constant 0; 1 otherwise. */ \
, (((d7) | (d6) | (d5) | (d4) | (d3) | (d2) | (d1) | (d0)) != 0) \
/* Normalized is 1 unless sum(d_i<<(32*i) for i=0..7) exceeds field modulus. */ \
, (!(((d7) & (d6) & (d5) & (d4) & (d3) & (d2)) == 0xfffffffful && ((d1) == 0xfffffffful || ((d1) == 0xfffffffe && (d0 >= 0xfffffc2f)))))
#else
#define SECP256K1_FE_VERIFY_CONST(d7, d6, d5, d4, d3, d2, d1, d0)
#endif
/** This expands to an initializer for a secp256k1_fe valued sum((i*32) * d_i, i=0..7) mod p.
*
* It has magnitude 1, unless d_i are all 0, in which case the magnitude is 0.
* It is normalized, unless sum(2^(i*32) * d_i, i=0..7) >= p.
*
* SECP256K1_FE_CONST_INNER is provided by the implementation.
*/
#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0)) SECP256K1_FE_VERIFY_CONST((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0)) }
static const secp256k1_fe secp256k1_fe_one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1);
static const secp256k1_fe secp256k1_const_beta = SECP256K1_FE_CONST(
0x7ae96a2bul, 0x657c0710ul, 0x6e64479eul, 0xac3434e9ul,
0x9cf04975ul, 0x12f58995ul, 0xc1396c28ul, 0x719501eeul
);
#ifndef VERIFY
/* In non-VERIFY mode, we #define the fe operations to be identical to their
* internal field implementation, to avoid the potential overhead of a
* function call (even though presumably inlinable). */
# define secp256k1_fe_normalize secp256k1_fe_impl_normalize
# define secp256k1_fe_normalize_weak secp256k1_fe_impl_normalize_weak
# define secp256k1_fe_normalize_var secp256k1_fe_impl_normalize_var
# define secp256k1_fe_normalizes_to_zero secp256k1_fe_impl_normalizes_to_zero
# define secp256k1_fe_normalizes_to_zero_var secp256k1_fe_impl_normalizes_to_zero_var
# define secp256k1_fe_set_int secp256k1_fe_impl_set_int
# define secp256k1_fe_is_zero secp256k1_fe_impl_is_zero
# define secp256k1_fe_is_odd secp256k1_fe_impl_is_odd
# define secp256k1_fe_cmp_var secp256k1_fe_impl_cmp_var
# define secp256k1_fe_set_b32_mod secp256k1_fe_impl_set_b32_mod
# define secp256k1_fe_set_b32_limit secp256k1_fe_impl_set_b32_limit
# define secp256k1_fe_get_b32 secp256k1_fe_impl_get_b32
# define secp256k1_fe_negate_unchecked secp256k1_fe_impl_negate_unchecked
# define secp256k1_fe_mul_int_unchecked secp256k1_fe_impl_mul_int_unchecked
# define secp256k1_fe_add secp256k1_fe_impl_add
# define secp256k1_fe_mul secp256k1_fe_impl_mul
# define secp256k1_fe_sqr secp256k1_fe_impl_sqr
# define secp256k1_fe_cmov secp256k1_fe_impl_cmov
# define secp256k1_fe_to_storage secp256k1_fe_impl_to_storage
# define secp256k1_fe_from_storage secp256k1_fe_impl_from_storage
# define secp256k1_fe_inv secp256k1_fe_impl_inv
# define secp256k1_fe_inv_var secp256k1_fe_impl_inv_var
# define secp256k1_fe_get_bounds secp256k1_fe_impl_get_bounds
# define secp256k1_fe_half secp256k1_fe_impl_half
# define secp256k1_fe_add_int secp256k1_fe_impl_add_int
# define secp256k1_fe_is_square_var secp256k1_fe_impl_is_square_var
#endif /* !defined(VERIFY) */
/** Normalize a field element.
*
* On input, r must be a valid field element.
* On output, r represents the same value but has normalized=1 and magnitude=1.
*/
static void secp256k1_fe_normalize(secp256k1_fe *r);
/** Give a field element magnitude 1.
*
* On input, r must be a valid field element.
* On output, r represents the same value but has magnitude=1. Normalized is unchanged.
*/
static void secp256k1_fe_normalize_weak(secp256k1_fe *r);
/** Normalize a field element, without constant-time guarantee.
*
* Identical in behavior to secp256k1_fe_normalize, but not constant time in r.
*/
static void secp256k1_fe_normalize_var(secp256k1_fe *r);
/** Determine whether r represents field element 0.
*
* On input, r must be a valid field element.
* Returns whether r = 0 (mod p).
*/
static int secp256k1_fe_normalizes_to_zero(const secp256k1_fe *r);
/** Determine whether r represents field element 0, without constant-time guarantee.
*
* Identical in behavior to secp256k1_normalizes_to_zero, but not constant time in r.
*/
static int secp256k1_fe_normalizes_to_zero_var(const secp256k1_fe *r);
/** Set a field element to an integer in range [0,0x7FFF].
*
* On input, r does not need to be initialized, a must be in [0,0x7FFF].
* On output, r represents value a, is normalized and has magnitude (a!=0).
*/
static void secp256k1_fe_set_int(secp256k1_fe *r, int a);
/** Clear a field element to prevent leaking sensitive information. */
static void secp256k1_fe_clear(secp256k1_fe *a);
/** Determine whether a represents field element 0.
*
* On input, a must be a valid normalized field element.
* Returns whether a = 0 (mod p).
*
* This behaves identical to secp256k1_normalizes_to_zero{,_var}, but requires
* normalized input (and is much faster).
*/
static int secp256k1_fe_is_zero(const secp256k1_fe *a);
/** Determine whether a (mod p) is odd.
*
* On input, a must be a valid normalized field element.
* Returns (int(a) mod p) & 1.
*/
static int secp256k1_fe_is_odd(const secp256k1_fe *a);
/** Determine whether two field elements are equal.
*
* On input, a and b must be valid field elements with magnitudes not exceeding
* 1 and 31, respectively.
* Returns a = b (mod p).
*/
static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b);
/** Compare the values represented by 2 field elements, without constant-time guarantee.
*
* On input, a and b must be valid normalized field elements.
* Returns 1 if a > b, -1 if a < b, and 0 if a = b (comparisons are done as integers
* in range 0..p-1).
*/
static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b);
/** Set a field element equal to the element represented by a provided 32-byte big endian value
* interpreted modulo p.
*
* On input, r does not need to be initialized. a must be a pointer to an initialized 32-byte array.
* On output, r = a (mod p). It will have magnitude 1, and not be normalized.
*/
static void secp256k1_fe_set_b32_mod(secp256k1_fe *r, const unsigned char *a);
/** Set a field element equal to a provided 32-byte big endian value, checking for overflow.
*
* On input, r does not need to be initialized. a must be a pointer to an initialized 32-byte array.
* On output, r = a if (a < p), it will be normalized with magnitude 1, and 1 is returned.
* If a >= p, 0 is returned, and r will be made invalid (and must not be used without overwriting).
*/
static int secp256k1_fe_set_b32_limit(secp256k1_fe *r, const unsigned char *a);
/** Convert a field element to 32-byte big endian byte array.
* On input, a must be a valid normalized field element, and r a pointer to a 32-byte array.
* On output, r = a (mod p).
*/
static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a);
/** Negate a field element.
*
* On input, r does not need to be initialized. a must be a valid field element with
* magnitude not exceeding m. m must be an integer constant expression in [0,31].
* Performs {r = -a}.
* On output, r will not be normalized, and will have magnitude m+1.
*/
#define secp256k1_fe_negate(r, a, m) ASSERT_INT_CONST_AND_DO(m, secp256k1_fe_negate_unchecked(r, a, m))
/** Like secp256k1_fe_negate_unchecked but m is not checked to be an integer constant expression.
*
* Should not be called directly outside of tests.
*/
static void secp256k1_fe_negate_unchecked(secp256k1_fe *r, const secp256k1_fe *a, int m);
/** Add a small integer to a field element.
*
* Performs {r += a}. The magnitude of r increases by 1, and normalized is cleared.
* a must be in range [0,0x7FFF].
*/
static void secp256k1_fe_add_int(secp256k1_fe *r, int a);
/** Multiply a field element with a small integer.
*
* On input, r must be a valid field element. a must be an integer constant expression in [0,32].
* The magnitude of r times a must not exceed 32.
* Performs {r *= a}.
* On output, r's magnitude is multiplied by a, and r will not be normalized.
*/
#define secp256k1_fe_mul_int(r, a) ASSERT_INT_CONST_AND_DO(a, secp256k1_fe_mul_int_unchecked(r, a))
/** Like secp256k1_fe_mul_int but a is not checked to be an integer constant expression.
*
* Should not be called directly outside of tests.
*/
static void secp256k1_fe_mul_int_unchecked(secp256k1_fe *r, int a);
/** Increment a field element by another.
*
* On input, r and a must be valid field elements, not necessarily normalized.
* The sum of their magnitudes must not exceed 32.
* Performs {r += a}.
* On output, r will not be normalized, and will have magnitude incremented by a's.
*/
static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a);
/** Multiply two field elements.
*
* On input, a and b must be valid field elements; r does not need to be initialized.
* r and a may point to the same object, but neither may point to the object pointed
* to by b. The magnitudes of a and b must not exceed 8.
* Performs {r = a * b}
* On output, r will have magnitude 1, but won't be normalized.
*/
static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b);
/** Square a field element.
*
* On input, a must be a valid field element; r does not need to be initialized. The magnitude
* of a must not exceed 8.
* Performs {r = a**2}
* On output, r will have magnitude 1, but won't be normalized.
*/
static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a);
/** Compute a square root of a field element.
*
* On input, a must be a valid field element with magnitude<=8; r need not be initialized.
* If sqrt(a) exists, performs {r = sqrt(a)} and returns 1.
* Otherwise, sqrt(-a) exists. The function performs {r = sqrt(-a)} and returns 0.
* The resulting value represented by r will be a square itself.
* Variables r and a must not point to the same object.
* On output, r will have magnitude 1 but will not be normalized.
*/
static int secp256k1_fe_sqrt(secp256k1_fe * SECP256K1_RESTRICT r, const secp256k1_fe * SECP256K1_RESTRICT a);
/** Compute the modular inverse of a field element.
*
* On input, a must be a valid field element; r need not be initialized.
* Performs {r = a**(p-2)} (which maps 0 to 0, and every other element to its
* inverse).
* On output, r will have magnitude (a.magnitude != 0) and be normalized.
*/
static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a);
/** Compute the modular inverse of a field element, without constant-time guarantee.
*
* Behaves identically to secp256k1_fe_inv, but is not constant-time in a.
*/
static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a);
/** Convert a field element to secp256k1_fe_storage.
*
* On input, a must be a valid normalized field element.
* Performs {r = a}.
*/
static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a);
/** Convert a field element back from secp256k1_fe_storage.
*
* On input, r need not be initialized.
* Performs {r = a}.
* On output, r will be normalized and will have magnitude 1.
*/
static void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a);
/** If flag is 1, set *r equal to *a; if flag is 0, leave it. Constant-time.
* Both *r and *a must be initialized. Flag must be 0 or 1. */
static void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag);
/** Conditionally move a field element in constant time.
*
* On input, both r and a must be valid field elements. Flag must be 0 or 1.
* Performs {r = flag ? a : r}.
*
* On output, r's magnitude will be the maximum of both input magnitudes.
* It will be normalized if and only if both inputs were normalized.
*/
static void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag);
/** Halve the value of a field element modulo the field prime in constant-time.
*
* On input, r must be a valid field element.
* On output, r will be normalized and have magnitude floor(m/2) + 1 where m is
* the magnitude of r on input.
*/
static void secp256k1_fe_half(secp256k1_fe *r);
/** Sets r to a field element with magnitude m, normalized if (and only if) m==0.
* The value is chosen so that it is likely to trigger edge cases related to
* internal overflows. */
static void secp256k1_fe_get_bounds(secp256k1_fe *r, int m);
/** Determine whether a is a square (modulo p).
*
* On input, a must be a valid field element.
*/
static int secp256k1_fe_is_square_var(const secp256k1_fe *a);
/** Check invariants on a field element (no-op unless VERIFY is enabled). */
static void secp256k1_fe_verify(const secp256k1_fe *a);
#define SECP256K1_FE_VERIFY(a) secp256k1_fe_verify(a)
/** Check that magnitude of a is at most m (no-op unless VERIFY is enabled). */
static void secp256k1_fe_verify_magnitude(const secp256k1_fe *a, int m);
#define SECP256K1_FE_VERIFY_MAGNITUDE(a, m) secp256k1_fe_verify_magnitude(a, m)
#endif /* SECP256K1_FIELD_H */

View File

@@ -0,0 +1,57 @@
/***********************************************************************
* Copyright (c) 2013, 2014 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_FIELD_REPR_H
#define SECP256K1_FIELD_REPR_H
#include <stdint.h>
/** This field implementation represents the value as 10 uint32_t limbs in base
* 2^26. */
typedef struct {
/* A field element f represents the sum(i=0..9, f.n[i] << (i*26)) mod p,
* where p is the field modulus, 2^256 - 2^32 - 977.
*
* The individual limbs f.n[i] can exceed 2^26; the field's magnitude roughly
* corresponds to how much excess is allowed. The value
* sum(i=0..9, f.n[i] << (i*26)) may exceed p, unless the field element is
* normalized. */
uint32_t n[10];
/*
* Magnitude m requires:
* n[i] <= 2 * m * (2^26 - 1) for i=0..8
* n[9] <= 2 * m * (2^22 - 1)
*
* Normalized requires:
* n[i] <= (2^26 - 1) for i=0..8
* sum(i=0..9, n[i] << (i*26)) < p
* (together these imply n[9] <= 2^22 - 1)
*/
SECP256K1_FE_VERIFY_FIELDS
} secp256k1_fe;
/* Unpacks a constant into a overlapping multi-limbed FE element. */
#define SECP256K1_FE_CONST_INNER(d7, d6, d5, d4, d3, d2, d1, d0) { \
(d0) & 0x3FFFFFFUL, \
(((uint32_t)d0) >> 26) | (((uint32_t)(d1) & 0xFFFFFUL) << 6), \
(((uint32_t)d1) >> 20) | (((uint32_t)(d2) & 0x3FFFUL) << 12), \
(((uint32_t)d2) >> 14) | (((uint32_t)(d3) & 0xFFUL) << 18), \
(((uint32_t)d3) >> 8) | (((uint32_t)(d4) & 0x3UL) << 24), \
(((uint32_t)d4) >> 2) & 0x3FFFFFFUL, \
(((uint32_t)d4) >> 28) | (((uint32_t)(d5) & 0x3FFFFFUL) << 4), \
(((uint32_t)d5) >> 22) | (((uint32_t)(d6) & 0xFFFFUL) << 10), \
(((uint32_t)d6) >> 16) | (((uint32_t)(d7) & 0x3FFUL) << 16), \
(((uint32_t)d7) >> 10) \
}
typedef struct {
uint32_t n[8];
} secp256k1_fe_storage;
#define SECP256K1_FE_STORAGE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{ (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }}
#define SECP256K1_FE_STORAGE_CONST_GET(d) d.n[7], d.n[6], d.n[5], d.n[4],d.n[3], d.n[2], d.n[1], d.n[0]
#endif /* SECP256K1_FIELD_REPR_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,62 @@
/***********************************************************************
* Copyright (c) 2013, 2014 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_FIELD_REPR_H
#define SECP256K1_FIELD_REPR_H
#include <stdint.h>
/** This field implementation represents the value as 5 uint64_t limbs in base
* 2^52. */
typedef struct {
/* A field element f represents the sum(i=0..4, f.n[i] << (i*52)) mod p,
* where p is the field modulus, 2^256 - 2^32 - 977.
*
* The individual limbs f.n[i] can exceed 2^52; the field's magnitude roughly
* corresponds to how much excess is allowed. The value
* sum(i=0..4, f.n[i] << (i*52)) may exceed p, unless the field element is
* normalized. */
uint64_t n[5];
/*
* Magnitude m requires:
* n[i] <= 2 * m * (2^52 - 1) for i=0..3
* n[4] <= 2 * m * (2^48 - 1)
*
* Normalized requires:
* n[i] <= (2^52 - 1) for i=0..3
* sum(i=0..4, n[i] << (i*52)) < p
* (together these imply n[4] <= 2^48 - 1)
*/
SECP256K1_FE_VERIFY_FIELDS
} secp256k1_fe;
/* Unpacks a constant into a overlapping multi-limbed FE element. */
#define SECP256K1_FE_CONST_INNER(d7, d6, d5, d4, d3, d2, d1, d0) { \
(d0) | (((uint64_t)(d1) & 0xFFFFFUL) << 32), \
((uint64_t)(d1) >> 20) | (((uint64_t)(d2)) << 12) | (((uint64_t)(d3) & 0xFFUL) << 44), \
((uint64_t)(d3) >> 8) | (((uint64_t)(d4) & 0xFFFFFFFUL) << 24), \
((uint64_t)(d4) >> 28) | (((uint64_t)(d5)) << 4) | (((uint64_t)(d6) & 0xFFFFUL) << 36), \
((uint64_t)(d6) >> 16) | (((uint64_t)(d7)) << 16) \
}
typedef struct {
uint64_t n[4];
} secp256k1_fe_storage;
#define SECP256K1_FE_STORAGE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{ \
(d0) | (((uint64_t)(d1)) << 32), \
(d2) | (((uint64_t)(d3)) << 32), \
(d4) | (((uint64_t)(d5)) << 32), \
(d6) | (((uint64_t)(d7)) << 32) \
}}
#define SECP256K1_FE_STORAGE_CONST_GET(d) \
(uint32_t)(d.n[3] >> 32), (uint32_t)d.n[3], \
(uint32_t)(d.n[2] >> 32), (uint32_t)d.n[2], \
(uint32_t)(d.n[1] >> 32), (uint32_t)d.n[1], \
(uint32_t)(d.n[0] >> 32), (uint32_t)d.n[0]
#endif /* SECP256K1_FIELD_REPR_H */

View File

@@ -0,0 +1,524 @@
/***********************************************************************
* Copyright (c) 2013, 2014 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_FIELD_REPR_IMPL_H
#define SECP256K1_FIELD_REPR_IMPL_H
#include "checkmem.h"
#include "util.h"
#include "field.h"
#include "modinv64_impl.h"
#include "field_5x52_int128_impl.h"
#ifdef VERIFY
static void secp256k1_fe_impl_verify(const secp256k1_fe *a) {
const uint64_t *d = a->n;
int m = a->normalized ? 1 : 2 * a->magnitude;
/* secp256k1 'p' value defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */
VERIFY_CHECK(d[0] <= 0xFFFFFFFFFFFFFULL * m);
VERIFY_CHECK(d[1] <= 0xFFFFFFFFFFFFFULL * m);
VERIFY_CHECK(d[2] <= 0xFFFFFFFFFFFFFULL * m);
VERIFY_CHECK(d[3] <= 0xFFFFFFFFFFFFFULL * m);
VERIFY_CHECK(d[4] <= 0x0FFFFFFFFFFFFULL * m);
if (a->normalized) {
if ((d[4] == 0x0FFFFFFFFFFFFULL) && ((d[3] & d[2] & d[1]) == 0xFFFFFFFFFFFFFULL)) {
VERIFY_CHECK(d[0] < 0xFFFFEFFFFFC2FULL);
}
}
}
#endif
static void secp256k1_fe_impl_get_bounds(secp256k1_fe *r, int m) {
r->n[0] = 0xFFFFFFFFFFFFFULL * 2 * m;
r->n[1] = 0xFFFFFFFFFFFFFULL * 2 * m;
r->n[2] = 0xFFFFFFFFFFFFFULL * 2 * m;
r->n[3] = 0xFFFFFFFFFFFFFULL * 2 * m;
r->n[4] = 0x0FFFFFFFFFFFFULL * 2 * m;
}
static void secp256k1_fe_impl_normalize(secp256k1_fe *r) {
uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4];
/* Reduce t4 at the start so there will be at most a single carry from the first pass */
uint64_t m;
uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL;
/* The first pass ensures the magnitude is 1, ... */
t0 += x * 0x1000003D1ULL;
t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL;
t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; m = t1;
t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; m &= t2;
t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; m &= t3;
/* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */
VERIFY_CHECK(t4 >> 49 == 0);
/* At most a single final reduction is needed; check if the value is >= the field characteristic */
x = (t4 >> 48) | ((t4 == 0x0FFFFFFFFFFFFULL) & (m == 0xFFFFFFFFFFFFFULL)
& (t0 >= 0xFFFFEFFFFFC2FULL));
/* Apply the final reduction (for constant-time behaviour, we do it always) */
t0 += x * 0x1000003D1ULL;
t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL;
t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL;
t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL;
t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL;
/* If t4 didn't carry to bit 48 already, then it should have after any final reduction */
VERIFY_CHECK(t4 >> 48 == x);
/* Mask off the possible multiple of 2^256 from the final reduction */
t4 &= 0x0FFFFFFFFFFFFULL;
r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4;
}
static void secp256k1_fe_impl_normalize_weak(secp256k1_fe *r) {
uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4];
/* Reduce t4 at the start so there will be at most a single carry from the first pass */
uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL;
/* The first pass ensures the magnitude is 1, ... */
t0 += x * 0x1000003D1ULL;
t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL;
t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL;
t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL;
t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL;
/* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */
VERIFY_CHECK(t4 >> 49 == 0);
r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4;
}
static void secp256k1_fe_impl_normalize_var(secp256k1_fe *r) {
uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4];
/* Reduce t4 at the start so there will be at most a single carry from the first pass */
uint64_t m;
uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL;
/* The first pass ensures the magnitude is 1, ... */
t0 += x * 0x1000003D1ULL;
t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL;
t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; m = t1;
t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; m &= t2;
t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; m &= t3;
/* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */
VERIFY_CHECK(t4 >> 49 == 0);
/* At most a single final reduction is needed; check if the value is >= the field characteristic */
x = (t4 >> 48) | ((t4 == 0x0FFFFFFFFFFFFULL) & (m == 0xFFFFFFFFFFFFFULL)
& (t0 >= 0xFFFFEFFFFFC2FULL));
if (x) {
t0 += 0x1000003D1ULL;
t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL;
t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL;
t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL;
t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL;
/* If t4 didn't carry to bit 48 already, then it should have after any final reduction */
VERIFY_CHECK(t4 >> 48 == x);
/* Mask off the possible multiple of 2^256 from the final reduction */
t4 &= 0x0FFFFFFFFFFFFULL;
}
r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4;
}
static int secp256k1_fe_impl_normalizes_to_zero(const secp256k1_fe *r) {
uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4];
/* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */
uint64_t z0, z1;
/* Reduce t4 at the start so there will be at most a single carry from the first pass */
uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL;
/* The first pass ensures the magnitude is 1, ... */
t0 += x * 0x1000003D1ULL;
t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; z0 = t0; z1 = t0 ^ 0x1000003D0ULL;
t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; z0 |= t1; z1 &= t1;
t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; z0 |= t2; z1 &= t2;
t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; z0 |= t3; z1 &= t3;
z0 |= t4; z1 &= t4 ^ 0xF000000000000ULL;
/* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */
VERIFY_CHECK(t4 >> 49 == 0);
return (z0 == 0) | (z1 == 0xFFFFFFFFFFFFFULL);
}
static int secp256k1_fe_impl_normalizes_to_zero_var(const secp256k1_fe *r) {
uint64_t t0, t1, t2, t3, t4;
uint64_t z0, z1;
uint64_t x;
t0 = r->n[0];
t4 = r->n[4];
/* Reduce t4 at the start so there will be at most a single carry from the first pass */
x = t4 >> 48;
/* The first pass ensures the magnitude is 1, ... */
t0 += x * 0x1000003D1ULL;
/* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */
z0 = t0 & 0xFFFFFFFFFFFFFULL;
z1 = z0 ^ 0x1000003D0ULL;
/* Fast return path should catch the majority of cases */
if ((z0 != 0ULL) & (z1 != 0xFFFFFFFFFFFFFULL)) {
return 0;
}
t1 = r->n[1];
t2 = r->n[2];
t3 = r->n[3];
t4 &= 0x0FFFFFFFFFFFFULL;
t1 += (t0 >> 52);
t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; z0 |= t1; z1 &= t1;
t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; z0 |= t2; z1 &= t2;
t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; z0 |= t3; z1 &= t3;
z0 |= t4; z1 &= t4 ^ 0xF000000000000ULL;
/* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */
VERIFY_CHECK(t4 >> 49 == 0);
return (z0 == 0) | (z1 == 0xFFFFFFFFFFFFFULL);
}
SECP256K1_INLINE static void secp256k1_fe_impl_set_int(secp256k1_fe *r, int a) {
r->n[0] = a;
r->n[1] = r->n[2] = r->n[3] = r->n[4] = 0;
}
SECP256K1_INLINE static int secp256k1_fe_impl_is_zero(const secp256k1_fe *a) {
const uint64_t *t = a->n;
return (t[0] | t[1] | t[2] | t[3] | t[4]) == 0;
}
SECP256K1_INLINE static int secp256k1_fe_impl_is_odd(const secp256k1_fe *a) {
return a->n[0] & 1;
}
static int secp256k1_fe_impl_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) {
int i;
for (i = 4; i >= 0; i--) {
if (a->n[i] > b->n[i]) {
return 1;
}
if (a->n[i] < b->n[i]) {
return -1;
}
}
return 0;
}
static void secp256k1_fe_impl_set_b32_mod(secp256k1_fe *r, const unsigned char *a) {
r->n[0] = (uint64_t)a[31]
| ((uint64_t)a[30] << 8)
| ((uint64_t)a[29] << 16)
| ((uint64_t)a[28] << 24)
| ((uint64_t)a[27] << 32)
| ((uint64_t)a[26] << 40)
| ((uint64_t)(a[25] & 0xF) << 48);
r->n[1] = (uint64_t)((a[25] >> 4) & 0xF)
| ((uint64_t)a[24] << 4)
| ((uint64_t)a[23] << 12)
| ((uint64_t)a[22] << 20)
| ((uint64_t)a[21] << 28)
| ((uint64_t)a[20] << 36)
| ((uint64_t)a[19] << 44);
r->n[2] = (uint64_t)a[18]
| ((uint64_t)a[17] << 8)
| ((uint64_t)a[16] << 16)
| ((uint64_t)a[15] << 24)
| ((uint64_t)a[14] << 32)
| ((uint64_t)a[13] << 40)
| ((uint64_t)(a[12] & 0xF) << 48);
r->n[3] = (uint64_t)((a[12] >> 4) & 0xF)
| ((uint64_t)a[11] << 4)
| ((uint64_t)a[10] << 12)
| ((uint64_t)a[9] << 20)
| ((uint64_t)a[8] << 28)
| ((uint64_t)a[7] << 36)
| ((uint64_t)a[6] << 44);
r->n[4] = (uint64_t)a[5]
| ((uint64_t)a[4] << 8)
| ((uint64_t)a[3] << 16)
| ((uint64_t)a[2] << 24)
| ((uint64_t)a[1] << 32)
| ((uint64_t)a[0] << 40);
}
static int secp256k1_fe_impl_set_b32_limit(secp256k1_fe *r, const unsigned char *a) {
secp256k1_fe_impl_set_b32_mod(r, a);
return !((r->n[4] == 0x0FFFFFFFFFFFFULL) & ((r->n[3] & r->n[2] & r->n[1]) == 0xFFFFFFFFFFFFFULL) & (r->n[0] >= 0xFFFFEFFFFFC2FULL));
}
/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */
static void secp256k1_fe_impl_get_b32(unsigned char *r, const secp256k1_fe *a) {
r[0] = (a->n[4] >> 40) & 0xFF;
r[1] = (a->n[4] >> 32) & 0xFF;
r[2] = (a->n[4] >> 24) & 0xFF;
r[3] = (a->n[4] >> 16) & 0xFF;
r[4] = (a->n[4] >> 8) & 0xFF;
r[5] = a->n[4] & 0xFF;
r[6] = (a->n[3] >> 44) & 0xFF;
r[7] = (a->n[3] >> 36) & 0xFF;
r[8] = (a->n[3] >> 28) & 0xFF;
r[9] = (a->n[3] >> 20) & 0xFF;
r[10] = (a->n[3] >> 12) & 0xFF;
r[11] = (a->n[3] >> 4) & 0xFF;
r[12] = ((a->n[2] >> 48) & 0xF) | ((a->n[3] & 0xF) << 4);
r[13] = (a->n[2] >> 40) & 0xFF;
r[14] = (a->n[2] >> 32) & 0xFF;
r[15] = (a->n[2] >> 24) & 0xFF;
r[16] = (a->n[2] >> 16) & 0xFF;
r[17] = (a->n[2] >> 8) & 0xFF;
r[18] = a->n[2] & 0xFF;
r[19] = (a->n[1] >> 44) & 0xFF;
r[20] = (a->n[1] >> 36) & 0xFF;
r[21] = (a->n[1] >> 28) & 0xFF;
r[22] = (a->n[1] >> 20) & 0xFF;
r[23] = (a->n[1] >> 12) & 0xFF;
r[24] = (a->n[1] >> 4) & 0xFF;
r[25] = ((a->n[0] >> 48) & 0xF) | ((a->n[1] & 0xF) << 4);
r[26] = (a->n[0] >> 40) & 0xFF;
r[27] = (a->n[0] >> 32) & 0xFF;
r[28] = (a->n[0] >> 24) & 0xFF;
r[29] = (a->n[0] >> 16) & 0xFF;
r[30] = (a->n[0] >> 8) & 0xFF;
r[31] = a->n[0] & 0xFF;
}
SECP256K1_INLINE static void secp256k1_fe_impl_negate_unchecked(secp256k1_fe *r, const secp256k1_fe *a, int m) {
/* For all legal values of m (0..31), the following properties hold: */
VERIFY_CHECK(0xFFFFEFFFFFC2FULL * 2 * (m + 1) >= 0xFFFFFFFFFFFFFULL * 2 * m);
VERIFY_CHECK(0xFFFFFFFFFFFFFULL * 2 * (m + 1) >= 0xFFFFFFFFFFFFFULL * 2 * m);
VERIFY_CHECK(0x0FFFFFFFFFFFFULL * 2 * (m + 1) >= 0x0FFFFFFFFFFFFULL * 2 * m);
/* Due to the properties above, the left hand in the subtractions below is never less than
* the right hand. */
r->n[0] = 0xFFFFEFFFFFC2FULL * 2 * (m + 1) - a->n[0];
r->n[1] = 0xFFFFFFFFFFFFFULL * 2 * (m + 1) - a->n[1];
r->n[2] = 0xFFFFFFFFFFFFFULL * 2 * (m + 1) - a->n[2];
r->n[3] = 0xFFFFFFFFFFFFFULL * 2 * (m + 1) - a->n[3];
r->n[4] = 0x0FFFFFFFFFFFFULL * 2 * (m + 1) - a->n[4];
}
SECP256K1_INLINE static void secp256k1_fe_impl_mul_int_unchecked(secp256k1_fe *r, int a) {
r->n[0] *= a;
r->n[1] *= a;
r->n[2] *= a;
r->n[3] *= a;
r->n[4] *= a;
}
SECP256K1_INLINE static void secp256k1_fe_impl_add_int(secp256k1_fe *r, int a) {
r->n[0] += a;
}
SECP256K1_INLINE static void secp256k1_fe_impl_add(secp256k1_fe *r, const secp256k1_fe *a) {
r->n[0] += a->n[0];
r->n[1] += a->n[1];
r->n[2] += a->n[2];
r->n[3] += a->n[3];
r->n[4] += a->n[4];
}
SECP256K1_INLINE static void secp256k1_fe_impl_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b) {
secp256k1_fe_mul_inner(r->n, a->n, b->n);
}
SECP256K1_INLINE static void secp256k1_fe_impl_sqr(secp256k1_fe *r, const secp256k1_fe *a) {
secp256k1_fe_sqr_inner(r->n, a->n);
}
SECP256K1_INLINE static void secp256k1_fe_impl_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) {
uint64_t mask0, mask1;
volatile int vflag = flag;
VERIFY_CHECK(flag == 0 || flag == 1);
SECP256K1_CHECKMEM_CHECK_VERIFY(r->n, sizeof(r->n));
mask0 = vflag + ~((uint64_t)0);
mask1 = ~mask0;
r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1);
r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1);
r->n[2] = (r->n[2] & mask0) | (a->n[2] & mask1);
r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1);
r->n[4] = (r->n[4] & mask0) | (a->n[4] & mask1);
}
static SECP256K1_INLINE void secp256k1_fe_impl_half(secp256k1_fe *r) {
uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4];
uint64_t one = (uint64_t)1;
uint64_t mask = -(t0 & one) >> 12;
/* Bounds analysis (over the rationals).
*
* Let m = r->magnitude
* C = 0xFFFFFFFFFFFFFULL * 2
* D = 0x0FFFFFFFFFFFFULL * 2
*
* Initial bounds: t0..t3 <= C * m
* t4 <= D * m
*/
t0 += 0xFFFFEFFFFFC2FULL & mask;
t1 += mask;
t2 += mask;
t3 += mask;
t4 += mask >> 4;
VERIFY_CHECK((t0 & one) == 0);
/* t0..t3: added <= C/2
* t4: added <= D/2
*
* Current bounds: t0..t3 <= C * (m + 1/2)
* t4 <= D * (m + 1/2)
*/
r->n[0] = (t0 >> 1) + ((t1 & one) << 51);
r->n[1] = (t1 >> 1) + ((t2 & one) << 51);
r->n[2] = (t2 >> 1) + ((t3 & one) << 51);
r->n[3] = (t3 >> 1) + ((t4 & one) << 51);
r->n[4] = (t4 >> 1);
/* t0..t3: shifted right and added <= C/4 + 1/2
* t4: shifted right
*
* Current bounds: t0..t3 <= C * (m/2 + 1/2)
* t4 <= D * (m/2 + 1/4)
*
* Therefore the output magnitude (M) has to be set such that:
* t0..t3: C * M >= C * (m/2 + 1/2)
* t4: D * M >= D * (m/2 + 1/4)
*
* It suffices for all limbs that, for any input magnitude m:
* M >= m/2 + 1/2
*
* and since we want the smallest such integer value for M:
* M == floor(m/2) + 1
*/
}
static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) {
uint64_t mask0, mask1;
volatile int vflag = flag;
VERIFY_CHECK(flag == 0 || flag == 1);
SECP256K1_CHECKMEM_CHECK_VERIFY(r->n, sizeof(r->n));
mask0 = vflag + ~((uint64_t)0);
mask1 = ~mask0;
r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1);
r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1);
r->n[2] = (r->n[2] & mask0) | (a->n[2] & mask1);
r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1);
}
static void secp256k1_fe_impl_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a) {
r->n[0] = a->n[0] | a->n[1] << 52;
r->n[1] = a->n[1] >> 12 | a->n[2] << 40;
r->n[2] = a->n[2] >> 24 | a->n[3] << 28;
r->n[3] = a->n[3] >> 36 | a->n[4] << 16;
}
static SECP256K1_INLINE void secp256k1_fe_impl_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a) {
r->n[0] = a->n[0] & 0xFFFFFFFFFFFFFULL;
r->n[1] = a->n[0] >> 52 | ((a->n[1] << 12) & 0xFFFFFFFFFFFFFULL);
r->n[2] = a->n[1] >> 40 | ((a->n[2] << 24) & 0xFFFFFFFFFFFFFULL);
r->n[3] = a->n[2] >> 28 | ((a->n[3] << 36) & 0xFFFFFFFFFFFFFULL);
r->n[4] = a->n[3] >> 16;
}
static void secp256k1_fe_from_signed62(secp256k1_fe *r, const secp256k1_modinv64_signed62 *a) {
const uint64_t M52 = UINT64_MAX >> 12;
const uint64_t a0 = a->v[0], a1 = a->v[1], a2 = a->v[2], a3 = a->v[3], a4 = a->v[4];
/* The output from secp256k1_modinv64{_var} should be normalized to range [0,modulus), and
* have limbs in [0,2^62). The modulus is < 2^256, so the top limb must be below 2^(256-62*4).
*/
VERIFY_CHECK(a0 >> 62 == 0);
VERIFY_CHECK(a1 >> 62 == 0);
VERIFY_CHECK(a2 >> 62 == 0);
VERIFY_CHECK(a3 >> 62 == 0);
VERIFY_CHECK(a4 >> 8 == 0);
r->n[0] = a0 & M52;
r->n[1] = (a0 >> 52 | a1 << 10) & M52;
r->n[2] = (a1 >> 42 | a2 << 20) & M52;
r->n[3] = (a2 >> 32 | a3 << 30) & M52;
r->n[4] = (a3 >> 22 | a4 << 40);
}
static void secp256k1_fe_to_signed62(secp256k1_modinv64_signed62 *r, const secp256k1_fe *a) {
const uint64_t M62 = UINT64_MAX >> 2;
const uint64_t a0 = a->n[0], a1 = a->n[1], a2 = a->n[2], a3 = a->n[3], a4 = a->n[4];
r->v[0] = (a0 | a1 << 52) & M62;
r->v[1] = (a1 >> 10 | a2 << 42) & M62;
r->v[2] = (a2 >> 20 | a3 << 32) & M62;
r->v[3] = (a3 >> 30 | a4 << 22) & M62;
r->v[4] = a4 >> 40;
}
static const secp256k1_modinv64_modinfo secp256k1_const_modinfo_fe = {
{{-0x1000003D1LL, 0, 0, 0, 256}},
0x27C7F6E22DDACACFLL
};
static void secp256k1_fe_impl_inv(secp256k1_fe *r, const secp256k1_fe *x) {
secp256k1_fe tmp = *x;
secp256k1_modinv64_signed62 s;
secp256k1_fe_normalize(&tmp);
secp256k1_fe_to_signed62(&s, &tmp);
secp256k1_modinv64(&s, &secp256k1_const_modinfo_fe);
secp256k1_fe_from_signed62(r, &s);
}
static void secp256k1_fe_impl_inv_var(secp256k1_fe *r, const secp256k1_fe *x) {
secp256k1_fe tmp = *x;
secp256k1_modinv64_signed62 s;
secp256k1_fe_normalize_var(&tmp);
secp256k1_fe_to_signed62(&s, &tmp);
secp256k1_modinv64_var(&s, &secp256k1_const_modinfo_fe);
secp256k1_fe_from_signed62(r, &s);
}
static int secp256k1_fe_impl_is_square_var(const secp256k1_fe *x) {
secp256k1_fe tmp;
secp256k1_modinv64_signed62 s;
int jac, ret;
tmp = *x;
secp256k1_fe_normalize_var(&tmp);
/* secp256k1_jacobi64_maybe_var cannot deal with input 0. */
if (secp256k1_fe_is_zero(&tmp)) return 1;
secp256k1_fe_to_signed62(&s, &tmp);
jac = secp256k1_jacobi64_maybe_var(&s, &secp256k1_const_modinfo_fe);
if (jac == 0) {
/* secp256k1_jacobi64_maybe_var failed to compute the Jacobi symbol. Fall back
* to computing a square root. This should be extremely rare with random
* input (except in VERIFY mode, where a lower iteration count is used). */
secp256k1_fe dummy;
ret = secp256k1_fe_sqrt(&dummy, &tmp);
} else {
ret = jac >= 0;
}
return ret;
}
#endif /* SECP256K1_FIELD_REPR_IMPL_H */

View File

@@ -0,0 +1,274 @@
/***********************************************************************
* Copyright (c) 2013, 2014 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_FIELD_INNER5X52_IMPL_H
#define SECP256K1_FIELD_INNER5X52_IMPL_H
#include <stdint.h>
#include "int128.h"
#include "util.h"
#define VERIFY_BITS(x, n) VERIFY_CHECK(((x) >> (n)) == 0)
#define VERIFY_BITS_128(x, n) VERIFY_CHECK(secp256k1_u128_check_bits((x), (n)))
SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t *a, const uint64_t * SECP256K1_RESTRICT b) {
secp256k1_uint128 c, d;
uint64_t t3, t4, tx, u0;
uint64_t a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4];
const uint64_t M = 0xFFFFFFFFFFFFFULL, R = 0x1000003D10ULL;
VERIFY_BITS(a[0], 56);
VERIFY_BITS(a[1], 56);
VERIFY_BITS(a[2], 56);
VERIFY_BITS(a[3], 56);
VERIFY_BITS(a[4], 52);
VERIFY_BITS(b[0], 56);
VERIFY_BITS(b[1], 56);
VERIFY_BITS(b[2], 56);
VERIFY_BITS(b[3], 56);
VERIFY_BITS(b[4], 52);
VERIFY_CHECK(r != b);
VERIFY_CHECK(a != b);
/* [... a b c] is a shorthand for ... + a<<104 + b<<52 + c<<0 mod n.
* for 0 <= x <= 4, px is a shorthand for sum(a[i]*b[x-i], i=0..x).
* for 4 <= x <= 8, px is a shorthand for sum(a[i]*b[x-i], i=(x-4)..4)
* Note that [x 0 0 0 0 0] = [x*R].
*/
secp256k1_u128_mul(&d, a0, b[3]);
secp256k1_u128_accum_mul(&d, a1, b[2]);
secp256k1_u128_accum_mul(&d, a2, b[1]);
secp256k1_u128_accum_mul(&d, a3, b[0]);
VERIFY_BITS_128(&d, 114);
/* [d 0 0 0] = [p3 0 0 0] */
secp256k1_u128_mul(&c, a4, b[4]);
VERIFY_BITS_128(&c, 112);
/* [c 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */
secp256k1_u128_accum_mul(&d, R, secp256k1_u128_to_u64(&c)); secp256k1_u128_rshift(&c, 64);
VERIFY_BITS_128(&d, 115);
VERIFY_BITS_128(&c, 48);
/* [(c<<12) 0 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */
t3 = secp256k1_u128_to_u64(&d) & M; secp256k1_u128_rshift(&d, 52);
VERIFY_BITS(t3, 52);
VERIFY_BITS_128(&d, 63);
/* [(c<<12) 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */
secp256k1_u128_accum_mul(&d, a0, b[4]);
secp256k1_u128_accum_mul(&d, a1, b[3]);
secp256k1_u128_accum_mul(&d, a2, b[2]);
secp256k1_u128_accum_mul(&d, a3, b[1]);
secp256k1_u128_accum_mul(&d, a4, b[0]);
VERIFY_BITS_128(&d, 115);
/* [(c<<12) 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */
secp256k1_u128_accum_mul(&d, R << 12, secp256k1_u128_to_u64(&c));
VERIFY_BITS_128(&d, 116);
/* [d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */
t4 = secp256k1_u128_to_u64(&d) & M; secp256k1_u128_rshift(&d, 52);
VERIFY_BITS(t4, 52);
VERIFY_BITS_128(&d, 64);
/* [d t4 t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */
tx = (t4 >> 48); t4 &= (M >> 4);
VERIFY_BITS(tx, 4);
VERIFY_BITS(t4, 48);
/* [d t4+(tx<<48) t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */
secp256k1_u128_mul(&c, a0, b[0]);
VERIFY_BITS_128(&c, 112);
/* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 0 p4 p3 0 0 p0] */
secp256k1_u128_accum_mul(&d, a1, b[4]);
secp256k1_u128_accum_mul(&d, a2, b[3]);
secp256k1_u128_accum_mul(&d, a3, b[2]);
secp256k1_u128_accum_mul(&d, a4, b[1]);
VERIFY_BITS_128(&d, 114);
/* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */
u0 = secp256k1_u128_to_u64(&d) & M; secp256k1_u128_rshift(&d, 52);
VERIFY_BITS(u0, 52);
VERIFY_BITS_128(&d, 62);
/* [d u0 t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */
/* [d 0 t4+(tx<<48)+(u0<<52) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */
u0 = (u0 << 4) | tx;
VERIFY_BITS(u0, 56);
/* [d 0 t4+(u0<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */
secp256k1_u128_accum_mul(&c, u0, R >> 4);
VERIFY_BITS_128(&c, 113);
/* [d 0 t4 t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */
r[0] = secp256k1_u128_to_u64(&c) & M; secp256k1_u128_rshift(&c, 52);
VERIFY_BITS(r[0], 52);
VERIFY_BITS_128(&c, 61);
/* [d 0 t4 t3 0 c r0] = [p8 0 0 p5 p4 p3 0 0 p0] */
secp256k1_u128_accum_mul(&c, a0, b[1]);
secp256k1_u128_accum_mul(&c, a1, b[0]);
VERIFY_BITS_128(&c, 114);
/* [d 0 t4 t3 0 c r0] = [p8 0 0 p5 p4 p3 0 p1 p0] */
secp256k1_u128_accum_mul(&d, a2, b[4]);
secp256k1_u128_accum_mul(&d, a3, b[3]);
secp256k1_u128_accum_mul(&d, a4, b[2]);
VERIFY_BITS_128(&d, 114);
/* [d 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */
secp256k1_u128_accum_mul(&c, secp256k1_u128_to_u64(&d) & M, R); secp256k1_u128_rshift(&d, 52);
VERIFY_BITS_128(&c, 115);
VERIFY_BITS_128(&d, 62);
/* [d 0 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */
r[1] = secp256k1_u128_to_u64(&c) & M; secp256k1_u128_rshift(&c, 52);
VERIFY_BITS(r[1], 52);
VERIFY_BITS_128(&c, 63);
/* [d 0 0 t4 t3 c r1 r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */
secp256k1_u128_accum_mul(&c, a0, b[2]);
secp256k1_u128_accum_mul(&c, a1, b[1]);
secp256k1_u128_accum_mul(&c, a2, b[0]);
VERIFY_BITS_128(&c, 114);
/* [d 0 0 t4 t3 c r1 r0] = [p8 0 p6 p5 p4 p3 p2 p1 p0] */
secp256k1_u128_accum_mul(&d, a3, b[4]);
secp256k1_u128_accum_mul(&d, a4, b[3]);
VERIFY_BITS_128(&d, 114);
/* [d 0 0 t4 t3 c t1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
secp256k1_u128_accum_mul(&c, R, secp256k1_u128_to_u64(&d)); secp256k1_u128_rshift(&d, 64);
VERIFY_BITS_128(&c, 115);
VERIFY_BITS_128(&d, 50);
/* [(d<<12) 0 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
r[2] = secp256k1_u128_to_u64(&c) & M; secp256k1_u128_rshift(&c, 52);
VERIFY_BITS(r[2], 52);
VERIFY_BITS_128(&c, 63);
/* [(d<<12) 0 0 0 t4 t3+c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
secp256k1_u128_accum_mul(&c, R << 12, secp256k1_u128_to_u64(&d));
secp256k1_u128_accum_u64(&c, t3);
VERIFY_BITS_128(&c, 100);
/* [t4 c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
r[3] = secp256k1_u128_to_u64(&c) & M; secp256k1_u128_rshift(&c, 52);
VERIFY_BITS(r[3], 52);
VERIFY_BITS_128(&c, 48);
/* [t4+c r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
r[4] = secp256k1_u128_to_u64(&c) + t4;
VERIFY_BITS(r[4], 49);
/* [r4 r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
}
SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint64_t *r, const uint64_t *a) {
secp256k1_uint128 c, d;
uint64_t a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4];
uint64_t t3, t4, tx, u0;
const uint64_t M = 0xFFFFFFFFFFFFFULL, R = 0x1000003D10ULL;
VERIFY_BITS(a[0], 56);
VERIFY_BITS(a[1], 56);
VERIFY_BITS(a[2], 56);
VERIFY_BITS(a[3], 56);
VERIFY_BITS(a[4], 52);
/** [... a b c] is a shorthand for ... + a<<104 + b<<52 + c<<0 mod n.
* px is a shorthand for sum(a[i]*a[x-i], i=0..x).
* Note that [x 0 0 0 0 0] = [x*R].
*/
secp256k1_u128_mul(&d, a0*2, a3);
secp256k1_u128_accum_mul(&d, a1*2, a2);
VERIFY_BITS_128(&d, 114);
/* [d 0 0 0] = [p3 0 0 0] */
secp256k1_u128_mul(&c, a4, a4);
VERIFY_BITS_128(&c, 112);
/* [c 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */
secp256k1_u128_accum_mul(&d, R, secp256k1_u128_to_u64(&c)); secp256k1_u128_rshift(&c, 64);
VERIFY_BITS_128(&d, 115);
VERIFY_BITS_128(&c, 48);
/* [(c<<12) 0 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */
t3 = secp256k1_u128_to_u64(&d) & M; secp256k1_u128_rshift(&d, 52);
VERIFY_BITS(t3, 52);
VERIFY_BITS_128(&d, 63);
/* [(c<<12) 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */
a4 *= 2;
secp256k1_u128_accum_mul(&d, a0, a4);
secp256k1_u128_accum_mul(&d, a1*2, a3);
secp256k1_u128_accum_mul(&d, a2, a2);
VERIFY_BITS_128(&d, 115);
/* [(c<<12) 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */
secp256k1_u128_accum_mul(&d, R << 12, secp256k1_u128_to_u64(&c));
VERIFY_BITS_128(&d, 116);
/* [d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */
t4 = secp256k1_u128_to_u64(&d) & M; secp256k1_u128_rshift(&d, 52);
VERIFY_BITS(t4, 52);
VERIFY_BITS_128(&d, 64);
/* [d t4 t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */
tx = (t4 >> 48); t4 &= (M >> 4);
VERIFY_BITS(tx, 4);
VERIFY_BITS(t4, 48);
/* [d t4+(tx<<48) t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */
secp256k1_u128_mul(&c, a0, a0);
VERIFY_BITS_128(&c, 112);
/* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 0 p4 p3 0 0 p0] */
secp256k1_u128_accum_mul(&d, a1, a4);
secp256k1_u128_accum_mul(&d, a2*2, a3);
VERIFY_BITS_128(&d, 114);
/* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */
u0 = secp256k1_u128_to_u64(&d) & M; secp256k1_u128_rshift(&d, 52);
VERIFY_BITS(u0, 52);
VERIFY_BITS_128(&d, 62);
/* [d u0 t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */
/* [d 0 t4+(tx<<48)+(u0<<52) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */
u0 = (u0 << 4) | tx;
VERIFY_BITS(u0, 56);
/* [d 0 t4+(u0<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */
secp256k1_u128_accum_mul(&c, u0, R >> 4);
VERIFY_BITS_128(&c, 113);
/* [d 0 t4 t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */
r[0] = secp256k1_u128_to_u64(&c) & M; secp256k1_u128_rshift(&c, 52);
VERIFY_BITS(r[0], 52);
VERIFY_BITS_128(&c, 61);
/* [d 0 t4 t3 0 c r0] = [p8 0 0 p5 p4 p3 0 0 p0] */
a0 *= 2;
secp256k1_u128_accum_mul(&c, a0, a1);
VERIFY_BITS_128(&c, 114);
/* [d 0 t4 t3 0 c r0] = [p8 0 0 p5 p4 p3 0 p1 p0] */
secp256k1_u128_accum_mul(&d, a2, a4);
secp256k1_u128_accum_mul(&d, a3, a3);
VERIFY_BITS_128(&d, 114);
/* [d 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */
secp256k1_u128_accum_mul(&c, secp256k1_u128_to_u64(&d) & M, R); secp256k1_u128_rshift(&d, 52);
VERIFY_BITS_128(&c, 115);
VERIFY_BITS_128(&d, 62);
/* [d 0 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */
r[1] = secp256k1_u128_to_u64(&c) & M; secp256k1_u128_rshift(&c, 52);
VERIFY_BITS(r[1], 52);
VERIFY_BITS_128(&c, 63);
/* [d 0 0 t4 t3 c r1 r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */
secp256k1_u128_accum_mul(&c, a0, a2);
secp256k1_u128_accum_mul(&c, a1, a1);
VERIFY_BITS_128(&c, 114);
/* [d 0 0 t4 t3 c r1 r0] = [p8 0 p6 p5 p4 p3 p2 p1 p0] */
secp256k1_u128_accum_mul(&d, a3, a4);
VERIFY_BITS_128(&d, 114);
/* [d 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
secp256k1_u128_accum_mul(&c, R, secp256k1_u128_to_u64(&d)); secp256k1_u128_rshift(&d, 64);
VERIFY_BITS_128(&c, 115);
VERIFY_BITS_128(&d, 50);
/* [(d<<12) 0 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
r[2] = secp256k1_u128_to_u64(&c) & M; secp256k1_u128_rshift(&c, 52);
VERIFY_BITS(r[2], 52);
VERIFY_BITS_128(&c, 63);
/* [(d<<12) 0 0 0 t4 t3+c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
secp256k1_u128_accum_mul(&c, R << 12, secp256k1_u128_to_u64(&d));
secp256k1_u128_accum_u64(&c, t3);
VERIFY_BITS_128(&c, 100);
/* [t4 c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
r[3] = secp256k1_u128_to_u64(&c) & M; secp256k1_u128_rshift(&c, 52);
VERIFY_BITS(r[3], 52);
VERIFY_BITS_128(&c, 48);
/* [t4+c r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
r[4] = secp256k1_u128_to_u64(&c) + t4;
VERIFY_BITS(r[4], 49);
/* [r4 r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
}
#endif /* SECP256K1_FIELD_INNER5X52_IMPL_H */

View File

@@ -0,0 +1,457 @@
/***********************************************************************
* Copyright (c) 2013, 2014 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_FIELD_IMPL_H
#define SECP256K1_FIELD_IMPL_H
#include "field.h"
#include "util.h"
#if defined(SECP256K1_WIDEMUL_INT128)
#include "field_5x52_impl.h"
#elif defined(SECP256K1_WIDEMUL_INT64)
#include "field_10x26_impl.h"
#else
#error "Please select wide multiplication implementation"
#endif
SECP256K1_INLINE static void secp256k1_fe_clear(secp256k1_fe *a) {
secp256k1_memclear_explicit(a, sizeof(secp256k1_fe));
}
SECP256K1_INLINE static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) {
secp256k1_fe na;
SECP256K1_FE_VERIFY(a);
SECP256K1_FE_VERIFY(b);
SECP256K1_FE_VERIFY_MAGNITUDE(a, 1);
SECP256K1_FE_VERIFY_MAGNITUDE(b, 31);
secp256k1_fe_negate(&na, a, 1);
secp256k1_fe_add(&na, b);
return secp256k1_fe_normalizes_to_zero(&na);
}
static int secp256k1_fe_sqrt(secp256k1_fe * SECP256K1_RESTRICT r, const secp256k1_fe * SECP256K1_RESTRICT a) {
/** Given that p is congruent to 3 mod 4, we can compute the square root of
* a mod p as the (p+1)/4'th power of a.
*
* As (p+1)/4 is an even number, it will have the same result for a and for
* (-a). Only one of these two numbers actually has a square root however,
* so we test at the end by squaring and comparing to the input.
* Also because (p+1)/4 is an even number, the computed square root is
* itself always a square (a ** ((p+1)/4) is the square of a ** ((p+1)/8)).
*/
secp256k1_fe x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1;
int j, ret;
VERIFY_CHECK(r != a);
SECP256K1_FE_VERIFY(a);
SECP256K1_FE_VERIFY_MAGNITUDE(a, 8);
/** The binary representation of (p + 1)/4 has 3 blocks of 1s, with lengths in
* { 2, 22, 223 }. Use an addition chain to calculate 2^n - 1 for each block:
* 1, [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223]
*/
secp256k1_fe_sqr(&x2, a);
secp256k1_fe_mul(&x2, &x2, a);
secp256k1_fe_sqr(&x3, &x2);
secp256k1_fe_mul(&x3, &x3, a);
x6 = x3;
for (j=0; j<3; j++) {
secp256k1_fe_sqr(&x6, &x6);
}
secp256k1_fe_mul(&x6, &x6, &x3);
x9 = x6;
for (j=0; j<3; j++) {
secp256k1_fe_sqr(&x9, &x9);
}
secp256k1_fe_mul(&x9, &x9, &x3);
x11 = x9;
for (j=0; j<2; j++) {
secp256k1_fe_sqr(&x11, &x11);
}
secp256k1_fe_mul(&x11, &x11, &x2);
x22 = x11;
for (j=0; j<11; j++) {
secp256k1_fe_sqr(&x22, &x22);
}
secp256k1_fe_mul(&x22, &x22, &x11);
x44 = x22;
for (j=0; j<22; j++) {
secp256k1_fe_sqr(&x44, &x44);
}
secp256k1_fe_mul(&x44, &x44, &x22);
x88 = x44;
for (j=0; j<44; j++) {
secp256k1_fe_sqr(&x88, &x88);
}
secp256k1_fe_mul(&x88, &x88, &x44);
x176 = x88;
for (j=0; j<88; j++) {
secp256k1_fe_sqr(&x176, &x176);
}
secp256k1_fe_mul(&x176, &x176, &x88);
x220 = x176;
for (j=0; j<44; j++) {
secp256k1_fe_sqr(&x220, &x220);
}
secp256k1_fe_mul(&x220, &x220, &x44);
x223 = x220;
for (j=0; j<3; j++) {
secp256k1_fe_sqr(&x223, &x223);
}
secp256k1_fe_mul(&x223, &x223, &x3);
/* The final result is then assembled using a sliding window over the blocks. */
t1 = x223;
for (j=0; j<23; j++) {
secp256k1_fe_sqr(&t1, &t1);
}
secp256k1_fe_mul(&t1, &t1, &x22);
for (j=0; j<6; j++) {
secp256k1_fe_sqr(&t1, &t1);
}
secp256k1_fe_mul(&t1, &t1, &x2);
secp256k1_fe_sqr(&t1, &t1);
secp256k1_fe_sqr(r, &t1);
/* Check that a square root was actually calculated */
secp256k1_fe_sqr(&t1, r);
ret = secp256k1_fe_equal(&t1, a);
#ifdef VERIFY
if (!ret) {
secp256k1_fe_negate(&t1, &t1, 1);
secp256k1_fe_normalize_var(&t1);
VERIFY_CHECK(secp256k1_fe_equal(&t1, a));
}
#endif
return ret;
}
#ifndef VERIFY
static void secp256k1_fe_verify(const secp256k1_fe *a) { (void)a; }
static void secp256k1_fe_verify_magnitude(const secp256k1_fe *a, int m) { (void)a; (void)m; }
#else
static void secp256k1_fe_impl_verify(const secp256k1_fe *a);
static void secp256k1_fe_verify(const secp256k1_fe *a) {
/* Magnitude between 0 and 32. */
SECP256K1_FE_VERIFY_MAGNITUDE(a, 32);
/* Normalized is 0 or 1. */
VERIFY_CHECK((a->normalized == 0) || (a->normalized == 1));
/* If normalized, magnitude must be 0 or 1. */
if (a->normalized) SECP256K1_FE_VERIFY_MAGNITUDE(a, 1);
/* Invoke implementation-specific checks. */
secp256k1_fe_impl_verify(a);
}
static void secp256k1_fe_verify_magnitude(const secp256k1_fe *a, int m) {
VERIFY_CHECK(m >= 0);
VERIFY_CHECK(m <= 32);
VERIFY_CHECK(a->magnitude <= m);
}
static void secp256k1_fe_impl_normalize(secp256k1_fe *r);
SECP256K1_INLINE static void secp256k1_fe_normalize(secp256k1_fe *r) {
SECP256K1_FE_VERIFY(r);
secp256k1_fe_impl_normalize(r);
r->magnitude = 1;
r->normalized = 1;
SECP256K1_FE_VERIFY(r);
}
static void secp256k1_fe_impl_normalize_weak(secp256k1_fe *r);
SECP256K1_INLINE static void secp256k1_fe_normalize_weak(secp256k1_fe *r) {
SECP256K1_FE_VERIFY(r);
secp256k1_fe_impl_normalize_weak(r);
r->magnitude = 1;
SECP256K1_FE_VERIFY(r);
}
static void secp256k1_fe_impl_normalize_var(secp256k1_fe *r);
SECP256K1_INLINE static void secp256k1_fe_normalize_var(secp256k1_fe *r) {
SECP256K1_FE_VERIFY(r);
secp256k1_fe_impl_normalize_var(r);
r->magnitude = 1;
r->normalized = 1;
SECP256K1_FE_VERIFY(r);
}
static int secp256k1_fe_impl_normalizes_to_zero(const secp256k1_fe *r);
SECP256K1_INLINE static int secp256k1_fe_normalizes_to_zero(const secp256k1_fe *r) {
SECP256K1_FE_VERIFY(r);
return secp256k1_fe_impl_normalizes_to_zero(r);
}
static int secp256k1_fe_impl_normalizes_to_zero_var(const secp256k1_fe *r);
SECP256K1_INLINE static int secp256k1_fe_normalizes_to_zero_var(const secp256k1_fe *r) {
SECP256K1_FE_VERIFY(r);
return secp256k1_fe_impl_normalizes_to_zero_var(r);
}
static void secp256k1_fe_impl_set_int(secp256k1_fe *r, int a);
SECP256K1_INLINE static void secp256k1_fe_set_int(secp256k1_fe *r, int a) {
VERIFY_CHECK(0 <= a && a <= 0x7FFF);
secp256k1_fe_impl_set_int(r, a);
r->magnitude = (a != 0);
r->normalized = 1;
SECP256K1_FE_VERIFY(r);
}
static void secp256k1_fe_impl_add_int(secp256k1_fe *r, int a);
SECP256K1_INLINE static void secp256k1_fe_add_int(secp256k1_fe *r, int a) {
VERIFY_CHECK(0 <= a && a <= 0x7FFF);
SECP256K1_FE_VERIFY(r);
secp256k1_fe_impl_add_int(r, a);
r->magnitude += 1;
r->normalized = 0;
SECP256K1_FE_VERIFY(r);
}
static int secp256k1_fe_impl_is_zero(const secp256k1_fe *a);
SECP256K1_INLINE static int secp256k1_fe_is_zero(const secp256k1_fe *a) {
SECP256K1_FE_VERIFY(a);
VERIFY_CHECK(a->normalized);
return secp256k1_fe_impl_is_zero(a);
}
static int secp256k1_fe_impl_is_odd(const secp256k1_fe *a);
SECP256K1_INLINE static int secp256k1_fe_is_odd(const secp256k1_fe *a) {
SECP256K1_FE_VERIFY(a);
VERIFY_CHECK(a->normalized);
return secp256k1_fe_impl_is_odd(a);
}
static int secp256k1_fe_impl_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b);
SECP256K1_INLINE static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) {
SECP256K1_FE_VERIFY(a);
SECP256K1_FE_VERIFY(b);
VERIFY_CHECK(a->normalized);
VERIFY_CHECK(b->normalized);
return secp256k1_fe_impl_cmp_var(a, b);
}
static void secp256k1_fe_impl_set_b32_mod(secp256k1_fe *r, const unsigned char *a);
SECP256K1_INLINE static void secp256k1_fe_set_b32_mod(secp256k1_fe *r, const unsigned char *a) {
secp256k1_fe_impl_set_b32_mod(r, a);
r->magnitude = 1;
r->normalized = 0;
SECP256K1_FE_VERIFY(r);
}
static int secp256k1_fe_impl_set_b32_limit(secp256k1_fe *r, const unsigned char *a);
SECP256K1_INLINE static int secp256k1_fe_set_b32_limit(secp256k1_fe *r, const unsigned char *a) {
if (secp256k1_fe_impl_set_b32_limit(r, a)) {
r->magnitude = 1;
r->normalized = 1;
SECP256K1_FE_VERIFY(r);
return 1;
} else {
/* Mark the output field element as invalid. */
r->magnitude = -1;
return 0;
}
}
static void secp256k1_fe_impl_get_b32(unsigned char *r, const secp256k1_fe *a);
SECP256K1_INLINE static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a) {
SECP256K1_FE_VERIFY(a);
VERIFY_CHECK(a->normalized);
secp256k1_fe_impl_get_b32(r, a);
}
static void secp256k1_fe_impl_negate_unchecked(secp256k1_fe *r, const secp256k1_fe *a, int m);
SECP256K1_INLINE static void secp256k1_fe_negate_unchecked(secp256k1_fe *r, const secp256k1_fe *a, int m) {
SECP256K1_FE_VERIFY(a);
VERIFY_CHECK(m >= 0 && m <= 31);
SECP256K1_FE_VERIFY_MAGNITUDE(a, m);
secp256k1_fe_impl_negate_unchecked(r, a, m);
r->magnitude = m + 1;
r->normalized = 0;
SECP256K1_FE_VERIFY(r);
}
static void secp256k1_fe_impl_mul_int_unchecked(secp256k1_fe *r, int a);
SECP256K1_INLINE static void secp256k1_fe_mul_int_unchecked(secp256k1_fe *r, int a) {
SECP256K1_FE_VERIFY(r);
VERIFY_CHECK(a >= 0 && a <= 32);
VERIFY_CHECK(a*r->magnitude <= 32);
secp256k1_fe_impl_mul_int_unchecked(r, a);
r->magnitude *= a;
r->normalized = 0;
SECP256K1_FE_VERIFY(r);
}
static void secp256k1_fe_impl_add(secp256k1_fe *r, const secp256k1_fe *a);
SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a) {
SECP256K1_FE_VERIFY(r);
SECP256K1_FE_VERIFY(a);
VERIFY_CHECK(r->magnitude + a->magnitude <= 32);
secp256k1_fe_impl_add(r, a);
r->magnitude += a->magnitude;
r->normalized = 0;
SECP256K1_FE_VERIFY(r);
}
static void secp256k1_fe_impl_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b);
SECP256K1_INLINE static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b) {
SECP256K1_FE_VERIFY(a);
SECP256K1_FE_VERIFY(b);
SECP256K1_FE_VERIFY_MAGNITUDE(a, 8);
SECP256K1_FE_VERIFY_MAGNITUDE(b, 8);
VERIFY_CHECK(r != b);
VERIFY_CHECK(a != b);
secp256k1_fe_impl_mul(r, a, b);
r->magnitude = 1;
r->normalized = 0;
SECP256K1_FE_VERIFY(r);
}
static void secp256k1_fe_impl_sqr(secp256k1_fe *r, const secp256k1_fe *a);
SECP256K1_INLINE static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) {
SECP256K1_FE_VERIFY(a);
SECP256K1_FE_VERIFY_MAGNITUDE(a, 8);
secp256k1_fe_impl_sqr(r, a);
r->magnitude = 1;
r->normalized = 0;
SECP256K1_FE_VERIFY(r);
}
static void secp256k1_fe_impl_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag);
SECP256K1_INLINE static void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) {
VERIFY_CHECK(flag == 0 || flag == 1);
SECP256K1_FE_VERIFY(a);
SECP256K1_FE_VERIFY(r);
secp256k1_fe_impl_cmov(r, a, flag);
if (a->magnitude > r->magnitude) r->magnitude = a->magnitude;
if (!a->normalized) r->normalized = 0;
SECP256K1_FE_VERIFY(r);
}
static void secp256k1_fe_impl_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a);
SECP256K1_INLINE static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a) {
SECP256K1_FE_VERIFY(a);
VERIFY_CHECK(a->normalized);
secp256k1_fe_impl_to_storage(r, a);
}
static void secp256k1_fe_impl_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a);
SECP256K1_INLINE static void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a) {
secp256k1_fe_impl_from_storage(r, a);
r->magnitude = 1;
r->normalized = 1;
SECP256K1_FE_VERIFY(r);
}
static void secp256k1_fe_impl_inv(secp256k1_fe *r, const secp256k1_fe *x);
SECP256K1_INLINE static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *x) {
int input_is_zero = secp256k1_fe_normalizes_to_zero(x);
SECP256K1_FE_VERIFY(x);
secp256k1_fe_impl_inv(r, x);
r->magnitude = x->magnitude > 0;
r->normalized = 1;
VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == input_is_zero);
SECP256K1_FE_VERIFY(r);
}
static void secp256k1_fe_impl_inv_var(secp256k1_fe *r, const secp256k1_fe *x);
SECP256K1_INLINE static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *x) {
int input_is_zero = secp256k1_fe_normalizes_to_zero(x);
SECP256K1_FE_VERIFY(x);
secp256k1_fe_impl_inv_var(r, x);
r->magnitude = x->magnitude > 0;
r->normalized = 1;
VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == input_is_zero);
SECP256K1_FE_VERIFY(r);
}
static int secp256k1_fe_impl_is_square_var(const secp256k1_fe *x);
SECP256K1_INLINE static int secp256k1_fe_is_square_var(const secp256k1_fe *x) {
int ret;
secp256k1_fe tmp = *x, sqrt;
SECP256K1_FE_VERIFY(x);
ret = secp256k1_fe_impl_is_square_var(x);
secp256k1_fe_normalize_weak(&tmp);
VERIFY_CHECK(ret == secp256k1_fe_sqrt(&sqrt, &tmp));
return ret;
}
static void secp256k1_fe_impl_get_bounds(secp256k1_fe* r, int m);
SECP256K1_INLINE static void secp256k1_fe_get_bounds(secp256k1_fe* r, int m) {
VERIFY_CHECK(m >= 0);
VERIFY_CHECK(m <= 32);
secp256k1_fe_impl_get_bounds(r, m);
r->magnitude = m;
r->normalized = (m == 0);
SECP256K1_FE_VERIFY(r);
}
static void secp256k1_fe_impl_half(secp256k1_fe *r);
SECP256K1_INLINE static void secp256k1_fe_half(secp256k1_fe *r) {
SECP256K1_FE_VERIFY(r);
SECP256K1_FE_VERIFY_MAGNITUDE(r, 31);
secp256k1_fe_impl_half(r);
r->magnitude = (r->magnitude >> 1) + 1;
r->normalized = 0;
SECP256K1_FE_VERIFY(r);
}
#endif /* defined(VERIFY) */
#endif /* SECP256K1_FIELD_IMPL_H */

View File

@@ -0,0 +1,218 @@
/***********************************************************************
* Copyright (c) 2013, 2014 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_GROUP_H
#define SECP256K1_GROUP_H
#include "field.h"
/** A group element in affine coordinates on the secp256k1 curve,
* or occasionally on an isomorphic curve of the form y^2 = x^3 + 7*t^6.
* Note: For exhaustive test mode, secp256k1 is replaced by a small subgroup of a different curve.
*/
typedef struct {
secp256k1_fe x;
secp256k1_fe y;
int infinity; /* whether this represents the point at infinity */
} secp256k1_ge;
#define SECP256K1_GE_CONST(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) {SECP256K1_FE_CONST((a),(b),(c),(d),(e),(f),(g),(h)), SECP256K1_FE_CONST((i),(j),(k),(l),(m),(n),(o),(p)), 0}
#define SECP256K1_GE_CONST_INFINITY {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), 1}
/** A group element of the secp256k1 curve, in jacobian coordinates.
* Note: For exhastive test mode, secp256k1 is replaced by a small subgroup of a different curve.
*/
typedef struct {
secp256k1_fe x; /* actual X: x/z^2 */
secp256k1_fe y; /* actual Y: y/z^3 */
secp256k1_fe z;
int infinity; /* whether this represents the point at infinity */
} secp256k1_gej;
#define SECP256K1_GEJ_CONST(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) {SECP256K1_FE_CONST((a),(b),(c),(d),(e),(f),(g),(h)), SECP256K1_FE_CONST((i),(j),(k),(l),(m),(n),(o),(p)), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1), 0}
#define SECP256K1_GEJ_CONST_INFINITY {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), 1}
typedef struct {
secp256k1_fe_storage x;
secp256k1_fe_storage y;
} secp256k1_ge_storage;
#define SECP256K1_GE_STORAGE_CONST(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) {SECP256K1_FE_STORAGE_CONST((a),(b),(c),(d),(e),(f),(g),(h)), SECP256K1_FE_STORAGE_CONST((i),(j),(k),(l),(m),(n),(o),(p))}
#define SECP256K1_GE_STORAGE_CONST_GET(t) SECP256K1_FE_STORAGE_CONST_GET(t.x), SECP256K1_FE_STORAGE_CONST_GET(t.y)
/** Maximum allowed magnitudes for group element coordinates
* in affine (x, y) and jacobian (x, y, z) representation. */
#define SECP256K1_GE_X_MAGNITUDE_MAX 4
#define SECP256K1_GE_Y_MAGNITUDE_MAX 3
#define SECP256K1_GEJ_X_MAGNITUDE_MAX 4
#define SECP256K1_GEJ_Y_MAGNITUDE_MAX 4
#define SECP256K1_GEJ_Z_MAGNITUDE_MAX 1
/** Set a group element equal to the point with given X and Y coordinates */
static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const secp256k1_fe *y);
/** Set a group element (affine) equal to the point with the given X coordinate, and given oddness
* for Y. Return value indicates whether the result is valid. */
static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd);
/** Determine whether x is a valid X coordinate on the curve. */
static int secp256k1_ge_x_on_curve_var(const secp256k1_fe *x);
/** Determine whether fraction xn/xd is a valid X coordinate on the curve (xd != 0). */
static int secp256k1_ge_x_frac_on_curve_var(const secp256k1_fe *xn, const secp256k1_fe *xd);
/** Check whether a group element is the point at infinity. */
static int secp256k1_ge_is_infinity(const secp256k1_ge *a);
/** Check whether a group element is valid (i.e., on the curve). */
static int secp256k1_ge_is_valid_var(const secp256k1_ge *a);
/** Set r equal to the inverse of a (i.e., mirrored around the X axis) */
static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a);
/** Set a group element equal to another which is given in jacobian coordinates. Constant time. */
static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a);
/** Set a group element equal to another which is given in jacobian coordinates. */
static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a);
/** Set group elements r[0:len] (affine) equal to group elements a[0:len] (jacobian).
* None of the group elements in a[0:len] may be infinity. Constant time. */
static void secp256k1_ge_set_all_gej(secp256k1_ge *r, const secp256k1_gej *a, size_t len);
/** Set group elements r[0:len] (affine) equal to group elements a[0:len] (jacobian). */
static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len);
/** Bring a batch of inputs to the same global z "denominator", based on ratios between
* (omitted) z coordinates of adjacent elements.
*
* Although the elements a[i] are _ge rather than _gej, they actually represent elements
* in Jacobian coordinates with their z coordinates omitted.
*
* Using the notation z(b) to represent the omitted z coordinate of b, the array zr of
* z coordinate ratios must satisfy zr[i] == z(a[i]) / z(a[i-1]) for 0 < 'i' < len.
* The zr[0] value is unused.
*
* This function adjusts the coordinates of 'a' in place so that for all 'i', z(a[i]) == z(a[len-1]).
* In other words, the initial value of z(a[len-1]) becomes the global z "denominator". Only the
* a[i].x and a[i].y coordinates are explicitly modified; the adjustment of the omitted z coordinate is
* implicit.
*
* The coordinates of the final element a[len-1] are not changed.
*/
static void secp256k1_ge_table_set_globalz(size_t len, secp256k1_ge *a, const secp256k1_fe *zr);
/** Check two group elements (affine) for equality in variable time. */
static int secp256k1_ge_eq_var(const secp256k1_ge *a, const secp256k1_ge *b);
/** Set a group element (affine) equal to the point at infinity. */
static void secp256k1_ge_set_infinity(secp256k1_ge *r);
/** Set a group element (jacobian) equal to the point at infinity. */
static void secp256k1_gej_set_infinity(secp256k1_gej *r);
/** Set a group element (jacobian) equal to another which is given in affine coordinates. */
static void secp256k1_gej_set_ge(secp256k1_gej *r, const secp256k1_ge *a);
/** Check two group elements (jacobian) for equality in variable time. */
static int secp256k1_gej_eq_var(const secp256k1_gej *a, const secp256k1_gej *b);
/** Check two group elements (jacobian and affine) for equality in variable time. */
static int secp256k1_gej_eq_ge_var(const secp256k1_gej *a, const secp256k1_ge *b);
/** Compare the X coordinate of a group element (jacobian).
* The magnitude of the group element's X coordinate must not exceed 31. */
static int secp256k1_gej_eq_x_var(const secp256k1_fe *x, const secp256k1_gej *a);
/** Set r equal to the inverse of a (i.e., mirrored around the X axis) */
static void secp256k1_gej_neg(secp256k1_gej *r, const secp256k1_gej *a);
/** Check whether a group element is the point at infinity. */
static int secp256k1_gej_is_infinity(const secp256k1_gej *a);
/** Set r equal to the double of a. Constant time. */
static void secp256k1_gej_double(secp256k1_gej *r, const secp256k1_gej *a);
/** Set r equal to the double of a. If rzr is not-NULL this sets *rzr such that r->z == a->z * *rzr (where infinity means an implicit z = 0). */
static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr);
/** Set r equal to the sum of a and b. If rzr is non-NULL this sets *rzr such that r->z == a->z * *rzr (a cannot be infinity in that case). */
static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_gej *b, secp256k1_fe *rzr);
/** Set r equal to the sum of a and b (with b given in affine coordinates, and not infinity). */
static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b);
/** Set r equal to the sum of a and b (with b given in affine coordinates). This is more efficient
than secp256k1_gej_add_var. It is identical to secp256k1_gej_add_ge but without constant-time
guarantee, and b is allowed to be infinity. If rzr is non-NULL this sets *rzr such that r->z == a->z * *rzr (a cannot be infinity in that case). */
static void secp256k1_gej_add_ge_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, secp256k1_fe *rzr);
/** Set r equal to the sum of a and b (with the inverse of b's Z coordinate passed as bzinv). */
static void secp256k1_gej_add_zinv_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, const secp256k1_fe *bzinv);
/** Set r to be equal to lambda times a, where lambda is chosen in a way such that this is very fast. */
static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a);
/** Clear a secp256k1_gej to prevent leaking sensitive information. */
static void secp256k1_gej_clear(secp256k1_gej *r);
/** Clear a secp256k1_ge to prevent leaking sensitive information. */
static void secp256k1_ge_clear(secp256k1_ge *r);
/** Convert a group element to the storage type. */
static void secp256k1_ge_to_storage(secp256k1_ge_storage *r, const secp256k1_ge *a);
/** Convert a group element back from the storage type. */
static void secp256k1_ge_from_storage(secp256k1_ge *r, const secp256k1_ge_storage *a);
/** If flag is 1, set *r equal to *a; if flag is 0, leave it. Constant-time.
* Both *r and *a must be initialized. Flag must be 0 or 1. */
static void secp256k1_gej_cmov(secp256k1_gej *r, const secp256k1_gej *a, int flag);
/** If flag is 1, set *r equal to *a; if flag is 0, leave it. Constant-time.
* Both *r and *a must be initialized. Flag must be 0 or 1. */
static void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_ge_storage *a, int flag);
/** Rescale a jacobian point by b which must be non-zero. Constant-time. */
static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *b);
/** Convert a group element that is not infinity to a 64-byte array. The output
* array is platform-dependent. */
static void secp256k1_ge_to_bytes(unsigned char *buf, const secp256k1_ge *a);
/** Convert a 64-byte array into group element. This function assumes that the
* provided buffer correctly encodes a group element. */
static void secp256k1_ge_from_bytes(secp256k1_ge *r, const unsigned char *buf);
/** Convert a group element (that is allowed to be infinity) to a 64-byte
* array. The output array is platform-dependent. */
static void secp256k1_ge_to_bytes_ext(unsigned char *data, const secp256k1_ge *ge);
/** Convert a 64-byte array into a group element. This function assumes that the
* provided buffer is the output of secp256k1_ge_to_bytes_ext. */
static void secp256k1_ge_from_bytes_ext(secp256k1_ge *ge, const unsigned char *data);
/** Determine if a point (which is assumed to be on the curve) is in the correct (sub)group of the curve.
*
* In normal mode, the used group is secp256k1, which has cofactor=1 meaning that every point on the curve is in the
* group, and this function returns always true.
*
* When compiling in exhaustive test mode, a slightly different curve equation is used, leading to a group with a
* (very) small subgroup, and that subgroup is what is used for all cryptographic operations. In that mode, this
* function checks whether a point that is on the curve is in fact also in that subgroup.
*/
static int secp256k1_ge_is_in_correct_subgroup(const secp256k1_ge* ge);
/** Check invariants on an affine group element (no-op unless VERIFY is enabled). */
static void secp256k1_ge_verify(const secp256k1_ge *a);
#define SECP256K1_GE_VERIFY(a) secp256k1_ge_verify(a)
/** Check invariants on a Jacobian group element (no-op unless VERIFY is enabled). */
static void secp256k1_gej_verify(const secp256k1_gej *a);
#define SECP256K1_GEJ_VERIFY(a) secp256k1_gej_verify(a)
#endif /* SECP256K1_GROUP_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,54 @@
/***********************************************************************
* Copyright (c) 2014 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_HASH_H
#define SECP256K1_HASH_H
#include <stdlib.h>
#include <stdint.h>
typedef struct {
secp256k1_sha256_compression_function fn_sha256_compression;
} secp256k1_hash_ctx;
static void secp256k1_hash_ctx_init(secp256k1_hash_ctx *hash_ctx);
typedef struct {
uint32_t s[8];
unsigned char buf[64];
uint64_t bytes;
} secp256k1_sha256;
static void secp256k1_sha256_initialize(secp256k1_sha256 *hash);
/* Initialize a SHA256 hash state with a precomputed midstate.
* The byte counter must be a multiple of 64, i.e., there must be no unwritten
* bytes in the buffer. */
static void secp256k1_sha256_initialize_midstate(secp256k1_sha256 *hash, uint64_t bytes, const uint32_t state[8]);
static void secp256k1_sha256_write(const secp256k1_hash_ctx *hash_ctx, secp256k1_sha256 *hash, const unsigned char *data, size_t size);
static void secp256k1_sha256_finalize(const secp256k1_hash_ctx *hash_ctx, secp256k1_sha256 *hash, unsigned char *out32);
static void secp256k1_sha256_clear(secp256k1_sha256 *hash);
typedef struct {
secp256k1_sha256 inner, outer;
} secp256k1_hmac_sha256;
static void secp256k1_hmac_sha256_initialize(const secp256k1_hash_ctx *hash_ctx, secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t size);
static void secp256k1_hmac_sha256_write(const secp256k1_hash_ctx *hash_ctx, secp256k1_hmac_sha256 *hash, const unsigned char *data, size_t size);
static void secp256k1_hmac_sha256_finalize(const secp256k1_hash_ctx *hash_ctx, secp256k1_hmac_sha256 *hash, unsigned char *out32);
static void secp256k1_hmac_sha256_clear(secp256k1_hmac_sha256 *hash);
typedef struct {
unsigned char v[32];
unsigned char k[32];
int retry;
} secp256k1_rfc6979_hmac_sha256;
static void secp256k1_rfc6979_hmac_sha256_initialize(const secp256k1_hash_ctx *hash_ctx, secp256k1_rfc6979_hmac_sha256 *rng, const unsigned char *key, size_t keylen);
static void secp256k1_rfc6979_hmac_sha256_generate(const secp256k1_hash_ctx *hash_ctx, secp256k1_rfc6979_hmac_sha256 *rng, unsigned char *out, size_t outlen);
static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256 *rng);
static void secp256k1_rfc6979_hmac_sha256_clear(secp256k1_rfc6979_hmac_sha256 *rng);
#endif /* SECP256K1_HASH_H */

View File

@@ -0,0 +1,332 @@
/***********************************************************************
* Copyright (c) 2014 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_HASH_IMPL_H
#define SECP256K1_HASH_IMPL_H
#include "hash.h"
#include "util.h"
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#define Ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z))))
#define Maj(x,y,z) (((x) & (y)) | ((z) & ((x) | (y))))
#define Sigma0(x) (((x) >> 2 | (x) << 30) ^ ((x) >> 13 | (x) << 19) ^ ((x) >> 22 | (x) << 10))
#define Sigma1(x) (((x) >> 6 | (x) << 26) ^ ((x) >> 11 | (x) << 21) ^ ((x) >> 25 | (x) << 7))
#define sigma0(x) (((x) >> 7 | (x) << 25) ^ ((x) >> 18 | (x) << 14) ^ ((x) >> 3))
#define sigma1(x) (((x) >> 17 | (x) << 15) ^ ((x) >> 19 | (x) << 13) ^ ((x) >> 10))
#define Round(a,b,c,d,e,f,g,h,k,w) do { \
uint32_t t1 = (h) + Sigma1(e) + Ch((e), (f), (g)) + (k) + (w); \
uint32_t t2 = Sigma0(a) + Maj((a), (b), (c)); \
(d) += t1; \
(h) = t1 + t2; \
} while(0)
static void secp256k1_sha256_initialize(secp256k1_sha256 *hash) {
hash->s[0] = 0x6a09e667ul;
hash->s[1] = 0xbb67ae85ul;
hash->s[2] = 0x3c6ef372ul;
hash->s[3] = 0xa54ff53aul;
hash->s[4] = 0x510e527ful;
hash->s[5] = 0x9b05688cul;
hash->s[6] = 0x1f83d9abul;
hash->s[7] = 0x5be0cd19ul;
hash->bytes = 0;
}
static void secp256k1_sha256_initialize_midstate(secp256k1_sha256 *hash, uint64_t bytes, const uint32_t state[8]) {
VERIFY_CHECK((bytes & 0x3F) == 0);
VERIFY_CHECK(state != NULL);
memcpy(hash->s, state, sizeof(hash->s));
hash->bytes = bytes;
}
/** Perform one SHA-256 transformation, processing 16 big endian 32-bit words. */
static void secp256k1_sha256_transform_impl(uint32_t* s, const unsigned char* buf) {
uint32_t a = s[0], b = s[1], c = s[2], d = s[3], e = s[4], f = s[5], g = s[6], h = s[7];
uint32_t w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15;
Round(a, b, c, d, e, f, g, h, 0x428a2f98, w0 = secp256k1_read_be32(&buf[0]));
Round(h, a, b, c, d, e, f, g, 0x71374491, w1 = secp256k1_read_be32(&buf[4]));
Round(g, h, a, b, c, d, e, f, 0xb5c0fbcf, w2 = secp256k1_read_be32(&buf[8]));
Round(f, g, h, a, b, c, d, e, 0xe9b5dba5, w3 = secp256k1_read_be32(&buf[12]));
Round(e, f, g, h, a, b, c, d, 0x3956c25b, w4 = secp256k1_read_be32(&buf[16]));
Round(d, e, f, g, h, a, b, c, 0x59f111f1, w5 = secp256k1_read_be32(&buf[20]));
Round(c, d, e, f, g, h, a, b, 0x923f82a4, w6 = secp256k1_read_be32(&buf[24]));
Round(b, c, d, e, f, g, h, a, 0xab1c5ed5, w7 = secp256k1_read_be32(&buf[28]));
Round(a, b, c, d, e, f, g, h, 0xd807aa98, w8 = secp256k1_read_be32(&buf[32]));
Round(h, a, b, c, d, e, f, g, 0x12835b01, w9 = secp256k1_read_be32(&buf[36]));
Round(g, h, a, b, c, d, e, f, 0x243185be, w10 = secp256k1_read_be32(&buf[40]));
Round(f, g, h, a, b, c, d, e, 0x550c7dc3, w11 = secp256k1_read_be32(&buf[44]));
Round(e, f, g, h, a, b, c, d, 0x72be5d74, w12 = secp256k1_read_be32(&buf[48]));
Round(d, e, f, g, h, a, b, c, 0x80deb1fe, w13 = secp256k1_read_be32(&buf[52]));
Round(c, d, e, f, g, h, a, b, 0x9bdc06a7, w14 = secp256k1_read_be32(&buf[56]));
Round(b, c, d, e, f, g, h, a, 0xc19bf174, w15 = secp256k1_read_be32(&buf[60]));
Round(a, b, c, d, e, f, g, h, 0xe49b69c1, w0 += sigma1(w14) + w9 + sigma0(w1));
Round(h, a, b, c, d, e, f, g, 0xefbe4786, w1 += sigma1(w15) + w10 + sigma0(w2));
Round(g, h, a, b, c, d, e, f, 0x0fc19dc6, w2 += sigma1(w0) + w11 + sigma0(w3));
Round(f, g, h, a, b, c, d, e, 0x240ca1cc, w3 += sigma1(w1) + w12 + sigma0(w4));
Round(e, f, g, h, a, b, c, d, 0x2de92c6f, w4 += sigma1(w2) + w13 + sigma0(w5));
Round(d, e, f, g, h, a, b, c, 0x4a7484aa, w5 += sigma1(w3) + w14 + sigma0(w6));
Round(c, d, e, f, g, h, a, b, 0x5cb0a9dc, w6 += sigma1(w4) + w15 + sigma0(w7));
Round(b, c, d, e, f, g, h, a, 0x76f988da, w7 += sigma1(w5) + w0 + sigma0(w8));
Round(a, b, c, d, e, f, g, h, 0x983e5152, w8 += sigma1(w6) + w1 + sigma0(w9));
Round(h, a, b, c, d, e, f, g, 0xa831c66d, w9 += sigma1(w7) + w2 + sigma0(w10));
Round(g, h, a, b, c, d, e, f, 0xb00327c8, w10 += sigma1(w8) + w3 + sigma0(w11));
Round(f, g, h, a, b, c, d, e, 0xbf597fc7, w11 += sigma1(w9) + w4 + sigma0(w12));
Round(e, f, g, h, a, b, c, d, 0xc6e00bf3, w12 += sigma1(w10) + w5 + sigma0(w13));
Round(d, e, f, g, h, a, b, c, 0xd5a79147, w13 += sigma1(w11) + w6 + sigma0(w14));
Round(c, d, e, f, g, h, a, b, 0x06ca6351, w14 += sigma1(w12) + w7 + sigma0(w15));
Round(b, c, d, e, f, g, h, a, 0x14292967, w15 += sigma1(w13) + w8 + sigma0(w0));
Round(a, b, c, d, e, f, g, h, 0x27b70a85, w0 += sigma1(w14) + w9 + sigma0(w1));
Round(h, a, b, c, d, e, f, g, 0x2e1b2138, w1 += sigma1(w15) + w10 + sigma0(w2));
Round(g, h, a, b, c, d, e, f, 0x4d2c6dfc, w2 += sigma1(w0) + w11 + sigma0(w3));
Round(f, g, h, a, b, c, d, e, 0x53380d13, w3 += sigma1(w1) + w12 + sigma0(w4));
Round(e, f, g, h, a, b, c, d, 0x650a7354, w4 += sigma1(w2) + w13 + sigma0(w5));
Round(d, e, f, g, h, a, b, c, 0x766a0abb, w5 += sigma1(w3) + w14 + sigma0(w6));
Round(c, d, e, f, g, h, a, b, 0x81c2c92e, w6 += sigma1(w4) + w15 + sigma0(w7));
Round(b, c, d, e, f, g, h, a, 0x92722c85, w7 += sigma1(w5) + w0 + sigma0(w8));
Round(a, b, c, d, e, f, g, h, 0xa2bfe8a1, w8 += sigma1(w6) + w1 + sigma0(w9));
Round(h, a, b, c, d, e, f, g, 0xa81a664b, w9 += sigma1(w7) + w2 + sigma0(w10));
Round(g, h, a, b, c, d, e, f, 0xc24b8b70, w10 += sigma1(w8) + w3 + sigma0(w11));
Round(f, g, h, a, b, c, d, e, 0xc76c51a3, w11 += sigma1(w9) + w4 + sigma0(w12));
Round(e, f, g, h, a, b, c, d, 0xd192e819, w12 += sigma1(w10) + w5 + sigma0(w13));
Round(d, e, f, g, h, a, b, c, 0xd6990624, w13 += sigma1(w11) + w6 + sigma0(w14));
Round(c, d, e, f, g, h, a, b, 0xf40e3585, w14 += sigma1(w12) + w7 + sigma0(w15));
Round(b, c, d, e, f, g, h, a, 0x106aa070, w15 += sigma1(w13) + w8 + sigma0(w0));
Round(a, b, c, d, e, f, g, h, 0x19a4c116, w0 += sigma1(w14) + w9 + sigma0(w1));
Round(h, a, b, c, d, e, f, g, 0x1e376c08, w1 += sigma1(w15) + w10 + sigma0(w2));
Round(g, h, a, b, c, d, e, f, 0x2748774c, w2 += sigma1(w0) + w11 + sigma0(w3));
Round(f, g, h, a, b, c, d, e, 0x34b0bcb5, w3 += sigma1(w1) + w12 + sigma0(w4));
Round(e, f, g, h, a, b, c, d, 0x391c0cb3, w4 += sigma1(w2) + w13 + sigma0(w5));
Round(d, e, f, g, h, a, b, c, 0x4ed8aa4a, w5 += sigma1(w3) + w14 + sigma0(w6));
Round(c, d, e, f, g, h, a, b, 0x5b9cca4f, w6 += sigma1(w4) + w15 + sigma0(w7));
Round(b, c, d, e, f, g, h, a, 0x682e6ff3, w7 += sigma1(w5) + w0 + sigma0(w8));
Round(a, b, c, d, e, f, g, h, 0x748f82ee, w8 += sigma1(w6) + w1 + sigma0(w9));
Round(h, a, b, c, d, e, f, g, 0x78a5636f, w9 += sigma1(w7) + w2 + sigma0(w10));
Round(g, h, a, b, c, d, e, f, 0x84c87814, w10 += sigma1(w8) + w3 + sigma0(w11));
Round(f, g, h, a, b, c, d, e, 0x8cc70208, w11 += sigma1(w9) + w4 + sigma0(w12));
Round(e, f, g, h, a, b, c, d, 0x90befffa, w12 += sigma1(w10) + w5 + sigma0(w13));
Round(d, e, f, g, h, a, b, c, 0xa4506ceb, w13 += sigma1(w11) + w6 + sigma0(w14));
Round(c, d, e, f, g, h, a, b, 0xbef9a3f7, w14 + sigma1(w12) + w7 + sigma0(w15));
Round(b, c, d, e, f, g, h, a, 0xc67178f2, w15 + sigma1(w13) + w8 + sigma0(w0));
s[0] += a;
s[1] += b;
s[2] += c;
s[3] += d;
s[4] += e;
s[5] += f;
s[6] += g;
s[7] += h;
}
static void secp256k1_sha256_transform(uint32_t *state, const unsigned char *blocks64, size_t n_blocks) {
while (n_blocks--) {
secp256k1_sha256_transform_impl(state, blocks64);
blocks64 += 64;
}
}
static void secp256k1_hash_ctx_init(secp256k1_hash_ctx *hash_ctx) {
VERIFY_CHECK(hash_ctx != NULL);
hash_ctx->fn_sha256_compression = secp256k1_sha256_transform;
}
static void secp256k1_sha256_write(const secp256k1_hash_ctx *hash_ctx, secp256k1_sha256 *hash, const unsigned char *data, size_t len) {
size_t chunk_len;
size_t bufsize = hash->bytes & 0x3F;
hash->bytes += len;
VERIFY_CHECK(hash->bytes >= len);
VERIFY_CHECK(hash_ctx != NULL);
VERIFY_CHECK(hash_ctx->fn_sha256_compression != NULL);
/* If we exceed the 64-byte block size with this input, process it and wipe the buffer */
chunk_len = 64 - bufsize;
if (bufsize && len >= chunk_len) {
memcpy(hash->buf + bufsize, data, chunk_len);
data += chunk_len;
len -= chunk_len;
hash_ctx->fn_sha256_compression(hash->s, hash->buf, 1);
bufsize = 0;
}
/* If we still have data to process, invoke compression directly on the input */
if (len >= 64) {
const size_t n_blocks = len / 64;
const size_t advance = n_blocks * 64;
hash_ctx->fn_sha256_compression(hash->s, data, n_blocks);
data += advance;
len -= advance;
}
/* Fill the buffer with what remains */
if (len) {
memcpy(hash->buf + bufsize, data, len);
}
}
static void secp256k1_sha256_finalize(const secp256k1_hash_ctx *hash_ctx, secp256k1_sha256 *hash, unsigned char *out32) {
static const unsigned char pad[64] = {0x80};
unsigned char sizedesc[8];
int i;
/* The maximum message size of SHA256 is 2^64-1 bits. */
VERIFY_CHECK(hash->bytes < ((uint64_t)1 << 61));
secp256k1_write_be32(&sizedesc[0], hash->bytes >> 29);
secp256k1_write_be32(&sizedesc[4], hash->bytes << 3);
secp256k1_sha256_write(hash_ctx, hash, pad, 1 + ((119 - (hash->bytes % 64)) % 64));
secp256k1_sha256_write(hash_ctx, hash, sizedesc, 8);
for (i = 0; i < 8; i++) {
secp256k1_write_be32(&out32[4*i], hash->s[i]);
hash->s[i] = 0;
}
}
/* Initializes a sha256 struct and writes the 64 byte string
* SHA256(tag)||SHA256(tag) into it. */
static void secp256k1_sha256_initialize_tagged(const secp256k1_hash_ctx *hash_ctx, secp256k1_sha256 *hash, const unsigned char *tag, size_t taglen) {
unsigned char buf[32];
secp256k1_sha256_initialize(hash);
secp256k1_sha256_write(hash_ctx, hash, tag, taglen);
secp256k1_sha256_finalize(hash_ctx, hash, buf);
secp256k1_sha256_initialize(hash);
secp256k1_sha256_write(hash_ctx, hash, buf, 32);
secp256k1_sha256_write(hash_ctx, hash, buf, 32);
}
static void secp256k1_sha256_clear(secp256k1_sha256 *hash) {
secp256k1_memclear_explicit(hash, sizeof(*hash));
}
static void secp256k1_hmac_sha256_initialize(const secp256k1_hash_ctx *hash_ctx, secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t keylen) {
size_t n;
unsigned char rkey[64];
if (keylen <= sizeof(rkey)) {
memcpy(rkey, key, keylen);
memset(rkey + keylen, 0, sizeof(rkey) - keylen);
} else {
secp256k1_sha256 sha256;
secp256k1_sha256_initialize(&sha256);
secp256k1_sha256_write(hash_ctx, &sha256, key, keylen);
secp256k1_sha256_finalize(hash_ctx, &sha256, rkey);
memset(rkey + 32, 0, 32);
}
secp256k1_sha256_initialize(&hash->outer);
for (n = 0; n < sizeof(rkey); n++) {
rkey[n] ^= 0x5c;
}
secp256k1_sha256_write(hash_ctx, &hash->outer, rkey, sizeof(rkey));
secp256k1_sha256_initialize(&hash->inner);
for (n = 0; n < sizeof(rkey); n++) {
rkey[n] ^= 0x5c ^ 0x36;
}
secp256k1_sha256_write(hash_ctx, &hash->inner, rkey, sizeof(rkey));
secp256k1_memclear_explicit(rkey, sizeof(rkey));
}
static void secp256k1_hmac_sha256_write(const secp256k1_hash_ctx *hash_ctx, secp256k1_hmac_sha256 *hash, const unsigned char *data, size_t size) {
secp256k1_sha256_write(hash_ctx, &hash->inner, data, size);
}
static void secp256k1_hmac_sha256_finalize(const secp256k1_hash_ctx *hash_ctx, secp256k1_hmac_sha256 *hash, unsigned char *out32) {
unsigned char temp[32];
secp256k1_sha256_finalize(hash_ctx, &hash->inner, temp);
secp256k1_sha256_write(hash_ctx, &hash->outer, temp, 32);
secp256k1_memclear_explicit(temp, sizeof(temp));
secp256k1_sha256_finalize(hash_ctx, &hash->outer, out32);
}
static void secp256k1_hmac_sha256_clear(secp256k1_hmac_sha256 *hash) {
secp256k1_memclear_explicit(hash, sizeof(*hash));
}
static void secp256k1_rfc6979_hmac_sha256_initialize(const secp256k1_hash_ctx *hash_ctx, secp256k1_rfc6979_hmac_sha256 *rng, const unsigned char *key, size_t keylen) {
secp256k1_hmac_sha256 hmac;
static const unsigned char zero[1] = {0x00};
static const unsigned char one[1] = {0x01};
memset(rng->v, 0x01, 32); /* RFC6979 3.2.b. */
memset(rng->k, 0x00, 32); /* RFC6979 3.2.c. */
/* RFC6979 3.2.d. */
secp256k1_hmac_sha256_initialize(hash_ctx, &hmac, rng->k, 32);
secp256k1_hmac_sha256_write(hash_ctx, &hmac, rng->v, 32);
secp256k1_hmac_sha256_write(hash_ctx, &hmac, zero, 1);
secp256k1_hmac_sha256_write(hash_ctx, &hmac, key, keylen);
secp256k1_hmac_sha256_finalize(hash_ctx, &hmac, rng->k);
secp256k1_hmac_sha256_initialize(hash_ctx, &hmac, rng->k, 32);
secp256k1_hmac_sha256_write(hash_ctx, &hmac, rng->v, 32);
secp256k1_hmac_sha256_finalize(hash_ctx, &hmac, rng->v);
/* RFC6979 3.2.f. */
secp256k1_hmac_sha256_initialize(hash_ctx, &hmac, rng->k, 32);
secp256k1_hmac_sha256_write(hash_ctx, &hmac, rng->v, 32);
secp256k1_hmac_sha256_write(hash_ctx, &hmac, one, 1);
secp256k1_hmac_sha256_write(hash_ctx, &hmac, key, keylen);
secp256k1_hmac_sha256_finalize(hash_ctx, &hmac, rng->k);
secp256k1_hmac_sha256_initialize(hash_ctx, &hmac, rng->k, 32);
secp256k1_hmac_sha256_write(hash_ctx, &hmac, rng->v, 32);
secp256k1_hmac_sha256_finalize(hash_ctx, &hmac, rng->v);
rng->retry = 0;
}
static void secp256k1_rfc6979_hmac_sha256_generate(const secp256k1_hash_ctx *hash_ctx, secp256k1_rfc6979_hmac_sha256 *rng, unsigned char *out, size_t outlen) {
/* RFC6979 3.2.h. */
static const unsigned char zero[1] = {0x00};
if (rng->retry) {
secp256k1_hmac_sha256 hmac;
secp256k1_hmac_sha256_initialize(hash_ctx, &hmac, rng->k, 32);
secp256k1_hmac_sha256_write(hash_ctx, &hmac, rng->v, 32);
secp256k1_hmac_sha256_write(hash_ctx, &hmac, zero, 1);
secp256k1_hmac_sha256_finalize(hash_ctx, &hmac, rng->k);
secp256k1_hmac_sha256_initialize(hash_ctx, &hmac, rng->k, 32);
secp256k1_hmac_sha256_write(hash_ctx, &hmac, rng->v, 32);
secp256k1_hmac_sha256_finalize(hash_ctx, &hmac, rng->v);
}
while (outlen > 0) {
secp256k1_hmac_sha256 hmac;
size_t now = outlen;
secp256k1_hmac_sha256_initialize(hash_ctx, &hmac, rng->k, 32);
secp256k1_hmac_sha256_write(hash_ctx, &hmac, rng->v, 32);
secp256k1_hmac_sha256_finalize(hash_ctx, &hmac, rng->v);
if (now > 32) {
now = 32;
}
memcpy(out, rng->v, now);
out += now;
outlen -= now;
}
rng->retry = 1;
}
static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256 *rng) {
(void) rng;
}
static void secp256k1_rfc6979_hmac_sha256_clear(secp256k1_rfc6979_hmac_sha256 *rng) {
secp256k1_memclear_explicit(rng, sizeof(*rng));
}
#undef Round
#undef sigma1
#undef sigma0
#undef Sigma1
#undef Sigma0
#undef Maj
#undef Ch
#endif /* SECP256K1_HASH_IMPL_H */

View File

@@ -0,0 +1,33 @@
/***********************************************************************
* Copyright (c) 2021 Russell O'Connor, Jonas Nick *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_HSORT_H
#define SECP256K1_HSORT_H
#include <stddef.h>
#include <string.h>
/* In-place, iterative heapsort with an interface matching glibc's qsort_r. This
* is preferred over standard library implementations because they generally
* make no guarantee about being fast for malicious inputs.
* Remember that heapsort is unstable.
*
* In/Out: ptr: pointer to the array to sort. The contents of the array are
* sorted in ascending order according to the comparison function.
* In: count: number of elements in the array.
* size: size in bytes of each element.
* cmp: pointer to a comparison function that is called with two
* arguments that point to the objects being compared. The cmp_data
* argument of secp256k1_hsort is passed as third argument. The
* function must return an integer less than, equal to, or greater
* than zero if the first argument is considered to be respectively
* less than, equal to, or greater than the second.
* cmp_data: pointer passed as third argument to cmp.
*/
static void secp256k1_hsort(void *ptr, size_t count, size_t size,
int (*cmp)(const void *, const void *, void *),
void *cmp_data);
#endif

View File

@@ -0,0 +1,125 @@
/***********************************************************************
* Copyright (c) 2021 Russell O'Connor, Jonas Nick *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_HSORT_IMPL_H
#define SECP256K1_HSORT_IMPL_H
#include "hsort.h"
/* An array is a heap when, for all non-zero indexes i, the element at index i
* compares as less than or equal to the element at index parent(i) = (i-1)/2.
*/
static SECP256K1_INLINE size_t secp256k1_heap_child1(size_t i) {
VERIFY_CHECK(i <= (SIZE_MAX - 1)/2);
return 2*i + 1;
}
static SECP256K1_INLINE size_t secp256k1_heap_child2(size_t i) {
VERIFY_CHECK(i <= SIZE_MAX/2 - 1);
return secp256k1_heap_child1(i)+1;
}
static SECP256K1_INLINE void secp256k1_heap_swap64(unsigned char *a, unsigned char *b, size_t len) {
unsigned char tmp[64];
VERIFY_CHECK(len <= 64);
memcpy(tmp, a, len);
memmove(a, b, len);
memcpy(b, tmp, len);
}
static SECP256K1_INLINE void secp256k1_heap_swap(unsigned char *arr, size_t i, size_t j, size_t stride) {
unsigned char *a = arr + i*stride;
unsigned char *b = arr + j*stride;
size_t len = stride;
while (64 < len) {
secp256k1_heap_swap64(a + (len - 64), b + (len - 64), 64);
len -= 64;
}
secp256k1_heap_swap64(a, b, len);
}
/* This function accepts an array arr containing heap_size elements, each of
* size stride. The elements in the array at indices >i satisfy the max-heap
* property, i.e., for any element at index j (where j > i), all of its children
* are smaller than the element itself. The purpose of the function is to update
* the array so that all elements at indices >=i satisfy the max-heap
* property. */
static SECP256K1_INLINE void secp256k1_heap_down(unsigned char *arr, size_t i, size_t heap_size, size_t stride,
int (*cmp)(const void *, const void *, void *), void *cmp_data) {
while (i < heap_size/2) {
VERIFY_CHECK(i <= SIZE_MAX/2 - 1);
/* Proof:
* i < heap_size/2
* i + 1 <= heap_size/2
* 2*i + 2 <= heap_size <= SIZE_MAX
* 2*i <= SIZE_MAX - 2
*/
VERIFY_CHECK(secp256k1_heap_child1(i) < heap_size);
/* Proof:
* i < heap_size/2
* i + 1 <= heap_size/2
* 2*i + 2 <= heap_size
* 2*i + 1 < heap_size
* child1(i) < heap_size
*/
/* Let [x] be notation for the contents at arr[x*stride].
*
* If [child1(i)] > [i] and [child2(i)] > [i],
* swap [i] with the larger child to ensure the new parent is larger
* than both children. When [child1(i)] == [child2(i)], swap [i] with
* [child2(i)].
* Else if [child1(i)] > [i], swap [i] with [child1(i)].
* Else if [child2(i)] > [i], swap [i] with [child2(i)].
*/
if (secp256k1_heap_child2(i) < heap_size
&& 0 <= cmp(arr + secp256k1_heap_child2(i)*stride, arr + secp256k1_heap_child1(i)*stride, cmp_data)) {
if (0 < cmp(arr + secp256k1_heap_child2(i)*stride, arr + i*stride, cmp_data)) {
secp256k1_heap_swap(arr, i, secp256k1_heap_child2(i), stride);
i = secp256k1_heap_child2(i);
} else {
/* At this point we have [child2(i)] >= [child1(i)] and we have
* [child2(i)] <= [i], and thus [child1(i)] <= [i] which means
* that the next comparison can be skipped. */
return;
}
} else if (0 < cmp(arr + secp256k1_heap_child1(i)*stride, arr + i*stride, cmp_data)) {
secp256k1_heap_swap(arr, i, secp256k1_heap_child1(i), stride);
i = secp256k1_heap_child1(i);
} else {
return;
}
}
/* heap_size/2 <= i
* heap_size/2 < i + 1
* heap_size < 2*i + 2
* heap_size <= 2*i + 1
* heap_size <= child1(i)
* Thus child1(i) and child2(i) are now out of bounds and we are at a leaf.
*/
}
/* In-place heap sort. */
static void secp256k1_hsort(void *ptr, size_t count, size_t size,
int (*cmp)(const void *, const void *, void *),
void *cmp_data) {
size_t i;
for (i = count/2; 0 < i; --i) {
secp256k1_heap_down(ptr, i-1, count, size, cmp, cmp_data);
}
for (i = count; 1 < i; --i) {
/* Extract the largest value from the heap */
secp256k1_heap_swap(ptr, 0, i-1, size);
/* Repair the heap condition */
secp256k1_heap_down(ptr, 0, i-1, size, cmp, cmp_data);
}
}
#endif

View File

@@ -0,0 +1,90 @@
#ifndef SECP256K1_INT128_H
#define SECP256K1_INT128_H
#include "util.h"
#if defined(SECP256K1_WIDEMUL_INT128)
# if defined(SECP256K1_INT128_NATIVE)
# include "int128_native.h"
# elif defined(SECP256K1_INT128_STRUCT)
# include "int128_struct.h"
# else
# error "Please select int128 implementation"
# endif
/* Construct an unsigned 128-bit value from a high and a low 64-bit value. */
static SECP256K1_INLINE void secp256k1_u128_load(secp256k1_uint128 *r, uint64_t hi, uint64_t lo);
/* Multiply two unsigned 64-bit values a and b and write the result to r. */
static SECP256K1_INLINE void secp256k1_u128_mul(secp256k1_uint128 *r, uint64_t a, uint64_t b);
/* Multiply two unsigned 64-bit values a and b and add the result to r.
* The final result is taken modulo 2^128.
*/
static SECP256K1_INLINE void secp256k1_u128_accum_mul(secp256k1_uint128 *r, uint64_t a, uint64_t b);
/* Add an unsigned 64-bit value a to r.
* The final result is taken modulo 2^128.
*/
static SECP256K1_INLINE void secp256k1_u128_accum_u64(secp256k1_uint128 *r, uint64_t a);
/* Unsigned (logical) right shift.
* Non-constant time in n.
*/
static SECP256K1_INLINE void secp256k1_u128_rshift(secp256k1_uint128 *r, unsigned int n);
/* Return the low 64-bits of a 128-bit value as an unsigned 64-bit value. */
static SECP256K1_INLINE uint64_t secp256k1_u128_to_u64(const secp256k1_uint128 *a);
/* Return the high 64-bits of a 128-bit value as an unsigned 64-bit value. */
static SECP256K1_INLINE uint64_t secp256k1_u128_hi_u64(const secp256k1_uint128 *a);
/* Write an unsigned 64-bit value to r. */
static SECP256K1_INLINE void secp256k1_u128_from_u64(secp256k1_uint128 *r, uint64_t a);
/* Tests if r is strictly less than to 2^n.
* n must be strictly less than 128.
*/
static SECP256K1_INLINE int secp256k1_u128_check_bits(const secp256k1_uint128 *r, unsigned int n);
/* Construct an signed 128-bit value from a high and a low 64-bit value. */
static SECP256K1_INLINE void secp256k1_i128_load(secp256k1_int128 *r, int64_t hi, uint64_t lo);
/* Multiply two signed 64-bit values a and b and write the result to r. */
static SECP256K1_INLINE void secp256k1_i128_mul(secp256k1_int128 *r, int64_t a, int64_t b);
/* Multiply two signed 64-bit values a and b and add the result to r.
* Overflow or underflow from the addition is undefined behaviour.
*/
static SECP256K1_INLINE void secp256k1_i128_accum_mul(secp256k1_int128 *r, int64_t a, int64_t b);
/* Compute a*d - b*c from signed 64-bit values and write the result to r. */
static SECP256K1_INLINE void secp256k1_i128_det(secp256k1_int128 *r, int64_t a, int64_t b, int64_t c, int64_t d);
/* Signed (arithmetic) right shift.
* Non-constant time in b.
*/
static SECP256K1_INLINE void secp256k1_i128_rshift(secp256k1_int128 *r, unsigned int b);
/* Return the input value modulo 2^64. */
static SECP256K1_INLINE uint64_t secp256k1_i128_to_u64(const secp256k1_int128 *a);
/* Return the value as a signed 64-bit value.
* Requires the input to be between INT64_MIN and INT64_MAX.
*/
static SECP256K1_INLINE int64_t secp256k1_i128_to_i64(const secp256k1_int128 *a);
/* Write a signed 64-bit value to r. */
static SECP256K1_INLINE void secp256k1_i128_from_i64(secp256k1_int128 *r, int64_t a);
/* Compare two 128-bit values for equality. */
static SECP256K1_INLINE int secp256k1_i128_eq_var(const secp256k1_int128 *a, const secp256k1_int128 *b);
/* Tests if r is equal to sign*2^n (sign must be 1 or -1).
* n must be strictly less than 127.
*/
static SECP256K1_INLINE int secp256k1_i128_check_pow2(const secp256k1_int128 *r, unsigned int n, int sign);
#endif
#endif

View File

@@ -0,0 +1,18 @@
#ifndef SECP256K1_INT128_IMPL_H
#define SECP256K1_INT128_IMPL_H
#include "util.h"
#include "int128.h"
#if defined(SECP256K1_WIDEMUL_INT128)
# if defined(SECP256K1_INT128_NATIVE)
# include "int128_native_impl.h"
# elif defined(SECP256K1_INT128_STRUCT)
# include "int128_struct_impl.h"
# else
# error "Please select int128 implementation"
# endif
#endif
#endif

View File

@@ -0,0 +1,19 @@
#ifndef SECP256K1_INT128_NATIVE_H
#define SECP256K1_INT128_NATIVE_H
#include <stdint.h>
#include "util.h"
#if !defined(UINT128_MAX) && defined(__SIZEOF_INT128__)
SECP256K1_GNUC_EXT typedef unsigned __int128 uint128_t;
SECP256K1_GNUC_EXT typedef __int128 int128_t;
# define UINT128_MAX ((uint128_t)(-1))
# define INT128_MAX ((int128_t)(UINT128_MAX >> 1))
# define INT128_MIN (-INT128_MAX - 1)
/* No (U)INT128_C macros because compilers providing __int128 do not support 128-bit literals. */
#endif
typedef uint128_t secp256k1_uint128;
typedef int128_t secp256k1_int128;
#endif

View File

@@ -0,0 +1,94 @@
#ifndef SECP256K1_INT128_NATIVE_IMPL_H
#define SECP256K1_INT128_NATIVE_IMPL_H
#include "int128.h"
#include "util.h"
static SECP256K1_INLINE void secp256k1_u128_load(secp256k1_uint128 *r, uint64_t hi, uint64_t lo) {
*r = (((uint128_t)hi) << 64) + lo;
}
static SECP256K1_INLINE void secp256k1_u128_mul(secp256k1_uint128 *r, uint64_t a, uint64_t b) {
*r = (uint128_t)a * b;
}
static SECP256K1_INLINE void secp256k1_u128_accum_mul(secp256k1_uint128 *r, uint64_t a, uint64_t b) {
*r += (uint128_t)a * b;
}
static SECP256K1_INLINE void secp256k1_u128_accum_u64(secp256k1_uint128 *r, uint64_t a) {
*r += a;
}
static SECP256K1_INLINE void secp256k1_u128_rshift(secp256k1_uint128 *r, unsigned int n) {
VERIFY_CHECK(n < 128);
*r >>= n;
}
static SECP256K1_INLINE uint64_t secp256k1_u128_to_u64(const secp256k1_uint128 *a) {
return (uint64_t)(*a);
}
static SECP256K1_INLINE uint64_t secp256k1_u128_hi_u64(const secp256k1_uint128 *a) {
return (uint64_t)(*a >> 64);
}
static SECP256K1_INLINE void secp256k1_u128_from_u64(secp256k1_uint128 *r, uint64_t a) {
*r = a;
}
static SECP256K1_INLINE int secp256k1_u128_check_bits(const secp256k1_uint128 *r, unsigned int n) {
VERIFY_CHECK(n < 128);
return (*r >> n == 0);
}
static SECP256K1_INLINE void secp256k1_i128_load(secp256k1_int128 *r, int64_t hi, uint64_t lo) {
*r = (((uint128_t)(uint64_t)hi) << 64) + lo;
}
static SECP256K1_INLINE void secp256k1_i128_mul(secp256k1_int128 *r, int64_t a, int64_t b) {
*r = (int128_t)a * b;
}
static SECP256K1_INLINE void secp256k1_i128_accum_mul(secp256k1_int128 *r, int64_t a, int64_t b) {
int128_t ab = (int128_t)a * b;
VERIFY_CHECK(0 <= ab ? *r <= INT128_MAX - ab : INT128_MIN - ab <= *r);
*r += ab;
}
static SECP256K1_INLINE void secp256k1_i128_det(secp256k1_int128 *r, int64_t a, int64_t b, int64_t c, int64_t d) {
int128_t ad = (int128_t)a * d;
int128_t bc = (int128_t)b * c;
VERIFY_CHECK(0 <= bc ? INT128_MIN + bc <= ad : ad <= INT128_MAX + bc);
*r = ad - bc;
}
static SECP256K1_INLINE void secp256k1_i128_rshift(secp256k1_int128 *r, unsigned int n) {
VERIFY_CHECK(n < 128);
*r >>= n;
}
static SECP256K1_INLINE uint64_t secp256k1_i128_to_u64(const secp256k1_int128 *a) {
return (uint64_t)*a;
}
static SECP256K1_INLINE int64_t secp256k1_i128_to_i64(const secp256k1_int128 *a) {
VERIFY_CHECK(INT64_MIN <= *a && *a <= INT64_MAX);
return *a;
}
static SECP256K1_INLINE void secp256k1_i128_from_i64(secp256k1_int128 *r, int64_t a) {
*r = a;
}
static SECP256K1_INLINE int secp256k1_i128_eq_var(const secp256k1_int128 *a, const secp256k1_int128 *b) {
return *a == *b;
}
static SECP256K1_INLINE int secp256k1_i128_check_pow2(const secp256k1_int128 *r, unsigned int n, int sign) {
VERIFY_CHECK(n < 127);
VERIFY_CHECK(sign == 1 || sign == -1);
return (*r == (int128_t)((uint128_t)sign << n));
}
#endif

View File

@@ -0,0 +1,14 @@
#ifndef SECP256K1_INT128_STRUCT_H
#define SECP256K1_INT128_STRUCT_H
#include <stdint.h>
#include "util.h"
typedef struct {
uint64_t lo;
uint64_t hi;
} secp256k1_uint128;
typedef secp256k1_uint128 secp256k1_int128;
#endif

View File

@@ -0,0 +1,205 @@
#ifndef SECP256K1_INT128_STRUCT_IMPL_H
#define SECP256K1_INT128_STRUCT_IMPL_H
#include "int128.h"
#include "util.h"
#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64)) /* MSVC */
# include <intrin.h>
# if defined(_M_ARM64) || defined(SECP256K1_MSVC_MULH_TEST_OVERRIDE)
/* On ARM64 MSVC, use __(u)mulh for the upper half of 64x64 multiplications.
(Define SECP256K1_MSVC_MULH_TEST_OVERRIDE to test this code path on X64,
which supports both __(u)mulh and _umul128.) */
# if defined(SECP256K1_MSVC_MULH_TEST_OVERRIDE)
# pragma message(__FILE__ ": SECP256K1_MSVC_MULH_TEST_OVERRIDE is defined, forcing use of __(u)mulh.")
# endif
static SECP256K1_INLINE uint64_t secp256k1_umul128(uint64_t a, uint64_t b, uint64_t* hi) {
*hi = __umulh(a, b);
return a * b;
}
static SECP256K1_INLINE int64_t secp256k1_mul128(int64_t a, int64_t b, int64_t* hi) {
*hi = __mulh(a, b);
return (uint64_t)a * (uint64_t)b;
}
# else
/* On x84_64 MSVC, use native _(u)mul128 for 64x64->128 multiplications. */
# define secp256k1_umul128 _umul128
# define secp256k1_mul128 _mul128
# endif
#else
/* On other systems, emulate 64x64->128 multiplications using 32x32->64 multiplications. */
static SECP256K1_INLINE uint64_t secp256k1_umul128(uint64_t a, uint64_t b, uint64_t* hi) {
uint64_t ll = (uint64_t)(uint32_t)a * (uint32_t)b;
uint64_t lh = (uint32_t)a * (b >> 32);
uint64_t hl = (a >> 32) * (uint32_t)b;
uint64_t hh = (a >> 32) * (b >> 32);
uint64_t mid34 = (ll >> 32) + (uint32_t)lh + (uint32_t)hl;
*hi = hh + (lh >> 32) + (hl >> 32) + (mid34 >> 32);
return (mid34 << 32) + (uint32_t)ll;
}
static SECP256K1_INLINE int64_t secp256k1_mul128(int64_t a, int64_t b, int64_t* hi) {
uint64_t ll = (uint64_t)(uint32_t)a * (uint32_t)b;
int64_t lh = (uint32_t)a * (b >> 32);
int64_t hl = (a >> 32) * (uint32_t)b;
int64_t hh = (a >> 32) * (b >> 32);
uint64_t mid34 = (ll >> 32) + (uint32_t)lh + (uint32_t)hl;
*hi = hh + (lh >> 32) + (hl >> 32) + (mid34 >> 32);
return (mid34 << 32) + (uint32_t)ll;
}
#endif
static SECP256K1_INLINE void secp256k1_u128_load(secp256k1_uint128 *r, uint64_t hi, uint64_t lo) {
r->hi = hi;
r->lo = lo;
}
static SECP256K1_INLINE void secp256k1_u128_mul(secp256k1_uint128 *r, uint64_t a, uint64_t b) {
r->lo = secp256k1_umul128(a, b, &r->hi);
}
static SECP256K1_INLINE void secp256k1_u128_accum_mul(secp256k1_uint128 *r, uint64_t a, uint64_t b) {
uint64_t lo, hi;
lo = secp256k1_umul128(a, b, &hi);
r->lo += lo;
r->hi += hi + (r->lo < lo);
}
static SECP256K1_INLINE void secp256k1_u128_accum_u64(secp256k1_uint128 *r, uint64_t a) {
r->lo += a;
r->hi += r->lo < a;
}
/* Unsigned (logical) right shift.
* Non-constant time in n.
*/
static SECP256K1_INLINE void secp256k1_u128_rshift(secp256k1_uint128 *r, unsigned int n) {
VERIFY_CHECK(n < 128);
if (n >= 64) {
r->lo = r->hi >> (n-64);
r->hi = 0;
} else if (n > 0) {
#if defined(_MSC_VER) && defined(_M_X64)
VERIFY_CHECK(n < 64);
r->lo = __shiftright128(r->lo, r->hi, n);
#else
r->lo = ((1U * r->hi) << (64-n)) | r->lo >> n;
#endif
r->hi >>= n;
}
}
static SECP256K1_INLINE uint64_t secp256k1_u128_to_u64(const secp256k1_uint128 *a) {
return a->lo;
}
static SECP256K1_INLINE uint64_t secp256k1_u128_hi_u64(const secp256k1_uint128 *a) {
return a->hi;
}
static SECP256K1_INLINE void secp256k1_u128_from_u64(secp256k1_uint128 *r, uint64_t a) {
r->hi = 0;
r->lo = a;
}
static SECP256K1_INLINE int secp256k1_u128_check_bits(const secp256k1_uint128 *r, unsigned int n) {
VERIFY_CHECK(n < 128);
return n >= 64 ? r->hi >> (n - 64) == 0
: r->hi == 0 && r->lo >> n == 0;
}
static SECP256K1_INLINE void secp256k1_i128_load(secp256k1_int128 *r, int64_t hi, uint64_t lo) {
r->hi = hi;
r->lo = lo;
}
static SECP256K1_INLINE void secp256k1_i128_mul(secp256k1_int128 *r, int64_t a, int64_t b) {
int64_t hi;
r->lo = (uint64_t)secp256k1_mul128(a, b, &hi);
r->hi = (uint64_t)hi;
}
static SECP256K1_INLINE void secp256k1_i128_accum_mul(secp256k1_int128 *r, int64_t a, int64_t b) {
int64_t hi;
uint64_t lo = (uint64_t)secp256k1_mul128(a, b, &hi);
r->lo += lo;
hi += r->lo < lo;
/* Verify no overflow.
* If r represents a positive value (the sign bit is not set) and the value we are adding is a positive value (the sign bit is not set),
* then we require that the resulting value also be positive (the sign bit is not set).
* Note that (X <= Y) means (X implies Y) when X and Y are boolean values (i.e. 0 or 1).
*/
VERIFY_CHECK((r->hi <= 0x7fffffffffffffffu && (uint64_t)hi <= 0x7fffffffffffffffu) <= (r->hi + (uint64_t)hi <= 0x7fffffffffffffffu));
/* Verify no underflow.
* If r represents a negative value (the sign bit is set) and the value we are adding is a negative value (the sign bit is set),
* then we require that the resulting value also be negative (the sign bit is set).
*/
VERIFY_CHECK((r->hi > 0x7fffffffffffffffu && (uint64_t)hi > 0x7fffffffffffffffu) <= (r->hi + (uint64_t)hi > 0x7fffffffffffffffu));
r->hi += hi;
}
static SECP256K1_INLINE void secp256k1_i128_dissip_mul(secp256k1_int128 *r, int64_t a, int64_t b) {
int64_t hi;
uint64_t lo = (uint64_t)secp256k1_mul128(a, b, &hi);
hi += r->lo < lo;
/* Verify no overflow.
* If r represents a positive value (the sign bit is not set) and the value we are subtracting is a negative value (the sign bit is set),
* then we require that the resulting value also be positive (the sign bit is not set).
*/
VERIFY_CHECK((r->hi <= 0x7fffffffffffffffu && (uint64_t)hi > 0x7fffffffffffffffu) <= (r->hi - (uint64_t)hi <= 0x7fffffffffffffffu));
/* Verify no underflow.
* If r represents a negative value (the sign bit is set) and the value we are subtracting is a positive value (the sign sign bit is not set),
* then we require that the resulting value also be negative (the sign bit is set).
*/
VERIFY_CHECK((r->hi > 0x7fffffffffffffffu && (uint64_t)hi <= 0x7fffffffffffffffu) <= (r->hi - (uint64_t)hi > 0x7fffffffffffffffu));
r->hi -= hi;
r->lo -= lo;
}
static SECP256K1_INLINE void secp256k1_i128_det(secp256k1_int128 *r, int64_t a, int64_t b, int64_t c, int64_t d) {
secp256k1_i128_mul(r, a, d);
secp256k1_i128_dissip_mul(r, b, c);
}
/* Signed (arithmetic) right shift.
* Non-constant time in n.
*/
static SECP256K1_INLINE void secp256k1_i128_rshift(secp256k1_int128 *r, unsigned int n) {
VERIFY_CHECK(n < 128);
if (n >= 64) {
r->lo = (uint64_t)((int64_t)(r->hi) >> (n-64));
r->hi = (uint64_t)((int64_t)(r->hi) >> 63);
} else if (n > 0) {
r->lo = ((1U * r->hi) << (64-n)) | r->lo >> n;
r->hi = (uint64_t)((int64_t)(r->hi) >> n);
}
}
static SECP256K1_INLINE uint64_t secp256k1_i128_to_u64(const secp256k1_int128 *a) {
return a->lo;
}
static SECP256K1_INLINE int64_t secp256k1_i128_to_i64(const secp256k1_int128 *a) {
/* Verify that a represents a 64 bit signed value by checking that the high bits are a sign extension of the low bits. */
VERIFY_CHECK(a->hi == -(a->lo >> 63));
return (int64_t)secp256k1_i128_to_u64(a);
}
static SECP256K1_INLINE void secp256k1_i128_from_i64(secp256k1_int128 *r, int64_t a) {
r->hi = (uint64_t)(a >> 63);
r->lo = (uint64_t)a;
}
static SECP256K1_INLINE int secp256k1_i128_eq_var(const secp256k1_int128 *a, const secp256k1_int128 *b) {
return a->hi == b->hi && a->lo == b->lo;
}
static SECP256K1_INLINE int secp256k1_i128_check_pow2(const secp256k1_int128 *r, unsigned int n, int sign) {
VERIFY_CHECK(n < 127);
VERIFY_CHECK(sign == 1 || sign == -1);
return n >= 64 ? r->hi == (uint64_t)sign << (n - 64) && r->lo == 0
: r->hi == (uint64_t)(sign >> 1) && r->lo == (uint64_t)sign << n;
}
#endif

View File

@@ -0,0 +1,43 @@
/***********************************************************************
* Copyright (c) 2020 Peter Dettman *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
#ifndef SECP256K1_MODINV32_H
#define SECP256K1_MODINV32_H
#include "util.h"
/* A signed 30-bit limb representation of integers.
*
* Its value is sum(v[i] * 2^(30*i), i=0..8). */
typedef struct {
int32_t v[9];
} secp256k1_modinv32_signed30;
typedef struct {
/* The modulus in signed30 notation, must be odd and in [3, 2^256]. */
secp256k1_modinv32_signed30 modulus;
/* modulus^{-1} mod 2^30 */
uint32_t modulus_inv30;
} secp256k1_modinv32_modinfo;
/* Replace x with its modular inverse mod modinfo->modulus. x must be in range [0, modulus).
* If x is zero, the result will be zero as well. If not, the inverse must exist (i.e., the gcd of
* x and modulus must be 1). These rules are automatically satisfied if the modulus is prime.
*
* On output, all of x's limbs will be in [0, 2^30).
*/
static void secp256k1_modinv32_var(secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo);
/* Same as secp256k1_modinv32_var, but constant time in x (not in the modulus). */
static void secp256k1_modinv32(secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo);
/* Compute the Jacobi symbol for (x | modinfo->modulus). x must be coprime with modulus (and thus
* cannot be 0, as modulus >= 3). All limbs of x must be non-negative. Returns 0 if the result
* cannot be computed. */
static int secp256k1_jacobi32_maybe_var(const secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo);
#endif /* SECP256K1_MODINV32_H */

View File

@@ -0,0 +1,725 @@
/***********************************************************************
* Copyright (c) 2020 Peter Dettman *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
#ifndef SECP256K1_MODINV32_IMPL_H
#define SECP256K1_MODINV32_IMPL_H
#include "modinv32.h"
#include "util.h"
#include <stdlib.h>
/* This file implements modular inversion based on the paper "Fast constant-time gcd computation and
* modular inversion" by Daniel J. Bernstein and Bo-Yin Yang.
*
* For an explanation of the algorithm, see doc/safegcd_implementation.md. This file contains an
* implementation for N=30, using 30-bit signed limbs represented as int32_t.
*/
#ifdef VERIFY
static const secp256k1_modinv32_signed30 SECP256K1_SIGNED30_ONE = {{1}};
/* Compute a*factor and put it in r. All but the top limb in r will be in range [0,2^30). */
static void secp256k1_modinv32_mul_30(secp256k1_modinv32_signed30 *r, const secp256k1_modinv32_signed30 *a, int alen, int32_t factor) {
const int32_t M30 = (int32_t)(UINT32_MAX >> 2);
int64_t c = 0;
int i;
for (i = 0; i < 8; ++i) {
if (i < alen) c += (int64_t)a->v[i] * factor;
r->v[i] = (int32_t)c & M30; c >>= 30;
}
if (8 < alen) c += (int64_t)a->v[8] * factor;
VERIFY_CHECK(c == (int32_t)c);
r->v[8] = (int32_t)c;
}
/* Return -1 for a<b*factor, 0 for a==b*factor, 1 for a>b*factor. A consists of alen limbs; b has 9. */
static int secp256k1_modinv32_mul_cmp_30(const secp256k1_modinv32_signed30 *a, int alen, const secp256k1_modinv32_signed30 *b, int32_t factor) {
int i;
secp256k1_modinv32_signed30 am, bm;
secp256k1_modinv32_mul_30(&am, a, alen, 1); /* Normalize all but the top limb of a. */
secp256k1_modinv32_mul_30(&bm, b, 9, factor);
for (i = 0; i < 8; ++i) {
/* Verify that all but the top limb of a and b are normalized. */
VERIFY_CHECK(am.v[i] >> 30 == 0);
VERIFY_CHECK(bm.v[i] >> 30 == 0);
}
for (i = 8; i >= 0; --i) {
if (am.v[i] < bm.v[i]) return -1;
if (am.v[i] > bm.v[i]) return 1;
}
return 0;
}
#endif
/* Take as input a signed30 number in range (-2*modulus,modulus), and add a multiple of the modulus
* to it to bring it to range [0,modulus). If sign < 0, the input will also be negated in the
* process. The input must have limbs in range (-2^30,2^30). The output will have limbs in range
* [0,2^30). */
static void secp256k1_modinv32_normalize_30(secp256k1_modinv32_signed30 *r, int32_t sign, const secp256k1_modinv32_modinfo *modinfo) {
const int32_t M30 = (int32_t)(UINT32_MAX >> 2);
int32_t r0 = r->v[0], r1 = r->v[1], r2 = r->v[2], r3 = r->v[3], r4 = r->v[4],
r5 = r->v[5], r6 = r->v[6], r7 = r->v[7], r8 = r->v[8];
volatile int32_t cond_add, cond_negate;
#ifdef VERIFY
/* Verify that all limbs are in range (-2^30,2^30). */
int i;
for (i = 0; i < 9; ++i) {
VERIFY_CHECK(r->v[i] >= -M30);
VERIFY_CHECK(r->v[i] <= M30);
}
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(r, 9, &modinfo->modulus, -2) > 0); /* r > -2*modulus */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(r, 9, &modinfo->modulus, 1) < 0); /* r < modulus */
#endif
/* In a first step, add the modulus if the input is negative, and then negate if requested.
* This brings r from range (-2*modulus,modulus) to range (-modulus,modulus). As all input
* limbs are in range (-2^30,2^30), this cannot overflow an int32_t. Note that the right
* shifts below are signed sign-extending shifts (see assumptions.h for tests that that is
* indeed the behavior of the right shift operator). */
cond_add = r8 >> 31;
r0 += modinfo->modulus.v[0] & cond_add;
r1 += modinfo->modulus.v[1] & cond_add;
r2 += modinfo->modulus.v[2] & cond_add;
r3 += modinfo->modulus.v[3] & cond_add;
r4 += modinfo->modulus.v[4] & cond_add;
r5 += modinfo->modulus.v[5] & cond_add;
r6 += modinfo->modulus.v[6] & cond_add;
r7 += modinfo->modulus.v[7] & cond_add;
r8 += modinfo->modulus.v[8] & cond_add;
cond_negate = sign >> 31;
r0 = (r0 ^ cond_negate) - cond_negate;
r1 = (r1 ^ cond_negate) - cond_negate;
r2 = (r2 ^ cond_negate) - cond_negate;
r3 = (r3 ^ cond_negate) - cond_negate;
r4 = (r4 ^ cond_negate) - cond_negate;
r5 = (r5 ^ cond_negate) - cond_negate;
r6 = (r6 ^ cond_negate) - cond_negate;
r7 = (r7 ^ cond_negate) - cond_negate;
r8 = (r8 ^ cond_negate) - cond_negate;
/* Propagate the top bits, to bring limbs back to range (-2^30,2^30). */
r1 += r0 >> 30; r0 &= M30;
r2 += r1 >> 30; r1 &= M30;
r3 += r2 >> 30; r2 &= M30;
r4 += r3 >> 30; r3 &= M30;
r5 += r4 >> 30; r4 &= M30;
r6 += r5 >> 30; r5 &= M30;
r7 += r6 >> 30; r6 &= M30;
r8 += r7 >> 30; r7 &= M30;
/* In a second step add the modulus again if the result is still negative, bringing r to range
* [0,modulus). */
cond_add = r8 >> 31;
r0 += modinfo->modulus.v[0] & cond_add;
r1 += modinfo->modulus.v[1] & cond_add;
r2 += modinfo->modulus.v[2] & cond_add;
r3 += modinfo->modulus.v[3] & cond_add;
r4 += modinfo->modulus.v[4] & cond_add;
r5 += modinfo->modulus.v[5] & cond_add;
r6 += modinfo->modulus.v[6] & cond_add;
r7 += modinfo->modulus.v[7] & cond_add;
r8 += modinfo->modulus.v[8] & cond_add;
/* And propagate again. */
r1 += r0 >> 30; r0 &= M30;
r2 += r1 >> 30; r1 &= M30;
r3 += r2 >> 30; r2 &= M30;
r4 += r3 >> 30; r3 &= M30;
r5 += r4 >> 30; r4 &= M30;
r6 += r5 >> 30; r5 &= M30;
r7 += r6 >> 30; r6 &= M30;
r8 += r7 >> 30; r7 &= M30;
r->v[0] = r0;
r->v[1] = r1;
r->v[2] = r2;
r->v[3] = r3;
r->v[4] = r4;
r->v[5] = r5;
r->v[6] = r6;
r->v[7] = r7;
r->v[8] = r8;
VERIFY_CHECK(r0 >> 30 == 0);
VERIFY_CHECK(r1 >> 30 == 0);
VERIFY_CHECK(r2 >> 30 == 0);
VERIFY_CHECK(r3 >> 30 == 0);
VERIFY_CHECK(r4 >> 30 == 0);
VERIFY_CHECK(r5 >> 30 == 0);
VERIFY_CHECK(r6 >> 30 == 0);
VERIFY_CHECK(r7 >> 30 == 0);
VERIFY_CHECK(r8 >> 30 == 0);
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(r, 9, &modinfo->modulus, 0) >= 0); /* r >= 0 */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(r, 9, &modinfo->modulus, 1) < 0); /* r < modulus */
}
/* Data type for transition matrices (see section 3 of explanation).
*
* t = [ u v ]
* [ q r ]
*/
typedef struct {
int32_t u, v, q, r;
} secp256k1_modinv32_trans2x2;
/* Compute the transition matrix and zeta for 30 divsteps.
*
* Input: zeta: initial zeta
* f0: bottom limb of initial f
* g0: bottom limb of initial g
* Output: t: transition matrix
* Return: final zeta
*
* Implements the divsteps_n_matrix function from the explanation.
*/
static int32_t secp256k1_modinv32_divsteps_30(int32_t zeta, uint32_t f0, uint32_t g0, secp256k1_modinv32_trans2x2 *t) {
/* u,v,q,r are the elements of the transformation matrix being built up,
* starting with the identity matrix. Semantically they are signed integers
* in range [-2^30,2^30], but here represented as unsigned mod 2^32. This
* permits left shifting (which is UB for negative numbers). The range
* being inside [-2^31,2^31) means that casting to signed works correctly.
*/
uint32_t u = 1, v = 0, q = 0, r = 1;
volatile uint32_t c1, c2;
uint32_t mask1, mask2, f = f0, g = g0, x, y, z;
int i;
for (i = 0; i < 30; ++i) {
VERIFY_CHECK((f & 1) == 1); /* f must always be odd */
VERIFY_CHECK((u * f0 + v * g0) == f << i);
VERIFY_CHECK((q * f0 + r * g0) == g << i);
/* Compute conditional masks for (zeta < 0) and for (g & 1). */
c1 = zeta >> 31;
mask1 = c1;
c2 = g & 1;
mask2 = -c2;
/* Compute x,y,z, conditionally negated versions of f,u,v. */
x = (f ^ mask1) - mask1;
y = (u ^ mask1) - mask1;
z = (v ^ mask1) - mask1;
/* Conditionally add x,y,z to g,q,r. */
g += x & mask2;
q += y & mask2;
r += z & mask2;
/* In what follows, mask1 is a condition mask for (zeta < 0) and (g & 1). */
mask1 &= mask2;
/* Conditionally change zeta into -zeta-2 or zeta-1. */
zeta = (zeta ^ mask1) - 1;
/* Conditionally add g,q,r to f,u,v. */
f += g & mask1;
u += q & mask1;
v += r & mask1;
/* Shifts */
g >>= 1;
u <<= 1;
v <<= 1;
/* Bounds on zeta that follow from the bounds on iteration count (max 20*30 divsteps). */
VERIFY_CHECK(zeta >= -601 && zeta <= 601);
}
/* Return data in t and return value. */
t->u = (int32_t)u;
t->v = (int32_t)v;
t->q = (int32_t)q;
t->r = (int32_t)r;
/* The determinant of t must be a power of two. This guarantees that multiplication with t
* does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which
* will be divided out again). As each divstep's individual matrix has determinant 2, the
* aggregate of 30 of them will have determinant 2^30. */
VERIFY_CHECK((int64_t)t->u * t->r - (int64_t)t->v * t->q == ((int64_t)1) << 30);
return zeta;
}
/* secp256k1_modinv32_inv256[i] = -(2*i+1)^-1 (mod 256) */
static const uint8_t secp256k1_modinv32_inv256[128] = {
0xFF, 0x55, 0x33, 0x49, 0xC7, 0x5D, 0x3B, 0x11, 0x0F, 0xE5, 0xC3, 0x59,
0xD7, 0xED, 0xCB, 0x21, 0x1F, 0x75, 0x53, 0x69, 0xE7, 0x7D, 0x5B, 0x31,
0x2F, 0x05, 0xE3, 0x79, 0xF7, 0x0D, 0xEB, 0x41, 0x3F, 0x95, 0x73, 0x89,
0x07, 0x9D, 0x7B, 0x51, 0x4F, 0x25, 0x03, 0x99, 0x17, 0x2D, 0x0B, 0x61,
0x5F, 0xB5, 0x93, 0xA9, 0x27, 0xBD, 0x9B, 0x71, 0x6F, 0x45, 0x23, 0xB9,
0x37, 0x4D, 0x2B, 0x81, 0x7F, 0xD5, 0xB3, 0xC9, 0x47, 0xDD, 0xBB, 0x91,
0x8F, 0x65, 0x43, 0xD9, 0x57, 0x6D, 0x4B, 0xA1, 0x9F, 0xF5, 0xD3, 0xE9,
0x67, 0xFD, 0xDB, 0xB1, 0xAF, 0x85, 0x63, 0xF9, 0x77, 0x8D, 0x6B, 0xC1,
0xBF, 0x15, 0xF3, 0x09, 0x87, 0x1D, 0xFB, 0xD1, 0xCF, 0xA5, 0x83, 0x19,
0x97, 0xAD, 0x8B, 0xE1, 0xDF, 0x35, 0x13, 0x29, 0xA7, 0x3D, 0x1B, 0xF1,
0xEF, 0xC5, 0xA3, 0x39, 0xB7, 0xCD, 0xAB, 0x01
};
/* Compute the transition matrix and eta for 30 divsteps (variable time).
*
* Input: eta: initial eta
* f0: bottom limb of initial f
* g0: bottom limb of initial g
* Output: t: transition matrix
* Return: final eta
*
* Implements the divsteps_n_matrix_var function from the explanation.
*/
static int32_t secp256k1_modinv32_divsteps_30_var(int32_t eta, uint32_t f0, uint32_t g0, secp256k1_modinv32_trans2x2 *t) {
/* Transformation matrix; see comments in secp256k1_modinv32_divsteps_30. */
uint32_t u = 1, v = 0, q = 0, r = 1;
uint32_t f = f0, g = g0, m;
uint16_t w;
int i = 30, limit, zeros;
for (;;) {
/* Use a sentinel bit to count zeros only up to i. */
zeros = secp256k1_ctz32_var(g | (UINT32_MAX << i));
/* Perform zeros divsteps at once; they all just divide g by two. */
g >>= zeros;
u <<= zeros;
v <<= zeros;
eta -= zeros;
i -= zeros;
/* We're done once we've done 30 divsteps. */
if (i == 0) break;
VERIFY_CHECK((f & 1) == 1);
VERIFY_CHECK((g & 1) == 1);
VERIFY_CHECK((u * f0 + v * g0) == f << (30 - i));
VERIFY_CHECK((q * f0 + r * g0) == g << (30 - i));
/* Bounds on eta that follow from the bounds on iteration count (max 25*30 divsteps). */
VERIFY_CHECK(eta >= -751 && eta <= 751);
/* If eta is negative, negate it and replace f,g with g,-f. */
if (eta < 0) {
uint32_t tmp;
eta = -eta;
tmp = f; f = g; g = -tmp;
tmp = u; u = q; q = -tmp;
tmp = v; v = r; r = -tmp;
}
/* eta is now >= 0. In what follows we're going to cancel out the bottom bits of g. No more
* than i can be cancelled out (as we'd be done before that point), and no more than eta+1
* can be done as its sign will flip once that happens. */
limit = ((int)eta + 1) > i ? i : ((int)eta + 1);
/* m is a mask for the bottom min(limit, 8) bits (our table only supports 8 bits). */
VERIFY_CHECK(limit > 0 && limit <= 30);
m = (UINT32_MAX >> (32 - limit)) & 255U;
/* Find what multiple of f must be added to g to cancel its bottom min(limit, 8) bits. */
w = (g * secp256k1_modinv32_inv256[(f >> 1) & 127]) & m;
/* Do so. */
g += f * w;
q += u * w;
r += v * w;
VERIFY_CHECK((g & m) == 0);
}
/* Return data in t and return value. */
t->u = (int32_t)u;
t->v = (int32_t)v;
t->q = (int32_t)q;
t->r = (int32_t)r;
/* The determinant of t must be a power of two. This guarantees that multiplication with t
* does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which
* will be divided out again). As each divstep's individual matrix has determinant 2, the
* aggregate of 30 of them will have determinant 2^30. */
VERIFY_CHECK((int64_t)t->u * t->r - (int64_t)t->v * t->q == ((int64_t)1) << 30);
return eta;
}
/* Compute the transition matrix and eta for 30 posdivsteps (variable time, eta=-delta), and keeps track
* of the Jacobi symbol along the way. f0 and g0 must be f and g mod 2^32 rather than 2^30, because
* Jacobi tracking requires knowing (f mod 8) rather than just (f mod 2).
*
* Input: eta: initial eta
* f0: bottom limb of initial f
* g0: bottom limb of initial g
* Output: t: transition matrix
* Input/Output: (*jacp & 1) is bitflipped if and only if the Jacobi symbol of (f | g) changes sign
* by applying the returned transformation matrix to it. The other bits of *jacp may
* change, but are meaningless.
* Return: final eta
*/
static int32_t secp256k1_modinv32_posdivsteps_30_var(int32_t eta, uint32_t f0, uint32_t g0, secp256k1_modinv32_trans2x2 *t, int *jacp) {
/* Transformation matrix. */
uint32_t u = 1, v = 0, q = 0, r = 1;
uint32_t f = f0, g = g0, m;
uint16_t w;
int i = 30, limit, zeros;
int jac = *jacp;
for (;;) {
/* Use a sentinel bit to count zeros only up to i. */
zeros = secp256k1_ctz32_var(g | (UINT32_MAX << i));
/* Perform zeros divsteps at once; they all just divide g by two. */
g >>= zeros;
u <<= zeros;
v <<= zeros;
eta -= zeros;
i -= zeros;
/* Update the bottom bit of jac: when dividing g by an odd power of 2,
* if (f mod 8) is 3 or 5, the Jacobi symbol changes sign. */
jac ^= (zeros & ((f >> 1) ^ (f >> 2)));
/* We're done once we've done 30 posdivsteps. */
if (i == 0) break;
VERIFY_CHECK((f & 1) == 1);
VERIFY_CHECK((g & 1) == 1);
VERIFY_CHECK((u * f0 + v * g0) == f << (30 - i));
VERIFY_CHECK((q * f0 + r * g0) == g << (30 - i));
/* If eta is negative, negate it and replace f,g with g,f. */
if (eta < 0) {
uint32_t tmp;
eta = -eta;
/* Update bottom bit of jac: when swapping f and g, the Jacobi symbol changes sign
* if both f and g are 3 mod 4. */
jac ^= ((f & g) >> 1);
tmp = f; f = g; g = tmp;
tmp = u; u = q; q = tmp;
tmp = v; v = r; r = tmp;
}
/* eta is now >= 0. In what follows we're going to cancel out the bottom bits of g. No more
* than i can be cancelled out (as we'd be done before that point), and no more than eta+1
* can be done as its sign will flip once that happens. */
limit = ((int)eta + 1) > i ? i : ((int)eta + 1);
/* m is a mask for the bottom min(limit, 8) bits (our table only supports 8 bits). */
VERIFY_CHECK(limit > 0 && limit <= 30);
m = (UINT32_MAX >> (32 - limit)) & 255U;
/* Find what multiple of f must be added to g to cancel its bottom min(limit, 8) bits. */
w = (g * secp256k1_modinv32_inv256[(f >> 1) & 127]) & m;
/* Do so. */
g += f * w;
q += u * w;
r += v * w;
VERIFY_CHECK((g & m) == 0);
}
/* Return data in t and return value. */
t->u = (int32_t)u;
t->v = (int32_t)v;
t->q = (int32_t)q;
t->r = (int32_t)r;
/* The determinant of t must be a power of two. This guarantees that multiplication with t
* does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which
* will be divided out again). As each divstep's individual matrix has determinant 2 or -2,
* the aggregate of 30 of them will have determinant 2^30 or -2^30. */
VERIFY_CHECK((int64_t)t->u * t->r - (int64_t)t->v * t->q == ((int64_t)1) << 30 ||
(int64_t)t->u * t->r - (int64_t)t->v * t->q == -(((int64_t)1) << 30));
*jacp = jac;
return eta;
}
/* Compute (t/2^30) * [d, e] mod modulus, where t is a transition matrix for 30 divsteps.
*
* On input and output, d and e are in range (-2*modulus,modulus). All output limbs will be in range
* (-2^30,2^30).
*
* This implements the update_de function from the explanation.
*/
static void secp256k1_modinv32_update_de_30(secp256k1_modinv32_signed30 *d, secp256k1_modinv32_signed30 *e, const secp256k1_modinv32_trans2x2 *t, const secp256k1_modinv32_modinfo* modinfo) {
const int32_t M30 = (int32_t)(UINT32_MAX >> 2);
const int32_t u = t->u, v = t->v, q = t->q, r = t->r;
int32_t di, ei, md, me, sd, se;
int64_t cd, ce;
int i;
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(d, 9, &modinfo->modulus, -2) > 0); /* d > -2*modulus */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(d, 9, &modinfo->modulus, 1) < 0); /* d < modulus */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(e, 9, &modinfo->modulus, -2) > 0); /* e > -2*modulus */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(e, 9, &modinfo->modulus, 1) < 0); /* e < modulus */
VERIFY_CHECK(labs(u) <= (M30 + 1 - labs(v))); /* |u|+|v| <= 2^30 */
VERIFY_CHECK(labs(q) <= (M30 + 1 - labs(r))); /* |q|+|r| <= 2^30 */
/* [md,me] start as zero; plus [u,q] if d is negative; plus [v,r] if e is negative. */
sd = d->v[8] >> 31;
se = e->v[8] >> 31;
md = (u & sd) + (v & se);
me = (q & sd) + (r & se);
/* Begin computing t*[d,e]. */
di = d->v[0];
ei = e->v[0];
cd = (int64_t)u * di + (int64_t)v * ei;
ce = (int64_t)q * di + (int64_t)r * ei;
/* Correct md,me so that t*[d,e]+modulus*[md,me] has 30 zero bottom bits. */
md -= (modinfo->modulus_inv30 * (uint32_t)cd + md) & M30;
me -= (modinfo->modulus_inv30 * (uint32_t)ce + me) & M30;
/* Update the beginning of computation for t*[d,e]+modulus*[md,me] now md,me are known. */
cd += (int64_t)modinfo->modulus.v[0] * md;
ce += (int64_t)modinfo->modulus.v[0] * me;
/* Verify that the low 30 bits of the computation are indeed zero, and then throw them away. */
VERIFY_CHECK(((int32_t)cd & M30) == 0); cd >>= 30;
VERIFY_CHECK(((int32_t)ce & M30) == 0); ce >>= 30;
/* Now iteratively compute limb i=1..8 of t*[d,e]+modulus*[md,me], and store them in output
* limb i-1 (shifting down by 30 bits). */
for (i = 1; i < 9; ++i) {
di = d->v[i];
ei = e->v[i];
cd += (int64_t)u * di + (int64_t)v * ei;
ce += (int64_t)q * di + (int64_t)r * ei;
cd += (int64_t)modinfo->modulus.v[i] * md;
ce += (int64_t)modinfo->modulus.v[i] * me;
d->v[i - 1] = (int32_t)cd & M30; cd >>= 30;
e->v[i - 1] = (int32_t)ce & M30; ce >>= 30;
}
/* What remains is limb 9 of t*[d,e]+modulus*[md,me]; store it as output limb 8. */
d->v[8] = (int32_t)cd;
e->v[8] = (int32_t)ce;
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(d, 9, &modinfo->modulus, -2) > 0); /* d > -2*modulus */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(d, 9, &modinfo->modulus, 1) < 0); /* d < modulus */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(e, 9, &modinfo->modulus, -2) > 0); /* e > -2*modulus */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(e, 9, &modinfo->modulus, 1) < 0); /* e < modulus */
}
/* Compute (t/2^30) * [f, g], where t is a transition matrix for 30 divsteps.
*
* This implements the update_fg function from the explanation.
*/
static void secp256k1_modinv32_update_fg_30(secp256k1_modinv32_signed30 *f, secp256k1_modinv32_signed30 *g, const secp256k1_modinv32_trans2x2 *t) {
const int32_t M30 = (int32_t)(UINT32_MAX >> 2);
const int32_t u = t->u, v = t->v, q = t->q, r = t->r;
int32_t fi, gi;
int64_t cf, cg;
int i;
/* Start computing t*[f,g]. */
fi = f->v[0];
gi = g->v[0];
cf = (int64_t)u * fi + (int64_t)v * gi;
cg = (int64_t)q * fi + (int64_t)r * gi;
/* Verify that the bottom 30 bits of the result are zero, and then throw them away. */
VERIFY_CHECK(((int32_t)cf & M30) == 0); cf >>= 30;
VERIFY_CHECK(((int32_t)cg & M30) == 0); cg >>= 30;
/* Now iteratively compute limb i=1..8 of t*[f,g], and store them in output limb i-1 (shifting
* down by 30 bits). */
for (i = 1; i < 9; ++i) {
fi = f->v[i];
gi = g->v[i];
cf += (int64_t)u * fi + (int64_t)v * gi;
cg += (int64_t)q * fi + (int64_t)r * gi;
f->v[i - 1] = (int32_t)cf & M30; cf >>= 30;
g->v[i - 1] = (int32_t)cg & M30; cg >>= 30;
}
/* What remains is limb 9 of t*[f,g]; store it as output limb 8. */
f->v[8] = (int32_t)cf;
g->v[8] = (int32_t)cg;
}
/* Compute (t/2^30) * [f, g], where t is a transition matrix for 30 divsteps.
*
* Version that operates on a variable number of limbs in f and g.
*
* This implements the update_fg function from the explanation in modinv64_impl.h.
*/
static void secp256k1_modinv32_update_fg_30_var(int len, secp256k1_modinv32_signed30 *f, secp256k1_modinv32_signed30 *g, const secp256k1_modinv32_trans2x2 *t) {
const int32_t M30 = (int32_t)(UINT32_MAX >> 2);
const int32_t u = t->u, v = t->v, q = t->q, r = t->r;
int32_t fi, gi;
int64_t cf, cg;
int i;
VERIFY_CHECK(len > 0);
/* Start computing t*[f,g]. */
fi = f->v[0];
gi = g->v[0];
cf = (int64_t)u * fi + (int64_t)v * gi;
cg = (int64_t)q * fi + (int64_t)r * gi;
/* Verify that the bottom 62 bits of the result are zero, and then throw them away. */
VERIFY_CHECK(((int32_t)cf & M30) == 0); cf >>= 30;
VERIFY_CHECK(((int32_t)cg & M30) == 0); cg >>= 30;
/* Now iteratively compute limb i=1..len of t*[f,g], and store them in output limb i-1 (shifting
* down by 30 bits). */
for (i = 1; i < len; ++i) {
fi = f->v[i];
gi = g->v[i];
cf += (int64_t)u * fi + (int64_t)v * gi;
cg += (int64_t)q * fi + (int64_t)r * gi;
f->v[i - 1] = (int32_t)cf & M30; cf >>= 30;
g->v[i - 1] = (int32_t)cg & M30; cg >>= 30;
}
/* What remains is limb (len) of t*[f,g]; store it as output limb (len-1). */
f->v[len - 1] = (int32_t)cf;
g->v[len - 1] = (int32_t)cg;
}
/* Compute the inverse of x modulo modinfo->modulus, and replace x with it (constant time in x). */
static void secp256k1_modinv32(secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo) {
/* Start with d=0, e=1, f=modulus, g=x, zeta=-1. */
secp256k1_modinv32_signed30 d = {{0}};
secp256k1_modinv32_signed30 e = {{1}};
secp256k1_modinv32_signed30 f = modinfo->modulus;
secp256k1_modinv32_signed30 g = *x;
int i;
int32_t zeta = -1; /* zeta = -(delta+1/2); delta is initially 1/2. */
/* Do 20 iterations of 30 divsteps each = 600 divsteps. 590 suffices for 256-bit inputs. */
for (i = 0; i < 20; ++i) {
/* Compute transition matrix and new zeta after 30 divsteps. */
secp256k1_modinv32_trans2x2 t;
zeta = secp256k1_modinv32_divsteps_30(zeta, f.v[0], g.v[0], &t);
/* Update d,e using that transition matrix. */
secp256k1_modinv32_update_de_30(&d, &e, &t, modinfo);
/* Update f,g using that transition matrix. */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, -1) > 0); /* f > -modulus */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, 1) <= 0); /* f <= modulus */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &modinfo->modulus, -1) > 0); /* g > -modulus */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &modinfo->modulus, 1) < 0); /* g < modulus */
secp256k1_modinv32_update_fg_30(&f, &g, &t);
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, -1) > 0); /* f > -modulus */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, 1) <= 0); /* f <= modulus */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &modinfo->modulus, -1) > 0); /* g > -modulus */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &modinfo->modulus, 1) < 0); /* g < modulus */
}
/* At this point sufficient iterations have been performed that g must have reached 0
* and (if g was not originally 0) f must now equal +/- GCD of the initial f, g
* values i.e. +/- 1, and d now contains +/- the modular inverse. */
/* g == 0 */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &SECP256K1_SIGNED30_ONE, 0) == 0);
/* |f| == 1, or (x == 0 and d == 0 and f == modulus) */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &SECP256K1_SIGNED30_ONE, -1) == 0 ||
secp256k1_modinv32_mul_cmp_30(&f, 9, &SECP256K1_SIGNED30_ONE, 1) == 0 ||
(secp256k1_modinv32_mul_cmp_30(x, 9, &SECP256K1_SIGNED30_ONE, 0) == 0 &&
secp256k1_modinv32_mul_cmp_30(&d, 9, &SECP256K1_SIGNED30_ONE, 0) == 0 &&
secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, 1) == 0));
/* Optionally negate d, normalize to [0,modulus), and return it. */
secp256k1_modinv32_normalize_30(&d, f.v[8], modinfo);
*x = d;
}
/* Compute the inverse of x modulo modinfo->modulus, and replace x with it (variable time). */
static void secp256k1_modinv32_var(secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo) {
/* Start with d=0, e=1, f=modulus, g=x, eta=-1. */
secp256k1_modinv32_signed30 d = {{0, 0, 0, 0, 0, 0, 0, 0, 0}};
secp256k1_modinv32_signed30 e = {{1, 0, 0, 0, 0, 0, 0, 0, 0}};
secp256k1_modinv32_signed30 f = modinfo->modulus;
secp256k1_modinv32_signed30 g = *x;
#ifdef VERIFY
int i = 0;
#endif
int j, len = 9;
int32_t eta = -1; /* eta = -delta; delta is initially 1 (faster for the variable-time code) */
int32_t cond, fn, gn;
/* Do iterations of 30 divsteps each until g=0. */
while (1) {
/* Compute transition matrix and new eta after 30 divsteps. */
secp256k1_modinv32_trans2x2 t;
eta = secp256k1_modinv32_divsteps_30_var(eta, f.v[0], g.v[0], &t);
/* Update d,e using that transition matrix. */
secp256k1_modinv32_update_de_30(&d, &e, &t, modinfo);
/* Update f,g using that transition matrix. */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, -1) > 0); /* f > -modulus */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, -1) > 0); /* g > -modulus */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */
secp256k1_modinv32_update_fg_30_var(len, &f, &g, &t);
/* If the bottom limb of g is 0, there is a chance g=0. */
if (g.v[0] == 0) {
cond = 0;
/* Check if all other limbs are also 0. */
for (j = 1; j < len; ++j) {
cond |= g.v[j];
}
/* If so, we're done. */
if (cond == 0) break;
}
/* Determine if len>1 and limb (len-1) of both f and g is 0 or -1. */
fn = f.v[len - 1];
gn = g.v[len - 1];
cond = ((int32_t)len - 2) >> 31;
cond |= fn ^ (fn >> 31);
cond |= gn ^ (gn >> 31);
/* If so, reduce length, propagating the sign of f and g's top limb into the one below. */
if (cond == 0) {
f.v[len - 2] |= (uint32_t)fn << 30;
g.v[len - 2] |= (uint32_t)gn << 30;
--len;
}
VERIFY_CHECK(++i < 25); /* We should never need more than 25*30 = 750 divsteps */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, -1) > 0); /* f > -modulus */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, -1) > 0); /* g > -modulus */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */
}
/* At this point g is 0 and (if g was not originally 0) f must now equal +/- GCD of
* the initial f, g values i.e. +/- 1, and d now contains +/- the modular inverse. */
/* g == 0 */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &SECP256K1_SIGNED30_ONE, 0) == 0);
/* |f| == 1, or (x == 0 and d == 0 and f == modulus) */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &SECP256K1_SIGNED30_ONE, -1) == 0 ||
secp256k1_modinv32_mul_cmp_30(&f, len, &SECP256K1_SIGNED30_ONE, 1) == 0 ||
(secp256k1_modinv32_mul_cmp_30(x, 9, &SECP256K1_SIGNED30_ONE, 0) == 0 &&
secp256k1_modinv32_mul_cmp_30(&d, 9, &SECP256K1_SIGNED30_ONE, 0) == 0 &&
secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) == 0));
/* Optionally negate d, normalize to [0,modulus), and return it. */
secp256k1_modinv32_normalize_30(&d, f.v[len - 1], modinfo);
*x = d;
}
/* Do up to 50 iterations of 30 posdivsteps (up to 1500 steps; more is extremely rare) each until f=1.
* In VERIFY mode use a lower number of iterations (750, close to the median 756), so failure actually occurs. */
#ifdef VERIFY
#define JACOBI32_ITERATIONS 25
#else
#define JACOBI32_ITERATIONS 50
#endif
/* Compute the Jacobi symbol of x modulo modinfo->modulus (variable time). gcd(x,modulus) must be 1. */
static int secp256k1_jacobi32_maybe_var(const secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo) {
/* Start with f=modulus, g=x, eta=-1. */
secp256k1_modinv32_signed30 f = modinfo->modulus;
secp256k1_modinv32_signed30 g = *x;
int j, len = 9;
int32_t eta = -1; /* eta = -delta; delta is initially 1 */
int32_t cond, fn, gn;
int jac = 0;
int count;
/* The input limbs must all be non-negative. */
VERIFY_CHECK(g.v[0] >= 0 && g.v[1] >= 0 && g.v[2] >= 0 && g.v[3] >= 0 && g.v[4] >= 0 && g.v[5] >= 0 && g.v[6] >= 0 && g.v[7] >= 0 && g.v[8] >= 0);
/* If x > 0, then if the loop below converges, it converges to f=g=gcd(x,modulus). Since we
* require that gcd(x,modulus)=1 and modulus>=3, x cannot be 0. Thus, we must reach f=1 (or
* time out). */
VERIFY_CHECK((g.v[0] | g.v[1] | g.v[2] | g.v[3] | g.v[4] | g.v[5] | g.v[6] | g.v[7] | g.v[8]) != 0);
for (count = 0; count < JACOBI32_ITERATIONS; ++count) {
/* Compute transition matrix and new eta after 30 posdivsteps. */
secp256k1_modinv32_trans2x2 t;
eta = secp256k1_modinv32_posdivsteps_30_var(eta, f.v[0] | ((uint32_t)f.v[1] << 30), g.v[0] | ((uint32_t)g.v[1] << 30), &t, &jac);
/* Update f,g using that transition matrix. */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 0) > 0); /* f > 0 */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 0) > 0); /* g > 0 */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */
secp256k1_modinv32_update_fg_30_var(len, &f, &g, &t);
/* If the bottom limb of f is 1, there is a chance that f=1. */
if (f.v[0] == 1) {
cond = 0;
/* Check if the other limbs are also 0. */
for (j = 1; j < len; ++j) {
cond |= f.v[j];
}
/* If so, we're done. If f=1, the Jacobi symbol (g | f)=1. */
if (cond == 0) return 1 - 2*(jac & 1);
}
/* Determine if len>1 and limb (len-1) of both f and g is 0. */
fn = f.v[len - 1];
gn = g.v[len - 1];
cond = ((int32_t)len - 2) >> 31;
cond |= fn;
cond |= gn;
/* If so, reduce length. */
if (cond == 0) --len;
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 0) > 0); /* f > 0 */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 0) > 0); /* g > 0 */
VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */
}
/* The loop failed to converge to f=g after 1500 iterations. Return 0, indicating unknown result. */
return 0;
}
#endif /* SECP256K1_MODINV32_IMPL_H */

View File

@@ -0,0 +1,47 @@
/***********************************************************************
* Copyright (c) 2020 Peter Dettman *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
#ifndef SECP256K1_MODINV64_H
#define SECP256K1_MODINV64_H
#include "util.h"
#ifndef SECP256K1_WIDEMUL_INT128
#error "modinv64 requires 128-bit wide multiplication support"
#endif
/* A signed 62-bit limb representation of integers.
*
* Its value is sum(v[i] * 2^(62*i), i=0..4). */
typedef struct {
int64_t v[5];
} secp256k1_modinv64_signed62;
typedef struct {
/* The modulus in signed62 notation, must be odd and in [3, 2^256]. */
secp256k1_modinv64_signed62 modulus;
/* modulus^{-1} mod 2^62 */
uint64_t modulus_inv62;
} secp256k1_modinv64_modinfo;
/* Replace x with its modular inverse mod modinfo->modulus. x must be in range [0, modulus).
* If x is zero, the result will be zero as well. If not, the inverse must exist (i.e., the gcd of
* x and modulus must be 1). These rules are automatically satisfied if the modulus is prime.
*
* On output, all of x's limbs will be in [0, 2^62).
*/
static void secp256k1_modinv64_var(secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo);
/* Same as secp256k1_modinv64_var, but constant time in x (not in the modulus). */
static void secp256k1_modinv64(secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo);
/* Compute the Jacobi symbol for (x | modinfo->modulus). x must be coprime with modulus (and thus
* cannot be 0, as modulus >= 3). All limbs of x must be non-negative. Returns 0 if the result
* cannot be computed. */
static int secp256k1_jacobi64_maybe_var(const secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo);
#endif /* SECP256K1_MODINV64_H */

View File

@@ -0,0 +1,780 @@
/***********************************************************************
* Copyright (c) 2020 Peter Dettman *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
#ifndef SECP256K1_MODINV64_IMPL_H
#define SECP256K1_MODINV64_IMPL_H
#include "int128.h"
#include "modinv64.h"
/* This file implements modular inversion based on the paper "Fast constant-time gcd computation and
* modular inversion" by Daniel J. Bernstein and Bo-Yin Yang.
*
* For an explanation of the algorithm, see doc/safegcd_implementation.md. This file contains an
* implementation for N=62, using 62-bit signed limbs represented as int64_t.
*/
/* Data type for transition matrices (see section 3 of explanation).
*
* t = [ u v ]
* [ q r ]
*/
typedef struct {
int64_t u, v, q, r;
} secp256k1_modinv64_trans2x2;
#ifdef VERIFY
/* Helper function to compute the absolute value of an int64_t.
* (we don't use abs/labs/llabs as it depends on the int sizes). */
static int64_t secp256k1_modinv64_abs(int64_t v) {
VERIFY_CHECK(v > INT64_MIN);
if (v < 0) return -v;
return v;
}
static const secp256k1_modinv64_signed62 SECP256K1_SIGNED62_ONE = {{1}};
/* Compute a*factor and put it in r. All but the top limb in r will be in range [0,2^62). */
static void secp256k1_modinv64_mul_62(secp256k1_modinv64_signed62 *r, const secp256k1_modinv64_signed62 *a, int alen, int64_t factor) {
const uint64_t M62 = UINT64_MAX >> 2;
secp256k1_int128 c, d;
int i;
secp256k1_i128_from_i64(&c, 0);
for (i = 0; i < 4; ++i) {
if (i < alen) secp256k1_i128_accum_mul(&c, a->v[i], factor);
r->v[i] = secp256k1_i128_to_u64(&c) & M62; secp256k1_i128_rshift(&c, 62);
}
if (4 < alen) secp256k1_i128_accum_mul(&c, a->v[4], factor);
secp256k1_i128_from_i64(&d, secp256k1_i128_to_i64(&c));
VERIFY_CHECK(secp256k1_i128_eq_var(&c, &d));
r->v[4] = secp256k1_i128_to_i64(&c);
}
/* Return -1 for a<b*factor, 0 for a==b*factor, 1 for a>b*factor. A has alen limbs; b has 5. */
static int secp256k1_modinv64_mul_cmp_62(const secp256k1_modinv64_signed62 *a, int alen, const secp256k1_modinv64_signed62 *b, int64_t factor) {
int i;
secp256k1_modinv64_signed62 am, bm;
secp256k1_modinv64_mul_62(&am, a, alen, 1); /* Normalize all but the top limb of a. */
secp256k1_modinv64_mul_62(&bm, b, 5, factor);
for (i = 0; i < 4; ++i) {
/* Verify that all but the top limb of a and b are normalized. */
VERIFY_CHECK(am.v[i] >> 62 == 0);
VERIFY_CHECK(bm.v[i] >> 62 == 0);
}
for (i = 4; i >= 0; --i) {
if (am.v[i] < bm.v[i]) return -1;
if (am.v[i] > bm.v[i]) return 1;
}
return 0;
}
/* Check if the determinant of t is equal to 1 << n. If abs, check if |det t| == 1 << n. */
static int secp256k1_modinv64_det_check_pow2(const secp256k1_modinv64_trans2x2 *t, unsigned int n, int abs) {
secp256k1_int128 a;
secp256k1_i128_det(&a, t->u, t->v, t->q, t->r);
if (secp256k1_i128_check_pow2(&a, n, 1)) return 1;
if (abs && secp256k1_i128_check_pow2(&a, n, -1)) return 1;
return 0;
}
#endif
/* Take as input a signed62 number in range (-2*modulus,modulus), and add a multiple of the modulus
* to it to bring it to range [0,modulus). If sign < 0, the input will also be negated in the
* process. The input must have limbs in range (-2^62,2^62). The output will have limbs in range
* [0,2^62). */
static void secp256k1_modinv64_normalize_62(secp256k1_modinv64_signed62 *r, int64_t sign, const secp256k1_modinv64_modinfo *modinfo) {
const int64_t M62 = (int64_t)(UINT64_MAX >> 2);
int64_t r0 = r->v[0], r1 = r->v[1], r2 = r->v[2], r3 = r->v[3], r4 = r->v[4];
volatile int64_t cond_add, cond_negate;
#ifdef VERIFY
/* Verify that all limbs are in range (-2^62,2^62). */
int i;
for (i = 0; i < 5; ++i) {
VERIFY_CHECK(r->v[i] >= -M62);
VERIFY_CHECK(r->v[i] <= M62);
}
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(r, 5, &modinfo->modulus, -2) > 0); /* r > -2*modulus */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(r, 5, &modinfo->modulus, 1) < 0); /* r < modulus */
#endif
/* In a first step, add the modulus if the input is negative, and then negate if requested.
* This brings r from range (-2*modulus,modulus) to range (-modulus,modulus). As all input
* limbs are in range (-2^62,2^62), this cannot overflow an int64_t. Note that the right
* shifts below are signed sign-extending shifts (see assumptions.h for tests that that is
* indeed the behavior of the right shift operator). */
cond_add = r4 >> 63;
r0 += modinfo->modulus.v[0] & cond_add;
r1 += modinfo->modulus.v[1] & cond_add;
r2 += modinfo->modulus.v[2] & cond_add;
r3 += modinfo->modulus.v[3] & cond_add;
r4 += modinfo->modulus.v[4] & cond_add;
cond_negate = sign >> 63;
r0 = (r0 ^ cond_negate) - cond_negate;
r1 = (r1 ^ cond_negate) - cond_negate;
r2 = (r2 ^ cond_negate) - cond_negate;
r3 = (r3 ^ cond_negate) - cond_negate;
r4 = (r4 ^ cond_negate) - cond_negate;
/* Propagate the top bits, to bring limbs back to range (-2^62,2^62). */
r1 += r0 >> 62; r0 &= M62;
r2 += r1 >> 62; r1 &= M62;
r3 += r2 >> 62; r2 &= M62;
r4 += r3 >> 62; r3 &= M62;
/* In a second step add the modulus again if the result is still negative, bringing
* r to range [0,modulus). */
cond_add = r4 >> 63;
r0 += modinfo->modulus.v[0] & cond_add;
r1 += modinfo->modulus.v[1] & cond_add;
r2 += modinfo->modulus.v[2] & cond_add;
r3 += modinfo->modulus.v[3] & cond_add;
r4 += modinfo->modulus.v[4] & cond_add;
/* And propagate again. */
r1 += r0 >> 62; r0 &= M62;
r2 += r1 >> 62; r1 &= M62;
r3 += r2 >> 62; r2 &= M62;
r4 += r3 >> 62; r3 &= M62;
r->v[0] = r0;
r->v[1] = r1;
r->v[2] = r2;
r->v[3] = r3;
r->v[4] = r4;
VERIFY_CHECK(r0 >> 62 == 0);
VERIFY_CHECK(r1 >> 62 == 0);
VERIFY_CHECK(r2 >> 62 == 0);
VERIFY_CHECK(r3 >> 62 == 0);
VERIFY_CHECK(r4 >> 62 == 0);
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(r, 5, &modinfo->modulus, 0) >= 0); /* r >= 0 */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(r, 5, &modinfo->modulus, 1) < 0); /* r < modulus */
}
/* Compute the transition matrix and eta for 59 divsteps (where zeta=-(delta+1/2)).
* Note that the transformation matrix is scaled by 2^62 and not 2^59.
*
* Input: zeta: initial zeta
* f0: bottom limb of initial f
* g0: bottom limb of initial g
* Output: t: transition matrix
* Return: final zeta
*
* Implements the divsteps_n_matrix function from the explanation.
*/
static int64_t secp256k1_modinv64_divsteps_59(int64_t zeta, uint64_t f0, uint64_t g0, secp256k1_modinv64_trans2x2 *t) {
/* u,v,q,r are the elements of the transformation matrix being built up,
* starting with the identity matrix times 8 (because the caller expects
* a result scaled by 2^62). Semantically they are signed integers
* in range [-2^62,2^62], but here represented as unsigned mod 2^64. This
* permits left shifting (which is UB for negative numbers). The range
* being inside [-2^63,2^63) means that casting to signed works correctly.
*/
uint64_t u = 8, v = 0, q = 0, r = 8;
volatile uint64_t c1, c2;
uint64_t mask1, mask2, f = f0, g = g0, x, y, z;
int i;
for (i = 3; i < 62; ++i) {
VERIFY_CHECK((f & 1) == 1); /* f must always be odd */
VERIFY_CHECK((u * f0 + v * g0) == f << i);
VERIFY_CHECK((q * f0 + r * g0) == g << i);
/* Compute conditional masks for (zeta < 0) and for (g & 1). */
c1 = zeta >> 63;
mask1 = c1;
c2 = g & 1;
mask2 = -c2;
/* Compute x,y,z, conditionally negated versions of f,u,v. */
x = (f ^ mask1) - mask1;
y = (u ^ mask1) - mask1;
z = (v ^ mask1) - mask1;
/* Conditionally add x,y,z to g,q,r. */
g += x & mask2;
q += y & mask2;
r += z & mask2;
/* In what follows, c1 is a condition mask for (zeta < 0) and (g & 1). */
mask1 &= mask2;
/* Conditionally change zeta into -zeta-2 or zeta-1. */
zeta = (zeta ^ mask1) - 1;
/* Conditionally add g,q,r to f,u,v. */
f += g & mask1;
u += q & mask1;
v += r & mask1;
/* Shifts */
g >>= 1;
u <<= 1;
v <<= 1;
/* Bounds on zeta that follow from the bounds on iteration count (max 10*59 divsteps). */
VERIFY_CHECK(zeta >= -591 && zeta <= 591);
}
/* Return data in t and return value. */
t->u = (int64_t)u;
t->v = (int64_t)v;
t->q = (int64_t)q;
t->r = (int64_t)r;
/* The determinant of t must be a power of two. This guarantees that multiplication with t
* does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which
* will be divided out again). As each divstep's individual matrix has determinant 2, the
* aggregate of 59 of them will have determinant 2^59. Multiplying with the initial
* 8*identity (which has determinant 2^6) means the overall outputs has determinant
* 2^65. */
VERIFY_CHECK(secp256k1_modinv64_det_check_pow2(t, 65, 0));
return zeta;
}
/* Compute the transition matrix and eta for 62 divsteps (variable time, eta=-delta).
*
* Input: eta: initial eta
* f0: bottom limb of initial f
* g0: bottom limb of initial g
* Output: t: transition matrix
* Return: final eta
*
* Implements the divsteps_n_matrix_var function from the explanation.
*/
static int64_t secp256k1_modinv64_divsteps_62_var(int64_t eta, uint64_t f0, uint64_t g0, secp256k1_modinv64_trans2x2 *t) {
/* Transformation matrix; see comments in secp256k1_modinv64_divsteps_62. */
uint64_t u = 1, v = 0, q = 0, r = 1;
uint64_t f = f0, g = g0, m;
uint32_t w;
int i = 62, limit, zeros;
for (;;) {
/* Use a sentinel bit to count zeros only up to i. */
zeros = secp256k1_ctz64_var(g | (UINT64_MAX << i));
/* Perform zeros divsteps at once; they all just divide g by two. */
g >>= zeros;
u <<= zeros;
v <<= zeros;
eta -= zeros;
i -= zeros;
/* We're done once we've done 62 divsteps. */
if (i == 0) break;
VERIFY_CHECK((f & 1) == 1);
VERIFY_CHECK((g & 1) == 1);
VERIFY_CHECK((u * f0 + v * g0) == f << (62 - i));
VERIFY_CHECK((q * f0 + r * g0) == g << (62 - i));
/* Bounds on eta that follow from the bounds on iteration count (max 12*62 divsteps). */
VERIFY_CHECK(eta >= -745 && eta <= 745);
/* If eta is negative, negate it and replace f,g with g,-f. */
if (eta < 0) {
uint64_t tmp;
eta = -eta;
tmp = f; f = g; g = -tmp;
tmp = u; u = q; q = -tmp;
tmp = v; v = r; r = -tmp;
/* Use a formula to cancel out up to 6 bits of g. Also, no more than i can be cancelled
* out (as we'd be done before that point), and no more than eta+1 can be done as its
* sign will flip again once that happens. */
limit = ((int)eta + 1) > i ? i : ((int)eta + 1);
VERIFY_CHECK(limit > 0 && limit <= 62);
/* m is a mask for the bottom min(limit, 6) bits. */
m = (UINT64_MAX >> (64 - limit)) & 63U;
/* Find what multiple of f must be added to g to cancel its bottom min(limit, 6)
* bits. */
w = (f * g * (f * f - 2)) & m;
} else {
/* In this branch, use a simpler formula that only lets us cancel up to 4 bits of g, as
* eta tends to be smaller here. */
limit = ((int)eta + 1) > i ? i : ((int)eta + 1);
VERIFY_CHECK(limit > 0 && limit <= 62);
/* m is a mask for the bottom min(limit, 4) bits. */
m = (UINT64_MAX >> (64 - limit)) & 15U;
/* Find what multiple of f must be added to g to cancel its bottom min(limit, 4)
* bits. */
w = f + (((f + 1) & 4) << 1);
w = (-w * g) & m;
}
g += f * w;
q += u * w;
r += v * w;
VERIFY_CHECK((g & m) == 0);
}
/* Return data in t and return value. */
t->u = (int64_t)u;
t->v = (int64_t)v;
t->q = (int64_t)q;
t->r = (int64_t)r;
/* The determinant of t must be a power of two. This guarantees that multiplication with t
* does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which
* will be divided out again). As each divstep's individual matrix has determinant 2, the
* aggregate of 62 of them will have determinant 2^62. */
VERIFY_CHECK(secp256k1_modinv64_det_check_pow2(t, 62, 0));
return eta;
}
/* Compute the transition matrix and eta for 62 posdivsteps (variable time, eta=-delta), and keeps track
* of the Jacobi symbol along the way. f0 and g0 must be f and g mod 2^64 rather than 2^62, because
* Jacobi tracking requires knowing (f mod 8) rather than just (f mod 2).
*
* Input: eta: initial eta
* f0: bottom limb of initial f
* g0: bottom limb of initial g
* Output: t: transition matrix
* Input/Output: (*jacp & 1) is bitflipped if and only if the Jacobi symbol of (f | g) changes sign
* by applying the returned transformation matrix to it. The other bits of *jacp may
* change, but are meaningless.
* Return: final eta
*/
static int64_t secp256k1_modinv64_posdivsteps_62_var(int64_t eta, uint64_t f0, uint64_t g0, secp256k1_modinv64_trans2x2 *t, int *jacp) {
/* Transformation matrix; see comments in secp256k1_modinv64_divsteps_62. */
uint64_t u = 1, v = 0, q = 0, r = 1;
uint64_t f = f0, g = g0, m;
uint32_t w;
int i = 62, limit, zeros;
int jac = *jacp;
for (;;) {
/* Use a sentinel bit to count zeros only up to i. */
zeros = secp256k1_ctz64_var(g | (UINT64_MAX << i));
/* Perform zeros divsteps at once; they all just divide g by two. */
g >>= zeros;
u <<= zeros;
v <<= zeros;
eta -= zeros;
i -= zeros;
/* Update the bottom bit of jac: when dividing g by an odd power of 2,
* if (f mod 8) is 3 or 5, the Jacobi symbol changes sign. */
jac ^= (zeros & ((f >> 1) ^ (f >> 2)));
/* We're done once we've done 62 posdivsteps. */
if (i == 0) break;
VERIFY_CHECK((f & 1) == 1);
VERIFY_CHECK((g & 1) == 1);
VERIFY_CHECK((u * f0 + v * g0) == f << (62 - i));
VERIFY_CHECK((q * f0 + r * g0) == g << (62 - i));
/* If eta is negative, negate it and replace f,g with g,f. */
if (eta < 0) {
uint64_t tmp;
eta = -eta;
tmp = f; f = g; g = tmp;
tmp = u; u = q; q = tmp;
tmp = v; v = r; r = tmp;
/* Update bottom bit of jac: when swapping f and g, the Jacobi symbol changes sign
* if both f and g are 3 mod 4. */
jac ^= ((f & g) >> 1);
/* Use a formula to cancel out up to 6 bits of g. Also, no more than i can be cancelled
* out (as we'd be done before that point), and no more than eta+1 can be done as its
* sign will flip again once that happens. */
limit = ((int)eta + 1) > i ? i : ((int)eta + 1);
VERIFY_CHECK(limit > 0 && limit <= 62);
/* m is a mask for the bottom min(limit, 6) bits. */
m = (UINT64_MAX >> (64 - limit)) & 63U;
/* Find what multiple of f must be added to g to cancel its bottom min(limit, 6)
* bits. */
w = (f * g * (f * f - 2)) & m;
} else {
/* In this branch, use a simpler formula that only lets us cancel up to 4 bits of g, as
* eta tends to be smaller here. */
limit = ((int)eta + 1) > i ? i : ((int)eta + 1);
VERIFY_CHECK(limit > 0 && limit <= 62);
/* m is a mask for the bottom min(limit, 4) bits. */
m = (UINT64_MAX >> (64 - limit)) & 15U;
/* Find what multiple of f must be added to g to cancel its bottom min(limit, 4)
* bits. */
w = f + (((f + 1) & 4) << 1);
w = (-w * g) & m;
}
g += f * w;
q += u * w;
r += v * w;
VERIFY_CHECK((g & m) == 0);
}
/* Return data in t and return value. */
t->u = (int64_t)u;
t->v = (int64_t)v;
t->q = (int64_t)q;
t->r = (int64_t)r;
/* The determinant of t must be a power of two. This guarantees that multiplication with t
* does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which
* will be divided out again). As each divstep's individual matrix has determinant 2 or -2,
* the aggregate of 62 of them will have determinant 2^62 or -2^62. */
VERIFY_CHECK(secp256k1_modinv64_det_check_pow2(t, 62, 1));
*jacp = jac;
return eta;
}
/* Compute (t/2^62) * [d, e] mod modulus, where t is a transition matrix scaled by 2^62.
*
* On input and output, d and e are in range (-2*modulus,modulus). All output limbs will be in range
* (-2^62,2^62).
*
* This implements the update_de function from the explanation.
*/
static void secp256k1_modinv64_update_de_62(secp256k1_modinv64_signed62 *d, secp256k1_modinv64_signed62 *e, const secp256k1_modinv64_trans2x2 *t, const secp256k1_modinv64_modinfo* modinfo) {
const uint64_t M62 = UINT64_MAX >> 2;
const int64_t d0 = d->v[0], d1 = d->v[1], d2 = d->v[2], d3 = d->v[3], d4 = d->v[4];
const int64_t e0 = e->v[0], e1 = e->v[1], e2 = e->v[2], e3 = e->v[3], e4 = e->v[4];
const int64_t u = t->u, v = t->v, q = t->q, r = t->r;
int64_t md, me, sd, se;
secp256k1_int128 cd, ce;
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(d, 5, &modinfo->modulus, -2) > 0); /* d > -2*modulus */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(d, 5, &modinfo->modulus, 1) < 0); /* d < modulus */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(e, 5, &modinfo->modulus, -2) > 0); /* e > -2*modulus */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(e, 5, &modinfo->modulus, 1) < 0); /* e < modulus */
VERIFY_CHECK(secp256k1_modinv64_abs(u) <= (((int64_t)1 << 62) - secp256k1_modinv64_abs(v))); /* |u|+|v| <= 2^62 */
VERIFY_CHECK(secp256k1_modinv64_abs(q) <= (((int64_t)1 << 62) - secp256k1_modinv64_abs(r))); /* |q|+|r| <= 2^62 */
/* [md,me] start as zero; plus [u,q] if d is negative; plus [v,r] if e is negative. */
sd = d4 >> 63;
se = e4 >> 63;
md = (u & sd) + (v & se);
me = (q & sd) + (r & se);
/* Begin computing t*[d,e]. */
secp256k1_i128_mul(&cd, u, d0);
secp256k1_i128_accum_mul(&cd, v, e0);
secp256k1_i128_mul(&ce, q, d0);
secp256k1_i128_accum_mul(&ce, r, e0);
/* Correct md,me so that t*[d,e]+modulus*[md,me] has 62 zero bottom bits. */
md -= (modinfo->modulus_inv62 * secp256k1_i128_to_u64(&cd) + md) & M62;
me -= (modinfo->modulus_inv62 * secp256k1_i128_to_u64(&ce) + me) & M62;
/* Update the beginning of computation for t*[d,e]+modulus*[md,me] now md,me are known. */
secp256k1_i128_accum_mul(&cd, modinfo->modulus.v[0], md);
secp256k1_i128_accum_mul(&ce, modinfo->modulus.v[0], me);
/* Verify that the low 62 bits of the computation are indeed zero, and then throw them away. */
VERIFY_CHECK((secp256k1_i128_to_u64(&cd) & M62) == 0); secp256k1_i128_rshift(&cd, 62);
VERIFY_CHECK((secp256k1_i128_to_u64(&ce) & M62) == 0); secp256k1_i128_rshift(&ce, 62);
/* Compute limb 1 of t*[d,e]+modulus*[md,me], and store it as output limb 0 (= down shift). */
secp256k1_i128_accum_mul(&cd, u, d1);
secp256k1_i128_accum_mul(&cd, v, e1);
secp256k1_i128_accum_mul(&ce, q, d1);
secp256k1_i128_accum_mul(&ce, r, e1);
if (modinfo->modulus.v[1]) { /* Optimize for the case where limb of modulus is zero. */
secp256k1_i128_accum_mul(&cd, modinfo->modulus.v[1], md);
secp256k1_i128_accum_mul(&ce, modinfo->modulus.v[1], me);
}
d->v[0] = secp256k1_i128_to_u64(&cd) & M62; secp256k1_i128_rshift(&cd, 62);
e->v[0] = secp256k1_i128_to_u64(&ce) & M62; secp256k1_i128_rshift(&ce, 62);
/* Compute limb 2 of t*[d,e]+modulus*[md,me], and store it as output limb 1. */
secp256k1_i128_accum_mul(&cd, u, d2);
secp256k1_i128_accum_mul(&cd, v, e2);
secp256k1_i128_accum_mul(&ce, q, d2);
secp256k1_i128_accum_mul(&ce, r, e2);
if (modinfo->modulus.v[2]) { /* Optimize for the case where limb of modulus is zero. */
secp256k1_i128_accum_mul(&cd, modinfo->modulus.v[2], md);
secp256k1_i128_accum_mul(&ce, modinfo->modulus.v[2], me);
}
d->v[1] = secp256k1_i128_to_u64(&cd) & M62; secp256k1_i128_rshift(&cd, 62);
e->v[1] = secp256k1_i128_to_u64(&ce) & M62; secp256k1_i128_rshift(&ce, 62);
/* Compute limb 3 of t*[d,e]+modulus*[md,me], and store it as output limb 2. */
secp256k1_i128_accum_mul(&cd, u, d3);
secp256k1_i128_accum_mul(&cd, v, e3);
secp256k1_i128_accum_mul(&ce, q, d3);
secp256k1_i128_accum_mul(&ce, r, e3);
if (modinfo->modulus.v[3]) { /* Optimize for the case where limb of modulus is zero. */
secp256k1_i128_accum_mul(&cd, modinfo->modulus.v[3], md);
secp256k1_i128_accum_mul(&ce, modinfo->modulus.v[3], me);
}
d->v[2] = secp256k1_i128_to_u64(&cd) & M62; secp256k1_i128_rshift(&cd, 62);
e->v[2] = secp256k1_i128_to_u64(&ce) & M62; secp256k1_i128_rshift(&ce, 62);
/* Compute limb 4 of t*[d,e]+modulus*[md,me], and store it as output limb 3. */
secp256k1_i128_accum_mul(&cd, u, d4);
secp256k1_i128_accum_mul(&cd, v, e4);
secp256k1_i128_accum_mul(&ce, q, d4);
secp256k1_i128_accum_mul(&ce, r, e4);
secp256k1_i128_accum_mul(&cd, modinfo->modulus.v[4], md);
secp256k1_i128_accum_mul(&ce, modinfo->modulus.v[4], me);
d->v[3] = secp256k1_i128_to_u64(&cd) & M62; secp256k1_i128_rshift(&cd, 62);
e->v[3] = secp256k1_i128_to_u64(&ce) & M62; secp256k1_i128_rshift(&ce, 62);
/* What remains is limb 5 of t*[d,e]+modulus*[md,me]; store it as output limb 4. */
d->v[4] = secp256k1_i128_to_i64(&cd);
e->v[4] = secp256k1_i128_to_i64(&ce);
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(d, 5, &modinfo->modulus, -2) > 0); /* d > -2*modulus */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(d, 5, &modinfo->modulus, 1) < 0); /* d < modulus */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(e, 5, &modinfo->modulus, -2) > 0); /* e > -2*modulus */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(e, 5, &modinfo->modulus, 1) < 0); /* e < modulus */
}
/* Compute (t/2^62) * [f, g], where t is a transition matrix scaled by 2^62.
*
* This implements the update_fg function from the explanation.
*/
static void secp256k1_modinv64_update_fg_62(secp256k1_modinv64_signed62 *f, secp256k1_modinv64_signed62 *g, const secp256k1_modinv64_trans2x2 *t) {
const uint64_t M62 = UINT64_MAX >> 2;
const int64_t f0 = f->v[0], f1 = f->v[1], f2 = f->v[2], f3 = f->v[3], f4 = f->v[4];
const int64_t g0 = g->v[0], g1 = g->v[1], g2 = g->v[2], g3 = g->v[3], g4 = g->v[4];
const int64_t u = t->u, v = t->v, q = t->q, r = t->r;
secp256k1_int128 cf, cg;
/* Start computing t*[f,g]. */
secp256k1_i128_mul(&cf, u, f0);
secp256k1_i128_accum_mul(&cf, v, g0);
secp256k1_i128_mul(&cg, q, f0);
secp256k1_i128_accum_mul(&cg, r, g0);
/* Verify that the bottom 62 bits of the result are zero, and then throw them away. */
VERIFY_CHECK((secp256k1_i128_to_u64(&cf) & M62) == 0); secp256k1_i128_rshift(&cf, 62);
VERIFY_CHECK((secp256k1_i128_to_u64(&cg) & M62) == 0); secp256k1_i128_rshift(&cg, 62);
/* Compute limb 1 of t*[f,g], and store it as output limb 0 (= down shift). */
secp256k1_i128_accum_mul(&cf, u, f1);
secp256k1_i128_accum_mul(&cf, v, g1);
secp256k1_i128_accum_mul(&cg, q, f1);
secp256k1_i128_accum_mul(&cg, r, g1);
f->v[0] = secp256k1_i128_to_u64(&cf) & M62; secp256k1_i128_rshift(&cf, 62);
g->v[0] = secp256k1_i128_to_u64(&cg) & M62; secp256k1_i128_rshift(&cg, 62);
/* Compute limb 2 of t*[f,g], and store it as output limb 1. */
secp256k1_i128_accum_mul(&cf, u, f2);
secp256k1_i128_accum_mul(&cf, v, g2);
secp256k1_i128_accum_mul(&cg, q, f2);
secp256k1_i128_accum_mul(&cg, r, g2);
f->v[1] = secp256k1_i128_to_u64(&cf) & M62; secp256k1_i128_rshift(&cf, 62);
g->v[1] = secp256k1_i128_to_u64(&cg) & M62; secp256k1_i128_rshift(&cg, 62);
/* Compute limb 3 of t*[f,g], and store it as output limb 2. */
secp256k1_i128_accum_mul(&cf, u, f3);
secp256k1_i128_accum_mul(&cf, v, g3);
secp256k1_i128_accum_mul(&cg, q, f3);
secp256k1_i128_accum_mul(&cg, r, g3);
f->v[2] = secp256k1_i128_to_u64(&cf) & M62; secp256k1_i128_rshift(&cf, 62);
g->v[2] = secp256k1_i128_to_u64(&cg) & M62; secp256k1_i128_rshift(&cg, 62);
/* Compute limb 4 of t*[f,g], and store it as output limb 3. */
secp256k1_i128_accum_mul(&cf, u, f4);
secp256k1_i128_accum_mul(&cf, v, g4);
secp256k1_i128_accum_mul(&cg, q, f4);
secp256k1_i128_accum_mul(&cg, r, g4);
f->v[3] = secp256k1_i128_to_u64(&cf) & M62; secp256k1_i128_rshift(&cf, 62);
g->v[3] = secp256k1_i128_to_u64(&cg) & M62; secp256k1_i128_rshift(&cg, 62);
/* What remains is limb 5 of t*[f,g]; store it as output limb 4. */
f->v[4] = secp256k1_i128_to_i64(&cf);
g->v[4] = secp256k1_i128_to_i64(&cg);
}
/* Compute (t/2^62) * [f, g], where t is a transition matrix for 62 divsteps.
*
* Version that operates on a variable number of limbs in f and g.
*
* This implements the update_fg function from the explanation.
*/
static void secp256k1_modinv64_update_fg_62_var(int len, secp256k1_modinv64_signed62 *f, secp256k1_modinv64_signed62 *g, const secp256k1_modinv64_trans2x2 *t) {
const uint64_t M62 = UINT64_MAX >> 2;
const int64_t u = t->u, v = t->v, q = t->q, r = t->r;
int64_t fi, gi;
secp256k1_int128 cf, cg;
int i;
VERIFY_CHECK(len > 0);
/* Start computing t*[f,g]. */
fi = f->v[0];
gi = g->v[0];
secp256k1_i128_mul(&cf, u, fi);
secp256k1_i128_accum_mul(&cf, v, gi);
secp256k1_i128_mul(&cg, q, fi);
secp256k1_i128_accum_mul(&cg, r, gi);
/* Verify that the bottom 62 bits of the result are zero, and then throw them away. */
VERIFY_CHECK((secp256k1_i128_to_u64(&cf) & M62) == 0); secp256k1_i128_rshift(&cf, 62);
VERIFY_CHECK((secp256k1_i128_to_u64(&cg) & M62) == 0); secp256k1_i128_rshift(&cg, 62);
/* Now iteratively compute limb i=1..len of t*[f,g], and store them in output limb i-1 (shifting
* down by 62 bits). */
for (i = 1; i < len; ++i) {
fi = f->v[i];
gi = g->v[i];
secp256k1_i128_accum_mul(&cf, u, fi);
secp256k1_i128_accum_mul(&cf, v, gi);
secp256k1_i128_accum_mul(&cg, q, fi);
secp256k1_i128_accum_mul(&cg, r, gi);
f->v[i - 1] = secp256k1_i128_to_u64(&cf) & M62; secp256k1_i128_rshift(&cf, 62);
g->v[i - 1] = secp256k1_i128_to_u64(&cg) & M62; secp256k1_i128_rshift(&cg, 62);
}
/* What remains is limb (len) of t*[f,g]; store it as output limb (len-1). */
f->v[len - 1] = secp256k1_i128_to_i64(&cf);
g->v[len - 1] = secp256k1_i128_to_i64(&cg);
}
/* Compute the inverse of x modulo modinfo->modulus, and replace x with it (constant time in x). */
static void secp256k1_modinv64(secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo) {
/* Start with d=0, e=1, f=modulus, g=x, zeta=-1. */
secp256k1_modinv64_signed62 d = {{0, 0, 0, 0, 0}};
secp256k1_modinv64_signed62 e = {{1, 0, 0, 0, 0}};
secp256k1_modinv64_signed62 f = modinfo->modulus;
secp256k1_modinv64_signed62 g = *x;
int i;
int64_t zeta = -1; /* zeta = -(delta+1/2); delta starts at 1/2. */
/* Do 10 iterations of 59 divsteps each = 590 divsteps. This suffices for 256-bit inputs. */
for (i = 0; i < 10; ++i) {
/* Compute transition matrix and new zeta after 59 divsteps. */
secp256k1_modinv64_trans2x2 t;
zeta = secp256k1_modinv64_divsteps_59(zeta, f.v[0], g.v[0], &t);
/* Update d,e using that transition matrix. */
secp256k1_modinv64_update_de_62(&d, &e, &t, modinfo);
/* Update f,g using that transition matrix. */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, -1) > 0); /* f > -modulus */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, 1) <= 0); /* f <= modulus */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &modinfo->modulus, -1) > 0); /* g > -modulus */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &modinfo->modulus, 1) < 0); /* g < modulus */
secp256k1_modinv64_update_fg_62(&f, &g, &t);
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, -1) > 0); /* f > -modulus */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, 1) <= 0); /* f <= modulus */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &modinfo->modulus, -1) > 0); /* g > -modulus */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &modinfo->modulus, 1) < 0); /* g < modulus */
}
/* At this point sufficient iterations have been performed that g must have reached 0
* and (if g was not originally 0) f must now equal +/- GCD of the initial f, g
* values i.e. +/- 1, and d now contains +/- the modular inverse. */
/* g == 0 */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &SECP256K1_SIGNED62_ONE, 0) == 0);
/* |f| == 1, or (x == 0 and d == 0 and f == modulus) */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &SECP256K1_SIGNED62_ONE, -1) == 0 ||
secp256k1_modinv64_mul_cmp_62(&f, 5, &SECP256K1_SIGNED62_ONE, 1) == 0 ||
(secp256k1_modinv64_mul_cmp_62(x, 5, &SECP256K1_SIGNED62_ONE, 0) == 0 &&
secp256k1_modinv64_mul_cmp_62(&d, 5, &SECP256K1_SIGNED62_ONE, 0) == 0 &&
secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, 1) == 0));
/* Optionally negate d, normalize to [0,modulus), and return it. */
secp256k1_modinv64_normalize_62(&d, f.v[4], modinfo);
*x = d;
}
/* Compute the inverse of x modulo modinfo->modulus, and replace x with it (variable time). */
static void secp256k1_modinv64_var(secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo) {
/* Start with d=0, e=1, f=modulus, g=x, eta=-1. */
secp256k1_modinv64_signed62 d = {{0, 0, 0, 0, 0}};
secp256k1_modinv64_signed62 e = {{1, 0, 0, 0, 0}};
secp256k1_modinv64_signed62 f = modinfo->modulus;
secp256k1_modinv64_signed62 g = *x;
#ifdef VERIFY
int i = 0;
#endif
int j, len = 5;
int64_t eta = -1; /* eta = -delta; delta is initially 1 */
int64_t cond, fn, gn;
/* Do iterations of 62 divsteps each until g=0. */
while (1) {
/* Compute transition matrix and new eta after 62 divsteps. */
secp256k1_modinv64_trans2x2 t;
eta = secp256k1_modinv64_divsteps_62_var(eta, f.v[0], g.v[0], &t);
/* Update d,e using that transition matrix. */
secp256k1_modinv64_update_de_62(&d, &e, &t, modinfo);
/* Update f,g using that transition matrix. */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, -1) > 0); /* f > -modulus */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, -1) > 0); /* g > -modulus */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */
secp256k1_modinv64_update_fg_62_var(len, &f, &g, &t);
/* If the bottom limb of g is zero, there is a chance that g=0. */
if (g.v[0] == 0) {
cond = 0;
/* Check if the other limbs are also 0. */
for (j = 1; j < len; ++j) {
cond |= g.v[j];
}
/* If so, we're done. */
if (cond == 0) break;
}
/* Determine if len>1 and limb (len-1) of both f and g is 0 or -1. */
fn = f.v[len - 1];
gn = g.v[len - 1];
cond = ((int64_t)len - 2) >> 63;
cond |= fn ^ (fn >> 63);
cond |= gn ^ (gn >> 63);
/* If so, reduce length, propagating the sign of f and g's top limb into the one below. */
if (cond == 0) {
f.v[len - 2] |= (uint64_t)fn << 62;
g.v[len - 2] |= (uint64_t)gn << 62;
--len;
}
VERIFY_CHECK(++i < 12); /* We should never need more than 12*62 = 744 divsteps */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, -1) > 0); /* f > -modulus */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, -1) > 0); /* g > -modulus */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */
}
/* At this point g is 0 and (if g was not originally 0) f must now equal +/- GCD of
* the initial f, g values i.e. +/- 1, and d now contains +/- the modular inverse. */
/* g == 0 */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &SECP256K1_SIGNED62_ONE, 0) == 0);
/* |f| == 1, or (x == 0 and d == 0 and f == modulus) */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &SECP256K1_SIGNED62_ONE, -1) == 0 ||
secp256k1_modinv64_mul_cmp_62(&f, len, &SECP256K1_SIGNED62_ONE, 1) == 0 ||
(secp256k1_modinv64_mul_cmp_62(x, 5, &SECP256K1_SIGNED62_ONE, 0) == 0 &&
secp256k1_modinv64_mul_cmp_62(&d, 5, &SECP256K1_SIGNED62_ONE, 0) == 0 &&
secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) == 0));
/* Optionally negate d, normalize to [0,modulus), and return it. */
secp256k1_modinv64_normalize_62(&d, f.v[len - 1], modinfo);
*x = d;
}
/* Do up to 25 iterations of 62 posdivsteps (up to 1550 steps; more is extremely rare) each until f=1.
* In VERIFY mode use a lower number of iterations (744, close to the median 756), so failure actually occurs. */
#ifdef VERIFY
#define JACOBI64_ITERATIONS 12
#else
#define JACOBI64_ITERATIONS 25
#endif
/* Compute the Jacobi symbol of x modulo modinfo->modulus (variable time). gcd(x,modulus) must be 1. */
static int secp256k1_jacobi64_maybe_var(const secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo) {
/* Start with f=modulus, g=x, eta=-1. */
secp256k1_modinv64_signed62 f = modinfo->modulus;
secp256k1_modinv64_signed62 g = *x;
int j, len = 5;
int64_t eta = -1; /* eta = -delta; delta is initially 1 */
int64_t cond, fn, gn;
int jac = 0;
int count;
/* The input limbs must all be non-negative. */
VERIFY_CHECK(g.v[0] >= 0 && g.v[1] >= 0 && g.v[2] >= 0 && g.v[3] >= 0 && g.v[4] >= 0);
/* If x > 0, then if the loop below converges, it converges to f=g=gcd(x,modulus). Since we
* require that gcd(x,modulus)=1 and modulus>=3, x cannot be 0. Thus, we must reach f=1 (or
* time out). */
VERIFY_CHECK((g.v[0] | g.v[1] | g.v[2] | g.v[3] | g.v[4]) != 0);
for (count = 0; count < JACOBI64_ITERATIONS; ++count) {
/* Compute transition matrix and new eta after 62 posdivsteps. */
secp256k1_modinv64_trans2x2 t;
eta = secp256k1_modinv64_posdivsteps_62_var(eta, f.v[0] | ((uint64_t)f.v[1] << 62), g.v[0] | ((uint64_t)g.v[1] << 62), &t, &jac);
/* Update f,g using that transition matrix. */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 0) > 0); /* f > 0 */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 0) > 0); /* g > 0 */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */
secp256k1_modinv64_update_fg_62_var(len, &f, &g, &t);
/* If the bottom limb of f is 1, there is a chance that f=1. */
if (f.v[0] == 1) {
cond = 0;
/* Check if the other limbs are also 0. */
for (j = 1; j < len; ++j) {
cond |= f.v[j];
}
/* If so, we're done. When f=1, the Jacobi symbol (g | f)=1. */
if (cond == 0) return 1 - 2*(jac & 1);
}
/* Determine if len>1 and limb (len-1) of both f and g is 0. */
fn = f.v[len - 1];
gn = g.v[len - 1];
cond = ((int64_t)len - 2) >> 63;
cond |= fn;
cond |= gn;
/* If so, reduce length. */
if (cond == 0) --len;
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 0) > 0); /* f > 0 */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 0) > 0); /* g > 0 */
VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */
}
/* The loop failed to converge to f=g after 1550 iterations. Return 0, indicating unknown result. */
return 0;
}
#endif /* SECP256K1_MODINV64_IMPL_H */

View File

@@ -0,0 +1,5 @@
include_HEADERS += include/secp256k1_ecdh.h
noinst_HEADERS += src/modules/ecdh/main_impl.h
noinst_HEADERS += src/modules/ecdh/tests_impl.h
noinst_HEADERS += src/modules/ecdh/bench_impl.h
noinst_HEADERS += src/wycheproof/ecdh_secp256k1_test.h

View File

@@ -0,0 +1,54 @@
/***********************************************************************
* Copyright (c) 2015 Pieter Wuille, Andrew Poelstra *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_MODULE_ECDH_BENCH_H
#define SECP256K1_MODULE_ECDH_BENCH_H
#include "../../../include/secp256k1_ecdh.h"
typedef struct {
const secp256k1_context *ctx;
secp256k1_pubkey point;
unsigned char scalar[32];
} bench_ecdh_data;
static void bench_ecdh_setup(void* arg) {
int i;
bench_ecdh_data *data = (bench_ecdh_data*)arg;
const unsigned char point[] = {
0x03,
0x54, 0x94, 0xc1, 0x5d, 0x32, 0x09, 0x97, 0x06,
0xc2, 0x39, 0x5f, 0x94, 0x34, 0x87, 0x45, 0xfd,
0x75, 0x7c, 0xe3, 0x0e, 0x4e, 0x8c, 0x90, 0xfb,
0xa2, 0xba, 0xd1, 0x84, 0xf8, 0x83, 0xc6, 0x9f
};
for (i = 0; i < 32; i++) {
data->scalar[i] = i + 1;
}
CHECK(secp256k1_ec_pubkey_parse(data->ctx, &data->point, point, sizeof(point)) == 1);
}
static void bench_ecdh(void* arg, int iters) {
int i;
unsigned char res[32];
bench_ecdh_data *data = (bench_ecdh_data*)arg;
for (i = 0; i < iters; i++) {
CHECK(secp256k1_ecdh(data->ctx, res, &data->point, data->scalar, NULL, NULL) == 1);
}
}
static void run_ecdh_bench(int iters, int argc, char** argv) {
bench_ecdh_data data;
int d = argc == 1;
data.ctx = secp256k1_context_static;
if (d || have_flag(argc, argv, "ecdh")) run_benchmark("ecdh", bench_ecdh, bench_ecdh_setup, NULL, &data, 10, iters);
}
#endif /* SECP256K1_MODULE_ECDH_BENCH_H */

View File

@@ -0,0 +1,79 @@
/***********************************************************************
* Copyright (c) 2015 Andrew Poelstra *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_MODULE_ECDH_MAIN_H
#define SECP256K1_MODULE_ECDH_MAIN_H
#include "../../../include/secp256k1_ecdh.h"
#include "../../ecmult_const_impl.h"
static int ecdh_hash_function_sha256_impl(const secp256k1_hash_ctx *hash_ctx, unsigned char *output, const unsigned char *x32, const unsigned char *y32, void *data) {
unsigned char version = (y32[31] & 0x01) | 0x02;
secp256k1_sha256 sha;
(void)data;
secp256k1_sha256_initialize(&sha);
secp256k1_sha256_write(hash_ctx, &sha, &version, 1);
secp256k1_sha256_write(hash_ctx, &sha, x32, 32);
secp256k1_sha256_finalize(hash_ctx, &sha, output);
secp256k1_sha256_clear(&sha);
return 1;
}
static int ecdh_hash_function_sha256(unsigned char *output, const unsigned char *x32, const unsigned char *y32, void *data) {
return ecdh_hash_function_sha256_impl(secp256k1_get_hash_context(secp256k1_context_static), output, x32, y32, data);
}
const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_sha256 = ecdh_hash_function_sha256;
const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_default = ecdh_hash_function_sha256;
int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *output, const secp256k1_pubkey *point, const unsigned char *scalar, secp256k1_ecdh_hash_function hashfp, void *data) {
int ret = 0;
int overflow = 0;
secp256k1_gej res;
secp256k1_ge pt;
secp256k1_scalar s;
unsigned char x[32];
unsigned char y[32];
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(output != NULL);
ARG_CHECK(point != NULL);
ARG_CHECK(scalar != NULL);
secp256k1_pubkey_load(ctx, &pt, point);
secp256k1_scalar_set_b32(&s, scalar, &overflow);
overflow |= secp256k1_scalar_is_zero(&s);
secp256k1_scalar_cmov(&s, &secp256k1_scalar_one, overflow);
secp256k1_ecmult_const(&res, &pt, &s);
secp256k1_ge_set_gej(&pt, &res);
/* Compute a hash of the point */
secp256k1_fe_normalize(&pt.x);
secp256k1_fe_normalize(&pt.y);
secp256k1_fe_get_b32(x, &pt.x);
secp256k1_fe_get_b32(y, &pt.y);
if (hashfp == NULL) {
/* Use ctx-aware function by default */
ret = ecdh_hash_function_sha256_impl(secp256k1_get_hash_context(ctx), output, x, y, data);
} else {
ret = hashfp(output, x, y, data);
}
secp256k1_memclear_explicit(x, sizeof(x));
secp256k1_memclear_explicit(y, sizeof(y));
secp256k1_scalar_clear(&s);
secp256k1_ge_clear(&pt);
secp256k1_gej_clear(&res);
return !!ret & !overflow;
}
#endif /* SECP256K1_MODULE_ECDH_MAIN_H */

View File

@@ -0,0 +1,218 @@
/***********************************************************************
* Copyright (c) 2015 Andrew Poelstra *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_MODULE_ECDH_TESTS_H
#define SECP256K1_MODULE_ECDH_TESTS_H
#include "../../unit_test.h"
#include "../../testutil.h"
static int ecdh_hash_function_test_xpassthru(unsigned char *output, const unsigned char *x, const unsigned char *y, void *data) {
(void)y;
(void)data;
memcpy(output, x, 32);
return 1;
}
static int ecdh_hash_function_test_fail(unsigned char *output, const unsigned char *x, const unsigned char *y, void *data) {
(void)output;
(void)x;
(void)y;
(void)data;
return 0;
}
static int ecdh_hash_function_custom(unsigned char *output, const unsigned char *x, const unsigned char *y, void *data) {
(void)data;
/* Save x and y as uncompressed public key */
output[0] = 0x04;
memcpy(output + 1, x, 32);
memcpy(output + 33, y, 32);
return 1;
}
static void test_ecdh_api(void) {
secp256k1_pubkey point;
unsigned char res[32];
unsigned char s_one[32] = { 0 };
s_one[31] = 1;
CHECK(secp256k1_ec_pubkey_create(CTX, &point, s_one) == 1);
/* Check all NULLs are detected */
CHECK(secp256k1_ecdh(CTX, res, &point, s_one, NULL, NULL) == 1);
CHECK_ILLEGAL(CTX, secp256k1_ecdh(CTX, NULL, &point, s_one, NULL, NULL));
CHECK_ILLEGAL(CTX, secp256k1_ecdh(CTX, res, NULL, s_one, NULL, NULL));
CHECK_ILLEGAL(CTX, secp256k1_ecdh(CTX, res, &point, NULL, NULL, NULL));
CHECK(secp256k1_ecdh(CTX, res, &point, s_one, NULL, NULL) == 1);
}
static void test_ecdh_generator_basepoint(void) {
unsigned char s_one[32] = { 0 };
secp256k1_pubkey point[2];
int i;
s_one[31] = 1;
/* Check against pubkey creation when the basepoint is the generator */
for (i = 0; i < 2 * COUNT; ++i) {
secp256k1_sha256 sha;
unsigned char s_b32[32];
unsigned char output_ecdh[65];
unsigned char output_ser[32];
unsigned char point_ser[65];
size_t point_ser_len = sizeof(point_ser);
secp256k1_scalar s;
testutil_random_scalar_order(&s);
secp256k1_scalar_get_b32(s_b32, &s);
CHECK(secp256k1_ec_pubkey_create(CTX, &point[0], s_one) == 1);
CHECK(secp256k1_ec_pubkey_create(CTX, &point[1], s_b32) == 1);
/* compute using ECDH function with custom hash function */
CHECK(secp256k1_ecdh(CTX, output_ecdh, &point[0], s_b32, ecdh_hash_function_custom, NULL) == 1);
/* compute "explicitly" */
CHECK(secp256k1_ec_pubkey_serialize(CTX, point_ser, &point_ser_len, &point[1], SECP256K1_EC_UNCOMPRESSED) == 1);
/* compare */
CHECK(secp256k1_memcmp_var(output_ecdh, point_ser, 65) == 0);
/* compute using ECDH function with default hash function */
CHECK(secp256k1_ecdh(CTX, output_ecdh, &point[0], s_b32, NULL, NULL) == 1);
/* compute "explicitly" */
CHECK(secp256k1_ec_pubkey_serialize(CTX, point_ser, &point_ser_len, &point[1], SECP256K1_EC_COMPRESSED) == 1);
secp256k1_sha256_initialize(&sha);
secp256k1_sha256_write(secp256k1_get_hash_context(CTX), &sha, point_ser, point_ser_len);
secp256k1_sha256_finalize(secp256k1_get_hash_context(CTX), &sha, output_ser);
/* compare */
CHECK(secp256k1_memcmp_var(output_ecdh, output_ser, 32) == 0);
}
}
DEFINE_SHA256_TRANSFORM_PROBE(sha256_ecdh)
static void test_ecdh_ctx_sha256(void) {
/* Check ctx-provided SHA256 compression override takes effect */
secp256k1_context *ctx = secp256k1_context_clone(CTX);
unsigned char out_default[65], out_custom[65];
const unsigned char sk[32] = {1};
secp256k1_pubkey pubkey;
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, sk) == 1);
/* Default behavior */
CHECK(secp256k1_ecdh(ctx, out_default, &pubkey, sk, NULL, NULL) == 1);
CHECK(!sha256_ecdh_called);
/* Override SHA256 compression directly, bypassing the ctx setter sanity checks */
ctx->hash_ctx.fn_sha256_compression = sha256_ecdh;
CHECK(secp256k1_ecdh(ctx, out_custom, &pubkey, sk, NULL, NULL) == 1);
/* Outputs must differ if custom compression was used */
CHECK(secp256k1_memcmp_var(out_default, out_custom, 32) != 0);
CHECK(sha256_ecdh_called);
secp256k1_context_destroy(ctx);
}
static void test_bad_scalar(void) {
unsigned char s_zero[32] = { 0 };
unsigned char s_overflow[32] = { 0 };
unsigned char s_rand[32] = { 0 };
unsigned char output[32];
secp256k1_scalar rand;
secp256k1_pubkey point;
/* Create random point */
testutil_random_scalar_order(&rand);
secp256k1_scalar_get_b32(s_rand, &rand);
CHECK(secp256k1_ec_pubkey_create(CTX, &point, s_rand) == 1);
/* Try to multiply it by bad values */
memcpy(s_overflow, secp256k1_group_order_bytes, 32);
CHECK(secp256k1_ecdh(CTX, output, &point, s_zero, NULL, NULL) == 0);
CHECK(secp256k1_ecdh(CTX, output, &point, s_overflow, NULL, NULL) == 0);
/* ...and a good one */
s_overflow[31] -= 1;
CHECK(secp256k1_ecdh(CTX, output, &point, s_overflow, NULL, NULL) == 1);
/* Hash function failure results in ecdh failure */
CHECK(secp256k1_ecdh(CTX, output, &point, s_overflow, ecdh_hash_function_test_fail, NULL) == 0);
}
/** Test that ECDH(sG, 1/s) == ECDH((1/s)G, s) == ECDH(G, 1) for a few random s. */
static void test_result_basepoint(void) {
secp256k1_pubkey point;
secp256k1_scalar rand;
unsigned char s[32];
unsigned char s_inv[32];
unsigned char out[32];
unsigned char out_inv[32];
unsigned char out_base[32];
int i;
unsigned char s_one[32] = { 0 };
s_one[31] = 1;
CHECK(secp256k1_ec_pubkey_create(CTX, &point, s_one) == 1);
CHECK(secp256k1_ecdh(CTX, out_base, &point, s_one, NULL, NULL) == 1);
for (i = 0; i < 2 * COUNT; i++) {
testutil_random_scalar_order(&rand);
secp256k1_scalar_get_b32(s, &rand);
secp256k1_scalar_inverse(&rand, &rand);
secp256k1_scalar_get_b32(s_inv, &rand);
CHECK(secp256k1_ec_pubkey_create(CTX, &point, s) == 1);
CHECK(secp256k1_ecdh(CTX, out, &point, s_inv, NULL, NULL) == 1);
CHECK(secp256k1_memcmp_var(out, out_base, 32) == 0);
CHECK(secp256k1_ec_pubkey_create(CTX, &point, s_inv) == 1);
CHECK(secp256k1_ecdh(CTX, out_inv, &point, s, NULL, NULL) == 1);
CHECK(secp256k1_memcmp_var(out_inv, out_base, 32) == 0);
}
}
static void test_ecdh_wycheproof(void) {
#include "../../wycheproof/ecdh_secp256k1_test.h"
int t;
for (t = 0; t < SECP256K1_ECDH_WYCHEPROOF_NUMBER_TESTVECTORS; t++) {
int parsed_ok;
secp256k1_pubkey point;
const unsigned char *pk;
const unsigned char *sk;
const unsigned char *expected_shared_secret;
unsigned char output_ecdh[65] = { 0 };
int expected_result;
memset(&point, 0, sizeof(point));
pk = &wycheproof_ecdh_public_keys[testvectors[t].pk_offset];
parsed_ok = secp256k1_ec_pubkey_parse(CTX, &point, pk, testvectors[t].pk_len);
expected_result = testvectors[t].expected_result;
CHECK(parsed_ok == expected_result);
if (!parsed_ok) {
continue;
}
sk = &wycheproof_ecdh_private_keys[testvectors[t].sk_offset];
CHECK(testvectors[t].sk_len == 32);
CHECK(secp256k1_ecdh(CTX, output_ecdh, &point, sk, ecdh_hash_function_test_xpassthru, NULL) == 1);
expected_shared_secret = &wycheproof_ecdh_shared_secrets[testvectors[t].shared_offset];
CHECK(secp256k1_memcmp_var(output_ecdh, expected_shared_secret, testvectors[t].shared_len) == 0);
}
}
/* --- Test registry --- */
static const struct tf_test_entry tests_ecdh[] = {
CASE1(test_ecdh_api),
CASE1(test_ecdh_generator_basepoint),
CASE1(test_bad_scalar),
CASE1(test_result_basepoint),
CASE1(test_ecdh_wycheproof),
CASE1(test_ecdh_ctx_sha256),
};
#endif /* SECP256K1_MODULE_ECDH_TESTS_H */

View File

@@ -0,0 +1,5 @@
include_HEADERS += include/secp256k1_ellswift.h
noinst_HEADERS += src/modules/ellswift/bench_impl.h
noinst_HEADERS += src/modules/ellswift/main_impl.h
noinst_HEADERS += src/modules/ellswift/tests_impl.h
noinst_HEADERS += src/modules/ellswift/tests_exhaustive_impl.h

View File

@@ -0,0 +1,106 @@
/***********************************************************************
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_MODULE_ELLSWIFT_BENCH_H
#define SECP256K1_MODULE_ELLSWIFT_BENCH_H
#include "../../../include/secp256k1_ellswift.h"
typedef struct {
secp256k1_context *ctx;
secp256k1_pubkey point[256];
unsigned char rnd64[64];
} bench_ellswift_data;
static void bench_ellswift_setup(void *arg) {
int i;
bench_ellswift_data *data = (bench_ellswift_data*)arg;
static const unsigned char init[64] = {
0x78, 0x1f, 0xb7, 0xd4, 0x67, 0x7f, 0x08, 0x68,
0xdb, 0xe3, 0x1d, 0x7f, 0x1b, 0xb0, 0xf6, 0x9e,
0x0a, 0x64, 0xca, 0x32, 0x9e, 0xc6, 0x20, 0x79,
0x03, 0xf3, 0xd0, 0x46, 0x7a, 0x0f, 0xd2, 0x21,
0xb0, 0x2c, 0x46, 0xd8, 0xba, 0xca, 0x26, 0x4f,
0x8f, 0x8c, 0xd4, 0xdd, 0x2d, 0x04, 0xbe, 0x30,
0x48, 0x51, 0x1e, 0xd4, 0x16, 0xfd, 0x42, 0x85,
0x62, 0xc9, 0x02, 0xf9, 0x89, 0x84, 0xff, 0xdc
};
memcpy(data->rnd64, init, 64);
for (i = 0; i < 256; ++i) {
int j;
CHECK(secp256k1_ellswift_decode(data->ctx, &data->point[i], data->rnd64));
for (j = 0; j < 64; ++j) {
data->rnd64[j] += 1;
}
}
CHECK(secp256k1_ellswift_encode(data->ctx, data->rnd64, &data->point[255], init + 16));
}
static void bench_ellswift_encode(void *arg, int iters) {
int i;
bench_ellswift_data *data = (bench_ellswift_data*)arg;
for (i = 0; i < iters; i++) {
CHECK(secp256k1_ellswift_encode(data->ctx, data->rnd64, &data->point[i & 255], data->rnd64 + 16));
}
}
static void bench_ellswift_create(void *arg, int iters) {
int i;
bench_ellswift_data *data = (bench_ellswift_data*)arg;
for (i = 0; i < iters; i++) {
unsigned char buf[64];
CHECK(secp256k1_ellswift_create(data->ctx, buf, data->rnd64, data->rnd64 + 32));
memcpy(data->rnd64, buf, 64);
}
}
static void bench_ellswift_decode(void *arg, int iters) {
int i;
secp256k1_pubkey out;
size_t len;
bench_ellswift_data *data = (bench_ellswift_data*)arg;
for (i = 0; i < iters; i++) {
CHECK(secp256k1_ellswift_decode(data->ctx, &out, data->rnd64) == 1);
len = 33;
CHECK(secp256k1_ec_pubkey_serialize(data->ctx, data->rnd64 + (i % 32), &len, &out, SECP256K1_EC_COMPRESSED));
}
}
static void bench_ellswift_xdh(void *arg, int iters) {
int i;
bench_ellswift_data *data = (bench_ellswift_data*)arg;
for (i = 0; i < iters; i++) {
int party = i & 1;
CHECK(secp256k1_ellswift_xdh(data->ctx,
data->rnd64 + (i % 33),
data->rnd64,
data->rnd64,
data->rnd64 + ((i + 16) % 33),
party,
secp256k1_ellswift_xdh_hash_function_bip324,
NULL) == 1);
}
}
void run_ellswift_bench(int iters, int argc, char **argv) {
bench_ellswift_data data;
int d = argc == 1;
/* create a context with signing capabilities */
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
if (d || have_flag(argc, argv, "ellswift") || have_flag(argc, argv, "encode") || have_flag(argc, argv, "ellswift_encode")) run_benchmark("ellswift_encode", bench_ellswift_encode, bench_ellswift_setup, NULL, &data, 10, iters);
if (d || have_flag(argc, argv, "ellswift") || have_flag(argc, argv, "decode") || have_flag(argc, argv, "ellswift_decode")) run_benchmark("ellswift_decode", bench_ellswift_decode, bench_ellswift_setup, NULL, &data, 10, iters);
if (d || have_flag(argc, argv, "ellswift") || have_flag(argc, argv, "keygen") || have_flag(argc, argv, "ellswift_keygen")) run_benchmark("ellswift_keygen", bench_ellswift_create, bench_ellswift_setup, NULL, &data, 10, iters);
if (d || have_flag(argc, argv, "ellswift") || have_flag(argc, argv, "ecdh") || have_flag(argc, argv, "ellswift_ecdh")) run_benchmark("ellswift_ecdh", bench_ellswift_xdh, bench_ellswift_setup, NULL, &data, 10, iters);
secp256k1_context_destroy(data.ctx);
}
#endif

View File

@@ -0,0 +1,581 @@
/***********************************************************************
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_MODULE_ELLSWIFT_MAIN_H
#define SECP256K1_MODULE_ELLSWIFT_MAIN_H
#include "../../../include/secp256k1.h"
#include "../../../include/secp256k1_ellswift.h"
#include "../../eckey.h"
#include "../../hash.h"
/** c1 = (sqrt(-3)-1)/2 */
static const secp256k1_fe secp256k1_ellswift_c1 = SECP256K1_FE_CONST(0x851695d4, 0x9a83f8ef, 0x919bb861, 0x53cbcb16, 0x630fb68a, 0xed0a766a, 0x3ec693d6, 0x8e6afa40);
/** c2 = (-sqrt(-3)-1)/2 = -(c1+1) */
static const secp256k1_fe secp256k1_ellswift_c2 = SECP256K1_FE_CONST(0x7ae96a2b, 0x657c0710, 0x6e64479e, 0xac3434e9, 0x9cf04975, 0x12f58995, 0xc1396c28, 0x719501ee);
/** c3 = (-sqrt(-3)+1)/2 = -c1 = c2+1 */
static const secp256k1_fe secp256k1_ellswift_c3 = SECP256K1_FE_CONST(0x7ae96a2b, 0x657c0710, 0x6e64479e, 0xac3434e9, 0x9cf04975, 0x12f58995, 0xc1396c28, 0x719501ef);
/** c4 = (sqrt(-3)+1)/2 = -c2 = c1+1 */
static const secp256k1_fe secp256k1_ellswift_c4 = SECP256K1_FE_CONST(0x851695d4, 0x9a83f8ef, 0x919bb861, 0x53cbcb16, 0x630fb68a, 0xed0a766a, 0x3ec693d6, 0x8e6afa41);
/** Decode ElligatorSwift encoding (u, t) to a fraction xn/xd representing a curve X coordinate. */
static void secp256k1_ellswift_xswiftec_frac_var(secp256k1_fe *xn, secp256k1_fe *xd, const secp256k1_fe *u, const secp256k1_fe *t) {
/* The implemented algorithm is the following (all operations in GF(p)):
*
* - Let c0 = sqrt(-3) = 0xa2d2ba93507f1df233770c2a797962cc61f6d15da14ecd47d8d27ae1cd5f852.
* - If u = 0, set u = 1.
* - If t = 0, set t = 1.
* - If u^3+7+t^2 = 0, set t = 2*t.
* - Let X = (u^3+7-t^2)/(2*t).
* - Let Y = (X+t)/(c0*u).
* - If x3 = u+4*Y^2 is a valid x coordinate, return it.
* - If x2 = (-X/Y-u)/2 is a valid x coordinate, return it.
* - Return x1 = (X/Y-u)/2 (which is now guaranteed to be a valid x coordinate).
*
* Introducing s=t^2, g=u^3+7, and simplifying x1=-(x2+u) we get:
*
* - Let c0 = ...
* - If u = 0, set u = 1.
* - If t = 0, set t = 1.
* - Let s = t^2
* - Let g = u^3+7
* - If g+s = 0, set t = 2*t, s = 4*s
* - Let X = (g-s)/(2*t).
* - Let Y = (X+t)/(c0*u) = (g+s)/(2*c0*t*u).
* - If x3 = u+4*Y^2 is a valid x coordinate, return it.
* - If x2 = (-X/Y-u)/2 is a valid x coordinate, return it.
* - Return x1 = -(x2+u).
*
* Now substitute Y^2 = -(g+s)^2/(12*s*u^2) and X/Y = c0*u*(g-s)/(g+s). This
* means X and Y do not need to be evaluated explicitly anymore.
*
* - ...
* - If g+s = 0, set s = 4*s.
* - If x3 = u-(g+s)^2/(3*s*u^2) is a valid x coordinate, return it.
* - If x2 = (-c0*u*(g-s)/(g+s)-u)/2 is a valid x coordinate, return it.
* - Return x1 = -(x2+u).
*
* Simplifying x2 using 2 additional constants:
*
* - Let c1 = (c0-1)/2 = 0x851695d49a83f8ef919bb86153cbcb16630fb68aed0a766a3ec693d68e6afa40.
* - Let c2 = (-c0-1)/2 = 0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee.
* - ...
* - If x2 = u*(c1*s+c2*g)/(g+s) is a valid x coordinate, return it.
* - ...
*
* Writing x3 as a fraction:
*
* - ...
* - If x3 = (3*s*u^3-(g+s)^2)/(3*s*u^2) ...
* - ...
* Overall, we get:
*
* - Let c1 = 0x851695d49a83f8ef919bb86153cbcb16630fb68aed0a766a3ec693d68e6afa40.
* - Let c2 = 0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee.
* - If u = 0, set u = 1.
* - If t = 0, set s = 1, else set s = t^2.
* - Let g = u^3+7.
* - If g+s = 0, set s = 4*s.
* - If x3 = (3*s*u^3-(g+s)^2)/(3*s*u^2) is a valid x coordinate, return it.
* - If x2 = u*(c1*s+c2*g)/(g+s) is a valid x coordinate, return it.
* - Return x1 = -(x2+u).
*/
secp256k1_fe u1, s, g, p, d, n, l;
u1 = *u;
if (EXPECT(secp256k1_fe_normalizes_to_zero_var(&u1), 0)) u1 = secp256k1_fe_one;
secp256k1_fe_sqr(&s, t);
if (EXPECT(secp256k1_fe_normalizes_to_zero_var(t), 0)) s = secp256k1_fe_one;
secp256k1_fe_sqr(&l, &u1); /* l = u^2 */
secp256k1_fe_mul(&g, &l, &u1); /* g = u^3 */
secp256k1_fe_add_int(&g, SECP256K1_B); /* g = u^3 + 7 */
p = g; /* p = g */
secp256k1_fe_add(&p, &s); /* p = g+s */
if (EXPECT(secp256k1_fe_normalizes_to_zero_var(&p), 0)) {
secp256k1_fe_mul_int(&s, 4);
/* Recompute p = g+s */
p = g; /* p = g */
secp256k1_fe_add(&p, &s); /* p = g+s */
}
secp256k1_fe_mul(&d, &s, &l); /* d = s*u^2 */
secp256k1_fe_mul_int(&d, 3); /* d = 3*s*u^2 */
secp256k1_fe_sqr(&l, &p); /* l = (g+s)^2 */
secp256k1_fe_negate(&l, &l, 1); /* l = -(g+s)^2 */
secp256k1_fe_mul(&n, &d, &u1); /* n = 3*s*u^3 */
secp256k1_fe_add(&n, &l); /* n = 3*s*u^3-(g+s)^2 */
if (secp256k1_ge_x_frac_on_curve_var(&n, &d)) {
/* Return x3 = n/d = (3*s*u^3-(g+s)^2)/(3*s*u^2) */
*xn = n;
*xd = d;
return;
}
*xd = p;
secp256k1_fe_mul(&l, &secp256k1_ellswift_c1, &s); /* l = c1*s */
secp256k1_fe_mul(&n, &secp256k1_ellswift_c2, &g); /* n = c2*g */
secp256k1_fe_add(&n, &l); /* n = c1*s+c2*g */
secp256k1_fe_mul(&n, &n, &u1); /* n = u*(c1*s+c2*g) */
/* Possible optimization: in the invocation below, p^2 = (g+s)^2 is computed,
* which we already have computed above. This could be deduplicated. */
if (secp256k1_ge_x_frac_on_curve_var(&n, &p)) {
/* Return x2 = n/p = u*(c1*s+c2*g)/(g+s) */
*xn = n;
return;
}
secp256k1_fe_mul(&l, &p, &u1); /* l = u*(g+s) */
secp256k1_fe_add(&n, &l); /* n = u*(c1*s+c2*g)+u*(g+s) */
secp256k1_fe_negate(xn, &n, 2); /* n = -u*(c1*s+c2*g)-u*(g+s) */
VERIFY_CHECK(secp256k1_ge_x_frac_on_curve_var(xn, &p));
/* Return x3 = n/p = -(u*(c1*s+c2*g)/(g+s)+u) */
}
/** Decode ElligatorSwift encoding (u, t) to X coordinate. */
static void secp256k1_ellswift_xswiftec_var(secp256k1_fe *x, const secp256k1_fe *u, const secp256k1_fe *t) {
secp256k1_fe xn, xd;
secp256k1_ellswift_xswiftec_frac_var(&xn, &xd, u, t);
secp256k1_fe_inv_var(&xd, &xd);
secp256k1_fe_mul(x, &xn, &xd);
}
/** Decode ElligatorSwift encoding (u, t) to point P. */
static void secp256k1_ellswift_swiftec_var(secp256k1_ge *p, const secp256k1_fe *u, const secp256k1_fe *t) {
secp256k1_fe x;
secp256k1_ellswift_xswiftec_var(&x, u, t);
secp256k1_ge_set_xo_var(p, &x, secp256k1_fe_is_odd(t));
}
/* Try to complete an ElligatorSwift encoding (u, t) for X coordinate x, given u and x.
*
* There may be up to 8 distinct t values such that (u, t) decodes back to x, but also
* fewer, or none at all. Each such partial inverse can be accessed individually using a
* distinct input argument c (in range 0-7), and some or all of these may return failure.
* The following guarantees exist:
* - Given (x, u), no two distinct c values give the same successful result t.
* - Every successful result maps back to x through secp256k1_ellswift_xswiftec_var.
* - Given (x, u), all t values that map back to x can be reached by combining the
* successful results from this function over all c values, with the exception of:
* - this function cannot be called with u=0
* - no result with t=0 will be returned
* - no result for which u^3 + t^2 + 7 = 0 will be returned.
*
* The rather unusual encoding of bits in c (a large "if" based on the middle bit, and then
* using the low and high bits to pick signs of square roots) is to match the paper's
* encoding more closely: c=0 through c=3 match branches 1..4 in the paper, while c=4 through
* c=7 are copies of those with an additional negation of sqrt(w).
*/
static int secp256k1_ellswift_xswiftec_inv_var(secp256k1_fe *t, const secp256k1_fe *x_in, const secp256k1_fe *u_in, int c) {
/* The implemented algorithm is this (all arithmetic, except involving c, is mod p):
*
* - If (c & 2) = 0:
* - If (-x-u) is a valid X coordinate, fail.
* - Let s=-(u^3+7)/(u^2+u*x+x^2).
* - If s is not square, fail.
* - Let v=x.
* - If (c & 2) = 2:
* - Let s=x-u.
* - If s is not square, fail.
* - Let r=sqrt(-s*(4*(u^3+7)+3*u^2*s)); fail if it doesn't exist.
* - If (c & 1) = 1 and r = 0, fail.
* - If s=0, fail.
* - Let v=(r/s-u)/2.
* - Let w=sqrt(s).
* - If (c & 5) = 0: return -w*(c3*u + v).
* - If (c & 5) = 1: return w*(c4*u + v).
* - If (c & 5) = 4: return w*(c3*u + v).
* - If (c & 5) = 5: return -w*(c4*u + v).
*/
secp256k1_fe x = *x_in, u = *u_in, g, v, s, m, r, q;
int ret;
secp256k1_fe_normalize_weak(&x);
secp256k1_fe_normalize_weak(&u);
VERIFY_CHECK(c >= 0 && c < 8);
VERIFY_CHECK(secp256k1_ge_x_on_curve_var(&x));
if (!(c & 2)) {
/* c is in {0, 1, 4, 5}. In this case we look for an inverse under the x1 (if c=0 or
* c=4) formula, or x2 (if c=1 or c=5) formula. */
/* If -u-x is a valid X coordinate, fail. This would yield an encoding that roundtrips
* back under the x3 formula instead (which has priority over x1 and x2, so the decoding
* would not match x). */
m = x; /* m = x */
secp256k1_fe_add(&m, &u); /* m = u+x */
secp256k1_fe_negate(&m, &m, 2); /* m = -u-x */
/* Test if (-u-x) is a valid X coordinate. If so, fail. */
if (secp256k1_ge_x_on_curve_var(&m)) return 0;
/* Let s = -(u^3 + 7)/(u^2 + u*x + x^2) [first part] */
secp256k1_fe_sqr(&s, &m); /* s = (u+x)^2 */
secp256k1_fe_negate(&s, &s, 1); /* s = -(u+x)^2 */
secp256k1_fe_mul(&m, &u, &x); /* m = u*x */
secp256k1_fe_add(&s, &m); /* s = -(u^2 + u*x + x^2) */
/* Note that at this point, s = 0 is impossible. If it were the case:
* s = -(u^2 + u*x + x^2) = 0
* => u^2 + u*x + x^2 = 0
* => (u + 2*x) * (u^2 + u*x + x^2) = 0
* => 2*x^3 + 3*x^2*u + 3*x*u^2 + u^3 = 0
* => (x + u)^3 + x^3 = 0
* => x^3 = -(x + u)^3
* => x^3 + B = (-u - x)^3 + B
*
* However, we know x^3 + B is square (because x is on the curve) and
* that (-u-x)^3 + B is not square (the secp256k1_ge_x_on_curve_var(&m)
* test above would have failed). This is a contradiction, and thus the
* assumption s=0 is false. */
VERIFY_CHECK(!secp256k1_fe_normalizes_to_zero_var(&s));
/* If s is not square, fail. We have not fully computed s yet, but s is square iff
* -(u^3+7)*(u^2+u*x+x^2) is square (because a/b is square iff a*b is square and b is
* nonzero). */
secp256k1_fe_sqr(&g, &u); /* g = u^2 */
secp256k1_fe_mul(&g, &g, &u); /* g = u^3 */
secp256k1_fe_add_int(&g, SECP256K1_B); /* g = u^3+7 */
secp256k1_fe_mul(&m, &s, &g); /* m = -(u^3 + 7)*(u^2 + u*x + x^2) */
if (!secp256k1_fe_is_square_var(&m)) return 0;
/* Let s = -(u^3 + 7)/(u^2 + u*x + x^2) [second part] */
secp256k1_fe_inv_var(&s, &s); /* s = -1/(u^2 + u*x + x^2) [no div by 0] */
secp256k1_fe_mul(&s, &s, &g); /* s = -(u^3 + 7)/(u^2 + u*x + x^2) */
/* Let v = x. */
v = x;
} else {
/* c is in {2, 3, 6, 7}. In this case we look for an inverse under the x3 formula. */
/* Let s = x-u. */
secp256k1_fe_negate(&m, &u, 1); /* m = -u */
s = m; /* s = -u */
secp256k1_fe_add(&s, &x); /* s = x-u */
/* If s is not square, fail. */
if (!secp256k1_fe_is_square_var(&s)) return 0;
/* Let r = sqrt(-s*(4*(u^3+7)+3*u^2*s)); fail if it doesn't exist. */
secp256k1_fe_sqr(&g, &u); /* g = u^2 */
secp256k1_fe_mul(&q, &s, &g); /* q = s*u^2 */
secp256k1_fe_mul_int(&q, 3); /* q = 3*s*u^2 */
secp256k1_fe_mul(&g, &g, &u); /* g = u^3 */
secp256k1_fe_mul_int(&g, 4); /* g = 4*u^3 */
secp256k1_fe_add_int(&g, 4 * SECP256K1_B); /* g = 4*(u^3+7) */
secp256k1_fe_add(&q, &g); /* q = 4*(u^3+7)+3*s*u^2 */
secp256k1_fe_mul(&q, &q, &s); /* q = s*(4*(u^3+7)+3*u^2*s) */
secp256k1_fe_negate(&q, &q, 1); /* q = -s*(4*(u^3+7)+3*u^2*s) */
if (!secp256k1_fe_is_square_var(&q)) return 0;
ret = secp256k1_fe_sqrt(&r, &q); /* r = sqrt(-s*(4*(u^3+7)+3*u^2*s)) */
#ifdef VERIFY
VERIFY_CHECK(ret);
#else
(void)ret;
#endif
/* If (c & 1) = 1 and r = 0, fail. */
if (EXPECT((c & 1) && secp256k1_fe_normalizes_to_zero_var(&r), 0)) return 0;
/* If s = 0, fail. */
if (EXPECT(secp256k1_fe_normalizes_to_zero_var(&s), 0)) return 0;
/* Let v = (r/s-u)/2. */
secp256k1_fe_inv_var(&v, &s); /* v = 1/s [no div by 0] */
secp256k1_fe_mul(&v, &v, &r); /* v = r/s */
secp256k1_fe_add(&v, &m); /* v = r/s-u */
secp256k1_fe_half(&v); /* v = (r/s-u)/2 */
}
/* Let w = sqrt(s). */
ret = secp256k1_fe_sqrt(&m, &s); /* m = sqrt(s) = w */
VERIFY_CHECK(ret);
/* Return logic. */
if ((c & 5) == 0 || (c & 5) == 5) {
secp256k1_fe_negate(&m, &m, 1); /* m = -w */
}
/* Now m = {-w if c&5=0 or c&5=5; w otherwise}. */
secp256k1_fe_mul(&u, &u, c&1 ? &secp256k1_ellswift_c4 : &secp256k1_ellswift_c3);
/* u = {c4 if c&1=1; c3 otherwise}*u */
secp256k1_fe_add(&u, &v); /* u = {c4 if c&1=1; c3 otherwise}*u + v */
secp256k1_fe_mul(t, &m, &u);
return 1;
}
/** Use SHA256 as a PRNG, returning SHA256(hasher || cnt).
*
* hasher is a SHA256 object to which an incrementing 4-byte counter is written to generate randomness.
* Writing 13 bytes (4 bytes for counter, plus 9 bytes for the SHA256 padding) cannot cross a
* 64-byte block size boundary (to make sure it only triggers a single SHA256 compression). */
static void secp256k1_ellswift_prng(const secp256k1_hash_ctx *hash_ctx, unsigned char* out32, const secp256k1_sha256 *hasher, uint32_t cnt) {
secp256k1_sha256 hash = *hasher;
unsigned char buf4[4];
#ifdef VERIFY
size_t blocks = hash.bytes >> 6;
#endif
buf4[0] = cnt;
buf4[1] = cnt >> 8;
buf4[2] = cnt >> 16;
buf4[3] = cnt >> 24;
secp256k1_sha256_write(hash_ctx, &hash, buf4, 4);
secp256k1_sha256_finalize(hash_ctx, &hash, out32);
/* Writing and finalizing together should trigger exactly one SHA256 compression. */
VERIFY_CHECK(((hash.bytes) >> 6) == (blocks + 1));
}
/** Find an ElligatorSwift encoding (u, t) for X coordinate x, and random Y coordinate.
*
* u32 is the 32-byte big endian encoding of u; t is the output field element t that still
* needs encoding.
*
* hasher is a hasher in the secp256k1_ellswift_prng sense, with the same restrictions. */
static void secp256k1_ellswift_xelligatorswift_var(const secp256k1_context *ctx, unsigned char *u32, secp256k1_fe *t, const secp256k1_fe *x, const secp256k1_sha256 *hasher) {
/* Pool of 3-bit branch values. */
unsigned char branch_hash[32];
/* Number of 3-bit values in branch_hash left. */
int branches_left = 0;
/* Field elements u and branch values are extracted from RNG based on hasher for consecutive
* values of cnt. cnt==0 is first used to populate a pool of 64 4-bit branch values. The 64
* cnt values that follow are used to generate field elements u. cnt==65 (and multiples
* thereof) are used to repopulate the pool and start over, if that were ever necessary.
* On average, 4 iterations are needed. */
uint32_t cnt = 0;
while (1) {
int branch;
secp256k1_fe u;
/* If the pool of branch values is empty, populate it. */
if (branches_left == 0) {
secp256k1_ellswift_prng(secp256k1_get_hash_context(ctx), branch_hash, hasher, cnt++);
branches_left = 64;
}
/* Take a 3-bit branch value from the branch pool (top bit is discarded). */
--branches_left;
branch = (branch_hash[branches_left >> 1] >> ((branches_left & 1) << 2)) & 7;
/* Compute a new u value by hashing. */
secp256k1_ellswift_prng(secp256k1_get_hash_context(ctx), u32, hasher, cnt++);
/* overflow is not a problem (we prefer uniform u32 over uniform u). */
secp256k1_fe_set_b32_mod(&u, u32);
/* Since u is the output of a hash, it should practically never be 0. We could apply the
* u=0 to u=1 correction here too to deal with that case still, but it's such a low
* probability event that we do not bother. */
VERIFY_CHECK(!secp256k1_fe_normalizes_to_zero_var(&u));
/* Find a remainder t, and return it if found. */
if (EXPECT(secp256k1_ellswift_xswiftec_inv_var(t, x, &u, branch), 0)) break;
}
}
/** Find an ElligatorSwift encoding (u, t) for point P.
*
* This is similar secp256k1_ellswift_xelligatorswift_var, except it takes a full group element p
* as input, and returns an encoding that matches the provided Y coordinate rather than a random
* one.
*/
static void secp256k1_ellswift_elligatorswift_var(const secp256k1_context *ctx, unsigned char *u32, secp256k1_fe *t, const secp256k1_ge *p, const secp256k1_sha256 *hasher) {
secp256k1_ellswift_xelligatorswift_var(ctx, u32, t, &p->x, hasher);
secp256k1_fe_normalize_var(t);
if (secp256k1_fe_is_odd(t) != secp256k1_fe_is_odd(&p->y)) {
secp256k1_fe_negate(t, t, 1);
secp256k1_fe_normalize_var(t);
}
}
/** Set hash state to the BIP340 tagged hash midstate for "secp256k1_ellswift_encode". */
static void secp256k1_ellswift_sha256_init_encode(secp256k1_sha256* hash) {
static const uint32_t midstate[8] = {
0xd1a6524bul, 0x028594b3ul, 0x96e42f4eul, 0x1037a177ul,
0x1b8fcb8bul, 0x56023885ul, 0x2560ede1ul, 0xd626b715ul
};
secp256k1_sha256_initialize_midstate(hash, 64, midstate);
}
int secp256k1_ellswift_encode(const secp256k1_context *ctx, unsigned char *ell64, const secp256k1_pubkey *pubkey, const unsigned char *rnd32) {
secp256k1_ge p;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(ell64 != NULL);
ARG_CHECK(pubkey != NULL);
ARG_CHECK(rnd32 != NULL);
if (secp256k1_pubkey_load(ctx, &p, pubkey)) {
secp256k1_fe t;
unsigned char p64[64] = {0};
secp256k1_sha256 hash;
/* Set up hasher state; the used RNG is H(pubkey || "\x00"*31 || rnd32 || cnt++), using
* BIP340 tagged hash with tag "secp256k1_ellswift_encode". */
secp256k1_ellswift_sha256_init_encode(&hash);
secp256k1_eckey_pubkey_serialize33(&p, p64);
secp256k1_sha256_write(secp256k1_get_hash_context(ctx), &hash, p64, sizeof(p64));
secp256k1_sha256_write(secp256k1_get_hash_context(ctx), &hash, rnd32, 32);
/* Compute ElligatorSwift encoding and construct output. */
secp256k1_ellswift_elligatorswift_var(ctx, ell64, &t, &p, &hash); /* puts u in ell64[0..32] */
secp256k1_fe_get_b32(ell64 + 32, &t); /* puts t in ell64[32..64] */
return 1;
}
/* Only reached in case the provided pubkey is invalid. */
memset(ell64, 0, 64);
return 0;
}
/** Set hash state to the BIP340 tagged hash midstate for "secp256k1_ellswift_create". */
static void secp256k1_ellswift_sha256_init_create(secp256k1_sha256* hash) {
static const uint32_t midstate[8] = {
0xd29e1bf5ul, 0xf7025f42ul, 0x9b024773ul, 0x094cb7d5ul,
0xe59ed789ul, 0x03bc9786ul, 0x68335b35ul, 0x4e363b53ul
};
secp256k1_sha256_initialize_midstate(hash, 64, midstate);
}
int secp256k1_ellswift_create(const secp256k1_context *ctx, unsigned char *ell64, const unsigned char *seckey32, const unsigned char *auxrnd32) {
secp256k1_ge p;
secp256k1_fe t;
secp256k1_sha256 hash;
secp256k1_scalar seckey_scalar;
int ret;
static const unsigned char zero32[32] = {0};
/* Sanity check inputs. */
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(ell64 != NULL);
memset(ell64, 0, 64);
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
ARG_CHECK(seckey32 != NULL);
/* Compute (affine) public key */
ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &seckey_scalar, &p, seckey32);
secp256k1_declassify(ctx, &p, sizeof(p)); /* not constant time in produced pubkey */
secp256k1_fe_normalize_var(&p.x);
secp256k1_fe_normalize_var(&p.y);
/* Set up hasher state. The used RNG is H(privkey || "\x00"*32 [|| auxrnd32] || cnt++),
* using BIP340 tagged hash with tag "secp256k1_ellswift_create". */
secp256k1_ellswift_sha256_init_create(&hash);
secp256k1_sha256_write(secp256k1_get_hash_context(ctx), &hash, seckey32, 32);
secp256k1_sha256_write(secp256k1_get_hash_context(ctx), &hash, zero32, sizeof(zero32));
secp256k1_declassify(ctx, &hash, sizeof(hash)); /* private key is hashed now */
if (auxrnd32) secp256k1_sha256_write(secp256k1_get_hash_context(ctx), &hash, auxrnd32, 32);
/* Compute ElligatorSwift encoding and construct output. */
secp256k1_ellswift_elligatorswift_var(ctx, ell64, &t, &p, &hash); /* puts u in ell64[0..32] */
secp256k1_fe_get_b32(ell64 + 32, &t); /* puts t in ell64[32..64] */
secp256k1_memczero(ell64, 64, !ret);
secp256k1_scalar_clear(&seckey_scalar);
return ret;
}
int secp256k1_ellswift_decode(const secp256k1_context *ctx, secp256k1_pubkey *pubkey, const unsigned char *ell64) {
secp256k1_fe u, t;
secp256k1_ge p;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(pubkey != NULL);
ARG_CHECK(ell64 != NULL);
secp256k1_fe_set_b32_mod(&u, ell64);
secp256k1_fe_set_b32_mod(&t, ell64 + 32);
secp256k1_fe_normalize_var(&t);
secp256k1_ellswift_swiftec_var(&p, &u, &t);
secp256k1_pubkey_save(pubkey, &p);
return 1;
}
static int ellswift_xdh_hash_function_prefix_impl(const secp256k1_hash_ctx *hash_ctx, unsigned char *output, const unsigned char *x32, const unsigned char *ell_a64, const unsigned char *ell_b64, void *data) {
secp256k1_sha256 sha;
secp256k1_sha256_initialize(&sha);
secp256k1_sha256_write(hash_ctx, &sha, data, 64);
secp256k1_sha256_write(hash_ctx, &sha, ell_a64, 64);
secp256k1_sha256_write(hash_ctx, &sha, ell_b64, 64);
secp256k1_sha256_write(hash_ctx, &sha, x32, 32);
secp256k1_sha256_finalize(hash_ctx, &sha, output);
secp256k1_sha256_clear(&sha);
return 1;
}
static int ellswift_xdh_hash_function_prefix(unsigned char *output, const unsigned char *x32, const unsigned char *ell_a64, const unsigned char *ell_b64, void *data) {
return ellswift_xdh_hash_function_prefix_impl(secp256k1_get_hash_context(secp256k1_context_static), output, x32, ell_a64, ell_b64, data);
}
/** Set hash state to the BIP340 tagged hash midstate for "bip324_ellswift_xonly_ecdh". */
static void secp256k1_ellswift_sha256_init_bip324(secp256k1_sha256* hash) {
static const uint32_t midstate[8] = {
0x8c12d730ul, 0x827bd392ul, 0x9e4fb2eeul, 0x207b373eul,
0x2292bd7aul, 0xaa5441bcul, 0x15c3779ful, 0xcfb52549ul
};
secp256k1_sha256_initialize_midstate(hash, 64, midstate);
}
static int ellswift_xdh_hash_function_bip324_impl(const secp256k1_hash_ctx *hash_ctx, unsigned char* output, const unsigned char *x32, const unsigned char *ell_a64, const unsigned char *ell_b64, void *data) {
secp256k1_sha256 sha;
(void)data;
secp256k1_ellswift_sha256_init_bip324(&sha);
secp256k1_sha256_write(hash_ctx, &sha, ell_a64, 64);
secp256k1_sha256_write(hash_ctx, &sha, ell_b64, 64);
secp256k1_sha256_write(hash_ctx, &sha, x32, 32);
secp256k1_sha256_finalize(hash_ctx, &sha, output);
secp256k1_sha256_clear(&sha);
return 1;
}
static int ellswift_xdh_hash_function_bip324(unsigned char* output, const unsigned char *x32, const unsigned char *ell_a64, const unsigned char *ell_b64, void *data) {
return ellswift_xdh_hash_function_bip324_impl(secp256k1_get_hash_context(secp256k1_context_static), output, x32, ell_a64, ell_b64, data);
}
const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_prefix = ellswift_xdh_hash_function_prefix;
const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_bip324 = ellswift_xdh_hash_function_bip324;
int secp256k1_ellswift_xdh(const secp256k1_context *ctx, unsigned char *output, const unsigned char *ell_a64, const unsigned char *ell_b64, const unsigned char *seckey32, int party, secp256k1_ellswift_xdh_hash_function hashfp, void *data) {
int ret = 0;
int overflow;
secp256k1_scalar s;
secp256k1_fe xn, xd, px, u, t;
unsigned char sx[32];
const unsigned char* theirs64;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(output != NULL);
ARG_CHECK(ell_a64 != NULL);
ARG_CHECK(ell_b64 != NULL);
ARG_CHECK(seckey32 != NULL);
ARG_CHECK(hashfp != NULL);
/* Load remote public key (as fraction). */
theirs64 = party ? ell_a64 : ell_b64;
secp256k1_fe_set_b32_mod(&u, theirs64);
secp256k1_fe_set_b32_mod(&t, theirs64 + 32);
secp256k1_ellswift_xswiftec_frac_var(&xn, &xd, &u, &t);
/* Load private key (using one if invalid). */
secp256k1_scalar_set_b32(&s, seckey32, &overflow);
overflow |= secp256k1_scalar_is_zero(&s);
secp256k1_scalar_cmov(&s, &secp256k1_scalar_one, overflow);
/* Compute shared X coordinate. */
secp256k1_ecmult_const_xonly(&px, &xn, &xd, &s, 1);
secp256k1_fe_normalize(&px);
secp256k1_fe_get_b32(sx, &px);
/* Invoke hasher. Use ctx-aware function by default */
if (hashfp == secp256k1_ellswift_xdh_hash_function_bip324) {
ret = ellswift_xdh_hash_function_bip324_impl(secp256k1_get_hash_context(ctx), output, sx, ell_a64, ell_b64, data);
} else if (hashfp == secp256k1_ellswift_xdh_hash_function_prefix) {
ret = ellswift_xdh_hash_function_prefix_impl(secp256k1_get_hash_context(ctx), output, sx, ell_a64, ell_b64, data);
} else {
ret = hashfp(output, sx, ell_a64, ell_b64, data);
}
secp256k1_memclear_explicit(sx, sizeof(sx));
secp256k1_fe_clear(&px);
secp256k1_scalar_clear(&s);
return !!ret & !overflow;
}
#endif

View File

@@ -0,0 +1,39 @@
/***********************************************************************
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_MODULE_ELLSWIFT_TESTS_EXHAUSTIVE_H
#define SECP256K1_MODULE_ELLSWIFT_TESTS_EXHAUSTIVE_H
#include "../../../include/secp256k1_ellswift.h"
#include "main_impl.h"
static void test_exhaustive_ellswift(const secp256k1_context *ctx, const secp256k1_ge *group) {
int i;
/* Note that SwiftEC/ElligatorSwift are inherently curve operations, not
* group operations, and this test only checks the curve points which are in
* a tiny subgroup. In that sense it can't be really seen as exhaustive as
* it doesn't (and for computational reasons obviously cannot) test the
* entire domain ellswift operates under. */
for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) {
secp256k1_scalar scalar_i;
unsigned char sec32[32];
unsigned char ell64[64];
secp256k1_pubkey pub_decoded;
secp256k1_ge ge_decoded;
/* Construct ellswift pubkey from exhaustive loop scalar i. */
secp256k1_scalar_set_int(&scalar_i, i);
secp256k1_scalar_get_b32(sec32, &scalar_i);
CHECK(secp256k1_ellswift_create(ctx, ell64, sec32, NULL));
/* Decode ellswift pubkey and check that it matches the precomputed group element. */
secp256k1_ellswift_decode(ctx, &pub_decoded, ell64);
secp256k1_pubkey_load(ctx, &ge_decoded, &pub_decoded);
CHECK(secp256k1_ge_eq_var(&ge_decoded, &group[i]));
}
}
#endif

View File

@@ -0,0 +1,544 @@
/***********************************************************************
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_MODULE_ELLSWIFT_TESTS_H
#define SECP256K1_MODULE_ELLSWIFT_TESTS_H
#include "../../../include/secp256k1_ellswift.h"
#include "../../unit_test.h"
#include "../../util.h"
struct ellswift_xswiftec_inv_test {
int enc_bitmap;
secp256k1_fe u;
secp256k1_fe x;
secp256k1_fe encs[8];
};
struct ellswift_decode_test {
unsigned char enc[64];
secp256k1_fe x;
int odd_y;
};
struct ellswift_xdh_test {
unsigned char priv_ours[32];
unsigned char ellswift_ours[64];
unsigned char ellswift_theirs[64];
int initiating;
unsigned char shared_secret[32];
};
/* Set of (point, encodings) test vectors, selected to maximize branch coverage, part of the BIP324
* test vectors. Created using an independent implementation, and tested decoding against paper
* authors' code. */
static const struct ellswift_xswiftec_inv_test ellswift_xswiftec_inv_tests[] = {
{0xcc, SECP256K1_FE_CONST(0x05ff6bda, 0xd900fc32, 0x61bc7fe3, 0x4e2fb0f5, 0x69f06e09, 0x1ae437d3, 0xa52e9da0, 0xcbfb9590), SECP256K1_FE_CONST(0x80cdf637, 0x74ec7022, 0xc89a5a85, 0x58e373a2, 0x79170285, 0xe0ab2741, 0x2dbce510, 0xbdfe23fc), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x45654798, 0xece071ba, 0x79286d04, 0xf7f3eb1c, 0x3f1d17dd, 0x883610f2, 0xad2efd82, 0xa287466b), SECP256K1_FE_CONST(0x0aeaa886, 0xf6b76c71, 0x58452418, 0xcbf5033a, 0xdc5747e9, 0xe9b5d3b2, 0x303db969, 0x36528557), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xba9ab867, 0x131f8e45, 0x86d792fb, 0x080c14e3, 0xc0e2e822, 0x77c9ef0d, 0x52d1027c, 0x5d78b5c4), SECP256K1_FE_CONST(0xf5155779, 0x0948938e, 0xa7badbe7, 0x340afcc5, 0x23a8b816, 0x164a2c4d, 0xcfc24695, 0xc9ad76d8)}},
{0x33, SECP256K1_FE_CONST(0x1737a85f, 0x4c8d146c, 0xec96e3ff, 0xdca76d99, 0x03dcf3bd, 0x53061868, 0xd478c78c, 0x63c2aa9e), SECP256K1_FE_CONST(0x39e48dd1, 0x50d2f429, 0xbe088dfd, 0x5b61882e, 0x7e840748, 0x3702ae9a, 0x5ab35927, 0xb15f85ea), {SECP256K1_FE_CONST(0x1be8cc0b, 0x04be0c68, 0x1d0c6a68, 0xf733f82c, 0x6c896e0c, 0x8a262fcd, 0x392918e3, 0x03a7abf4), SECP256K1_FE_CONST(0x605b5814, 0xbf9b8cb0, 0x66667c9e, 0x5480d22d, 0xc5b6c92f, 0x14b4af3e, 0xe0a9eb83, 0xb03685e3), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xe41733f4, 0xfb41f397, 0xe2f39597, 0x08cc07d3, 0x937691f3, 0x75d9d032, 0xc6d6e71b, 0xfc58503b), SECP256K1_FE_CONST(0x9fa4a7eb, 0x4064734f, 0x99998361, 0xab7f2dd2, 0x3a4936d0, 0xeb4b50c1, 0x1f56147b, 0x4fc9764c), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}},
{0x00, SECP256K1_FE_CONST(0x1aaa1cce, 0xbf9c7241, 0x91033df3, 0x66b36f69, 0x1c4d902c, 0x228033ff, 0x4516d122, 0xb2564f68), SECP256K1_FE_CONST(0xc7554125, 0x9d3ba98f, 0x207eaa30, 0xc69634d1, 0x87d0b6da, 0x594e719e, 0x420f4898, 0x638fc5b0), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}},
{0x33, SECP256K1_FE_CONST(0x2323a1d0, 0x79b0fd72, 0xfc8bb62e, 0xc34230a8, 0x15cb0596, 0xc2bfac99, 0x8bd6b842, 0x60f5dc26), SECP256K1_FE_CONST(0x239342df, 0xb675500a, 0x34a19631, 0x0b8d87d5, 0x4f49dcac, 0x9da50c17, 0x43ceab41, 0xa7b249ff), {SECP256K1_FE_CONST(0xf63580b8, 0xaa49c484, 0x6de56e39, 0xe1b3e73f, 0x171e881e, 0xba8c66f6, 0x14e67e5c, 0x975dfc07), SECP256K1_FE_CONST(0xb6307b33, 0x2e699f1c, 0xf77841d9, 0x0af25365, 0x404deb7f, 0xed5edb30, 0x90db49e6, 0x42a156b6), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x09ca7f47, 0x55b63b7b, 0x921a91c6, 0x1e4c18c0, 0xe8e177e1, 0x45739909, 0xeb1981a2, 0x68a20028), SECP256K1_FE_CONST(0x49cf84cc, 0xd19660e3, 0x0887be26, 0xf50dac9a, 0xbfb21480, 0x12a124cf, 0x6f24b618, 0xbd5ea579), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}},
{0x33, SECP256K1_FE_CONST(0x2dc90e64, 0x0cb646ae, 0x9164c0b5, 0xa9ef0169, 0xfebe34dc, 0x4437d6e4, 0x6acb0e27, 0xe219d1e8), SECP256K1_FE_CONST(0xd236f19b, 0xf349b951, 0x6e9b3f4a, 0x5610fe96, 0x0141cb23, 0xbbc8291b, 0x9534f1d7, 0x1de62a47), {SECP256K1_FE_CONST(0xe69df7d9, 0xc026c366, 0x00ebdf58, 0x80726758, 0x47c0c431, 0xc8eb7306, 0x82533e96, 0x4b6252c9), SECP256K1_FE_CONST(0x4f18bbdf, 0x7c2d6c5f, 0x818c1880, 0x2fa35cd0, 0x69eaa79f, 0xff74e4fc, 0x837c80d9, 0x3fece2f8), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x19620826, 0x3fd93c99, 0xff1420a7, 0x7f8d98a7, 0xb83f3bce, 0x37148cf9, 0x7dacc168, 0xb49da966), SECP256K1_FE_CONST(0xb0e74420, 0x83d293a0, 0x7e73e77f, 0xd05ca32f, 0x96155860, 0x008b1b03, 0x7c837f25, 0xc0131937), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}},
{0xcc, SECP256K1_FE_CONST(0x3edd7b39, 0x80e2f2f3, 0x4d1409a2, 0x07069f88, 0x1fda5f96, 0xf08027ac, 0x4465b63d, 0xc278d672), SECP256K1_FE_CONST(0x053a98de, 0x4a27b196, 0x1155822b, 0x3a3121f0, 0x3b2a1445, 0x8bd80eb4, 0xa560c4c7, 0xa85c149c), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xb3dae4b7, 0xdcf858e4, 0xc6968057, 0xcef2b156, 0x46543152, 0x6538199c, 0xf52dc1b2, 0xd62fda30), SECP256K1_FE_CONST(0x4aa77dd5, 0x5d6b6d3c, 0xfa10cc9d, 0x0fe42f79, 0x232e4575, 0x661049ae, 0x36779c1d, 0x0c666d88), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x4c251b48, 0x2307a71b, 0x39697fa8, 0x310d4ea9, 0xb9abcead, 0x9ac7e663, 0x0ad23e4c, 0x29d021ff), SECP256K1_FE_CONST(0xb558822a, 0xa29492c3, 0x05ef3362, 0xf01bd086, 0xdcd1ba8a, 0x99efb651, 0xc98863e1, 0xf3998ea7)}},
{0x00, SECP256K1_FE_CONST(0x4295737e, 0xfcb1da6f, 0xb1d96b9c, 0xa7dcd1e3, 0x20024b37, 0xa736c494, 0x8b625981, 0x73069f70), SECP256K1_FE_CONST(0xfa7ffe4f, 0x25f88362, 0x831c087a, 0xfe2e8a9b, 0x0713e2ca, 0xc1ddca6a, 0x383205a2, 0x66f14307), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}},
{0xff, SECP256K1_FE_CONST(0x587c1a0c, 0xee91939e, 0x7f784d23, 0xb963004a, 0x3bf44f5d, 0x4e32a008, 0x1995ba20, 0xb0fca59e), SECP256K1_FE_CONST(0x2ea98853, 0x0715e8d1, 0x0363907f, 0xf2512452, 0x4d471ba2, 0x454d5ce3, 0xbe3f0419, 0x4dfd3a3c), {SECP256K1_FE_CONST(0xcfd5a094, 0xaa0b9b88, 0x91b76c6a, 0xb9438f66, 0xaa1c095a, 0x65f9f701, 0x35e81712, 0x92245e74), SECP256K1_FE_CONST(0xa89057d7, 0xc6563f0d, 0x6efa19ae, 0x84412b8a, 0x7b47e791, 0xa191ecdf, 0xdf2af84f, 0xd97bc339), SECP256K1_FE_CONST(0x475d0ae9, 0xef46920d, 0xf07b3411, 0x7be5a081, 0x7de1023e, 0x3cc32689, 0xe9be145b, 0x406b0aef), SECP256K1_FE_CONST(0xa0759178, 0xad802324, 0x54f827ef, 0x05ea3e72, 0xad8d7541, 0x8e6d4cc1, 0xcd4f5306, 0xc5e7c453), SECP256K1_FE_CONST(0x302a5f6b, 0x55f46477, 0x6e489395, 0x46bc7099, 0x55e3f6a5, 0x9a0608fe, 0xca17e8ec, 0x6ddb9dbb), SECP256K1_FE_CONST(0x576fa828, 0x39a9c0f2, 0x9105e651, 0x7bbed475, 0x84b8186e, 0x5e6e1320, 0x20d507af, 0x268438f6), SECP256K1_FE_CONST(0xb8a2f516, 0x10b96df2, 0x0f84cbee, 0x841a5f7e, 0x821efdc1, 0xc33cd976, 0x1641eba3, 0xbf94f140), SECP256K1_FE_CONST(0x5f8a6e87, 0x527fdcdb, 0xab07d810, 0xfa15c18d, 0x52728abe, 0x7192b33e, 0x32b0acf8, 0x3a1837dc)}},
{0xcc, SECP256K1_FE_CONST(0x5fa88b33, 0x65a635cb, 0xbcee003c, 0xce9ef51d, 0xd1a310de, 0x277e441a, 0xbccdb7be, 0x1e4ba249), SECP256K1_FE_CONST(0x79461ff6, 0x2bfcbcac, 0x4249ba84, 0xdd040f2c, 0xec3c63f7, 0x25204dc7, 0xf464c16b, 0xf0ff3170), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x6bb700e1, 0xf4d7e236, 0xe8d193ff, 0x4a76c1b3, 0xbcd4e2b2, 0x5acac3d5, 0x1c8dac65, 0x3fe909a0), SECP256K1_FE_CONST(0xf4c73410, 0x633da7f6, 0x3a4f1d55, 0xaec6dd32, 0xc4c6d89e, 0xe74075ed, 0xb5515ed9, 0x0da9e683), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x9448ff1e, 0x0b281dc9, 0x172e6c00, 0xb5893e4c, 0x432b1d4d, 0xa5353c2a, 0xe3725399, 0xc016f28f), SECP256K1_FE_CONST(0x0b38cbef, 0x9cc25809, 0xc5b0e2aa, 0x513922cd, 0x3b392761, 0x18bf8a12, 0x4aaea125, 0xf25615ac)}},
{0xcc, SECP256K1_FE_CONST(0x6fb31c75, 0x31f03130, 0xb42b155b, 0x952779ef, 0xbb46087d, 0xd9807d24, 0x1a48eac6, 0x3c3d96d6), SECP256K1_FE_CONST(0x56f81be7, 0x53e8d4ae, 0x4940ea6f, 0x46f6ec9f, 0xda66a6f9, 0x6cc95f50, 0x6cb2b574, 0x90e94260), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x59059774, 0x795bdb7a, 0x837fbe11, 0x40a5fa59, 0x984f48af, 0x8df95d57, 0xdd6d1c05, 0x437dcec1), SECP256K1_FE_CONST(0x22a644db, 0x79376ad4, 0xe7b3a009, 0xe58b3f13, 0x137c54fd, 0xf911122c, 0xc93667c4, 0x7077d784), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xa6fa688b, 0x86a42485, 0x7c8041ee, 0xbf5a05a6, 0x67b0b750, 0x7206a2a8, 0x2292e3f9, 0xbc822d6e), SECP256K1_FE_CONST(0xdd59bb24, 0x86c8952b, 0x184c5ff6, 0x1a74c0ec, 0xec83ab02, 0x06eeedd3, 0x36c9983a, 0x8f8824ab)}},
{0x00, SECP256K1_FE_CONST(0x704cd226, 0xe71cb682, 0x6a590e80, 0xdac90f2d, 0x2f5830f0, 0xfdf135a3, 0xeae3965b, 0xff25ff12), SECP256K1_FE_CONST(0x138e0afa, 0x68936ee6, 0x70bd2b8d, 0xb53aedbb, 0x7bea2a85, 0x97388b24, 0xd0518edd, 0x22ad66ec), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}},
{0x33, SECP256K1_FE_CONST(0x725e9147, 0x92cb8c89, 0x49e7e116, 0x8b7cdd8a, 0x8094c91c, 0x6ec2202c, 0xcd53a6a1, 0x8771edeb), SECP256K1_FE_CONST(0x8da16eb8, 0x6d347376, 0xb6181ee9, 0x74832275, 0x7f6b36e3, 0x913ddfd3, 0x32ac595d, 0x788e0e44), {SECP256K1_FE_CONST(0xdd357786, 0xb9f68733, 0x30391aa5, 0x62580965, 0x4e43116e, 0x82a5a5d8, 0x2ffd1d66, 0x24101fc4), SECP256K1_FE_CONST(0xa0b7efca, 0x01814594, 0xc59c9aae, 0x8e497001, 0x86ca5d95, 0xe88bcc80, 0x399044d9, 0xc2d8613d), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x22ca8879, 0x460978cc, 0xcfc6e55a, 0x9da7f69a, 0xb1bcee91, 0x7d5a5a27, 0xd002e298, 0xdbefdc6b), SECP256K1_FE_CONST(0x5f481035, 0xfe7eba6b, 0x3a636551, 0x71b68ffe, 0x7935a26a, 0x1774337f, 0xc66fbb25, 0x3d279af2), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}},
{0x00, SECP256K1_FE_CONST(0x78fe6b71, 0x7f2ea4a3, 0x2708d79c, 0x151bf503, 0xa5312a18, 0xc0963437, 0xe865cc6e, 0xd3f6ae97), SECP256K1_FE_CONST(0x8701948e, 0x80d15b5c, 0xd8f72863, 0xeae40afc, 0x5aced5e7, 0x3f69cbc8, 0x179a3390, 0x2c094d98), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}},
{0x44, SECP256K1_FE_CONST(0x7c37bb9c, 0x5061dc07, 0x413f11ac, 0xd5a34006, 0xe64c5c45, 0x7fdb9a43, 0x8f217255, 0xa961f50d), SECP256K1_FE_CONST(0x5c1a76b4, 0x4568eb59, 0xd6789a74, 0x42d9ed7c, 0xdc6226b7, 0x752b4ff8, 0xeaf8e1a9, 0x5736e507), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xb94d30cd, 0x7dbff60b, 0x64620c17, 0xca0fafaa, 0x40b3d1f5, 0x2d077a60, 0xa2e0cafd, 0x145086c2), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x46b2cf32, 0x824009f4, 0x9b9df3e8, 0x35f05055, 0xbf4c2e0a, 0xd2f8859f, 0x5d1f3501, 0xebaf756d), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}},
{0x00, SECP256K1_FE_CONST(0x82388888, 0x967f82a6, 0xb444438a, 0x7d44838e, 0x13c0d478, 0xb9ca060d, 0xa95a41fb, 0x94303de6), SECP256K1_FE_CONST(0x29e96541, 0x70628fec, 0x8b497289, 0x8b113cf9, 0x8807f460, 0x9274f4f3, 0x140d0674, 0x157c90a0), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}},
{0x33, SECP256K1_FE_CONST(0x91298f57, 0x70af7a27, 0xf0a47188, 0xd24c3b7b, 0xf98ab299, 0x0d84b0b8, 0x98507e3c, 0x561d6472), SECP256K1_FE_CONST(0x144f4ccb, 0xd9a74698, 0xa88cbf6f, 0xd00ad886, 0xd339d29e, 0xa19448f2, 0xc572cac0, 0xa07d5562), {SECP256K1_FE_CONST(0xe6a0ffa3, 0x807f09da, 0xdbe71e0f, 0x4be4725f, 0x2832e76c, 0xad8dc1d9, 0x43ce8393, 0x75eff248), SECP256K1_FE_CONST(0x837b8e68, 0xd4917544, 0x764ad090, 0x3cb11f86, 0x15d2823c, 0xefbb06d8, 0x9049dbab, 0xc69befda), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x195f005c, 0x7f80f625, 0x2418e1f0, 0xb41b8da0, 0xd7cd1893, 0x52723e26, 0xbc317c6b, 0x8a1009e7), SECP256K1_FE_CONST(0x7c847197, 0x2b6e8abb, 0x89b52f6f, 0xc34ee079, 0xea2d7dc3, 0x1044f927, 0x6fb62453, 0x39640c55), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}},
{0x00, SECP256K1_FE_CONST(0xb682f3d0, 0x3bbb5dee, 0x4f54b5eb, 0xfba931b4, 0xf52f6a19, 0x1e5c2f48, 0x3c73c66e, 0x9ace97e1), SECP256K1_FE_CONST(0x904717bf, 0x0bc0cb78, 0x73fcdc38, 0xaa97f19e, 0x3a626309, 0x72acff92, 0xb24cc6dd, 0xa197cb96), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}},
{0x77, SECP256K1_FE_CONST(0xc17ec69e, 0x665f0fb0, 0xdbab48d9, 0xc2f94d12, 0xec8a9d7e, 0xacb58084, 0x83309180, 0x1eb0b80b), SECP256K1_FE_CONST(0x147756e6, 0x6d96e31c, 0x426d3cc8, 0x5ed0c4cf, 0xbef6341d, 0xd8b28558, 0x5aa574ea, 0x0204b55e), {SECP256K1_FE_CONST(0x6f4aea43, 0x1a0043bd, 0xd03134d6, 0xd9159119, 0xce034b88, 0xc32e50e8, 0xe36c4ee4, 0x5eac7ae9), SECP256K1_FE_CONST(0xfd5be16d, 0x4ffa2690, 0x126c67c3, 0xef7cb9d2, 0x9b74d397, 0xc78b06b3, 0x605fda34, 0xdc9696a6), SECP256K1_FE_CONST(0x5e9c6079, 0x2a2f000e, 0x45c6250f, 0x296f875e, 0x174efc0e, 0x9703e628, 0x706103a9, 0xdd2d82c7), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x90b515bc, 0xe5ffbc42, 0x2fcecb29, 0x26ea6ee6, 0x31fcb477, 0x3cd1af17, 0x1c93b11a, 0xa1538146), SECP256K1_FE_CONST(0x02a41e92, 0xb005d96f, 0xed93983c, 0x1083462d, 0x648b2c68, 0x3874f94c, 0x9fa025ca, 0x23696589), SECP256K1_FE_CONST(0xa1639f86, 0xd5d0fff1, 0xba39daf0, 0xd69078a1, 0xe8b103f1, 0x68fc19d7, 0x8f9efc55, 0x22d27968), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}},
{0xcc, SECP256K1_FE_CONST(0xc25172fc, 0x3f29b6fc, 0x4a1155b8, 0x57523315, 0x5486b274, 0x64b74b8b, 0x260b499a, 0x3f53cb14), SECP256K1_FE_CONST(0x1ea9cbdb, 0x35cf6e03, 0x29aa31b0, 0xbb0a702a, 0x65123ed0, 0x08655a93, 0xb7dcd528, 0x0e52e1ab), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x7422edc7, 0x843136af, 0x0053bb88, 0x54448a82, 0x99994f9d, 0xdcefd3a9, 0xa92d4546, 0x2c59298a), SECP256K1_FE_CONST(0x78c7774a, 0x266f8b97, 0xea23d05d, 0x064f033c, 0x77319f92, 0x3f6b78bc, 0xe4e20bf0, 0x5fa5398d), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x8bdd1238, 0x7bcec950, 0xffac4477, 0xabbb757d, 0x6666b062, 0x23102c56, 0x56d2bab8, 0xd3a6d2a5), SECP256K1_FE_CONST(0x873888b5, 0xd9907468, 0x15dc2fa2, 0xf9b0fcc3, 0x88ce606d, 0xc0948743, 0x1b1df40e, 0xa05ac2a2)}},
{0x00, SECP256K1_FE_CONST(0xcab6626f, 0x832a4b12, 0x80ba7add, 0x2fc5322f, 0xf011caed, 0xedf7ff4d, 0xb6735d50, 0x26dc0367), SECP256K1_FE_CONST(0x2b2bef08, 0x52c6f7c9, 0x5d72ac99, 0xa23802b8, 0x75029cd5, 0x73b248d1, 0xf1b3fc80, 0x33788eb6), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}},
{0x33, SECP256K1_FE_CONST(0xd8621b4f, 0xfc85b9ed, 0x56e99d8d, 0xd1dd24ae, 0xdcecb147, 0x63b861a1, 0x7112dc77, 0x1a104fd2), SECP256K1_FE_CONST(0x812cabe9, 0x72a22aa6, 0x7c7da0c9, 0x4d8a9362, 0x96eb9949, 0xd70c37cb, 0x2b248757, 0x4cb3ce58), {SECP256K1_FE_CONST(0xfbc5febc, 0x6fdbc9ae, 0x3eb88a93, 0xb982196e, 0x8b6275a6, 0xd5a73c17, 0x387e000c, 0x711bd0e3), SECP256K1_FE_CONST(0x8724c96b, 0xd4e5527f, 0x2dd195a5, 0x1c468d2d, 0x211ba2fa, 0xc7cbe0b4, 0xb3434253, 0x409fb42d), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x043a0143, 0x90243651, 0xc147756c, 0x467de691, 0x749d8a59, 0x2a58c3e8, 0xc781fff2, 0x8ee42b4c), SECP256K1_FE_CONST(0x78db3694, 0x2b1aad80, 0xd22e6a5a, 0xe3b972d2, 0xdee45d05, 0x38341f4b, 0x4cbcbdab, 0xbf604802), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}},
{0x00, SECP256K1_FE_CONST(0xda463164, 0xc6f4bf71, 0x29ee5f0e, 0xc00f65a6, 0x75a8adf1, 0xbd931b39, 0xb64806af, 0xdcda9a22), SECP256K1_FE_CONST(0x25b9ce9b, 0x390b408e, 0xd611a0f1, 0x3ff09a59, 0x8a57520e, 0x426ce4c6, 0x49b7f94f, 0x2325620d), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}},
{0xcc, SECP256K1_FE_CONST(0xdafc971e, 0x4a3a7b6d, 0xcfb42a08, 0xd9692d82, 0xad9e7838, 0x523fcbda, 0x1d4827e1, 0x4481ae2d), SECP256K1_FE_CONST(0x250368e1, 0xb5c58492, 0x304bd5f7, 0x2696d27d, 0x526187c7, 0xadc03425, 0xe2b7d81d, 0xbb7e4e02), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x370c28f1, 0xbe665efa, 0xcde6aa43, 0x6bf86fe2, 0x1e6e314c, 0x1e53dd04, 0x0e6c73a4, 0x6b4c8c49), SECP256K1_FE_CONST(0xcd8acee9, 0x8ffe5653, 0x1a84d7eb, 0x3e48fa40, 0x34206ce8, 0x25ace907, 0xd0edf0ea, 0xeb5e9ca2), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xc8f3d70e, 0x4199a105, 0x321955bc, 0x9407901d, 0xe191ceb3, 0xe1ac22fb, 0xf1938c5a, 0x94b36fe6), SECP256K1_FE_CONST(0x32753116, 0x7001a9ac, 0xe57b2814, 0xc1b705bf, 0xcbdf9317, 0xda5316f8, 0x2f120f14, 0x14a15f8d)}},
{0x44, SECP256K1_FE_CONST(0xe0294c8b, 0xc1a36b41, 0x66ee92bf, 0xa70a5c34, 0x976fa982, 0x9405efea, 0x8f9cd54d, 0xcb29b99e), SECP256K1_FE_CONST(0xae9690d1, 0x3b8d20a0, 0xfbbf37be, 0xd8474f67, 0xa04e142f, 0x56efd787, 0x70a76b35, 0x9165d8a1), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xdcd45d93, 0x5613916a, 0xf167b029, 0x058ba3a7, 0x00d37150, 0xb9df3472, 0x8cb05412, 0xc16d4182), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x232ba26c, 0xa9ec6e95, 0x0e984fd6, 0xfa745c58, 0xff2c8eaf, 0x4620cb8d, 0x734fabec, 0x3e92baad), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}},
{0x00, SECP256K1_FE_CONST(0xe148441c, 0xd7b92b8b, 0x0e4fa3bd, 0x68712cfd, 0x0d709ad1, 0x98cace61, 0x1493c10e, 0x97f5394e), SECP256K1_FE_CONST(0x164a6397, 0x94d74c53, 0xafc4d329, 0x4e79cdb3, 0xcd25f99f, 0x6df45c00, 0x0f758aba, 0x54d699c0), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}},
{0xff, SECP256K1_FE_CONST(0xe4b00ec9, 0x7aadcca9, 0x7644d3b0, 0xc8a931b1, 0x4ce7bcf7, 0xbc877954, 0x6d6e35aa, 0x5937381c), SECP256K1_FE_CONST(0x94e9588d, 0x41647b3f, 0xcc772dc8, 0xd83c67ce, 0x3be00353, 0x8517c834, 0x103d2cd4, 0x9d62ef4d), {SECP256K1_FE_CONST(0xc88d25f4, 0x1407376b, 0xb2c03a7f, 0xffeb3ec7, 0x811cc434, 0x91a0c3aa, 0xc0378cdc, 0x78357bee), SECP256K1_FE_CONST(0x51c02636, 0xce00c234, 0x5ecd89ad, 0xb6089fe4, 0xd5e18ac9, 0x24e3145e, 0x6669501c, 0xd37a00d4), SECP256K1_FE_CONST(0x205b3512, 0xdb40521c, 0xb200952e, 0x67b46f67, 0xe09e7839, 0xe0de4400, 0x4138329e, 0xbd9138c5), SECP256K1_FE_CONST(0x58aab390, 0xab6fb55c, 0x1d1b8089, 0x7a207ce9, 0x4a78fa5b, 0x4aa61a33, 0x398bcae9, 0xadb20d3e), SECP256K1_FE_CONST(0x3772da0b, 0xebf8c894, 0x4d3fc580, 0x0014c138, 0x7ee33bcb, 0x6e5f3c55, 0x3fc87322, 0x87ca8041), SECP256K1_FE_CONST(0xae3fd9c9, 0x31ff3dcb, 0xa1327652, 0x49f7601b, 0x2a1e7536, 0xdb1ceba1, 0x9996afe2, 0x2c85fb5b), SECP256K1_FE_CONST(0xdfa4caed, 0x24bfade3, 0x4dff6ad1, 0x984b9098, 0x1f6187c6, 0x1f21bbff, 0xbec7cd60, 0x426ec36a), SECP256K1_FE_CONST(0xa7554c6f, 0x54904aa3, 0xe2e47f76, 0x85df8316, 0xb58705a4, 0xb559e5cc, 0xc6743515, 0x524deef1)}},
{0x00, SECP256K1_FE_CONST(0xe5bbb9ef, 0x360d0a50, 0x1618f006, 0x7d36dceb, 0x75f5be9a, 0x620232aa, 0x9fd5139d, 0x0863fde5), SECP256K1_FE_CONST(0xe5bbb9ef, 0x360d0a50, 0x1618f006, 0x7d36dceb, 0x75f5be9a, 0x620232aa, 0x9fd5139d, 0x0863fde5), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}},
{0xff, SECP256K1_FE_CONST(0xe6bcb5c3, 0xd63467d4, 0x90bfa54f, 0xbbc6092a, 0x7248c25e, 0x11b248dc, 0x2964a6e1, 0x5edb1457), SECP256K1_FE_CONST(0x19434a3c, 0x29cb982b, 0x6f405ab0, 0x4439f6d5, 0x8db73da1, 0xee4db723, 0xd69b591d, 0xa124e7d8), {SECP256K1_FE_CONST(0x67119877, 0x832ab8f4, 0x59a82165, 0x6d8261f5, 0x44a553b8, 0x9ae4f25c, 0x52a97134, 0xb70f3426), SECP256K1_FE_CONST(0xffee02f5, 0xe649c07f, 0x0560eff1, 0x867ec7b3, 0x2d0e595e, 0x9b1c0ea6, 0xe2a4fc70, 0xc97cd71f), SECP256K1_FE_CONST(0xb5e0c189, 0xeb5b4bac, 0xd025b744, 0x4d74178b, 0xe8d5246c, 0xfa4a9a20, 0x7964a057, 0xee969992), SECP256K1_FE_CONST(0x5746e459, 0x1bf7f4c3, 0x044609ea, 0x372e9086, 0x03975d27, 0x9fdef834, 0x9f0b08d3, 0x2f07619d), SECP256K1_FE_CONST(0x98ee6788, 0x7cd5470b, 0xa657de9a, 0x927d9e0a, 0xbb5aac47, 0x651b0da3, 0xad568eca, 0x48f0c809), SECP256K1_FE_CONST(0x0011fd0a, 0x19b63f80, 0xfa9f100e, 0x7981384c, 0xd2f1a6a1, 0x64e3f159, 0x1d5b038e, 0x36832510), SECP256K1_FE_CONST(0x4a1f3e76, 0x14a4b453, 0x2fda48bb, 0xb28be874, 0x172adb93, 0x05b565df, 0x869b5fa7, 0x1169629d), SECP256K1_FE_CONST(0xa8b91ba6, 0xe4080b3c, 0xfbb9f615, 0xc8d16f79, 0xfc68a2d8, 0x602107cb, 0x60f4f72b, 0xd0f89a92)}},
{0x33, SECP256K1_FE_CONST(0xf28fba64, 0xaf766845, 0xeb2f4302, 0x456e2b9f, 0x8d80affe, 0x57e7aae4, 0x2738d7cd, 0xdb1c2ce6), SECP256K1_FE_CONST(0xf28fba64, 0xaf766845, 0xeb2f4302, 0x456e2b9f, 0x8d80affe, 0x57e7aae4, 0x2738d7cd, 0xdb1c2ce6), {SECP256K1_FE_CONST(0x4f867ad8, 0xbb3d8404, 0x09d26b67, 0x307e6210, 0x0153273f, 0x72fa4b74, 0x84becfa1, 0x4ebe7408), SECP256K1_FE_CONST(0x5bbc4f59, 0xe452cc5f, 0x22a99144, 0xb10ce898, 0x9a89a995, 0xec3cea1c, 0x91ae10e8, 0xf721bb5d), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xb0798527, 0x44c27bfb, 0xf62d9498, 0xcf819def, 0xfeacd8c0, 0x8d05b48b, 0x7b41305d, 0xb1418827), SECP256K1_FE_CONST(0xa443b0a6, 0x1bad33a0, 0xdd566ebb, 0x4ef31767, 0x6576566a, 0x13c315e3, 0x6e51ef16, 0x08de40d2), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}},
{0xcc, SECP256K1_FE_CONST(0xf455605b, 0xc85bf48e, 0x3a908c31, 0x023faf98, 0x381504c6, 0xc6d3aeb9, 0xede55f8d, 0xd528924d), SECP256K1_FE_CONST(0xd31fbcd5, 0xcdb798f6, 0xc00db669, 0x2f8fe896, 0x7fa9c79d, 0xd10958f4, 0xa194f013, 0x74905e99), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x0c00c571, 0x5b56fe63, 0x2d814ad8, 0xa77f8e66, 0x628ea47a, 0x6116834f, 0x8c1218f3, 0xa03cbd50), SECP256K1_FE_CONST(0xdf88e44f, 0xac84fa52, 0xdf4d59f4, 0x8819f18f, 0x6a8cd415, 0x1d162afa, 0xf773166f, 0x57c7ff46), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xf3ff3a8e, 0xa4a9019c, 0xd27eb527, 0x58807199, 0x9d715b85, 0x9ee97cb0, 0x73ede70b, 0x5fc33edf), SECP256K1_FE_CONST(0x20771bb0, 0x537b05ad, 0x20b2a60b, 0x77e60e70, 0x95732bea, 0xe2e9d505, 0x088ce98f, 0xa837fce9)}},
{0xff, SECP256K1_FE_CONST(0xf58cd4d9, 0x830bad32, 0x2699035e, 0x8246007d, 0x4be27e19, 0xb6f53621, 0x317b4f30, 0x9b3daa9d), SECP256K1_FE_CONST(0x78ec2b3d, 0xc0948de5, 0x60148bbc, 0x7c6dc963, 0x3ad5df70, 0xa5a5750c, 0xbed72180, 0x4f082a3b), {SECP256K1_FE_CONST(0x6c4c580b, 0x76c75940, 0x43569f9d, 0xae16dc28, 0x01c16a1f, 0xbe128608, 0x81b75f8e, 0xf929bce5), SECP256K1_FE_CONST(0x94231355, 0xe7385c5f, 0x25ca436a, 0xa6419147, 0x1aea4393, 0xd6e86ab7, 0xa35fe2af, 0xacaefd0d), SECP256K1_FE_CONST(0xdff2a195, 0x1ada6db5, 0x74df8340, 0x48149da3, 0x397a75b8, 0x29abf58c, 0x7e69db1b, 0x41ac0989), SECP256K1_FE_CONST(0xa52b66d3, 0xc9070355, 0x48028bf8, 0x04711bf4, 0x22aba95f, 0x1a666fc8, 0x6f4648e0, 0x5f29caae), SECP256K1_FE_CONST(0x93b3a7f4, 0x8938a6bf, 0xbca96062, 0x51e923d7, 0xfe3e95e0, 0x41ed79f7, 0x7e48a070, 0x06d63f4a), SECP256K1_FE_CONST(0x6bdcecaa, 0x18c7a3a0, 0xda35bc95, 0x59be6eb8, 0xe515bc6c, 0x29179548, 0x5ca01d4f, 0x5350ff22), SECP256K1_FE_CONST(0x200d5e6a, 0xe525924a, 0x8b207cbf, 0xb7eb625c, 0xc6858a47, 0xd6540a73, 0x819624e3, 0xbe53f2a6), SECP256K1_FE_CONST(0x5ad4992c, 0x36f8fcaa, 0xb7fd7407, 0xfb8ee40b, 0xdd5456a0, 0xe5999037, 0x90b9b71e, 0xa0d63181)}},
{0x00, SECP256K1_FE_CONST(0xfd7d912a, 0x40f182a3, 0x588800d6, 0x9ebfb504, 0x8766da20, 0x6fd7ebc8, 0xd2436c81, 0xcbef6421), SECP256K1_FE_CONST(0x8d37c862, 0x054debe7, 0x31694536, 0xff46b273, 0xec122b35, 0xa9bf1445, 0xac3c4ff9, 0xf262c952), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}},
};
/* Set of (encoding, xcoord) test vectors, selected to maximize branch coverage, part of the BIP324
* test vectors. Created using an independent implementation, and tested decoding against the paper
* authors' code. */
static const struct ellswift_decode_test ellswift_decode_tests[] = {
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c), 0},
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd3, 0x47, 0x5b, 0xf7, 0x65, 0x5b, 0x0f, 0xb2, 0xd8, 0x52, 0x92, 0x10, 0x35, 0xb2, 0xef, 0x60, 0x7f, 0x49, 0x06, 0x9b, 0x97, 0x45, 0x4e, 0x67, 0x95, 0x25, 0x10, 0x62, 0x74, 0x17, 0x71}, SECP256K1_FE_CONST(0xb5da00b7, 0x3cd65605, 0x20e7c364, 0x086e7cd2, 0x3a34bf60, 0xd0e707be, 0x9fc34d4c, 0xd5fdfa2c), 1},
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x27, 0x7c, 0x4a, 0x71, 0xf9, 0xd2, 0x2e, 0x66, 0xec, 0xe5, 0x23, 0xf8, 0xfa, 0x08, 0x74, 0x1a, 0x7c, 0x09, 0x12, 0xc6, 0x6a, 0x69, 0xce, 0x68, 0x51, 0x4b, 0xfd, 0x35, 0x15, 0xb4, 0x9f}, SECP256K1_FE_CONST(0xf482f2e2, 0x41753ad0, 0xfb89150d, 0x8491dc1e, 0x34ff0b8a, 0xcfbb442c, 0xfe999e2e, 0x5e6fd1d2), 1},
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x21, 0xcc, 0x93, 0x0e, 0x77, 0xc9, 0xf5, 0x14, 0xb6, 0x91, 0x5c, 0x3d, 0xbe, 0x2a, 0x94, 0xc6, 0xd8, 0xf6, 0x90, 0xb5, 0xb7, 0x39, 0x86, 0x4b, 0xa6, 0x78, 0x9f, 0xb8, 0xa5, 0x5d, 0xd0}, SECP256K1_FE_CONST(0x9f59c402, 0x75f5085a, 0x006f05da, 0xe77eb98c, 0x6fd0db1a, 0xb4a72ac4, 0x7eae90a4, 0xfc9e57e0), 0},
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0xe7, 0x0d, 0xf5, 0x19, 0x39, 0xb9, 0x4c, 0x9c, 0x24, 0x97, 0x9f, 0xa7, 0xdd, 0x04, 0xeb, 0xd9, 0xb3, 0x57, 0x2d, 0xa7, 0x80, 0x22, 0x90, 0x43, 0x8a, 0xf2, 0xa6, 0x81, 0x89, 0x54, 0x41}, SECP256K1_FE_CONST(0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaa9, 0xfffffd6b), 1},
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd1, 0x9c, 0x18, 0x2d, 0x27, 0x59, 0xcd, 0x99, 0x82, 0x42, 0x28, 0xd9, 0x47, 0x99, 0xf8, 0xc6, 0x55, 0x7c, 0x38, 0xa1, 0xc0, 0xd6, 0x77, 0x9b, 0x9d, 0x4b, 0x72, 0x9c, 0x6f, 0x1c, 0xcc, 0x42}, SECP256K1_FE_CONST(0x70720db7, 0xe238d041, 0x21f5b1af, 0xd8cc5ad9, 0xd18944c6, 0xbdc94881, 0xf502b7a3, 0xaf3aecff), 0},
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c), 0},
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x26, 0x64, 0xbb, 0xd5}, SECP256K1_FE_CONST(0x50873db3, 0x1badcc71, 0x890e4f67, 0x753a6575, 0x7f97aaa7, 0xdd5f1e82, 0xb753ace3, 0x2219064b), 0},
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x70, 0x28, 0xde, 0x7d}, SECP256K1_FE_CONST(0x1eea9cc5, 0x9cfcf2fa, 0x151ac6c2, 0x74eea411, 0x0feb4f7b, 0x68c59657, 0x32e9992e, 0x976ef68e), 0},
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcb, 0xcf, 0xb7, 0xe7}, SECP256K1_FE_CONST(0x12303941, 0xaedc2088, 0x80735b1f, 0x1795c8e5, 0x5be520ea, 0x93e10335, 0x7b5d2adb, 0x7ed59b8e), 0},
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0x11, 0x3a, 0xd9}, SECP256K1_FE_CONST(0x7eed6b70, 0xe7b0767c, 0x7d7feac0, 0x4e57aa2a, 0x12fef5e0, 0xf48f878f, 0xcbb88b3b, 0x6b5e0783), 0},
{{0x0a, 0x2d, 0x2b, 0xa9, 0x35, 0x07, 0xf1, 0xdf, 0x23, 0x37, 0x70, 0xc2, 0xa7, 0x97, 0x96, 0x2c, 0xc6, 0x1f, 0x6d, 0x15, 0xda, 0x14, 0xec, 0xd4, 0x7d, 0x8d, 0x27, 0xae, 0x1c, 0xd5, 0xf8, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x532167c1, 0x1200b08c, 0x0e84a354, 0xe74dcc40, 0xf8b25f4f, 0xe686e308, 0x69526366, 0x278a0688), 0},
{{0x0a, 0x2d, 0x2b, 0xa9, 0x35, 0x07, 0xf1, 0xdf, 0x23, 0x37, 0x70, 0xc2, 0xa7, 0x97, 0x96, 0x2c, 0xc6, 0x1f, 0x6d, 0x15, 0xda, 0x14, 0xec, 0xd4, 0x7d, 0x8d, 0x27, 0xae, 0x1c, 0xd5, 0xf8, 0x53, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x532167c1, 0x1200b08c, 0x0e84a354, 0xe74dcc40, 0xf8b25f4f, 0xe686e308, 0x69526366, 0x278a0688), 0},
{{0x0f, 0xfd, 0xe9, 0xca, 0x81, 0xd7, 0x51, 0xe9, 0xcd, 0xaf, 0xfc, 0x1a, 0x50, 0x77, 0x92, 0x45, 0x32, 0x0b, 0x28, 0x99, 0x6d, 0xba, 0xf3, 0x2f, 0x82, 0x2f, 0x20, 0x11, 0x7c, 0x22, 0xfb, 0xd6, 0xc7, 0x4d, 0x99, 0xef, 0xce, 0xaa, 0x55, 0x0f, 0x1a, 0xd1, 0xc0, 0xf4, 0x3f, 0x46, 0xe7, 0xff, 0x1e, 0xe3, 0xbd, 0x01, 0x62, 0xb7, 0xbf, 0x55, 0xf2, 0x96, 0x5d, 0xa9, 0xc3, 0x45, 0x06, 0x46}, SECP256K1_FE_CONST(0x74e880b3, 0xffd18fe3, 0xcddf7902, 0x522551dd, 0xf97fa4a3, 0x5a3cfda8, 0x197f9470, 0x81a57b8f), 0},
{{0x0f, 0xfd, 0xe9, 0xca, 0x81, 0xd7, 0x51, 0xe9, 0xcd, 0xaf, 0xfc, 0x1a, 0x50, 0x77, 0x92, 0x45, 0x32, 0x0b, 0x28, 0x99, 0x6d, 0xba, 0xf3, 0x2f, 0x82, 0x2f, 0x20, 0x11, 0x7c, 0x22, 0xfb, 0xd6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x15, 0x6c, 0xa8, 0x96}, SECP256K1_FE_CONST(0x377b643f, 0xce2271f6, 0x4e5c8101, 0x566107c1, 0xbe498074, 0x50917838, 0x04f65478, 0x1ac9217c), 1},
{{0x12, 0x36, 0x58, 0x44, 0x4f, 0x32, 0xbe, 0x8f, 0x02, 0xea, 0x20, 0x34, 0xaf, 0xa7, 0xef, 0x4b, 0xbe, 0x8a, 0xdc, 0x91, 0x8c, 0xeb, 0x49, 0xb1, 0x27, 0x73, 0xb6, 0x25, 0xf4, 0x90, 0xb3, 0x68, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8d, 0xc5, 0xfe, 0x11}, SECP256K1_FE_CONST(0xed16d65c, 0xf3a9538f, 0xcb2c139f, 0x1ecbc143, 0xee148271, 0x20cbc265, 0x9e667256, 0x800b8142), 0},
{{0x14, 0x6f, 0x92, 0x46, 0x4d, 0x15, 0xd3, 0x6e, 0x35, 0x38, 0x2b, 0xd3, 0xca, 0x5b, 0x0f, 0x97, 0x6c, 0x95, 0xcb, 0x08, 0xac, 0xdc, 0xf2, 0xd5, 0xb3, 0x57, 0x06, 0x17, 0x99, 0x08, 0x39, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x31, 0x45, 0xe9, 0x3b}, SECP256K1_FE_CONST(0x0d5cd840, 0x427f941f, 0x65193079, 0xab8e2e83, 0x024ef2ee, 0x7ca558d8, 0x8879ffd8, 0x79fb6657), 0},
{{0x15, 0xfd, 0xf5, 0xcf, 0x09, 0xc9, 0x07, 0x59, 0xad, 0xd2, 0x27, 0x2d, 0x57, 0x4d, 0x2b, 0xb5, 0xfe, 0x14, 0x29, 0xf9, 0xf3, 0xc1, 0x4c, 0x65, 0xe3, 0x19, 0x4b, 0xf6, 0x1b, 0x82, 0xaa, 0x73, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x04, 0xcf, 0xd9, 0x06}, SECP256K1_FE_CONST(0x16d0e439, 0x46aec93f, 0x62d57eb8, 0xcde68951, 0xaf136cf4, 0xb307938d, 0xd1447411, 0xe07bffe1), 1},
{{0x1f, 0x67, 0xed, 0xf7, 0x79, 0xa8, 0xa6, 0x49, 0xd6, 0xde, 0xf6, 0x00, 0x35, 0xf2, 0xfa, 0x22, 0xd0, 0x22, 0xdd, 0x35, 0x90, 0x79, 0xa1, 0xa1, 0x44, 0x07, 0x3d, 0x84, 0xf1, 0x9b, 0x92, 0xd5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x025661f9, 0xaba9d15c, 0x3118456b, 0xbe980e3e, 0x1b8ba2e0, 0x47c737a4, 0xeb48a040, 0xbb566f6c), 0},
{{0x1f, 0x67, 0xed, 0xf7, 0x79, 0xa8, 0xa6, 0x49, 0xd6, 0xde, 0xf6, 0x00, 0x35, 0xf2, 0xfa, 0x22, 0xd0, 0x22, 0xdd, 0x35, 0x90, 0x79, 0xa1, 0xa1, 0x44, 0x07, 0x3d, 0x84, 0xf1, 0x9b, 0x92, 0xd5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x025661f9, 0xaba9d15c, 0x3118456b, 0xbe980e3e, 0x1b8ba2e0, 0x47c737a4, 0xeb48a040, 0xbb566f6c), 0},
{{0x1f, 0xe1, 0xe5, 0xef, 0x3f, 0xce, 0xb5, 0xc1, 0x35, 0xab, 0x77, 0x41, 0x33, 0x3c, 0xe5, 0xa6, 0xe8, 0x0d, 0x68, 0x16, 0x76, 0x53, 0xf6, 0xb2, 0xb2, 0x4b, 0xcb, 0xcf, 0xaa, 0xaf, 0xf5, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x98bec3b2, 0xa351fa96, 0xcfd191c1, 0x77835193, 0x1b9e9ba9, 0xad1149f6, 0xd9eadca8, 0x0981b801), 0},
{{0x40, 0x56, 0xa3, 0x4a, 0x21, 0x0e, 0xec, 0x78, 0x92, 0xe8, 0x82, 0x06, 0x75, 0xc8, 0x60, 0x09, 0x9f, 0x85, 0x7b, 0x26, 0xaa, 0xd8, 0x54, 0x70, 0xee, 0x6d, 0x3c, 0xf1, 0x30, 0x4a, 0x9d, 0xcf, 0x37, 0x5e, 0x70, 0x37, 0x42, 0x71, 0xf2, 0x0b, 0x13, 0xc9, 0x98, 0x6e, 0xd7, 0xd3, 0xc1, 0x77, 0x99, 0x69, 0x8c, 0xfc, 0x43, 0x5d, 0xbe, 0xd3, 0xa9, 0xf3, 0x4b, 0x38, 0xc8, 0x23, 0xc2, 0xb4}, SECP256K1_FE_CONST(0x868aac20, 0x03b29dbc, 0xad1a3e80, 0x3855e078, 0xa89d1654, 0x3ac64392, 0xd1224172, 0x98cec76e), 0},
{{0x41, 0x97, 0xec, 0x37, 0x23, 0xc6, 0x54, 0xcf, 0xdd, 0x32, 0xab, 0x07, 0x55, 0x06, 0x64, 0x8b, 0x2f, 0xf5, 0x07, 0x03, 0x62, 0xd0, 0x1a, 0x4f, 0xff, 0x14, 0xb3, 0x36, 0xb7, 0x8f, 0x96, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb3, 0xab, 0x1e, 0x95}, SECP256K1_FE_CONST(0xba5a6314, 0x502a8952, 0xb8f456e0, 0x85928105, 0xf665377a, 0x8ce27726, 0xa5b0eb7e, 0xc1ac0286), 0},
{{0x47, 0xeb, 0x3e, 0x20, 0x8f, 0xed, 0xcd, 0xf8, 0x23, 0x4c, 0x94, 0x21, 0xe9, 0xcd, 0x9a, 0x7a, 0xe8, 0x73, 0xbf, 0xbd, 0xbc, 0x39, 0x37, 0x23, 0xd1, 0xba, 0x1e, 0x1e, 0x6a, 0x8e, 0x6b, 0x24, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7c, 0xd1, 0x2c, 0xb1}, SECP256K1_FE_CONST(0xd192d520, 0x07e541c9, 0x807006ed, 0x0468df77, 0xfd214af0, 0xa795fe11, 0x9359666f, 0xdcf08f7c), 0},
{{0x5e, 0xb9, 0x69, 0x6a, 0x23, 0x36, 0xfe, 0x2c, 0x3c, 0x66, 0x6b, 0x02, 0xc7, 0x55, 0xdb, 0x4c, 0x0c, 0xfd, 0x62, 0x82, 0x5c, 0x7b, 0x58, 0x9a, 0x7b, 0x7b, 0xb4, 0x42, 0xe1, 0x41, 0xc1, 0xd6, 0x93, 0x41, 0x3f, 0x00, 0x52, 0xd4, 0x9e, 0x64, 0xab, 0xec, 0x6d, 0x58, 0x31, 0xd6, 0x6c, 0x43, 0x61, 0x28, 0x30, 0xa1, 0x7d, 0xf1, 0xfe, 0x43, 0x83, 0xdb, 0x89, 0x64, 0x68, 0x10, 0x02, 0x21}, SECP256K1_FE_CONST(0xef6e1da6, 0xd6c7627e, 0x80f7a723, 0x4cb08a02, 0x2c1ee1cf, 0x29e4d0f9, 0x642ae924, 0xcef9eb38), 1},
{{0x7b, 0xf9, 0x6b, 0x7b, 0x6d, 0xa1, 0x5d, 0x34, 0x76, 0xa2, 0xb1, 0x95, 0x93, 0x4b, 0x69, 0x0a, 0x3a, 0x3d, 0xe3, 0xe8, 0xab, 0x84, 0x74, 0x85, 0x68, 0x63, 0xb0, 0xde, 0x3a, 0xf9, 0x0b, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x50851dfc, 0x9f418c31, 0x4a437295, 0xb24feeea, 0x27af3d0c, 0xd2308348, 0xfda6e21c, 0x463e46ff), 0},
{{0x7b, 0xf9, 0x6b, 0x7b, 0x6d, 0xa1, 0x5d, 0x34, 0x76, 0xa2, 0xb1, 0x95, 0x93, 0x4b, 0x69, 0x0a, 0x3a, 0x3d, 0xe3, 0xe8, 0xab, 0x84, 0x74, 0x85, 0x68, 0x63, 0xb0, 0xde, 0x3a, 0xf9, 0x0b, 0x0e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x50851dfc, 0x9f418c31, 0x4a437295, 0xb24feeea, 0x27af3d0c, 0xd2308348, 0xfda6e21c, 0x463e46ff), 0},
{{0x85, 0x1b, 0x1c, 0xa9, 0x45, 0x49, 0x37, 0x1c, 0x4f, 0x1f, 0x71, 0x87, 0x32, 0x1d, 0x39, 0xbf, 0x51, 0xc6, 0xb7, 0xfb, 0x61, 0xf7, 0xcb, 0xf0, 0x27, 0xc9, 0xda, 0x62, 0x02, 0x1b, 0x7a, 0x65, 0xfc, 0x54, 0xc9, 0x68, 0x37, 0xfb, 0x22, 0xb3, 0x62, 0xed, 0xa6, 0x3e, 0xc5, 0x2e, 0xc8, 0x3d, 0x81, 0xbe, 0xdd, 0x16, 0x0c, 0x11, 0xb2, 0x2d, 0x96, 0x5d, 0x9f, 0x4a, 0x6d, 0x64, 0xd2, 0x51}, SECP256K1_FE_CONST(0x3e731051, 0xe12d3323, 0x7eb324f2, 0xaa5b16bb, 0x868eb49a, 0x1aa1fadc, 0x19b6e876, 0x1b5a5f7b), 1},
{{0x94, 0x3c, 0x2f, 0x77, 0x51, 0x08, 0xb7, 0x37, 0xfe, 0x65, 0xa9, 0x53, 0x1e, 0x19, 0xf2, 0xfc, 0x2a, 0x19, 0x7f, 0x56, 0x03, 0xe3, 0xa2, 0x88, 0x1d, 0x1d, 0x83, 0xe4, 0x00, 0x8f, 0x91, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x311c61f0, 0xab2f32b7, 0xb1f0223f, 0xa72f0a78, 0x752b8146, 0xe46107f8, 0x876dd9c4, 0xf92b2942), 0},
{{0x94, 0x3c, 0x2f, 0x77, 0x51, 0x08, 0xb7, 0x37, 0xfe, 0x65, 0xa9, 0x53, 0x1e, 0x19, 0xf2, 0xfc, 0x2a, 0x19, 0x7f, 0x56, 0x03, 0xe3, 0xa2, 0x88, 0x1d, 0x1d, 0x83, 0xe4, 0x00, 0x8f, 0x91, 0x25, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x311c61f0, 0xab2f32b7, 0xb1f0223f, 0xa72f0a78, 0x752b8146, 0xe46107f8, 0x876dd9c4, 0xf92b2942), 0},
{{0xa0, 0xf1, 0x84, 0x92, 0x18, 0x3e, 0x61, 0xe8, 0x06, 0x3e, 0x57, 0x36, 0x06, 0x59, 0x14, 0x21, 0xb0, 0x6b, 0xc3, 0x51, 0x36, 0x31, 0x57, 0x8a, 0x73, 0xa3, 0x9c, 0x1c, 0x33, 0x06, 0x23, 0x9f, 0x2f, 0x32, 0x90, 0x4f, 0x0d, 0x2a, 0x33, 0xec, 0xca, 0x8a, 0x54, 0x51, 0x70, 0x5b, 0xb5, 0x37, 0xd3, 0xbf, 0x44, 0xe0, 0x71, 0x22, 0x60, 0x25, 0xcd, 0xbf, 0xd2, 0x49, 0xfe, 0x0f, 0x7a, 0xd6}, SECP256K1_FE_CONST(0x97a09cf1, 0xa2eae7c4, 0x94df3c6f, 0x8a9445bf, 0xb8c09d60, 0x832f9b0b, 0x9d5eabe2, 0x5fbd14b9), 0},
{{0xa1, 0xed, 0x0a, 0x0b, 0xd7, 0x9d, 0x8a, 0x23, 0xcf, 0xe4, 0xec, 0x5f, 0xef, 0x5b, 0xa5, 0xcc, 0xcf, 0xd8, 0x44, 0xe4, 0xff, 0x5c, 0xb4, 0xb0, 0xf2, 0xe7, 0x16, 0x27, 0x34, 0x1f, 0x1c, 0x5b, 0x17, 0xc4, 0x99, 0x24, 0x9e, 0x0a, 0xc0, 0x8d, 0x5d, 0x11, 0xea, 0x1c, 0x2c, 0x8c, 0xa7, 0x00, 0x16, 0x16, 0x55, 0x9a, 0x79, 0x94, 0xea, 0xde, 0xc9, 0xca, 0x10, 0xfb, 0x4b, 0x85, 0x16, 0xdc}, SECP256K1_FE_CONST(0x65a89640, 0x744192cd, 0xac64b2d2, 0x1ddf989c, 0xdac75007, 0x25b645be, 0xf8e2200a, 0xe39691f2), 0},
{{0xba, 0x94, 0x59, 0x4a, 0x43, 0x27, 0x21, 0xaa, 0x35, 0x80, 0xb8, 0x4c, 0x16, 0x1d, 0x0d, 0x13, 0x4b, 0xc3, 0x54, 0xb6, 0x90, 0x40, 0x4d, 0x7c, 0xd4, 0xec, 0x57, 0xc1, 0x6d, 0x3f, 0xbe, 0x98, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xea, 0x50, 0x7d, 0xd7}, SECP256K1_FE_CONST(0x5e0d7656, 0x4aae92cb, 0x347e01a6, 0x2afd389a, 0x9aa401c7, 0x6c8dd227, 0x543dc9cd, 0x0efe685a), 0},
{{0xbc, 0xaf, 0x72, 0x19, 0xf2, 0xf6, 0xfb, 0xf5, 0x5f, 0xe5, 0xe0, 0x62, 0xdc, 0xe0, 0xe4, 0x8c, 0x18, 0xf6, 0x81, 0x03, 0xf1, 0x0b, 0x81, 0x98, 0xe9, 0x74, 0xc1, 0x84, 0x75, 0x0e, 0x1b, 0xe3, 0x93, 0x20, 0x16, 0xcb, 0xf6, 0x9c, 0x44, 0x71, 0xbd, 0x1f, 0x65, 0x6c, 0x6a, 0x10, 0x7f, 0x19, 0x73, 0xde, 0x4a, 0xf7, 0x08, 0x6d, 0xb8, 0x97, 0x27, 0x70, 0x60, 0xe2, 0x56, 0x77, 0xf1, 0x9a}, SECP256K1_FE_CONST(0x2d97f96c, 0xac882dfe, 0x73dc44db, 0x6ce0f1d3, 0x1d624135, 0x8dd5d74e, 0xb3d3b500, 0x03d24c2b), 0},
{{0xbc, 0xaf, 0x72, 0x19, 0xf2, 0xf6, 0xfb, 0xf5, 0x5f, 0xe5, 0xe0, 0x62, 0xdc, 0xe0, 0xe4, 0x8c, 0x18, 0xf6, 0x81, 0x03, 0xf1, 0x0b, 0x81, 0x98, 0xe9, 0x74, 0xc1, 0x84, 0x75, 0x0e, 0x1b, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x65, 0x07, 0xd0, 0x9a}, SECP256K1_FE_CONST(0xe7008afe, 0x6e8cbd50, 0x55df120b, 0xd748757c, 0x686dadb4, 0x1cce75e4, 0xaddcc5e0, 0x2ec02b44), 1},
{{0xc5, 0x98, 0x1b, 0xae, 0x27, 0xfd, 0x84, 0x40, 0x1c, 0x72, 0xa1, 0x55, 0xe5, 0x70, 0x7f, 0xbb, 0x81, 0x1b, 0x2b, 0x62, 0x06, 0x45, 0xd1, 0x02, 0x8e, 0xa2, 0x70, 0xcb, 0xe0, 0xee, 0x22, 0x5d, 0x4b, 0x62, 0xaa, 0x4d, 0xca, 0x65, 0x06, 0xc1, 0xac, 0xdb, 0xec, 0xc0, 0x55, 0x25, 0x69, 0xb4, 0xb2, 0x14, 0x36, 0xa5, 0x69, 0x2e, 0x25, 0xd9, 0x0d, 0x3b, 0xc2, 0xeb, 0x7c, 0xe2, 0x40, 0x78}, SECP256K1_FE_CONST(0x948b40e7, 0x181713bc, 0x018ec170, 0x2d3d054d, 0x15746c59, 0xa7020730, 0xdd13ecf9, 0x85a010d7), 0},
{{0xc8, 0x94, 0xce, 0x48, 0xbf, 0xec, 0x43, 0x30, 0x14, 0xb9, 0x31, 0xa6, 0xad, 0x42, 0x26, 0xd7, 0xdb, 0xd8, 0xea, 0xa7, 0xb6, 0xe3, 0xfa, 0xa8, 0xd0, 0xef, 0x94, 0x05, 0x2b, 0xcf, 0x8c, 0xff, 0x33, 0x6e, 0xeb, 0x39, 0x19, 0xe2, 0xb4, 0xef, 0xb7, 0x46, 0xc7, 0xf7, 0x1b, 0xbc, 0xa7, 0xe9, 0x38, 0x32, 0x30, 0xfb, 0xbc, 0x48, 0xff, 0xaf, 0xe7, 0x7e, 0x8b, 0xcc, 0x69, 0x54, 0x24, 0x71}, SECP256K1_FE_CONST(0xf1c91acd, 0xc2525330, 0xf9b53158, 0x434a4d43, 0xa1c547cf, 0xf29f1550, 0x6f5da4eb, 0x4fe8fa5a), 1},
{{0xcb, 0xb0, 0xde, 0xab, 0x12, 0x57, 0x54, 0xf1, 0xfd, 0xb2, 0x03, 0x8b, 0x04, 0x34, 0xed, 0x9c, 0xb3, 0xfb, 0x53, 0xab, 0x73, 0x53, 0x91, 0x12, 0x99, 0x94, 0xa5, 0x35, 0xd9, 0x25, 0xf6, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x872d81ed, 0x8831d999, 0x8b67cb71, 0x05243edb, 0xf86c10ed, 0xfebb786c, 0x110b02d0, 0x7b2e67cd), 0},
{{0xd9, 0x17, 0xb7, 0x86, 0xda, 0xc3, 0x56, 0x70, 0xc3, 0x30, 0xc9, 0xc5, 0xae, 0x59, 0x71, 0xdf, 0xb4, 0x95, 0xc8, 0xae, 0x52, 0x3e, 0xd9, 0x7e, 0xe2, 0x42, 0x01, 0x17, 0xb1, 0x71, 0xf4, 0x1e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x20, 0x01, 0xf6, 0xf6}, SECP256K1_FE_CONST(0xe45b71e1, 0x10b831f2, 0xbdad8651, 0x994526e5, 0x8393fde4, 0x328b1ec0, 0x4d598971, 0x42584691), 1},
{{0xe2, 0x8b, 0xd8, 0xf5, 0x92, 0x9b, 0x46, 0x7e, 0xb7, 0x0e, 0x04, 0x33, 0x23, 0x74, 0xff, 0xb7, 0xe7, 0x18, 0x02, 0x18, 0xad, 0x16, 0xea, 0xa4, 0x6b, 0x71, 0x61, 0xaa, 0x67, 0x9e, 0xb4, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x66b8c980, 0xa75c72e5, 0x98d383a3, 0x5a62879f, 0x844242ad, 0x1e73ff12, 0xedaa59f4, 0xe58632b5), 0},
{{0xe2, 0x8b, 0xd8, 0xf5, 0x92, 0x9b, 0x46, 0x7e, 0xb7, 0x0e, 0x04, 0x33, 0x23, 0x74, 0xff, 0xb7, 0xe7, 0x18, 0x02, 0x18, 0xad, 0x16, 0xea, 0xa4, 0x6b, 0x71, 0x61, 0xaa, 0x67, 0x9e, 0xb4, 0x26, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x66b8c980, 0xa75c72e5, 0x98d383a3, 0x5a62879f, 0x844242ad, 0x1e73ff12, 0xedaa59f4, 0xe58632b5), 0},
{{0xe7, 0xee, 0x58, 0x14, 0xc1, 0x70, 0x6b, 0xf8, 0xa8, 0x93, 0x96, 0xa9, 0xb0, 0x32, 0xbc, 0x01, 0x4c, 0x2c, 0xac, 0x9c, 0x12, 0x11, 0x27, 0xdb, 0xf6, 0xc9, 0x92, 0x78, 0xf8, 0xbb, 0x53, 0xd1, 0xdf, 0xd0, 0x4d, 0xbc, 0xda, 0x8e, 0x35, 0x24, 0x66, 0xb6, 0xfc, 0xd5, 0xf2, 0xde, 0xa3, 0xe1, 0x7d, 0x5e, 0x13, 0x31, 0x15, 0x88, 0x6e, 0xda, 0x20, 0xdb, 0x8a, 0x12, 0xb5, 0x4d, 0xe7, 0x1b}, SECP256K1_FE_CONST(0xe842c6e3, 0x529b2342, 0x70a5e977, 0x44edc34a, 0x04d7ba94, 0xe44b6d25, 0x23c9cf01, 0x95730a50), 1},
{{0xf2, 0x92, 0xe4, 0x68, 0x25, 0xf9, 0x22, 0x5a, 0xd2, 0x3d, 0xc0, 0x57, 0xc1, 0xd9, 0x1c, 0x4f, 0x57, 0xfc, 0xb1, 0x38, 0x6f, 0x29, 0xef, 0x10, 0x48, 0x1c, 0xb1, 0xd2, 0x25, 0x18, 0x59, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x70, 0x11, 0xc9, 0x89}, SECP256K1_FE_CONST(0x3cea2c53, 0xb8b01701, 0x66ac7da6, 0x7194694a, 0xdacc84d5, 0x6389225e, 0x330134da, 0xb85a4d55), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0x01, 0xd3, 0x47, 0x5b, 0xf7, 0x65, 0x5b, 0x0f, 0xb2, 0xd8, 0x52, 0x92, 0x10, 0x35, 0xb2, 0xef, 0x60, 0x7f, 0x49, 0x06, 0x9b, 0x97, 0x45, 0x4e, 0x67, 0x95, 0x25, 0x10, 0x62, 0x74, 0x17, 0x71}, SECP256K1_FE_CONST(0xb5da00b7, 0x3cd65605, 0x20e7c364, 0x086e7cd2, 0x3a34bf60, 0xd0e707be, 0x9fc34d4c, 0xd5fdfa2c), 1},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0x42, 0x18, 0xf2, 0x0a, 0xe6, 0xc6, 0x46, 0xb3, 0x63, 0xdb, 0x68, 0x60, 0x58, 0x22, 0xfb, 0x14, 0x26, 0x4c, 0xa8, 0xd2, 0x58, 0x7f, 0xdd, 0x6f, 0xbc, 0x75, 0x0d, 0x58, 0x7e, 0x76, 0xa7, 0xee}, SECP256K1_FE_CONST(0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaa9, 0xfffffd6b), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0x82, 0x27, 0x7c, 0x4a, 0x71, 0xf9, 0xd2, 0x2e, 0x66, 0xec, 0xe5, 0x23, 0xf8, 0xfa, 0x08, 0x74, 0x1a, 0x7c, 0x09, 0x12, 0xc6, 0x6a, 0x69, 0xce, 0x68, 0x51, 0x4b, 0xfd, 0x35, 0x15, 0xb4, 0x9f}, SECP256K1_FE_CONST(0xf482f2e2, 0x41753ad0, 0xfb89150d, 0x8491dc1e, 0x34ff0b8a, 0xcfbb442c, 0xfe999e2e, 0x5e6fd1d2), 1},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0x84, 0x21, 0xcc, 0x93, 0x0e, 0x77, 0xc9, 0xf5, 0x14, 0xb6, 0x91, 0x5c, 0x3d, 0xbe, 0x2a, 0x94, 0xc6, 0xd8, 0xf6, 0x90, 0xb5, 0xb7, 0x39, 0x86, 0x4b, 0xa6, 0x78, 0x9f, 0xb8, 0xa5, 0x5d, 0xd0}, SECP256K1_FE_CONST(0x9f59c402, 0x75f5085a, 0x006f05da, 0xe77eb98c, 0x6fd0db1a, 0xb4a72ac4, 0x7eae90a4, 0xfc9e57e0), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0xd1, 0x9c, 0x18, 0x2d, 0x27, 0x59, 0xcd, 0x99, 0x82, 0x42, 0x28, 0xd9, 0x47, 0x99, 0xf8, 0xc6, 0x55, 0x7c, 0x38, 0xa1, 0xc0, 0xd6, 0x77, 0x9b, 0x9d, 0x4b, 0x72, 0x9c, 0x6f, 0x1c, 0xcc, 0x42}, SECP256K1_FE_CONST(0x70720db7, 0xe238d041, 0x21f5b1af, 0xd8cc5ad9, 0xd18944c6, 0xbdc94881, 0xf502b7a3, 0xaf3aecff), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x26, 0x64, 0xbb, 0xd5}, SECP256K1_FE_CONST(0x50873db3, 0x1badcc71, 0x890e4f67, 0x753a6575, 0x7f97aaa7, 0xdd5f1e82, 0xb753ace3, 0x2219064b), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x70, 0x28, 0xde, 0x7d}, SECP256K1_FE_CONST(0x1eea9cc5, 0x9cfcf2fa, 0x151ac6c2, 0x74eea411, 0x0feb4f7b, 0x68c59657, 0x32e9992e, 0x976ef68e), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcb, 0xcf, 0xb7, 0xe7}, SECP256K1_FE_CONST(0x12303941, 0xaedc2088, 0x80735b1f, 0x1795c8e5, 0x5be520ea, 0x93e10335, 0x7b5d2adb, 0x7ed59b8e), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0x11, 0x3a, 0xd9}, SECP256K1_FE_CONST(0x7eed6b70, 0xe7b0767c, 0x7d7feac0, 0x4e57aa2a, 0x12fef5e0, 0xf48f878f, 0xcbb88b3b, 0x6b5e0783), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x13, 0xce, 0xa4, 0xa7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x64998443, 0x5b62b4a2, 0x5d40c613, 0x3e8d9ab8, 0xc53d4b05, 0x9ee8a154, 0xa3be0fcf, 0x4e892edb), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x13, 0xce, 0xa4, 0xa7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x64998443, 0x5b62b4a2, 0x5d40c613, 0x3e8d9ab8, 0xc53d4b05, 0x9ee8a154, 0xa3be0fcf, 0x4e892edb), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x15, 0x02, 0x8c, 0x59, 0x00, 0x63, 0xf6, 0x4d, 0x5a, 0x7f, 0x1c, 0x14, 0x91, 0x5c, 0xd6, 0x1e, 0xac, 0x88, 0x6a, 0xb2, 0x95, 0xbe, 0xbd, 0x91, 0x99, 0x25, 0x04, 0xcf, 0x77, 0xed, 0xb0, 0x28, 0xbd, 0xd6, 0x26, 0x7f}, SECP256K1_FE_CONST(0x3fde5713, 0xf8282eea, 0xd7d39d42, 0x01f44a7c, 0x85a5ac8a, 0x0681f35e, 0x54085c6b, 0x69543374), 1},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x27, 0x15, 0xde, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x3524f77f, 0xa3a6eb43, 0x89c3cb5d, 0x27f1f914, 0x62086429, 0xcd6c0cb0, 0xdf43ea8f, 0x1e7b3fb4), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x27, 0x15, 0xde, 0x86, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x3524f77f, 0xa3a6eb43, 0x89c3cb5d, 0x27f1f914, 0x62086429, 0xcd6c0cb0, 0xdf43ea8f, 0x1e7b3fb4), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x2c, 0x2c, 0x57, 0x09, 0xe7, 0x15, 0x6c, 0x41, 0x77, 0x17, 0xf2, 0xfe, 0xab, 0x14, 0x71, 0x41, 0xec, 0x3d, 0xa1, 0x9f, 0xb7, 0x59, 0x57, 0x5c, 0xc6, 0xe3, 0x7b, 0x2e, 0xa5, 0xac, 0x93, 0x09, 0xf2, 0x6f, 0x0f, 0x66}, SECP256K1_FE_CONST(0xd2469ab3, 0xe04acbb2, 0x1c65a180, 0x9f39caaf, 0xe7a77c13, 0xd10f9dd3, 0x8f391c01, 0xdc499c52), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3a, 0x08, 0xcc, 0x1e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x60, 0xe9, 0xf0}, SECP256K1_FE_CONST(0x38e2a5ce, 0x6a93e795, 0xe16d2c39, 0x8bc99f03, 0x69202ce2, 0x1e8f09d5, 0x6777b40f, 0xc512bccc), 1},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0x91, 0x25, 0x7d, 0x93, 0x20, 0x16, 0xcb, 0xf6, 0x9c, 0x44, 0x71, 0xbd, 0x1f, 0x65, 0x6c, 0x6a, 0x10, 0x7f, 0x19, 0x73, 0xde, 0x4a, 0xf7, 0x08, 0x6d, 0xb8, 0x97, 0x27, 0x70, 0x60, 0xe2, 0x56, 0x77, 0xf1, 0x9a}, SECP256K1_FE_CONST(0x864b3dc9, 0x02c37670, 0x9c10a93a, 0xd4bbe29f, 0xce0012f3, 0xdc8672c6, 0x286bba28, 0xd7d6d6fc), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x79, 0x5d, 0x6c, 0x1c, 0x32, 0x2c, 0xad, 0xf5, 0x99, 0xdb, 0xb8, 0x64, 0x81, 0x52, 0x2b, 0x3c, 0xc5, 0x5f, 0x15, 0xa6, 0x79, 0x32, 0xdb, 0x2a, 0xfa, 0x01, 0x11, 0xd9, 0xed, 0x69, 0x81, 0xbc, 0xd1, 0x24, 0xbf, 0x44}, SECP256K1_FE_CONST(0x766dfe4a, 0x700d9bee, 0x288b903a, 0xd58870e3, 0xd4fe2f0e, 0xf780bcac, 0x5c823f32, 0x0d9a9bef), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8e, 0x42, 0x6f, 0x03, 0x92, 0x38, 0x90, 0x78, 0xc1, 0x2b, 0x1a, 0x89, 0xe9, 0x54, 0x2f, 0x05, 0x93, 0xbc, 0x96, 0xb6, 0xbf, 0xde, 0x82, 0x24, 0xf8, 0x65, 0x4e, 0xf5, 0xd5, 0xcd, 0xa9, 0x35, 0xa3, 0x58, 0x21, 0x94}, SECP256K1_FE_CONST(0xfaec7bc1, 0x987b6323, 0x3fbc5f95, 0x6edbf37d, 0x54404e74, 0x61c58ab8, 0x631bc68e, 0x451a0478), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x91, 0x19, 0x21, 0x39, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x45, 0xf0, 0xf1, 0xeb}, SECP256K1_FE_CONST(0xec29a50b, 0xae138dbf, 0x7d8e2482, 0x5006bb5f, 0xc1a2cc12, 0x43ba335b, 0xc6116fb9, 0xe498ec1f), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x98, 0xeb, 0x9a, 0xb7, 0x6e, 0x84, 0x49, 0x9c, 0x48, 0x3b, 0x3b, 0xf0, 0x62, 0x14, 0xab, 0xfe, 0x06, 0x5d, 0xdd, 0xf4, 0x3b, 0x86, 0x01, 0xde, 0x59, 0x6d, 0x63, 0xb9, 0xe4, 0x5a, 0x16, 0x6a, 0x58, 0x05, 0x41, 0xfe}, SECP256K1_FE_CONST(0x1e0ff2de, 0xe9b09b13, 0x6292a9e9, 0x10f0d6ac, 0x3e552a64, 0x4bba39e6, 0x4e9dd3e3, 0xbbd3d4d4), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9b, 0x77, 0xb7, 0xf2, 0xc7, 0x4d, 0x99, 0xef, 0xce, 0xaa, 0x55, 0x0f, 0x1a, 0xd1, 0xc0, 0xf4, 0x3f, 0x46, 0xe7, 0xff, 0x1e, 0xe3, 0xbd, 0x01, 0x62, 0xb7, 0xbf, 0x55, 0xf2, 0x96, 0x5d, 0xa9, 0xc3, 0x45, 0x06, 0x46}, SECP256K1_FE_CONST(0x8b7dd5c3, 0xedba9ee9, 0x7b70eff4, 0x38f22dca, 0x9849c825, 0x4a2f3345, 0xa0a572ff, 0xeaae0928), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9b, 0x77, 0xb7, 0xf2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x15, 0x6c, 0xa8, 0x96}, SECP256K1_FE_CONST(0x0881950c, 0x8f51d6b9, 0xa6387465, 0xd5f12609, 0xef1bb254, 0x12a08a74, 0xcb2dfb20, 0x0c74bfbf), 1},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa2, 0xf5, 0xcd, 0x83, 0x88, 0x16, 0xc1, 0x6c, 0x4f, 0xe8, 0xa1, 0x66, 0x1d, 0x60, 0x6f, 0xdb, 0x13, 0xcf, 0x9a, 0xf0, 0x4b, 0x97, 0x9a, 0x2e, 0x15, 0x9a, 0x09, 0x40, 0x9e, 0xbc, 0x86, 0x45, 0xd5, 0x8f, 0xde, 0x02}, SECP256K1_FE_CONST(0x2f083207, 0xb9fd9b55, 0x0063c31c, 0xd62b8746, 0xbd543bdc, 0x5bbf10e3, 0xa35563e9, 0x27f440c8), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb1, 0x3f, 0x75, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x4f51e0be, 0x078e0cdd, 0xab274215, 0x6adba7e7, 0xa148e731, 0x57072fd6, 0x18cd6094, 0x2b146bd0), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb1, 0x3f, 0x75, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x4f51e0be, 0x078e0cdd, 0xab274215, 0x6adba7e7, 0xa148e731, 0x57072fd6, 0x18cd6094, 0x2b146bd0), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xbc, 0x1f, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x16c2ccb5, 0x4352ff4b, 0xd794f6ef, 0xd613c721, 0x97ab7082, 0xda5b563b, 0xdf9cb3ed, 0xaafe74c2), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xbc, 0x1f, 0x8d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x16c2ccb5, 0x4352ff4b, 0xd794f6ef, 0xd613c721, 0x97ab7082, 0xda5b563b, 0xdf9cb3ed, 0xaafe74c2), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x64, 0xd1, 0x62, 0x75, 0x05, 0x46, 0xce, 0x42, 0xb0, 0x43, 0x13, 0x61, 0xe5, 0x2d, 0x4f, 0x52, 0x42, 0xd8, 0xf2, 0x4f, 0x33, 0xe6, 0xb1, 0xf9, 0x9b, 0x59, 0x16, 0x47, 0xcb, 0xc8, 0x08, 0xf4, 0x62, 0xaf, 0x51}, SECP256K1_FE_CONST(0xd41244d1, 0x1ca4f652, 0x40687759, 0xf95ca9ef, 0xbab767ed, 0xedb38fd1, 0x8c36e18c, 0xd3b6f6a9), 1},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xe5, 0xbe, 0x52, 0x37, 0x2d, 0xd6, 0xe8, 0x94, 0xb2, 0xa3, 0x26, 0xfc, 0x36, 0x05, 0xa6, 0xe8, 0xf3, 0xc6, 0x9c, 0x71, 0x0b, 0xf2, 0x7d, 0x63, 0x0d, 0xfe, 0x20, 0x04, 0x98, 0x8b, 0x78, 0xeb, 0x6e, 0xab, 0x36}, SECP256K1_FE_CONST(0x64bf84dd, 0x5e03670f, 0xdb24c0f5, 0xd3c2c365, 0x736f51db, 0x6c92d950, 0x10716ad2, 0xd36134c8), 0},
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfb, 0xb9, 0x82, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xd6, 0xdb, 0x1f}, SECP256K1_FE_CONST(0x1c92ccdf, 0xcf4ac550, 0xc28db57c, 0xff0c8515, 0xcb26936c, 0x786584a7, 0x0114008d, 0x6c33a34b), 0},
};
/* Set of expected ellswift_xdh BIP324 shared secrets, given private key, encodings, initiating,
* taken from the BIP324 test vectors. Created using an independent implementation, and tested
* against the paper authors' decoding code. */
static const struct ellswift_xdh_test ellswift_xdh_tests_bip324[] = {
{{0x61, 0x06, 0x2e, 0xa5, 0x07, 0x1d, 0x80, 0x0b, 0xbf, 0xd5, 0x9e, 0x2e, 0x8b, 0x53, 0xd4, 0x7d, 0x19, 0x4b, 0x09, 0x5a, 0xe5, 0xa4, 0xdf, 0x04, 0x93, 0x6b, 0x49, 0x77, 0x2e, 0xf0, 0xd4, 0xd7}, {0xec, 0x0a, 0xdf, 0xf2, 0x57, 0xbb, 0xfe, 0x50, 0x0c, 0x18, 0x8c, 0x80, 0xb4, 0xfd, 0xd6, 0x40, 0xf6, 0xb4, 0x5a, 0x48, 0x2b, 0xbc, 0x15, 0xfc, 0x7c, 0xef, 0x59, 0x31, 0xde, 0xff, 0x0a, 0xa1, 0x86, 0xf6, 0xeb, 0x9b, 0xba, 0x7b, 0x85, 0xdc, 0x4d, 0xcc, 0x28, 0xb2, 0x87, 0x22, 0xde, 0x1e, 0x3d, 0x91, 0x08, 0xb9, 0x85, 0xe2, 0x96, 0x70, 0x45, 0x66, 0x8f, 0x66, 0x09, 0x8e, 0x47, 0x5b}, {0xa4, 0xa9, 0x4d, 0xfc, 0xe6, 0x9b, 0x4a, 0x2a, 0x0a, 0x09, 0x93, 0x13, 0xd1, 0x0f, 0x9f, 0x7e, 0x7d, 0x64, 0x9d, 0x60, 0x50, 0x1c, 0x9e, 0x1d, 0x27, 0x4c, 0x30, 0x0e, 0x0d, 0x89, 0xaa, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xaf, 0x88, 0xd5}, 1, {0xc6, 0x99, 0x2a, 0x11, 0x7f, 0x5e, 0xdb, 0xea, 0x70, 0xc3, 0xf5, 0x11, 0xd3, 0x2d, 0x26, 0xb9, 0x79, 0x8b, 0xe4, 0xb8, 0x1a, 0x62, 0xea, 0xee, 0x1a, 0x5a, 0xca, 0xa8, 0x45, 0x9a, 0x35, 0x92}},
{{0x1f, 0x9c, 0x58, 0x1b, 0x35, 0x23, 0x18, 0x38, 0xf0, 0xf1, 0x7c, 0xf0, 0xc9, 0x79, 0x83, 0x5b, 0xac, 0xcb, 0x7f, 0x3a, 0xbb, 0xbb, 0x96, 0xff, 0xcc, 0x31, 0x8a, 0xb7, 0x1e, 0x6e, 0x12, 0x6f}, {0xa1, 0x85, 0x5e, 0x10, 0xe9, 0x4e, 0x00, 0xba, 0xa2, 0x30, 0x41, 0xd9, 0x16, 0xe2, 0x59, 0xf7, 0x04, 0x4e, 0x49, 0x1d, 0xa6, 0x17, 0x12, 0x69, 0x69, 0x47, 0x63, 0xf0, 0x18, 0xc7, 0xe6, 0x36, 0x93, 0xd2, 0x95, 0x75, 0xdc, 0xb4, 0x64, 0xac, 0x81, 0x6b, 0xaa, 0x1b, 0xe3, 0x53, 0xba, 0x12, 0xe3, 0x87, 0x6c, 0xba, 0x76, 0x28, 0xbd, 0x0b, 0xd8, 0xe7, 0x55, 0xe7, 0x21, 0xeb, 0x01, 0x40}, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 0, {0xa0, 0x13, 0x8f, 0x56, 0x4f, 0x74, 0xd0, 0xad, 0x70, 0xbc, 0x33, 0x7d, 0xac, 0xc9, 0xd0, 0xbf, 0x1d, 0x23, 0x49, 0x36, 0x4c, 0xaf, 0x11, 0x88, 0xa1, 0xe6, 0xe8, 0xdd, 0xb3, 0xb7, 0xb1, 0x84}},
{{0x02, 0x86, 0xc4, 0x1c, 0xd3, 0x09, 0x13, 0xdb, 0x0f, 0xdf, 0xf7, 0xa6, 0x4e, 0xbd, 0xa5, 0xc8, 0xe3, 0xe7, 0xce, 0xf1, 0x0f, 0x2a, 0xeb, 0xc0, 0x0a, 0x76, 0x50, 0x44, 0x3c, 0xf4, 0xc6, 0x0d}, {0xd1, 0xee, 0x8a, 0x93, 0xa0, 0x11, 0x30, 0xcb, 0xf2, 0x99, 0x24, 0x9a, 0x25, 0x8f, 0x94, 0xfe, 0xb5, 0xf4, 0x69, 0xe7, 0xd0, 0xf2, 0xf2, 0x8f, 0x69, 0xee, 0x5e, 0x9a, 0xa8, 0xf9, 0xb5, 0x4a, 0x60, 0xf2, 0xc3, 0xff, 0x2d, 0x02, 0x36, 0x34, 0xec, 0x7f, 0x41, 0x27, 0xa9, 0x6c, 0xc1, 0x16, 0x62, 0xe4, 0x02, 0x89, 0x4c, 0xf1, 0xf6, 0x94, 0xfb, 0x9a, 0x7e, 0xaa, 0x5f, 0x1d, 0x92, 0x44}, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x22, 0xd5, 0xe4, 0x41, 0x52, 0x4d, 0x57, 0x1a, 0x52, 0xb3, 0xde, 0xf1, 0x26, 0x18, 0x9d, 0x3f, 0x41, 0x68, 0x90, 0xa9, 0x9d, 0x4d, 0xa6, 0xed, 0xe2, 0xb0, 0xcd, 0xe1, 0x76, 0x0c, 0xe2, 0xc3, 0xf9, 0x84, 0x57, 0xae}, 1, {0x25, 0x0b, 0x93, 0x57, 0x0d, 0x41, 0x11, 0x49, 0x10, 0x5a, 0xb8, 0xcb, 0x0b, 0xc5, 0x07, 0x99, 0x14, 0x90, 0x63, 0x06, 0x36, 0x8c, 0x23, 0xe9, 0xd7, 0x7c, 0x2a, 0x33, 0x26, 0x5b, 0x99, 0x4c}},
{{0x6c, 0x77, 0x43, 0x2d, 0x1f, 0xda, 0x31, 0xe9, 0xf9, 0x42, 0xf8, 0xaf, 0x44, 0x60, 0x7e, 0x10, 0xf3, 0xad, 0x38, 0xa6, 0x5f, 0x8a, 0x4b, 0xdd, 0xae, 0x82, 0x3e, 0x5e, 0xff, 0x90, 0xdc, 0x38}, {0xd2, 0x68, 0x50, 0x70, 0xc1, 0xe6, 0x37, 0x6e, 0x63, 0x3e, 0x82, 0x52, 0x96, 0x63, 0x4f, 0xd4, 0x61, 0xfa, 0x9e, 0x5b, 0xdf, 0x21, 0x09, 0xbc, 0xeb, 0xd7, 0x35, 0xe5, 0xa9, 0x1f, 0x3e, 0x58, 0x7c, 0x5c, 0xb7, 0x82, 0xab, 0xb7, 0x97, 0xfb, 0xf6, 0xbb, 0x50, 0x74, 0xfd, 0x15, 0x42, 0xa4, 0x74, 0xf2, 0xa4, 0x5b, 0x67, 0x37, 0x63, 0xec, 0x2d, 0xb7, 0xfb, 0x99, 0xb7, 0x37, 0xbb, 0xb9}, {0x56, 0xbd, 0x0c, 0x06, 0xf1, 0x03, 0x52, 0xc3, 0xa1, 0xa9, 0xf4, 0xb4, 0xc9, 0x2f, 0x6f, 0xa2, 0xb2, 0x6d, 0xf1, 0x24, 0xb5, 0x78, 0x78, 0x35, 0x3c, 0x1f, 0xc6, 0x91, 0xc5, 0x1a, 0xbe, 0xa7, 0x7c, 0x88, 0x17, 0xda, 0xee, 0xb9, 0xfa, 0x54, 0x6b, 0x77, 0xc8, 0xda, 0xf7, 0x9d, 0x89, 0xb2, 0x2b, 0x0e, 0x1b, 0x87, 0x57, 0x4e, 0xce, 0x42, 0x37, 0x1f, 0x00, 0x23, 0x7a, 0xa9, 0xd8, 0x3a}, 0, {0x19, 0x18, 0xb7, 0x41, 0xef, 0x5f, 0x9d, 0x1d, 0x76, 0x70, 0xb0, 0x50, 0xc1, 0x52, 0xb4, 0xa4, 0xea, 0xd2, 0xc3, 0x1b, 0xe9, 0xae, 0xcb, 0x06, 0x81, 0xc0, 0xcd, 0x43, 0x24, 0x15, 0x08, 0x53}},
{{0xa6, 0xec, 0x25, 0x12, 0x7c, 0xa1, 0xaa, 0x4c, 0xf1, 0x6b, 0x20, 0x08, 0x4b, 0xa1, 0xe6, 0x51, 0x6b, 0xaa, 0xe4, 0xd3, 0x24, 0x22, 0x28, 0x8e, 0x9b, 0x36, 0xd8, 0xbd, 0xdd, 0x2d, 0xe3, 0x5a}, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x05, 0x3d, 0x7e, 0xcc, 0xa5, 0x3e, 0x33, 0xe1, 0x85, 0xa8, 0xb9, 0xbe, 0x4e, 0x76, 0x99, 0xa9, 0x7c, 0x6f, 0xf4, 0xc7, 0x95, 0x52, 0x2e, 0x59, 0x18, 0xab, 0x7c, 0xd6, 0xb6, 0x88, 0x4f, 0x67, 0xe6, 0x83, 0xf3, 0xdc}, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa7, 0x73, 0x0b, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 1, {0xdd, 0x21, 0x0a, 0xa6, 0x62, 0x9f, 0x20, 0xbb, 0x32, 0x8e, 0x5d, 0x89, 0xda, 0xa6, 0xeb, 0x2a, 0xc3, 0xd1, 0xc6, 0x58, 0xa7, 0x25, 0x53, 0x6f, 0xf1, 0x54, 0xf3, 0x1b, 0x53, 0x6c, 0x23, 0xb2}},
{{0x0a, 0xf9, 0x52, 0x65, 0x9e, 0xd7, 0x6f, 0x80, 0xf5, 0x85, 0x96, 0x6b, 0x95, 0xab, 0x6e, 0x6f, 0xd6, 0x86, 0x54, 0x67, 0x28, 0x27, 0x87, 0x86, 0x84, 0xc8, 0xb5, 0x47, 0xb1, 0xb9, 0x4f, 0x5a}, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0x10, 0x17, 0xfd, 0x92, 0xfd, 0x31, 0x63, 0x7c, 0x26, 0xc9, 0x06, 0xb4, 0x20, 0x92, 0xe1, 0x1c, 0xc0, 0xd3, 0xaf, 0xae, 0x8d, 0x90, 0x19, 0xd2, 0x57, 0x8a, 0xf2, 0x27, 0x35, 0xce, 0x7b, 0xc4, 0x69, 0xc7, 0x2d}, {0x96, 0x52, 0xd7, 0x8b, 0xae, 0xfc, 0x02, 0x8c, 0xd3, 0x7a, 0x6a, 0x92, 0x62, 0x5b, 0x8b, 0x8f, 0x85, 0xfd, 0xe1, 0xe4, 0xc9, 0x44, 0xad, 0x3f, 0x20, 0xe1, 0x98, 0xbe, 0xf8, 0xc0, 0x2f, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf2, 0xe9, 0x18, 0x70}, 0, {0x35, 0x68, 0xf2, 0xae, 0xa2, 0xe1, 0x4e, 0xf4, 0xee, 0x4a, 0x3c, 0x2a, 0x8b, 0x8d, 0x31, 0xbc, 0x5e, 0x31, 0x87, 0xba, 0x86, 0xdb, 0x10, 0x73, 0x9b, 0x4f, 0xf8, 0xec, 0x92, 0xff, 0x66, 0x55}},
{{0xf9, 0x0e, 0x08, 0x0c, 0x64, 0xb0, 0x58, 0x24, 0xc5, 0xa2, 0x4b, 0x25, 0x01, 0xd5, 0xae, 0xaf, 0x08, 0xaf, 0x38, 0x72, 0xee, 0x86, 0x0a, 0xa8, 0x0b, 0xdc, 0xd4, 0x30, 0xf7, 0xb6, 0x34, 0x94}, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11, 0x51, 0x73, 0x76, 0x5d, 0xc2, 0x02, 0xcf, 0x02, 0x9a, 0xd3, 0xf1, 0x54, 0x79, 0x73, 0x5d, 0x57, 0x69, 0x7a, 0xf1, 0x2b, 0x01, 0x31, 0xdd, 0x21, 0x43, 0x0d, 0x57, 0x72, 0xe4, 0xef, 0x11, 0x47, 0x4d, 0x58, 0xb9}, {0x12, 0xa5, 0x0f, 0x3f, 0xaf, 0xea, 0x7c, 0x1e, 0xea, 0xda, 0x4c, 0xf8, 0xd3, 0x37, 0x77, 0x70, 0x4b, 0x77, 0x36, 0x14, 0x53, 0xaf, 0xc8, 0x3b, 0xda, 0x91, 0xee, 0xf3, 0x49, 0xae, 0x04, 0x4d, 0x20, 0x12, 0x6c, 0x62, 0x00, 0x54, 0x7e, 0xa5, 0xa6, 0x91, 0x17, 0x76, 0xc0, 0x5d, 0xee, 0x2a, 0x7f, 0x1a, 0x9b, 0xa7, 0xdf, 0xba, 0xbb, 0xbd, 0x27, 0x3c, 0x3e, 0xf2, 0x9e, 0xf4, 0x6e, 0x46}, 1, {0xe2, 0x54, 0x61, 0xfb, 0x0e, 0x4c, 0x16, 0x2e, 0x18, 0x12, 0x3e, 0xcd, 0xe8, 0x83, 0x42, 0xd5, 0x4d, 0x44, 0x96, 0x31, 0xe9, 0xb7, 0x5a, 0x26, 0x6f, 0xd9, 0x26, 0x0c, 0x2b, 0xb2, 0xf4, 0x1d}},
};
/** This is a hasher for ellswift_xdh which just returns the shared X coordinate.
*
* This is generally a bad idea as it means changes to the encoding of the
* exchanged public keys do not affect the shared secret. However, it's used here
* in tests to be able to verify the X coordinate through other means.
*/
static int ellswift_xdh_hash_x32(unsigned char *output, const unsigned char *x32, const unsigned char *ell_a64, const unsigned char *ell_b64, void *data) {
(void)ell_a64;
(void)ell_b64;
(void)data;
memcpy(output, x32, 32);
return 1;
}
/* Run the test vectors for ellswift encoding */
void ellswift_encoding_test_vectors_tests(void) {
int i;
for (i = 0; (unsigned)i < ARRAY_SIZE(ellswift_xswiftec_inv_tests); ++i) {
const struct ellswift_xswiftec_inv_test *testcase = &ellswift_xswiftec_inv_tests[i];
int c;
for (c = 0; c < 8; ++c) {
secp256k1_fe t;
int ret = secp256k1_ellswift_xswiftec_inv_var(&t, &testcase->x, &testcase->u, c);
CHECK(ret == ((testcase->enc_bitmap >> c) & 1));
if (ret) {
secp256k1_fe x2;
CHECK(fe_equal(&t, &testcase->encs[c]));
secp256k1_ellswift_xswiftec_var(&x2, &testcase->u, &testcase->encs[c]);
CHECK(fe_equal(&testcase->x, &x2));
}
}
}
}
/* Run the test vectors for ellswift decoding */
void ellswift_decoding_test_vectors_tests(void) {
int i;
for (i = 0; (unsigned)i < ARRAY_SIZE(ellswift_decode_tests); ++i) {
const struct ellswift_decode_test *testcase = &ellswift_decode_tests[i];
secp256k1_pubkey pubkey;
secp256k1_ge ge;
int ret;
ret = secp256k1_ellswift_decode(CTX, &pubkey, testcase->enc);
CHECK(ret);
ret = secp256k1_pubkey_load(CTX, &ge, &pubkey);
CHECK(ret);
CHECK(fe_equal(&testcase->x, &ge.x));
CHECK(secp256k1_fe_is_odd(&ge.y) == testcase->odd_y);
}
}
/* Run the test vectors for ellswift expected xdh BIP324 shared secrets */
void ellswift_xdh_test_vectors_tests(void) {
int i;
for (i = 0; (unsigned)i < ARRAY_SIZE(ellswift_xdh_tests_bip324); ++i) {
const struct ellswift_xdh_test *test = &ellswift_xdh_tests_bip324[i];
unsigned char shared_secret[32];
int ret;
int party = !test->initiating;
const unsigned char* ell_a64 = party ? test->ellswift_theirs : test->ellswift_ours;
const unsigned char* ell_b64 = party ? test->ellswift_ours : test->ellswift_theirs;
ret = secp256k1_ellswift_xdh(CTX, shared_secret,
ell_a64, ell_b64,
test->priv_ours,
party,
secp256k1_ellswift_xdh_hash_function_bip324,
NULL);
CHECK(ret);
CHECK(secp256k1_memcmp_var(shared_secret, test->shared_secret, 32) == 0);
}
}
/* Verify that secp256k1_ellswift_encode + decode roundtrips */
void ellswift_encode_decode_roundtrip_tests(void) {
int i;
for (i = 0; i < 1000 * COUNT; i++) {
unsigned char rnd32[32];
unsigned char ell64[64];
secp256k1_ge g, g2;
secp256k1_pubkey pubkey, pubkey2;
/* Generate random public key and random randomizer. */
testutil_random_ge_test(&g);
secp256k1_pubkey_save(&pubkey, &g);
testrand256(rnd32);
/* Convert the public key to ElligatorSwift and back. */
secp256k1_ellswift_encode(CTX, ell64, &pubkey, rnd32);
secp256k1_ellswift_decode(CTX, &pubkey2, ell64);
secp256k1_pubkey_load(CTX, &g2, &pubkey2);
/* Compare with original. */
CHECK(secp256k1_ge_eq_var(&g, &g2));
}
}
/* Verify the behavior of secp256k1_ellswift_create */
void ellswift_create_tests(void) {
int i;
for (i = 0; i < 400 * COUNT; i++) {
unsigned char auxrnd32[32], sec32[32];
secp256k1_scalar sec;
secp256k1_gej res;
secp256k1_ge dec;
secp256k1_pubkey pub;
unsigned char ell64[64];
int ret;
/* Generate random secret key and random randomizer. */
if (i & 1) testrand256_test(auxrnd32);
testutil_random_scalar_order_test(&sec);
secp256k1_scalar_get_b32(sec32, &sec);
/* Construct ElligatorSwift-encoded public keys for that key. */
ret = secp256k1_ellswift_create(CTX, ell64, sec32, (i & 1) ? auxrnd32 : NULL);
CHECK(ret);
/* Decode it, and compare with traditionally-computed public key. */
secp256k1_ellswift_decode(CTX, &pub, ell64);
secp256k1_pubkey_load(CTX, &dec, &pub);
secp256k1_ecmult(&res, NULL, &secp256k1_scalar_zero, &sec);
CHECK(secp256k1_gej_eq_ge_var(&res, &dec));
}
}
/* Verify that secp256k1_ellswift_xdh computes the right shared X coordinate */
void ellswift_compute_shared_secret_tests(void) {
int i;
for (i = 0; i < 800 * COUNT; i++) {
unsigned char ell64[64], sec32[32], share32[32];
secp256k1_scalar sec;
secp256k1_ge dec, res;
secp256k1_fe share_x;
secp256k1_gej decj, resj;
secp256k1_pubkey pub;
int ret;
/* Generate random secret key. */
testutil_random_scalar_order_test(&sec);
secp256k1_scalar_get_b32(sec32, &sec);
/* Generate random ElligatorSwift encoding for the remote key and decode it. */
testrand256_test(ell64);
testrand256_test(ell64 + 32);
secp256k1_ellswift_decode(CTX, &pub, ell64);
secp256k1_pubkey_load(CTX, &dec, &pub);
secp256k1_gej_set_ge(&decj, &dec);
/* Compute the X coordinate of seckey*pubkey using ellswift_xdh. Note that we
* pass ell64 as claimed (but incorrect) encoding for sec32 here; this works
* because the "hasher" function we use here ignores the ell64 arguments. */
ret = secp256k1_ellswift_xdh(CTX, share32, ell64, ell64, sec32, i & 1, &ellswift_xdh_hash_x32, NULL);
CHECK(ret);
(void)secp256k1_fe_set_b32_limit(&share_x, share32); /* no overflow is possible */
SECP256K1_FE_VERIFY(&share_x);
/* Compute seckey*pubkey directly. */
secp256k1_ecmult(&resj, &decj, &sec, NULL);
secp256k1_ge_set_gej(&res, &resj);
/* Compare. */
CHECK(fe_equal(&res.x, &share_x));
}
}
void ellswift_xdh_correctness_tests(void) {
int i;
/* Verify the joint behavior of secp256k1_ellswift_xdh */
for (i = 0; i < 200 * COUNT; i++) {
unsigned char auxrnd32a[32], auxrnd32b[32], auxrnd32a_bad[32], auxrnd32b_bad[32];
unsigned char sec32a[32], sec32b[32], sec32a_bad[32], sec32b_bad[32];
secp256k1_scalar seca, secb;
unsigned char ell64a[64], ell64b[64], ell64a_bad[64], ell64b_bad[64];
unsigned char share32a[32], share32b[32], share32_bad[32];
unsigned char prefix64[64];
secp256k1_ellswift_xdh_hash_function hash_function;
void* data;
int ret;
/* Pick hasher to use. */
if ((i % 3) == 0) {
hash_function = ellswift_xdh_hash_x32;
data = NULL;
} else if ((i % 3) == 1) {
hash_function = secp256k1_ellswift_xdh_hash_function_bip324;
data = NULL;
} else {
hash_function = secp256k1_ellswift_xdh_hash_function_prefix;
testrand256_test(prefix64);
testrand256_test(prefix64 + 32);
data = prefix64;
}
/* Generate random secret keys and random randomizers. */
testrand256_test(auxrnd32a);
testrand256_test(auxrnd32b);
testutil_random_scalar_order_test(&seca);
/* Draw secb uniformly at random to make sure that the secret keys
* differ */
testutil_random_scalar_order(&secb);
secp256k1_scalar_get_b32(sec32a, &seca);
secp256k1_scalar_get_b32(sec32b, &secb);
/* Construct ElligatorSwift-encoded public keys for those keys. */
/* For A: */
ret = secp256k1_ellswift_create(CTX, ell64a, sec32a, auxrnd32a);
CHECK(ret);
/* For B: */
ret = secp256k1_ellswift_create(CTX, ell64b, sec32b, auxrnd32b);
CHECK(ret);
/* Compute the shared secret both ways and compare with each other. */
/* For A: */
ret = secp256k1_ellswift_xdh(CTX, share32a, ell64a, ell64b, sec32a, 0, hash_function, data);
CHECK(ret);
/* For B: */
ret = secp256k1_ellswift_xdh(CTX, share32b, ell64a, ell64b, sec32b, 1, hash_function, data);
CHECK(ret);
/* And compare: */
CHECK(secp256k1_memcmp_var(share32a, share32b, 32) == 0);
/* Verify that the shared secret doesn't match if other side's public key is incorrect. */
/* For A (using a bad public key for B): */
memcpy(ell64b_bad, ell64b, sizeof(ell64a_bad));
testrand_flip(ell64b_bad, sizeof(ell64b_bad));
ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a, ell64b_bad, sec32a, 0, hash_function, data);
CHECK(ret); /* Mismatching encodings don't get detected by secp256k1_ellswift_xdh. */
CHECK(secp256k1_memcmp_var(share32_bad, share32a, 32) != 0);
/* For B (using a bad public key for A): */
memcpy(ell64a_bad, ell64a, sizeof(ell64a_bad));
testrand_flip(ell64a_bad, sizeof(ell64a_bad));
ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a_bad, ell64b, sec32b, 1, hash_function, data);
CHECK(ret);
CHECK(secp256k1_memcmp_var(share32_bad, share32b, 32) != 0);
/* Verify that the shared secret doesn't match if the private key is incorrect. */
/* For A: */
memcpy(sec32a_bad, sec32a, sizeof(sec32a_bad));
testrand_flip(sec32a_bad, sizeof(sec32a_bad));
ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a, ell64b, sec32a_bad, 0, hash_function, data);
CHECK(!ret || secp256k1_memcmp_var(share32_bad, share32a, 32) != 0);
/* For B: */
memcpy(sec32b_bad, sec32b, sizeof(sec32b_bad));
testrand_flip(sec32b_bad, sizeof(sec32b_bad));
ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a, ell64b, sec32b_bad, 1, hash_function, data);
CHECK(!ret || secp256k1_memcmp_var(share32_bad, share32b, 32) != 0);
if (hash_function != ellswift_xdh_hash_x32) {
/* Verify that the shared secret doesn't match when a different encoding of the same public key is used. */
/* For A (changing B's public key): */
memcpy(auxrnd32b_bad, auxrnd32b, sizeof(auxrnd32b_bad));
testrand_flip(auxrnd32b_bad, sizeof(auxrnd32b_bad));
ret = secp256k1_ellswift_create(CTX, ell64b_bad, sec32b, auxrnd32b_bad);
CHECK(ret);
ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a, ell64b_bad, sec32a, 0, hash_function, data);
CHECK(ret);
CHECK(secp256k1_memcmp_var(share32_bad, share32a, 32) != 0);
/* For B (changing A's public key): */
memcpy(auxrnd32a_bad, auxrnd32a, sizeof(auxrnd32a_bad));
testrand_flip(auxrnd32a_bad, sizeof(auxrnd32a_bad));
ret = secp256k1_ellswift_create(CTX, ell64a_bad, sec32a, auxrnd32a_bad);
CHECK(ret);
ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a_bad, ell64b, sec32b, 1, hash_function, data);
CHECK(ret);
CHECK(secp256k1_memcmp_var(share32_bad, share32b, 32) != 0);
/* Verify that swapping sides changes the shared secret. */
/* For A (claiming to be B): */
ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a, ell64b, sec32a, 1, hash_function, data);
CHECK(ret);
CHECK(secp256k1_memcmp_var(share32_bad, share32a, 32) != 0);
/* For B (claiming to be A): */
ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a, ell64b, sec32b, 0, hash_function, data);
CHECK(ret);
CHECK(secp256k1_memcmp_var(share32_bad, share32b, 32) != 0);
}
}
}
DEFINE_SHA256_TRANSFORM_PROBE(sha256_ellswift_xdh)
void ellswift_xdh_ctx_sha256_tests(void) {
/* Check ctx-provided SHA256 compression override takes effect */
secp256k1_context *ctx = secp256k1_context_clone(CTX);
unsigned char out_default[65], out_custom[65];
const unsigned char skA[32] = {1}, skB[32] = {2};
unsigned char keyA[64], keyB[64], data[64] = {0};
secp256k1_ellswift_xdh_hash_function hash_fn;
int i;
CHECK(secp256k1_ellswift_create(ctx, keyA, skA, NULL));
CHECK(secp256k1_ellswift_create(ctx, keyB, skB, NULL));
for (i = 0; i < 2; i++) {
if (i == 0) {
hash_fn = secp256k1_ellswift_xdh_hash_function_bip324;
} else {
hash_fn = secp256k1_ellswift_xdh_hash_function_prefix;
}
/* Default behavior. No ctx-provided SHA256 compression */
CHECK(secp256k1_ellswift_xdh(ctx, out_default, keyA, keyB, skA, 0, hash_fn, data));
CHECK(!sha256_ellswift_xdh_called);
/* Override SHA256 compression directly, bypassing the ctx setter sanity checks */
ctx->hash_ctx.fn_sha256_compression = sha256_ellswift_xdh;
CHECK(secp256k1_ellswift_xdh(ctx, out_custom, keyA, keyB, skA, 0, hash_fn, data));
CHECK(sha256_ellswift_xdh_called);
/* Outputs must differ if custom compression was used */
CHECK(secp256k1_memcmp_var(out_default, out_custom, 32) != 0);
/* Restore defaults */
sha256_ellswift_xdh_called = 0;
secp256k1_context_set_sha256_compression(ctx, NULL);
}
secp256k1_context_destroy(ctx);
}
/* Test hash initializers */
void ellswift_hash_init_tests(void) {
secp256k1_sha256 sha_optimized;
const secp256k1_hash_ctx *hash_ctx = secp256k1_get_hash_context(CTX);
/* "secp256k1_ellswift_encode" */
static const unsigned char encode_tag[] = {'s', 'e', 'c', 'p', '2', '5', '6', 'k', '1', '_', 'e', 'l', 'l', 's', 'w', 'i', 'f', 't', '_', 'e', 'n', 'c', 'o', 'd', 'e'};
/* "secp256k1_ellswift_create" */
static const unsigned char create_tag[] = {'s', 'e', 'c', 'p', '2', '5', '6', 'k', '1', '_', 'e', 'l', 'l', 's', 'w', 'i', 'f', 't', '_', 'c', 'r', 'e', 'a', 't', 'e'};
/* "bip324_ellswift_xonly_ecdh" */
static const unsigned char bip324_tag[] = {'b', 'i', 'p', '3', '2', '4', '_', 'e', 'l', 'l', 's', 'w', 'i', 'f', 't', '_', 'x', 'o', 'n', 'l', 'y', '_', 'e', 'c', 'd', 'h'};
/* Check that hash initialized by
* secp256k1_ellswift_sha256_init_encode has the expected
* state. */
secp256k1_ellswift_sha256_init_encode(&sha_optimized);
test_sha256_tag_midstate(hash_ctx, &sha_optimized, encode_tag, sizeof(encode_tag));
/* Check that hash initialized by
* secp256k1_ellswift_sha256_init_create has the expected
* state. */
secp256k1_ellswift_sha256_init_create(&sha_optimized);
test_sha256_tag_midstate(hash_ctx, &sha_optimized, create_tag, sizeof(create_tag));
/* Check that hash initialized by
* secp256k1_ellswift_sha256_init_bip324 has the expected
* state. */
secp256k1_ellswift_sha256_init_bip324(&sha_optimized);
test_sha256_tag_midstate(hash_ctx, &sha_optimized, bip324_tag, sizeof(bip324_tag));
}
void ellswift_xdh_bad_scalar_tests(void) {
unsigned char s_zero[32] = { 0 };
unsigned char s_overflow_minus1[32] = { 0 };
unsigned char s_overflow_plus1[32] = { 0 };
unsigned char s_good[32] = { 0 };
unsigned char ell_a64[64], ell_b64[64];
unsigned char output[32];
secp256k1_scalar rand_scalar;
testutil_random_scalar_order(&rand_scalar);
secp256k1_scalar_get_b32(s_good, &rand_scalar);
CHECK(secp256k1_ellswift_create(CTX, ell_a64, s_good, NULL) == 1);
testrand256_test(ell_b64);
testrand256_test(ell_b64 + 32);
memcpy(s_overflow_minus1, secp256k1_group_order_bytes, 32);
s_overflow_minus1[31] -= 1;
memcpy(s_overflow_plus1, secp256k1_group_order_bytes, 32);
s_overflow_plus1[31] += 1;
CHECK(secp256k1_ellswift_xdh(CTX, output, ell_a64, ell_b64, s_zero, 0, &ellswift_xdh_hash_x32, NULL) == 0);
CHECK(secp256k1_ellswift_xdh(CTX, output, ell_a64, ell_b64, secp256k1_group_order_bytes, 0, &ellswift_xdh_hash_x32, NULL) == 0);
CHECK(secp256k1_ellswift_xdh(CTX, output, ell_a64, ell_b64, s_overflow_plus1, 0, &ellswift_xdh_hash_x32, NULL) == 0);
CHECK(secp256k1_ellswift_xdh(CTX, output, ell_a64, ell_b64, s_overflow_minus1, 0, &ellswift_xdh_hash_x32, NULL) == 1);
}
/* --- Test registry --- */
static const struct tf_test_entry tests_ellswift[] = {
CASE1(ellswift_encoding_test_vectors_tests),
CASE1(ellswift_decoding_test_vectors_tests),
CASE1(ellswift_xdh_test_vectors_tests),
CASE1(ellswift_encode_decode_roundtrip_tests),
CASE1(ellswift_create_tests),
CASE1(ellswift_compute_shared_secret_tests),
CASE1(ellswift_xdh_correctness_tests),
CASE1(ellswift_hash_init_tests),
CASE1(ellswift_xdh_bad_scalar_tests),
CASE1(ellswift_xdh_ctx_sha256_tests),
};
#endif

Some files were not shown because too many files have changed in this diff Show More