Skip to content

Commit b79dcf4

Browse files
committed
feat: add pointNegate() (see issue #61)
1 parent f3e7916 commit b79dcf4

File tree

5 files changed

+126
-0
lines changed

5 files changed

+126
-0
lines changed

src/lib.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,20 @@ pub extern "C" fn private_sub() -> i32 {
418418
}
419419
}
420420

421+
#[allow(clippy::missing_panics_doc)]
422+
#[no_mangle]
423+
#[export_name = "privateNegate"]
424+
pub extern "C" fn private_key_negate() -> i32 {
425+
unsafe {
426+
if secp256k1_ec_seckey_negate(secp256k1_context_no_precomp, PRIVATE_INPUT.as_mut_ptr()) == 1
427+
{
428+
1
429+
} else {
430+
0
431+
}
432+
}
433+
}
434+
421435
#[allow(clippy::missing_panics_doc)]
422436
#[no_mangle]
423437
pub extern "C" fn sign(extra_data: i32) {

src_ts/index.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,19 @@ export function privateSub(
244244
}
245245
}
246246

247+
export function privateNegate(d: Uint8Array): Uint8Array | null {
248+
validate.validatePrivate(d);
249+
250+
try {
251+
PRIVATE_KEY_INPUT.set(d);
252+
return wasm.privateNegate() === 1
253+
? PRIVATE_KEY_INPUT.slice(0, validate.PRIVATE_KEY_SIZE)
254+
: null;
255+
} finally {
256+
PRIVATE_KEY_INPUT.fill(0);
257+
}
258+
}
259+
247260
export interface XOnlyPointAddTweakResult {
248261
parity: 1 | 0;
249262
xOnlyPubkey: Uint8Array;

src_ts/wasm_loader.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ interface Secp256k1WASM {
4848
pointMultiply: (p: number, outputlen: number) => number;
4949
privateAdd: () => number;
5050
privateSub: () => number;
51+
privateNegate: () => number;
5152
sign: (e: number) => void;
5253
signRecoverable: (e: number) => 0 | 1 | 2 | 3;
5354
signSchnorr: (e: number) => void;

tests/fixtures/points.json

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10207,6 +10207,78 @@
1020710207
"d": "0000000000000000000000000000000000000000000000000000000000000001",
1020810208
"expected": "02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13"
1020910209
}
10210+
],
10211+
"pointNegate": [
10212+
{
10213+
"d": "0000000000000000000000000000000000000000000000000000000000000001",
10214+
"expected": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
10215+
"description": "== 1"
10216+
},
10217+
{
10218+
"d": "0000000000000000000000000000000000000000000000000000000000000002",
10219+
"expected": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413f",
10220+
"description": "== 2"
10221+
},
10222+
{
10223+
"d": "0000000000000000000000000000000000000000000000000000000000000003",
10224+
"expected": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e",
10225+
"description": "== 3"
10226+
},
10227+
{
10228+
"d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
10229+
"expected": "0000000000000000000000000000000000000000000000000000000000000001",
10230+
"description": "== -1"
10231+
},
10232+
{
10233+
"d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413f",
10234+
"expected": "0000000000000000000000000000000000000000000000000000000000000002",
10235+
"description": "== -2"
10236+
},
10237+
{
10238+
"d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e",
10239+
"expected": "0000000000000000000000000000000000000000000000000000000000000003",
10240+
"description": "== -3"
10241+
},
10242+
{
10243+
"d": "b1121e4088a66a28f5b6b0f5844943ecd9f610196d7bb83b25214b60452c09af",
10244+
"expected": "4eede1bf775995d70a494f0a7bb6bc11e0b8cccd41cce8009ab1132c8b0a3792"
10245+
},
10246+
{
10247+
"d": "0705e4b49ea25478d60ba7287cf2cc020c074dd97d478c7f84a3cfbab8c376e6",
10248+
"expected": "f8fa1b4b615dab8729f458d7830d33fcaea78f0d320113bc3b2e8ed21772ca5b"
10249+
},
10250+
{
10251+
"d": "bd66074dae0276b29dd5d1136f53293e68eac94ceb82a2d2266411b22ec0f59c",
10252+
"expected": "4299f8b251fd894d622a2eec90acd6c051c41399c3c5fd69996e4cdaa1754ba5"
10253+
},
10254+
{
10255+
"d": "b5aa6da19452b8c9bb92b784fb6d47ab74bb066c2a879733b19ec8b1135c6a0b",
10256+
"expected": "4a55925e6bad4736446d487b0492b85345f3d67a84c109080e3395dbbcd9d736"
10257+
},
10258+
{
10259+
"d": "dc8c84fef13612bc12677f9d7300fce12fb2ce47340e28d9e478d5dabec4e813",
10260+
"expected": "23737b010ec9ed43ed9880628cff031d8afc0e9f7b3a7761db5988b21171592e"
10261+
},
10262+
{
10263+
"d": "348c8f3e7b11be8629ecaa28c8ef9fa6ee2d400cece1d49ea30e3b5653f01bf5",
10264+
"expected": "cb7370c184ee4179d61355d737106057cc819cd9c266cb9d1cc423367c46254c"
10265+
},
10266+
{
10267+
"d": "c29d155bad7e29720784de6783c4c7c7c5135597b390b256d19aa8cb9e493cc2",
10268+
"expected": "3d62eaa45281d68df87b21987c3b3836f59b874efbb7ede4ee37b5c131ed047f"
10269+
},
10270+
{
10271+
"d": "ebdddef7704d4ef7322074d84fd087ce73eafac8d3170e8a122aa9cf0486e42f",
10272+
"expected": "142221088fb2b108cddf8b27b02f783046c3e21ddc3191b1ada7b4bdcbaf5d12"
10273+
},
10274+
{
10275+
"d": "4f9694abc84c0d804bbff0f0609ee078a1284772b26efabf25c25ccf85dc272c",
10276+
"expected": "b0696b5437b3f27fb4400f0f9f611f8619869573fcd9a57c9a1001bd4a5a1a15"
10277+
},
10278+
{
10279+
"d": "c20f4427017ea5f9f7fedcf5a9d9f9f7e37933769efcba600e3494f010e00048",
10280+
"expected": "3df0bbd8fe815a060801230a56260606d735a970104be5dbb19dc99cbf5640f9"
10281+
}
1021010282
]
1021110283
},
1021210284
"invalid": {

tests/points.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,4 +213,30 @@ export default function (secp256k1) {
213213

214214
t.end();
215215
});
216+
217+
test("pointNegate", (t) => {
218+
for (const f of fpoints.valid.pointNegate) {
219+
const d = fromHex(f.d);
220+
const expected = fromHex(f.expected);
221+
let description = `-${f.d} = ${f.expected}`;
222+
if (f.description) description += ` (${f.description})`;
223+
t.same(secp256k1.privateNegate(d), expected, description);
224+
225+
t.equal(secp256k1.privateAdd(d, expected), null, description);
226+
}
227+
228+
// using the same data as point from scalar
229+
for (const f of fpoints.invalid.pointFromScalar) {
230+
const d = fromHex(f.d);
231+
t.throws(
232+
() => {
233+
secp256k1.privateNegate(d);
234+
},
235+
new RegExp(f.exception),
236+
`${f.description} throws ${f.exception}`
237+
);
238+
}
239+
240+
t.end();
241+
});
216242
}

0 commit comments

Comments
 (0)