diff --git a/Secp256k1.xs b/Secp256k1.xs index d696549..0f26a26 100644 --- a/Secp256k1.xs +++ b/Secp256k1.xs @@ -370,6 +370,48 @@ _verify_privkey(self, privkey) OUTPUT: RETVAL +SV* +_privkey_negate(self, privkey) + SV *self + SV *privkey + CODE: + secp256k1_perl *ctx = ctx_from_sv(self); + size_t seckey_size; + unsigned char *seckey_str = (unsigned char*) SvPVbyte(privkey, seckey_size); + + if (seckey_size != CURVE_SIZE) { + croak("negating a privkey requires a 32-byte secret key"); + } + + unsigned char new_seckey[CURVE_SIZE]; + for (int i = 0; i < CURVE_SIZE; ++i) { + new_seckey[i] = seckey_str[i]; + } + + int result = secp256k1_ec_seckey_negate(ctx->ctx, new_seckey); + + if (!result) { + croak("resulting negated privkey is not valid"); + } + + RETVAL = newSVpv((char*) new_seckey, CURVE_SIZE); + + /* Clean up the secret, since we copied it to the stack */ + for (int i = 0; i < CURVE_SIZE; ++i) { + new_seckey[i] = '\0'; + } + OUTPUT: + RETVAL + +SV* +_pubkey_negate(self) + SV *self + CODE: + secp256k1_perl *ctx = ctx_from_sv(self); + + /* NOTE: result is always 1 */ + int result = secp256k1_ec_pubkey_negate(ctx->ctx, ctx->pubkey); + void DESTROY(self) SV *self diff --git a/lib/Bitcoin/Secp256k1.pm b/lib/Bitcoin/Secp256k1.pm index 18ca1ba..658a3a5 100644 --- a/lib/Bitcoin/Secp256k1.pm +++ b/lib/Bitcoin/Secp256k1.pm @@ -113,6 +113,24 @@ sub verify_digest return $self->_verify($digest); } +sub negate_public_key +{ + my ($self, $public_key) = @_; + + $self->_pubkey($public_key); + $self->_pubkey_negate; + + return $self->_pubkey; +} + +sub negate_private_key +{ + my ($self, $private_key) = @_; + + $private_key = $self->_privkey_negate($private_key); + return $private_key; +} + 1; __END__ @@ -251,6 +269,18 @@ accept or reject malleable signatures explicitly. Same as L, but it does not perform double SHA256 on its input. Because of that, C<$message_digest> must be a bytestring of length C<32>. +=head3 negate_private_key + + $negated_private_key = $secp256k1->negated_private_key($private_key) + +Negates a private key and returns it. + +=head3 negate_public_key + + $negated_public_key = $secp256k1->negate_public_key($public_key) + +Negates a public key and returns it. + =head1 IMPLEMENTATION The module consists of two layers: diff --git a/t/api.t b/t/api.t index 3aba54a..d081da1 100644 --- a/t/api.t +++ b/t/api.t @@ -54,5 +54,13 @@ subtest 'should sign and verify a digest' => sub { }, 1, 'unnormalized signature warning ok'; }; +subtest 'should negate' => sub { + my $negated_pubkey = pack 'H*', '035476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357'; + my $negated_privkey = pack 'H*', '9e63ccafda380bfed1aa93d5a74daf9089f68bcb5b9ab6dd1cbb61009daf4288'; + + is $secp->negate_public_key($t{pubkey}), $negated_pubkey, 'negated pubkey ok'; + is $secp->negate_private_key($t{privkey}), $negated_privkey, 'negated privkey ok'; +}; + done_testing;