@qntx/nostr-native 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NostrNative.podspec +1 -1
- package/android/CMakeLists.txt +1 -1
- package/cpp/HybridNostrNative.cpp +16 -0
- package/cpp/nostr_core.c +29 -0
- package/cpp/nostr_core.h +7 -0
- package/cpp/nostr_native.c +9 -0
- package/cpp/nostr_native.h +17 -0
- package/dist/index.d.mts +10 -8
- package/dist/index.mjs +9 -5
- package/dist/index.mjs.map +1 -1
- package/nitrogen/generated/shared/c++/HybridNostrNativeSpec.cpp +1 -0
- package/nitrogen/generated/shared/c++/HybridNostrNativeSpec.hpp +1 -0
- package/package.json +5 -5
- package/vendor/secp256k1/src/modules/ecdh/main_impl.h +79 -0
package/NostrNative.podspec
CHANGED
|
@@ -25,7 +25,7 @@ Pod::Spec.new do |s|
|
|
|
25
25
|
]
|
|
26
26
|
|
|
27
27
|
s.pod_target_xcconfig = {
|
|
28
|
-
"GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) ENABLE_MODULE_EXTRAKEYS=1 ENABLE_MODULE_SCHNORRSIG=1",
|
|
28
|
+
"GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) ENABLE_MODULE_ECDH=1 ENABLE_MODULE_EXTRAKEYS=1 ENABLE_MODULE_SCHNORRSIG=1",
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
load 'nitrogen/generated/ios/NostrNative+autolinking.rb'
|
package/android/CMakeLists.txt
CHANGED
|
@@ -7,7 +7,7 @@ set(CMAKE_CXX_STANDARD 20)
|
|
|
7
7
|
|
|
8
8
|
# Vendored libsecp256k1 modules needed by cpp/nostr_core.c (its single-TU include of
|
|
9
9
|
# vendor/secp256k1/src/secp256k1.c reads these defines).
|
|
10
|
-
add_definitions(-DENABLE_MODULE_EXTRAKEYS -DENABLE_MODULE_SCHNORRSIG)
|
|
10
|
+
add_definitions(-DENABLE_MODULE_ECDH -DENABLE_MODULE_EXTRAKEYS -DENABLE_MODULE_SCHNORRSIG)
|
|
11
11
|
|
|
12
12
|
# C glue + C++ hybrid. nostr_core.c itself includes the vendored secp256k1.c — the only
|
|
13
13
|
# separately-compiled vendor sources are the two precomputed tables below.
|
|
@@ -29,6 +29,22 @@ std::shared_ptr<ArrayBuffer> HybridNostrNative::publicKey(
|
|
|
29
29
|
return out;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
std::shared_ptr<ArrayBuffer> HybridNostrNative::ecdhX(
|
|
33
|
+
const std::shared_ptr<ArrayBuffer>& secretKey,
|
|
34
|
+
const std::shared_ptr<ArrayBuffer>& publicKeyX) {
|
|
35
|
+
if (secretKey->size() != 32) {
|
|
36
|
+
throw std::invalid_argument("secretKey must be 32 bytes");
|
|
37
|
+
}
|
|
38
|
+
if (publicKeyX->size() != 32) {
|
|
39
|
+
throw std::invalid_argument("publicKeyX must be 32 bytes");
|
|
40
|
+
}
|
|
41
|
+
auto out = ArrayBuffer::allocate(32);
|
|
42
|
+
if (!nostr_native_ecdh_x(secretKey->data(), publicKeyX->data(), out->data())) {
|
|
43
|
+
throw std::invalid_argument("invalid ECDH input");
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
|
|
32
48
|
std::shared_ptr<ArrayBuffer> HybridNostrNative::sign(
|
|
33
49
|
const std::shared_ptr<ArrayBuffer>& id,
|
|
34
50
|
const std::shared_ptr<ArrayBuffer>& secretKey,
|
package/cpp/nostr_core.c
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
#include "../vendor/secp256k1/src/secp256k1.c"
|
|
17
17
|
#include "../vendor/secp256k1/include/secp256k1_extrakeys.h"
|
|
18
|
+
#include "../vendor/secp256k1/include/secp256k1_ecdh.h"
|
|
18
19
|
#include "../vendor/secp256k1/include/secp256k1_schnorrsig.h"
|
|
19
20
|
|
|
20
21
|
#include "nostr_core.h"
|
|
@@ -96,6 +97,34 @@ int nostr_core_public_key(const nostr_native_secp256k1_context *ctx,
|
|
|
96
97
|
return ok ? NOSTR_CORE_OK : NOSTR_CORE_INVALID_INPUT;
|
|
97
98
|
}
|
|
98
99
|
|
|
100
|
+
/* ECDH hash function: writes the shared point's x coordinate, nothing else. */
|
|
101
|
+
static int nostr_core_ecdh_xonly_hash(unsigned char *output, const unsigned char *x,
|
|
102
|
+
const unsigned char *y, void *data) {
|
|
103
|
+
(void)y;
|
|
104
|
+
(void)data;
|
|
105
|
+
memcpy(output, x, 32);
|
|
106
|
+
return 1;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
int nostr_core_ecdh_x(const nostr_native_secp256k1_context *ctx, const unsigned char seckey[32],
|
|
110
|
+
const unsigned char pubkey_x[32], unsigned char out32[32]) {
|
|
111
|
+
unsigned char compressed[33];
|
|
112
|
+
nostr_native_secp256k1_pubkey point;
|
|
113
|
+
int ok;
|
|
114
|
+
|
|
115
|
+
if (ctx == NULL || seckey == NULL || pubkey_x == NULL || out32 == NULL) {
|
|
116
|
+
return NOSTR_CORE_INVALID_INPUT;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
compressed[0] = 0x02;
|
|
120
|
+
memcpy(compressed + 1, pubkey_x, 32);
|
|
121
|
+
ok = nostr_native_secp256k1_ec_pubkey_parse(ctx, &point, compressed, 33) &&
|
|
122
|
+
nostr_native_secp256k1_ecdh(ctx, out32, &point, seckey, nostr_core_ecdh_xonly_hash, NULL);
|
|
123
|
+
nostr_native_secp256k1_memclear_explicit(compressed, sizeof(compressed));
|
|
124
|
+
nostr_native_secp256k1_memclear_explicit(&point, sizeof(point));
|
|
125
|
+
return ok ? NOSTR_CORE_OK : NOSTR_CORE_INVALID_INPUT;
|
|
126
|
+
}
|
|
127
|
+
|
|
99
128
|
#ifdef NOSTR_NATIVE_TEST
|
|
100
129
|
|
|
101
130
|
nostr_native_secp256k1_context *nostr_native_test_context(void) {
|
package/cpp/nostr_core.h
CHANGED
|
@@ -42,6 +42,13 @@ int nostr_core_sign(const nostr_native_secp256k1_context *ctx, const unsigned ch
|
|
|
42
42
|
int nostr_core_public_key(const nostr_native_secp256k1_context *ctx,
|
|
43
43
|
const unsigned char seckey[32], unsigned char out_xonly[32]);
|
|
44
44
|
|
|
45
|
+
/* Computes the ECDH shared point's x coordinate: `secp256k1_ecdh` with a hash function that
|
|
46
|
+
* copies the x coordinate only — the shared secret both NIP-04 (AES key) and NIP-44 (HKDF
|
|
47
|
+
* input) derive from. `pubkey_x` is lifted to an even-Y point as `0x02‖x`. Intermediates are
|
|
48
|
+
* wiped on every path. out32 gets the 32-byte x on NOSTR_CORE_OK. */
|
|
49
|
+
int nostr_core_ecdh_x(const nostr_native_secp256k1_context *ctx, const unsigned char seckey[32],
|
|
50
|
+
const unsigned char pubkey_x[32], unsigned char out32[32]);
|
|
51
|
+
|
|
45
52
|
/* Secure wipe for platform layers that must clear caller-provided regions. */
|
|
46
53
|
void nostr_core_wipe(void *ptr, size_t len);
|
|
47
54
|
|
package/cpp/nostr_native.c
CHANGED
|
@@ -36,6 +36,10 @@ static nostr_native_secp256k1_context *module_context(void) {
|
|
|
36
36
|
return module_ctx;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
int nostr_native_abi_version(void) {
|
|
40
|
+
return NOSTR_NATIVE_ABI_VERSION;
|
|
41
|
+
}
|
|
42
|
+
|
|
39
43
|
int nostr_native_verify_serialized(const unsigned char *serialized, size_t len,
|
|
40
44
|
const unsigned char id[32],
|
|
41
45
|
const unsigned char pubkey[32],
|
|
@@ -48,6 +52,11 @@ int nostr_native_public_key(const unsigned char seckey[32], unsigned char out_xo
|
|
|
48
52
|
return nostr_core_public_key(module_context(), seckey, out_xonly) == NOSTR_CORE_OK;
|
|
49
53
|
}
|
|
50
54
|
|
|
55
|
+
int nostr_native_ecdh_x(const unsigned char seckey[32], const unsigned char pubkey_x[32],
|
|
56
|
+
unsigned char out32[32]) {
|
|
57
|
+
return nostr_core_ecdh_x(module_context(), seckey, pubkey_x, out32) == NOSTR_CORE_OK;
|
|
58
|
+
}
|
|
59
|
+
|
|
51
60
|
int nostr_native_sign(const unsigned char id[32], const unsigned char seckey[32],
|
|
52
61
|
const unsigned char aux_rand[32], unsigned char out_sig[64]) {
|
|
53
62
|
return nostr_core_sign(module_context(), id, seckey, aux_rand, out_sig) == NOSTR_CORE_OK;
|
package/cpp/nostr_native.h
CHANGED
|
@@ -7,6 +7,14 @@
|
|
|
7
7
|
extern "C" {
|
|
8
8
|
#endif
|
|
9
9
|
|
|
10
|
+
/* Stable public C interface version. Bump when the export surface changes; embedders can check
|
|
11
|
+
* nostr_native_abi_version() at load time. v1: verify_serialized/public_key/sign.
|
|
12
|
+
* v2: adds ecdh_x. */
|
|
13
|
+
#define NOSTR_NATIVE_ABI_VERSION 2
|
|
14
|
+
|
|
15
|
+
/* Returns NOSTR_NATIVE_ABI_VERSION. */
|
|
16
|
+
int nostr_native_abi_version(void);
|
|
17
|
+
|
|
10
18
|
/**
|
|
11
19
|
* Return 1 iff `sha256(serialized[0..len])` equals `id` and `sig` is a valid BIP-340 signature of
|
|
12
20
|
* `id` for the x-only `pubkey`. `id`/`pubkey` are 32 bytes and `sig` is 64 bytes; NULL pointers
|
|
@@ -30,6 +38,15 @@ int nostr_native_public_key(const unsigned char seckey[32], unsigned char out_xo
|
|
|
30
38
|
int nostr_native_sign(const unsigned char id[32], const unsigned char seckey[32],
|
|
31
39
|
const unsigned char aux_rand[32], unsigned char out_sig[64]);
|
|
32
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Write the 32-byte x coordinate of the ECDH shared point between `seckey` and `pubkey_x`
|
|
43
|
+
* (lifted even-Y) to `out32` — the shared secret NIP-04 and NIP-44 derive from. The caller must
|
|
44
|
+
* wipe `out32` and its own `seckey` copy when done. Returns 1 on success; 0 on NULL input, an
|
|
45
|
+
* invalid secret key, or a `pubkey_x` that does not lift to a curve point.
|
|
46
|
+
*/
|
|
47
|
+
int nostr_native_ecdh_x(const unsigned char seckey[32], const unsigned char pubkey_x[32],
|
|
48
|
+
unsigned char out32[32]);
|
|
49
|
+
|
|
33
50
|
#ifdef NOSTR_NATIVE_TEST
|
|
34
51
|
/* Test-only entry points (host suite only): expose the SHA-256 and Schnorr halves individually
|
|
35
52
|
* so BIP-340 vectors with arbitrary-length messages and raw SHA-256 vectors can run. */
|
package/dist/index.d.mts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { NostrBackend } from "@qntx/nostr/core";
|
|
2
2
|
//#region src/index.d.ts
|
|
3
3
|
/**
|
|
4
|
-
* `
|
|
5
|
-
* `@qntx/nostr/core`. The Nitro hybrid object is created once at module
|
|
4
|
+
* `NostrBackend["verifySerialized"]` backed by native libsecp256k1 — plug into
|
|
5
|
+
* `createEventVerifier` from `@qntx/nostr/core`. The Nitro hybrid object is created once at module
|
|
6
|
+
* load.
|
|
6
7
|
*/
|
|
7
|
-
export declare const verifySerialized:
|
|
8
|
+
export declare const verifySerialized: NostrBackend["verifySerialized"];
|
|
8
9
|
/**
|
|
9
|
-
* `
|
|
10
|
-
*
|
|
11
|
-
*
|
|
10
|
+
* `NostrBackend` (without `ecdhX`, which lands in T4 with the native `ecdh_x` export) backed by
|
|
11
|
+
* native libsecp256k1 — plug into `Keys.fromSecretKey(secretKey, signing)` or
|
|
12
|
+
* `Keys.generate(signing)` so signing runs on the same randomized context as verification. Throws
|
|
13
|
+
* on an invalid secret key or wrong-length input.
|
|
12
14
|
*/
|
|
13
|
-
export declare const signing:
|
|
15
|
+
export declare const signing: NostrBackend;
|
|
14
16
|
//#endregion
|
|
15
17
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -7,16 +7,20 @@ function asArrayBuffer(view) {
|
|
|
7
7
|
return new Uint8Array(view).buffer;
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
|
-
* `
|
|
11
|
-
* `@qntx/nostr/core`. The Nitro hybrid object is created once at module
|
|
10
|
+
* `NostrBackend["verifySerialized"]` backed by native libsecp256k1 — plug into
|
|
11
|
+
* `createEventVerifier` from `@qntx/nostr/core`. The Nitro hybrid object is created once at module
|
|
12
|
+
* load.
|
|
12
13
|
*/
|
|
13
14
|
const verifySerialized = (serializedUtf8, id, pubkey, sig) => nostrNative.verifySerialized(asArrayBuffer(serializedUtf8), asArrayBuffer(id), asArrayBuffer(pubkey), asArrayBuffer(sig));
|
|
14
15
|
/**
|
|
15
|
-
* `
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* `NostrBackend` (without `ecdhX`, which lands in T4 with the native `ecdh_x` export) backed by
|
|
17
|
+
* native libsecp256k1 — plug into `Keys.fromSecretKey(secretKey, signing)` or
|
|
18
|
+
* `Keys.generate(signing)` so signing runs on the same randomized context as verification. Throws
|
|
19
|
+
* on an invalid secret key or wrong-length input.
|
|
18
20
|
*/
|
|
19
21
|
const signing = {
|
|
22
|
+
ecdhX: (secretKey, publicKeyX) => new Uint8Array(nostrNative.ecdhX(asArrayBuffer(secretKey), asArrayBuffer(publicKeyX))),
|
|
23
|
+
verifySerialized,
|
|
20
24
|
publicKey: (secretKey) => new Uint8Array(nostrNative.publicKey(asArrayBuffer(secretKey))),
|
|
21
25
|
sign: (id, secretKey, auxRand) => new Uint8Array(nostrNative.sign(asArrayBuffer(id), asArrayBuffer(secretKey), asArrayBuffer(auxRand)))
|
|
22
26
|
};
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { NitroModules } from \"react-native-nitro-modules\";\n\nimport type {
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { NitroModules } from \"react-native-nitro-modules\";\n\nimport type { NostrBackend } from \"@qntx/nostr/core\";\n\nimport type { NostrNative } from \"./specs/nostr-native.nitro.ts\";\n\nconst nostrNative = NitroModules.createHybridObject<NostrNative>(\"NostrNative\");\n\nfunction asArrayBuffer(view: Uint8Array): ArrayBuffer {\n const { buffer, byteOffset, byteLength } = view;\n if (buffer instanceof ArrayBuffer && byteOffset === 0 && byteLength === buffer.byteLength) {\n return buffer;\n }\n return new Uint8Array(view).buffer;\n}\n\n/**\n * `NostrBackend[\"verifySerialized\"]` backed by native libsecp256k1 — plug into\n * `createEventVerifier` from `@qntx/nostr/core`. The Nitro hybrid object is created once at module\n * load.\n */\nexport const verifySerialized: NostrBackend[\"verifySerialized\"] = (\n serializedUtf8,\n id,\n pubkey,\n sig,\n) =>\n nostrNative.verifySerialized(\n asArrayBuffer(serializedUtf8),\n asArrayBuffer(id),\n asArrayBuffer(pubkey),\n asArrayBuffer(sig),\n );\n\n/**\n * `NostrBackend` (without `ecdhX`, which lands in T4 with the native `ecdh_x` export) backed by\n * native libsecp256k1 — plug into `Keys.fromSecretKey(secretKey, signing)` or\n * `Keys.generate(signing)` so signing runs on the same randomized context as verification. Throws\n * on an invalid secret key or wrong-length input.\n */\nexport const signing: NostrBackend = {\n ecdhX: (secretKey, publicKeyX) =>\n new Uint8Array(nostrNative.ecdhX(asArrayBuffer(secretKey), asArrayBuffer(publicKeyX))),\n verifySerialized,\n publicKey: (secretKey) => new Uint8Array(nostrNative.publicKey(asArrayBuffer(secretKey))),\n sign: (id, secretKey, auxRand) =>\n new Uint8Array(\n nostrNative.sign(asArrayBuffer(id), asArrayBuffer(secretKey), asArrayBuffer(auxRand)),\n ),\n};\n"],"mappings":";;AAMA,MAAM,cAAc,aAAa,mBAAgC,aAAa;AAE9E,SAAS,cAAc,MAA+B;CACpD,MAAM,EAAE,QAAQ,YAAY,eAAe;CAC3C,IAAI,kBAAkB,eAAe,eAAe,KAAK,eAAe,OAAO,YAC7E,OAAO;CAET,OAAO,IAAI,WAAW,IAAI,CAAC,CAAC;AAC9B;;;;;;AAOA,MAAa,oBACX,gBACA,IACA,QACA,QAEA,YAAY,iBACV,cAAc,cAAc,GAC5B,cAAc,EAAE,GAChB,cAAc,MAAM,GACpB,cAAc,GAAG,CACnB;;;;;;;AAQF,MAAa,UAAwB;CACnC,QAAQ,WAAW,eACjB,IAAI,WAAW,YAAY,MAAM,cAAc,SAAS,GAAG,cAAc,UAAU,CAAC,CAAC;CACvF;CACA,YAAY,cAAc,IAAI,WAAW,YAAY,UAAU,cAAc,SAAS,CAAC,CAAC;CACxF,OAAO,IAAI,WAAW,YACpB,IAAI,WACF,YAAY,KAAK,cAAc,EAAE,GAAG,cAAc,SAAS,GAAG,cAAc,OAAO,CAAC,CACtF;AACJ"}
|
|
@@ -17,6 +17,7 @@ namespace margelo::nitro::nostrnative {
|
|
|
17
17
|
prototype.registerHybridMethod("verifySerialized", &HybridNostrNativeSpec::verifySerialized);
|
|
18
18
|
prototype.registerHybridMethod("publicKey", &HybridNostrNativeSpec::publicKey);
|
|
19
19
|
prototype.registerHybridMethod("sign", &HybridNostrNativeSpec::sign);
|
|
20
|
+
prototype.registerHybridMethod("ecdhX", &HybridNostrNativeSpec::ecdhX);
|
|
20
21
|
});
|
|
21
22
|
}
|
|
22
23
|
|
|
@@ -51,6 +51,7 @@ namespace margelo::nitro::nostrnative {
|
|
|
51
51
|
virtual bool verifySerialized(const std::shared_ptr<ArrayBuffer>& serializedUtf8, const std::shared_ptr<ArrayBuffer>& id, const std::shared_ptr<ArrayBuffer>& pubkey, const std::shared_ptr<ArrayBuffer>& sig) = 0;
|
|
52
52
|
virtual std::shared_ptr<ArrayBuffer> publicKey(const std::shared_ptr<ArrayBuffer>& secretKey) = 0;
|
|
53
53
|
virtual std::shared_ptr<ArrayBuffer> sign(const std::shared_ptr<ArrayBuffer>& id, const std::shared_ptr<ArrayBuffer>& secretKey, const std::shared_ptr<ArrayBuffer>& auxRand) = 0;
|
|
54
|
+
virtual std::shared_ptr<ArrayBuffer> ecdhX(const std::shared_ptr<ArrayBuffer>& secretKey, const std::shared_ptr<ArrayBuffer>& publicKeyX) = 0;
|
|
54
55
|
|
|
55
56
|
protected:
|
|
56
57
|
// Hybrid Setup
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qntx/nostr-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "Native libsecp256k1 (Nitro) verification and signing backend for @qntx/nostr on React Native",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bip340",
|
|
@@ -61,17 +61,17 @@
|
|
|
61
61
|
"@arethetypeswrong/cli": "0.18.5",
|
|
62
62
|
"@noble/curves": "2.4.0",
|
|
63
63
|
"@noble/hashes": "2.4.0",
|
|
64
|
-
"@qntx/nostr": "0.
|
|
64
|
+
"@qntx/nostr": "0.15.0",
|
|
65
65
|
"@qntx/tsconfig": "1.0.1",
|
|
66
|
-
"@types/node": "26.6.
|
|
66
|
+
"@types/node": "26.6.5",
|
|
67
67
|
"nitrogen": "0.37.1",
|
|
68
68
|
"publint": "0.3.25",
|
|
69
69
|
"react-native-nitro-modules": "0.37.1",
|
|
70
70
|
"typescript": "7.0.2",
|
|
71
|
-
"vite-plus": "1.
|
|
71
|
+
"vite-plus": "1.2.0"
|
|
72
72
|
},
|
|
73
73
|
"peerDependencies": {
|
|
74
|
-
"@qntx/nostr": "^0.
|
|
74
|
+
"@qntx/nostr": "^0.15.0",
|
|
75
75
|
"react-native": "*",
|
|
76
76
|
"react-native-nitro-modules": "^0.37.1"
|
|
77
77
|
},
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* Copyright (c) 2015 Andrew Poelstra *
|
|
3
|
+
* Distributed under the MIT software license, see the accompanying *
|
|
4
|
+
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
|
|
5
|
+
***********************************************************************/
|
|
6
|
+
|
|
7
|
+
#ifndef SECP256K1_MODULE_ECDH_MAIN_H
|
|
8
|
+
#define SECP256K1_MODULE_ECDH_MAIN_H
|
|
9
|
+
|
|
10
|
+
#include "../../../include/secp256k1_ecdh.h"
|
|
11
|
+
#include "../../ecmult_const_impl.h"
|
|
12
|
+
|
|
13
|
+
static int ecdh_hash_function_sha256_impl(const nostr_native_secp256k1_hash_ctx *hash_ctx, unsigned char *output, const unsigned char *x32, const unsigned char *y32, void *data) {
|
|
14
|
+
unsigned char version = (y32[31] & 0x01) | 0x02;
|
|
15
|
+
nostr_native_secp256k1_sha256 sha;
|
|
16
|
+
(void)data;
|
|
17
|
+
|
|
18
|
+
nostr_native_secp256k1_sha256_initialize(&sha);
|
|
19
|
+
nostr_native_secp256k1_sha256_write(hash_ctx, &sha, &version, 1);
|
|
20
|
+
nostr_native_secp256k1_sha256_write(hash_ctx, &sha, x32, 32);
|
|
21
|
+
nostr_native_secp256k1_sha256_finalize(hash_ctx, &sha, output);
|
|
22
|
+
nostr_native_secp256k1_sha256_clear(&sha);
|
|
23
|
+
|
|
24
|
+
return 1;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
static int ecdh_hash_function_sha256(unsigned char *output, const unsigned char *x32, const unsigned char *y32, void *data) {
|
|
28
|
+
return ecdh_hash_function_sha256_impl(nostr_native_secp256k1_get_hash_context(nostr_native_secp256k1_context_static), output, x32, y32, data);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const nostr_native_secp256k1_ecdh_hash_function nostr_native_secp256k1_ecdh_hash_function_sha256 = ecdh_hash_function_sha256;
|
|
32
|
+
const nostr_native_secp256k1_ecdh_hash_function nostr_native_secp256k1_ecdh_hash_function_default = ecdh_hash_function_sha256;
|
|
33
|
+
|
|
34
|
+
int nostr_native_secp256k1_ecdh(const nostr_native_secp256k1_context* ctx, unsigned char *output, const nostr_native_secp256k1_pubkey *point, const unsigned char *scalar, nostr_native_secp256k1_ecdh_hash_function hashfp, void *data) {
|
|
35
|
+
int ret = 0;
|
|
36
|
+
int overflow = 0;
|
|
37
|
+
nostr_native_secp256k1_gej res;
|
|
38
|
+
nostr_native_secp256k1_ge pt;
|
|
39
|
+
nostr_native_secp256k1_scalar s;
|
|
40
|
+
unsigned char x[32];
|
|
41
|
+
unsigned char y[32];
|
|
42
|
+
|
|
43
|
+
VERIFY_CHECK(ctx != NULL);
|
|
44
|
+
ARG_CHECK(output != NULL);
|
|
45
|
+
ARG_CHECK(point != NULL);
|
|
46
|
+
ARG_CHECK(scalar != NULL);
|
|
47
|
+
|
|
48
|
+
nostr_native_secp256k1_pubkey_load(ctx, &pt, point);
|
|
49
|
+
nostr_native_secp256k1_scalar_set_b32(&s, scalar, &overflow);
|
|
50
|
+
|
|
51
|
+
overflow |= nostr_native_secp256k1_scalar_is_zero(&s);
|
|
52
|
+
nostr_native_secp256k1_scalar_cmov(&s, &nostr_native_secp256k1_scalar_one, overflow);
|
|
53
|
+
|
|
54
|
+
nostr_native_secp256k1_ecmult_const(&res, &pt, &s);
|
|
55
|
+
nostr_native_secp256k1_ge_set_gej(&pt, &res);
|
|
56
|
+
|
|
57
|
+
/* Compute a hash of the point */
|
|
58
|
+
nostr_native_secp256k1_fe_normalize(&pt.x);
|
|
59
|
+
nostr_native_secp256k1_fe_normalize(&pt.y);
|
|
60
|
+
nostr_native_secp256k1_fe_get_b32(x, &pt.x);
|
|
61
|
+
nostr_native_secp256k1_fe_get_b32(y, &pt.y);
|
|
62
|
+
|
|
63
|
+
if (hashfp == NULL || hashfp == nostr_native_secp256k1_ecdh_hash_function_sha256) {
|
|
64
|
+
/* Use ctx-aware function by default */
|
|
65
|
+
ret = ecdh_hash_function_sha256_impl(nostr_native_secp256k1_get_hash_context(ctx), output, x, y, data);
|
|
66
|
+
} else {
|
|
67
|
+
ret = hashfp(output, x, y, data);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
nostr_native_secp256k1_memclear_explicit(x, sizeof(x));
|
|
71
|
+
nostr_native_secp256k1_memclear_explicit(y, sizeof(y));
|
|
72
|
+
nostr_native_secp256k1_scalar_clear(&s);
|
|
73
|
+
nostr_native_secp256k1_ge_clear(&pt);
|
|
74
|
+
nostr_native_secp256k1_gej_clear(&res);
|
|
75
|
+
|
|
76
|
+
return !!ret & !overflow;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
#endif /* SECP256K1_MODULE_ECDH_MAIN_H */
|