Skip to content

Commit

Permalink
Implement negation of keys
Browse files Browse the repository at this point in the history
  • Loading branch information
bbrtj committed Sep 17, 2024
1 parent b830afc commit 39d4a8d
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 0 deletions.
42 changes: 42 additions & 0 deletions Secp256k1.xs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
30 changes: 30 additions & 0 deletions lib/Bitcoin/Secp256k1.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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__
Expand Down Expand Up @@ -251,6 +269,18 @@ accept or reject malleable signatures explicitly.
Same as L</verify_message>, 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:
Expand Down
8 changes: 8 additions & 0 deletions t/api.t
Original file line number Diff line number Diff line change
Expand Up @@ -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;

0 comments on commit 39d4a8d

Please sign in to comment.