view release on metacpan or search on metacpan
Secp256k1.xs view on Meta::CPAN
}
# Multiplies private key by a tweak
SV*
_privkey_mul(self, privkey, tweak)
SV *self
SV *privkey
SV *tweak
CODE:
secp256k1_perl *ctx = ctx_from_sv(self);
unsigned char *seckey_str = size_bytestr_from_sv(privkey, CURVE_SIZE, "private_key");
unsigned char *tweak_str = size_bytestr_from_sv(tweak, CURVE_SIZE, "tweak");
unsigned char new_seckey[CURVE_SIZE];
copy_bytestr(new_seckey, seckey_str, CURVE_SIZE);
int result = secp256k1_ec_seckey_tweak_mul(
ctx->ctx,
new_seckey,
tweak_str
);
lib/Bitcoin/Secp256k1.pm view on Meta::CPAN
{
my $args_str = join ', ', @_;
my ($package) = caller;
my ($calling_sub) = (caller 1)[3];
$calling_sub =~ s/^${package}:://;
croak "bad or undefined arguments, usage: \$secp256k1->$calling_sub($args_str)";
}
sub verify_private_key
{
my ($self, $private_key) = @_;
(defined $private_key)
or _croak_usage(qw($private_key));
return $self->_verify_privkey($private_key);
}
sub verify_public_key
{
my ($self, $public_key) = @_;
(defined $public_key)
or _croak_usage(qw($public_key));
# scope for local $@
lib/Bitcoin/Secp256k1.pm view on Meta::CPAN
$self->_pubkey($public_key);
1;
};
return !!$success;
}
}
sub create_public_key
{
my ($self, $private_key) = @_;
(defined $private_key)
or _croak_usage(qw($private_key));
$self->_create_pubkey($private_key);
return $self->_pubkey;
}
sub normalize_signature
{
my ($self, $signature) = @_;
(defined $signature)
or _croak_usage(qw($signature));
lib/Bitcoin/Secp256k1.pm view on Meta::CPAN
$compressed //= !!1;
(defined $public_key)
or _croak_usage(qw($public_key [$compressed]));
return $self->_pubkey($public_key, $compressed);
}
sub sign_message
{
my ($self, $private_key, $message) = @_;
(defined $private_key and defined $message)
or _croak_usage(qw($private_key $message));
return $self->sign_digest($private_key, sha256(sha256($message)));
}
sub sign_message_schnorr
{
my ($self, $private_key, $message) = @_;
(defined $private_key and defined $message)
or _croak_usage(qw($private_key $message));
return $self->sign_digest_schnorr($private_key, sha256($message));
}
sub sign_message_recoverable
{
my ($self, $private_key, $message) = @_;
(defined $private_key and defined $message)
or _croak_usage(qw($private_key $message));
return $self->sign_digest_recoverable($private_key, sha256(sha256($message)));
}
sub sign_digest
{
my ($self, $private_key, $digest) = @_;
(defined $private_key and defined $digest)
or _croak_usage(qw($private_key $digest));
$self->_sign($private_key, $digest);
return $self->_signature;
}
sub sign_digest_schnorr
{
my ($self, $private_key, $digest) = @_;
(defined $private_key and defined $digest)
or _croak_usage(qw($private_key $digest));
$self->_sign_schnorr($private_key, $digest);
return $self->_signature_schnorr;
}
sub sign_digest_recoverable
{
my ($self, $private_key, $digest) = @_;
(defined $private_key and defined $digest)
or _croak_usage(qw($private_key $digest));
$self->_sign_recoverable($private_key, $digest);
return $self->_signature_recoverable;
}
sub recover_public_key_message
{
my ($self, $recoverable_signature, $message) = @_;
(defined $recoverable_signature and defined $message)
or _croak_usage(qw($signature $message));
lib/Bitcoin/Secp256k1.pm view on Meta::CPAN
(defined $public_key)
or _croak_usage(qw($public_key));
$self->_pubkey($public_key);
$self->_pubkey_negate;
return $self->_pubkey;
}
sub negate_private_key
{
my ($self, $private_key) = @_;
(defined $private_key)
or _croak_usage(qw($private_key));
return $self->_privkey_negate($private_key);
}
sub xonly_public_key
{
my ($self, $public_key) = @_;
(defined $public_key)
or _croak_usage(qw($public_key));
$self->_pubkey($public_key);
lib/Bitcoin/Secp256k1.pm view on Meta::CPAN
(defined $public_key and defined $tweak)
or _croak_usage(qw($public_key $tweak));
$self->_pubkey($public_key);
$self->_pubkey_add($tweak);
return $self->_pubkey;
}
sub add_private_key
{
my ($self, $private_key, $tweak) = @_;
(defined $private_key and defined $tweak)
or _croak_usage(qw($private_key $tweak));
return $self->_privkey_add($private_key, $tweak);
}
sub multiply_public_key
{
my ($self, $public_key, $tweak) = @_;
(defined $public_key and defined $tweak)
or _croak_usage(qw($public_key $tweak));
$self->_pubkey($public_key);
$self->_pubkey_mul($tweak);
return $self->_pubkey;
}
sub multiply_private_key
{
my ($self, $private_key, $tweak) = @_;
(defined $private_key and defined $tweak)
or _croak_usage(qw($private_key $tweak));
return $self->_privkey_mul($private_key, $tweak);
}
sub combine_public_keys
{
my ($self, @public_keys) = @_;
(@public_keys > 0)
or _croak_usage(qw($public_key [@more_public_keys]));
$self->_clear;
lib/Bitcoin/Secp256k1.pm view on Meta::CPAN
Bitcoin::Secp256k1 - Perl interface to libsecp256k1
=head1 SYNOPSIS
use Bitcoin::Secp256k1;
# first, create a context
my $secp256k1 = Bitcoin::Secp256k1->new;
# then, use it to perform ECC operations
my $public_key = $secp256k1->create_public_key($private_key);
my $signature = $secp256k1->sign_message($private_key, $message);
my $valid = $secp256k1->verify_message($public_key, $signature, $message);
# Schnorr signatures are implemented
my $schnorr_signature = $secp256k1->sign_message_schnorr($private_key, $message);
my $xonly_public_key = $secp256k1->xonly_public_key($public_key);
my $valid = $secp256k1->verify_message_schnorr($xonly_public_key, $schnorr_signature, $message);
# Recoverable signatures (used in Ethereum)
my $recoverable_signature = $secp256k1->sign_message_recoverable($private_key, $message);
my $recovered_pubkey = $secp256k1->recover_public_key_message($recoverable_signature, $message);
my $valid = $secp256k1->verify_message_recoverable($public_key, $recoverable_signature, $message);
=head1 DESCRIPTION
This module implements XS routines that allow accessing common elliptic curve
operations on secp256k1 curve using Perl code. It requires
L<libsecp256k1|https://github.com/bitcoin-core/secp256k1> to be installed on
the system, and will try to detect and install it automatically using
L<Alien::libsecp256k1>.
lib/Bitcoin/Secp256k1.pm view on Meta::CPAN
=head2 Methods
=head3 new
$secp256k1 = Bitcoin::Secp256k1->new()
Object constructor. All methods in this package require this object to work
properly. It accepts no arguments.
=head3 verify_private_key
$valid = $secp256k1->verify_private_key($private_key)
Checks whether bytestring C<$private_key> is a valid private key. Private key
is valid if its length is exactly C<32> and it is below curve order (when
interpreted as a big-endian integer).
Some methods in this module may die if their private key is not valid, but
chances of picking an invalid 32-byte private key at random are extremely slim.
=head3 verify_public_key
$valid = $secp256k1->verify_public_key($public_key)
Checks whether bytestring C<$public_key> is a valid public key. Some methods in
this module may die if their public key is not valid.
=head3 create_public_key
$public_key = $secp256k1->create_public_key($private_key)
Creates a public key from a bytestring C<$private_key> and returns a bytestring
C<$public_key>. C<$private_key> must have exact length of C<32>.
The public key is always returned in compressed form, use L</compress_public_key> to get uncompressed form.
=head3 normalize_signature
$signature = $secp256k1->normalize_signature($signature)
Performs signature normalization of C<$signature>, which is in DER encoding (a
bytestring). Returns the normalized signature. Will return the same signature
if it was already in a normalized form.
lib/Bitcoin/Secp256k1.pm view on Meta::CPAN
the key in compressed (default) form. If it is a false value, C<$public_key>
will be in uncompressed form. It accepts keys in both compressed and
uncompressed forms.
While both compressed and uncompressed keys will behave the same during
signature verification, they produce different Bitcoin addresses (because
address is a hashed public key).
=head3 sign_message
$signature = $secp256k1->sign_message($private_key, $message)
Signs C<$message>, which may be a bytestring of any length, with
C<$private_key>, which must be a bytestring of length C<32>. Returns
DER-encoded C<$signature> as a bytestring.
C<$message> is first hashed with double SHA256 (known an HASH256 in Bitcoin)
before passing it to signing algorithm (which expects length C<32> bytestrings).
This method always produces normalized, deterministic signatures suitable to
use inside a Bitcoin transaction.
=head3 sign_message_schnorr
$signature = $secp256k1->sign_message_schnorr($private_key, $message)
Signs C<$message>, which may be a bytestring of any length, with
C<$private_key>, which must be a bytestring of length C<32>. Returns
a Schnorr C<$signature> as a bytestring.
C<$message> is first hashed with SHA256 before passing it to signing algorithm.
This signature is not deterministic, since signing with Schnorr uses 32 bytes
of auxiliary randomness as an additional security measure. You can set a fixed
value to be used instead by setting package variable
C<$Bitcoin::Secp256k1::FORCED_SCHNORR_AUX_RAND> to any bytestring of length
C<32>.
=head3 sign_message_recoverable
$signature = $secp256k1->sign_message_recoverable($private_key, $message)
Signs C<$message>, which may be a bytestring of any length, with
C<$private_key>, which must be a bytestring of length C<32>. Returns
a hash reference containing the recoverable signature.
C<$message> is first hashed with double SHA256 (known an HASH256 in Bitcoin)
before passing it to signing algorithm (which expects length C<32> bytestrings).
The returned hash reference contains:
=over
=item C<signature> - 64 bytes containing r (32 bytes) and s (32 bytes) values
lib/Bitcoin/Secp256k1.pm view on Meta::CPAN
Recoverable signatures allow the public key to be recovered from the signature
and message, which is useful for systems like Ethereum where only the signature
is stored in transactions.
This method always produces deterministic signatures suitable for use in
recoverable signature systems.
=head3 sign_digest
$signature = $secp256k1->sign_digest($private_key, $message_digest)
Same as L</sign_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 sign_digest_schnorr
$signature = $secp256k1->sign_digest_schnorr($private_key, $message_digest)
Same as L</sign_message_schnorr>, but it does not perform SHA256 on its input.
While Schnorr allows any length message, this method requires
C<$message_digest> to be a bytestring of length C<32>.
=head3 sign_digest_recoverable
$signature = $secp256k1->sign_digest_recoverable($private_key, $message_digest)
Same as L</sign_message_recoverable>, but it does not perform double SHA256 on its input.
Because of that, C<$message_digest> must be a bytestring of length C<32>.
Returns the same hash reference format as L</sign_message_recoverable>.
=head3 recover_public_key_digest
$public_key = $secp256k1->recover_public_key_digest($recoverable_signature, $message_digest)
lib/Bitcoin/Secp256k1.pm view on Meta::CPAN
Same as L</verify_message_recoverable>, 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 xonly_public_key
$xonly_public_key = $secp256k1->xonly_public_key($public_key)
Returns a xonly form of C<$public_key>. This form is used in Taproot.
=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.
=head3 add_private_key
$tweaked = $secp256k1->add_private_key($private_key, $tweak)
Add a C<$tweak> (bytestring of length C<32>) to C<$private_key> (bytestring of
length C<32>). The result is a bytestring containing tweaked private key.
If the arguments or the resulting key are not valid, an exception will be thrown.
=head3 add_public_key
$tweaked = $secp256k1->add_public_key($public_key, $tweak)
Add a C<$tweak> (bytestring of length C<32>) to C<$public_key> (bytestring with
compressed or uncompressed public key). The result is a bytestring containing
tweaked public key in compressed form.
If the arguments or the resulting key are not valid, an exception will be thrown.
=head3 multiply_private_key
$tweaked = $secp256k1->multiply_private_key($private_key, $tweak)
Same as L</add_private_key>, but performs multiplication instead of addition.
=head3 multiply_public_key
$tweaked = $secp256k1->multiply_public_key($public_key, $tweak)
Same as L</add_public_key>, but performs multiplication instead of addition.
=head3 combine_public_keys
$combined = $secp256k1->combine_public_keys(@pubkeys)
use Secp256k1Test;
################################################################################
# This tests whether high level Perl API is working correctly.
################################################################################
my $secp = Bitcoin::Secp256k1->new;
my %t = Secp256k1Test->test_data;
subtest 'should verify a private key' => sub {
ok $secp->verify_private_key("\x12" x 32), 'verification ok';
ok !$secp->verify_private_key("\xff" x 32), 'larger than curve order ok';
ok !$secp->verify_private_key("\x12" x 31), 'not 32 bytes ok';
};
subtest 'should verify a public key' => sub {
ok $secp->verify_public_key("\x02" . ("\x12" x 32)), 'verification ok';
ok !$secp->verify_public_key("\x02" . ("\xff" x 32)), 'larger than curve order ok';
ok !$secp->verify_public_key("\x02" . ("\x12" x 30)), 'bad length ok';
};
subtest 'should derive a public key' => sub {
is $secp->create_public_key($t{privkey}), $t{pubkey}, 'pubkey derived ok';
subtest 'should get a xonly public key' => sub {
is $secp->xonly_public_key($t{pubkey}), $t{xonly_pubkey}, 'xonly public key 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';
};
subtest 'should add' => sub {
my $added_pubkey = pack 'H*', '0260213f6d967636c54d8845c23098e0f63d906b7903d23692efa155a155eda169';
my $added_privkey = pack 'H*', '67a239562bcdfa07345b72305eb8567436be572159b3ef64a91d0392388d04bf';
my $tweak = "\x06" x 32;
is $secp->add_public_key($t{pubkey}, $tweak), $added_pubkey, 'added pubkey ok';
is $secp->add_private_key($t{privkey}, $tweak), $added_privkey, 'added privkey ok';
};
subtest 'should multiply' => sub {
my $multiplied_pubkey = pack 'H*', '0311ab47c9252066f0ca5946d70c3aaac1486d65969b90cd57207476963c9f9af3';
my $multiplied_privkey = pack 'H*', '2d40a33515eb26b0fbce29e0e9645e15d6ff4892a17e59ab31b66b496780a683';
my $tweak = "\x06" x 32;
is $secp->multiply_public_key($t{pubkey}, $tweak), $multiplied_pubkey, 'multiplied pubkey ok';
is $secp->multiply_private_key($t{privkey}, $tweak), $multiplied_privkey, 'multiplied privkey ok';
};
subtest 'should combine one pubkey into itself' => sub {
is $secp->combine_public_keys($t{pubkey}), $t{pubkey}, 'combined pubkey ok';
};
subtest 'should combine multiple pubkeys' => sub {
my @to_combine = (
pack('H*', '0311ab47c9252066f0ca5946d70c3aaac1486d65969b90cd57207476963c9f9af3'),
pack('H*', '0260213f6d967636c54d8845c23098e0f63d906b7903d23692efa155a155eda169'),
t/author-leaks.t view on Meta::CPAN
use Digest::SHA qw(sha256);
use constant HAS_TEST_MEMORYGROWTH => eval { require Test::MemoryGrowth; 1 };
plan skip_all => 'This test requires Test::MemoryGrowth module'
unless HAS_TEST_MEMORYGROWTH;
################################################################################
# This tests whether Bitcoin::Secp256k1 leaks memory
################################################################################
my $private_key = "\x12" x 32;
my $message = 'a quick brown fox jumped over a lazy dog';
Test::MemoryGrowth::no_growth {
my $secp = Bitcoin::Secp256k1->new;
my $compressed_public_key = $secp->create_public_key($private_key);
my $public_key = $secp->compress_public_key($compressed_public_key, !!0);
my $result = eval {
$secp->combine_public_keys($public_key, "\x02" . "\xff" x 32);
1;
};
die 'valid pub?' if $result;
my $signature = $secp->sign_message($private_key, $message);
die 'invalid sig?' unless $secp->verify_message($public_key, $signature, $message);
my $schnorr_signature = $secp->sign_message_schnorr($private_key, $message);
my $xonly_pubkey = $secp->xonly_public_key($public_key);
die 'invalid sig?' unless $secp->verify_message_schnorr($xonly_pubkey, $schnorr_signature, $message);
# Test recoverable signatures
my $recoverable_signature = $secp->sign_message_recoverable($private_key, $message);
die 'invalid recoverable sig?'
unless defined $recoverable_signature->{signature} && defined $recoverable_signature->{recovery_id};
my $digest = sha256(sha256($message));
my $recovered_pubkey = $secp->recover_public_key_digest($recoverable_signature, $digest);
die 'recovery failed?' unless $recovered_pubkey eq $compressed_public_key;
die 'invalid recoverable verification?'
unless $secp->verify_message_recoverable($compressed_public_key, $recoverable_signature, $message);
}
t/edge-cases.t view on Meta::CPAN
my $ex = dies { $secp->verify_digest($t{pubkey}, "\x12" x 65, "\x12" x 32) };
like $ex, qr/the input does not appear to be a valid signature/, 'exception ok';
};
subtest 'should die with invalid digest' => sub {
my $ex = dies { $secp->verify_digest($t{pubkey}, $t{sig}, "\x12" x 35) };
like $ex, qr/digest must be a bytestring of length 32/, 'exception ok';
};
subtest 'should die on invalid addition' => sub {
my $negated = $secp->negate_private_key($t{privkey});
my $ex = dies { $secp->add_private_key($t{privkey}, $negated) };
like $ex, qr/resulting added privkey is not valid/, 'exception ok';
};
subtest 'should die on invalid multiplication' => sub {
my $ex = dies { $secp->multiply_public_key($t{pubkey}, "\xff" x 32) };
like $ex, qr/multiplication arguments are not valid/, 'exception ok';
};
subtest 'should die on invalid combination of public keys' => sub {
my $ex = dies { $secp->combine_public_keys($t{pubkey}, "\02" . "\xff" x 32) };